diff --git a/Cargo.lock b/Cargo.lock index 4dbd04d..2729329 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index b315773..7e2aa8a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" ]} diff --git a/crates/riscv-cpu/src/cpu.rs b/crates/riscv-cpu/src/cpu.rs index 3208f71..7856054 100644 --- a/crates/riscv-cpu/src/cpu.rs +++ b/crates/riscv-cpu/src/cpu.rs @@ -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, u64)>); + pub enum TickResult { Ok, ExitThread(u64), - PauseEmulation(Receiver<([i64; 8], Option<(Vec, u64)>)>), + PauseEmulation(Receiver), CpuTrap(Trap), } @@ -74,9 +76,8 @@ pub struct Cpu { pc: u64, csr: [u64; CSR_CAPACITY], mmu: Mmu, - memory: Arc>, - reservation: u64, // @TODO: Should support multiple address reservations - is_reservation_set: bool, + memory: Arc>, + reservation: Option, // @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, u64)>)>), + PauseEmulation(Receiver), } fn _get_privilege_mode_name(mode: &PrivilegeMode) -> &'static str { @@ -225,11 +226,11 @@ pub struct CpuBuilder { xlen: Xlen, pc: u64, sp: u64, - memory: Arc>, + memory: Arc>, } impl CpuBuilder { - pub fn new(memory: Arc>) -> Self { + pub fn new(memory: Arc>) -> Self { CpuBuilder { xlen: Xlen::Bit64, memory, @@ -266,7 +267,7 @@ impl Cpu { /// /// # Arguments /// * `Terminal` - pub fn new(memory: Arc>) -> Self { + pub fn new(memory: Arc>) -> 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 => {} - }; - } -} diff --git a/crates/riscv-cpu/src/lib.rs b/crates/riscv-cpu/src/lib.rs index e886bff..b07c500 100644 --- a/crates/riscv-cpu/src/lib.rs +++ b/crates/riscv-cpu/src/lib.rs @@ -1,5 +1,7 @@ pub mod cpu; -pub mod memory; pub mod mmu; +#[cfg(test)] +pub mod memory; + pub use cpu::{Cpu, CpuBuilder, Xlen}; diff --git a/crates/riscv-cpu/src/memory.rs b/crates/riscv-cpu/src/memory.rs index f74ea31..f8fe8ab 100644 --- a/crates/riscv-cpu/src/memory.rs +++ b/crates/riscv-cpu/src/memory.rs @@ -1,6 +1,5 @@ /// Emulates main memory. -pub struct Memory { - /// Memory content +pub struct Memory { /// Memory content data: Vec, } diff --git a/crates/riscv-cpu/src/mmu.rs b/crates/riscv-cpu/src/mmu.rs index 13cef57..724dd7a 100644 --- a/crates/riscv-cpu/src/mmu.rs +++ b/crates/riscv-cpu/src/mmu.rs @@ -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, u64)>)>), + Defer(Receiver), } impl From<[i64; 8]> for SyscallResult { @@ -16,8 +16,8 @@ impl From<[i64; 8]> for SyscallResult { } } -impl From, u64)>)>> for SyscallResult { - fn from(receiver: std::sync::mpsc::Receiver<([i64; 8], Option<(Vec, u64)>)>) -> Self { +impl From> for SyscallResult { + fn from(receiver: std::sync::mpsc::Receiver) -> 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; + 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>, + memory: Arc>, // /// The size of main memory (if initialized) // memory_length: Option, /// 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, - load_page_cache: HashMap, - store_page_cache: HashMap, } #[derive(Debug)] @@ -100,7 +87,7 @@ impl Mmu { /// /// # Arguments /// * `xlen` - pub fn new(xlen: Xlen, memory: Arc>) -> Self { + pub fn new(xlen: Xlen, memory: Arc>) -> 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 { - 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 { 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) -// } -// } diff --git a/src/xous.rs b/src/xous.rs index c016d67..37b5c2e 100644 --- a/src/xous.rs +++ b/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, u64)>); enum MemoryCommand { Exit, @@ -59,30 +66,14 @@ enum MemoryCommand { u32, /* argument 4 */ Sender, /* Thread ID */ ), - JoinThread(u32, Sender<([i64; 8], Option<(Vec, u64)>)>), -} - -struct Memory { - base: u32, - data: HashMap, - allocated_pages: BTreeSet, - free_pages: BTreeSet, - heap_start: u32, - heap_size: u32, - // allocation_start: u32, - allocation_previous: u32, - l1_pt: u32, - satp: u32, - connections: HashMap>, - memory_cmd: Sender, - turbo: bool, + JoinThread(u32, Sender), } struct Worker { cpu: riscv_cpu::Cpu, cmd: Sender, tid: i64, - memory: Arc>, + memory: Arc>, } impl Worker { @@ -90,7 +81,7 @@ impl Worker { cpu: riscv_cpu::Cpu, cmd: Sender, tid: i64, - memory: Arc>, + memory: Arc>, ) -> 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, + allocated_pages: BTreeSet, + free_pages: BTreeSet, + heap_start: u32, + heap_size: u32, + // allocation_start: u32, + allocation_previous: u32, + l1_pt: u32, + satp: u32, + connections: HashMap>, + memory_cmd: Sender, + translation_cache: HashMap, + allocated_bytes: u32, + reservations: HashSet, +} + impl Memory { pub fn new(base: u32, size: usize) -> (Self, Receiver) { 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 { + 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 { - 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 { + 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 { + 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>, + memory: Arc>, workers: Vec, satp: u64, memory_cmd_sender: Sender, @@ -870,7 +979,7 @@ impl Machine { pub fn new(program: &[u8]) -> Result { 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)?; diff --git a/src/xous/definitions.rs b/src/xous/definitions.rs index ae37d21..2f626c2 100644 --- a/src/xous/definitions.rs +++ b/src/xous/definitions.rs @@ -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]), diff --git a/src/xous/services.rs b/src/xous/services.rs index 16fc73c..0c744a6 100644 --- a/src/xous/services.rs +++ b/src/xous/services.rs @@ -2,16 +2,20 @@ use std::sync::mpsc::Receiver; pub mod log; pub mod ticktimer; +pub type ResponseData = ([i64; 8], Option<(Vec, u64)>); + +#[allow(dead_code)] pub enum ScalarResult { Scalar1(u32), Scalar2([u32; 2]), Scalar5([u32; 5]), - WaitForResponse(Receiver<([i64; 8], Option<(Vec, u64)>)>), + WaitForResponse(Receiver), } +#[allow(dead_code)] pub enum LendResult { MemoryReturned([u32; 2]), - WaitForResponse(Receiver<([i64; 8], Option<(Vec, u64)>)>), + WaitForResponse(Receiver), } pub trait Service { diff --git a/src/xous/services/log.rs b/src/xous/services/log.rs index 8771126..d648726 100644 --- a/src/xous/services/log.rs +++ b/src/xous/services/log.rs @@ -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); }