Revised trap API
This commit is contained in:
parent
6afec2a031
commit
0ed89234a7
|
@ -1,8 +1,4 @@
|
||||||
use hbvm::vm::{
|
use hbvm::vm::mem::{HandlePageFault, Memory, MemoryAccessReason, PageSize};
|
||||||
mem::{Memory, MemoryAccessReason, PageSize},
|
|
||||||
trap::HandleTrap,
|
|
||||||
value::Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
use {
|
use {
|
||||||
hbvm::{validate::validate, vm::Vm},
|
hbvm::{validate::validate, vm::Vm},
|
||||||
|
@ -32,7 +28,7 @@ pub fn time() -> u32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TestTrapHandler;
|
struct TestTrapHandler;
|
||||||
impl HandleTrap for TestTrapHandler {
|
impl HandlePageFault for TestTrapHandler {
|
||||||
fn page_fault(
|
fn page_fault(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: MemoryAccessReason,
|
_: MemoryAccessReason,
|
||||||
|
@ -43,17 +39,4 @@ impl HandleTrap for TestTrapHandler {
|
||||||
) -> bool {
|
) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invalid_op(&mut self, _: &mut [Value; 256], _: &mut usize, _: &mut Memory, _: u8) -> bool
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ecall(&mut self, _: &mut [Value; 256], _: &mut usize, _: &mut Memory)
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,13 @@
|
||||||
|
|
||||||
pub mod paging;
|
pub mod paging;
|
||||||
|
|
||||||
|
mod pfhandler;
|
||||||
|
|
||||||
|
pub use pfhandler::HandlePageFault;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
self::paging::{PageTable, Permission, PtEntry},
|
self::paging::{PageTable, Permission, PtEntry},
|
||||||
super::{trap::HandleTrap, VmRunError},
|
super::VmRunError,
|
||||||
alloc::boxed::Box,
|
alloc::boxed::Box,
|
||||||
core::mem::MaybeUninit,
|
core::mem::MaybeUninit,
|
||||||
derive_more::Display,
|
derive_more::Display,
|
||||||
|
@ -69,7 +73,7 @@ impl Memory {
|
||||||
addr: u64,
|
addr: u64,
|
||||||
target: *mut u8,
|
target: *mut u8,
|
||||||
count: usize,
|
count: usize,
|
||||||
traph: &mut impl HandleTrap,
|
traph: &mut impl HandlePageFault,
|
||||||
) -> Result<(), LoadError> {
|
) -> Result<(), LoadError> {
|
||||||
self.memory_access(
|
self.memory_access(
|
||||||
MemoryAccessReason::Load,
|
MemoryAccessReason::Load,
|
||||||
|
@ -97,7 +101,7 @@ impl Memory {
|
||||||
addr: u64,
|
addr: u64,
|
||||||
source: *const u8,
|
source: *const u8,
|
||||||
count: usize,
|
count: usize,
|
||||||
traph: &mut impl HandleTrap,
|
traph: &mut impl HandlePageFault,
|
||||||
) -> Result<(), StoreError> {
|
) -> Result<(), StoreError> {
|
||||||
self.memory_access(
|
self.memory_access(
|
||||||
MemoryAccessReason::Store,
|
MemoryAccessReason::Store,
|
||||||
|
@ -122,7 +126,7 @@ impl Memory {
|
||||||
src: u64,
|
src: u64,
|
||||||
dst: u64,
|
dst: u64,
|
||||||
count: usize,
|
count: usize,
|
||||||
traph: &mut impl HandleTrap,
|
traph: &mut impl HandlePageFault,
|
||||||
) -> Result<(), BlkCopyError> {
|
) -> Result<(), BlkCopyError> {
|
||||||
// Yea, i know it is possible to do this more efficiently, but I am too lazy.
|
// Yea, i know it is possible to do this more efficiently, but I am too lazy.
|
||||||
|
|
||||||
|
@ -209,7 +213,7 @@ impl Memory {
|
||||||
len: usize,
|
len: usize,
|
||||||
permission_check: fn(Permission) -> bool,
|
permission_check: fn(Permission) -> bool,
|
||||||
action: fn(*mut u8, *mut u8, usize),
|
action: fn(*mut u8, *mut u8, usize),
|
||||||
traph: &mut impl HandleTrap,
|
traph: &mut impl HandlePageFault,
|
||||||
) -> Result<(), u64> {
|
) -> Result<(), u64> {
|
||||||
let mut pspl = AddrSplitter::new(src, len, self.root_pt);
|
let mut pspl = AddrSplitter::new(src, len, self.root_pt);
|
||||||
loop {
|
loop {
|
||||||
|
|
16
hbvm/src/vm/mem/pfhandler.rs
Normal file
16
hbvm/src/vm/mem/pfhandler.rs
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
//! Program trap handling interfaces
|
||||||
|
|
||||||
|
use super::{Memory, MemoryAccessReason, PageSize};
|
||||||
|
|
||||||
|
/// Handle VM traps
|
||||||
|
pub trait HandlePageFault {
|
||||||
|
/// Handle page fault
|
||||||
|
fn page_fault(
|
||||||
|
&mut self,
|
||||||
|
reason: MemoryAccessReason,
|
||||||
|
memory: &mut Memory,
|
||||||
|
vaddr: u64,
|
||||||
|
size: PageSize,
|
||||||
|
dataptr: *mut u8,
|
||||||
|
) -> bool;
|
||||||
|
}
|
|
@ -11,10 +11,9 @@
|
||||||
// program size. If you are (rightfully) worried about the UB, for now just
|
// program size. If you are (rightfully) worried about the UB, for now just
|
||||||
// append your program with 11 zeroes.
|
// append your program with 11 zeroes.
|
||||||
|
|
||||||
use self::trap::HandleTrap;
|
use self::mem::HandlePageFault;
|
||||||
|
|
||||||
pub mod mem;
|
pub mod mem;
|
||||||
pub mod trap;
|
|
||||||
pub mod value;
|
pub mod value;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
|
@ -79,7 +78,7 @@ macro_rules! cond_jump {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// HoleyBytes Virtual Machine
|
/// HoleyBytes Virtual Machine
|
||||||
pub struct Vm<'a, T, const TIMER_QUOTIENT: usize> {
|
pub struct Vm<'a, PfHandler, const TIMER_QUOTIENT: usize> {
|
||||||
/// Holds 256 registers
|
/// Holds 256 registers
|
||||||
///
|
///
|
||||||
/// Writing to register 0 is considered undefined behaviour
|
/// Writing to register 0 is considered undefined behaviour
|
||||||
|
@ -90,7 +89,7 @@ pub struct Vm<'a, T, const TIMER_QUOTIENT: usize> {
|
||||||
pub memory: Memory,
|
pub memory: Memory,
|
||||||
|
|
||||||
/// Trap handler
|
/// Trap handler
|
||||||
pub traph: T,
|
pub pfhandler: PfHandler,
|
||||||
|
|
||||||
// Program counter
|
// Program counter
|
||||||
pc: usize,
|
pc: usize,
|
||||||
|
@ -102,16 +101,18 @@ pub struct Vm<'a, T, const TIMER_QUOTIENT: usize> {
|
||||||
timer: usize,
|
timer: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: HandleTrap, const TIMER_QUOTIENT: usize> Vm<'a, T, TIMER_QUOTIENT> {
|
impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize>
|
||||||
|
Vm<'a, PfHandler, TIMER_QUOTIENT>
|
||||||
|
{
|
||||||
/// Create a new VM with program and trap handler
|
/// Create a new VM with program and trap handler
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Program code has to be validated
|
/// Program code has to be validated
|
||||||
pub unsafe fn new_unchecked(program: &'a [u8], traph: T) -> Self {
|
pub unsafe fn new_unchecked(program: &'a [u8], traph: PfHandler) -> Self {
|
||||||
Self {
|
Self {
|
||||||
registers: [Value::from(0_u64); 256],
|
registers: [Value::from(0_u64); 256],
|
||||||
memory: Default::default(),
|
memory: Default::default(),
|
||||||
traph,
|
pfhandler: traph,
|
||||||
pc: 0,
|
pc: 0,
|
||||||
program,
|
program,
|
||||||
timer: 0,
|
timer: 0,
|
||||||
|
@ -119,7 +120,7 @@ impl<'a, T: HandleTrap, const TIMER_QUOTIENT: usize> Vm<'a, T, TIMER_QUOTIENT> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new VM with program and trap handler only if it passes validation
|
/// Create a new VM with program and trap handler only if it passes validation
|
||||||
pub fn new_validated(program: &'a [u8], traph: T) -> Result<Self, validate::Error> {
|
pub fn new_validated(program: &'a [u8], traph: PfHandler) -> Result<Self, validate::Error> {
|
||||||
validate::validate(program)?;
|
validate::validate(program)?;
|
||||||
Ok(unsafe { Self::new_unchecked(program, traph) })
|
Ok(unsafe { Self::new_unchecked(program, traph) })
|
||||||
}
|
}
|
||||||
|
@ -229,7 +230,7 @@ impl<'a, T: HandleTrap, const TIMER_QUOTIENT: usize> Vm<'a, T, TIMER_QUOTIENT> {
|
||||||
self.read_reg(base).as_u64() + off + n as u64,
|
self.read_reg(base).as_u64() + off + n as u64,
|
||||||
self.registers.as_mut_ptr().add(usize::from(dst) + n).cast(),
|
self.registers.as_mut_ptr().add(usize::from(dst) + n).cast(),
|
||||||
usize::from(count).saturating_sub(n),
|
usize::from(count).saturating_sub(n),
|
||||||
&mut self.traph,
|
&mut self.pfhandler,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
ST => {
|
ST => {
|
||||||
|
@ -238,7 +239,7 @@ impl<'a, T: HandleTrap, const TIMER_QUOTIENT: usize> Vm<'a, T, TIMER_QUOTIENT> {
|
||||||
self.read_reg(base).as_u64() + off,
|
self.read_reg(base).as_u64() + off,
|
||||||
self.registers.as_ptr().add(usize::from(dst)).cast(),
|
self.registers.as_ptr().add(usize::from(dst)).cast(),
|
||||||
count.into(),
|
count.into(),
|
||||||
&mut self.traph,
|
&mut self.pfhandler,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
BMC => {
|
BMC => {
|
||||||
|
@ -247,7 +248,7 @@ impl<'a, T: HandleTrap, const TIMER_QUOTIENT: usize> Vm<'a, T, TIMER_QUOTIENT> {
|
||||||
self.read_reg(src).as_u64(),
|
self.read_reg(src).as_u64(),
|
||||||
self.read_reg(dst).as_u64(),
|
self.read_reg(dst).as_u64(),
|
||||||
count as _,
|
count as _,
|
||||||
&mut self.traph,
|
&mut self.pfhandler,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
BRC => {
|
BRC => {
|
||||||
|
@ -275,8 +276,7 @@ impl<'a, T: HandleTrap, const TIMER_QUOTIENT: usize> Vm<'a, T, TIMER_QUOTIENT> {
|
||||||
JGTU => cond_jump!(self, sint, Greater),
|
JGTU => cond_jump!(self, sint, Greater),
|
||||||
ECALL => {
|
ECALL => {
|
||||||
param!(self, ());
|
param!(self, ());
|
||||||
self.traph
|
return Ok(VmRunOk::Ecall);
|
||||||
.ecall(&mut self.registers, &mut self.pc, &mut self.memory);
|
|
||||||
}
|
}
|
||||||
ADDF => binary_op!(self, as_f64, ops::Add::add),
|
ADDF => binary_op!(self, as_f64, ops::Add::add),
|
||||||
SUBF => binary_op!(self, as_f64, ops::Sub::sub),
|
SUBF => binary_op!(self, as_f64, ops::Sub::sub),
|
||||||
|
@ -310,16 +310,7 @@ impl<'a, T: HandleTrap, const TIMER_QUOTIENT: usize> Vm<'a, T, TIMER_QUOTIENT> {
|
||||||
}
|
}
|
||||||
ADDFI => binary_op_imm!(self, as_f64, ops::Add::add),
|
ADDFI => binary_op_imm!(self, as_f64, ops::Add::add),
|
||||||
MULFI => binary_op_imm!(self, as_f64, ops::Mul::mul),
|
MULFI => binary_op_imm!(self, as_f64, ops::Mul::mul),
|
||||||
op => {
|
op => return Err(VmRunError::InvalidOpcode(op)),
|
||||||
if !self.traph.invalid_op(
|
|
||||||
&mut self.registers,
|
|
||||||
&mut self.pc,
|
|
||||||
&mut self.memory,
|
|
||||||
op,
|
|
||||||
) {
|
|
||||||
return Err(VmRunError::InvalidOpcodeEx(op));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,8 +343,8 @@ impl<'a, T: HandleTrap, const TIMER_QUOTIENT: usize> Vm<'a, T, TIMER_QUOTIENT> {
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
pub enum VmRunError {
|
pub enum VmRunError {
|
||||||
/// Unhandled invalid opcode exceptions
|
/// Tried to execute invalid instruction
|
||||||
InvalidOpcodeEx(u8),
|
InvalidOpcode(u8),
|
||||||
|
|
||||||
/// Unhandled load access exception
|
/// Unhandled load access exception
|
||||||
LoadAccessEx(u64),
|
LoadAccessEx(u64),
|
||||||
|
@ -370,4 +361,7 @@ pub enum VmRunOk {
|
||||||
|
|
||||||
/// Program was interrupted by a timer
|
/// Program was interrupted by a timer
|
||||||
Timer,
|
Timer,
|
||||||
|
|
||||||
|
/// Environment call
|
||||||
|
Ecall,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
//! Program trap handling interfaces
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
mem::{Memory, MemoryAccessReason, PageSize},
|
|
||||||
value::Value,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Handle VM traps
|
|
||||||
pub trait HandleTrap {
|
|
||||||
/// Handle page fault
|
|
||||||
fn page_fault(
|
|
||||||
&mut self,
|
|
||||||
reason: MemoryAccessReason,
|
|
||||||
memory: &mut Memory,
|
|
||||||
vaddr: u64,
|
|
||||||
size: PageSize,
|
|
||||||
dataptr: *mut u8,
|
|
||||||
) -> bool;
|
|
||||||
|
|
||||||
/// Handle invalid opcode exception
|
|
||||||
fn invalid_op(
|
|
||||||
&mut self,
|
|
||||||
regs: &mut [Value; 256],
|
|
||||||
pc: &mut usize,
|
|
||||||
memory: &mut Memory,
|
|
||||||
op: u8,
|
|
||||||
) -> bool
|
|
||||||
where
|
|
||||||
Self: Sized;
|
|
||||||
|
|
||||||
/// Handle environment calls
|
|
||||||
fn ecall(&mut self, regs: &mut [Value; 256], pc: &mut usize, memory: &mut Memory)
|
|
||||||
where
|
|
||||||
Self: Sized;
|
|
||||||
}
|
|
Loading…
Reference in a new issue