2020-01-20 06:43:11 +00:00
|
|
|
use crate::definitions::{MemoryAddress, XousError, XousPid};
|
2020-01-21 09:04:09 +00:00
|
|
|
use crate::processtable::Process;
|
2020-01-20 06:43:11 +00:00
|
|
|
use crate::{print, println};
|
2019-12-25 12:11:15 +00:00
|
|
|
use core::num::NonZeroUsize;
|
2020-01-09 02:45:22 +00:00
|
|
|
use vexriscv::register::mstatus;
|
2019-12-24 01:33:02 +00:00
|
|
|
|
|
|
|
const FLASH_START: usize = 0x20000000;
|
|
|
|
const FLASH_SIZE: usize = 16_777_216;
|
|
|
|
const FLASH_END: usize = FLASH_START + FLASH_SIZE;
|
|
|
|
const RAM_START: usize = 0x40000000;
|
|
|
|
const RAM_SIZE: usize = 16_777_216;
|
|
|
|
const RAM_END: usize = RAM_START + RAM_SIZE;
|
|
|
|
const IO_START: usize = 0xe0000000;
|
|
|
|
const IO_SIZE: usize = 65_536;
|
|
|
|
const IO_END: usize = IO_START + IO_SIZE;
|
|
|
|
const LCD_START: usize = 0xB0000000;
|
|
|
|
const LCD_SIZE: usize = 32_768;
|
|
|
|
const LCD_END: usize = LCD_START + LCD_SIZE;
|
|
|
|
|
|
|
|
const PAGE_SIZE: usize = 4096;
|
|
|
|
|
|
|
|
const FLASH_PAGE_COUNT: usize = FLASH_SIZE / PAGE_SIZE;
|
|
|
|
const RAM_PAGE_COUNT: usize = RAM_SIZE / PAGE_SIZE;
|
|
|
|
const IO_PAGE_COUNT: usize = IO_SIZE;
|
|
|
|
const LCD_PAGE_COUNT: usize = LCD_SIZE / PAGE_SIZE;
|
2020-01-20 06:43:11 +00:00
|
|
|
|
2019-12-24 01:33:02 +00:00
|
|
|
pub struct MemoryManager {
|
2019-12-25 12:11:15 +00:00
|
|
|
ram: [XousPid; RAM_PAGE_COUNT],
|
2020-01-21 10:14:13 +00:00
|
|
|
flash: [XousPid; FLASH_PAGE_COUNT],
|
2019-12-25 12:11:15 +00:00
|
|
|
io: [XousPid; IO_PAGE_COUNT],
|
|
|
|
lcd: [XousPid; LCD_PAGE_COUNT],
|
2019-12-24 01:33:02 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 06:43:11 +00:00
|
|
|
impl core::fmt::Debug for MemoryManager {
|
|
|
|
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::result::Result<(), core::fmt::Error> {
|
|
|
|
writeln!(fmt, "MemoryManager: ")?;
|
|
|
|
writeln!(
|
|
|
|
fmt,
|
|
|
|
" flash: {:08x} .. {:08x} ({})",
|
|
|
|
FLASH_START,
|
|
|
|
FLASH_END,
|
|
|
|
self.flash.len()
|
|
|
|
)?;
|
|
|
|
writeln!(
|
|
|
|
fmt,
|
|
|
|
" ram: {:08x} .. {:08x} ({})",
|
|
|
|
RAM_START,
|
|
|
|
RAM_END,
|
|
|
|
self.ram.len()
|
|
|
|
)?;
|
|
|
|
writeln!(
|
|
|
|
fmt,
|
|
|
|
" io: {:08x} .. {:08x} ({})",
|
|
|
|
IO_START,
|
|
|
|
IO_END,
|
|
|
|
self.io.len()
|
|
|
|
)?;
|
|
|
|
writeln!(
|
|
|
|
fmt,
|
|
|
|
" lcd: {:08x} .. {:08x} ({})",
|
|
|
|
LCD_START,
|
|
|
|
LCD_END,
|
|
|
|
self.lcd.len()
|
|
|
|
)?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A single RISC-V page table entry. In order to resolve an address,
|
|
|
|
/// we need two entries: the top level, followed by the lower level.
|
2020-01-09 03:04:07 +00:00
|
|
|
struct PageTable {
|
|
|
|
entries: [usize; 1024],
|
|
|
|
}
|
|
|
|
|
2019-12-24 01:33:02 +00:00
|
|
|
extern "C" {
|
|
|
|
// Boundaries of the .bss section
|
|
|
|
static mut _ebss: usize;
|
|
|
|
static mut _sbss: usize;
|
|
|
|
|
|
|
|
// Boundaries of the .data section
|
|
|
|
static mut _edata: usize;
|
|
|
|
static mut _sdata: usize;
|
|
|
|
|
|
|
|
// Boundaries of the stack
|
|
|
|
static mut _estack: usize;
|
|
|
|
static mut _sstack: usize;
|
|
|
|
|
2019-12-25 12:11:15 +00:00
|
|
|
// Boundaries of the .text section
|
|
|
|
static mut _stext: usize;
|
|
|
|
static mut _etext: usize;
|
|
|
|
|
2019-12-24 01:33:02 +00:00
|
|
|
// Boundaries of the heap
|
|
|
|
static _sheap: usize;
|
|
|
|
static _eheap: usize;
|
|
|
|
|
|
|
|
// Initial values of the .data section (stored in Flash)
|
|
|
|
static _sidata: usize;
|
|
|
|
}
|
|
|
|
|
|
|
|
use core::mem::transmute;
|
|
|
|
|
2020-01-20 06:43:11 +00:00
|
|
|
/// Enable transmuting from pointers-to-addresses to addresses.
|
|
|
|
/// This is required because the linker creates variables
|
|
|
|
/// such as _stext that are located at specific offsets -- such
|
|
|
|
/// as the start of the text section -- and their address is
|
|
|
|
/// the actual piece of data we want.
|
|
|
|
/// Rust really doesn't like going from addresses to values, so
|
|
|
|
/// we transmute from one to the other in order to construct a
|
|
|
|
/// range that we can loop through.
|
2020-01-09 03:04:07 +00:00
|
|
|
macro_rules! mem_range {
|
|
|
|
( $s:expr, $e:expr ) => {{
|
|
|
|
let start = unsafe { transmute::<&usize, usize>(&$s) };
|
|
|
|
let end = unsafe { transmute::<&usize, usize>(&$e) };
|
|
|
|
(start..end).step_by(PAGE_SIZE)
|
|
|
|
}}
|
|
|
|
}
|
|
|
|
|
2020-01-20 06:43:11 +00:00
|
|
|
/// Initialize the memory map.
|
2019-12-24 01:33:02 +00:00
|
|
|
/// This will go through memory and map anything that the kernel is
|
2019-12-25 12:11:15 +00:00
|
|
|
/// using to process 1, then allocate a pagetable for this process
|
|
|
|
/// and place it at the usual offset. The MMU will not be enabled yet,
|
|
|
|
/// as the process entry has not yet been created.
|
2019-12-24 01:33:02 +00:00
|
|
|
impl MemoryManager {
|
|
|
|
pub fn new() -> MemoryManager {
|
|
|
|
let mut mm = MemoryManager {
|
|
|
|
flash: [0; FLASH_PAGE_COUNT],
|
|
|
|
ram: [0; RAM_PAGE_COUNT],
|
|
|
|
io: [0; IO_PAGE_COUNT],
|
|
|
|
lcd: [0; LCD_PAGE_COUNT],
|
|
|
|
};
|
2020-01-20 06:43:11 +00:00
|
|
|
println!("Created Memory Manager: {:?}", mm);
|
|
|
|
|
|
|
|
// Claim existing pages for PID 1, in preparation for turning on
|
|
|
|
// the MMU
|
|
|
|
unsafe { mstatus::clear_mie() };
|
|
|
|
|
|
|
|
let ranges = [
|
|
|
|
mem_range!(&_sbss, &_ebss),
|
|
|
|
mem_range!(&_sdata, &_edata),
|
|
|
|
mem_range!(&_estack, &_sstack), // NOTE: Stack is reversed
|
|
|
|
mem_range!(&_stext, &_etext),
|
|
|
|
];
|
|
|
|
for range in &ranges {
|
|
|
|
for region in range.clone() {
|
|
|
|
mm.claim_page(region & !0xfff, 1)
|
|
|
|
.expect("Unable to claim region for PID 1");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe { mstatus::set_mie() };
|
2019-12-24 01:33:02 +00:00
|
|
|
|
|
|
|
mm
|
|
|
|
}
|
|
|
|
|
2020-01-09 03:04:07 +00:00
|
|
|
/// Allocate a single page to the given process.
|
2020-01-20 06:43:11 +00:00
|
|
|
/// Ensures the page is zeroed out prior to handing it over to
|
|
|
|
/// the specified process.
|
2019-12-25 12:11:15 +00:00
|
|
|
pub fn alloc_page(&mut self, pid: XousPid) -> Result<MemoryAddress, XousError> {
|
|
|
|
// Go through all RAM pages looking for a free page.
|
|
|
|
// Optimization: start from the previous address.
|
2020-01-21 09:04:09 +00:00
|
|
|
// println!("Allocating page for PID {}", pid);
|
2019-12-25 12:11:15 +00:00
|
|
|
for index in 0..RAM_PAGE_COUNT {
|
2020-01-21 09:04:09 +00:00
|
|
|
// println!(" Checking {:08x}...", index * PAGE_SIZE + RAM_START);
|
2019-12-25 12:11:15 +00:00
|
|
|
if self.ram[index] == 0 {
|
|
|
|
self.ram[index] = pid;
|
|
|
|
let page_addr = (index * PAGE_SIZE + RAM_START) as *mut u32;
|
|
|
|
// Zero-out the page
|
|
|
|
unsafe {
|
2020-01-20 06:43:11 +00:00
|
|
|
for i in 0..PAGE_SIZE / 4 {
|
2019-12-25 12:11:15 +00:00
|
|
|
*page_addr.add(i) = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let new_page = unsafe { transmute::<*mut u32, usize>(page_addr) };
|
2020-01-21 09:04:09 +00:00
|
|
|
// println!(" Page {:08x} is free", new_page);
|
2019-12-25 12:11:15 +00:00
|
|
|
return Ok(NonZeroUsize::new(new_page).unwrap());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(XousError::OutOfMemory)
|
|
|
|
}
|
|
|
|
|
2020-01-21 09:04:09 +00:00
|
|
|
/// Map the given page to the specified process table. If necessary,
|
|
|
|
/// allocate a new page.
|
|
|
|
///
|
|
|
|
/// # Errors
|
|
|
|
///
|
|
|
|
/// * OutOfMemory - Tried to allocate a new pagetable, but ran out of memory.
|
|
|
|
fn map_page(
|
|
|
|
&mut self,
|
|
|
|
root: &mut PageTable,
|
|
|
|
phys: usize,
|
|
|
|
virt: usize,
|
|
|
|
) -> Result<(), XousError> {
|
|
|
|
let ppn1 = (phys >> 20) & ((1 << 12) - 1);
|
|
|
|
let ppn0 = (phys >> 10) & ((1 << 10) - 1);
|
|
|
|
let ppo = (phys >> 0) & ((1 << 12) - 1);
|
|
|
|
let vpn1 = (virt >> 22) & ((1 << 10) - 1);
|
|
|
|
let vpn0 = (virt >> 10) & ((1 << 10) - 1);
|
|
|
|
let vpo = (virt >> 0) & ((1 << 12) - 1);
|
2020-01-09 03:04:07 +00:00
|
|
|
|
2020-01-21 09:04:09 +00:00
|
|
|
println!(
|
|
|
|
"Mapping phys: {:08x} -> virt: {:08x} (vpn1: {:04x} vpn0: {:04x} ppn1: {:04x} ppn0: {:04x})",
|
|
|
|
phys, virt, vpn1, vpn0, ppn1, ppn0
|
|
|
|
);
|
|
|
|
assert!(ppn1 < 4096);
|
|
|
|
assert!(ppn0 < 1024);
|
|
|
|
assert!(ppo < 4096);
|
|
|
|
assert!(vpn1 < 1024);
|
|
|
|
assert!(vpn0 < 1024);
|
|
|
|
assert!(vpo < 4096);
|
2019-12-25 12:11:15 +00:00
|
|
|
|
2020-01-21 09:04:09 +00:00
|
|
|
let ref mut l1_pt = root.entries;
|
|
|
|
println!("l1_pt is at {:p} ({:p})", &l1_pt, &l1_pt[vpn1]);
|
|
|
|
|
|
|
|
// Allocate a new level 1 pagetable entry if one doesn't exist.
|
|
|
|
if l1_pt[vpn1] & 1 == 0 {
|
|
|
|
println!(" top-level VPN1 {:04x}: {:08x} (will allocate a new one)", vpn1, l1_pt[vpn1]);
|
|
|
|
// Allocate the page to the kernel (PID 1)
|
|
|
|
let new_addr = self.alloc_page(1)?.get();
|
|
|
|
println!(
|
|
|
|
" Allocated new top-level page for VPN1 {:04x} in process @ {:08x}",
|
|
|
|
vpn1, new_addr
|
|
|
|
);
|
|
|
|
|
|
|
|
// Mark this entry as a leaf node (WRX as 0), and indicate
|
|
|
|
// it is a valid page by setting "V".
|
|
|
|
l1_pt[vpn1] = (((new_addr >> 10) & ((1 << 22) - 1)) << 10) | 1;
|
|
|
|
println!(" New top-level page entry: {:08x}", l1_pt[vpn1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut l0_pt = unsafe {
|
|
|
|
let tmp = (l1_pt[vpn1] & ((1 << 10) - 1)) as *mut PageTable;
|
|
|
|
(*tmp).entries
|
|
|
|
};
|
|
|
|
|
|
|
|
// Allocate a new level 0 pagetable entry if one doesn't exist.
|
|
|
|
if l0_pt[vpn0] & 1 != 0 {
|
|
|
|
panic!("Page already allocated!");
|
|
|
|
}
|
|
|
|
l0_pt[vpn0] = (ppn1 << 20) | (ppn0 << 10) | 1 | 0xe;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create an identity mapping, copying the kernel to itself
|
2020-01-21 10:14:13 +00:00
|
|
|
pub fn create_identity(&mut self, process: &Process) -> Result<(), XousError> {
|
2020-01-21 09:04:09 +00:00
|
|
|
let root_page = (process.satp & ((1 << 22) - 1)) << 9;
|
|
|
|
let pt = unsafe { &mut (*(root_page as *mut PageTable)) };
|
|
|
|
println!("SATP value: {:08x} Root page: {:08x} pt: {:p} pt: {:p}", process.satp, root_page, &pt, pt);
|
2020-01-21 10:14:13 +00:00
|
|
|
|
|
|
|
let ranges = [
|
|
|
|
mem_range!(&_sbss, &_ebss),
|
|
|
|
mem_range!(&_sdata, &_edata),
|
|
|
|
mem_range!(&_estack, &_sstack), // NOTE: Stack is reversed
|
|
|
|
mem_range!(&_stext, &_etext),
|
|
|
|
];
|
|
|
|
for range in &ranges {
|
|
|
|
for region in range.clone() {
|
|
|
|
// mm.claim_page(region & !0xfff, 1)
|
|
|
|
// .expect("Unable to claim region for PID 1");
|
2020-01-21 09:04:09 +00:00
|
|
|
self.map_page(
|
|
|
|
pt,
|
2020-01-21 10:14:13 +00:00
|
|
|
region,
|
|
|
|
region,
|
2020-01-21 09:04:09 +00:00
|
|
|
)?;
|
|
|
|
print!("Entries mapped: >");
|
|
|
|
let mut i = 0;
|
|
|
|
for (entry_idx, entry) in pt.entries.iter().enumerate() {
|
|
|
|
i = i + 1;
|
|
|
|
if *entry != 0 {
|
|
|
|
print!(" {}:{:08x}", entry_idx, entry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
println!(" < ({})", i);
|
|
|
|
println!("");
|
|
|
|
}
|
|
|
|
}
|
2020-01-21 10:14:13 +00:00
|
|
|
// let flash_orig = self.flash.clone();
|
|
|
|
// for (flash_idx, flash_pid) in flash_orig.iter().enumerate() {
|
|
|
|
// if *flash_pid == pid {
|
|
|
|
// println!(
|
|
|
|
// "Flash addr {:08x} owned by PID {}, mapping it as ident",
|
|
|
|
// flash_idx * PAGE_SIZE + FLASH_START,
|
|
|
|
// pid
|
|
|
|
// );
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
// for (idx, page) in flash_orig.iter().enumerate() {
|
2020-01-21 09:04:09 +00:00
|
|
|
Ok(())
|
2019-12-25 12:11:15 +00:00
|
|
|
}
|
|
|
|
|
2020-01-09 03:04:07 +00:00
|
|
|
/// Mark a given address as being owned by the specified process ID
|
2019-12-24 01:33:02 +00:00
|
|
|
fn claim_page(&mut self, addr: usize, pid: XousPid) -> Result<(), XousError> {
|
2020-01-09 03:04:07 +00:00
|
|
|
fn claim_page_inner(tbl: &mut [u8], addr: usize, pid: XousPid) -> Result<(), XousError> {
|
|
|
|
let page = addr / PAGE_SIZE;
|
|
|
|
if page > tbl.len() {
|
|
|
|
return Err(XousError::BadAddress);
|
|
|
|
}
|
|
|
|
if tbl[page] != 0 && tbl[page] != pid {
|
|
|
|
return Err(XousError::MemoryInUse);
|
|
|
|
}
|
|
|
|
tbl[page] = pid;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-12-24 01:33:02 +00:00
|
|
|
// Ensure the address lies on a page boundary
|
|
|
|
if addr & 0xfff != 0 {
|
|
|
|
return Err(XousError::BadAlignment);
|
|
|
|
}
|
|
|
|
|
|
|
|
match addr {
|
2020-01-20 06:43:11 +00:00
|
|
|
FLASH_START..=FLASH_END => claim_page_inner(&mut self.flash, addr - FLASH_START, pid),
|
2020-01-09 03:04:07 +00:00
|
|
|
RAM_START..=RAM_END => claim_page_inner(&mut self.ram, addr - RAM_START, pid),
|
|
|
|
IO_START..=IO_END => claim_page_inner(&mut self.io, addr - IO_START, pid),
|
|
|
|
LCD_START..=LCD_END => claim_page_inner(&mut self.lcd, addr - LCD_START, pid),
|
2019-12-24 01:33:02 +00:00
|
|
|
_ => Err(XousError::BadAddress),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|