kernel: implement UpdateMemoryFlags syscall

This call is used to strip flags from a chunk of memory.

Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
Sean Cross 2022-02-07 13:36:48 +08:00
parent 3380a0e0ed
commit 5fddf7c230
4 changed files with 182 additions and 1 deletions

View file

@ -111,3 +111,11 @@ pub fn hand_page_to_user(_virt: *mut u8) -> Result<(), Error> {
pub fn virt_to_phys(virt: usize) -> Result<usize, Error> {
Ok(virt)
}
pub fn page_flags(_virt: usize) -> Option<MemoryFlags> {
None
}
pub fn update_page_flags(_virt: usize, _flags: MemoryFlags) -> Result<(), xous_kernel::Error> {
Ok(())
}

View file

@ -282,7 +282,7 @@ pub fn hand_page_to_user(virt: *mut u8) -> Result<(), xous_kernel::Error> {
// The root (l1) pagetable is defined to be mapped into our virtual
// address space at this address.
let l1_pt = unsafe { &mut (*(PAGE_TABLE_ROOT_OFFSET as *mut RootPageTable)) };
let l1_pt =&mut l1_pt.entries;
let l1_pt = &mut l1_pt.entries;
// Subsequent pagetables are defined as being mapped starting at
// PAGE_TABLE_OFFSET
@ -789,3 +789,124 @@ pub fn address_available(virt: usize) -> bool {
false
}
}
/// Get the `MemoryFlags` for the requested virtual address. The address must
/// be valid and page-aligned, and must not be Shared.
///
/// # Returns
///
/// * **None**: The page is not valid or is shared
/// * **Some(MemoryFlags)**: The translated sharing permissions of the given flags
pub fn page_flags(virt: usize) -> Option<MemoryFlags> {
let vpn1 = (virt >> 22) & ((1 << 10) - 1);
let vpn0 = (virt >> 12) & ((1 << 10) - 1);
// The root (l1) pagetable is defined to be mapped into our virtual
// address space at this address.
let l1_pt = unsafe { &mut (*(PAGE_TABLE_ROOT_OFFSET as *mut RootPageTable)) };
let l1_pt = &mut l1_pt.entries;
// Subsequent pagetables are defined as being mapped starting at
// offset 0x0020_0004, so 4 must be added to the ppn1 value.
let l0pt_virt = PAGE_TABLE_OFFSET + vpn1 * PAGE_SIZE;
let l0_pt = &mut unsafe { &mut (*(l0pt_virt as *mut LeafPageTable)) };
// If the level 1 pagetable doesn't exist, then this address is invalid
if l1_pt[vpn1] & MMUFlags::VALID.bits() == 0 {
return None;
}
let mmu_flags = l0_pt.entries[vpn0];
// If the page is "Valid" but shared, issue a sharing violation
if mmu_flags & MMUFlags::S.bits() != 0 {
return None;
}
let mut return_flags = MemoryFlags::empty();
if mmu_flags & MMUFlags::R.bits() != 0 {
return_flags = return_flags | MemoryFlags::R;
}
if mmu_flags & MMUFlags::W.bits() != 0 {
return_flags = return_flags | MemoryFlags::W;
}
if mmu_flags & MMUFlags::X.bits() != 0 {
return_flags = return_flags | MemoryFlags::X;
}
if return_flags.is_empty() {
None
} else {
Some(return_flags)
}
}
pub fn update_page_flags(virt: usize, flags: MemoryFlags) -> Result<(), xous_kernel::Error> {
// The resulting flags must actually be valid
if (flags & (MemoryFlags::R | MemoryFlags::W | MemoryFlags::X)).is_empty() {
return Err(xous_kernel::Error::MemoryInUse);
}
let vpn1 = (virt >> 22) & ((1 << 10) - 1);
let vpn0 = (virt >> 12) & ((1 << 10) - 1);
// The root (l1) pagetable is defined to be mapped into our virtual
// address space at this address.
let l1_pt = unsafe { &mut (*(PAGE_TABLE_ROOT_OFFSET as *mut RootPageTable)) };
let l1_pt = &mut l1_pt.entries;
// Subsequent pagetables are defined as being mapped starting at
// offset 0x0020_0004, so 4 must be added to the ppn1 value.
let l0pt_virt = PAGE_TABLE_OFFSET + vpn1 * PAGE_SIZE;
let l0_pt = &mut unsafe { &mut (*(l0pt_virt as *mut LeafPageTable)) };
// If the level 1 pagetable doesn't exist, then this address is invalid
if l1_pt[vpn1] & MMUFlags::VALID.bits() == 0 {
return Err(xous_kernel::Error::OutOfMemory);
}
let mut mmu_flags = l0_pt.entries[vpn0];
// If the page is "Valid" but shared, issue a sharing violation
if mmu_flags & MMUFlags::S.bits() != 0 {
return Err(xous_kernel::Error::ShareViolation);
}
// Strip the flags as requested
if (flags & MemoryFlags::X).is_empty() {
if mmu_flags & MMUFlags::X.bits() != 0 {
mmu_flags = mmu_flags & !MMUFlags::X.bits();
}
} else if mmu_flags & MMUFlags::X.bits() == 0 {
// Ensure we're not adding flags back
return Err(xous_kernel::Error::ShareViolation);
}
// Strip the flags as requested
if (flags & MemoryFlags::R).is_empty() {
if mmu_flags & MMUFlags::R.bits() != 0 {
mmu_flags = mmu_flags & !MMUFlags::R.bits();
}
} else if mmu_flags & MMUFlags::R.bits() == 0 {
// Ensure we're not adding flags back
return Err(xous_kernel::Error::ShareViolation);
}
// Strip the flags as requested
if (flags & MemoryFlags::W).is_empty() {
if mmu_flags & MMUFlags::W.bits() != 0 {
mmu_flags = mmu_flags & !MMUFlags::W.bits();
}
} else if mmu_flags & MMUFlags::W.bits() == 0 {
// Ensure we're not adding flags back
return Err(xous_kernel::Error::ShareViolation);
}
// Update the MMU
l0_pt.entries[vpn0] = mmu_flags;
Ok(())
}

View file

@ -776,4 +776,47 @@ impl MemoryManager {
}
}
}
/// Adjust the flags on the given memory range. This allows for stripping flags from a memory
/// range but does not allow adding flags. The memory range must exist, and the flags must be valid.
pub fn update_memory_flags(
&mut self,
range: MemoryRange,
flags: MemoryFlags,
) -> Result<(), xous_kernel::Error> {
let virt = range.as_mut_ptr() as usize;
let size = range.len();
if virt & (PAGE_SIZE - 1) != 0 {
return Err(xous_kernel::Error::BadAlignment);
}
if size & (PAGE_SIZE - 1) != 0 {
return Err(xous_kernel::Error::BadAlignment);
}
// Pre-check the range to ensure the new flags are valid
for virt in (virt..(virt + size)).step_by(PAGE_SIZE) {
let existing_flags =
crate::arch::mem::page_flags(virt).ok_or(xous_kernel::Error::MemoryInUse)?;
// If the new flags add to the range, return an error.
if !(!existing_flags & flags).is_empty() {
return Err(xous_kernel::Error::MemoryInUse);
}
}
// Now that the flags are validated, perform the update. This is fine as long as
// we're unicore.
for virt in (virt..(virt + size)).step_by(PAGE_SIZE) {
let existing_flags =
crate::arch::mem::page_flags(virt).ok_or(xous_kernel::Error::MemoryInUse)?;
// If the new flags add to the range, return an error.
if !(!existing_flags & flags).is_empty() {
return Err(xous_kernel::Error::MemoryInUse);
}
crate::arch::mem::update_page_flags(virt, flags)?;
}
Ok(())
}
}

View file

@ -884,6 +884,15 @@ pub fn handle_inner(pid: PID, tid: TID, in_irq: bool, call: SysCall) -> SysCallR
ret
})
}
SysCall::UpdateMemoryFlags(range, flags, pid) => {
// We do not yet support modifying flags for other processes.
if pid.is_some() {
return Err(xous_kernel::Error::ProcessNotChild);
}
MemoryManager::with_mut(|mm| mm.update_memory_flags(range, flags))?;
Ok(xous_kernel::Result::Ok)
}
/* https://github.com/betrusted-io/xous-core/issues/90
SysCall::SetExceptionHandler(pc, sp) => SystemServices::with_mut(|ss| {
ss.set_exception_handler(pid, pc, sp)