Add VmReader and VmWriter

This commit is contained in:
LI Qing
2023-10-30 13:29:01 +08:00
committed by Tate, Hongliang Tian
parent 644ebe5ab2
commit b0b25f9282
4 changed files with 271 additions and 94 deletions

View File

@ -14,6 +14,8 @@
#![feature(iter_from_generator)] #![feature(iter_from_generator)]
#![feature(const_mut_refs)] #![feature(const_mut_refs)]
#![feature(let_chains)] #![feature(let_chains)]
#![feature(ptr_sub_ptr)]
#![feature(const_ptr_sub_ptr)]
extern crate alloc; extern crate alloc;
#[macro_use] #[macro_use]

View File

@ -1,6 +1,7 @@
use alloc::vec; use alloc::vec;
use core::{ use core::{
iter::Iterator, iter::Iterator,
marker::PhantomData,
ops::{BitAnd, BitOr, Not}, ops::{BitAnd, BitOr, Not},
}; };
@ -9,8 +10,6 @@ use crate::{arch::iommu, config::PAGE_SIZE, prelude::*, Error};
use super::{frame_allocator, HasPaddr}; use super::{frame_allocator, HasPaddr};
use super::{Paddr, VmIo}; use super::{Paddr, VmIo};
use pod::Pod;
/// A collection of page frames (physical memory pages). /// A collection of page frames (physical memory pages).
/// ///
/// For the most parts, `VmFrameVec` is like `Vec<VmFrame>`. But the /// For the most parts, `VmFrameVec` is like `Vec<VmFrame>`. But the
@ -157,47 +156,37 @@ impl IntoIterator for VmFrameVec {
impl VmIo for VmFrameVec { impl VmIo for VmFrameVec {
fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> { fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
let mut start = offset; if buf.len() + offset > self.nbytes() {
let mut remain = buf.len(); return Err(Error::InvalidArgs);
let mut processed = 0; }
for pa in self.0.iter() {
if start >= PAGE_SIZE { let num_unread_pages = offset / PAGE_SIZE;
start -= PAGE_SIZE; let mut start = offset % PAGE_SIZE;
} else { let mut buf_writer: VmWriter = buf.into();
let copy_len = (PAGE_SIZE - start).min(remain); for frame in self.0.iter().skip(num_unread_pages) {
let src = &mut buf[processed..processed + copy_len]; let read_len = frame.reader().skip(start).read(&mut buf_writer);
let dst = unsafe { &pa.as_slice()[start..src.len() + start] }; if read_len == 0 {
src.copy_from_slice(dst);
processed += copy_len;
remain -= copy_len;
start = 0;
if remain == 0 {
break; break;
} }
} start = 0;
} }
Ok(()) Ok(())
} }
fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()> { fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()> {
let mut start = offset; if buf.len() + offset > self.nbytes() {
let mut remain = buf.len(); return Err(Error::InvalidArgs);
let mut processed = 0; }
for pa in self.0.iter() {
if start >= PAGE_SIZE { let num_unwrite_pages = offset / PAGE_SIZE;
start -= PAGE_SIZE; let mut start = offset % PAGE_SIZE;
} else { let mut buf_reader: VmReader = buf.into();
let copy_len = (PAGE_SIZE - start).min(remain); for frame in self.0.iter().skip(num_unwrite_pages) {
let src = &buf[processed..processed + copy_len]; let write_len = frame.writer().skip(start).write(&mut buf_reader);
let dst = unsafe { &mut pa.as_slice()[start..src.len() + start] }; if write_len == 0 {
dst.copy_from_slice(src);
processed += copy_len;
remain -= copy_len;
start = 0;
if remain == 0 {
break; break;
} }
} start = 0;
} }
Ok(()) Ok(())
} }
@ -364,17 +353,6 @@ impl VmFrame {
(*self.frame_index).bitand(VmFrameFlags::all().bits().not()) (*self.frame_index).bitand(VmFrameFlags::all().bits().not())
} }
// FIXME: need a sound reason for creating a mutable reference
// for getting the content of the frame.
#[allow(clippy::mut_from_ref)]
#[allow(clippy::missing_safety_doc)]
pub unsafe fn as_slice(&self) -> &mut [u8] {
core::slice::from_raw_parts_mut(
super::paddr_to_vaddr(self.start_paddr()) as *mut u8,
PAGE_SIZE,
)
}
pub fn as_ptr(&self) -> *const u8 { pub fn as_ptr(&self) -> *const u8 {
super::paddr_to_vaddr(self.start_paddr()) as *const u8 super::paddr_to_vaddr(self.start_paddr()) as *const u8
} }
@ -387,6 +365,7 @@ impl VmFrame {
if Arc::ptr_eq(&self.frame_index, &src.frame_index) { if Arc::ptr_eq(&self.frame_index, &src.frame_index) {
return; return;
} }
// Safety: src and dst is not overlapped. // Safety: src and dst is not overlapped.
unsafe { unsafe {
core::ptr::copy_nonoverlapping(src.as_ptr(), self.as_mut_ptr(), PAGE_SIZE); core::ptr::copy_nonoverlapping(src.as_ptr(), self.as_mut_ptr(), PAGE_SIZE);
@ -394,37 +373,36 @@ impl VmFrame {
} }
} }
impl<'a> VmFrame {
/// Returns a reader to read data from it.
pub fn reader(&'a self) -> VmReader<'a> {
// Safety: the memory of the page is contiguous and is valid during `'a`.
unsafe { VmReader::from_raw_parts(self.as_ptr(), PAGE_SIZE) }
}
/// Returns a writer to write data into it.
pub fn writer(&'a self) -> VmWriter<'a> {
// Safety: the memory of the page is contiguous and is valid during `'a`.
unsafe { VmWriter::from_raw_parts_mut(self.as_mut_ptr(), PAGE_SIZE) }
}
}
impl VmIo for VmFrame { impl VmIo for VmFrame {
fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> { fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
if offset >= PAGE_SIZE || buf.len() + offset > PAGE_SIZE { if buf.len() + offset > PAGE_SIZE {
Err(Error::InvalidArgs) return Err(Error::InvalidArgs);
} else {
let dst = unsafe { &self.as_slice()[offset..buf.len() + offset] };
buf.copy_from_slice(dst);
Ok(())
} }
let len = self.reader().skip(offset).read(&mut buf.into());
debug_assert!(len == buf.len());
Ok(())
} }
fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()> { fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()> {
if offset >= PAGE_SIZE || buf.len() + offset > PAGE_SIZE { if buf.len() + offset > PAGE_SIZE {
Err(Error::InvalidArgs) return Err(Error::InvalidArgs);
} else {
let dst = unsafe { &mut self.as_slice()[offset..buf.len() + offset] };
dst.copy_from_slice(buf);
Ok(())
} }
} let len = self.writer().skip(offset).write(&mut buf.into());
debug_assert!(len == buf.len());
/// Read a value of a specified type at a specified offset.
fn read_val<T: Pod>(&self, offset: usize) -> Result<T> {
let paddr = self.start_paddr() + offset;
Ok(unsafe { core::ptr::read(super::paddr_to_vaddr(paddr) as *const T) })
}
/// Write a value of a specified type at a specified offset.
fn write_val<T: Pod>(&self, offset: usize, new_val: &T) -> Result<()> {
let paddr = self.start_paddr() + offset;
unsafe { core::ptr::write(super::paddr_to_vaddr(paddr) as *mut T, *new_val) };
Ok(()) Ok(())
} }
} }
@ -443,9 +421,220 @@ impl Drop for VmFrame {
} }
} }
} }
// Safety: the frame index is valid.
unsafe { unsafe {
frame_allocator::dealloc(self.frame_index()); frame_allocator::dealloc(self.frame_index());
} }
} }
} }
} }
/// VmReader is a reader for reading data from a contiguous range of memory.
///
/// # Example
///
/// ```rust
/// impl VmIo for VmFrame {
/// fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
/// if buf.len() + offset > PAGE_SIZE {
/// return Err(Error::InvalidArgs);
/// }
/// let len = self.reader().skip(offset).read(&mut buf.into());
/// debug_assert!(len == buf.len());
/// Ok(())
/// }
/// }
/// ```
pub struct VmReader<'a> {
cursor: *const u8,
end: *const u8,
phantom: PhantomData<&'a [u8]>,
}
impl<'a> VmReader<'a> {
/// Constructs a VmReader from a pointer and a length.
///
/// # Safety
///
/// User 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`.
pub const unsafe fn from_raw_parts(ptr: *const u8, len: usize) -> Self {
Self {
cursor: ptr,
end: ptr.add(len),
phantom: PhantomData,
}
}
/// Returns the number of bytes for the remaining data.
pub const fn remain(&self) -> usize {
// Safety: the end is equal to or greater than the cursor.
unsafe { self.end.sub_ptr(self.cursor) }
}
/// Returns if it has remaining data to read.
pub const fn has_remain(&self) -> bool {
self.remain() > 0
}
/// Limits the length of remaining data.
///
/// This method ensures the postcondition of `self.remain() <= max_remain`.
pub const fn limit(&mut self, max_remain: usize) -> &mut Self {
if max_remain < self.remain() {
// Safety: the new end is less than the old end.
unsafe { self.end = self.cursor.add(max_remain) };
}
self
}
/// Skips the first `nbytes` bytes of data.
/// The length of remaining data is decreased accordingly.
///
/// # Panic
///
/// If `nbytes` is greater than `self.remain()`, then the method panics.
pub fn skip(&mut self, nbytes: usize) -> &mut Self {
assert!(nbytes <= self.remain());
// Safety: the new cursor is less than or equal to the end.
unsafe { self.cursor = self.cursor.add(nbytes) };
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
}
}
impl<'a> From<&'a [u8]> for VmReader<'a> {
fn from(slice: &'a [u8]) -> Self {
// Safety: the range of memory is contiguous and is valid during `'a`.
unsafe { Self::from_raw_parts(slice.as_ptr(), slice.len()) }
}
}
/// VmWriter is a writer for writing data to a contiguous range of memory.
///
/// # Example
///
/// ```rust
/// impl VmIo for VmFrame {
/// fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()> {
/// if buf.len() + offset > PAGE_SIZE {
/// return Err(Error::InvalidArgs);
/// }
/// let len = self.writer().skip(offset).write(&mut buf.into());
/// debug_assert!(len == buf.len());
/// Ok(())
/// }
/// }
/// ```
pub struct VmWriter<'a> {
cursor: *mut u8,
end: *mut u8,
phantom: PhantomData<&'a mut [u8]>,
}
impl<'a> VmWriter<'a> {
/// Constructs a VmWriter from a pointer and a length.
///
/// # Safety
///
/// User 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`.
pub const unsafe fn from_raw_parts_mut(ptr: *mut u8, len: usize) -> Self {
Self {
cursor: ptr,
end: ptr.add(len),
phantom: PhantomData,
}
}
/// 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 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) -> &mut 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) -> &mut 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.
///
/// It pulls the number of bytes data from the reader and
/// 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
// of the reader's remaining data and the writer's available space.
unsafe {
core::ptr::copy(reader.cursor, self.cursor, copy_len);
self.cursor = self.cursor.add(copy_len);
reader.cursor = reader.cursor.add(copy_len);
}
copy_len
}
}
impl<'a> From<&'a mut [u8]> for VmWriter<'a> {
fn from(slice: &'a mut [u8]) -> Self {
// Safety: the range of memory is contiguous and is valid during `'a`.
unsafe { Self::from_raw_parts_mut(slice.as_mut_ptr(), slice.len()) }
}
}

