fix stack alignment

Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
Sean Cross 2024-01-01 19:13:21 +08:00
parent f13d476a9f
commit 48206ee23c
4 changed files with 352 additions and 205 deletions

View File

@ -1,4 +1,4 @@
use std::sync::{Arc, RwLock, mpsc::Receiver}; use std::sync::{mpsc::Receiver, Arc, RwLock};
pub use super::mmu::Memory; pub use super::mmu::Memory;
use super::mmu::{AddressingMode, Mmu}; use super::mmu::{AddressingMode, Mmu};
@ -58,9 +58,9 @@ pub enum TickResult {
Ok, Ok,
ExitThread(u64), ExitThread(u64),
PauseEmulation(Receiver<([i64; 8], Option<(Vec<u8>, u64)>)>), PauseEmulation(Receiver<([i64; 8], Option<(Vec<u8>, u64)>)>),
CpuTrap(Trap),
} }
/// Emulates a RISC-V CPU core /// Emulates a RISC-V CPU core
pub struct Cpu { pub struct Cpu {
clock: u64, clock: u64,
@ -343,7 +343,6 @@ impl Cpu {
/// Runs program one cycle. Fetch, decode, and execution are completed in a cycle so far. /// Runs program one cycle. Fetch, decode, and execution are completed in a cycle so far.
pub fn tick(&mut self) -> TickResult { pub fn tick(&mut self) -> TickResult {
let instruction_address = self.pc;
match self.tick_operate() { match self.tick_operate() {
Ok(()) => {} Ok(()) => {}
Err(Trap { Err(Trap {
@ -358,7 +357,7 @@ impl Cpu {
}) => { }) => {
return TickResult::ExitThread(self.read_register(10) as u64); return TickResult::ExitThread(self.read_register(10) as u64);
} }
Err(e) => self.handle_exception(e, instruction_address), Err(e) => return TickResult::CpuTrap(e),
} }
self.mmu.tick(&mut self.csr[CSR_MIP_ADDRESS as usize]); self.mmu.tick(&mut self.csr[CSR_MIP_ADDRESS as usize]);
self.handle_interrupt(self.pc); self.handle_interrupt(self.pc);

View File

@ -1,15 +1,13 @@
/// Emulates main memory. /// Emulates main memory.
pub struct Memory { pub struct Memory {
/// Memory content /// Memory content
data: Vec<u64> data: Vec<u64>,
} }
impl Memory { impl Memory {
/// Creates a new `Memory` /// Creates a new `Memory`
pub fn new() -> Self { pub fn new() -> Self {
Memory { Memory { data: vec![] }
data: vec![]
}
} }
/// Initializes memory content. /// Initializes memory content.
@ -70,7 +68,8 @@ impl Memory {
let index = (address >> 3) as usize; let index = (address >> 3) as usize;
self.data[index] self.data[index]
} else if (address % 4) == 0 { } else if (address % 4) == 0 {
(self.read_word(address) as u64) | ((self.read_word(address.wrapping_add(4)) as u64) << 4) (self.read_word(address) as u64)
| ((self.read_word(address.wrapping_add(4)) as u64) << 4)
} else { } else {
self.read_bytes(address, 8) self.read_bytes(address, 8)
} }

View File

@ -75,20 +75,33 @@ struct Memory {
satp: u32, satp: u32,
connections: HashMap<u32, Box<dyn services::Service + Send + Sync>>, connections: HashMap<u32, Box<dyn services::Service + Send + Sync>>,
memory_cmd: Sender<MemoryCommand>, memory_cmd: Sender<MemoryCommand>,
turbo: bool,
} }
struct Worker { struct Worker {
cpu: riscv_cpu::Cpu, cpu: riscv_cpu::Cpu,
cmd: Sender<MemoryCommand>, cmd: Sender<MemoryCommand>,
tid: i64, tid: i64,
memory: Arc<RwLock<Memory>>,
} }
impl Worker { impl Worker {
fn new(cpu: riscv_cpu::Cpu, cmd: Sender<MemoryCommand>, tid: i64) -> Self { fn new(
Self { cpu, cmd, tid } cpu: riscv_cpu::Cpu,
cmd: Sender<MemoryCommand>,
tid: i64,
memory: Arc<RwLock<Memory>>,
) -> Self {
Self {
cpu,
cmd,
tid,
memory,
}
} }
fn run(&mut self) { fn run(&mut self) {
use riscv_cpu::cpu::TickResult; use riscv_cpu::cpu::TickResult;
// println!("Running CPU thread {}", self.tid);
loop { loop {
match self.cpu.tick() { match self.cpu.tick() {
TickResult::PauseEmulation(e) => { TickResult::PauseEmulation(e) => {
@ -110,7 +123,21 @@ impl Worker {
.send(MemoryCommand::ExitThread(self.tid as u32, val as u32)) .send(MemoryCommand::ExitThread(self.tid as u32, val as u32))
.unwrap(); .unwrap();
// println!("Thread {} exited", self.tid); // println!("Thread {} exited", self.tid);
break; return;
}
TickResult::CpuTrap(trap) => {
self.memory.read().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?}",
self.cpu.read_pc(),
self.tid,
trap
);
self.cmd
.send(MemoryCommand::ExitThread(self.tid as u32, 1))
.unwrap();
return;
} }
TickResult::Ok => {} TickResult::Ok => {}
} }
@ -124,21 +151,21 @@ struct WorkerHandle {
impl Memory { impl Memory {
pub fn new(base: u32, size: usize) -> (Self, Receiver<MemoryCommand>) { pub fn new(base: u32, size: usize) -> (Self, Receiver<MemoryCommand>) {
let mut data = HashMap::new(); let mut backing = HashMap::new();
let mut free_pages = BTreeSet::new(); let mut free_pages = BTreeSet::new();
let mut allocated_pages = BTreeSet::new(); let mut allocated_pages = BTreeSet::new();
for page in (base..(base + size as u32)).step_by(4096) { for phys in (base..(base + size as u32)).step_by(4096) {
data.insert(page as usize, [0; 4096]); backing.insert(phys as usize, [0; 4096]);
free_pages.insert(page as usize); free_pages.insert(phys as usize);
} }
// Remove the l0 page table // Remove the l0 page table
free_pages.remove(&(MEMORY_BASE as usize + 4096)); assert!(free_pages.remove(&(MEMORY_BASE as usize + 4096)));
allocated_pages.insert(MEMORY_BASE as usize + 4096); assert!(allocated_pages.insert(MEMORY_BASE as usize + 4096));
let (memory_cmd, memory_cmd_rx) = std::sync::mpsc::channel(); let (memory_cmd, memory_cmd_rx) = std::sync::mpsc::channel();
( (
Self { Self {
base, base,
data, data: backing,
allocated_pages, allocated_pages,
free_pages, free_pages,
l1_pt: MEMORY_BASE + 4096, l1_pt: MEMORY_BASE + 4096,
@ -148,26 +175,74 @@ impl Memory {
allocation_previous: 0x4000_0000, allocation_previous: 0x4000_0000,
connections: HashMap::new(), connections: HashMap::new(),
memory_cmd, memory_cmd,
turbo: false,
}, },
memory_cmd_rx, memory_cmd_rx,
) )
} }
pub fn turbo(&mut self) {
self.turbo = true;
}
pub fn normal(&mut self) {
self.turbo = false;
self.memory_ck();
}
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;
// }
// 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 { fn allocate_page(&mut self) -> u32 {
let page = self.free_pages.pop_first().expect("out of memory"); self.memory_ck();
self.allocated_pages.insert(page); let phys = self.free_pages.pop_first().expect("out of memory");
page as u32 assert!(self.allocated_pages.insert(phys));
// 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
} }
fn free_page(&mut self, page: u32) -> Result<(), ()> { fn free_page(&mut self, virt: u32) -> Result<(), ()> {
let phys = self.virt_to_phys(page).ok_or(())?; self.memory_ck();
if !self.allocated_pages.remove(&(phys as usize)) { let phys = self.virt_to_phys(virt).ok_or(())?;
panic!("Page wasn't allocated!");
}
self.free_pages.insert(phys as usize);
let vpn1 = ((page >> 22) & ((1 << 10) - 1)) as usize * 4; let vpn1 = ((virt >> 22) & ((1 << 10) - 1)) as usize * 4;
let vpn0 = ((page >> 12) & ((1 << 10) - 1)) as usize * 4; let vpn0 = ((virt >> 12) & ((1 << 10) - 1)) as usize * 4;
// The root (l1) pagetable is defined to be mapped into our virtual // The root (l1) pagetable is defined to be mapped into our virtual
// address space at this address. // address space at this address.
@ -178,13 +253,25 @@ impl Memory {
return Ok(()); return Ok(());
} }
// 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.free_pages.insert(phys as usize));
let l0_pt_phys = ((l1_pt_entry >> 10) << 12) + vpn0 as u32; let l0_pt_phys = ((l1_pt_entry >> 10) << 12) + vpn0 as u32;
self.write_u32(l0_pt_phys as u64, 0); self.write_u32(l0_pt_phys as u64, 0);
self.memory_ck();
Ok(()) Ok(())
} }
fn allocate_virt_region(&mut self, size: usize) -> Option<u32> { fn allocate_virt_region(&mut self, size: usize) -> Option<u32> {
self.memory_ck();
let mut start = self.allocation_previous; let mut start = self.allocation_previous;
// Find a free region that will fit this page. // Find a free region that will fit this page.
'outer: loop { 'outer: loop {
@ -199,18 +286,22 @@ impl Memory {
// Allocate the region // Allocate the region
for page in (start..(start + size as u32)).step_by(4096) { for page in (start..(start + size as u32)).step_by(4096) {
self.ensure_page(page); self.ensure_page(page);
// println!(
// "Allocated {:08x} @ {:08x}",
// page,
// self.virt_to_phys(page).unwrap()
// );
} }
self.allocation_previous = start + size as u32 + 4096; self.allocation_previous = start + size as u32 + 4096;
self.memory_ck();
Some(start) Some(start)
} }
fn ensure_page(&mut self, address: u32) { fn ensure_page(&mut self, address: u32) {
self.memory_ck();
let vpn1 = ((address >> 22) & ((1 << 10) - 1)) as usize * 4; let vpn1 = ((address >> 22) & ((1 << 10) - 1)) as usize * 4;
let vpn0 = ((address >> 12) & ((1 << 10) - 1)) as usize * 4; let vpn0 = ((address >> 12) & ((1 << 10) - 1)) as usize * 4;
// The root (l1) pagetable is defined to be mapped into our virtual
// address space at this address.
// If the level 1 pagetable doesn't exist, then this address is invalid // 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); let mut l1_pt_entry = self.read_u32(self.l1_pt as u64 + vpn1 as u64);
if l1_pt_entry & MMUFLAG_VALID == 0 { if l1_pt_entry & MMUFLAG_VALID == 0 {
@ -240,6 +331,52 @@ impl Memory {
// Map the level 0 pagetable into the level 1 pagetable // Map the level 0 pagetable into the level 1 pagetable
self.write_u32(l0_pt_phys as u64, l0_pt_entry); self.write_u32(l0_pt_phys as u64, l0_pt_entry);
} }
assert!(self
.allocated_pages
.contains(&(((l0_pt_entry >> 10) << 12) as usize)));
assert!(!self
.free_pages
.contains(&(((l0_pt_entry >> 10) << 12) as usize)));
self.memory_ck();
}
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);
let vpn1 = ((virt >> 22) & ((1 << 10) - 1)) as usize * 4;
let vpn0 = ((virt >> 12) & ((1 << 10) - 1)) as usize * 4;
// The root (l1) pagetable is defined to be mapped into our virtual
// address space at this address.
let l1_pt_entry = self.read_u32(self.l1_pt as u64 + vpn1 as u64);
// If the level 1 pagetable doesn't exist, then this address is invalid
if l1_pt_entry & MMUFLAG_VALID == 0 {
return;
}
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.
if l0_pt_entry & MMUFLAG_VALID == 0 {
return;
}
let old_flags = l0_pt_entry & 0xff;
// Ensure we're not adding flags
assert!(old_flags | new_flags == old_flags);
let l0_pt_entry =
(l0_pt_entry & !(MMUFLAG_READABLE | MMUFLAG_WRITABLE | MMUFLAG_EXECUTABLE)) | new_flags;
self.write_u32(
(((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) { fn write_bytes(&mut self, data: &[u8], start: u32) {
@ -254,35 +391,35 @@ impl Memory {
#[allow(dead_code)] #[allow(dead_code)]
pub fn print_mmu(&self) { pub fn print_mmu(&self) {
use crate::xous::definitions::memoryflags::MemoryFlags;
println!();
println!("Memory Map:"); println!("Memory Map:");
for vpn1 in (0..4096).step_by(4) { for vpn1 in 0..1024 {
let l1_entry = self.read_u32(self.l1_pt as u64 + vpn1); let l1_entry = self.read_u32(self.l1_pt as u64 + vpn1 * 4);
if l1_entry & MMUFLAG_VALID == 0 { if l1_entry & MMUFLAG_VALID == 0 {
continue; continue;
} }
let superpage_addr = vpn1 as u32 * (1 << 22); let superpage_addr = vpn1 as u32 * (1 << 22);
println!( println!(
" {:4} Superpage for {:08x} @ {:08x} (flags: {:?})", " {:4} Superpage for {:08x} @ {:08x} (flags: {})",
vpn1, vpn1,
superpage_addr, superpage_addr,
(l1_entry >> 10) << 12, (l1_entry >> 10) << 12,
// MMUFlags::from_bits(l1_entry & 0xff).unwrap() MemoryFlags::from_bits(l1_entry as usize & 0xff).unwrap(),
l1_entry & 0xff,
); );
for vpn0 in (0..4096).step_by(4) { for vpn0 in 0..1024 {
let l0_entry = self.read_u32((((l1_entry >> 10) << 12) as u64) + vpn0 as u64); let l0_entry = self.read_u32((((l1_entry >> 10) << 12) as u64) + vpn0 as u64 * 4);
if l0_entry & 0x7 == 0 { if l0_entry & 0x1 == 0 {
continue; continue;
} }
let page_addr = vpn0 as u32 * (1 << 12); let page_addr = vpn0 as u32 * (1 << 12);
println!( println!(
" {:4} {:08x} -> {:08x} (flags: {:?})", " {:4} {:08x} -> {:08x} (flags: {})",
vpn0, vpn0,
superpage_addr + page_addr, superpage_addr + page_addr,
(l0_entry >> 10) << 12, (l0_entry >> 10) << 12,
// MMUFlags::from_bits(l0_entry & 0xff).unwrap() MemoryFlags::from_bits(l0_entry as usize & 0xff).unwrap()
l0_entry & 0xff,
); );
} }
} }
@ -424,7 +561,7 @@ impl riscv_cpu::cpu::Memory for Memory {
fn syscall(&mut self, args: [i64; 8]) -> SyscallResult { fn syscall(&mut self, args: [i64; 8]) -> SyscallResult {
let syscall: Syscall = args.into(); let syscall: Syscall = args.into();
// print!("Syscall: "); // println!("Syscall {:?}", SyscallNumber::from(args[0]));
match syscall { match syscall {
Syscall::IncreaseHeap(bytes, _flags) => { Syscall::IncreaseHeap(bytes, _flags) => {
// println!("IncreaseHeap({} bytes, flags: {:02x})", bytes, _flags); // println!("IncreaseHeap({} bytes, flags: {:02x})", bytes, _flags);
@ -458,7 +595,7 @@ impl riscv_cpu::cpu::Memory for Memory {
} }
Syscall::MapMemory(phys, virt, size, _flags) => { Syscall::MapMemory(phys, virt, size, _flags) => {
// println!( // print!(
// "MapMemory(phys: {:08x}, virt: {:08x}, bytes: {}, flags: {:02x})", // "MapMemory(phys: {:08x}, virt: {:08x}, bytes: {}, flags: {:02x})",
// phys, virt, size, _flags // phys, virt, size, _flags
// ); // );
@ -471,6 +608,7 @@ impl riscv_cpu::cpu::Memory for Memory {
let region = self let region = self
.allocate_virt_region(size as usize) .allocate_virt_region(size as usize)
.expect("out of memory"); .expect("out of memory");
// println!(" -> {:08x}", region);
[ [
SyscallResultNumber::MemoryRange as i64, SyscallResultNumber::MemoryRange as i64,
region as i64, region as i64,
@ -649,7 +787,10 @@ impl riscv_cpu::cpu::Memory for Memory {
} }
} }
} }
Syscall::UpdateMemoryFlags(_address, _range, _value) => { Syscall::UpdateMemoryFlags(address, range, value) => {
for addr in address..(address + range) {
self.remove_memory_flags(addr as u32, value as u32);
}
[SyscallResultNumber::Ok as i64, 0, 0, 0, 0, 0, 0, 0].into() [SyscallResultNumber::Ok as i64, 0, 0, 0, 0, 0, 0, 0].into()
} }
Syscall::Yield => [SyscallResultNumber::Ok as i64, 0, 0, 0, 0, 0, 0, 0].into(), Syscall::Yield => [SyscallResultNumber::Ok as i64, 0, 0, 0, 0, 0, 0, 0].into(),
@ -722,7 +863,7 @@ pub struct Machine {
satp: u64, satp: u64,
memory_cmd_sender: Sender<MemoryCommand>, memory_cmd_sender: Sender<MemoryCommand>,
memory_cmd: Receiver<MemoryCommand>, memory_cmd: Receiver<MemoryCommand>,
thread_id: AtomicI64, thread_id_counter: AtomicI64,
} }
impl Machine { impl Machine {
@ -737,7 +878,7 @@ impl Machine {
satp: 0, satp: 0,
memory_cmd, memory_cmd,
memory_cmd_sender, memory_cmd_sender,
thread_id: AtomicI64::new(1), thread_id_counter: AtomicI64::new(1),
}; };
machine.load_program(program)?; machine.load_program(program)?;
@ -760,6 +901,7 @@ impl Machine {
} }
let mut memory_writer = self.memory.write().unwrap(); let mut memory_writer = self.memory.write().unwrap();
memory_writer.turbo();
for sh in elf.section_headers { for sh in elf.section_headers {
if sh.sh_flags as u32 & goblin::elf::section_header::SHF_ALLOC == 0 { if sh.sh_flags as u32 & goblin::elf::section_header::SHF_ALLOC == 0 {
continue; continue;
@ -776,7 +918,8 @@ impl Machine {
} }
} }
memory_writer.print_mmu(); // memory_writer.print_mmu();
memory_writer.normal();
// TODO: Get memory permissions correct // TODO: Get memory permissions correct
@ -802,10 +945,11 @@ impl Machine {
cpu.execute_opcode(0x10200073).map_err(LoadError::CpuTrap)?; cpu.execute_opcode(0x10200073).map_err(LoadError::CpuTrap)?;
// Update the stack pointer // Update the stack pointer
cpu.write_register(2, 0xc002_0000 - 4); cpu.write_register(2, 0xc002_0000 - 16);
let cmd = self.memory_cmd_sender.clone(); let cmd = self.memory_cmd_sender.clone();
let joiner = std::thread::spawn(move || Worker::new(cpu, cmd, 0).run()); let memory = self.memory.clone();
let joiner = std::thread::spawn(move || Worker::new(cpu, cmd, 0, memory).run());
self.workers.push(WorkerHandle { joiner }); self.workers.push(WorkerHandle { joiner });
self.satp = satp; self.satp = satp;
@ -868,7 +1012,7 @@ impl Machine {
MemoryCommand::CreateThread( MemoryCommand::CreateThread(
entry_point, entry_point,
stack_pointer, stack_pointer,
_stack_length, stack_length,
argument_1, argument_1,
argument_2, argument_2,
argument_3, argument_3,
@ -894,16 +1038,19 @@ impl Machine {
cpu.execute_opcode(0x10200073).map_err(LoadError::CpuTrap)?; cpu.execute_opcode(0x10200073).map_err(LoadError::CpuTrap)?;
// Update the stack pointer // Update the stack pointer
cpu.write_register(2, stack_pointer as i64 - 16); cpu.write_register(2, (stack_pointer + stack_length) as i64 - 16);
cpu.write_register(10, argument_1 as i64); cpu.write_register(10, argument_1 as i64);
cpu.write_register(11, argument_2 as i64); cpu.write_register(11, argument_2 as i64);
cpu.write_register(12, argument_3 as i64); cpu.write_register(12, argument_3 as i64);
cpu.write_register(13, argument_4 as i64); cpu.write_register(13, argument_4 as i64);
let cmd = self.memory_cmd_sender.clone(); let cmd = self.memory_cmd_sender.clone();
let tid = self.thread_id.fetch_add(1, Ordering::SeqCst); let tid = self.thread_id_counter.fetch_add(1, Ordering::SeqCst);
let memory = self.memory.clone();
join_tx join_tx
.send(std::thread::spawn(move || Worker::new(cpu, cmd, tid).run())) .send(std::thread::spawn(move || {
Worker::new(cpu, cmd, tid, memory).run()
}))
.unwrap(); .unwrap();
tx.send(tid).unwrap(); tx.send(tid).unwrap();
} }

View File

@ -1,5 +1,7 @@
#![allow(dead_code)] #![allow(dead_code)]
pub mod memoryflags;
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum Message { pub enum Message {
MutableBorrow = 0, MutableBorrow = 0,