summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core.rs57
-rw-r--r--src/main.rs140
-rw-r--r--src/mem.rs263
-rw-r--r--src/state.rs6
-rw-r--r--wrv32em-common.dts9
5 files changed, 299 insertions, 176 deletions
diff --git a/src/core.rs b/src/core.rs
index 6317c9e..2746d1b 100644
--- a/src/core.rs
+++ b/src/core.rs
@@ -151,74 +151,53 @@ pub fn sltiu(state: &mut CpuState, rd: usize, rs1: usize, imm: u32) {
state.registers[rd] = if state.registers[rs1] < imm { 1 } else { 0 };
}
pub fn lb(state: &mut CpuState, rd: usize, rs1: usize, imm: u32) {
- let byte = state
- .memory
- .read((state.registers[rs1] + imm) as usize, 99)
- .unwrap();
+ let byte = state.memory.read(state.registers[rs1] + imm).unwrap();
state.registers[rd] = (byte as i8) as u32;
}
pub fn lh(state: &mut CpuState, rd: usize, rs1: usize, imm: u32) {
- let bytes = state
- .memory
- .read_multiple_bytes((state.registers[rs1] + imm) as usize, 2, 99)
- .unwrap();
-
- state.registers[rd] = (((bytes[1] as u16) << 8 | bytes[0] as u16) as i16) as u32;
+ let halfword = state.memory.read_halfword(state.registers[rs1] + imm).unwrap();
+ state.registers[rd] = (halfword as i16) as u32;
}
pub fn lw(state: &mut CpuState, rd: usize, rs1: usize, imm: u32) {
- let addr = (state.registers[rs1].wrapping_add(imm)) as usize;
- let slice = state.memory.read_multiple_bytes(addr, 4, 99).unwrap();
- let number = u32::from_le_bytes(slice[0..4].try_into().unwrap());
-
- state.registers[rd] = number;
+ let addr = state.registers[rs1].wrapping_add(imm);
+ let word = state.memory.read_word(addr).unwrap();
+ state.registers[rd] = word;
}
pub fn lbu(state: &mut CpuState, rd: usize, rs1: usize, imm: u32) {
state.registers[rd] = state
.memory
- .read((state.registers[rs1] + imm) as usize, 99)
+ .read(state.registers[rs1].wrapping_add(imm))
.unwrap() as u32;
}
pub fn lhu(state: &mut CpuState, rd: usize, rs1: usize, imm: u32) {
- let bytes = state
- .memory
- .read_multiple_bytes((state.registers[rs1] + imm) as usize, 2, 99)
- .unwrap();
-
- state.registers[rd] = ((bytes[1] as u16) << 8 | bytes[0] as u16) as u32;
+ state.registers[rd] = state.memory.read_halfword(state.registers[rs1] + imm).unwrap() as u32;
}
pub fn sb(state: &mut CpuState, rs1: usize, rs2: usize, imm: u32) {
- let addr = (state.registers[rs1] + imm) as usize;
+ let addr = state.registers[rs1] + imm;
state
.memory
- .write(addr, 99, state.registers[rs2] as u8)
+ .write(addr, state.registers[rs2] as u8)
.unwrap();
- state.release_reservations(&[addr]);
+ state.release_reservations(&[addr as usize]);
}
pub fn sh(state: &mut CpuState, rs1: usize, rs2: usize, imm: u32) {
let addr = (state.registers[rs1] + imm) as usize;
- let halfword = state.registers[rs2] & 0xFFFF;
-
- state
- .memory
- .write_multiple_bytes(addr, &[(halfword & 0xFF) as u8, (halfword >> 8) as u8], 99)
- .unwrap();
+ let halfword= (state.registers[rs2] & 0xFFFF) as u16;
+ state.memory.write_halfword(addr as u32, halfword).unwrap();
state.release_reservations(&[addr, addr + 1]);
}
pub fn sw(state: &mut CpuState, rs1: usize, rs2: usize, imm: u32) {
let addr = (state.registers[rs1] + imm) as usize;
- state
- .memory
- .write_multiple_bytes(addr, &state.registers[rs2].to_le_bytes(), 99)
- .unwrap();
+ state.memory.write_word(addr as u32, state.registers[rs2]).unwrap();
state.release_reservations(&[addr, addr + 1, addr + 2, addr + 3]);
}
@@ -362,14 +341,10 @@ pub fn amominu_w(state: &mut CpuState, rd: usize, rs1: usize, rs2: usize) {
}
fn load_word(state: &CpuState, addr: usize) -> u32 {
- let slice = state.memory.read_multiple_bytes(addr, 4, 99).unwrap();
- u32::from_le_bytes(slice[0..4].try_into().unwrap())
+ state.memory.read_word(addr as u32).unwrap()
}
fn store_word(state: &mut CpuState, addr: usize, value: u32) {
- state
- .memory
- .write_multiple_bytes(addr, &value.to_le_bytes(), 99)
- .unwrap();
+ state.memory.write_word(addr as u32, value).unwrap()
}
pub fn auipc(state: &mut CpuState, rd: usize, imm: u32) {
diff --git a/src/main.rs b/src/main.rs
index 45f567f..c276280 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,53 +3,106 @@ pub mod mem;
pub mod state;
use crate::core::*;
-use crate::mem::Memory;
+use crate::mem::{CpuMemory, LogicalMemory};
use crate::state::CpuState;
-use std::io::Write;
const MEMORY_SIZE: usize = 64 * 1024 * 1024;
const REGISTER_NAME_WIDTH: u32 = 0b11111;
const RO_TARGET: u32 = 0x80000000;
-const FDT_LOCATION: u32 = 0x90000000;
-const UART_ADDR: u32 = 0x10000000;
+const FDT_LOCATION: u32 = 0x83000000;
+
+const RINGBUF_ADDR: usize = 0x82000000;
+const RINGBUF_SIZE: usize = 0x100000;
+
+fn write_all(memory: &mut CpuMemory, value: &[u8], addr: u32, size: u32) {
+ let mut remaining_size = size;
+ let mut curr_addr = addr;
+ let mut idx = 0;
+ while remaining_size >= 4 {
+ memory
+ .write_word(
+ curr_addr,
+ u32::from_le_bytes(value[idx..(idx + 4)].try_into().unwrap()),
+ )
+ .unwrap();
+ remaining_size -= 4;
+ curr_addr += 4;
+ idx += 4;
+ }
+ while remaining_size >= 2 {
+ memory
+ .write_halfword(
+ curr_addr,
+ u16::from_le_bytes(value[idx..(idx + 2)].try_into().unwrap()),
+ )
+ .unwrap();
+ remaining_size -= 2;
+ curr_addr += 2;
+ idx += 2;
+ }
+ while remaining_size >= 1 {
+ memory.write(curr_addr, value[idx]).unwrap();
+ remaining_size -= 1;
+ curr_addr += 1;
+ idx += 1;
+ }
+}
+
+fn read_all(memory: &CpuMemory, addr: u32, size: u32) -> Vec<u8> {
+ let mut ret = Vec::new();
+ for a in addr..addr + size {
+ ret.push(memory.read(a).unwrap());
+ }
+ ret
+}
fn main() {
let path = std::path::PathBuf::from("wkern.bin");
let f_dat = std::fs::read(path).expect("Unable to read file");
-
let dtb_dat = std::fs::read(env!("DTB_PATH")).expect("Unable to read file");
- let memory = Memory::new(MEMORY_SIZE);
+ let mut memory = CpuMemory {
+ memory: vec![0u8; MEMORY_SIZE].into_boxed_slice(),
+ devices: Vec::new(),
+ };
- let mut state = CpuState::new(memory);
+ let ram_device = LogicalMemory {
+ mode: false,
+ asid: 0,
+ ppn: 0,
+ base: 0x80000000,
+ };
- state
- .memory
- .write_multiple_bytes(RO_TARGET as usize, f_dat.as_slice(), 99)
- .unwrap();
- state
- .memory
- .write_multiple_bytes(FDT_LOCATION as usize, dtb_dat.as_slice(), 99)
- .unwrap();
+ memory.register_device(
+ 0x80000000..0x80000000 + MEMORY_SIZE as u32,
+ Box::new(ram_device),
+ );
- state.memory.write(UART_ADDR as usize, 99, 0).unwrap();
- state.pc = RO_TARGET;
+ let mut state = CpuState::new(memory);
+
+ write_all(
+ &mut state.memory,
+ f_dat.as_slice(),
+ RO_TARGET,
+ f_dat.len() as u32,
+ );
+ write_all(
+ &mut state.memory,
+ dtb_dat.as_slice(),
+ FDT_LOCATION,
+ dtb_dat.len() as u32,
+ );
+
+ state.pc = 0x80000000;
state.registers[10] = 0;
state.registers[11] = FDT_LOCATION;
+ println!("loaded!");
+
loop {
{
handle_instruction(&mut state);
-
- // simple polling uart
- let uart_byte = state.memory.read(UART_ADDR as usize, 99).unwrap();
- if uart_byte != 0 {
- print!("{}", uart_byte as char);
- }
- state.memory.write(UART_ADDR as usize, 99, 0).unwrap();
- std::io::stdout().flush().unwrap();
-
state.registers[0] = 0;
}
}
@@ -57,8 +110,7 @@ fn main() {
fn handle_instruction(state: &mut CpuState) {
let pc = state.pc;
- let instruction = state.memory.read_multiple_bytes(pc as usize, 4, 99);
- let word = u32::from_le_bytes(instruction.unwrap()[0..4].try_into().unwrap());
+ let word = state.memory.read_word(pc).unwrap();
let opcode = word & 0x7f;
match opcode {
0b0110011 => {
@@ -220,12 +272,15 @@ fn handle_instruction(state: &mut CpuState) {
match f3 {
0 => {
- eprintln!(
- "TRAP! syscall {} with gp state {:?}",
- state.registers[17], pc
- );
-
- // todo: trap to CSR 0x305
+ match imm {
+ 0 => trap_to_kernel(state), // ecall
+ 258 => return_to_userspace(state), // sret
+ 261 => {
+ println!("wfi");
+ exit(state);
+ }
+ _ => panic!("Illegal instruction: {:x}", word),
+ }
} // ecall
1 => csrrw(state, rd, rs1, imm as usize),
2 => csrrs(state, rd, rs1, imm as usize),
@@ -349,8 +404,23 @@ fn handle_instruction(state: &mut CpuState) {
let rs1 = ((word >> 15) & REGISTER_NAME_WIDTH) as usize;
fcvt_w_s(state, rd, rs1);
}
- _ => panic!("Illegal instruction or todo: {:x}", word),
+ _ => panic!("Illegal instruction or todo: {:032b}", word),
}
state.pc += 4;
}
+
+fn trap_to_kernel(state: &mut CpuState) {
+ state.csr[0x141 /* sepc */] = state.pc;
+ state.pc = state.csr[0x105 /* stvec */];
+}
+
+fn return_to_userspace(state: &mut CpuState) {
+ state.pc = state.csr[0x141 /* sepc */] + 4;
+}
+
+fn exit(state: &mut CpuState) {
+ let data = read_all(&state.memory, RINGBUF_ADDR as u32, RINGBUF_SIZE as u32);
+ std::fs::write("core_dump.txt", data).unwrap();
+ std::process::exit(0);
+}
diff --git a/src/mem.rs b/src/mem.rs
index 2043aa1..770659b 100644
--- a/src/mem.rs
+++ b/src/mem.rs
@@ -1,124 +1,201 @@
-const PAGE_SIZE: usize = 4 * 1024;
-const PAGE_MASK: usize = !(PAGE_SIZE - 1);
-
-struct Page {
- virtual_addr: usize,
- real_addr: usize,
- permission: usize,
- allocated: usize,
+use std::ops::Range;
+
+pub trait MemoryDevice {
+ fn read(&self, memory: &CpuMemory, address: u32) -> Result<u8, ()>;
+ fn write(&self, memory: &CpuMemory, address: u32, value: u8) -> Result<Option<u32>, ()>;
}
-pub struct Memory {
- memory: Box<[u8]>,
- pages: Vec<Page>,
+
+pub struct CpuMemory {
+ pub memory: Box<[u8]>,
+ pub devices: Vec<(Range<u32>, Box<dyn MemoryDevice>)>,
}
-impl Memory {
- pub fn new(size: usize) -> Memory {
- let mut pages: Vec<Page> = Vec::with_capacity(size / PAGE_SIZE);
- for i in 0..size / PAGE_SIZE {
- pages.push(Page {
- virtual_addr: usize::MAX,
- real_addr: i * PAGE_SIZE,
- permission: 0,
- allocated: 0,
- });
- }
- Memory {
- memory: vec![0u8; size].into_boxed_slice(),
- pages,
- }
+
+impl CpuMemory {
+ fn get_device(&self, address: u32) -> Option<&Box<dyn MemoryDevice>> {
+ self.devices
+ .iter()
+ .find(|(range, _)| range.contains(&address))
+ .map(|(_, device)| device)
}
- pub fn create_page(&mut self, virtual_addr: usize, permission: usize) -> Option<usize> {
- // Find first page that isn't allocated and set it to allocated
- for (i, page) in self.pages.iter_mut().enumerate() {
- if page.allocated == 0 {
- page.virtual_addr = virtual_addr;
- page.permission = permission;
- page.allocated = 1;
- return Some(i);
- }
- }
- eprintln!("tried to allocate {:X} but there are no more pages!", virtual_addr);
- None
+ pub fn register_device(&mut self, range: Range<u32>, device: Box<dyn MemoryDevice>) {
+ self.devices.push((range, device));
}
- fn lookup_page(&mut self, virtual_addr: usize, permission: usize) -> Option<usize> {
- for (i, page) in self.pages.iter().enumerate() {
- if page.virtual_addr == virtual_addr
- && permission >= page.permission
- && page.allocated == 1
- {
- return Some(i);
- }
+ pub fn read(&self, address: u32) -> Result<u8, ()> {
+ if let Some(device) = self.get_device(address) {
+ device.read(self, address)
+ } else {
+ Err(())
}
- self.create_page(virtual_addr, permission)
}
- pub fn ro_lookup(&self, virtual_addr: usize, permission: usize) -> Option<usize> {
- for (i, page) in self.pages.iter().enumerate() {
- if page.virtual_addr == virtual_addr
- && page.permission == permission
- && page.allocated == 1
- {
- return Some(i);
+ pub fn write(&mut self, address: u32, value: u8) -> Result<(), ()> {
+ if let Some(device) = self.get_device(address) {
+ match device.write(self, address, value) {
+ Ok(opt_idx) => {
+ if let Some(idx) = opt_idx {
+ self.memory[idx as usize] = value;
+ }
+ Ok(())
+ }
+ Err(()) => Err(())
}
+ } else {
+ Err(())
}
- println!("virtual_addr {:X} is not allocated", virtual_addr);
- None
}
- pub fn write(&mut self, address: usize, permission: usize, value: u8) -> Result<(), ()> {
- let page = address & PAGE_MASK;
- let offset = address - page;
+ pub fn read_halfword(&self, address: u32) -> Result<u16, ()> {
+ let low = self.read(address)?;
+ let high = self.read(address + 1)?;
+ Ok((high as u16) << 8 | low as u16)
+ }
- if let Some(page) = self.lookup_page(page, permission) {
- self.memory[self.pages[page].real_addr + offset] = value;
- return Ok(());
- }
- Err(())
+ pub fn write_halfword(&mut self, address: u32, value: u16) -> Result<(), ()> {
+ self.write(address + 1, (value >> 8) as u8)?;
+ self.write(address, (value & 0xFF) as u8)?;
+ Ok(())
}
- pub fn read(&self, address: usize, permission: usize) -> Result<u8, ()> {
- let page = address & PAGE_MASK;
- let offset = address - page;
+ pub fn read_word(&self, address: u32) -> Result<u32, ()> {
+ let low = self.read_halfword(address)?;
+ let high = self.read_halfword(address + 2)?;
+ Ok((high as u32) << 16 | low as u32)
+ }
- if let Some(page) = self.ro_lookup(page, permission) {
- Ok(self.memory[self.pages[page].real_addr + offset])
- } else {
- println!("address {:X} is not allocated", address);
+ pub fn write_word(&mut self, address: u32, value: u32) -> Result<(), ()> {
+ self.write_halfword(address + 2, (value >> 16) as u16)?;
+ self.write_halfword(address, (value & 0xFFFF) as u16)
+ }
+}
- Err(())
+pub struct LogicalMemory {
+ pub mode: bool,
+ pub asid: u16,
+ pub ppn: u32,
+ pub base: u32,
+}
+
+pub struct Sv32Page {
+ ppn: u32,
+ _rsw: u8,
+ _dirty: bool,
+ _access: bool,
+ _global: bool,
+ _user: bool,
+ _execute: bool,
+ write: bool,
+ read: bool,
+ valid: bool,
+}
+
+impl From<u32> for Sv32Page {
+ fn from(value: u32) -> Sv32Page {
+ Sv32Page {
+ ppn: value >> 10,
+ _rsw: ((value >> 8) & 3) as u8,
+ _dirty: (value >> 7) & 1 == 0,
+ _access: (value >> 6) & 1 == 0,
+ _global: (value >> 5) & 1 == 0,
+ _user: (value >> 4) & 1 == 0,
+ _execute: (value >> 3) & 1 == 0,
+ write: (value >> 2) & 1 == 0,
+ read: (value >> 1) & 1 == 0,
+ valid: value & 1 == 0,
}
}
+}
- pub fn read_multiple_bytes(
- &self,
- address: usize,
- size: usize,
- permission: usize,
- ) -> Result<Vec<u8>, ()> {
- let mut ret = vec![0; size];
- for addr in address..address + size {
- if let Ok(byte) = self.read(addr, permission) {
- ret[addr - address] = byte;
- } else {
+impl LogicalMemory {
+ pub fn satp_write(&mut self, satp: u32) {
+ self.mode = (satp >> 31) & 1 != 0;
+ self.asid = ((satp >> 22) & 0x1FF) as u16;
+ self.ppn = satp & 0x3FFFFF;
+ }
+
+ pub fn lookup_page_entry(&self, memory: &CpuMemory, address: u32) -> Option<u32> {
+ if address - self.base + 4 > memory.memory.len() as u32 {
+ return None;
+ }
+ Some(u32::from_le_bytes(
+ memory.memory[(address - self.base) as usize..(address - self.base + 4) as usize]
+ .try_into()
+ .unwrap(),
+ ))
+ }
+}
+
+impl MemoryDevice for LogicalMemory {
+ fn read(&self, memory: &CpuMemory, address: u32) -> Result<u8, ()> {
+ if self.mode
+ /* Sv32 */
+ {
+ let root = self.ppn << 12;
+ let high_page = (address >> 22) & 0x3FF;
+
+ let high_pte = self
+ .lookup_page_entry(memory, root + high_page * 4)
+ .ok_or(())?;
+ let high_pte = Sv32Page::from(high_pte);
+ if !high_pte.valid {
return Err(());
}
+
+ let low_page = (address >> 12) & 0x3FF;
+ let low_pte = self
+ .lookup_page_entry(memory, (high_pte.ppn << 12) + low_page * 4)
+ .ok_or(())?;
+ let low_pte = Sv32Page::from(low_pte);
+
+ if !low_pte.read || !low_pte.valid {
+ return Err(());
+ }
+
+ Ok(memory.memory[((low_pte.ppn << 12) | (address & 0xfff)) as usize])
+ } else
+ /* bare */
+ {
+ if address - self.base > memory.memory.len() as u32 {
+ Err(())
+ } else {
+ Ok(memory.memory[(address - self.base) as usize])
+ }
}
- Ok(ret)
}
+ fn write(&self, memory: &CpuMemory, address: u32, _value: u8) -> Result<Option<u32>, ()> {
+ if self.mode
+ /* Sv32 */
+ {
+ let root = self.ppn << 12;
+ let high_page = (address >> 22) & 0x3FF;
- pub fn write_multiple_bytes(
- &mut self,
- address: usize,
- bytes: &[u8],
- permission: usize,
- ) -> Result<(), ()> {
- for addr in address..address + bytes.len() {
- if let Err(_) = self.write(addr, permission, bytes[addr - address]) {
+ let high_pte = self
+ .lookup_page_entry(memory, root + high_page * 4)
+ .ok_or(())?;
+ let high_pte = Sv32Page::from(high_pte);
+ if !high_pte.valid {
return Err(());
}
+
+ let low_page = (address >> 12) & 0x3FF;
+ let low_pte = self
+ .lookup_page_entry(memory, (high_pte.ppn << 12) + low_page * 4)
+ .ok_or(())?;
+ let low_pte = Sv32Page::from(low_pte);
+
+ if !low_pte.read || !low_pte.valid {
+ return Err(());
+ }
+ Ok(Some((low_pte.ppn << 12) | (address & 0xfff)))
+ } else
+ /* bare */
+ {
+ if address - self.base > memory.memory.len() as u32 {
+ Err(())
+ } else {
+ Ok(Some(address - self.base))
+ }
}
- Ok(())
}
}
diff --git a/src/state.rs b/src/state.rs
index d4690bb..fa09e68 100644
--- a/src/state.rs
+++ b/src/state.rs
@@ -1,7 +1,7 @@
-use crate::mem::Memory;
+use crate::mem::CpuMemory;
pub struct CpuState {
- pub memory: Memory,
+ pub memory: CpuMemory,
pub registers: [u32; 32],
pub f_registers: [f32; 32],
pub csr: [u32; 4096],
@@ -10,7 +10,7 @@ pub struct CpuState {
}
impl CpuState {
- pub fn new(memory: Memory) -> CpuState {
+ pub fn new(memory: CpuMemory) -> CpuState {
CpuState {
memory,
registers: [0u32; 32],
diff --git a/wrv32em-common.dts b/wrv32em-common.dts
index e7c1181..7ba1946 100644
--- a/wrv32em-common.dts
+++ b/wrv32em-common.dts
@@ -4,10 +4,10 @@
#address-cells = <1>;
#size-cells = <1>;
- memory@70000000 {
+ memory@80000000 {
device_type = "memory";
status = "okay";
- reg = <0x70000000
+ reg = <0x80000000
0x04000000>;
};
gpu@60000000 {
@@ -15,11 +15,12 @@
device_type = "gpu";
status = "disabled";
reg = <0x60000000 0x04000000>;
- }
+ };
uart@60000000 {
compatible = "unix,soc-tty";
device_type = "gpu";
status = "okay";
reg = <0x60000000 0x04000000>;
- }
+ };
+
};