Refactor the read/write operations to userspace

This commit is contained in:
Chen Chengjun
2024-06-28 09:39:51 +08:00
committed by Tate, Hongliang Tian
parent 09b6153d29
commit af908c29cf
7 changed files with 234 additions and 47 deletions

View File

@ -17,7 +17,7 @@ pub(crate) use bitflags::bitflags;
pub(crate) use int_to_c_enum::TryFromInt;
pub(crate) use log::{debug, error, info, log_enabled, trace, warn};
pub(crate) use ostd::{
mm::{Vaddr, PAGE_SIZE},
mm::{Vaddr, VmReader, VmWriter, PAGE_SIZE},
sync::{Mutex, MutexGuard, RwLock, RwMutex, SpinLock, SpinLockGuard},
};
pub(crate) use pod::Pod;

View File

@ -2,7 +2,7 @@
#![allow(unused_variables)]
use ostd::cpu::*;
use ostd::{cpu::*, mm::VmSpace};
use crate::{
prelude::*, process::signal::signals::fault::FaultSignal,
@ -18,7 +18,11 @@ pub fn handle_exception(context: &UserContext) {
let root_vmar = current.root_vmar();
match *exception {
PAGE_FAULT => handle_page_fault(trap_info),
PAGE_FAULT => {
if handle_page_fault(root_vmar.vm_space(), trap_info).is_err() {
generate_fault_signal(trap_info);
}
}
_ => {
// We current do nothing about other exceptions
generate_fault_signal(trap_info);
@ -26,7 +30,11 @@ pub fn handle_exception(context: &UserContext) {
}
}
fn handle_page_fault(trap_info: &CpuExceptionInfo) {
/// Handles the page fault occurs in the input `VmSpace`.
pub(crate) fn handle_page_fault(
vm_space: &VmSpace,
trap_info: &CpuExceptionInfo,
) -> core::result::Result<(), ()> {
const PAGE_NOT_PRESENT_ERROR_MASK: usize = 0x1 << 0;
const WRITE_ACCESS_MASK: usize = 0x1 << 1;
let page_fault_addr = trap_info.page_fault_addr as Vaddr;
@ -41,16 +49,23 @@ fn handle_page_fault(trap_info: &CpuExceptionInfo) {
// If page is not present or due to write access, we should ask the vmar try to commit this page
let current = current!();
let root_vmar = current.root_vmar();
debug_assert_eq!(
Arc::as_ptr(root_vmar.vm_space()),
vm_space as *const VmSpace
);
if let Err(e) = root_vmar.handle_page_fault(page_fault_addr, not_present, write) {
error!(
"page fault handler failed: addr: 0x{:x}, err: {:?}",
page_fault_addr, e
);
generate_fault_signal(trap_info);
return Err(());
}
Ok(())
} else {
// Otherwise, the page fault cannot be handled
generate_fault_signal(trap_info);
Err(())
}
}

View File

@ -3,7 +3,10 @@
use core::mem;
use aster_rights::Full;
use ostd::mm::VmIo;
use ostd::{
mm::{KernelSpace, VmIo, VmReader, VmWriter},
task::current_task,
};
use crate::{prelude::*, vm::vmar::Vmar};
mod iovec;
@ -12,38 +15,87 @@ pub mod random;
pub use iovec::{copy_iovs_from_user, IoVec};
/// Read bytes into the `dest` buffer
/// Reads bytes into the `dest` `VmWriter`
/// from the user space of the current process.
/// If successful,
/// the `dest` buffer is filled with exact `dest.len` bytes.
pub fn read_bytes_from_user(src: Vaddr, dest: &mut [u8]) -> Result<()> {
let current = current!();
let root_vmar = current.root_vmar();
Ok(root_vmar.read_bytes(src, dest)?)
///
/// If the reading is completely successful, returns `Ok`.
/// Otherwise, returns `Err`.
///
/// TODO: this API can be discarded and replaced with the API of `VmReader`
/// after replacing all related `buf` usages.
pub fn read_bytes_from_user(src: Vaddr, dest: &mut VmWriter<'_>) -> Result<()> {
let current_task = current_task().ok_or(Error::with_message(
Errno::EFAULT,
"the current task is missing",
))?;
let user_space = current_task.user_space().ok_or(Error::with_message(
Errno::EFAULT,
"the user space is missing",
))?;
let copy_len = dest.avail();
let mut user_reader = user_space.vm_space().reader(src, copy_len)?;
user_reader.read_fallible(dest).map_err(|err| err.0)?;
Ok(())
}
/// Read a value of `Pod` type
/// Reads a value of `Pod` type
/// from the user space of the current process.
pub fn read_val_from_user<T: Pod>(src: Vaddr) -> Result<T> {
let current = current!();
let root_vmar = current.root_vmar();
Ok(root_vmar.read_val(src)?)
let current_task = current_task().ok_or(Error::with_message(
Errno::EFAULT,
"the current task is missing",
))?;
let user_space = current_task.user_space().ok_or(Error::with_message(
Errno::EFAULT,
"the user space is missing",
))?;
let mut user_reader = user_space
.vm_space()
.reader(src, core::mem::size_of::<T>())?;
Ok(user_reader.read_val()?)
}
/// Write bytes from the `src` buffer
/// to the user space of the current process. If successful,
/// the write length will be equal to `src.len`.
pub fn write_bytes_to_user(dest: Vaddr, src: &[u8]) -> Result<()> {
let current = current!();
let root_vmar = current.root_vmar();
Ok(root_vmar.write_bytes(dest, src)?)
/// Writes bytes from the `src` `VmReader`
/// to the user space of the current process.
///
/// If the writing is completely successful, returns `Ok`,
/// Otherwise, returns `Err`.
///
/// TODO: this API can be discarded and replaced with the API of `VmWriter`
/// after replacing all related `buf` usages.
pub fn write_bytes_to_user(dest: Vaddr, src: &mut VmReader<'_, KernelSpace>) -> Result<()> {
let current_task = current_task().ok_or(Error::with_message(
Errno::EFAULT,
"the current task is missing",
))?;
let user_space = current_task.user_space().ok_or(Error::with_message(
Errno::EFAULT,
"the user space is missing",
))?;
let copy_len = src.remain();
let mut user_writer = user_space.vm_space().writer(dest, copy_len)?;
user_writer.write_fallible(src).map_err(|err| err.0)?;
Ok(())
}
/// Write `val` to the user space of the current process.
/// Writes `val` to the user space of the current process.
pub fn write_val_to_user<T: Pod>(dest: Vaddr, val: &T) -> Result<()> {
let current = current!();
let root_vmar = current.root_vmar();
Ok(root_vmar.write_val(dest, val)?)
let current_task = current_task().ok_or(Error::with_message(
Errno::EFAULT,
"the current task is missing",
))?;
let user_space = current_task.user_space().ok_or(Error::with_message(
Errno::EFAULT,
"the user space is missing",
))?;
let mut user_writer = user_space
.vm_space()
.writer(dest, core::mem::size_of::<T>())?;
Ok(user_writer.write_val(val)?)
}
/// Read a C string from the user space of the current process.

View File

@ -21,7 +21,7 @@ use self::{
vm_mapping::VmMapping,
};
use super::page_fault_handler::PageFaultHandler;
use crate::{prelude::*, vm::perms::VmPerms};
use crate::{prelude::*, thread::exception::handle_page_fault, vm::perms::VmPerms};
/// Virtual Memory Address Regions (VMARs) are a type of capability that manages
/// user address spaces.
@ -165,13 +165,9 @@ impl Vmar_ {
vm_mappings: BTreeMap::new(),
free_regions,
};
Vmar_::new(
vmar_inner,
Arc::new(VmSpace::new()),
0,
ROOT_VMAR_CAP_ADDR,
None,
)
let vm_space = VmSpace::new();
vm_space.register_page_fault_handler(handle_page_fault);
Vmar_::new(vmar_inner, Arc::new(vm_space), 0, ROOT_VMAR_CAP_ADDR, None)
}
fn is_root_vmar(&self) -> bool {

View File

@ -752,6 +752,10 @@ impl<R1, R2> VmarMapOptions<R1, R2> {
if self.align % PAGE_SIZE != 0 || !self.align.is_power_of_two() {
return_errno_with_message!(Errno::EINVAL, "invalid align");
}
debug_assert!(self.size % self.align == 0);
if self.size % self.align != 0 {
return_errno_with_message!(Errno::EINVAL, "invalid mapping size");
}
debug_assert!(self.vmo_offset % self.align == 0);
if self.vmo_offset % self.align != 0 {
return_errno_with_message!(Errno::EINVAL, "invalid vmo offset");