Support physmap via memory:physical@<mem type>.

This commit is contained in:
4lDO2
2023-07-15 00:28:56 +02:00
parent 5a9a38a948
commit 37eb577ada
3 changed files with 127 additions and 62 deletions
+112 -16
View File
@@ -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<Self> {
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<usize> {
// 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<usize> {
Ok(0)
fn open(&self, path: &str, _flags: usize, uid: u32, _gid: u32) -> Result<usize> {
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<usize> {
Self::fmap_anonymous(&Arc::clone(context::current()?.read().addr_space()?), map)
fn fmap(&self, id: usize, map: &Map) -> Result<usize> {
self.kfmap(id, &AddrSpace::current()?, map, false)
}
fn fcntl(&self, _id: usize, _cmd: usize, _arg: usize) -> Result<usize> {
@@ -46,16 +135,24 @@ impl Scheme for MemoryScheme {
Ok(0)
}
}
impl crate::scheme::KernelScheme for MemoryScheme {
fn kfmap(&self, _number: usize, addr_space: &Arc<RwLock<AddrSpace>>, map: &Map, _consume: bool) -> Result<usize> {
Self::fmap_anonymous(addr_space, map)
impl KernelScheme for MemoryScheme {
fn kfmap(&self, id: usize, addr_space: &Arc<RwLock<AddrSpace>>, map: &Map, _consume: bool) -> Result<usize> {
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<usize> {
fn kfpath(&self, id: usize, dst: UserSliceWo) -> Result<usize> {
// 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<usize> {
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::<StatVfs>()).ok_or(Error::new(EINVAL))?.copy_from_slice(&stat)?;
dst.copy_exactly(&stat)?;
Ok(0)
}
}
+13 -43
View File
@@ -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<usize> {
inner_physfree(physical_address, size)
}
//TODO: verify exlusive access to physical memory
pub fn inner_physmap(physical_address: usize, size: usize, flags: PhysmapFlags) -> Result<usize> {
// 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<usize> {
enforce_root()?;
inner_physmap(physical_address, size, flags)
+2 -3
View File
@@ -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::<Map>()? };
if b == !0 {
MemoryScheme.fmap(!0, &map)
MemoryScheme::fmap_anonymous(&AddrSpace::current()?, &map)
} else {
file_op_generic(fd, |scheme, _, number| scheme.fmap(number, &map))
}