diff --git a/kernel/src/arch/hosted/mem.rs b/kernel/src/arch/hosted/mem.rs index b301b1b9a..c2d5a26fc 100644 --- a/kernel/src/arch/hosted/mem.rs +++ b/kernel/src/arch/hosted/mem.rs @@ -111,3 +111,11 @@ pub fn hand_page_to_user(_virt: *mut u8) -> Result<(), Error> { pub fn virt_to_phys(virt: usize) -> Result { Ok(virt) } + +pub fn page_flags(_virt: usize) -> Option { + None +} + +pub fn update_page_flags(_virt: usize, _flags: MemoryFlags) -> Result<(), xous_kernel::Error> { + Ok(()) +} \ No newline at end of file diff --git a/kernel/src/arch/riscv/mem.rs b/kernel/src/arch/riscv/mem.rs index e58913a08..cf52c3402 100644 --- a/kernel/src/arch/riscv/mem.rs +++ b/kernel/src/arch/riscv/mem.rs @@ -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 { + 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(()) +} diff --git a/kernel/src/mem.rs b/kernel/src/mem.rs index c84002146..4a5c95c09 100644 --- a/kernel/src/mem.rs +++ b/kernel/src/mem.rs @@ -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(()) + } } diff --git a/kernel/src/syscall.rs b/kernel/src/syscall.rs index fb42a3038..d0ebcf399 100644 --- a/kernel/src/syscall.rs +++ b/kernel/src/syscall.rs @@ -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)