Refactor VmReader/Writer to support both kernel space and user space

This commit is contained in:
Chen Chengjun
2024-06-28 09:45:51 +08:00
committed by Tate, Hongliang Tian
parent 994427c71a
commit 09b6153d29
5 changed files with 398 additions and 133 deletions

View File

@ -82,7 +82,7 @@ impl Default for TtyDriver {
fn console_input_callback(mut reader: VmReader) { fn console_input_callback(mut reader: VmReader) {
let tty_driver = get_tty_driver(); let tty_driver = get_tty_driver();
while reader.remain() > 0 { while reader.remain() > 0 {
let ch = reader.read_val(); let ch = reader.read_val().unwrap();
tty_driver.push_char(ch); tty_driver.push_char(ch);
} }
} }

View File

@ -114,14 +114,20 @@ impl HasPaddr for Frame {
impl<'a> Frame { impl<'a> Frame {
/// Returns a reader to read data from it. /// Returns a reader to read data from it.
pub fn reader(&'a self) -> VmReader<'a> { pub fn reader(&'a self) -> VmReader<'a> {
// SAFETY: the memory of the page is contiguous and is valid during `'a`. // SAFETY: the memory of the page is untyped, contiguous and is valid during `'a`.
unsafe { VmReader::from_raw_parts(self.as_ptr(), self.size()) } // Currently, only slice can generate `VmWriter` with typed memory, and this `Frame` cannot
// generate or be generated from an alias slice, so the reader will not overlap with `VmWriter`
// with typed memory.
unsafe { VmReader::from_kernel_space(self.as_ptr(), self.size()) }
} }
/// Returns a writer to write data into it. /// Returns a writer to write data into it.
pub fn writer(&'a self) -> VmWriter<'a> { pub fn writer(&'a self) -> VmWriter<'a> {
// SAFETY: the memory of the page is contiguous and is valid during `'a`. // SAFETY: the memory of the page is untyped, contiguous and is valid during `'a`.
unsafe { VmWriter::from_raw_parts_mut(self.as_mut_ptr(), self.size()) } // Currently, only slice can generate `VmReader` with typed memory, and this `Frame` cannot
// generate or be generated from an alias slice, so the writer will not overlap with `VmReader`
// with typed memory.
unsafe { VmWriter::from_kernel_space(self.as_mut_ptr(), self.size()) }
} }
} }

View File

@ -101,14 +101,20 @@ impl Segment {
impl<'a> Segment { impl<'a> Segment {
/// Returns a reader to read data from it. /// Returns a reader to read data from it.
pub fn reader(&'a self) -> VmReader<'a> { pub fn reader(&'a self) -> VmReader<'a> {
// SAFETY: the memory of the page frames is contiguous and is valid during `'a`. // SAFETY: the memory of the page frames is untyped, contiguous and is valid during `'a`.
unsafe { VmReader::from_raw_parts(self.as_ptr(), self.nbytes()) } // Currently, only slice can generate `VmWriter` with typed memory, and this `Segment` cannot
// generate or be generated from an alias slice, so the reader will not overlap with `VmWriter`
// with typed memory.
unsafe { VmReader::from_kernel_space(self.as_ptr(), self.nbytes()) }
} }
/// Returns a writer to write data into it. /// Returns a writer to write data into it.
pub fn writer(&'a self) -> VmWriter<'a> { pub fn writer(&'a self) -> VmWriter<'a> {
// SAFETY: the memory of the page frames is contiguous and is valid during `'a`. // SAFETY: the memory of the page frames is untyped, contiguous and is valid during `'a`.
unsafe { VmWriter::from_raw_parts_mut(self.as_mut_ptr(), self.nbytes()) } // Currently, only slice can generate `VmReader` with typed memory, and this `Segment` cannot
// generate or be generated from an alias slice, so the writer will not overlap with `VmReader`
// with typed memory.
unsafe { VmWriter::from_kernel_space(self.as_mut_ptr(), self.nbytes()) }
} }
} }

View File

@ -8,7 +8,15 @@ use align_ext::AlignExt;
use inherit_methods_macro::inherit_methods; use inherit_methods_macro::inherit_methods;
use pod::Pod; use pod::Pod;
use crate::prelude::*; use crate::{
arch::mm::__memcpy_fallible,
mm::{
kspace::{KERNEL_BASE_VADDR, KERNEL_END_VADDR},
MAX_USERSPACE_VADDR,
},
prelude::*,
Error,
};
/// A trait that enables reading/writing data from/to a VM object, /// A trait that enables reading/writing data from/to a VM object,
/// e.g., [`VmSpace`], [`FrameVec`], and [`Frame`]. /// e.g., [`VmSpace`], [`FrameVec`], and [`Frame`].
@ -168,21 +176,160 @@ impl_vmio_pointer!(&mut T, "(**self)");
impl_vmio_pointer!(Box<T>, "(**self)"); impl_vmio_pointer!(Box<T>, "(**self)");
impl_vmio_pointer!(Arc<T>, "(**self)"); impl_vmio_pointer!(Arc<T>, "(**self)");
/// VmReader is a reader for reading data from a contiguous range of memory. /// A marker structure used for [`VmReader`] and [`VmWriter`],
pub struct VmReader<'a> { /// representing their operated memory scope is in user space.
cursor: *const u8, pub struct UserSpace;
end: *const u8,
phantom: PhantomData<&'a [u8]>,
}
impl<'a> VmReader<'a> { /// A marker structure used for [`VmReader`] and [`VmWriter`],
/// Constructs a VmReader from a pointer and a length. /// representing their operated memory scope is in kernel space.
pub struct KernelSpace;
/// Copies `len` bytes from `src` to `dst`.
/// ///
/// # Safety /// # Safety
/// ///
/// User must ensure the memory from `ptr` to `ptr.add(len)` is contiguous. /// - Mappings of virtual memory range [`src`..`src` + len] and [`dst`..`dst` + len]
/// User must ensure the memory is valid during the entire period of `'a`. /// must be [valid].
pub const unsafe fn from_raw_parts(ptr: *const u8, len: usize) -> Self { /// - If one of the memory represents typed memory, these two virtual
/// memory ranges and their corresponding physical pages should _not_ overlap.
///
/// Operation on typed memory may be safe only if it is plain-old-data. Otherwise
/// the safety requirements of [`core::ptr::copy`] should also be considered.
///
/// [valid]: core::ptr#safety
unsafe fn memcpy(src: *const u8, dst: *mut u8, len: usize) {
core::ptr::copy(src, dst, len);
}
/// Copies `len` bytes from `src` to `dst`.
/// This function will early stop copying if encountering an unresolvable page fault.
///
/// Returns the number of successfully copied bytes.
///
/// # Safety
///
/// - Users should ensure one of [`src`..`src` + len] and [`dst`..`dst` + len]
/// is in user space, and the other virtual memory range is in kernel space
/// and is ensured to be [valid].
/// - Users should ensure this function only be invoked when a suitable page
/// table is activated.
/// - The underlying physical memory range of [`src`..`src` + len] and [`dst`..`dst` + len]
/// should _not_ overlap if the kernel space memory represent typed memory.
///
/// [valid]: core::ptr#safety
unsafe fn memcpy_fallible(src: *const u8, dst: *mut u8, len: usize) -> usize {
let failed_bytes = __memcpy_fallible(dst, src, len);
len - failed_bytes
}
/// `VmReader` is a reader for reading data from a contiguous range of memory.
///
/// The memory range read by `VmReader` can be in either kernel space or user space.
/// When the operating range is in kernel space, the memory within that range
/// is guaranteed to be valid.
/// When the operating range is in user space, it is ensured that the page table of
/// the process creating the `VmReader` is active for the duration of `'a`.
///
/// When perform reading with a `VmWriter`, if one of them represents typed memory,
/// it can ensure that the reading range in this reader and writing range in the
/// writer are not overlapped.
///
/// NOTE: The overlap mentioned above is at both the virtual address level
/// and physical address level. There is not guarantee for the operation results
/// of `VmReader` and `VmWriter` in overlapping untyped addresses, and it is
/// the user's responsibility to handle this situation.
pub struct VmReader<'a, Space = KernelSpace> {
cursor: *const u8,
end: *const u8,
phantom: PhantomData<(&'a [u8], Space)>,
}
macro_rules! impl_read_fallible {
($read_space:ty, $write_space:ty) => {
impl<'a> VmReader<'a, $read_space> {
/// Reads all data into the writer until one of the three conditions is met:
/// 1. The reader has no remaining data.
/// 2. The writer has no available space.
/// 3. The reader/writer encounters some error.
///
/// On success, the number of bytes read is returned;
/// On error, both the error and the number of bytes read so far are returned.
pub fn read_fallible(
&mut self,
writer: &mut VmWriter<'_, $write_space>,
) -> core::result::Result<usize, (Error, usize)> {
let copy_len = self.remain().min(writer.avail());
if copy_len == 0 {
return Ok(0);
}
// SAFETY: This method is only implemented when one of the operated
// `VmReader` or `VmWriter` is in user space.
// The the corresponding page table of the user space memory is
// guaranteed to be activated due to its construction requirement.
// The kernel space memory range will be valid since `copy_len` is the minimum
// of the reader's remaining data and the writer's available space, and will
// not overlap with user space memory range in physical address level if it
// represents typed memory.
let copied_len = unsafe {
let copied_len = memcpy_fallible(self.cursor, writer.cursor, copy_len);
self.cursor = self.cursor.add(copied_len);
writer.cursor = writer.cursor.add(copied_len);
copied_len
};
if copied_len < copy_len {
Err((Error::PageFault, copied_len))
} else {
Ok(copied_len)
}
}
}
};
}
macro_rules! impl_write_fallible {
($read_space:ty, $write_space:ty) => {
impl<'a> VmWriter<'a, $write_space> {
/// Writes all data from the reader until one of the three conditions is met:
/// 1. The reader has no remaining data.
/// 2. The writer has no available space.
/// 3. The reader/writer encounters some error.
///
/// On success, the number of bytes written is returned;
/// On error, both the error and the number of bytes written so far are returned.
pub fn write_fallible(
&mut self,
reader: &mut VmReader<'_, $read_space>,
) -> core::result::Result<usize, (Error, usize)> {
reader.read_fallible(self)
}
}
};
}
// TODO: implement an additional function `memcpy_nonoverlapping_fallible`
// to implement read/write instruction from user space to user space.
impl_read_fallible!(UserSpace, KernelSpace);
impl_read_fallible!(KernelSpace, UserSpace);
impl_write_fallible!(UserSpace, KernelSpace);
impl_write_fallible!(KernelSpace, UserSpace);
impl<'a> VmReader<'a, KernelSpace> {
/// Constructs a `VmReader` from a pointer and a length, which represents
/// a memory range in kernel space.
///
/// # Safety
///
/// Users must ensure the memory from `ptr` to `ptr.add(len)` is contiguous.
/// Users must ensure the memory is valid during the entire period of `'a`.
/// Users must ensure the memory should _not_ overlap with other `VmWriter`s
/// with typed memory, and if the memory range in this `VmReader` is typed,
/// it should _not_ overlap with other `VmWriter`s.
/// The user space memory is treated as untyped.
pub unsafe fn from_kernel_space(ptr: *const u8, len: usize) -> Self {
debug_assert!(KERNEL_BASE_VADDR <= ptr as usize);
debug_assert!(ptr.add(len) as usize <= KERNEL_END_VADDR);
Self { Self {
cursor: ptr, cursor: ptr,
end: ptr.add(len), end: ptr.add(len),
@ -190,6 +337,85 @@ impl<'a> VmReader<'a> {
} }
} }
/// Reads all data into the writer until one of the two conditions is met:
/// 1. The reader has no remaining data.
/// 2. The writer has no available space.
///
/// Returns the number of bytes read.
pub fn read(&mut self, writer: &mut VmWriter<'_, KernelSpace>) -> usize {
let copy_len = self.remain().min(writer.avail());
if copy_len == 0 {
return 0;
}
// SAFETY: the reading memory range and writing memory range will be valid
// since `copy_len` is the minimum of the reader's remaining data and the
// writer's available space, and will not overlap if one of them represents
// typed memory.
unsafe {
memcpy(self.cursor, writer.cursor, copy_len);
self.cursor = self.cursor.add(copy_len);
writer.cursor = writer.cursor.add(copy_len);
}
copy_len
}
/// Reads a value of `Pod` type.
///
/// If the length of the `Pod` type exceeds `self.remain()`,
/// this method will return `Err`.
pub fn read_val<T: Pod>(&mut self) -> Result<T> {
if self.remain() < core::mem::size_of::<T>() {
return Err(Error::InvalidArgs);
}
let mut val = T::new_uninit();
let mut writer = VmWriter::from(val.as_bytes_mut());
self.read(&mut writer);
Ok(val)
}
}
impl<'a> VmReader<'a, UserSpace> {
/// Constructs a `VmReader` from a pointer and a length, which represents
/// a memory range in user space.
///
/// # Safety
///
/// Users must ensure the memory from `ptr` to `ptr.add(len)` is contiguous.
/// Users must ensure that the page table for the process in which this constructor is called
/// are active during the entire period of `'a`.
pub unsafe fn from_user_space(ptr: *const u8, len: usize) -> Self {
debug_assert!((ptr as usize).checked_add(len).unwrap_or(usize::MAX) <= MAX_USERSPACE_VADDR);
Self {
cursor: ptr,
end: ptr.add(len),
phantom: PhantomData,
}
}
/// Reads a value of `Pod` type.
///
/// If the length of the `Pod` type exceeds `self.remain()`,
/// or the value can not be read completely,
/// this method will return `Err`.
pub fn read_val<T: Pod>(&mut self) -> Result<T> {
if self.remain() < core::mem::size_of::<T>() {
return Err(Error::InvalidArgs);
}
let mut val = T::new_uninit();
let mut writer = VmWriter::from(val.as_bytes_mut());
self.read_fallible(&mut writer)
.map(|_| val)
.map_err(|err| err.0)
}
}
impl<'a, Space> VmReader<'a, Space> {
/// Returns the number of bytes for the remaining data. /// Returns the number of bytes for the remaining data.
pub const fn remain(&self) -> usize { pub const fn remain(&self) -> usize {
// SAFETY: the end is equal to or greater than the cursor. // SAFETY: the end is equal to or greater than the cursor.
@ -230,69 +456,56 @@ impl<'a> VmReader<'a> {
unsafe { self.cursor = self.cursor.add(nbytes) }; unsafe { self.cursor = self.cursor.add(nbytes) };
self self
} }
/// Reads all data into the writer until one of the two conditions is met:
/// 1. The reader has no remaining data.
/// 2. The writer has no available space.
///
/// Returns the number of bytes read.
///
/// It pulls the number of bytes data from the reader and
/// fills in the writer with the number of bytes.
pub fn read(&mut self, writer: &mut VmWriter<'_>) -> usize {
let copy_len = self.remain().min(writer.avail());
if copy_len == 0 {
return 0;
}
// SAFETY: the memory range is valid since `copy_len` is the minimum
// of the reader's remaining data and the writer's available space.
unsafe {
core::ptr::copy(self.cursor, writer.cursor, copy_len);
self.cursor = self.cursor.add(copy_len);
writer.cursor = writer.cursor.add(copy_len);
}
copy_len
}
/// Read a value of `Pod` type.
///
/// # Panic
///
/// If the length of the `Pod` type exceeds `self.remain()`, then this method will panic.
pub fn read_val<T: Pod>(&mut self) -> T {
assert!(self.remain() >= core::mem::size_of::<T>());
let mut val = T::new_uninit();
let mut writer = VmWriter::from(val.as_bytes_mut());
let read_len = self.read(&mut writer);
val
}
} }
impl<'a> From<&'a [u8]> for VmReader<'a> { impl<'a> From<&'a [u8]> for VmReader<'a> {
fn from(slice: &'a [u8]) -> Self { fn from(slice: &'a [u8]) -> Self {
// SAFETY: the range of memory is contiguous and is valid during `'a`. // SAFETY: the range of memory is contiguous and is valid during `'a`,
unsafe { Self::from_raw_parts(slice.as_ptr(), slice.len()) } // and will not overlap with other `VmWriter` since the slice already has
// an immutable reference. The slice will not be mapped to the user space hence
// it will also not overlap with `VmWriter` generated from user space.
unsafe { Self::from_kernel_space(slice.as_ptr(), slice.len()) }
} }
} }
/// VmWriter is a writer for writing data to a contiguous range of memory. /// `VmWriter` is a writer for writing data to a contiguous range of memory.
pub struct VmWriter<'a> { ///
/// The memory range write by `VmWriter` can be in either kernel space or user space.
/// When the operating range is in kernel space, the memory within that range
/// is guaranteed to be valid.
/// When the operating range is in user space, it is ensured that the page table of
/// the process creating the `VmWriter` is active for the duration of `'a`.
///
/// When perform writing with a `VmReader`, if one of them represents typed memory,
/// it can ensure that the writing range in this writer and reading range in the
/// reader are not overlapped.
///
/// NOTE: The overlap mentioned above is at both the virtual address level
/// and physical address level. There is not guarantee for the operation results
/// of `VmReader` and `VmWriter` in overlapping untyped addresses, and it is
/// the user's responsibility to handle this situation.
pub struct VmWriter<'a, Space = KernelSpace> {
cursor: *mut u8, cursor: *mut u8,
end: *mut u8, end: *mut u8,
phantom: PhantomData<&'a mut [u8]>, phantom: PhantomData<(&'a mut [u8], Space)>,
} }
impl<'a> VmWriter<'a> { impl<'a> VmWriter<'a, KernelSpace> {
/// Constructs a VmWriter from a pointer and a length. /// Constructs a `VmWriter` from a pointer and a length, which represents
/// a memory range in kernel space.
/// ///
/// # Safety /// # Safety
/// ///
/// User must ensure the memory from `ptr` to `ptr.add(len)` is contiguous. /// Users must ensure the memory from `ptr` to `ptr.add(len)` is contiguous.
/// User must ensure the memory is valid during the entire period of `'a`. /// Users must ensure the memory is valid during the entire period of `'a`.
pub const unsafe fn from_raw_parts_mut(ptr: *mut u8, len: usize) -> Self { /// Users must ensure the memory should _not_ overlap with other `VmWriter`s
/// and `VmReader`s with typed memory, and if the memory range in this `VmWriter`
/// is typed, it should _not_ overlap with other `VmReader`s and `VmWriter`s.
/// The user space memory is treated as untyped.
pub unsafe fn from_kernel_space(ptr: *mut u8, len: usize) -> Self {
debug_assert!(KERNEL_BASE_VADDR <= ptr as usize);
debug_assert!(ptr.add(len) as usize <= KERNEL_END_VADDR);
Self { Self {
cursor: ptr, cursor: ptr,
end: ptr.add(len), end: ptr.add(len),
@ -300,69 +513,27 @@ impl<'a> VmWriter<'a> {
} }
} }
/// Returns the number of bytes for the available space. /// Writes all data from the reader until one of the two conditions is met:
pub const fn avail(&self) -> usize { /// 1. The reader has no remaining data.
// SAFETY: the end is equal to or greater than the cursor. /// 2. The writer has no available space.
unsafe { self.end.sub_ptr(self.cursor) }
}
/// Returns the cursor pointer, which refers to the address of the next byte to write.
pub const fn cursor(&self) -> *mut u8 {
self.cursor
}
/// Returns if it has avaliable space to write.
pub const fn has_avail(&self) -> bool {
self.avail() > 0
}
/// Limits the length of available space.
///
/// This method ensures the postcondition of `self.avail() <= max_avail`.
pub const fn limit(mut self, max_avail: usize) -> Self {
if max_avail < self.avail() {
// SAFETY: the new end is less than the old end.
unsafe { self.end = self.cursor.add(max_avail) };
}
self
}
/// Skips the first `nbytes` bytes of data.
/// The length of available space is decreased accordingly.
///
/// # Panic
///
/// If `nbytes` is greater than `self.avail()`, then the method panics.
pub fn skip(mut self, nbytes: usize) -> Self {
assert!(nbytes <= self.avail());
// SAFETY: the new cursor is less than or equal to the end.
unsafe { self.cursor = self.cursor.add(nbytes) };
self
}
/// Writes data from the reader until one of the two conditions is met:
/// 1. The writer has no available space.
/// 2. The reader has no remaining data.
/// ///
/// Returns the number of bytes written. /// Returns the number of bytes written.
/// pub fn write(&mut self, reader: &mut VmReader<'_, KernelSpace>) -> usize {
/// It pulls the number of bytes data from the reader and reader.read(self)
/// fills in the writer with the number of bytes.
pub fn write(&mut self, reader: &mut VmReader<'_>) -> usize {
let copy_len = self.avail().min(reader.remain());
if copy_len == 0 {
return 0;
} }
// SAFETY: the memory range is valid since `copy_len` is the minimum /// Writes a value of `Pod` type.
// of the reader's remaining data and the writer's available space. ///
unsafe { /// If the length of the `Pod` type exceeds `self.avail()`,
core::ptr::copy(reader.cursor, self.cursor, copy_len); /// this method will return `Err`.
self.cursor = self.cursor.add(copy_len); pub fn write_val<T: Pod>(&mut self, new_val: &T) -> Result<()> {
reader.cursor = reader.cursor.add(copy_len); if self.avail() < core::mem::size_of::<T>() {
return Err(Error::InvalidArgs);
} }
copy_len
let mut reader = VmReader::from(new_val.as_bytes());
self.write(&mut reader);
Ok(())
} }
/// Fills the available space by repeating `value`. /// Fills the available space by repeating `value`.
@ -396,9 +567,91 @@ impl<'a> VmWriter<'a> {
} }
} }
impl<'a> VmWriter<'a, UserSpace> {
/// Constructs a `VmWriter` from a pointer and a length, which represents
/// a memory range in user space.
///
/// # Safety
///
/// Users must ensure the memory from `ptr` to `ptr.add(len)` is contiguous.
/// Users must ensure that the page table for the process in which this constructor is called
/// are active during the entire period of `'a`.
pub unsafe fn from_user_space(ptr: *mut u8, len: usize) -> Self {
debug_assert!((ptr as usize).checked_add(len).unwrap_or(usize::MAX) <= MAX_USERSPACE_VADDR);
Self {
cursor: ptr,
end: ptr.add(len),
phantom: PhantomData,
}
}
/// Writes a value of `Pod` type.
///
/// If the length of the `Pod` type exceeds `self.avail()`,
/// or the value can not be write completely,
/// this method will return `Err`.
pub fn write_val<T: Pod>(&mut self, new_val: &T) -> Result<()> {
if self.avail() < core::mem::size_of::<T>() {
return Err(Error::InvalidArgs);
}
let mut reader = VmReader::from(new_val.as_bytes());
self.write_fallible(&mut reader).map_err(|err| err.0)?;
Ok(())
}
}
impl<'a, Space> VmWriter<'a, Space> {
/// Returns the number of bytes for the available space.
pub const fn avail(&self) -> usize {
// SAFETY: the end is equal to or greater than the cursor.
unsafe { self.end.sub_ptr(self.cursor) }
}
/// Returns the cursor pointer, which refers to the address of the next byte to write.
pub const fn cursor(&self) -> *mut u8 {
self.cursor
}
/// Returns if it has available space to write.
pub const fn has_avail(&self) -> bool {
self.avail() > 0
}
/// Limits the length of available space.
///
/// This method ensures the post condition of `self.avail() <= max_avail`.
pub const fn limit(mut self, max_avail: usize) -> Self {
if max_avail < self.avail() {
// SAFETY: the new end is less than the old end.
unsafe { self.end = self.cursor.add(max_avail) };
}
self
}
/// Skips the first `nbytes` bytes of data.
/// The length of available space is decreased accordingly.
///
/// # Panic
///
/// If `nbytes` is greater than `self.avail()`, then the method panics.
pub fn skip(mut self, nbytes: usize) -> Self {
assert!(nbytes <= self.avail());
// SAFETY: the new cursor is less than or equal to the end.
unsafe { self.cursor = self.cursor.add(nbytes) };
self
}
}
impl<'a> From<&'a mut [u8]> for VmWriter<'a> { impl<'a> From<&'a mut [u8]> for VmWriter<'a> {
fn from(slice: &'a mut [u8]) -> Self { fn from(slice: &'a mut [u8]) -> Self {
// SAFETY: the range of memory is contiguous and is valid during `'a`. // SAFETY: the range of memory is contiguous and is valid during `'a`, and
unsafe { Self::from_raw_parts_mut(slice.as_mut_ptr(), slice.len()) } // will not overlap with other `VmWriter`s and `VmReader`s since the slice
// already has an mutable reference. The slice will not be mapped to the user
// space hence it will also not overlap with `VmWriter`s and `VmReader`s
// generated from user space.
unsafe { Self::from_kernel_space(slice.as_mut_ptr(), slice.len()) }
} }
} }

View File

@ -27,7 +27,7 @@ use spin::Once;
pub use self::{ pub use self::{
dma::{Daddr, DmaCoherent, DmaDirection, DmaStream, DmaStreamSlice, HasDaddr}, dma::{Daddr, DmaCoherent, DmaDirection, DmaStream, DmaStreamSlice, HasDaddr},
frame::{options::FrameAllocOptions, Frame, FrameVec, FrameVecIter, Segment}, frame::{options::FrameAllocOptions, Frame, FrameVec, FrameVecIter, Segment},
io::{VmIo, VmReader, VmWriter}, io::{KernelSpace, UserSpace, VmIo, VmReader, VmWriter},
page_prop::{CachePolicy, PageFlags, PageProperty}, page_prop::{CachePolicy, PageFlags, PageProperty},
space::{VmMapOptions, VmSpace}, space::{VmMapOptions, VmSpace},
}; };