diff --git a/docs/memory.md b/docs/memory.md index 4c4966cbc..b1dacfd90 100644 --- a/docs/memory.md +++ b/docs/memory.md @@ -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. diff --git a/examples/shell/src/main.rs b/examples/shell/src/main.rs index 96d401a6f..8ffdeaf92 100644 --- a/examples/shell/src/main.rs +++ b/examples/shell/src/main.rs @@ -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; diff --git a/kernel/src/arch/riscv/irq.rs b/kernel/src/arch/riscv/irq.rs index c275adb10..031311487 100644 --- a/kernel/src/arch/riscv/irq.rs +++ b/kernel/src/arch/riscv/irq.rs @@ -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::()); // 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 ({}, {})", diff --git a/kernel/src/arch/riscv/mem.rs b/kernel/src/arch/riscv/mem.rs index 9ab82b456..43497eade 100644 --- a/kernel/src/arch/riscv/mem.rs +++ b/kernel/src/arch/riscv/mem.rs @@ -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 { - 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 { let vpn1 = (virt >> 22) & ((1 << 10) - 1); let vpn0 = (virt >> 12) & ((1 << 10) - 1);