mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-15 08:30:02 +00:00
kernel: initial work to support lending pages
This lays groundwork to support lending pages from one process to another, and to restore permissions when pages are handed back. Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
parent
bedb499edb
commit
ed977fd564
4 changed files with 95 additions and 57 deletions
|
|
@ -46,7 +46,6 @@ assigned to every process simply by mapping megapage 1023 (`0xffc00000`).
|
|||
|
||||
| Address | Description
|
||||
| ---------- | -----------
|
||||
0x10000
|
||||
| 0x00100000 | Default entrypoint for riscv64-unknown-elf-ld (as shown by `riscv64-unknown-elf-ld --verbose`)
|
||||
| 0x80000000 | Process stack top
|
||||
| 0xff000000 | End of memory available to processes
|
||||
|
|
@ -151,3 +150,29 @@ struct PageAllocations {
|
|||
Each process requires its own page table. The kernel will be mapped to
|
||||
a fixed offset in each process, in order to save some RAM and make
|
||||
context switches easier.
|
||||
|
||||
## RISC-V `RSW` and `V` Page Table Entry Fields
|
||||
|
||||
The RISC-V Page Table Entry specification reserves two bits in a field
|
||||
called `RSW`. Additionally, it states that if `V` is `0`, then the
|
||||
entry is invalid and all other entries are `don't care`. In particular,
|
||||
the `RSW` fields are for use by the kernel for its own purposes. Xous
|
||||
uses these to keep track of borrowed memory and pre-allocating pages.
|
||||
|
||||
For the purposes of Xous, `PTE[8]` shall have the bit name `S`, and
|
||||
`PTE[9]` shall have the bit name `W`.
|
||||
|
||||
| W[9] | S[8] | X[3] | W[2] | R[1] | V[0] | Meaning |
|
||||
| ---- | ---- | ---- | ---- | ---- | ---- | --------------------- |
|
||||
| 0 | 0 | 0 | 0 | 0 | 0 | Page is unallocated |
|
||||
| 0 | 0 | 0 | 1 | 0 | 0 | _Invalid_ |
|
||||
| 0 | 0 | 1 | 1 | 0 | 0 | _Invalid_ |
|
||||
| 0 | 0 | 0 | 0 | 0 | 1 | _Invalid_ |
|
||||
| 0 | 0 | 0 | 1 | 0 | 1 | _Invalid_ |
|
||||
| 0 | 0 | 1 | 1 | 0 | 1 | _Invalid_ |
|
||||
| 0 | 0 | _x_ | _x_ | _x_ | 0 | Page is on-demand allocated, with permissions according to _RWX_ |
|
||||
| 0 | 0 | _x_ | _x_ | _x_ | 1 | Page is allocated and valid, with permissions according to _RWX_ |
|
||||
| _x_ | 1 | _x_ | 0 | _x_ | 1 | Page is immutably shared |
|
||||
| _x_ | 1 | 0 | 0 | _x_ | 0 | Page is mutably shared (and is therefore unavailable) |
|
||||
|
||||
The `W[9]` bit indicates whether the page was writable prior to the borrow.
|
||||
|
|
|
|||
|
|
@ -82,11 +82,11 @@ fn main() {
|
|||
// if let xous::Result::MemoryRange(range) = heap {
|
||||
// println!(
|
||||
// "Heap goes from {:08x} - {:08x}",
|
||||
// range.base as usize,
|
||||
// range.base as usize + range.size
|
||||
// range.addr.get(),
|
||||
// range.addr.get() + range.size.get()
|
||||
// );
|
||||
// use core::slice;
|
||||
// let mem_range = unsafe { slice::from_raw_parts_mut(range.base, range.size) };
|
||||
// let mem_range = unsafe { slice::from_raw_parts_mut(range.as_mut_ptr() as *mut u8, range.len()) };
|
||||
// println!("Filling with bytes...");
|
||||
// for word in mem_range.iter_mut() {
|
||||
// *word = 42;
|
||||
|
|
|
|||
|
|
@ -130,21 +130,20 @@ pub extern "C" fn trap_handler(
|
|||
match ex {
|
||||
RiscvException::StorePageFault(pc, addr) | RiscvException::LoadPageFault(pc, addr) => {
|
||||
println!("Fault {} @ {:08x}, addr {:08x}", ex, pc, addr);
|
||||
let mapping = MemoryMapping::current();
|
||||
let entry = mapping.pagetable_entry(addr);
|
||||
if entry as usize == 0 {
|
||||
let entry = crate::arch::mem::pagetable_entry(addr).unwrap_or_else(|x| {
|
||||
// MemoryManagerHandle::get().print_ownership();
|
||||
MemoryMapping::current().print_map();
|
||||
panic!(
|
||||
"error at {:08x}: memory not mapped or reserved for addr {:08x}",
|
||||
pc, addr
|
||||
);
|
||||
}
|
||||
let flags = unsafe { entry.read_volatile() } & 0xf;
|
||||
});
|
||||
let flags = *entry & 0x1ff;
|
||||
|
||||
// If the flags are nonzero, but the "Valid" bit is not 1, then this is
|
||||
// a reserved page. Allocate a real page to back it and resume execution.
|
||||
if flags & 1 == 0 && flags != 0 {
|
||||
// If the flags are nonzero, but the "Valid" bit is not 1 and
|
||||
// the page isn't shared, then this is a reserved page. Allocate
|
||||
// a real page to back it and resume execution.
|
||||
if flags & 1 == 0 && flags != 0 && flags & (1 << 8) == 0 {
|
||||
let new_page = {
|
||||
let mut mm = MemoryManagerHandle::get();
|
||||
mm.alloc_page(pid).expect("Couldn't allocate new page")
|
||||
|
|
@ -153,8 +152,9 @@ pub extern "C" fn trap_handler(
|
|||
let ppn0 = (new_page >> 12) & ((1 << 10) - 1);
|
||||
unsafe {
|
||||
// Map the page to our process
|
||||
entry.write_volatile((ppn1 << 20) | (ppn0 << 10) |
|
||||
(flags | (1 << 0) /* valid */ | (1 << 6) /* D */ | (1 << 7) /* A */));
|
||||
*entry = (ppn1 << 20)
|
||||
| (ppn0 << 10)
|
||||
| (flags | (1 << 0) /* valid */ | (1 << 6) /* D */ | (1 << 7)/* A */);
|
||||
flush_mmu();
|
||||
|
||||
// Zero-out the page
|
||||
|
|
@ -163,8 +163,9 @@ pub extern "C" fn trap_handler(
|
|||
.write_bytes(0, PAGE_SIZE / core::mem::size_of::<usize>());
|
||||
|
||||
// Move the page into userspace
|
||||
entry.write_volatile((ppn1 << 20) | (ppn0 << 10) |
|
||||
(flags | (1 << 0) /* valid */ | (1 << 4) /* USER */ | (1 << 6) /* D */ | (1 << 7) /* A */));
|
||||
*entry = (ppn1 << 20)
|
||||
| (ppn0 << 10)
|
||||
| (flags | (1 << 0) /* valid */ | (1 << 4) /* USER */ | (1 << 6) /* D */ | (1 << 7)/* A */);
|
||||
flush_mmu();
|
||||
};
|
||||
|
||||
|
|
@ -177,7 +178,9 @@ pub extern "C" fn trap_handler(
|
|||
// we're in an interrupt context, it is safe to access this
|
||||
// global variable.
|
||||
let (previous_pid, previous_context) = unsafe {
|
||||
PREVIOUS_PAIR.take().expect("got an instruction page fault with no previous PID")
|
||||
PREVIOUS_PAIR
|
||||
.take()
|
||||
.expect("got an instruction page fault with no previous PID")
|
||||
};
|
||||
println!(
|
||||
"ISR: Resuming previous pair of ({}, {})",
|
||||
|
|
|
|||
|
|
@ -139,21 +139,6 @@ impl MemoryMapping {
|
|||
println!("End of map");
|
||||
}
|
||||
|
||||
/// Get the pagetable entry for a given address, or `0` if none is set.
|
||||
pub fn pagetable_entry(&self, addr: usize) -> *mut usize {
|
||||
let vpn1 = (addr >> 22) & ((1 << 10) - 1);
|
||||
let vpn0 = (addr >> 12) & ((1 << 10) - 1);
|
||||
|
||||
let l1_pt = unsafe { &(*(PAGE_TABLE_ROOT_OFFSET as *mut RootPageTable)) };
|
||||
let l1_pte = l1_pt.entries[vpn1];
|
||||
if l1_pte & 1 == 0 {
|
||||
return 0 as *mut usize;
|
||||
}
|
||||
let l0_pt_virt = PAGE_TABLE_OFFSET + vpn1 * PAGE_SIZE;
|
||||
let entry = (l0_pt_virt + vpn0 * 4) as *mut usize;
|
||||
entry
|
||||
}
|
||||
|
||||
pub fn reserve_address(
|
||||
&mut self,
|
||||
mm: &mut MemoryManager,
|
||||
|
|
@ -370,6 +355,26 @@ pub fn map_page_inner(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the pagetable entry for a given address, or `Err()` if the address is invalid
|
||||
pub fn pagetable_entry(addr: usize) -> Result<&'static mut usize, xous::Error> {
|
||||
if addr & 3 != 0 {
|
||||
return Err(xous::Error::BadAlignment);
|
||||
}
|
||||
let vpn1 = (addr >> 22) & ((1 << 10) - 1);
|
||||
let vpn0 = (addr >> 12) & ((1 << 10) - 1);
|
||||
assert!(vpn1 < 1024);
|
||||
assert!(vpn0 < 1024);
|
||||
|
||||
let l1_pt = unsafe { &(*(PAGE_TABLE_ROOT_OFFSET as *mut RootPageTable)) };
|
||||
let l1_pte = l1_pt.entries[vpn1];
|
||||
if l1_pte & 1 == 0 {
|
||||
return Err(xous::Error::BadAddress);
|
||||
}
|
||||
let l0_pt_virt = PAGE_TABLE_OFFSET + vpn1 * PAGE_SIZE;
|
||||
let entry = unsafe { &mut (*((l0_pt_virt + vpn0 * 4) as *mut usize)) };
|
||||
Ok(entry)
|
||||
}
|
||||
|
||||
/// Ummap the given page from the specified process table. Never allocate a new
|
||||
/// page.
|
||||
///
|
||||
|
|
@ -381,40 +386,45 @@ pub fn map_page_inner(
|
|||
///
|
||||
/// * BadAddress - Address was not already mapped.
|
||||
pub fn unmap_page_inner(_mm: &mut MemoryManager, virt: usize) -> Result<usize, xous::Error> {
|
||||
let vpn1 = (virt >> 22) & ((1 << 10) - 1);
|
||||
let vpn0 = (virt >> 12) & ((1 << 10) - 1);
|
||||
let vpo = (virt >> 0) & ((1 << 12) - 1);
|
||||
|
||||
assert!(vpn1 < 1024);
|
||||
assert!(vpn0 < 1024);
|
||||
assert!(vpo < 4096);
|
||||
|
||||
// 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 ref mut l1_pt = 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 ref mut l0_pt = unsafe { &mut (*(l0pt_virt as *mut LeafPageTable)) };
|
||||
|
||||
// If the level 1 pagetable doesn't exist, then this address isn't valid.
|
||||
if l1_pt[vpn1] & MMUFlags::VALID.bits() == 0 {
|
||||
return Err(xous::Error::BadAddress);
|
||||
}
|
||||
let mut entry = pagetable_entry(virt)?;
|
||||
|
||||
// Ensure the entry hasn't already been mapped.
|
||||
if l0_pt.entries[vpn0] & 1 == 0 {
|
||||
if *entry & 1 == 0 {
|
||||
return Err(xous::Error::BadAddress);
|
||||
}
|
||||
let phys = (l0_pt.entries[vpn0] >> 10) << 12;
|
||||
l0_pt.entries[vpn0] = 0;
|
||||
let phys = (*entry >> 10) << 12;
|
||||
*entry = 0;
|
||||
unsafe { flush_mmu() };
|
||||
|
||||
Ok(phys)
|
||||
}
|
||||
|
||||
/// Mark the given virtual address as being lent. If `writable`, clear the
|
||||
/// `valid` bit so that this process can't accidentally write to this page while
|
||||
/// it is lent.
|
||||
///
|
||||
/// This uses the `RWS` fields to keep track of the following pieces of information:
|
||||
///
|
||||
/// * **PTE[8]**: This is set to `1` indicating the page is lent
|
||||
/// * **PTE[9]**: This is `1` if the page was previously writable
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// * BadAddress - Address was not already mapped.
|
||||
pub fn lend_page_inner(
|
||||
_mm: &mut MemoryManager,
|
||||
virt: usize,
|
||||
writable: bool,
|
||||
) -> Result<(), xous::Error> {
|
||||
let mut entry = pagetable_entry(virt)?;
|
||||
*entry = 0;
|
||||
unsafe { flush_mmu() };
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn virt_to_phys(virt: usize) -> Result<usize, xous::Error> {
|
||||
let vpn1 = (virt >> 22) & ((1 << 10) - 1);
|
||||
let vpn0 = (virt >> 12) & ((1 << 10) - 1);
|
||||
|
|
|
|||
Loading…
Reference in a new issue