No longer use setjmp/longjmp to ensure things are atomic

This commit is contained in:
Ry 2022-09-18 16:21:35 -07:00
parent 3aaebeaf3e
commit f7b2320138
4 changed files with 566 additions and 562 deletions

File diff suppressed because it is too large Load Diff

View File

@ -7,11 +7,10 @@ pub mod cpu;
pub mod keyboard;
pub mod mouse;
pub mod disk;
pub mod setjmp;
use audio::AudioChannel;
use bus::Bus;
use cpu::{Cpu, Interrupt};
use cpu::{Cpu, Exception, Interrupt};
use keyboard::Keyboard;
use mouse::Mouse;
use disk::DiskController;
@ -86,9 +85,9 @@ fn main() {
let audio_channel_2 = Arc::new(Mutex::new(AudioChannel::new(2)));
let audio_channel_3 = Arc::new(Mutex::new(AudioChannel::new(3)));
//let (exception_sender, exception_receiver) = mpsc::channel::<Exception>();
let (exception_sender, exception_receiver) = mpsc::channel::<Exception>();
let memory = Memory::new(read_rom().as_slice());
let memory = Memory::new(read_rom().as_slice(), exception_sender);
let mut bus = Bus {
memory: memory.clone(),
audio_channel_0: audio_channel_0.clone(),
@ -159,16 +158,14 @@ fn main() {
builder.spawn({
move || {
loop {
if let Some(exception) = unsafe { cpu.setjmp() } {
cpu.interrupt(Interrupt::Exception(exception));
}
while !cpu.halted {
/*if let Ok(exception) = exception_receiver.try_recv() {
if let Ok(exception) = exception_receiver.try_recv() {
cpu.interrupt(Interrupt::Exception(exception));
}*/
} else {
if let Ok(interrupt) = interrupt_receiver.try_recv() {
cpu.interrupt(interrupt);
}
}
cpu.execute_memory_instruction();
if let Ok(_) = exit_receiver.try_recv() {
// the rest of the VM has exited, stop the CPU thread

View File

@ -2,13 +2,13 @@
use crate::error;
use crate::cpu::Exception;
use crate::setjmp::{JumpEnv, longjmp};
use std::cell::UnsafeCell;
use std::collections::HashMap;
use std::sync::Arc;
use std::io::Write;
use std::fs::File;
use std::sync::mpsc::Sender;
pub const MEMORY_RAM_SIZE: usize = 0x04000000; // 64 MiB
pub const MEMORY_ROM_SIZE: usize = 0x00080000; // 512 KiB
@ -32,10 +32,11 @@ struct MemoryInner {
mmu_enabled: Box<bool>,
tlb: Box<HashMap<u32, MemoryPage>>,
paging_directory_address: Box<u32>,
exception_sender: Sender<Exception>,
}
impl MemoryInner {
pub fn new(rom: &[u8]) -> Self {
pub fn new(rom: &[u8], exception_sender: Sender<Exception>) -> Self {
let mut this = Self {
// HACK: allocate directly on the heap to avoid a stack overflow
// at runtime while trying to move around a 64MB array
@ -44,6 +45,7 @@ impl MemoryInner {
mmu_enabled: Box::from(false),
tlb: Box::from(HashMap::with_capacity(1024)),
paging_directory_address: Box::from(0x00000000),
exception_sender,
};
this.rom.as_mut_slice().write(rom).expect("failed to copy ROM to memory");
this
@ -60,8 +62,8 @@ unsafe impl Send for Memory {}
unsafe impl Sync for Memory {}
impl Memory {
pub fn new(rom: &[u8]) -> Self {
Self(Arc::new(UnsafeCell::new(MemoryInner::new(rom))))
pub fn new(rom: &[u8], exception_sender: Sender<Exception>) -> Self {
Self(Arc::new(UnsafeCell::new(MemoryInner::new(rom, exception_sender))))
}
fn inner(&self) -> &mut MemoryInner {
@ -73,6 +75,7 @@ impl Memory {
pub fn mmu_enabled(&self) -> &mut bool { &mut self.inner().mmu_enabled }
pub fn tlb(&self) -> &mut HashMap<u32, MemoryPage> { &mut self.inner().tlb }
pub fn paging_directory_address(&self) -> &mut u32 { &mut self.inner().paging_directory_address }
pub fn exception_sender(&self) -> &mut Sender<Exception> { &mut self.inner().exception_sender }
pub fn dump(&self) {
let mut file = File::create("memory.dump").expect("failed to open memory dump file");
@ -212,26 +215,45 @@ impl Memory {
)
}
pub fn read_8(&mut self, onfault: &JumpEnv<Exception>, address: u32) -> u8 {
self.read_opt_8(address).unwrap_or_else(|| unsafe { longjmp(onfault, Exception::PageFaultRead(address)) })
pub fn read_8(&mut self, address: u32) -> Option<u8> {
let mut read_ok = true;
let value = self.read_opt_8(address).unwrap_or_else(|| { read_ok = false; 0 });
if read_ok {
Some(value)
} else {
self.exception_sender().send(Exception::PageFaultRead(address)).unwrap();
None
}
pub fn read_16(&mut self, onfault: &JumpEnv<Exception>, address: u32) -> u16 {
(self.read_opt_8(address).unwrap_or_else(|| unsafe { longjmp(onfault, Exception::PageFaultRead(address)) }) as u16) |
(self.read_opt_8(address + 1).unwrap_or_else(|| unsafe { longjmp(onfault, Exception::PageFaultRead(address + 1)) }) as u16) << 8
}
pub fn read_32(&mut self, onfault: &JumpEnv<Exception>, address: u32) -> u32 {
(self.read_opt_8(address).unwrap_or_else(|| unsafe { longjmp(onfault, Exception::PageFaultRead(address)) }) as u32) |
(self.read_opt_8(address + 1).unwrap_or_else(|| unsafe { longjmp(onfault, Exception::PageFaultRead(address + 1)) }) as u32) << 8 |
(self.read_opt_8(address + 2).unwrap_or_else(|| unsafe { longjmp(onfault, Exception::PageFaultRead(address + 2)) }) as u32) << 16 |
(self.read_opt_8(address + 3).unwrap_or_else(|| unsafe { longjmp(onfault, Exception::PageFaultRead(address + 3)) }) as u32) << 24
pub fn read_16(&mut self, address: u32) -> Option<u16> {
let mut read_ok = true;
let value = self.read_8(address).unwrap_or_else(|| { read_ok = false; 0 }) as u16 |
(self.read_8(address + 1).unwrap_or_else(|| { read_ok = false; 0 }) as u16) << 8;
if read_ok {
Some(value)
} else {
None
}
}
pub fn read_32(&mut self, address: u32) -> Option<u32> {
let mut read_ok = true;
let value = self.read_8(address).unwrap_or_else(|| { read_ok = false; 0 }) as u32 |
(self.read_8(address + 1).unwrap_or_else(|| { read_ok = false; 0 }) as u32) << 8 |
(self.read_8(address + 2).unwrap_or_else(|| { read_ok = false; 0 }) as u32) << 16 |
(self.read_8(address + 3).unwrap_or_else(|| { read_ok = false; 0 }) as u32) << 24;
if read_ok {
Some(value)
} else {
None
}
}
pub fn write_8(&mut self, onfault: &JumpEnv<Exception>, mut address: u32, byte: u8) {
pub fn write_8(&mut self, mut address: u32, byte: u8) -> Option<()> {
let original_address = address;
let mut writable = true;
if *self.mmu_enabled() {
(address, writable) = self.virtual_to_physical(address as u32).unwrap_or_else(|| {
unsafe { longjmp(onfault, Exception::PageFaultWrite(original_address)) }
(0, false)
});
}
@ -247,21 +269,33 @@ impl Memory {
*value = byte;
}
None => {
unsafe { longjmp(onfault, Exception::PageFaultWrite(original_address)) }
self.exception_sender().send(Exception::PageFaultWrite(original_address)).unwrap();
}
}
Some(())
} else {
unsafe { longjmp(onfault, Exception::PageFaultWrite(original_address)) }
self.exception_sender().send(Exception::PageFaultWrite(original_address)).unwrap();
None
}
}
pub fn write_16(&mut self, onfault: &JumpEnv<Exception>, address: u32, half: u16) {
self.write_8(onfault, address, (half & 0x00FF) as u8);
self.write_8(onfault, address + 1, (half >> 8) as u8);
}
pub fn write_32(&mut self, onfault: &JumpEnv<Exception>, address: u32, word: u32) {
self.write_8(onfault, address, (word & 0x000000FF) as u8);
self.write_8(onfault, address + 1, ((word & 0x0000FF00) >> 8) as u8);
self.write_8(onfault, address + 2, ((word & 0x00FF0000) >> 16) as u8);
self.write_8(onfault, address + 3, ((word & 0xFF000000) >> 24) as u8);
pub fn write_16(&mut self, address: u32, half: u16) -> Option<()> {
let result_0 = self.write_8(address, (half & 0x00FF) as u8);
let result_1 = self.write_8(address + 1, (half >> 8) as u8);
if let (Some(_), Some(_)) = (result_0, result_1) {
Some(())
} else {
None
}
}
pub fn write_32(&mut self, address: u32, word: u32) -> Option<()> {
let result_0 = self.write_8(address, (word & 0x000000FF) as u8);
let result_1 = self.write_8(address + 1, ((word & 0x0000FF00) >> 8) as u8);
let result_2 = self.write_8(address + 2, ((word & 0x00FF0000) >> 16) as u8);
let result_3 = self.write_8(address + 3, ((word & 0xFF000000) >> 24) as u8);
if let (Some(_), Some(_), Some(_), Some(_)) = (result_0, result_1, result_2, result_3) {
Some(())
} else {
None
}
}
}

View File

@ -1,56 +0,0 @@
// setjmp.rs
use std::os::raw::*;
mod internal {
use std::os::raw::*;
use std::cell::*;
extern "C" {
pub fn setjmp(env: *mut c_void) -> c_int;
pub fn longjmp(env: *mut c_void, status: c_int) -> !;
}
pub type JumpBuf = [u8; 512];
pub struct JumpEnv<T>{
buffer: Box<RefCell<JumpBuf>>,
value: Cell<Option<T>>,
}
impl<T> JumpEnv<T> {
pub fn new() -> Self {
Self {
buffer: Box::new(RefCell::new([0u8; 512])),
value: Cell::default(),
}
}
pub fn buffer(&self) -> RefMut<JumpBuf> {
self.buffer.borrow_mut()
}
pub fn value_set(&self, value: T) {
self.value.set(Some(value));
}
pub fn value_take(&self) -> Option<T> {
self.value.take()
}
}
}
pub use internal::JumpEnv;
pub unsafe fn setjmp<T>(env: &JumpEnv<T>) -> Option<T> {
if internal::setjmp(env.buffer().as_mut_ptr() as *mut c_void) != 0 {
env.value_take()
} else {
None
}
}
pub unsafe fn longjmp<T>(env: &JumpEnv<T>, value: T) -> ! {
env.value_set(value);
internal::longjmp(env.buffer().as_mut_ptr() as *mut c_void, 1)
}