tweaks
This commit is contained in:
parent
1e46ee1009
commit
0e15f55e02
1 changed files with 63 additions and 22 deletions
85
src/main.rs
85
src/main.rs
|
|
@ -13,36 +13,73 @@ const BIT_X: u8 = 8;
|
||||||
// (Not used) - user mode accessible
|
// (Not used) - user mode accessible
|
||||||
const BIT_U: u8 = 16;
|
const BIT_U: u8 = 16;
|
||||||
|
|
||||||
|
/*
|
||||||
// (Not used) - global mapping
|
// (Not used) - global mapping
|
||||||
const BIT_G: u8 = 32;
|
const BIT_G: u8 = 32;
|
||||||
|
|
||||||
// (Not presently used) accessed -- set if we've touched this since last time A cleared
|
// (Not presently used) accessed -- set if we've touched this since last time A cleared
|
||||||
const BIT_A: u8 = 64;
|
const BIT_A: u8 = 64;
|
||||||
|
|
||||||
// (Will be used for JIT on writes to executable pags) dirty -- written since last time D cleared
|
// (Not presently used)dirty -- written since last time D cleared
|
||||||
|
// Note each core/hart has its own dirty state for local JIT
|
||||||
const BIT_D: u8 = 128;
|
const BIT_D: u8 = 128;
|
||||||
|
|
||||||
/*
|
// (Not used or room for it) reserved for supervisor
|
||||||
// (Not used) reserved for supervisor
|
|
||||||
const BITS_RSW_LO: i64 = 256;
|
const BITS_RSW_LO: i64 = 256;
|
||||||
const BITS_RSW_HI: i64 = 512;
|
const BITS_RSW_HI: i64 = 512;
|
||||||
const BITS_RSW: i64 = BITS_RSW_LO | BITS_RSW_HI;
|
const BITS_RSW: i64 = BITS_RSW_LO | BITS_RSW_HI;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
struct PageTableEntry {
|
||||||
|
flags: u8
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PageTableEntry {
|
||||||
|
fn new(flags: u8) -> Self {
|
||||||
|
return Self {
|
||||||
|
flags
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_u8(&self) -> u8 {
|
||||||
|
return self.flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_valid(&self) -> bool {
|
||||||
|
return self.flags & BIT_V == BIT_V;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_readable(&self) -> bool {
|
||||||
|
return self.flags & (BIT_V | BIT_R) == (BIT_V | BIT_R);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_writable(&self) -> bool {
|
||||||
|
return self.flags & (BIT_V | BIT_R | BIT_W) == (BIT_V | BIT_R | BIT_W);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_executable(&self) -> bool {
|
||||||
|
return self.flags & (BIT_V | BIT_R | BIT_X) == (BIT_V | BIT_R | BIT_X);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
type ExecutorFunc = fn(i64, &mut CoreState, &mut MachineState) -> i64;
|
type ExecutorFunc = fn(i64, &mut CoreState, &mut MachineState) -> i64;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct MachineState {
|
struct MachineState {
|
||||||
memory: Vec<u8>,
|
memory: Vec<u8>,
|
||||||
pages: Vec<u8>,
|
pages: Vec<PageTableEntry>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Note that physical memory accessors can traverse page boundaries;
|
* Note that physical memory accessors can traverse page boundaries;
|
||||||
* we lay out linear memory from 0 to +4 gigabytes and will allocate
|
* we lay out linear memory from 0 to +4 gigabytes and will allocate
|
||||||
* as many page table entries as are needed to cover RAM. These eat
|
* as many page table entries as are needed to cover RAM. These eat
|
||||||
* up an extra 1 byte per 4 kilobytes of address space used, initially
|
* up an extra 1 byte per 4 KiB of address space used (1 MiB per 4 GiB),
|
||||||
* allocating enough for all physical memory initially allocated.
|
* initially allocating enough for all physical memory allocated.
|
||||||
*
|
*
|
||||||
* This will be relatively space-inefficient for sparse address spaces
|
* This will be relatively space-inefficient for sparse address spaces
|
||||||
* in the range of several gigabytes and more but requires only one
|
* in the range of several gigabytes and more but requires only one
|
||||||
|
|
@ -52,7 +89,7 @@ struct MachineState {
|
||||||
* page tables, even if running on different threads.
|
* page tables, even if running on different threads.
|
||||||
*/
|
*/
|
||||||
impl MachineState {
|
impl MachineState {
|
||||||
fn restore(memory: Vec<u8>, pages: Vec<u8>) -> Self {
|
fn new_with_state(memory: Vec<u8>, pages: Vec<PageTableEntry>) -> Self {
|
||||||
if ((memory.len() >> PAGE_BITS) << PAGE_BITS) != memory.len() {
|
if ((memory.len() >> PAGE_BITS) << PAGE_BITS) != memory.len() {
|
||||||
panic!("memory size must be a multiple of 4096 bytes");
|
panic!("memory size must be a multiple of 4096 bytes");
|
||||||
}
|
}
|
||||||
|
|
@ -67,23 +104,23 @@ impl MachineState {
|
||||||
|
|
||||||
fn new(memory_size: usize) -> Self {
|
fn new(memory_size: usize) -> Self {
|
||||||
let memory = vec![0u8; memory_size];
|
let memory = vec![0u8; memory_size];
|
||||||
let pages = vec![0u8; memory_size >> PAGE_BITS];
|
let pages = vec![PageTableEntry::new(0); memory_size >> PAGE_BITS];
|
||||||
return Self::restore(memory, pages);
|
return Self::new_with_state(memory, pages);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_page_table_entry(&mut self, address: usize) -> u32 {
|
fn get_page_table_entry(&mut self, address: usize) -> PageTableEntry {
|
||||||
let page = address >> PAGE_BITS;
|
let page = address >> PAGE_BITS;
|
||||||
if page < self.pages.len() {
|
if page < self.pages.len() {
|
||||||
return self.pages[page] as u32;
|
return self.pages[page];
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return PageTableEntry::new(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_page_table_entry(&mut self, address: usize, bits: u32) {
|
fn set_page_table_entry(&mut self, address: usize, entry: PageTableEntry) {
|
||||||
let page = address >> PAGE_BITS;
|
let page = address >> PAGE_BITS;
|
||||||
if page < self.pages.len() {
|
if page < self.pages.len() {
|
||||||
self.pages[address >> 12] = bits as u8;
|
self.pages[address >> 12] = entry;
|
||||||
} else {
|
} else {
|
||||||
panic!("@fixme: handle attempts to expand address space");
|
panic!("@fixme: handle attempts to expand address space");
|
||||||
}
|
}
|
||||||
|
|
@ -188,9 +225,6 @@ struct CoreState {
|
||||||
// bytes separately
|
// bytes separately
|
||||||
f: [f64; 32],
|
f: [f64; 32],
|
||||||
|
|
||||||
// 4096 csrs? no we're not gonna store them all
|
|
||||||
satp: i64,
|
|
||||||
|
|
||||||
// * fflags, accrued exceptions: bits 0-4
|
// * fflags, accrued exceptions: bits 0-4
|
||||||
// * nx: bit 0
|
// * nx: bit 0
|
||||||
// * uf: bit 1
|
// * uf: bit 1
|
||||||
|
|
@ -206,11 +240,18 @@ struct CoreState {
|
||||||
// Because function references are linked separately on
|
// Because function references are linked separately on
|
||||||
// each thread in WebAssembly, this has to live in each
|
// each thread in WebAssembly, this has to live in each
|
||||||
// core's state separately.
|
// core's state separately.
|
||||||
executors: HashMap<i64, ExecutorFunc>
|
executors: HashMap<i64, ExecutorFunc>,
|
||||||
|
|
||||||
|
// Local dirty flags for JIT pages.
|
||||||
|
// When we get a fence.i instructrion, look for all dirty
|
||||||
|
// pages and invalidate any functions including them
|
||||||
|
// Takes up to 1 byte per 4 KiB (1 MiB per 4 GiB) per thread.
|
||||||
|
// Could be made more compact if only 1 bit is needed.
|
||||||
|
dirty: Vec<u8>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CoreState {
|
impl CoreState {
|
||||||
fn new() -> Self {
|
fn new(machine: &MachineState) -> Self {
|
||||||
return Self {
|
return Self {
|
||||||
x: [
|
x: [
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
|
@ -218,7 +259,6 @@ impl CoreState {
|
||||||
0, 0, 0, 0, 0, 0, 0, 0,
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
0, 0, 0, 0, 0, 0, 0, 0
|
0, 0, 0, 0, 0, 0, 0, 0
|
||||||
],
|
],
|
||||||
satp: 0,
|
|
||||||
f: [
|
f: [
|
||||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
|
||||||
|
|
@ -226,7 +266,8 @@ impl CoreState {
|
||||||
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
|
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
|
||||||
],
|
],
|
||||||
fcsr: 0,
|
fcsr: 0,
|
||||||
executors: HashMap::new()
|
executors: HashMap::new(),
|
||||||
|
dirty: vec![0u8; machine.memory.len()]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -300,7 +341,7 @@ extern "C" fn interpreter(
|
||||||
fn main() {
|
fn main() {
|
||||||
let size = 8 * 1024 * 1024;
|
let size = 8 * 1024 * 1024;
|
||||||
let mut machine = MachineState::new(size);
|
let mut machine = MachineState::new(size);
|
||||||
let mut core = CoreState::new();
|
let mut core = CoreState::new(&machine);
|
||||||
let pc = interpreter(&mut machine, &mut core, 0);
|
let pc = interpreter(&mut machine, &mut core, 0);
|
||||||
println!("Ended with PC {}", pc);
|
println!("Ended with PC {}", pc);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue