almost got out-of-memory test working
Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
parent
4b2e4b0548
commit
abef5b7db3
16
Cargo.lock
generated
16
Cargo.lock
generated
@ -13,6 +13,14 @@ dependencies = [
|
||||
"scroll",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jurubas"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"goblin",
|
||||
"riscv-cpu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.20"
|
||||
@ -47,14 +55,6 @@ dependencies = [
|
||||
name = "riscv-cpu"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "rouns"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"goblin",
|
||||
"riscv-cpu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scroll"
|
||||
version = "0.11.0"
|
||||
|
@ -1,12 +1,10 @@
|
||||
workspace = { members = ["crates/riscv-cpu"] }
|
||||
|
||||
[package]
|
||||
name = "rouns"
|
||||
name = "jurubas"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
riscv-cpu = { path = "crates/riscv-cpu" }
|
||||
goblin = { version = "0.7.1", features = [ "std", "elf32", "alloc" ]}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::sync::{mpsc::Receiver, Arc, RwLock};
|
||||
use std::sync::{mpsc::Receiver, Arc, Mutex};
|
||||
|
||||
pub use super::mmu::Memory;
|
||||
use super::mmu::{AddressingMode, Mmu};
|
||||
@ -54,10 +54,12 @@ pub const MIP_SEIP: u64 = 0x200;
|
||||
const MIP_STIP: u64 = 0x020;
|
||||
const MIP_SSIP: u64 = 0x002;
|
||||
|
||||
pub type ResponseData = ([i64; 8], Option<(Vec<u8>, u64)>);
|
||||
|
||||
pub enum TickResult {
|
||||
Ok,
|
||||
ExitThread(u64),
|
||||
PauseEmulation(Receiver<([i64; 8], Option<(Vec<u8>, u64)>)>),
|
||||
PauseEmulation(Receiver<ResponseData>),
|
||||
CpuTrap(Trap),
|
||||
}
|
||||
|
||||
@ -74,9 +76,8 @@ pub struct Cpu {
|
||||
pc: u64,
|
||||
csr: [u64; CSR_CAPACITY],
|
||||
mmu: Mmu,
|
||||
memory: Arc<RwLock<dyn Memory + Send + Sync>>,
|
||||
reservation: u64, // @TODO: Should support multiple address reservations
|
||||
is_reservation_set: bool,
|
||||
memory: Arc<Mutex<dyn Memory + Send + Sync>>,
|
||||
reservation: Option<u64>, // @TODO: Should support multiple address reservations
|
||||
_dump_flag: bool,
|
||||
// decode_cache: DecodeCache,
|
||||
unsigned_data_mask: u64,
|
||||
@ -127,7 +128,7 @@ pub enum TrapType {
|
||||
UserExternalInterrupt,
|
||||
SupervisorExternalInterrupt,
|
||||
MachineExternalInterrupt,
|
||||
PauseEmulation(std::sync::mpsc::Receiver<([i64; 8], Option<(Vec<u8>, u64)>)>),
|
||||
PauseEmulation(Receiver<ResponseData>),
|
||||
}
|
||||
|
||||
fn _get_privilege_mode_name(mode: &PrivilegeMode) -> &'static str {
|
||||
@ -225,11 +226,11 @@ pub struct CpuBuilder {
|
||||
xlen: Xlen,
|
||||
pc: u64,
|
||||
sp: u64,
|
||||
memory: Arc<RwLock<dyn Memory + Send + Sync>>,
|
||||
memory: Arc<Mutex<dyn Memory + Send + Sync>>,
|
||||
}
|
||||
|
||||
impl CpuBuilder {
|
||||
pub fn new(memory: Arc<RwLock<dyn Memory + Send + Sync>>) -> Self {
|
||||
pub fn new(memory: Arc<Mutex<dyn Memory + Send + Sync>>) -> Self {
|
||||
CpuBuilder {
|
||||
xlen: Xlen::Bit64,
|
||||
memory,
|
||||
@ -266,7 +267,7 @@ impl Cpu {
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `Terminal`
|
||||
pub fn new(memory: Arc<RwLock<dyn Memory + Send + Sync>>) -> Self {
|
||||
pub fn new(memory: Arc<Mutex<dyn Memory + Send + Sync>>) -> Self {
|
||||
Cpu {
|
||||
clock: 0,
|
||||
xlen: Xlen::Bit64,
|
||||
@ -277,8 +278,7 @@ impl Cpu {
|
||||
pc: 0,
|
||||
csr: [0; CSR_CAPACITY],
|
||||
mmu: Mmu::new(Xlen::Bit64, memory.clone()),
|
||||
reservation: 0,
|
||||
is_reservation_set: false,
|
||||
reservation: None,
|
||||
_dump_flag: false,
|
||||
// decode_cache: DecodeCache::new(),
|
||||
unsigned_data_mask: 0xffffffffffffffff,
|
||||
@ -563,11 +563,6 @@ impl Cpu {
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_exception(&mut self, exception: Trap, instruction_address: u64) {
|
||||
println!("!!! Exception Trap !!!: {:x?}", exception);
|
||||
self.handle_trap(exception, instruction_address, false);
|
||||
}
|
||||
|
||||
fn handle_trap(&mut self, trap: Trap, instruction_address: u64, is_interrupt: bool) -> bool {
|
||||
let current_privilege_encoding = get_privilege_encoding(&self.privilege_mode) as u64;
|
||||
let cause = get_trap_cause(&trap, &self.xlen);
|
||||
@ -2448,7 +2443,7 @@ const INSTRUCTIONS: [Instruction; INSTRUCTION_NUM] = [
|
||||
for (src, dest) in cpu.x[10..].iter().zip(args.iter_mut()) {
|
||||
*dest = *src;
|
||||
}
|
||||
match cpu.memory.write().unwrap().syscall(args) {
|
||||
match cpu.memory.lock().unwrap().syscall(args) {
|
||||
super::mmu::SyscallResult::Ok(result) => {
|
||||
for (src, dest) in result.iter().zip(cpu.x[10..].iter_mut()) {
|
||||
*dest = *src;
|
||||
@ -2938,14 +2933,11 @@ const INSTRUCTIONS: [Instruction; INSTRUCTION_NUM] = [
|
||||
operation: |cpu, word, _address| {
|
||||
let f = parse_format_r(word);
|
||||
// @TODO: Implement properly
|
||||
cpu.x[f.rd] = match cpu.mmu.load_doubleword(cpu.x[f.rs1] as u64) {
|
||||
Ok(data) => {
|
||||
cpu.is_reservation_set = true;
|
||||
cpu.reservation = cpu.x[f.rs1] as u64; // Is virtual address ok?
|
||||
data as i64
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
let address = cpu.x[f.rs1] as u64;
|
||||
cpu.x[f.rd] = cpu.mmu.load_doubleword(address)? as i64;
|
||||
if cpu.mmu.reserve(address) {
|
||||
cpu.reservation = Some(address);
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
disassemble: dump_format_r,
|
||||
@ -2957,14 +2949,11 @@ const INSTRUCTIONS: [Instruction; INSTRUCTION_NUM] = [
|
||||
operation: |cpu, word, _address| {
|
||||
let f = parse_format_r(word);
|
||||
// @TODO: Implement properly
|
||||
cpu.x[f.rd] = match cpu.mmu.load_word(cpu.x[f.rs1] as u64) {
|
||||
Ok(data) => {
|
||||
cpu.is_reservation_set = true;
|
||||
cpu.reservation = cpu.x[f.rs1] as u64; // Is virtual address ok?
|
||||
data as i32 as i64
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
let address = cpu.x[f.rs1] as u64;
|
||||
cpu.x[f.rd] = cpu.mmu.load_word(address)? as i64;
|
||||
if cpu.mmu.reserve(address) {
|
||||
cpu.reservation = Some(address);
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
disassemble: dump_format_r,
|
||||
@ -3223,19 +3212,14 @@ const INSTRUCTIONS: [Instruction; INSTRUCTION_NUM] = [
|
||||
operation: |cpu, word, _address| {
|
||||
let f = parse_format_r(word);
|
||||
// @TODO: Implement properly
|
||||
cpu.x[f.rd] = match cpu.is_reservation_set && cpu.reservation == (cpu.x[f.rs1] as u64) {
|
||||
true => match cpu
|
||||
.mmu
|
||||
.store_doubleword(cpu.x[f.rs1] as u64, cpu.x[f.rs2] as u64)
|
||||
{
|
||||
Ok(()) => {
|
||||
cpu.is_reservation_set = false;
|
||||
0
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
},
|
||||
false => 1,
|
||||
};
|
||||
let address = cpu.x[f.rs1] as u64;
|
||||
if Some(address) == cpu.reservation.take() {
|
||||
cpu.mmu.store_doubleword(address, cpu.x[f.rs2] as u64)?;
|
||||
cpu.mmu.clear_reservation(address);
|
||||
cpu.x[f.rd] = 0;
|
||||
return Ok(());
|
||||
}
|
||||
cpu.x[f.rd] = 1;
|
||||
Ok(())
|
||||
},
|
||||
disassemble: dump_format_r,
|
||||
@ -3247,16 +3231,14 @@ const INSTRUCTIONS: [Instruction; INSTRUCTION_NUM] = [
|
||||
operation: |cpu, word, _address| {
|
||||
let f = parse_format_r(word);
|
||||
// @TODO: Implement properly
|
||||
cpu.x[f.rd] = match cpu.is_reservation_set && cpu.reservation == (cpu.x[f.rs1] as u64) {
|
||||
true => match cpu.mmu.store_word(cpu.x[f.rs1] as u64, cpu.x[f.rs2] as u32) {
|
||||
Ok(()) => {
|
||||
cpu.is_reservation_set = false;
|
||||
0
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
},
|
||||
false => 1,
|
||||
};
|
||||
let address = cpu.x[f.rs1] as u64;
|
||||
if Some(address) == cpu.reservation.take() {
|
||||
cpu.mmu.clear_reservation(address);
|
||||
cpu.mmu.store_word(address, cpu.x[f.rs2] as u32)?;
|
||||
cpu.x[f.rd] = 0;
|
||||
return Ok(());
|
||||
}
|
||||
cpu.x[f.rd] = 1;
|
||||
Ok(())
|
||||
},
|
||||
disassemble: dump_format_r,
|
||||
@ -4144,77 +4126,3 @@ mod test_cpu {
|
||||
assert_eq!(memory_base, cpu.read_pc());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
mod test_decode_cache {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn initialize() {
|
||||
let _cache = DecodeCache::new();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert() {
|
||||
let mut cache = DecodeCache::new();
|
||||
cache.insert(0, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get() {
|
||||
let mut cache = DecodeCache::new();
|
||||
cache.insert(1, 2);
|
||||
|
||||
// Cache hit test
|
||||
match cache.get(1) {
|
||||
Some(index) => assert_eq!(2, index),
|
||||
None => panic!("Unexpected cache miss"),
|
||||
};
|
||||
|
||||
// Cache miss test
|
||||
match cache.get(2) {
|
||||
Some(_index) => panic!("Unexpected cache hit"),
|
||||
None => {}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lru() {
|
||||
let mut cache = DecodeCache::new();
|
||||
cache.insert(0, 1);
|
||||
|
||||
match cache.get(0) {
|
||||
Some(index) => assert_eq!(1, index),
|
||||
None => panic!("Unexpected cache miss"),
|
||||
};
|
||||
|
||||
for i in 1..DECODE_CACHE_ENTRY_NUM + 1 {
|
||||
cache.insert(i as u32, i + 1);
|
||||
}
|
||||
|
||||
// The oldest entry should have been removed because of the overflow
|
||||
match cache.get(0) {
|
||||
Some(_index) => panic!("Unexpected cache hit"),
|
||||
None => {}
|
||||
};
|
||||
|
||||
// With this .get(), the entry with the word "1" moves to the tail of the list
|
||||
// and the entry with the word "2" becomes the oldest entry.
|
||||
match cache.get(1) {
|
||||
Some(index) => assert_eq!(2, index),
|
||||
None => {}
|
||||
};
|
||||
|
||||
// The oldest entry with the word "2" will be removed due to the overflow
|
||||
cache.insert(
|
||||
DECODE_CACHE_ENTRY_NUM as u32 + 1,
|
||||
DECODE_CACHE_ENTRY_NUM + 2,
|
||||
);
|
||||
|
||||
match cache.get(2) {
|
||||
Some(_index) => panic!("Unexpected cache hit"),
|
||||
None => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
pub mod cpu;
|
||||
pub mod memory;
|
||||
pub mod mmu;
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod memory;
|
||||
|
||||
pub use cpu::{Cpu, CpuBuilder, Xlen};
|
||||
|
@ -1,6 +1,5 @@
|
||||
/// Emulates main memory.
|
||||
pub struct Memory {
|
||||
/// Memory content
|
||||
pub struct Memory { /// Memory content
|
||||
data: Vec<u64>,
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Arc, RwLock},
|
||||
sync::mpsc::Receiver,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use crate::cpu::{decode_privilege_mode, PrivilegeMode, Trap, TrapType, Xlen};
|
||||
use crate::cpu::{decode_privilege_mode, PrivilegeMode, ResponseData, Trap, TrapType, Xlen};
|
||||
|
||||
pub enum SyscallResult {
|
||||
Ok([i64; 8]),
|
||||
Defer(std::sync::mpsc::Receiver<([i64; 8], Option<(Vec<u8>, u64)>)>),
|
||||
Defer(Receiver<ResponseData>),
|
||||
}
|
||||
|
||||
impl From<[i64; 8]> for SyscallResult {
|
||||
@ -16,8 +16,8 @@ impl From<[i64; 8]> for SyscallResult {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::sync::mpsc::Receiver<([i64; 8], Option<(Vec<u8>, u64)>)>> for SyscallResult {
|
||||
fn from(receiver: std::sync::mpsc::Receiver<([i64; 8], Option<(Vec<u8>, u64)>)>) -> Self {
|
||||
impl From<std::sync::mpsc::Receiver<ResponseData>> for SyscallResult {
|
||||
fn from(receiver: std::sync::mpsc::Receiver<ResponseData>) -> Self {
|
||||
SyscallResult::Defer(receiver)
|
||||
}
|
||||
}
|
||||
@ -33,6 +33,9 @@ pub trait Memory {
|
||||
fn write_u64(&mut self, p_address: u64, value: u64);
|
||||
fn validate_address(&self, address: u64) -> bool;
|
||||
fn syscall(&mut self, args: [i64; 8]) -> SyscallResult;
|
||||
fn translate(&self, v_address: u64) -> Option<u64>;
|
||||
fn reserve(&mut self, p_address: u64) -> bool;
|
||||
fn clear_reservation(&mut self, p_address: u64);
|
||||
}
|
||||
|
||||
/// Emulates Memory Management Unit. It holds the Main memory and peripheral
|
||||
@ -46,29 +49,13 @@ pub struct Mmu {
|
||||
ppn: u64,
|
||||
addressing_mode: AddressingMode,
|
||||
privilege_mode: PrivilegeMode,
|
||||
memory: Arc<RwLock<dyn Memory + Send + Sync>>,
|
||||
memory: Arc<Mutex<dyn Memory + Send + Sync>>,
|
||||
|
||||
// /// The size of main memory (if initialized)
|
||||
// memory_length: Option<NonZeroU64>,
|
||||
/// Address translation can be affected `mstatus` (MPRV, MPP in machine mode)
|
||||
/// then `Mmu` has copy of it.
|
||||
mstatus: u64,
|
||||
|
||||
/// Address translation page cache. Experimental feature.
|
||||
/// The cache is cleared when translation mapping can be changed;
|
||||
/// xlen, ppn, privilege_mode, or addressing_mode is updated.
|
||||
/// Precisely it isn't good enough because page table entries
|
||||
/// can be updated anytime with store instructions, of course
|
||||
/// very depending on how pages are mapped tho.
|
||||
/// But observing all page table entries is high cost so
|
||||
/// ignoring so far. Then this cache optimization can cause a bug
|
||||
/// due to unexpected (meaning not in page fault handler)
|
||||
/// page table entry update. So this is experimental feature and
|
||||
/// disabled by default. If you want to enable, use `enable_page_cache()`.
|
||||
page_cache_enabled: bool,
|
||||
fetch_page_cache: HashMap<u64, u64>,
|
||||
load_page_cache: HashMap<u64, u64>,
|
||||
store_page_cache: HashMap<u64, u64>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -100,7 +87,7 @@ impl Mmu {
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `xlen`
|
||||
pub fn new(xlen: Xlen, memory: Arc<RwLock<dyn Memory + Send + Sync>>) -> Self {
|
||||
pub fn new(xlen: Xlen, memory: Arc<Mutex<dyn Memory + Send + Sync>>) -> Self {
|
||||
Mmu {
|
||||
// clock: 0,
|
||||
xlen,
|
||||
@ -109,10 +96,6 @@ impl Mmu {
|
||||
privilege_mode: PrivilegeMode::Machine,
|
||||
memory,
|
||||
mstatus: 0,
|
||||
page_cache_enabled: false,
|
||||
fetch_page_cache: HashMap::default(),
|
||||
load_page_cache: HashMap::default(),
|
||||
store_page_cache: HashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,37 +105,6 @@ impl Mmu {
|
||||
/// * `xlen`
|
||||
pub fn update_xlen(&mut self, xlen: Xlen) {
|
||||
self.xlen = xlen;
|
||||
self.clear_page_cache();
|
||||
}
|
||||
|
||||
// /// Initializes Main memory. This method is expected to be called only once.
|
||||
// ///
|
||||
// /// # Arguments
|
||||
// /// * `capacity`
|
||||
// pub fn init_memory(&mut self, capacity: u64) {
|
||||
// assert!(self.memory_length.is_none());
|
||||
// self.memory_length = Some(NonZeroU64::new(capacity).unwrap());
|
||||
// self.memory.init(capacity);
|
||||
// }
|
||||
|
||||
// pub fn memory_size(&self) -> u64 {
|
||||
// self.memory_length.unwrap().get()
|
||||
// }
|
||||
|
||||
/// Enables or disables page cache optimization.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `enabled`
|
||||
pub fn enable_page_cache(&mut self, enabled: bool) {
|
||||
self.page_cache_enabled = enabled;
|
||||
self.clear_page_cache();
|
||||
}
|
||||
|
||||
/// Clears page cache entries
|
||||
fn clear_page_cache(&mut self) {
|
||||
self.fetch_page_cache.clear();
|
||||
self.load_page_cache.clear();
|
||||
self.store_page_cache.clear();
|
||||
}
|
||||
|
||||
/// Runs one cycle of MMU and peripheral devices.
|
||||
@ -164,7 +116,6 @@ impl Mmu {
|
||||
/// * `new_addressing_mode`
|
||||
pub fn update_addressing_mode(&mut self, new_addressing_mode: AddressingMode) {
|
||||
self.addressing_mode = new_addressing_mode;
|
||||
self.clear_page_cache();
|
||||
}
|
||||
|
||||
/// Updates privilege mode
|
||||
@ -173,7 +124,6 @@ impl Mmu {
|
||||
/// * `mode`
|
||||
pub fn update_privilege_mode(&mut self, mode: PrivilegeMode) {
|
||||
self.privilege_mode = mode;
|
||||
self.clear_page_cache();
|
||||
}
|
||||
|
||||
/// Updates mstatus copy. `CPU` needs to call this method whenever
|
||||
@ -191,7 +141,6 @@ impl Mmu {
|
||||
/// * `ppn`
|
||||
pub fn update_ppn(&mut self, ppn: u64) {
|
||||
self.ppn = ppn;
|
||||
self.clear_page_cache();
|
||||
}
|
||||
|
||||
fn trim_to_xlen(&self, address: u64) -> u64 {
|
||||
@ -426,7 +375,7 @@ impl Mmu {
|
||||
/// * `p_address` Physical address
|
||||
pub(crate) fn load_raw(&self, p_address: u64) -> u8 {
|
||||
self.memory
|
||||
.read()
|
||||
.lock() // .read()
|
||||
.unwrap()
|
||||
.read_u8(self.trim_to_xlen(p_address))
|
||||
}
|
||||
@ -438,7 +387,7 @@ impl Mmu {
|
||||
/// * `p_address` Physical address
|
||||
fn load_halfword_raw(&self, p_address: u64) -> u16 {
|
||||
self.memory
|
||||
.read()
|
||||
.lock() // .read()
|
||||
.unwrap()
|
||||
.read_u16(self.trim_to_xlen(p_address))
|
||||
}
|
||||
@ -450,7 +399,7 @@ impl Mmu {
|
||||
/// * `p_address` Physical address
|
||||
pub fn load_word_raw(&self, p_address: u64) -> u32 {
|
||||
self.memory
|
||||
.read()
|
||||
.lock() // .read()
|
||||
.unwrap()
|
||||
.read_u32(self.trim_to_xlen(p_address))
|
||||
}
|
||||
@ -462,7 +411,7 @@ impl Mmu {
|
||||
/// * `p_address` Physical address
|
||||
fn load_doubleword_raw(&self, p_address: u64) -> u64 {
|
||||
self.memory
|
||||
.read()
|
||||
.lock() // .read()
|
||||
.unwrap()
|
||||
.read_u64(self.trim_to_xlen(p_address))
|
||||
}
|
||||
@ -475,7 +424,7 @@ impl Mmu {
|
||||
/// * `value` data written
|
||||
pub(crate) fn store_raw(&self, p_address: u64, value: u8) {
|
||||
self.memory
|
||||
.write()
|
||||
.lock() // .write()
|
||||
.unwrap()
|
||||
.write_u8(self.trim_to_xlen(p_address), value)
|
||||
}
|
||||
@ -488,7 +437,7 @@ impl Mmu {
|
||||
/// * `value` data written
|
||||
pub(crate) fn store_halfword_raw(&self, p_address: u64, value: u16) {
|
||||
self.memory
|
||||
.write()
|
||||
.lock() // .write()
|
||||
.unwrap()
|
||||
.write_u16(self.trim_to_xlen(p_address), value)
|
||||
}
|
||||
@ -501,7 +450,7 @@ impl Mmu {
|
||||
/// * `value` data written
|
||||
pub(crate) fn store_word_raw(&self, p_address: u64, value: u32) {
|
||||
self.memory
|
||||
.write()
|
||||
.lock() // .write()
|
||||
.unwrap()
|
||||
.write_u32(self.trim_to_xlen(p_address), value)
|
||||
}
|
||||
@ -514,7 +463,7 @@ impl Mmu {
|
||||
/// * `value` data written
|
||||
fn store_doubleword_raw(&self, p_address: u64, value: u64) {
|
||||
self.memory
|
||||
.write()
|
||||
.lock() // .write()
|
||||
.unwrap()
|
||||
.write_u64(self.trim_to_xlen(p_address), value)
|
||||
}
|
||||
@ -529,14 +478,38 @@ impl Mmu {
|
||||
.ok()
|
||||
.map(|p_address| {
|
||||
self.memory
|
||||
.write()
|
||||
.lock() // .read()
|
||||
.unwrap()
|
||||
.validate_address(self.trim_to_xlen(p_address))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn reserve(&mut self, p_address: u64) -> bool {
|
||||
self.memory
|
||||
.lock() // .write()
|
||||
.unwrap()
|
||||
.reserve(self.trim_to_xlen(p_address))
|
||||
}
|
||||
|
||||
pub fn clear_reservation(&mut self, p_address: u64) {
|
||||
self.memory
|
||||
.lock() // .write()
|
||||
.unwrap()
|
||||
.clear_reservation(self.trim_to_xlen(p_address))
|
||||
}
|
||||
|
||||
fn translate_address(&self, v_address: u64, access_type: &MemoryAccessType) -> Result<u64, ()> {
|
||||
self.translate_address_with_privilege_mode(v_address, access_type, self.privilege_mode)
|
||||
if let AddressingMode::None = self.addressing_mode {
|
||||
Ok(v_address)
|
||||
} else {
|
||||
// self.memory.lock() // .read().unwrap().translate(v_address).ok_or(())
|
||||
let phys = self.translate_address_with_privilege_mode(
|
||||
v_address,
|
||||
access_type,
|
||||
self.privilege_mode,
|
||||
)?;
|
||||
Ok(phys)
|
||||
}
|
||||
}
|
||||
|
||||
fn translate_address_with_privilege_mode(
|
||||
@ -546,18 +519,6 @@ impl Mmu {
|
||||
privilege_mode: PrivilegeMode,
|
||||
) -> Result<u64, ()> {
|
||||
let address = self.trim_to_xlen(v_address);
|
||||
let v_page = address & !0xfff;
|
||||
if let Some(p_page) = match self.page_cache_enabled {
|
||||
true => match access_type {
|
||||
MemoryAccessType::Execute => self.fetch_page_cache.get(&v_page),
|
||||
MemoryAccessType::Read => self.load_page_cache.get(&v_page),
|
||||
MemoryAccessType::Write => self.store_page_cache.get(&v_page),
|
||||
MemoryAccessType::DontCare => None,
|
||||
},
|
||||
false => None,
|
||||
} {
|
||||
return Ok(p_page | (address & 0xfff));
|
||||
}
|
||||
|
||||
match self.addressing_mode {
|
||||
AddressingMode::None => Ok(address),
|
||||
@ -618,24 +579,6 @@ impl Mmu {
|
||||
panic!("AddressingMode SV48 is not supported yet.");
|
||||
}
|
||||
}
|
||||
|
||||
// if self.page_cache_enabled {
|
||||
// match p_address {
|
||||
// Ok(p_address) => {
|
||||
// let p_page = p_address & !0xfff;
|
||||
// match access_type {
|
||||
// MemoryAccessType::Execute => self.fetch_page_cache.insert(v_page, p_page),
|
||||
// MemoryAccessType::Read => self.load_page_cache.insert(v_page, p_page),
|
||||
// MemoryAccessType::Write => self.store_page_cache.insert(v_page, p_page),
|
||||
// MemoryAccessType::DontCare => None,
|
||||
// };
|
||||
// Ok(p_address)
|
||||
// }
|
||||
// Err(()) => Err(()),
|
||||
// }
|
||||
// } else {
|
||||
// p_address
|
||||
// }
|
||||
}
|
||||
|
||||
fn traverse_page(
|
||||
@ -766,99 +709,3 @@ impl Mmu {
|
||||
Ok(p_address)
|
||||
}
|
||||
}
|
||||
|
||||
// pub struct MemoryWrapper {
|
||||
// memory: Memory,
|
||||
// dram_base: u64,
|
||||
// }
|
||||
|
||||
// impl MemoryWrapper {
|
||||
// fn new(dram_base: u64) -> Self {
|
||||
// MemoryWrapper {
|
||||
// memory: Memory::new(),
|
||||
// dram_base,
|
||||
// }
|
||||
// }
|
||||
|
||||
// fn init(&mut self, capacity: u64) {
|
||||
// self.memory.init(capacity);
|
||||
// }
|
||||
|
||||
// pub fn read_byte(&self, p_address: u64) -> u8 {
|
||||
// debug_assert!(
|
||||
// p_address >= self.dram_base,
|
||||
// "Memory address must equals to or bigger than self.dram_base. {:X}",
|
||||
// p_address
|
||||
// );
|
||||
// self.memory.read_byte(p_address - self.dram_base)
|
||||
// }
|
||||
|
||||
// pub fn read_halfword(&self, p_address: u64) -> u16 {
|
||||
// debug_assert!(
|
||||
// p_address >= self.dram_base && p_address.wrapping_add(1) >= self.dram_base,
|
||||
// "Memory address must equals to or bigger than self.dram_base. {:X}",
|
||||
// p_address
|
||||
// );
|
||||
// self.memory.read_halfword(p_address - self.dram_base)
|
||||
// }
|
||||
|
||||
// pub fn read_word(&self, p_address: u64) -> u32 {
|
||||
// debug_assert!(
|
||||
// p_address >= self.dram_base && p_address.wrapping_add(3) >= self.dram_base,
|
||||
// "Memory address must equals to or bigger than self.dram_base. {:X}",
|
||||
// p_address
|
||||
// );
|
||||
// self.memory.read_word(p_address - self.dram_base)
|
||||
// }
|
||||
|
||||
// pub fn read_doubleword(&self, p_address: u64) -> u64 {
|
||||
// debug_assert!(
|
||||
// p_address >= self.dram_base && p_address.wrapping_add(7) >= self.dram_base,
|
||||
// "Memory address must equals to or bigger than self.dram_base. {:X}",
|
||||
// p_address
|
||||
// );
|
||||
// self.memory.read_doubleword(p_address - self.dram_base)
|
||||
// }
|
||||
|
||||
// pub fn write_byte(&mut self, p_address: u64, value: u8) {
|
||||
// debug_assert!(
|
||||
// p_address >= self.dram_base,
|
||||
// "Memory address must equals to or bigger than self.dram_base. {:X}",
|
||||
// p_address
|
||||
// );
|
||||
// self.memory.write_byte(p_address - self.dram_base, value)
|
||||
// }
|
||||
|
||||
// pub fn write_halfword(&mut self, p_address: u64, value: u16) {
|
||||
// debug_assert!(
|
||||
// p_address >= self.dram_base && p_address.wrapping_add(1) >= self.dram_base,
|
||||
// "Memory address must equals to or bigger than self.dram_base. {:X}",
|
||||
// p_address
|
||||
// );
|
||||
// self.memory
|
||||
// .write_halfword(p_address - self.dram_base, value)
|
||||
// }
|
||||
|
||||
// pub fn write_word(&mut self, p_address: u64, value: u32) {
|
||||
// debug_assert!(
|
||||
// p_address >= self.dram_base && p_address.wrapping_add(3) >= self.dram_base,
|
||||
// "Memory address must equals to or bigger than self.dram_base. {:X}",
|
||||
// p_address
|
||||
// );
|
||||
// self.memory.write_word(p_address - self.dram_base, value)
|
||||
// }
|
||||
|
||||
// pub fn write_doubleword(&mut self, p_address: u64, value: u64) {
|
||||
// debug_assert!(
|
||||
// p_address >= self.dram_base && p_address.wrapping_add(7) >= self.dram_base,
|
||||
// "Memory address must equals to or bigger than self.dram_base. {:X}",
|
||||
// p_address
|
||||
// );
|
||||
// self.memory
|
||||
// .write_doubleword(p_address - self.dram_base, value)
|
||||
// }
|
||||
|
||||
// pub fn validate_address(&self, address: u64) -> bool {
|
||||
// self.memory.validate_address(address - self.dram_base)
|
||||
// }
|
||||
// }
|
||||
|
437
src/xous.rs
437
src/xous.rs
@ -8,11 +8,17 @@ use std::{
|
||||
sync::{
|
||||
atomic::{AtomicI64, Ordering},
|
||||
mpsc::{Receiver, Sender},
|
||||
Arc, RwLock,
|
||||
Arc, Mutex,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::xous::definitions::SyscallErrorNumber;
|
||||
|
||||
const MEMORY_BASE: u32 = 0x8000_0000;
|
||||
const ALLOCATION_START: u32 = 0x4000_0000;
|
||||
const ALLOCATION_END: u32 = ALLOCATION_START + 5 * 1024 * 1024;
|
||||
const HEAP_START: u32 = 0xa000_0000;
|
||||
const HEAP_END: u32 = HEAP_START + 5 * 1024 * 1024;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum LoadError {
|
||||
@ -45,6 +51,7 @@ const MMUFLAG_ACCESSED: u32 = 0x40;
|
||||
const MMUFLAG_DIRTY: u32 = 0x80;
|
||||
|
||||
impl std::error::Error for LoadError {}
|
||||
pub type ResponseData = ([i64; 8], Option<(Vec<u8>, u64)>);
|
||||
|
||||
enum MemoryCommand {
|
||||
Exit,
|
||||
@ -59,30 +66,14 @@ enum MemoryCommand {
|
||||
u32, /* argument 4 */
|
||||
Sender<i64>, /* Thread ID */
|
||||
),
|
||||
JoinThread(u32, Sender<([i64; 8], Option<(Vec<u8>, u64)>)>),
|
||||
}
|
||||
|
||||
struct Memory {
|
||||
base: u32,
|
||||
data: HashMap<usize, [u8; 4096]>,
|
||||
allocated_pages: BTreeSet<usize>,
|
||||
free_pages: BTreeSet<usize>,
|
||||
heap_start: u32,
|
||||
heap_size: u32,
|
||||
// allocation_start: u32,
|
||||
allocation_previous: u32,
|
||||
l1_pt: u32,
|
||||
satp: u32,
|
||||
connections: HashMap<u32, Box<dyn services::Service + Send + Sync>>,
|
||||
memory_cmd: Sender<MemoryCommand>,
|
||||
turbo: bool,
|
||||
JoinThread(u32, Sender<ResponseData>),
|
||||
}
|
||||
|
||||
struct Worker {
|
||||
cpu: riscv_cpu::Cpu,
|
||||
cmd: Sender<MemoryCommand>,
|
||||
tid: i64,
|
||||
memory: Arc<RwLock<Memory>>,
|
||||
memory: Arc<Mutex<Memory>>,
|
||||
}
|
||||
|
||||
impl Worker {
|
||||
@ -90,7 +81,7 @@ impl Worker {
|
||||
cpu: riscv_cpu::Cpu,
|
||||
cmd: Sender<MemoryCommand>,
|
||||
tid: i64,
|
||||
memory: Arc<RwLock<Memory>>,
|
||||
memory: Arc<Mutex<Memory>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
cpu,
|
||||
@ -126,7 +117,7 @@ impl Worker {
|
||||
return;
|
||||
}
|
||||
TickResult::CpuTrap(trap) => {
|
||||
self.memory.read().unwrap().print_mmu();
|
||||
self.memory.lock().unwrap().print_mmu();
|
||||
// called `Result::unwrap()` on an `Err` value: "Valid bit is 0, or read is 0 and write is 1 at 40002fec: 000802e6"
|
||||
println!(
|
||||
"CPU trap at PC {:08x}, exiting thread {}: {:x?}",
|
||||
@ -149,18 +140,39 @@ struct WorkerHandle {
|
||||
joiner: std::thread::JoinHandle<()>,
|
||||
}
|
||||
|
||||
struct Memory {
|
||||
base: u32,
|
||||
data: HashMap<usize, [u8; 4096]>,
|
||||
allocated_pages: BTreeSet<usize>,
|
||||
free_pages: BTreeSet<usize>,
|
||||
heap_start: u32,
|
||||
heap_size: u32,
|
||||
// allocation_start: u32,
|
||||
allocation_previous: u32,
|
||||
l1_pt: u32,
|
||||
satp: u32,
|
||||
connections: HashMap<u32, Box<dyn services::Service + Send + Sync>>,
|
||||
memory_cmd: Sender<MemoryCommand>,
|
||||
translation_cache: HashMap<u32, u32>,
|
||||
allocated_bytes: u32,
|
||||
reservations: HashSet<u32>,
|
||||
}
|
||||
|
||||
impl Memory {
|
||||
pub fn new(base: u32, size: usize) -> (Self, Receiver<MemoryCommand>) {
|
||||
let mut backing = HashMap::new();
|
||||
let mut free_pages = BTreeSet::new();
|
||||
let mut allocated_pages = BTreeSet::new();
|
||||
|
||||
// Populate the backing table as well as the list of free pages
|
||||
for phys in (base..(base + size as u32)).step_by(4096) {
|
||||
backing.insert(phys as usize, [0; 4096]);
|
||||
free_pages.insert(phys as usize);
|
||||
}
|
||||
// Remove the l0 page table
|
||||
// Allocate the l0 page table
|
||||
assert!(free_pages.remove(&(MEMORY_BASE as usize + 4096)));
|
||||
assert!(allocated_pages.insert(MEMORY_BASE as usize + 4096));
|
||||
|
||||
let (memory_cmd, memory_cmd_rx) = std::sync::mpsc::channel();
|
||||
(
|
||||
Self {
|
||||
@ -170,79 +182,81 @@ impl Memory {
|
||||
free_pages,
|
||||
l1_pt: MEMORY_BASE + 4096,
|
||||
satp: ((4096 + MEMORY_BASE) >> 12) | 0x8000_0000,
|
||||
heap_start: 0x6000_0000,
|
||||
heap_start: HEAP_START,
|
||||
heap_size: 0,
|
||||
allocation_previous: 0x4000_0000,
|
||||
allocation_previous: ALLOCATION_START,
|
||||
connections: HashMap::new(),
|
||||
memory_cmd,
|
||||
turbo: false,
|
||||
translation_cache: HashMap::new(),
|
||||
allocated_bytes: 4096,
|
||||
reservations: HashSet::new(),
|
||||
},
|
||||
memory_cmd_rx,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn turbo(&mut self) {
|
||||
self.turbo = true;
|
||||
}
|
||||
// fn memory_ck(&self) {
|
||||
// if self.turbo {
|
||||
// return;
|
||||
// }
|
||||
// let mut seen_pages = HashMap::new();
|
||||
// seen_pages.insert(self.l1_pt, 0);
|
||||
// for vpn1 in 0..1024 {
|
||||
// let l1_entry = self.read_u32(self.l1_pt as u64 + vpn1 * 4);
|
||||
// if l1_entry & MMUFLAG_VALID == 0 {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
pub fn normal(&mut self) {
|
||||
self.turbo = false;
|
||||
self.memory_ck();
|
||||
}
|
||||
// let superpage_addr = vpn1 as u32 * (1 << 22);
|
||||
|
||||
fn memory_ck(&self) {
|
||||
// if self.turbo {
|
||||
// return;
|
||||
// }
|
||||
// let mut seen_pages = HashMap::new();
|
||||
// seen_pages.insert(self.l1_pt, 0);
|
||||
// for vpn1 in 0..1024 {
|
||||
// let l1_entry = self.read_u32(self.l1_pt as u64 + vpn1 * 4);
|
||||
// if l1_entry & MMUFLAG_VALID == 0 {
|
||||
// continue;
|
||||
// }
|
||||
// for vpn0 in 0..1024 {
|
||||
// let l0_entry = self.read_u32((((l1_entry >> 10) << 12) as u64) + vpn0 as u64 * 4);
|
||||
// if l0_entry & 0x1 == 0 {
|
||||
// continue;
|
||||
// }
|
||||
// let phys = (l0_entry >> 10) << 12;
|
||||
// let current = superpage_addr + vpn0 as u32 * (1 << 12);
|
||||
// if let Some(existing) = seen_pages.get(&phys) {
|
||||
// self.print_mmu();
|
||||
// panic!(
|
||||
// "Error! Page {:08x} is mapped twice! Once at {:08x} and once at {:08x}",
|
||||
// phys, existing, current,
|
||||
// );
|
||||
// }
|
||||
// seen_pages.insert(phys, current);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// let superpage_addr = vpn1 as u32 * (1 << 22);
|
||||
|
||||
// for vpn0 in 0..1024 {
|
||||
// let l0_entry = self.read_u32((((l1_entry >> 10) << 12) as u64) + vpn0 as u64 * 4);
|
||||
// if l0_entry & 0x1 == 0 {
|
||||
// continue;
|
||||
// }
|
||||
// let phys = (l0_entry >> 10) << 12;
|
||||
// let current = superpage_addr + vpn0 as u32 * (1 << 12);
|
||||
// if let Some(existing) = seen_pages.get(&phys) {
|
||||
// self.print_mmu();
|
||||
// panic!(
|
||||
// "Error! Page {:08x} is mapped twice! Once at {:08x} and once at {:08x}",
|
||||
// phys, existing, current,
|
||||
// );
|
||||
// }
|
||||
// seen_pages.insert(phys, current);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
fn allocate_page(&mut self) -> u32 {
|
||||
self.memory_ck();
|
||||
let phys = self.free_pages.pop_first().expect("out of memory");
|
||||
/// Allocate a physical page from RAM.
|
||||
fn allocate_phys_page(&mut self) -> Option<u32> {
|
||||
let Some(phys) = self.free_pages.pop_first() else {
|
||||
// panic!(
|
||||
// "out of memory when attempting to allocate a page. There are {} bytes allocated.",
|
||||
// self.allocated_bytes
|
||||
// );
|
||||
return None;
|
||||
};
|
||||
assert!(self.allocated_pages.insert(phys));
|
||||
self.allocated_bytes += 4096;
|
||||
|
||||
// The root (l1) pagetable is defined to be mapped into our virtual
|
||||
// address space at this address.
|
||||
if phys == 0 {
|
||||
panic!("Attempt to allocate zero page");
|
||||
}
|
||||
self.memory_ck();
|
||||
phys as u32
|
||||
Some(phys as u32)
|
||||
}
|
||||
|
||||
fn free_page(&mut self, virt: u32) -> Result<(), ()> {
|
||||
self.memory_ck();
|
||||
let phys = self.virt_to_phys(virt).ok_or(())?;
|
||||
fn free_virt_page(&mut self, virt: u32) -> Result<(), ()> {
|
||||
let phys = self
|
||||
.virt_to_phys(virt)
|
||||
.ok_or(())
|
||||
.expect("tried to free a page that was allocated");
|
||||
|
||||
let vpn1 = ((virt >> 22) & ((1 << 10) - 1)) as usize * 4;
|
||||
let vpn0 = ((virt >> 12) & ((1 << 10) - 1)) as usize * 4;
|
||||
self.allocated_bytes -= 4096;
|
||||
|
||||
// The root (l1) pagetable is defined to be mapped into our virtual
|
||||
// address space at this address.
|
||||
@ -250,68 +264,123 @@ impl Memory {
|
||||
// If the level 1 pagetable doesn't exist, then this address is invalid
|
||||
let l1_pt_entry = self.read_u32(self.l1_pt as u64 + vpn1 as u64);
|
||||
if l1_pt_entry & MMUFLAG_VALID == 0 {
|
||||
return Ok(());
|
||||
panic!("Tried to free a page where the level 1 pagetable didn't exist");
|
||||
}
|
||||
|
||||
// println!("Deallocating page {:08x} @ {:08x}", virt, phys);
|
||||
if !self.allocated_pages.remove(&(phys as usize)) {
|
||||
// self.print_mmu();
|
||||
panic!(
|
||||
"Page {:08x} @ {:08x} wasn't in the list of allocated pages!",
|
||||
phys, virt
|
||||
);
|
||||
}
|
||||
assert!(self.allocated_pages.remove(&(phys as usize)));
|
||||
assert!(self.free_pages.insert(phys as usize));
|
||||
assert!(self.translation_cache.remove(&virt).is_some());
|
||||
|
||||
let l0_pt_phys = ((l1_pt_entry >> 10) << 12) + vpn0 as u32;
|
||||
assert!(self.read_u32(l0_pt_phys as u64) & MMUFLAG_VALID != 0);
|
||||
self.write_u32(l0_pt_phys as u64, 0);
|
||||
self.memory_ck();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn allocate_virt_region(&mut self, size: usize) -> Option<u32> {
|
||||
self.memory_ck();
|
||||
let mut start = self.allocation_previous;
|
||||
// Find a free region that will fit this page.
|
||||
'outer: loop {
|
||||
for page in (start..(start + size as u32)).step_by(4096) {
|
||||
if self.virt_to_phys(page).is_some() {
|
||||
start = page + 4096;
|
||||
continue 'outer;
|
||||
let size = size as u32;
|
||||
// Look for a sequence of `size` pages that are free.
|
||||
let mut address = None;
|
||||
for potential_start in (self.allocation_previous..ALLOCATION_END - size)
|
||||
.step_by(4096)
|
||||
.chain((ALLOCATION_START..self.allocation_previous - size).step_by(4096))
|
||||
{
|
||||
let mut all_free = true;
|
||||
for check_page in (potential_start..potential_start + size).step_by(4096) {
|
||||
if self.virt_to_phys(check_page).is_some() {
|
||||
all_free = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
if all_free {
|
||||
self.allocation_previous = potential_start + size;
|
||||
address = Some(potential_start);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Allocate the region
|
||||
for page in (start..(start + size as u32)).step_by(4096) {
|
||||
self.ensure_page(page);
|
||||
// println!(
|
||||
// "Allocated {:08x} @ {:08x}",
|
||||
// page,
|
||||
// self.virt_to_phys(page).unwrap()
|
||||
// );
|
||||
if let Some(address) = address {
|
||||
let mut error_mark = None;
|
||||
for page in (address..(address + size)).step_by(4096) {
|
||||
if self.ensure_page(page).is_none() {
|
||||
error_mark = Some(page);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let Some(error_mark) = error_mark {
|
||||
for page in (address..error_mark).step_by(4096) {
|
||||
self.free_virt_page(page).unwrap();
|
||||
}
|
||||
return None;
|
||||
}
|
||||
}
|
||||
self.allocation_previous = start + size as u32 + 4096;
|
||||
self.memory_ck();
|
||||
Some(start)
|
||||
address
|
||||
// for potential_start in (start..initial).step_by(PAGE_SIZE) {
|
||||
// let mut all_free = true;
|
||||
// for check_page in (potential_start..potential_start + size).step_by(PAGE_SIZE) {
|
||||
// if !crate::arch::mem::address_available(check_page) {
|
||||
// all_free = false;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// if all_free {
|
||||
// match kind {
|
||||
// xous_kernel::MemoryType::Default => {
|
||||
// process_inner.mem_default_last = potential_start
|
||||
// }
|
||||
// xous_kernel::MemoryType::Messages => {
|
||||
// process_inner.mem_message_last = potential_start
|
||||
// }
|
||||
// other => panic!("invalid kind: {:?}", other),
|
||||
// }
|
||||
// return Ok(potential_start as *mut u8);
|
||||
// }
|
||||
// }
|
||||
// Err(xous_kernel::Error::BadAddress)
|
||||
|
||||
// let mut start = self.allocation_previous;
|
||||
// // Find a free region that will fit this page.
|
||||
// 'outer: loop {
|
||||
// for page in (start..(start + size as u32)).step_by(4096) {
|
||||
// // If this page is allocated, skip it
|
||||
// if self.virt_to_phys(page).is_some() {
|
||||
// start = page + 4096;
|
||||
// continue 'outer;
|
||||
// }
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
// // Allocate the region
|
||||
// for page in (start..(start + size as u32)).step_by(4096) {
|
||||
// self.ensure_page(page);
|
||||
// // println!(
|
||||
// // "Allocated {:08x} @ {:08x}",
|
||||
// // page,
|
||||
// // self.virt_to_phys(page).unwrap()
|
||||
// // );
|
||||
// }
|
||||
// self.allocation_previous = start + size as u32 + 4096;
|
||||
// Some(start)
|
||||
}
|
||||
|
||||
fn ensure_page(&mut self, address: u32) {
|
||||
self.memory_ck();
|
||||
let vpn1 = ((address >> 22) & ((1 << 10) - 1)) as usize * 4;
|
||||
let vpn0 = ((address >> 12) & ((1 << 10) - 1)) as usize * 4;
|
||||
fn ensure_page(&mut self, virt: u32) -> Option<bool> {
|
||||
let mut allocated = false;
|
||||
let vpn1 = ((virt >> 22) & ((1 << 10) - 1)) as usize * 4;
|
||||
let vpn0 = ((virt >> 12) & ((1 << 10) - 1)) as usize * 4;
|
||||
|
||||
// If the level 1 pagetable doesn't exist, then this address is invalid
|
||||
let mut l1_pt_entry = self.read_u32(self.l1_pt as u64 + vpn1 as u64);
|
||||
if l1_pt_entry & MMUFLAG_VALID == 0 {
|
||||
// Allocate a new page for the level 1 pagetable
|
||||
let l0_pt_phys = self.allocate_page();
|
||||
let Some(l0_pt_phys) = self.allocate_phys_page() else {
|
||||
return None;
|
||||
};
|
||||
// println!("Allocating level 0 pagetable at {:08x}", l0_pt_phys);
|
||||
l1_pt_entry =
|
||||
((l0_pt_phys >> 12) << 10) | MMUFLAG_VALID | MMUFLAG_DIRTY | MMUFLAG_ACCESSED;
|
||||
// Map the level 1 pagetable into the root pagetable
|
||||
self.write_u32(self.l1_pt as u64 + vpn1 as u64, l1_pt_entry);
|
||||
allocated = true;
|
||||
}
|
||||
|
||||
let l0_pt_phys = ((l1_pt_entry >> 10) << 12) + vpn0 as u32;
|
||||
@ -319,8 +388,10 @@ impl Memory {
|
||||
|
||||
// Ensure the entry hasn't already been mapped.
|
||||
if l0_pt_entry & MMUFLAG_VALID == 0 {
|
||||
let page_phys = self.allocate_page();
|
||||
l0_pt_entry = ((page_phys >> 12) << 10)
|
||||
let Some(phys) = self.allocate_phys_page() else {
|
||||
return None;
|
||||
};
|
||||
l0_pt_entry = ((phys >> 12) << 10)
|
||||
| MMUFLAG_VALID
|
||||
| MMUFLAG_WRITABLE
|
||||
| MMUFLAG_READABLE
|
||||
@ -330,6 +401,8 @@ impl Memory {
|
||||
| MMUFLAG_ACCESSED;
|
||||
// Map the level 0 pagetable into the level 1 pagetable
|
||||
self.write_u32(l0_pt_phys as u64, l0_pt_entry);
|
||||
assert!(self.translation_cache.insert(virt, phys).is_none());
|
||||
allocated = true;
|
||||
}
|
||||
assert!(self
|
||||
.allocated_pages
|
||||
@ -337,11 +410,10 @@ impl Memory {
|
||||
assert!(!self
|
||||
.free_pages
|
||||
.contains(&(((l0_pt_entry >> 10) << 12) as usize)));
|
||||
self.memory_ck();
|
||||
Some(allocated)
|
||||
}
|
||||
|
||||
fn remove_memory_flags(&mut self, virt: u32, new_flags: u32) {
|
||||
self.memory_ck();
|
||||
// Ensure they're only adjusting legal flags
|
||||
assert!(new_flags & !(MMUFLAG_READABLE | MMUFLAG_WRITABLE | MMUFLAG_EXECUTABLE) == 0);
|
||||
|
||||
@ -376,7 +448,6 @@ impl Memory {
|
||||
(((l1_pt_entry >> 10) << 12) + vpn0 as u32) as u64,
|
||||
l0_pt_entry,
|
||||
);
|
||||
self.memory_ck();
|
||||
}
|
||||
|
||||
fn write_bytes(&mut self, data: &[u8], start: u32) {
|
||||
@ -444,11 +515,12 @@ impl Memory {
|
||||
|
||||
let l0_pt_entry = self.read_u32((((l1_pt_entry >> 10) << 12) + vpn0 as u32) as u64);
|
||||
|
||||
// Ensure the entry hasn't already been mapped.
|
||||
// Check if the mapping is valid
|
||||
if l0_pt_entry & MMUFLAG_VALID == 0 {
|
||||
return None;
|
||||
None
|
||||
} else {
|
||||
Some(((l0_pt_entry >> 10) << 12) | offset)
|
||||
}
|
||||
Some(((l0_pt_entry >> 10) << 12) | offset)
|
||||
}
|
||||
}
|
||||
|
||||
@ -565,33 +637,38 @@ impl riscv_cpu::cpu::Memory for Memory {
|
||||
match syscall {
|
||||
Syscall::IncreaseHeap(bytes, _flags) => {
|
||||
// println!("IncreaseHeap({} bytes, flags: {:02x})", bytes, _flags);
|
||||
let increase_bytes = bytes as u32;
|
||||
let heap_address = self.heap_start + self.heap_size;
|
||||
match bytes {
|
||||
bytes if bytes < 0 => {
|
||||
self.heap_size -= bytes.unsigned_abs() as u32;
|
||||
panic!("Reducing size not supported!");
|
||||
if self.heap_size.wrapping_add(increase_bytes) > HEAP_END {
|
||||
[
|
||||
SyscallResultNumber::Error as i64,
|
||||
SyscallErrorNumber::OutOfMemory as i64,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]
|
||||
.into()
|
||||
} else {
|
||||
for new_address in (heap_address..(heap_address + increase_bytes)).step_by(4096)
|
||||
{
|
||||
self.ensure_page(new_address);
|
||||
}
|
||||
bytes if bytes > 0 => {
|
||||
for new_address in
|
||||
(heap_address..(heap_address + bytes as u32)).step_by(4096)
|
||||
{
|
||||
self.ensure_page(new_address);
|
||||
}
|
||||
self.heap_size += bytes as u32;
|
||||
}
|
||||
_ => {}
|
||||
self.heap_size += increase_bytes;
|
||||
[
|
||||
SyscallResultNumber::MemoryRange as i64,
|
||||
heap_address as i64,
|
||||
bytes,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]
|
||||
.into()
|
||||
}
|
||||
[
|
||||
SyscallResultNumber::MemoryRange as i64,
|
||||
heap_address as i64,
|
||||
bytes,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]
|
||||
.into()
|
||||
}
|
||||
|
||||
Syscall::MapMemory(phys, virt, size, _flags) => {
|
||||
@ -605,21 +682,36 @@ impl riscv_cpu::cpu::Memory for Memory {
|
||||
if phys != 0 {
|
||||
unimplemented!("Non-zero phys address");
|
||||
}
|
||||
let region = self
|
||||
.allocate_virt_region(size as usize)
|
||||
.expect("out of memory");
|
||||
// println!(" -> {:08x}", region);
|
||||
[
|
||||
SyscallResultNumber::MemoryRange as i64,
|
||||
region as i64,
|
||||
size,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]
|
||||
.into()
|
||||
if let Some(region) = self.allocate_virt_region(size as usize) {
|
||||
[
|
||||
SyscallResultNumber::MemoryRange as i64,
|
||||
region as i64,
|
||||
size,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]
|
||||
.into()
|
||||
} else {
|
||||
// self.print_mmu();
|
||||
println!(
|
||||
"Couldn't find a free spot to allocate {} bytes of virtual memory, or out of memory",
|
||||
size as usize
|
||||
);
|
||||
[
|
||||
SyscallResultNumber::Error as i64,
|
||||
SyscallErrorNumber::OutOfMemory as i64,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
]
|
||||
.into()
|
||||
}
|
||||
}
|
||||
Syscall::Connect(id) => {
|
||||
// println!(
|
||||
@ -832,7 +924,7 @@ impl riscv_cpu::cpu::Memory for Memory {
|
||||
Syscall::UnmapMemory(address, size) => {
|
||||
// println!("UnmapMemory({:08x}, {})", address, size);
|
||||
for offset in (address..address + size).step_by(4096) {
|
||||
self.free_page(offset as u32).unwrap();
|
||||
self.free_virt_page(offset as u32).unwrap();
|
||||
}
|
||||
[SyscallResultNumber::Ok as i64, 0, 0, 0, 0, 0, 0, 0].into()
|
||||
}
|
||||
@ -855,10 +947,27 @@ impl riscv_cpu::cpu::Memory for Memory {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn translate(&self, v_address: u64) -> Option<u64> {
|
||||
let v_address = v_address as u32;
|
||||
let ppn = v_address & !0xfff;
|
||||
let offset = v_address & 0xfff;
|
||||
self.translation_cache
|
||||
.get(&ppn)
|
||||
.map(|x| (*x + offset) as u64)
|
||||
}
|
||||
|
||||
fn reserve(&mut self, p_address: u64) -> bool {
|
||||
self.reservations.insert(p_address as u32)
|
||||
}
|
||||
|
||||
fn clear_reservation(&mut self, p_address: u64) {
|
||||
self.reservations.remove(&(p_address as u32));
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Machine {
|
||||
memory: Arc<RwLock<Memory>>,
|
||||
memory: Arc<Mutex<Memory>>,
|
||||
workers: Vec<WorkerHandle>,
|
||||
satp: u64,
|
||||
memory_cmd_sender: Sender<MemoryCommand>,
|
||||
@ -870,7 +979,7 @@ impl Machine {
|
||||
pub fn new(program: &[u8]) -> Result<Self, LoadError> {
|
||||
let (memory, memory_cmd) = Memory::new(MEMORY_BASE, 16 * 1024 * 1024);
|
||||
let memory_cmd_sender = memory.memory_cmd.clone();
|
||||
let memory = Arc::new(RwLock::new(memory));
|
||||
let memory = Arc::new(Mutex::new(memory));
|
||||
|
||||
let mut machine = Self {
|
||||
memory,
|
||||
@ -900,8 +1009,7 @@ impl Machine {
|
||||
return Err(LoadError::BitSizeError);
|
||||
}
|
||||
|
||||
let mut memory_writer = self.memory.write().unwrap();
|
||||
memory_writer.turbo();
|
||||
let mut memory_writer = self.memory.lock().unwrap();
|
||||
for sh in elf.section_headers {
|
||||
if sh.sh_flags as u32 & goblin::elf::section_header::SHF_ALLOC == 0 {
|
||||
// println!(
|
||||
@ -918,7 +1026,9 @@ impl Machine {
|
||||
|
||||
if sh.sh_type & goblin::elf::section_header::SHT_NOBITS != 0 {
|
||||
for addr in sh.sh_addr..(sh.sh_addr + sh.sh_size) {
|
||||
memory_writer.ensure_page(addr.try_into().unwrap());
|
||||
memory_writer
|
||||
.ensure_page(addr.try_into().unwrap())
|
||||
.expect("out of memory");
|
||||
}
|
||||
} else {
|
||||
memory_writer.write_bytes(
|
||||
@ -928,16 +1038,13 @@ impl Machine {
|
||||
}
|
||||
}
|
||||
|
||||
memory_writer.normal();
|
||||
|
||||
// TODO: Get memory permissions correct
|
||||
|
||||
let satp = memory_writer.satp.into();
|
||||
|
||||
// Ensure stack is allocated
|
||||
for page in (0xc000_0000..0xc002_0000).step_by(4096) {
|
||||
memory_writer.ensure_page(page);
|
||||
memory_writer.ensure_page(page).expect("out of memory");
|
||||
}
|
||||
drop(memory_writer);
|
||||
|
||||
cpu.write_csr(riscv_cpu::cpu::CSR_SATP_ADDRESS, satp)
|
||||
.map_err(|_| LoadError::SatpWriteError)?;
|
||||
|
@ -26,6 +26,38 @@ pub enum SyscallResultNumber {
|
||||
Scalar5 = 20,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum SyscallErrorNumber {
|
||||
NoError = 0,
|
||||
BadAlignment = 1,
|
||||
BadAddress = 2,
|
||||
OutOfMemory = 3,
|
||||
MemoryInUse = 4,
|
||||
InterruptNotFound = 5,
|
||||
InterruptInUse = 6,
|
||||
InvalidString = 7,
|
||||
ServerExists = 8,
|
||||
ServerNotFound = 9,
|
||||
ProcessNotFound = 10,
|
||||
ProcessNotChild = 11,
|
||||
ProcessTerminated = 12,
|
||||
Timeout = 13,
|
||||
InternalError = 14,
|
||||
ServerQueueFull = 15,
|
||||
ThreadNotAvailable = 16,
|
||||
UnhandledSyscall = 17,
|
||||
InvalidSyscall = 18,
|
||||
ShareViolation = 19,
|
||||
InvalidThread = 20,
|
||||
InvalidPID = 21,
|
||||
UnknownError = 22,
|
||||
AccessDenied = 23,
|
||||
UseBeforeInit = 24,
|
||||
DoubleFree = 25,
|
||||
DebugInProgress = 26,
|
||||
InvalidLimit = 27,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Syscall {
|
||||
Unknown([i64; 8]),
|
||||
|
@ -2,16 +2,20 @@ use std::sync::mpsc::Receiver;
|
||||
pub mod log;
|
||||
pub mod ticktimer;
|
||||
|
||||
pub type ResponseData = ([i64; 8], Option<(Vec<u8>, u64)>);
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub enum ScalarResult {
|
||||
Scalar1(u32),
|
||||
Scalar2([u32; 2]),
|
||||
Scalar5([u32; 5]),
|
||||
WaitForResponse(Receiver<([i64; 8], Option<(Vec<u8>, u64)>)>),
|
||||
WaitForResponse(Receiver<ResponseData>),
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub enum LendResult {
|
||||
MemoryReturned([u32; 2]),
|
||||
WaitForResponse(Receiver<([i64; 8], Option<(Vec<u8>, u64)>)>),
|
||||
WaitForResponse(Receiver<ResponseData>),
|
||||
}
|
||||
|
||||
pub trait Service {
|
||||
|
@ -39,10 +39,12 @@ impl Service for Log {
|
||||
let print_buffer = &buf[0..extra[1] as usize];
|
||||
// println!("Log stdout:");
|
||||
std::io::stdout().write_all(print_buffer).unwrap();
|
||||
std::io::stdout().flush().unwrap();
|
||||
} else if opcode == LogLendOpcode::StandardError as u32 {
|
||||
let print_buffer = &buf[0..extra[1] as usize];
|
||||
// println!("Log stderr:");
|
||||
std::io::stderr().write_all(print_buffer).unwrap();
|
||||
std::io::stderr().flush().unwrap();
|
||||
} else {
|
||||
panic!("Log lend {}: {} {:x?}", sender, opcode, buf);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user