diff --git a/src/scheme/memory.rs b/src/scheme/memory.rs index 7586000b80..211af45b69 100644 --- a/src/scheme/memory.rs +++ b/src/scheme/memory.rs @@ -1,17 +1,52 @@ use alloc::sync::Arc; +use rmm::PhysicalAddress; use spin::RwLock; +use syscall::MapFlags; -use crate::context; use crate::context::memory::{AddrSpace, Grant}; -use crate::memory::{free_frames, used_frames, PAGE_SIZE}; +use crate::memory::{free_frames, used_frames, PAGE_SIZE, Frame}; +use crate::paging::entry::EntryFlags; use crate::syscall::data::{Map, StatVfs}; use crate::syscall::error::*; use crate::syscall::scheme::Scheme; use crate::syscall::usercopy::UserSliceWo; +use super::KernelScheme; + pub struct MemoryScheme; +// TODO: Use crate that autogenerates conversion functions. +#[repr(u8)] +enum Handle { + Anonymous = 0, + + PhysicalWb = 1, + PhysicalUc = 2, + PhysicalWc = 3, + + // TODO: More/make arch-specific? +} +pub enum MemoryType { + Writeback, + Uncacheable, + WriteCombining, +} + +impl Handle { + fn from_raw(raw: usize) -> Option { + Some(match raw { + 0 => Self::Anonymous, + + 1 => Self::PhysicalWb, + 2 => Self::PhysicalUc, + 3 => Self::PhysicalWc, + + _ => return None, + }) + } +} + impl MemoryScheme { pub fn new() -> Self { MemoryScheme @@ -28,14 +63,68 @@ impl MemoryScheme { Ok(page.start_address().data()) } + pub fn physmap(physical_address: usize, size: usize, flags: MapFlags, memory_type: MemoryType) -> Result { + // TODO: Check physical_address against the real MAXPHYADDR. + let end = 1 << 52; + if (physical_address.saturating_add(size) as u64) > end || physical_address % PAGE_SIZE != 0 { + return Err(Error::new(EINVAL)); + } + // TODO: Check that the physical address is not owned by the frame allocator, although this + // requires replacing physalloc and physfree with e.g. MAP_PHYS_CONTIGUOUS. + + if size % PAGE_SIZE != 0 { + log::warn!("physmap size {} is not multiple of PAGE_SIZE {}", size, PAGE_SIZE); + return Err(Error::new(EINVAL)); + } + let page_count = size.div_ceil(PAGE_SIZE); + + AddrSpace::current()?.write().mmap(None, page_count, flags, |dst_page, mut page_flags, dst_mapper, dst_flusher| { + match memory_type { + // Default + MemoryType::Writeback => (), + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] // TODO: AARCH64 + MemoryType::WriteCombining => page_flags = page_flags.custom_flag(EntryFlags::HUGE_PAGE.bits(), true), + + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] // TODO: AARCH64 + MemoryType::Uncacheable => page_flags = page_flags.custom_flag(EntryFlags::NO_CACHE.bits(), true), + + #[cfg(target_arch = "aarch64")] + _ => (), + } + + Grant::physmap( + Frame::containing_address(PhysicalAddress::new(physical_address)), + dst_page, + page_count, + page_flags, + dst_mapper, + dst_flusher, + ) + }).map(|page| page.start_address().data()) + + } } impl Scheme for MemoryScheme { - fn open(&self, _path: &str, _flags: usize, _uid: u32, _gid: u32) -> Result { - Ok(0) + fn open(&self, path: &str, _flags: usize, uid: u32, _gid: u32) -> Result { + let intended_handle = match path.trim_start_matches('/') { + "" => Handle::Anonymous, + "physical" | "physical@wb" => Handle::PhysicalWb, + "physical@uc" => Handle::PhysicalUc, + "physical@wc" => Handle::PhysicalWc, + + _ => return Err(Error::new(ENOENT)), + }; + + if uid != 0 && !matches!(intended_handle, Handle::Anonymous) { + return Err(Error::new(EACCES)); + } + + Ok(intended_handle as usize) } - fn fmap(&self, _id: usize, map: &Map) -> Result { - Self::fmap_anonymous(&Arc::clone(context::current()?.read().addr_space()?), map) + fn fmap(&self, id: usize, map: &Map) -> Result { + self.kfmap(id, &AddrSpace::current()?, map, false) } fn fcntl(&self, _id: usize, _cmd: usize, _arg: usize) -> Result { @@ -46,16 +135,24 @@ impl Scheme for MemoryScheme { Ok(0) } } -impl crate::scheme::KernelScheme for MemoryScheme { - fn kfmap(&self, _number: usize, addr_space: &Arc>, map: &Map, _consume: bool) -> Result { - Self::fmap_anonymous(addr_space, map) +impl KernelScheme for MemoryScheme { + fn kfmap(&self, id: usize, addr_space: &Arc>, map: &Map, _consume: bool) -> Result { + match Handle::from_raw(id).ok_or(Error::new(EBADF))? { + Handle::Anonymous => Self::fmap_anonymous(addr_space, map), + Handle::PhysicalWb => Self::physmap(map.offset, map.size, map.flags, MemoryType::Writeback), + Handle::PhysicalUc => Self::physmap(map.offset, map.size, map.flags, MemoryType::Uncacheable), + Handle::PhysicalWc => Self::physmap(map.offset, map.size, map.flags, MemoryType::WriteCombining), + } } - fn kfpath(&self, _id: usize, dst: UserSliceWo) -> Result { + fn kfpath(&self, id: usize, dst: UserSliceWo) -> Result { // TODO: Copy scheme name elsewhere in the kernel? - const SRC: &[u8] = b"memory:"; - let byte_count = core::cmp::min(SRC.len(), dst.len()); - dst.limit(byte_count).ok_or(Error::new(EINVAL))?.copy_from_slice(SRC)?; - Ok(0) + let src = match Handle::from_raw(id).ok_or(Error::new(EBADF))? { + Handle::Anonymous => "memory:", + Handle::PhysicalWb => "memory:physical@wb", + Handle::PhysicalUc => "memory:physical@uc", + Handle::PhysicalWc => "memory:physical@wc", + }; + dst.copy_common_bytes_from_slice(src.as_bytes()) } fn kfstatvfs(&self, _file: usize, dst: UserSliceWo) -> Result { let used = used_frames() as u64; @@ -67,9 +164,8 @@ impl crate::scheme::KernelScheme for MemoryScheme { f_bfree: free, f_bavail: free, }; - dst.limit(core::mem::size_of::()).ok_or(Error::new(EINVAL))?.copy_from_slice(&stat)?; + dst.copy_exactly(&stat)?; Ok(0) } - } diff --git a/src/syscall/driver.rs b/src/syscall/driver.rs index 6a829f912c..cdc17b5212 100644 --- a/src/syscall/driver.rs +++ b/src/syscall/driver.rs @@ -1,13 +1,12 @@ use crate::interrupt::InterruptStack; use crate::memory::{allocate_frames_complex, deallocate_frames, Frame, PAGE_SIZE}; -use crate::paging::{PageFlags, PhysicalAddress, VirtualAddress}; +use crate::paging::{PhysicalAddress, VirtualAddress}; use crate::context; -use crate::context::memory::Grant; +use crate::scheme::memory::{MemoryScheme, MemoryType}; use crate::syscall::error::{Error, EFAULT, EINVAL, ENOMEM, EPERM, ESRCH, Result}; -use crate::syscall::flag::{PhysallocFlags, PartialAllocStrategy, PhysmapFlags, PHYSMAP_WRITE, PHYSMAP_WRITE_COMBINE, PHYSMAP_NO_CACHE}; +use crate::syscall::flag::{MapFlags, PhysallocFlags, PartialAllocStrategy, PhysmapFlags}; #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -use crate::paging::entry::EntryFlags; use alloc::sync::Arc; @@ -92,49 +91,20 @@ pub fn physfree(physical_address: usize, size: usize) -> Result { inner_physfree(physical_address, size) } -//TODO: verify exlusive access to physical memory pub fn inner_physmap(physical_address: usize, size: usize, flags: PhysmapFlags) -> Result { - // TODO: Check physical_address against MAXPHYADDR. + let mut map_flags = MapFlags::MAP_SHARED | MapFlags::PROT_READ; + map_flags.set(MapFlags::PROT_WRITE, flags.contains(PhysmapFlags::PHYSMAP_WRITE)); - let end = 1 << 52; - if (physical_address.saturating_add(size) as u64) > end || physical_address % PAGE_SIZE != 0 { - return Err(Error::new(EINVAL)); - } - - if size % PAGE_SIZE != 0 { - log::warn!("physmap size {} is not multiple of PAGE_SIZE {}", size, PAGE_SIZE); - } - let pages = size.div_ceil(PAGE_SIZE); - - let addr_space = Arc::clone(context::current()?.read().addr_space()?); - let mut addr_space = addr_space.write(); - - addr_space.mmap(None, pages, Default::default(), |dst_page, _, dst_mapper, dst_flusher| { - let mut page_flags = PageFlags::new().user(true); - if flags.contains(PHYSMAP_WRITE) { - page_flags = page_flags.write(true); - } - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] // TODO: AARCH64 - if flags.contains(PHYSMAP_WRITE_COMBINE) { - page_flags = page_flags.custom_flag(EntryFlags::HUGE_PAGE.bits(), true); - } - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] // TODO: AARCH64 - if flags.contains(PHYSMAP_NO_CACHE) { - page_flags = page_flags.custom_flag(EntryFlags::NO_CACHE.bits(), true); - } - Grant::physmap( - Frame::containing_address(PhysicalAddress::new(physical_address)), - dst_page, - pages, - page_flags, - dst_mapper, - dst_flusher, - ) - }).map(|page| page.start_address().data()) + let memory_type = if flags.contains(PhysmapFlags::PHYSMAP_NO_CACHE) { + MemoryType::Uncacheable + } else if flags.contains(PhysmapFlags::PHYSMAP_WRITE_COMBINE) { + MemoryType::WriteCombining + } else { + MemoryType::Writeback + }; + MemoryScheme::physmap(physical_address, size, map_flags, memory_type) } -// TODO: Replace physmap with e.g. fmap fd=`memory:physical@wc` or `memory:physical@uncacheable`, -// or represent memory types as fmap flags. pub fn physmap(physical_address: usize, size: usize, flags: PhysmapFlags) -> Result { enforce_root()?; inner_physmap(physical_address, size, flags) diff --git a/src/syscall/mod.rs b/src/syscall/mod.rs index 91597a677d..93675d4380 100644 --- a/src/syscall/mod.rs +++ b/src/syscall/mod.rs @@ -27,14 +27,13 @@ pub use self::process::*; pub use self::time::*; pub use self::usercopy::validate_region; -use self::scheme::Scheme as _; - use self::data::{Map, SigAction, TimeSpec}; use self::error::{Error, Result, ENOSYS}; use self::flag::{MapFlags, PhysmapFlags, WaitFlags}; use self::number::*; use crate::context::ContextId; +use crate::context::memory::AddrSpace; use crate::interrupt::InterruptStack; use crate::scheme::{FileHandle, SchemeNamespace, memory::MemoryScheme}; use crate::syscall::usercopy::UserSlice; @@ -78,7 +77,7 @@ pub fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, stack SYS_FMAP => { let map = unsafe { UserSlice::ro(c, d)?.read_exact::()? }; if b == !0 { - MemoryScheme.fmap(!0, &map) + MemoryScheme::fmap_anonymous(&AddrSpace::current()?, &map) } else { file_op_generic(fd, |scheme, _, number| scheme.fmap(number, &map)) }