Skip to content

Commit

Permalink
Introduce a new io_buffer module.
Browse files Browse the repository at this point in the history
It abstracts buffers involved in IO. This is in preparation for allowing
drivers to implement `write_iter` and `read_iter`.

No behaviour change is intended, this is a pure refactor.

Signed-off-by: Wedson Almeida Filho <wedsonaf@google.com>
  • Loading branch information
wedsonaf committed Apr 27, 2021
1 parent e3dea04 commit d3ab005
Show file tree
Hide file tree
Showing 14 changed files with 191 additions and 143 deletions.
2 changes: 1 addition & 1 deletion drivers/android/defs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use core::ops::{Deref, DerefMut};
use kernel::{
bindings,
bindings::*,
user_ptr::{ReadableFromBytes, WritableToBytes},
io_buffer::{ReadableFromBytes, WritableToBytes},
};

macro_rules! pub_no_prefix {
Expand Down
1 change: 1 addition & 0 deletions drivers/android/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use core::{
sync::atomic::{AtomicU64, Ordering},
};
use kernel::{
io_buffer::IoBufferWriter,
linked_list::{GetLinks, Links, List},
prelude::*,
sync::{Guard, LockedBy, Mutex, Ref, SpinLock},
Expand Down
1 change: 1 addition & 0 deletions drivers/android/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use core::{
use kernel::{
bindings, c_types,
file_operations::{File, FileOpener, FileOperations, IoctlCommand, IoctlHandler, PollTable},
io_buffer::{IoBufferReader, IoBufferWriter},
linked_list::List,
pages::Pages,
prelude::*,
Expand Down
1 change: 1 addition & 0 deletions drivers/android/rust_binder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use alloc::{boxed::Box, sync::Arc};
use core::pin::Pin;
use kernel::{
cstr,
io_buffer::IoBufferWriter,
linked_list::{GetLinks, GetLinksWrapped, Links},
miscdev::Registration,
prelude::*,
Expand Down
1 change: 1 addition & 0 deletions drivers/android/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use core::{alloc::AllocError, mem::size_of, pin::Pin};
use kernel::{
bindings,
file_operations::{File, PollTable},
io_buffer::{IoBufferReader, IoBufferWriter},
linked_list::{GetLinks, Links, List},
prelude::*,
sync::{CondVar, Ref, SpinLock},
Expand Down
5 changes: 4 additions & 1 deletion drivers/android/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

use alloc::sync::Arc;
use core::sync::atomic::{AtomicBool, Ordering};
use kernel::{bindings, linked_list::Links, prelude::*, sync::Ref, user_ptr::UserSlicePtrWriter};
use kernel::{
bindings, io_buffer::IoBufferWriter, linked_list::Links, prelude::*, sync::Ref,
user_ptr::UserSlicePtrWriter,
};

use crate::{
defs::*,
Expand Down
154 changes: 154 additions & 0 deletions rust/kernel/io_buffer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// SPDX-License-Identifier: GPL-2.0

//! Buffers used in IO.
use crate::KernelResult;
use alloc::vec::Vec;
use core::mem::{size_of, MaybeUninit};

/// Represents a buffer to be read from during IO.
pub trait IoBufferReader {
/// Returns the number of bytes left to be read from the io buffer.
///
/// Note that even reading less than this number of bytes may fail.
fn len(&self) -> usize;

/// Returns `true` if no data is available in the io buffer.
fn is_empty(&self) -> bool {
self.len() == 0
}

/// Reads raw data from the io buffer into a raw kernel buffer.
///
/// # Safety
///
/// The output buffer must be valid.
unsafe fn read_raw(&mut self, out: *mut u8, len: usize) -> KernelResult;

/// Reads all data remaining in the io buffer.
///
/// Returns `EFAULT` if the address does not currently point to mapped, readable memory.
fn read_all(&mut self) -> KernelResult<Vec<u8>> {
let mut data = Vec::<u8>::new();
data.try_reserve_exact(self.len())?;
data.resize(self.len(), 0);

// SAFETY: The output buffer is valid as we just allocated it.
unsafe { self.read_raw(data.as_mut_ptr(), data.len())? };
Ok(data)
}

/// Reads a byte slice from the io buffer.
///
/// Returns `EFAULT` if the byte slice is bigger than the remaining size of the user slice or
/// if the address does not currently point to mapped, readable memory.
fn read_slice(&mut self, data: &mut [u8]) -> KernelResult {
// SAFETY: The output buffer is valid as it's coming from a live reference.
unsafe { self.read_raw(data.as_mut_ptr(), data.len()) }
}

/// Reads the contents of a plain old data (POD) type from the io buffer.
fn read<T: ReadableFromBytes>(&mut self) -> KernelResult<T> {
let mut out = MaybeUninit::<T>::uninit();
// SAFETY: The buffer is valid as it was just allocated.
unsafe { self.read_raw(out.as_mut_ptr() as _, size_of::<T>()) }?;
// SAFETY: We just initialised the data.
Ok(unsafe { out.assume_init() })
}
}

/// Represents a buffer to be written to during IO.
pub trait IoBufferWriter {
/// Returns the number of bytes left to be written into the io buffer.
///
/// Note that even writing less than this number of bytes may fail.
fn len(&self) -> usize;

/// Returns `true` if the io buffer cannot hold any additional data.
fn is_empty(&self) -> bool {
self.len() == 0
}

/// Writes zeroes to the io buffer.
///
/// Differently from the other write functions, `clear` will zero as much as it can and update
/// the writer internal state to reflect this. It will, however, return an error if it cannot
/// clear `len` bytes.
///
/// For example, if a caller requests that 100 bytes be cleared but a segfault happens after
/// 20 bytes, then EFAULT is returned and the writer is advanced by 20 bytes.
fn clear(&mut self, len: usize) -> KernelResult;

/// Writes a byte slice into the io buffer.
///
/// Returns `EFAULT` if the byte slice is bigger than the remaining size of the io buffer or if
/// the address does not currently point to mapped, writable memory.
fn write_slice(&mut self, data: &[u8]) -> KernelResult {
// SAFETY: The input buffer is valid as it's coming from a live reference.
unsafe { self.write_raw(data.as_ptr(), data.len()) }
}

/// Writes raw data to the io buffer from a raw kernel buffer.
///
/// # Safety
///
/// The input buffer must be valid.
unsafe fn write_raw(&mut self, data: *const u8, len: usize) -> KernelResult;

/// Writes the contents of the given data into the io buffer.
fn write<T: WritableToBytes>(&mut self, data: &T) -> KernelResult<()> {
// SAFETY: The input buffer is valid as it's coming from a live
// reference to a type that implements `WritableToBytes`.
unsafe { self.write_raw(data as *const T as _, size_of::<T>()) }
}
}

/// Specifies that a type is safely readable from byte slices.
///
/// Not all types can be safely read from byte slices; examples from
/// <https://doc.rust-lang.org/reference/behavior-considered-undefined.html> include `bool`
/// that must be either `0` or `1`, and `char` that cannot be a surrogate or above `char::MAX`.
///
/// # Safety
///
/// Implementers must ensure that the type is made up only of types that can be safely read from
/// arbitrary byte sequences (e.g., `u32`, `u64`, etc.).
pub unsafe trait ReadableFromBytes {}

// SAFETY: All bit patterns are acceptable values of the types below.
unsafe impl ReadableFromBytes for u8 {}
unsafe impl ReadableFromBytes for u16 {}
unsafe impl ReadableFromBytes for u32 {}
unsafe impl ReadableFromBytes for u64 {}
unsafe impl ReadableFromBytes for usize {}
unsafe impl ReadableFromBytes for i8 {}
unsafe impl ReadableFromBytes for i16 {}
unsafe impl ReadableFromBytes for i32 {}
unsafe impl ReadableFromBytes for i64 {}
unsafe impl ReadableFromBytes for isize {}

/// Specifies that a type is safely writable to byte slices.
///
/// This means that we don't read undefined values (which leads to UB) in preparation for writing
/// to the byte slice. It also ensures that no potentially sensitive information is leaked into the
/// byte slices.
///
/// # Safety
///
/// A type must not include padding bytes and must be fully initialised to safely implement
/// [`WritableToBytes`] (i.e., it doesn't contain [`MaybeUninit`] fields). A composition of
/// writable types in a structure is not necessarily writable because it may result in padding
/// bytes.
pub unsafe trait WritableToBytes {}

// SAFETY: Initialised instances of the following types have no uninitialised portions.
unsafe impl WritableToBytes for u8 {}
unsafe impl WritableToBytes for u16 {}
unsafe impl WritableToBytes for u32 {}
unsafe impl WritableToBytes for u64 {}
unsafe impl WritableToBytes for usize {}
unsafe impl WritableToBytes for i8 {}
unsafe impl WritableToBytes for i16 {}
unsafe impl WritableToBytes for i32 {}
unsafe impl WritableToBytes for i64 {}
unsafe impl WritableToBytes for isize {}
1 change: 1 addition & 0 deletions rust/kernel/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ pub mod sync;
#[cfg(CONFIG_SYSCTL)]
pub mod sysctl;

pub mod io_buffer;
mod types;
pub mod user_ptr;

Expand Down
9 changes: 6 additions & 3 deletions rust/kernel/pages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
//!
//! TODO: This module is a work in progress.
use crate::{bindings, c_types, user_ptr::UserSlicePtrReader, Error, KernelResult, PAGE_SIZE};
use crate::{
bindings, c_types, io_buffer::IoBufferReader, user_ptr::UserSlicePtrReader, Error,
KernelResult, PAGE_SIZE,
};
use core::{marker::PhantomData, ptr};

extern "C" {
Expand Down Expand Up @@ -95,7 +98,7 @@ impl<const ORDER: u32> Pages<ORDER> {
///
/// Callers must ensure that the destination buffer is valid for the given length.
/// Additionally, if the raw buffer is intended to be recast, they must ensure that the data
/// can be safely cast; [`crate::user_ptr::ReadableFromBytes`] has more details about it.
/// can be safely cast; [`crate::io_buffer::ReadableFromBytes`] has more details about it.
pub unsafe fn read(&self, dest: *mut u8, offset: usize, len: usize) -> KernelResult {
// TODO: For now this only works on the first page.
let end = offset.checked_add(len).ok_or(Error::EINVAL)?;
Expand All @@ -114,7 +117,7 @@ impl<const ORDER: u32> Pages<ORDER> {
///
/// Callers must ensure that the buffer is valid for the given length. Additionally, if the
/// page is (or will be) mapped by userspace, they must ensure that no kernel data is leaked
/// through padding if it was cast from another type; [`crate::user_ptr::WritableToBytes`] has
/// through padding if it was cast from another type; [`crate::io_buffer::WritableToBytes`] has
/// more details about it.
pub unsafe fn write(&self, src: *const u8, offset: usize, len: usize) -> KernelResult {
// TODO: For now this only works on the first page.
Expand Down
11 changes: 6 additions & 5 deletions rust/kernel/sysctl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ use core::mem;
use core::ptr;
use core::sync::atomic;

use crate::bindings;
use crate::c_types;
use crate::error;
use crate::types;
use crate::user_ptr::{UserSlicePtr, UserSlicePtrWriter};
use crate::{
bindings, c_types, error,
io_buffer::IoBufferWriter,
types,
user_ptr::{UserSlicePtr, UserSlicePtrWriter},
};

/// Sysctl storage.
pub trait SysctlStorage: Sync {
Expand Down
Loading

0 comments on commit d3ab005

Please sign in to comment.