comments
This commit is contained in:
parent
b237ad90ba
commit
a47b556317
35
Cargo.lock
generated
35
Cargo.lock
generated
|
@ -25,6 +25,12 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||
|
||||
[[package]]
|
||||
name = "delegate"
|
||||
version = "0.9.0"
|
||||
|
@ -36,6 +42,19 @@ dependencies = [
|
|||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
|
@ -70,6 +89,7 @@ name = "hbvm"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"delegate",
|
||||
"derive_more",
|
||||
"hashbrown",
|
||||
"hbbytecode",
|
||||
"log",
|
||||
|
@ -163,6 +183,21 @@ version = "0.6.29"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
|
|
|
@ -8,6 +8,7 @@ lto = true
|
|||
|
||||
[dependencies]
|
||||
delegate = "0.9"
|
||||
derive_more = "0.99"
|
||||
hashbrown = "0.13"
|
||||
hbbytecode.path = "../hbbytecode"
|
||||
log = "0.4"
|
||||
|
|
|
@ -3,20 +3,3 @@ extern crate alloc;
|
|||
|
||||
pub mod validate;
|
||||
pub mod vm;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum RuntimeErrors {
|
||||
InvalidOpcodePair(u8, u8),
|
||||
RegisterTooSmall,
|
||||
HostError(u64),
|
||||
PageNotMapped(u64),
|
||||
InvalidJumpAddress(u64),
|
||||
InvalidSystemCall(u8),
|
||||
}
|
||||
|
||||
// If you solve the halting problem feel free to remove this
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum HaltStatus {
|
||||
Halted,
|
||||
Running,
|
||||
}
|
||||
|
|
|
@ -1,21 +1,31 @@
|
|||
/// Program validation error kind
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum ErrorKind {
|
||||
/// Unknown opcode
|
||||
InvalidInstruction,
|
||||
/// VM doesn't implement this valid opcode
|
||||
Unimplemented,
|
||||
/// Attempted to copy over register boundary
|
||||
RegisterArrayOverflow,
|
||||
}
|
||||
|
||||
/// Error
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct Error {
|
||||
/// Kind
|
||||
pub kind: ErrorKind,
|
||||
/// Location in bytecode
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
/// Perform bytecode validation. If it passes, the program should be
|
||||
/// sound to execute.
|
||||
pub fn validate(mut program: &[u8]) -> Result<(), Error> {
|
||||
use hbbytecode::opcode::*;
|
||||
|
||||
let start = program;
|
||||
loop {
|
||||
// Match on instruction types and perform necessary checks
|
||||
program = match program {
|
||||
[] => return Ok(()),
|
||||
[LD..=ST, reg, _, _, _, _, _, _, _, _, _, count, ..]
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
mod paging;
|
||||
|
||||
use self::paging::{PageTable, Permission, PtEntry};
|
||||
use alloc::boxed::Box;
|
||||
|
||||
use super::trap::HandleTrap;
|
||||
use alloc::boxed::Box;
|
||||
use derive_more::Display;
|
||||
|
||||
/// HoleyBytes virtual memory
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Memory {
|
||||
/// Root page table
|
||||
root_pt: *mut PageTable,
|
||||
}
|
||||
|
||||
|
@ -49,18 +51,21 @@ impl Memory {
|
|||
entry = PtEntry::new(Box::into_raw(pt) as _, Permission::Node);
|
||||
}
|
||||
|
||||
self.root_pt_mut()[0] = entry;
|
||||
(*self.root_pt)[0] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
/// Load value from an address
|
||||
///
|
||||
/// # Safety
|
||||
/// Applies same conditions as for [`core::ptr::copy_nonoverlapping`]
|
||||
pub unsafe fn load(
|
||||
&mut self,
|
||||
addr: u64,
|
||||
target: *mut u8,
|
||||
count: usize,
|
||||
traph: &mut impl HandleTrap,
|
||||
) -> Result<(), ()> {
|
||||
) -> Result<(), LoadError> {
|
||||
self.memory_access(
|
||||
addr,
|
||||
target,
|
||||
|
@ -74,16 +79,20 @@ impl Memory {
|
|||
|src, dst, count| core::ptr::copy_nonoverlapping(src, dst, count),
|
||||
traph,
|
||||
)
|
||||
.map_err(|_| LoadError)
|
||||
}
|
||||
|
||||
/// Store value to an address
|
||||
///
|
||||
/// # Safety
|
||||
/// Applies same conditions as for [`core::ptr::copy_nonoverlapping`]
|
||||
pub unsafe fn store(
|
||||
&mut self,
|
||||
addr: u64,
|
||||
source: *const u8,
|
||||
count: usize,
|
||||
traph: &mut impl HandleTrap,
|
||||
) -> Result<(), ()> {
|
||||
) -> Result<(), StoreError> {
|
||||
self.memory_access(
|
||||
addr,
|
||||
source.cast_mut(),
|
||||
|
@ -92,6 +101,7 @@ impl Memory {
|
|||
|dst, src, count| core::ptr::copy_nonoverlapping(src, dst, count),
|
||||
traph,
|
||||
)
|
||||
.map_err(|_| StoreError)
|
||||
}
|
||||
|
||||
/// Copy a block of memory
|
||||
|
@ -124,19 +134,14 @@ impl Memory {
|
|||
_ => return Err(()),
|
||||
}
|
||||
} */
|
||||
// TODO
|
||||
Err(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn root_pt(&self) -> &PageTable {
|
||||
unsafe { &*self.root_pt }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn root_pt_mut(&mut self) -> &mut PageTable {
|
||||
unsafe { &mut *self.root_pt }
|
||||
}
|
||||
|
||||
/// Split address to pages, check their permissions and feed pointers with offset
|
||||
/// to a specified function.
|
||||
///
|
||||
/// If page is not found, execute page fault trap handler.
|
||||
fn memory_access(
|
||||
&mut self,
|
||||
src: u64,
|
||||
|
@ -146,23 +151,29 @@ impl Memory {
|
|||
action: fn(*mut u8, *mut u8, usize),
|
||||
traph: &mut impl HandleTrap,
|
||||
) -> Result<(), ()> {
|
||||
let mut pspl = PageSplitter::new(src, len, self.root_pt);
|
||||
let mut pspl = AddrSplitter::new(src, len, self.root_pt);
|
||||
loop {
|
||||
match pspl.next() {
|
||||
Some(Ok(PageSplitResult { ptr, size, perm })) => {
|
||||
// Page found
|
||||
Some(Ok(AddrSplitOk { ptr, size, perm })) => {
|
||||
if !permission_check(perm) {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
// Perform memory action and bump dst pointer
|
||||
action(ptr, dst, size);
|
||||
dst = unsafe { dst.add(size) };
|
||||
}
|
||||
Some(Err(PageSplitError { addr, size })) => {
|
||||
Some(Err(AddrSplitError { addr, size })) => {
|
||||
// Execute page fault handler
|
||||
if traph.page_fault(self, addr, size, dst) {
|
||||
pspl.jump_page(size);
|
||||
// Shift the splitter address
|
||||
pspl.bump(size);
|
||||
|
||||
// Bump dst pointer
|
||||
dst = unsafe { dst.add(size as _) };
|
||||
} else {
|
||||
return Err(());
|
||||
return Err(()); // Unhandleable
|
||||
}
|
||||
}
|
||||
None => return Ok(()),
|
||||
|
@ -171,24 +182,40 @@ impl Memory {
|
|||
}
|
||||
}
|
||||
|
||||
struct PageSplitResult {
|
||||
/// Result from address split
|
||||
struct AddrSplitOk {
|
||||
/// Pointer to the start for perform operation
|
||||
ptr: *mut u8,
|
||||
|
||||
/// Size to the end of page / end of desired size
|
||||
size: usize,
|
||||
|
||||
/// Page permission
|
||||
perm: Permission,
|
||||
}
|
||||
|
||||
struct PageSplitError {
|
||||
struct AddrSplitError {
|
||||
/// Address of failure
|
||||
addr: u64,
|
||||
|
||||
/// Requested page size
|
||||
size: PageSize,
|
||||
}
|
||||
|
||||
struct PageSplitter {
|
||||
/// Address splitter into pages
|
||||
struct AddrSplitter {
|
||||
/// Current address
|
||||
addr: u64,
|
||||
|
||||
/// Size left
|
||||
size: usize,
|
||||
|
||||
/// Page table
|
||||
pagetable: *const PageTable,
|
||||
}
|
||||
|
||||
impl PageSplitter {
|
||||
impl AddrSplitter {
|
||||
/// Create a new page splitter
|
||||
pub const fn new(addr: u64, size: usize, pagetable: *const PageTable) -> Self {
|
||||
Self {
|
||||
addr,
|
||||
|
@ -197,23 +224,28 @@ impl PageSplitter {
|
|||
}
|
||||
}
|
||||
|
||||
fn jump_page(&mut self, page_size: PageSize) {
|
||||
/// Bump address by size X
|
||||
fn bump(&mut self, page_size: PageSize) {
|
||||
self.addr += page_size as u64;
|
||||
self.size = self.size.saturating_sub(page_size as _);
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for PageSplitter {
|
||||
type Item = Result<PageSplitResult, PageSplitError>;
|
||||
impl Iterator for AddrSplitter {
|
||||
type Item = Result<AddrSplitOk, AddrSplitError>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
// The end, everything is fine
|
||||
if self.size == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let (base, perm, size, offset) = 'a: {
|
||||
let mut current_pt = self.pagetable;
|
||||
|
||||
// Walk the page table
|
||||
for lvl in (0..5).rev() {
|
||||
// Get an entry
|
||||
unsafe {
|
||||
let entry = (*current_pt).get_unchecked(
|
||||
usize::try_from((self.addr >> (lvl * 9 + 12)) & ((1 << 9) - 1))
|
||||
|
@ -222,46 +254,62 @@ impl Iterator for PageSplitter {
|
|||
|
||||
let ptr = entry.ptr();
|
||||
match entry.permission() {
|
||||
// No page → page fault
|
||||
Permission::Empty => {
|
||||
return Some(Err(PageSplitError {
|
||||
return Some(Err(AddrSplitError {
|
||||
addr: self.addr,
|
||||
size: PageSize::from_lvl(lvl)?,
|
||||
}))
|
||||
}
|
||||
|
||||
// Node → proceed waking
|
||||
Permission::Node => current_pt = ptr as _,
|
||||
|
||||
// Leaft → return relevant data
|
||||
perm => {
|
||||
break 'a (
|
||||
// Pointer in host memory
|
||||
ptr as *mut u8,
|
||||
perm,
|
||||
PageSize::from_lvl(lvl)?,
|
||||
// In-page offset
|
||||
self.addr as usize & ((1 << (lvl * 9 + 12)) - 1),
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return None;
|
||||
return None; // Reached the end (should not happen)
|
||||
};
|
||||
|
||||
// Get available byte count in the selected page with offset
|
||||
let avail = (size as usize - offset).clamp(0, self.size);
|
||||
self.jump_page(size);
|
||||
Some(Ok(PageSplitResult {
|
||||
ptr: unsafe { base.add(offset) },
|
||||
self.bump(size);
|
||||
|
||||
Some(Ok(AddrSplitOk {
|
||||
ptr: unsafe { base.add(offset) }, // Return pointer to the start of region
|
||||
size: avail,
|
||||
perm,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// Page size
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum PageSize {
|
||||
/// 4 KiB page (on level 0)
|
||||
Size4K = 4096,
|
||||
|
||||
/// 2 MiB page (on level 1)
|
||||
Size2M = 1024 * 1024 * 2,
|
||||
|
||||
/// 1 GiB page (on level 2)
|
||||
Size1G = 1024 * 1024 * 1024,
|
||||
}
|
||||
|
||||
impl PageSize {
|
||||
pub fn from_lvl(lvl: u8) -> Option<Self> {
|
||||
/// Convert page table level to size of page
|
||||
fn from_lvl(lvl: u8) -> Option<Self> {
|
||||
match lvl {
|
||||
0 => Some(PageSize::Size4K),
|
||||
1 => Some(PageSize::Size2M),
|
||||
|
@ -270,3 +318,11 @@ impl PageSize {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Unhandled load access trap
|
||||
#[derive(Clone, Copy, Display, Debug, PartialEq, Eq)]
|
||||
pub struct LoadError;
|
||||
|
||||
/// Unhandled store access trap
|
||||
#[derive(Clone, Copy, Display, Debug, PartialEq, Eq)]
|
||||
pub struct StoreError;
|
||||
|
|
|
@ -6,30 +6,40 @@ use core::{
|
|||
};
|
||||
use delegate::delegate;
|
||||
|
||||
/// Page entry permission
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub enum Permission {
|
||||
/// No page present
|
||||
#[default]
|
||||
Empty,
|
||||
/// Points to another pagetable
|
||||
Node,
|
||||
/// Page is read only
|
||||
Readonly,
|
||||
/// Page is readable and writable
|
||||
Write,
|
||||
/// Page is readable and executable
|
||||
Exec,
|
||||
}
|
||||
|
||||
/// Page table entry
|
||||
#[derive(Clone, Copy, Default, PartialEq, Eq)]
|
||||
pub struct PtEntry(u64);
|
||||
impl PtEntry {
|
||||
/// Create new
|
||||
#[inline]
|
||||
pub unsafe fn new(ptr: *mut PtPointedData, permission: Permission) -> Self {
|
||||
Self(ptr as u64 | permission as u64)
|
||||
}
|
||||
|
||||
/// Get permission
|
||||
#[inline]
|
||||
pub fn permission(&self) -> Permission {
|
||||
unsafe { core::mem::transmute(self.0 as u8 & 0b111) }
|
||||
}
|
||||
|
||||
/// Get pointer to the data (leaf) or next page table (node)
|
||||
#[inline]
|
||||
pub fn ptr(&self) -> *mut PtPointedData {
|
||||
(self.0 & !((1 << 12) - 1)) as _
|
||||
|
@ -45,6 +55,8 @@ impl Debug for PtEntry {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// Page table
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[repr(align(4096))]
|
||||
pub struct PageTable([PtEntry; 512]);
|
||||
|
@ -89,13 +101,17 @@ where
|
|||
|
||||
impl Default for PageTable {
|
||||
fn default() -> Self {
|
||||
// SAFETY: It's fine, zeroed page table entry is valid (= empty)
|
||||
Self(unsafe { MaybeUninit::zeroed().assume_init() })
|
||||
}
|
||||
}
|
||||
|
||||
/// Data page table entry can possibly point to
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C, align(4096))]
|
||||
pub union PtPointedData {
|
||||
/// Node - next page table
|
||||
pub pt: PageTable,
|
||||
/// Leaf
|
||||
pub page: u8,
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ use {
|
|||
value::Value,
|
||||
};
|
||||
|
||||
|
||||
/// Extract a parameter from program
|
||||
macro_rules! param {
|
||||
($self:expr, $ty:ty) => {{
|
||||
assert_impl_one!($ty: OpParam);
|
||||
|
@ -40,6 +42,7 @@ macro_rules! param {
|
|||
}};
|
||||
}
|
||||
|
||||
/// Perform binary operation `#0 ← #1 OP #2`
|
||||
macro_rules! binary_op {
|
||||
($self:expr, $ty:ident, $handler:expr) => {{
|
||||
let ParamBBB(tg, a0, a1) = param!($self, ParamBBB);
|
||||
|
@ -54,6 +57,7 @@ macro_rules! binary_op {
|
|||
}};
|
||||
}
|
||||
|
||||
/// Perform binary operation with immediate `#0 ← #1 OP imm #2`
|
||||
macro_rules! binary_op_imm {
|
||||
($self:expr, $ty:ident, $handler:expr) => {{
|
||||
let ParamBBD(tg, a0, imm) = param!($self, ParamBBD);
|
||||
|
@ -64,6 +68,7 @@ macro_rules! binary_op_imm {
|
|||
}};
|
||||
}
|
||||
|
||||
/// Jump at `#3` if ordering on `#0 <=> #1` is equal to expected
|
||||
macro_rules! cond_jump {
|
||||
($self:expr, $ty:ident, $expected:ident) => {{
|
||||
let ParamBBD(a0, a1, jt) = param!($self, ParamBBD);
|
||||
|
@ -75,15 +80,30 @@ macro_rules! cond_jump {
|
|||
}};
|
||||
}
|
||||
|
||||
/// HoleyBytes Virtual Machine
|
||||
pub struct Vm<'a, T> {
|
||||
/// Holds 256 registers
|
||||
///
|
||||
/// Writing to register 0 is considered undefined behaviour
|
||||
/// in terms of HoleyBytes program execution
|
||||
pub registers: [Value; 256],
|
||||
|
||||
/// Memory implementation
|
||||
pub memory: Memory,
|
||||
|
||||
/// Trap handler
|
||||
pub traph: T,
|
||||
|
||||
// Program counter
|
||||
pc: usize,
|
||||
|
||||
/// Program
|
||||
program: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a, T: HandleTrap> Vm<'a, T> {
|
||||
/// Create a new VM with program and trap handler
|
||||
///
|
||||
/// # Safety
|
||||
/// Program code has to be validated
|
||||
pub unsafe fn new_unchecked(program: &'a [u8], traph: T) -> Self {
|
||||
|
@ -96,17 +116,24 @@ impl<'a, T: HandleTrap> Vm<'a, T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
validate::validate(program)?;
|
||||
Ok(unsafe { Self::new_unchecked(program, traph) })
|
||||
}
|
||||
|
||||
pub fn run(&mut self) -> HaltReason {
|
||||
/// Execute program
|
||||
///
|
||||
/// Program returns [`HaltReason`] which is either [`HaltReason::ProgramEnd`] in case
|
||||
/// of sucessful VM run or other in case of unhandled trap.
|
||||
pub fn run(&mut self) -> Result<(), VmRunError> {
|
||||
use hbbytecode::opcode::*;
|
||||
loop {
|
||||
// Fetch instruction
|
||||
let Some(&opcode) = self.program.get(self.pc)
|
||||
else { return HaltReason::ProgramEnd };
|
||||
else { return Ok(()) };
|
||||
|
||||
// Big match
|
||||
unsafe {
|
||||
match opcode {
|
||||
NOP => param!(self, ()),
|
||||
|
@ -211,7 +238,7 @@ impl<'a, T: HandleTrap> Vm<'a, T> {
|
|||
)
|
||||
.is_err()
|
||||
{
|
||||
return HaltReason::LoadAccessEx;
|
||||
return Err(VmRunError::LoadAccessEx);
|
||||
}
|
||||
}
|
||||
ST => {
|
||||
|
@ -226,7 +253,7 @@ impl<'a, T: HandleTrap> Vm<'a, T> {
|
|||
)
|
||||
.is_err()
|
||||
{
|
||||
return HaltReason::LoadAccessEx;
|
||||
return Err(VmRunError::LoadAccessEx);
|
||||
}
|
||||
}
|
||||
BMC => {
|
||||
|
@ -240,7 +267,7 @@ impl<'a, T: HandleTrap> Vm<'a, T> {
|
|||
)
|
||||
.is_err()
|
||||
{
|
||||
return HaltReason::LoadAccessEx;
|
||||
return Err(VmRunError::LoadAccessEx);
|
||||
}
|
||||
}
|
||||
BRC => {
|
||||
|
@ -289,7 +316,7 @@ impl<'a, T: HandleTrap> Vm<'a, T> {
|
|||
&mut self.memory,
|
||||
op,
|
||||
) {
|
||||
return HaltReason::InvalidOpcode;
|
||||
return Err(VmRunError::InvalidOpcodeEx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -297,15 +324,14 @@ impl<'a, T: HandleTrap> Vm<'a, T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Read register
|
||||
#[inline]
|
||||
unsafe fn read_reg(&self, n: u8) -> Value {
|
||||
if n == 0 {
|
||||
0_u64.into()
|
||||
} else {
|
||||
*self.registers.get_unchecked(n as usize)
|
||||
}
|
||||
}
|
||||
|
||||
/// Write a register.
|
||||
/// Writing to register 0 is no-op.
|
||||
#[inline]
|
||||
unsafe fn write_reg(&mut self, n: u8, value: Value) {
|
||||
if n != 0 {
|
||||
|
@ -314,12 +340,16 @@ impl<'a, T: HandleTrap> Vm<'a, T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Virtual machine halt error
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub enum HaltReason {
|
||||
ProgramEnd,
|
||||
Ecall,
|
||||
InvalidOpcode,
|
||||
pub enum VmRunError {
|
||||
/// Unhandled invalid opcode exceptions
|
||||
InvalidOpcodeEx,
|
||||
|
||||
/// Unhandled load access exception
|
||||
LoadAccessEx,
|
||||
|
||||
/// Unhandled store access exception
|
||||
StoreAccessEx,
|
||||
}
|
||||
|
|
|
@ -4,7 +4,10 @@ use super::{
|
|||
};
|
||||
|
||||
pub trait HandleTrap {
|
||||
/// Handle page fault
|
||||
fn page_fault(&mut self, memory: &mut Memory, addr: u64, size: PageSize, dst: *mut u8) -> bool;
|
||||
|
||||
/// Handle invalid opcode exception
|
||||
fn invalid_op(
|
||||
&mut self,
|
||||
regs: &mut [Value; 256],
|
||||
|
@ -14,6 +17,8 @@ pub trait HandleTrap {
|
|||
) -> 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