View File

@ -6,7 +6,7 @@ use crate::{
arch::mm::{PageTableEntry, PageTableFlags}, arch::mm::{PageTableEntry, PageTableFlags},
config::{PAGE_SIZE, PHYS_OFFSET}, config::{PAGE_SIZE, PHYS_OFFSET},
vm::is_page_aligned, vm::is_page_aligned,
vm::{VmFrame, VmFrameVec}, vm::{VmFrame, VmFrameVec, VmReader, VmWriter},
}; };
use crate::{prelude::*, Error}; use crate::{prelude::*, Error};
use alloc::collections::{btree_map::Entry, BTreeMap}; use alloc::collections::{btree_map::Entry, BTreeMap};
@ -33,9 +33,7 @@ impl Clone for MapArea {
let mut mapper = BTreeMap::new(); let mut mapper = BTreeMap::new();
for (&va, old) in &self.mapper { for (&va, old) in &self.mapper {
let new = frame_allocator::alloc(VmFrameFlags::empty()).unwrap(); let new = frame_allocator::alloc(VmFrameFlags::empty()).unwrap();
unsafe { new.copy_from_frame(old);
new.as_slice().copy_from_slice(old.as_slice());
}
mapper.insert(va, new.clone()); mapper.insert(va, new.clone());
} }
Self { Self {
@ -110,18 +108,12 @@ impl MapArea {
pub fn write_data(&mut self, addr: usize, data: &[u8]) { pub fn write_data(&mut self, addr: usize, data: &[u8]) {
let mut current_start_address = addr; let mut current_start_address = addr;
let mut remain = data.len(); let mut buf_reader: VmReader = data.into();
let mut processed = 0;
for (va, pa) in self.mapper.iter() { for (va, pa) in self.mapper.iter() {
if current_start_address >= *va && current_start_address < va + PAGE_SIZE { if current_start_address >= *va && current_start_address < va + PAGE_SIZE {
let offset = current_start_address - va; let offset = current_start_address - va;
let copy_len = (va + PAGE_SIZE - current_start_address).min(remain); let _ = pa.writer().skip(offset).write(&mut buf_reader);
let src = &data[processed..processed + copy_len]; if !buf_reader.has_remain() {
let dst = unsafe { &mut pa.as_slice()[offset..(offset + copy_len)] };
dst.copy_from_slice(src);
processed += copy_len;
remain -= copy_len;
if remain == 0 {
return; return;
} }
current_start_address = va + PAGE_SIZE; current_start_address = va + PAGE_SIZE;
@ -131,18 +123,12 @@ impl MapArea {
pub fn read_data(&self, addr: usize, data: &mut [u8]) { pub fn read_data(&self, addr: usize, data: &mut [u8]) {
let mut start = addr; let mut start = addr;
let mut remain = data.len(); let mut buf_writer: VmWriter = data.into();
let mut processed = 0;
for (va, pa) in self.mapper.iter() { for (va, pa) in self.mapper.iter() {
if start >= *va && start < va + PAGE_SIZE { if start >= *va && start < va + PAGE_SIZE {
let offset = start - va; let offset = start - va;
let copy_len = (va + PAGE_SIZE - start).min(remain); let _ = pa.reader().skip(offset).read(&mut buf_writer);
let src = &mut data[processed..processed + copy_len]; if !buf_writer.has_avail() {
let dst = unsafe { &pa.as_slice()[offset..(offset + copy_len)] };
src.copy_from_slice(dst);
processed += copy_len;
remain -= copy_len;
if remain == 0 {
return; return;
} }
start = va + PAGE_SIZE; start = va + PAGE_SIZE;

View File

@ -17,7 +17,7 @@ mod space;
use crate::config::{KERNEL_OFFSET, PAGE_SIZE, PHYS_OFFSET}; use crate::config::{KERNEL_OFFSET, PAGE_SIZE, PHYS_OFFSET};
pub use self::frame::{VmAllocOptions, VmFrame, VmFrameVec, VmFrameVecIter}; pub use self::frame::{VmAllocOptions, VmFrame, VmFrameVec, VmFrameVecIter, VmReader, VmWriter};
pub use self::io::VmIo; pub use self::io::VmIo;
pub use self::space::{VmMapOptions, VmPerm, VmSpace}; pub use self::space::{VmMapOptions, VmPerm, VmSpace};