mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-08 21:06:48 +00:00
Introduce {read,write}_once
This commit is contained in:
parent
79578c9aa0
commit
cda23937dd
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -398,6 +398,12 @@ dependencies = [
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-assert"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8298db53081b3a951cadb6e0f4ebbe36def7bcb591a34676708d0d7ac87dd86"
|
||||
|
||||
[[package]]
|
||||
name = "controlled"
|
||||
version = "0.1.0"
|
||||
@ -1070,6 +1076,7 @@ dependencies = [
|
||||
"bitvec",
|
||||
"buddy_system_allocator",
|
||||
"cfg-if",
|
||||
"const-assert",
|
||||
"gimli 0.28.0",
|
||||
"iced-x86",
|
||||
"id-alloc",
|
||||
|
@ -22,6 +22,7 @@ buddy_system_allocator = "0.9.0"
|
||||
bitflags = "1.3"
|
||||
bitvec = { version = "1.0", default-features = false, features = ["alloc"] }
|
||||
cfg-if = "1.0"
|
||||
const-assert = "1.0"
|
||||
gimli = { version = "0.28", default-features = false, features = ["read-core"] }
|
||||
id-alloc = { path = "libs/id-alloc", version = "0.1.0" }
|
||||
inherit-methods-macro = { git = "https://github.com/asterinas/inherit-methods-macro", rev = "98f7e3e", version = "0.1.0" }
|
||||
|
@ -5,6 +5,7 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use align_ext::AlignExt;
|
||||
use const_assert::{Assert, IsTrue};
|
||||
use inherit_methods_macro::inherit_methods;
|
||||
|
||||
use crate::{
|
||||
@ -155,7 +156,28 @@ pub trait VmIo: Send + Sync {
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_vmio_pointer {
|
||||
/// A trait that enables reading/writing data from/to a VM object using one non-tearing memory
|
||||
/// load/store.
|
||||
///
|
||||
/// See also [`VmIo`], which enables reading/writing data from/to a VM object without the guarantee
|
||||
/// of using one non-tearing memory load/store.
|
||||
pub trait VmIoOnce {
|
||||
/// Reads a value of the `PodOnce` type at the specified offset using one non-tearing memory
|
||||
/// load.
|
||||
///
|
||||
/// Except that the offset is specified explicitly, the semantics of this method is the same as
|
||||
/// [`VmReader::read_once`].
|
||||
fn read_once<T: PodOnce>(&self, offset: usize) -> Result<T>;
|
||||
|
||||
/// Writes a value of the `PodOnce` type at the specified offset using one non-tearing memory
|
||||
/// store.
|
||||
///
|
||||
/// Except that the offset is specified explicitly, the semantics of this method is the same as
|
||||
/// [`VmWriter::write_once`].
|
||||
fn write_once<T: PodOnce>(&self, offset: usize, new_val: &T) -> Result<()>;
|
||||
}
|
||||
|
||||
macro_rules! impl_vm_io_pointer {
|
||||
($typ:ty,$from:tt) => {
|
||||
#[inherit_methods(from = $from)]
|
||||
impl<T: VmIo> VmIo for $typ {
|
||||
@ -169,10 +191,25 @@ macro_rules! impl_vmio_pointer {
|
||||
};
|
||||
}
|
||||
|
||||
impl_vmio_pointer!(&T, "(**self)");
|
||||
impl_vmio_pointer!(&mut T, "(**self)");
|
||||
impl_vmio_pointer!(Box<T>, "(**self)");
|
||||
impl_vmio_pointer!(Arc<T>, "(**self)");
|
||||
impl_vm_io_pointer!(&T, "(**self)");
|
||||
impl_vm_io_pointer!(&mut T, "(**self)");
|
||||
impl_vm_io_pointer!(Box<T>, "(**self)");
|
||||
impl_vm_io_pointer!(Arc<T>, "(**self)");
|
||||
|
||||
macro_rules! impl_vm_io_once_pointer {
|
||||
($typ:ty,$from:tt) => {
|
||||
#[inherit_methods(from = $from)]
|
||||
impl<T: VmIoOnce> VmIoOnce for $typ {
|
||||
fn read_once<F: PodOnce>(&self, offset: usize) -> Result<F>;
|
||||
fn write_once<F: PodOnce>(&self, offset: usize, new_val: &F) -> Result<()>;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_vm_io_once_pointer!(&T, "(**self)");
|
||||
impl_vm_io_once_pointer!(&mut T, "(**self)");
|
||||
impl_vm_io_once_pointer!(Box<T>, "(**self)");
|
||||
impl_vm_io_once_pointer!(Arc<T>, "(**self)");
|
||||
|
||||
/// A marker structure used for [`VmReader`] and [`VmWriter`],
|
||||
/// representing their operated memory scope is in user space.
|
||||
@ -391,6 +428,34 @@ impl<'a> VmReader<'a, KernelSpace> {
|
||||
self.read(&mut writer);
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
/// Reads a value of the `PodOnce` type using one non-tearing memory load.
|
||||
///
|
||||
/// If the length of the `PodOnce` type exceeds `self.remain()`, this method will return `Err`.
|
||||
///
|
||||
/// This method will not compile if the `Pod` type is too large for the current architecture
|
||||
/// and the operation must be tear into multiple memory loads.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method will panic if the current position of the reader does not meet the alignment
|
||||
/// requirements of type `T`.
|
||||
pub fn read_once<T: PodOnce>(&mut self) -> Result<T> {
|
||||
if self.remain() < core::mem::size_of::<T>() {
|
||||
return Err(Error::InvalidArgs);
|
||||
}
|
||||
|
||||
let cursor = self.cursor.cast::<T>();
|
||||
assert!(cursor.is_aligned());
|
||||
|
||||
// SAFETY: We have checked that the number of bytes remaining is at least the size of `T`
|
||||
// and that the cursor is properly aligned with respect to the type `T`. All other safety
|
||||
// requirements are the same as for `Self::read`.
|
||||
let val = unsafe { cursor.read_volatile() };
|
||||
self.cursor = unsafe { self.cursor.add(core::mem::size_of::<T>()) };
|
||||
|
||||
Ok(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> VmReader<'a, UserSpace> {
|
||||
@ -553,6 +618,31 @@ impl<'a> VmWriter<'a, KernelSpace> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes a value of the `PodOnce` type using one non-tearing memory store.
|
||||
///
|
||||
/// If the length of the `PodOnce` type exceeds `self.remain()`, this method will return `Err`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method will panic if the current position of the writer does not meet the alignment
|
||||
/// requirements of type `T`.
|
||||
pub fn write_once<T: PodOnce>(&mut self, new_val: &T) -> Result<()> {
|
||||
if self.avail() < core::mem::size_of::<T>() {
|
||||
return Err(Error::InvalidArgs);
|
||||
}
|
||||
|
||||
let cursor = self.cursor.cast::<T>();
|
||||
assert!(cursor.is_aligned());
|
||||
|
||||
// SAFETY: We have checked that the number of bytes remaining is at least the size of `T`
|
||||
// and that the cursor is properly aligned with respect to the type `T`. All other safety
|
||||
// requirements are the same as for `Self::writer`.
|
||||
unsafe { cursor.cast::<T>().write_volatile(*new_val) };
|
||||
self.cursor = unsafe { self.cursor.add(core::mem::size_of::<T>()) };
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Fills the available space by repeating `value`.
|
||||
///
|
||||
/// Returns the number of values written.
|
||||
@ -672,3 +762,23 @@ impl<'a> From<&'a mut [u8]> for VmWriter<'a> {
|
||||
unsafe { Self::from_kernel_space(slice.as_mut_ptr(), slice.len()) }
|
||||
}
|
||||
}
|
||||
|
||||
/// A marker trait for POD types that can be read or written with one instruction.
|
||||
///
|
||||
/// We currently rely on this trait to ensure that the memory operation created by
|
||||
/// `ptr::read_volatile` and `ptr::write_volatile` doesn't tear. However, the Rust documentation
|
||||
/// makes no such guarantee, and even the wording in the LLVM LangRef is ambiguous.
|
||||
///
|
||||
/// At this point, we can only _hope_ that this doesn't break in future versions of the Rust or
|
||||
/// LLVM compilers. However, this is unlikely to happen in practice, since the Linux kernel also
|
||||
/// uses "volatile" semantics to implement `READ_ONCE`/`WRITE_ONCE`.
|
||||
pub trait PodOnce: Pod {}
|
||||
|
||||
impl<T: Pod> PodOnce for T where Assert<{ is_pod_once::<T>() }>: IsTrue {}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
const fn is_pod_once<T: Pod>() -> bool {
|
||||
let size = size_of::<T>();
|
||||
|
||||
size == 1 || size == 2 || size == 4 || size == 8
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ use spin::Once;
|
||||
pub use self::{
|
||||
dma::{Daddr, DmaCoherent, DmaDirection, DmaStream, DmaStreamSlice, HasDaddr},
|
||||
frame::{options::FrameAllocOptions, Frame, Segment},
|
||||
io::{KernelSpace, UserSpace, VmIo, VmReader, VmWriter},
|
||||
io::{KernelSpace, PodOnce, UserSpace, VmIo, VmIoOnce, VmReader, VmWriter},
|
||||
page_prop::{CachePolicy, PageFlags, PageProperty},
|
||||
vm_space::VmSpace,
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user