use alloc::vec;
use core::marker::PhantomData;
use align_ext::AlignExt;
use const_assert::{Assert, IsTrue};
use inherit_methods_macro::inherit_methods;
use crate::{
arch::mm::{__memcpy_fallible, __memset_fallible},
mm::{
kspace::{KERNEL_BASE_VADDR, KERNEL_END_VADDR},
MAX_USERSPACE_VADDR,
},
prelude::*,
Error, Pod,
};
pub trait VmIo: Send + Sync {
fn read(&self, offset: usize, writer: &mut VmWriter) -> Result<()>;
fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()> {
let mut writer = VmWriter::from(buf).to_fallible();
self.read(offset, &mut writer)
}
fn read_val<T: Pod>(&self, offset: usize) -> Result<T> {
let mut val = T::new_uninit();
self.read_bytes(offset, val.as_bytes_mut())?;
Ok(val)
}
fn read_slice<T: Pod>(&self, offset: usize, slice: &mut [T]) -> Result<()> {
let len_in_bytes = core::mem::size_of_val(slice);
let ptr = slice as *mut [T] as *mut u8;
let buf = unsafe { core::slice::from_raw_parts_mut(ptr, len_in_bytes) };
self.read_bytes(offset, buf)
}
fn write(&self, offset: usize, reader: &mut VmReader) -> Result<()>;
fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()> {
let mut reader = VmReader::from(buf).to_fallible();
self.write(offset, &mut reader)
}
fn write_val<T: Pod>(&self, offset: usize, new_val: &T) -> Result<()> {
self.write_bytes(offset, new_val.as_bytes())?;
Ok(())
}
fn write_slice<T: Pod>(&self, offset: usize, slice: &[T]) -> Result<()> {
let len_in_bytes = core::mem::size_of_val(slice);
let ptr = slice as *const [T] as *const u8;
let buf = unsafe { core::slice::from_raw_parts(ptr, len_in_bytes) };
self.write_bytes(offset, buf)
}
fn write_vals<'a, T: Pod + 'a, I: Iterator<Item = &'a T>>(
&self,
offset: usize,
iter: I,
align: usize,
) -> Result<usize> {
let mut nr_written = 0;
let (mut offset, item_size) = if (align >> 1) == 0 {
(offset, core::mem::size_of::<T>())
} else {
(
offset.align_up(align),
core::mem::size_of::<T>().align_up(align),
)
};
for item in iter {
match self.write_val(offset, item) {
Ok(_) => {
offset += item_size;
nr_written += 1;
}
Err(e) => {
if nr_written > 0 {
return Ok(nr_written);
}
return Err(e);
}
}
}
Ok(nr_written)
}
}
pub trait VmIoOnce {
fn read_once<T: PodOnce>(&self, offset: usize) -> Result<T>;
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 {
fn read(&self, offset: usize, writer: &mut VmWriter) -> Result<()>;
fn read_bytes(&self, offset: usize, buf: &mut [u8]) -> Result<()>;
fn read_val<F: Pod>(&self, offset: usize) -> Result<F>;
fn read_slice<F: Pod>(&self, offset: usize, slice: &mut [F]) -> Result<()>;
fn write(&self, offset: usize, reader: &mut VmReader) -> Result<()>;
fn write_bytes(&self, offset: usize, buf: &[u8]) -> Result<()>;
fn write_val<F: Pod>(&self, offset: usize, new_val: &F) -> Result<()>;
fn write_slice<F: Pod>(&self, offset: usize, slice: &[F]) -> Result<()>;
}
};
}
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)");
pub struct Fallible;
pub struct Infallible;
unsafe fn memcpy(dst: *mut u8, src: *const u8, len: usize) {
core::intrinsics::volatile_copy_memory(dst, src, len);
}
unsafe fn memcpy_fallible(dst: *mut u8, src: *const u8, len: usize) -> usize {
let failed_bytes = __memcpy_fallible(dst, src, len);
len - failed_bytes
}
unsafe fn memset_fallible(dst: *mut u8, value: u8, len: usize) -> usize {
let failed_bytes = __memset_fallible(dst, value, len);
len - failed_bytes
}
pub trait FallibleVmRead<F> {
fn read_fallible(
&mut self,
writer: &mut VmWriter<'_, F>,
) -> core::result::Result<usize, (Error, usize)>;
}
pub trait FallibleVmWrite<F> {
fn write_fallible(
&mut self,
reader: &mut VmReader<'_, F>,
) -> core::result::Result<usize, (Error, usize)>;
}
pub struct VmReader<'a, Fallibility = Fallible> {
cursor: *const u8,
end: *const u8,
phantom: PhantomData<(&'a [u8], Fallibility)>,
}
macro_rules! impl_read_fallible {
($reader_fallibility:ty, $writer_fallibility:ty) => {
impl<'a> FallibleVmRead<$writer_fallibility> for VmReader<'a, $reader_fallibility> {
fn read_fallible(
&mut self,
writer: &mut VmWriter<'_, $writer_fallibility>,
) -> core::result::Result<usize, (Error, usize)> {
let copy_len = self.remain().min(writer.avail());
if copy_len == 0 {
return Ok(0);
}
let copied_len = unsafe {
let copied_len = memcpy_fallible(writer.cursor, self.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 {
($writer_fallibility:ty, $reader_fallibility:ty) => {
impl<'a> FallibleVmWrite<$reader_fallibility> for VmWriter<'a, $writer_fallibility> {
fn write_fallible(
&mut self,
reader: &mut VmReader<'_, $reader_fallibility>,
) -> core::result::Result<usize, (Error, usize)> {
reader.read_fallible(self)
}
}
};
}
impl_read_fallible!(Fallible, Infallible);
impl_read_fallible!(Fallible, Fallible);
impl_read_fallible!(Infallible, Fallible);
impl_write_fallible!(Fallible, Infallible);
impl_write_fallible!(Fallible, Fallible);
impl_write_fallible!(Infallible, Fallible);
impl<'a> VmReader<'a, Infallible> {
pub unsafe fn from_kernel_space(ptr: *const u8, len: usize) -> Self {
debug_assert!(len == 0 || KERNEL_BASE_VADDR <= ptr as usize);
debug_assert!(len == 0 || ptr.add(len) as usize <= KERNEL_END_VADDR);
Self {
cursor: ptr,
end: ptr.add(len),
phantom: PhantomData,
}
}
pub fn read(&mut self, writer: &mut VmWriter<'_, Infallible>) -> usize {
let copy_len = self.remain().min(writer.avail());
if copy_len == 0 {
return 0;
}
unsafe {
memcpy(writer.cursor, self.cursor, copy_len);
self.cursor = self.cursor.add(copy_len);
writer.cursor = writer.cursor.add(copy_len);
}
copy_len
}
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)
}
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());
let val = unsafe { cursor.read_volatile() };
self.cursor = unsafe { self.cursor.add(core::mem::size_of::<T>()) };
Ok(val)
}
pub fn to_fallible(self) -> VmReader<'a, Fallible> {
unsafe { core::mem::transmute(self) }
}
}
impl<'a> VmReader<'a, Fallible> {
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,
}
}
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_err(|(err, copied_len)| {
unsafe {
self.cursor = self.cursor.sub(copied_len);
}
err
})?;
Ok(val)
}
pub fn collect(&mut self) -> Result<Vec<u8>> {
let mut buf = vec![0u8; self.remain()];
self.read_fallible(&mut buf.as_mut_slice().into())
.map_err(|(err, copied_len)| {
unsafe {
self.cursor = self.cursor.sub(copied_len);
}
err
})?;
Ok(buf)
}
}
impl<'a, Fallibility> VmReader<'a, Fallibility> {
pub const fn remain(&self) -> usize {
unsafe { self.end.sub_ptr(self.cursor) }
}
pub const fn cursor(&self) -> *const u8 {
self.cursor
}
pub const fn has_remain(&self) -> bool {
self.remain() > 0
}
pub const fn limit(mut self, max_remain: usize) -> Self {
if max_remain < self.remain() {
unsafe { self.end = self.cursor.add(max_remain) };
}
self
}
pub fn skip(mut self, nbytes: usize) -> Self {
assert!(nbytes <= self.remain());
unsafe { self.cursor = self.cursor.add(nbytes) };
self
}
}
impl<'a> From<&'a [u8]> for VmReader<'a, Infallible> {
fn from(slice: &'a [u8]) -> Self {
unsafe { Self::from_kernel_space(slice.as_ptr(), slice.len()) }
}
}
pub struct VmWriter<'a, Fallibility = Fallible> {
cursor: *mut u8,
end: *mut u8,
phantom: PhantomData<(&'a mut [u8], Fallibility)>,
}
impl<'a> VmWriter<'a, Infallible> {
pub unsafe fn from_kernel_space(ptr: *mut u8, len: usize) -> Self {
debug_assert!(len == 0 || KERNEL_BASE_VADDR <= ptr as usize);
debug_assert!(len == 0 || ptr.add(len) as usize <= KERNEL_END_VADDR);
Self {
cursor: ptr,
end: ptr.add(len),
phantom: PhantomData,
}
}
pub fn write(&mut self, reader: &mut VmReader<'_, Infallible>) -> usize {
reader.read(self)
}
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(&mut reader);
Ok(())
}
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());
unsafe { cursor.cast::<T>().write_volatile(*new_val) };
self.cursor = unsafe { self.cursor.add(core::mem::size_of::<T>()) };
Ok(())
}
pub fn fill<T: Pod>(&mut self, value: T) -> usize {
let avail = self.avail();
assert!((self.cursor as *mut T).is_aligned());
assert!(avail % core::mem::size_of::<T>() == 0);
let written_num = avail / core::mem::size_of::<T>();
for i in 0..written_num {
unsafe {
(self.cursor as *mut T).add(i).write_volatile(value);
}
}
self.cursor = self.end;
written_num
}
pub fn to_fallible(self) -> VmWriter<'a, Fallible> {
unsafe { core::mem::transmute(self) }
}
}
impl<'a> VmWriter<'a, Fallible> {
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,
}
}
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, copied_len)| {
unsafe {
self.cursor = self.cursor.sub(copied_len);
}
err
})?;
Ok(())
}
pub fn fill_zeros(&mut self, len: usize) -> core::result::Result<usize, (Error, usize)> {
let len_to_set = self.avail().min(len);
if len_to_set == 0 {
return Ok(0);
}
let set_len = unsafe {
let set_len = memset_fallible(self.cursor, 0u8, len_to_set);
self.cursor = self.cursor.add(set_len);
set_len
};
if set_len < len_to_set {
Err((Error::PageFault, set_len))
} else {
Ok(len_to_set)
}
}
}
impl<'a, Fallibility> VmWriter<'a, Fallibility> {
pub const fn avail(&self) -> usize {
unsafe { self.end.sub_ptr(self.cursor) }
}
pub const fn cursor(&self) -> *mut u8 {
self.cursor
}
pub const fn has_avail(&self) -> bool {
self.avail() > 0
}
pub const fn limit(mut self, max_avail: usize) -> Self {
if max_avail < self.avail() {
unsafe { self.end = self.cursor.add(max_avail) };
}
self
}
pub fn skip(mut self, nbytes: usize) -> Self {
assert!(nbytes <= self.avail());
unsafe { self.cursor = self.cursor.add(nbytes) };
self
}
}
impl<'a> From<&'a mut [u8]> for VmWriter<'a, Infallible> {
fn from(slice: &'a mut [u8]) -> Self {
unsafe { Self::from_kernel_space(slice.as_mut_ptr(), slice.len()) }
}
}
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
}
#[cfg(target_arch = "riscv64")]
const fn is_pod_once<T: Pod>() -> bool {
let size = size_of::<T>();
size == 1 || size == 2 || size == 4 || size == 8
}