use syscall::dirent::Buffer; use crate::{ context::memory::PageSpan, memory::PAGE_SIZE, paging::{Page, VirtualAddress}, }; use crate::arch::{arch_copy_from_user, arch_copy_to_user}; use crate::syscall::error::{Error, Result, EFAULT, EINVAL}; #[derive(Clone, Copy)] pub struct UserSlice { base: usize, len: usize, } pub type UserSliceRo = UserSlice; pub type UserSliceWo = UserSlice; pub type UserSliceRw = UserSlice; impl UserSlice { pub fn empty() -> Self { Self { base: 0, len: 0 } } pub fn len(&self) -> usize { self.len } pub fn is_empty(&self) -> bool { self.len == 0 } pub fn addr(&self) -> usize { self.base } pub fn new(base: usize, len: usize) -> Result { if base >= crate::USER_END_OFFSET || base.saturating_add(len) >= crate::USER_END_OFFSET { return Err(Error::new(EFAULT)); } Ok(Self { base, len }) } /// Split [0, end) into [0, idx) and [idx, end) pub fn split_at(self, idx: usize) -> Option<(Self, Self)> { if idx > self.len { return None; } Some(( Self { base: self.base, len: idx, }, Self { base: self.base + idx, len: self.len - idx, }, )) } pub fn advance(self, by: usize) -> Option { Some(self.split_at(by)?.1) } pub fn limit(self, to: usize) -> Option { Some(self.split_at(to)?.0) } pub fn none_if_null(self) -> Option { if self.addr() == 0 { None } else { Some(self) } } /// Not unsafe, because user memory is not covered by the memory model that decides if /// something is UB, but it can break logic invariants pub fn reinterpret_unchecked( self, ) -> UserSlice { UserSlice { base: self.base, len: self.len, } } pub fn in_variable_chunks(self, chunk_size: usize) -> impl Iterator { (0..self.len()).step_by(chunk_size).map(move |i| { self.advance(i) .expect("already limited by length, must succeed") }) } pub fn in_exact_chunks(self, chunk_size: usize) -> impl Iterator { (0..self.len().div_floor(chunk_size)).map(move |i| { self.advance(i * chunk_size) .expect("already limited by length, must succeed") .limit(chunk_size) .expect("length is aligned") }) } } impl UserSlice { pub fn copy_to_slice(self, slice: &mut [u8]) -> Result<()> { debug_assert!(is_kernel_mem(slice)); if self.len != slice.len() { return Err(Error::new(EINVAL)); } if unsafe { arch_copy_from_user(slice.as_mut_ptr() as usize, self.base, self.len) } == 0 { Ok(()) } else { Err(Error::new(EFAULT)) } } pub unsafe fn read_exact(self) -> Result { let mut t: T = unsafe { core::mem::zeroed() }; let slice = unsafe { core::slice::from_raw_parts_mut( (&mut t as *mut T).cast::(), core::mem::size_of::(), ) }; self.limit(core::mem::size_of::()) .ok_or(Error::new(EINVAL))? .copy_to_slice(slice)?; Ok(t) } pub fn copy_common_bytes_to_slice(self, slice: &mut [u8]) -> Result { let min = core::cmp::min(self.len(), slice.len()); self.limit(min) .expect("min(len, x) is always <= len") .copy_to_slice(&mut slice[..min])?; Ok(min) } // TODO: Merge int IO functions? pub fn read_usize(self) -> Result { let mut ret = 0_usize.to_ne_bytes(); self.limit(core::mem::size_of::()) .ok_or(Error::new(EINVAL))? .copy_to_slice(&mut ret)?; Ok(usize::from_ne_bytes(ret)) } pub fn read_u32(self) -> Result { let mut ret = 0_u32.to_ne_bytes(); self.limit(4) .ok_or(Error::new(EINVAL))? .copy_to_slice(&mut ret)?; Ok(u32::from_ne_bytes(ret)) } pub fn read_u64(self) -> Result { let mut ret = 0_u64.to_ne_bytes(); self.limit(8) .ok_or(Error::new(EINVAL))? .copy_to_slice(&mut ret)?; Ok(u64::from_ne_bytes(ret)) } pub fn usizes(self) -> impl Iterator> { self.in_exact_chunks(core::mem::size_of::()) .map(Self::read_usize) } } impl UserSlice { pub fn copy_from_slice(self, slice: &[u8]) -> Result<()> { // A zero sized slice will like have 0x1 as address debug_assert!(is_kernel_mem(slice) || slice.is_empty()); if self.len != slice.len() { return Err(Error::new(EINVAL)); } if unsafe { arch_copy_to_user(self.base, slice.as_ptr() as usize, self.len) } == 0 { Ok(()) } else { Err(Error::new(EFAULT)) } } pub fn copy_common_bytes_from_slice(self, slice: &[u8]) -> Result { let min = core::cmp::min(self.len(), slice.len()); self.limit(min) .expect("min(len, x) is always <= len") .copy_from_slice(&slice[..min])?; Ok(min) } pub fn copy_exactly(self, slice: &[u8]) -> Result<()> { self.limit(slice.len()) .ok_or(Error::new(EINVAL))? .copy_from_slice(slice)?; Ok(()) } pub fn write_usize(self, word: usize) -> Result<()> { self.limit(core::mem::size_of::()) .ok_or(Error::new(EINVAL))? .copy_from_slice(&word.to_ne_bytes())?; Ok(()) } pub fn write_u32(self, int: u32) -> Result<()> { self.limit(core::mem::size_of::()) .ok_or(Error::new(EINVAL))? .copy_from_slice(&int.to_ne_bytes())?; Ok(()) } } impl UserSliceRo { pub fn ro(base: usize, size: usize) -> Result { Self::new(base, size) } } impl UserSliceWo { pub fn wo(base: usize, size: usize) -> Result { Self::new(base, size) } } impl UserSliceRw { pub fn rw(base: usize, size: usize) -> Result { Self::new(base, size) } } fn is_kernel_mem(slice: &[u8]) -> bool { (slice.as_ptr() as usize) >= crate::USER_END_OFFSET && (slice.as_ptr() as usize).checked_add(slice.len()).is_some() } /// Convert `[addr, addr+size)` into `(page, page_count)`. /// /// This will fail if: /// /// - the base address is not page-aligned, /// - the length is not page-aligned, /// - the region is empty (EINVAL), or /// - any byte in the region exceeds USER_END_OFFSET (EFAULT). pub fn validate_region(address: usize, size: usize) -> Result { if address % PAGE_SIZE != 0 || size % PAGE_SIZE != 0 || size == 0 { return Err(Error::new(EINVAL)); } if address.saturating_add(size) > crate::USER_END_OFFSET { return Err(Error::new(EFAULT)); } Ok(PageSpan::new( Page::containing_address(VirtualAddress::new(address)), size / PAGE_SIZE, )) } impl Buffer<'static> for UserSliceWo { fn empty() -> Self { UserSliceWo::empty() } fn length(&self) -> usize { self.len() } fn split_at(self, index: usize) -> Option<[Self; 2]> { let (a, b) = self.split_at(index)?; Some([a, b]) } fn copy_from_slice_exact(self, src: &[u8]) -> Result<()> { self.copy_exactly(src) } fn zero_out(self) -> Result<()> { // TODO: Implement this. Don't need to as long as the header size is constant, for now. Ok(()) } }