Changed memory interfacing
This commit is contained in:
commit
d74b32a38d
|
@ -17,6 +17,7 @@ macro_rules! constmod {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(rustdoc::invalid_rust_codeblocks)]
|
||||||
/// Invoke macro with bytecode definition
|
/// Invoke macro with bytecode definition
|
||||||
/// # Input syntax
|
/// # Input syntax
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
|
@ -31,7 +32,7 @@ macro_rules! constmod {
|
||||||
/// - Per-instructions there will be generated opcode-specific functions calling the generic ones
|
/// - Per-instructions there will be generated opcode-specific functions calling the generic ones
|
||||||
/// - Operand types
|
/// - Operand types
|
||||||
/// - R: Register (u8)
|
/// - R: Register (u8)
|
||||||
/// - I: Immediate (implements [`crate::Imm`] trait)
|
/// - I: Immediate
|
||||||
/// - L: Memory load / store size (u16)
|
/// - L: Memory load / store size (u16)
|
||||||
/// - Other types are identity-mapped
|
/// - Other types are identity-mapped
|
||||||
///
|
///
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
|
//! Block memory copier state machine
|
||||||
|
|
||||||
use {
|
use {
|
||||||
super::MemoryAccessReason,
|
super::{Memory, MemoryAccessReason, VmRunError},
|
||||||
crate::{
|
|
||||||
mem::{perm_check, HandlePageFault, Memory},
|
|
||||||
VmRunError,
|
|
||||||
},
|
|
||||||
core::{mem::MaybeUninit, task::Poll},
|
core::{mem::MaybeUninit, task::Poll},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Buffer size (defaults to 4 KiB, a smallest page size on most platforms)
|
/// Buffer size (defaults to 4 KiB, a smallest page size on most platforms)
|
||||||
const BUF_SIZE: usize = 4096;
|
const BUF_SIZE: usize = 4096;
|
||||||
|
|
||||||
// This should be equal to `BUF_SIZE`
|
/// Buffer of possibly uninitialised bytes, aligned to [`BUF_SIZE`]
|
||||||
#[repr(align(4096))]
|
#[repr(align(4096))]
|
||||||
struct AlignedBuf([MaybeUninit<u8>; BUF_SIZE]);
|
struct AlignedBuf([MaybeUninit<u8>; BUF_SIZE]);
|
||||||
|
|
||||||
|
/// State for block memory copy
|
||||||
pub struct BlockCopier {
|
pub struct BlockCopier {
|
||||||
/// Source address
|
/// Source address
|
||||||
src: u64,
|
src: u64,
|
||||||
|
@ -26,6 +25,7 @@ pub struct BlockCopier {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlockCopier {
|
impl BlockCopier {
|
||||||
|
/// Construct a new one
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(src: u64, dst: u64, count: usize) -> Self {
|
pub fn new(src: u64, dst: u64, count: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -40,11 +40,7 @@ impl BlockCopier {
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// - Same as for [`Memory::load`] and [`Memory::store`]
|
/// - Same as for [`Memory::load`] and [`Memory::store`]
|
||||||
pub unsafe fn poll(
|
pub unsafe fn poll(&mut self, memory: &mut impl Memory) -> Poll<Result<(), BlkCopyError>> {
|
||||||
&mut self,
|
|
||||||
memory: &mut Memory,
|
|
||||||
traph: &mut impl HandlePageFault,
|
|
||||||
) -> Poll<Result<(), BlkCopyError>> {
|
|
||||||
// Safety: Assuming uninit of array of MaybeUninit is sound
|
// Safety: Assuming uninit of array of MaybeUninit is sound
|
||||||
let mut buf = AlignedBuf(MaybeUninit::uninit().assume_init());
|
let mut buf = AlignedBuf(MaybeUninit::uninit().assume_init());
|
||||||
|
|
||||||
|
@ -56,7 +52,6 @@ impl BlockCopier {
|
||||||
self.dst,
|
self.dst,
|
||||||
buf.0.as_mut_ptr().cast(),
|
buf.0.as_mut_ptr().cast(),
|
||||||
BUF_SIZE,
|
BUF_SIZE,
|
||||||
traph,
|
|
||||||
) {
|
) {
|
||||||
return Poll::Ready(Err(e));
|
return Poll::Ready(Err(e));
|
||||||
}
|
}
|
||||||
|
@ -68,7 +63,7 @@ impl BlockCopier {
|
||||||
Some(n) => self.src = n,
|
Some(n) => self.src = n,
|
||||||
None => return Poll::Ready(Err(BlkCopyError::OutOfBounds)),
|
None => return Poll::Ready(Err(BlkCopyError::OutOfBounds)),
|
||||||
};
|
};
|
||||||
|
|
||||||
match self.dst.checked_add(BUF_SIZE as u64) {
|
match self.dst.checked_add(BUF_SIZE as u64) {
|
||||||
Some(n) => self.dst = n,
|
Some(n) => self.dst = n,
|
||||||
None => return Poll::Ready(Err(BlkCopyError::OutOfBounds)),
|
None => return Poll::Ready(Err(BlkCopyError::OutOfBounds)),
|
||||||
|
@ -92,7 +87,6 @@ impl BlockCopier {
|
||||||
self.dst,
|
self.dst,
|
||||||
buf.0.as_mut_ptr().cast(),
|
buf.0.as_mut_ptr().cast(),
|
||||||
self.rem,
|
self.rem,
|
||||||
traph,
|
|
||||||
) {
|
) {
|
||||||
return Poll::Ready(Err(e));
|
return Poll::Ready(Err(e));
|
||||||
}
|
}
|
||||||
|
@ -102,43 +96,27 @@ impl BlockCopier {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Load to buffer and store from buffer
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn act(
|
unsafe fn act(
|
||||||
memory: &mut Memory,
|
memory: &mut impl Memory,
|
||||||
src: u64,
|
src: u64,
|
||||||
dst: u64,
|
dst: u64,
|
||||||
buf: *mut u8,
|
buf: *mut u8,
|
||||||
count: usize,
|
count: usize,
|
||||||
traph: &mut impl HandlePageFault,
|
|
||||||
) -> Result<(), BlkCopyError> {
|
) -> Result<(), BlkCopyError> {
|
||||||
// Load to buffer
|
// Load to buffer
|
||||||
memory
|
memory
|
||||||
.memory_access(
|
.load(src, buf, count)
|
||||||
MemoryAccessReason::Load,
|
.map_err(|super::LoadError(addr)| BlkCopyError::Access {
|
||||||
src,
|
|
||||||
buf,
|
|
||||||
count,
|
|
||||||
perm_check::readable,
|
|
||||||
|src, dst, count| core::ptr::copy(src, dst, count),
|
|
||||||
traph,
|
|
||||||
)
|
|
||||||
.map_err(|addr| BlkCopyError::Access {
|
|
||||||
access_reason: MemoryAccessReason::Load,
|
access_reason: MemoryAccessReason::Load,
|
||||||
addr,
|
addr,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Store from buffer
|
// Store from buffer
|
||||||
memory
|
memory
|
||||||
.memory_access(
|
.store(dst, buf, count)
|
||||||
MemoryAccessReason::Store,
|
.map_err(|super::StoreError(addr)| BlkCopyError::Access {
|
||||||
dst,
|
|
||||||
buf,
|
|
||||||
count,
|
|
||||||
perm_check::writable,
|
|
||||||
|dst, src, count| core::ptr::copy(src, dst, count),
|
|
||||||
traph,
|
|
||||||
)
|
|
||||||
.map_err(|addr| BlkCopyError::Access {
|
|
||||||
access_reason: MemoryAccessReason::Store,
|
access_reason: MemoryAccessReason::Store,
|
||||||
addr,
|
addr,
|
||||||
})?;
|
})?;
|
107
hbvm/src/lib.rs
107
hbvm/src/lib.rs
|
@ -2,8 +2,8 @@
|
||||||
//!
|
//!
|
||||||
//! # Alloc feature
|
//! # Alloc feature
|
||||||
//! - Enabled by default
|
//! - Enabled by default
|
||||||
//! - Provides [`mem::Memory`] mapping / unmapping, as well as
|
//! - Provides mapping / unmapping, as well as [`Default`] and [`Drop`]
|
||||||
//! [`Default`] and [`Drop`] implementation
|
//! implementations for soft-paged memory implementation
|
||||||
|
|
||||||
// # General safety notice:
|
// # General safety notice:
|
||||||
// - Validation has to assure there is 256 registers (r0 - r255)
|
// - Validation has to assure there is 256 registers (r0 - r255)
|
||||||
|
@ -11,26 +11,31 @@
|
||||||
// - Mapped pages should be at least 4 KiB
|
// - Mapped pages should be at least 4 KiB
|
||||||
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
#![cfg_attr(feature = "nightly", feature(fn_align))]
|
#![cfg_attr(feature = "nightly", feature(fn_align))]
|
||||||
|
#![warn(missing_docs, clippy::missing_docs_in_private_items)]
|
||||||
|
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
pub mod mem;
|
pub mod softpaging;
|
||||||
pub mod value;
|
pub mod value;
|
||||||
|
|
||||||
|
mod bmc;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
|
bmc::BlockCopier,
|
||||||
core::{cmp::Ordering, mem::size_of, ops},
|
core::{cmp::Ordering, mem::size_of, ops},
|
||||||
|
derive_more::Display,
|
||||||
hbbytecode::{
|
hbbytecode::{
|
||||||
valider, OpParam, ParamBB, ParamBBB, ParamBBBB, ParamBBD, ParamBBDH, ParamBBW, ParamBD,
|
valider, OpParam, ParamBB, ParamBBB, ParamBBBB, ParamBBD, ParamBBDH, ParamBBW, ParamBD,
|
||||||
},
|
},
|
||||||
mem::{bmc::BlockCopier, HandlePageFault, Memory},
|
|
||||||
value::{Value, ValueVariant},
|
value::{Value, ValueVariant},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// HoleyBytes Virtual Machine
|
/// HoleyBytes Virtual Machine
|
||||||
pub struct Vm<'a, PfHandler, const TIMER_QUOTIENT: usize> {
|
pub struct Vm<'a, Mem, 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
|
||||||
|
@ -38,20 +43,20 @@ pub struct Vm<'a, PfHandler, const TIMER_QUOTIENT: usize> {
|
||||||
pub registers: [Value; 256],
|
pub registers: [Value; 256],
|
||||||
|
|
||||||
/// Memory implementation
|
/// Memory implementation
|
||||||
pub memory: Memory,
|
pub memory: Mem,
|
||||||
|
|
||||||
/// Trap handler
|
|
||||||
pub pfhandler: PfHandler,
|
|
||||||
|
|
||||||
/// Program counter
|
/// Program counter
|
||||||
pub pc: usize,
|
pub pc: usize,
|
||||||
|
|
||||||
/// Program
|
/// Program
|
||||||
program: &'a [u8],
|
program: *const u8,
|
||||||
|
|
||||||
/// Cached program length (without unreachable end)
|
/// Cached program length (without unreachable end)
|
||||||
program_len: usize,
|
program_len: usize,
|
||||||
|
|
||||||
|
/// Program lifetime
|
||||||
|
_program_lt: PhantomData<&'a [u8]>,
|
||||||
|
|
||||||
/// Program timer
|
/// Program timer
|
||||||
timer: usize,
|
timer: usize,
|
||||||
|
|
||||||
|
@ -59,34 +64,31 @@ pub struct Vm<'a, PfHandler, const TIMER_QUOTIENT: usize> {
|
||||||
copier: Option<BlockCopier>,
|
copier: Option<BlockCopier>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize>
|
impl<'a, Mem, const TIMER_QUOTIENT: usize> Vm<'a, Mem, TIMER_QUOTIENT>
|
||||||
Vm<'a, PfHandler, TIMER_QUOTIENT>
|
where
|
||||||
|
Mem: Memory,
|
||||||
{
|
{
|
||||||
/// 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: PfHandler, memory: Memory) -> Self {
|
pub unsafe fn new_unchecked(program: &'a [u8], memory: Mem) -> Self {
|
||||||
Self {
|
Self {
|
||||||
registers: [Value::from(0_u64); 256],
|
registers: [Value::from(0_u64); 256],
|
||||||
memory,
|
memory,
|
||||||
pfhandler: traph,
|
|
||||||
pc: 0,
|
pc: 0,
|
||||||
program_len: program.len() - 12,
|
program_len: program.len() - 12,
|
||||||
program: &program[4..],
|
program: program[4..].as_ptr(),
|
||||||
|
_program_lt: Default::default(),
|
||||||
timer: 0,
|
timer: 0,
|
||||||
copier: None,
|
copier: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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(
|
pub fn new_validated(program: &'a [u8], memory: Mem) -> Result<Self, valider::Error> {
|
||||||
program: &'a [u8],
|
|
||||||
traph: PfHandler,
|
|
||||||
memory: Memory,
|
|
||||||
) -> Result<Self, valider::Error> {
|
|
||||||
valider::validate(program)?;
|
valider::validate(program)?;
|
||||||
Ok(unsafe { Self::new_unchecked(program, traph, memory) })
|
Ok(unsafe { Self::new_unchecked(program, memory) })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute program
|
/// Execute program
|
||||||
|
@ -121,7 +123,7 @@ impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize>
|
||||||
// - Yes, we assume you run 64 bit CPU. Else ?conradluget a better CPU
|
// - Yes, we assume you run 64 bit CPU. Else ?conradluget a better CPU
|
||||||
// sorry 8 bit fans, HBVM won't run on your Speccy :(
|
// sorry 8 bit fans, HBVM won't run on your Speccy :(
|
||||||
unsafe {
|
unsafe {
|
||||||
match *self.program.get_unchecked(self.pc) {
|
match *self.program.add(self.pc) {
|
||||||
UN => {
|
UN => {
|
||||||
self.decode::<()>();
|
self.decode::<()>();
|
||||||
return Err(VmRunError::Unreachable);
|
return Err(VmRunError::Unreachable);
|
||||||
|
@ -245,7 +247,6 @@ impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize>
|
||||||
.add(usize::from(dst) + usize::from(n))
|
.add(usize::from(dst) + usize::from(n))
|
||||||
.cast(),
|
.cast(),
|
||||||
usize::from(count).saturating_sub(n.into()),
|
usize::from(count).saturating_sub(n.into()),
|
||||||
&mut self.pfhandler,
|
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
ST => {
|
ST => {
|
||||||
|
@ -255,14 +256,13 @@ impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize>
|
||||||
self.ldst_addr_uber(dst, base, off, count, 0)?,
|
self.ldst_addr_uber(dst, base, off, count, 0)?,
|
||||||
self.registers.as_ptr().add(usize::from(dst)).cast(),
|
self.registers.as_ptr().add(usize::from(dst)).cast(),
|
||||||
count.into(),
|
count.into(),
|
||||||
&mut self.pfhandler,
|
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
BMC => {
|
BMC => {
|
||||||
// Block memory copy
|
// Block memory copy
|
||||||
match if let Some(copier) = &mut self.copier {
|
match if let Some(copier) = &mut self.copier {
|
||||||
// There is some copier, poll.
|
// There is some copier, poll.
|
||||||
copier.poll(&mut self.memory, &mut self.pfhandler)
|
copier.poll(&mut self.memory)
|
||||||
} else {
|
} else {
|
||||||
// There is none, make one!
|
// There is none, make one!
|
||||||
let ParamBBD(src, dst, count) = self.decode();
|
let ParamBBD(src, dst, count) = self.decode();
|
||||||
|
@ -279,7 +279,7 @@ impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize>
|
||||||
self.copier
|
self.copier
|
||||||
.as_mut()
|
.as_mut()
|
||||||
.unwrap_unchecked() // SAFETY: We just assigned there
|
.unwrap_unchecked() // SAFETY: We just assigned there
|
||||||
.poll(&mut self.memory, &mut self.pfhandler)
|
.poll(&mut self.memory)
|
||||||
} {
|
} {
|
||||||
// We are done, shift program counter
|
// We are done, shift program counter
|
||||||
core::task::Poll::Ready(Ok(())) => {
|
core::task::Poll::Ready(Ok(())) => {
|
||||||
|
@ -386,7 +386,7 @@ impl<'a, PfHandler: HandlePageFault, const TIMER_QUOTIENT: usize>
|
||||||
/// Decode instruction operands
|
/// Decode instruction operands
|
||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn decode<T: OpParam>(&mut self) -> T {
|
unsafe fn decode<T: OpParam>(&mut self) -> T {
|
||||||
let data = self.program.as_ptr().add(self.pc + 1).cast::<T>().read();
|
let data = self.program.add(self.pc + 1).cast::<T>().read();
|
||||||
self.pc += 1 + size_of::<T>();
|
self.pc += 1 + size_of::<T>();
|
||||||
data
|
data
|
||||||
}
|
}
|
||||||
|
@ -506,3 +506,54 @@ pub enum VmRunOk {
|
||||||
/// Environment call
|
/// Environment call
|
||||||
Ecall,
|
Ecall,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Load-store memory access
|
||||||
|
pub trait Memory {
|
||||||
|
/// Load data from memory on address
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// - Shall not overrun the buffer
|
||||||
|
unsafe fn load(&mut self, addr: u64, target: *mut u8, count: usize) -> Result<(), LoadError>;
|
||||||
|
|
||||||
|
/// Store data to memory on address
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// - Shall not overrun the buffer
|
||||||
|
unsafe fn store(
|
||||||
|
&mut self,
|
||||||
|
addr: u64,
|
||||||
|
source: *const u8,
|
||||||
|
count: usize,
|
||||||
|
) -> Result<(), StoreError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unhandled load access trap
|
||||||
|
#[derive(Clone, Copy, Display, Debug, PartialEq, Eq)]
|
||||||
|
#[display(fmt = "Load access error at address {_0:#x}")]
|
||||||
|
pub struct LoadError(pub u64);
|
||||||
|
|
||||||
|
/// Unhandled store access trap
|
||||||
|
#[derive(Clone, Copy, Display, Debug, PartialEq, Eq)]
|
||||||
|
#[display(fmt = "Store access error at address {_0:#x}")]
|
||||||
|
pub struct StoreError(pub u64);
|
||||||
|
|
||||||
|
/// Reason to access memory
|
||||||
|
#[derive(Clone, Copy, Display, Debug, PartialEq, Eq)]
|
||||||
|
pub enum MemoryAccessReason {
|
||||||
|
/// Memory was accessed for load (read)
|
||||||
|
Load,
|
||||||
|
/// Memory was accessed for store (write)
|
||||||
|
Store,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LoadError> for VmRunError {
|
||||||
|
fn from(value: LoadError) -> Self {
|
||||||
|
Self::LoadAccessEx(value.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<StoreError> for VmRunError {
|
||||||
|
fn from(value: StoreError) -> Self {
|
||||||
|
Self::StoreAccessEx(value.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use {
|
use {
|
||||||
hbbytecode::valider::validate,
|
hbbytecode::valider::validate,
|
||||||
hbvm::{
|
hbvm::{
|
||||||
mem::{HandlePageFault, Memory, MemoryAccessReason, PageSize},
|
softpaging::{paging::PageTable, HandlePageFault, PageSize, SoftPagedMem},
|
||||||
Vm,
|
MemoryAccessReason, Vm,
|
||||||
},
|
},
|
||||||
std::io::{stdin, Read},
|
std::io::{stdin, Read},
|
||||||
};
|
};
|
||||||
|
@ -16,7 +16,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
} else {
|
} else {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut vm = Vm::<_, 0>::new_unchecked(&prog, TestTrapHandler, Default::default());
|
let mut vm =
|
||||||
|
Vm::<_, 0>::new_unchecked(&prog, SoftPagedMem::<TestTrapHandler>::default());
|
||||||
let data = {
|
let data = {
|
||||||
let ptr = std::alloc::alloc_zeroed(std::alloc::Layout::from_size_align_unchecked(
|
let ptr = std::alloc::alloc_zeroed(std::alloc::Layout::from_size_align_unchecked(
|
||||||
4096, 4096,
|
4096, 4096,
|
||||||
|
@ -31,7 +32,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
.map(
|
.map(
|
||||||
data,
|
data,
|
||||||
0,
|
0,
|
||||||
hbvm::mem::paging::Permission::Write,
|
hbvm::softpaging::paging::Permission::Write,
|
||||||
PageSize::Size4K,
|
PageSize::Size4K,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -54,12 +55,13 @@ pub fn time() -> u32 {
|
||||||
9
|
9
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
struct TestTrapHandler;
|
struct TestTrapHandler;
|
||||||
impl HandlePageFault for TestTrapHandler {
|
impl HandlePageFault for TestTrapHandler {
|
||||||
fn page_fault(
|
fn page_fault(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: MemoryAccessReason,
|
_: MemoryAccessReason,
|
||||||
_: &mut Memory,
|
_: &mut PageTable,
|
||||||
_: u64,
|
_: u64,
|
||||||
_: PageSize,
|
_: PageSize,
|
||||||
_: *mut u8,
|
_: *mut u8,
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
//! Program trap handling interfaces
|
|
||||||
|
|
||||||
use super::{Memory, MemoryAccessReason, PageSize};
|
|
||||||
|
|
||||||
/// Handle VM traps
|
|
||||||
pub trait HandlePageFault {
|
|
||||||
/// Handle page fault
|
|
||||||
///
|
|
||||||
/// Return true if handling was sucessful,
|
|
||||||
/// otherwise the program will be interrupted and will
|
|
||||||
/// yield an error.
|
|
||||||
fn page_fault(
|
|
||||||
&mut self,
|
|
||||||
reason: MemoryAccessReason,
|
|
||||||
memory: &mut Memory,
|
|
||||||
vaddr: u64,
|
|
||||||
size: PageSize,
|
|
||||||
dataptr: *mut u8,
|
|
||||||
) -> bool;
|
|
||||||
}
|
|
|
@ -1,14 +1,9 @@
|
||||||
//! Program memory implementation
|
//! Platform independent, software paged memory implementation
|
||||||
|
|
||||||
pub mod bmc;
|
|
||||||
pub mod paging;
|
pub mod paging;
|
||||||
|
|
||||||
mod pfhandler;
|
|
||||||
|
|
||||||
pub use pfhandler::HandlePageFault;
|
|
||||||
|
|
||||||
use {
|
use {
|
||||||
super::VmRunError,
|
super::{LoadError, Memory, MemoryAccessReason, StoreError},
|
||||||
derive_more::Display,
|
derive_more::Display,
|
||||||
paging::{PageTable, Permission},
|
paging::{PageTable, Permission},
|
||||||
};
|
};
|
||||||
|
@ -16,171 +11,21 @@ use {
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use {alloc::boxed::Box, paging::PtEntry};
|
use {alloc::boxed::Box, paging::PtEntry};
|
||||||
|
|
||||||
/// HoleyBytes virtual memory
|
/// HoleyBytes software paged memory
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Memory {
|
pub struct SoftPagedMem<PfHandler> {
|
||||||
/// Root page table
|
/// Root page table
|
||||||
pub root_pt: *mut PageTable,
|
pub root_pt: *mut PageTable,
|
||||||
|
/// Page fault handler
|
||||||
|
pub pf_handler: PfHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
impl<PfHandler: HandlePageFault> Memory for SoftPagedMem<PfHandler> {
|
||||||
impl Default for Memory {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
root_pt: Box::into_raw(Default::default()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
impl Drop for Memory {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let _ = unsafe { Box::from_raw(self.root_pt) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Memory {
|
|
||||||
/// Maps host's memory into VM's memory
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
/// - Your faith in the gods of UB
|
|
||||||
/// - Addr-san claims it's fine but who knows is she isn't lying :ferrisSus:
|
|
||||||
/// - Alright, Miri-sama is also fine with this, who knows why
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
pub unsafe fn map(
|
|
||||||
&mut self,
|
|
||||||
host: *mut u8,
|
|
||||||
target: u64,
|
|
||||||
perm: Permission,
|
|
||||||
pagesize: PageSize,
|
|
||||||
) -> Result<(), MapError> {
|
|
||||||
let mut current_pt = self.root_pt;
|
|
||||||
|
|
||||||
// Decide on what level depth are we going
|
|
||||||
let lookup_depth = match pagesize {
|
|
||||||
PageSize::Size4K => 0,
|
|
||||||
PageSize::Size2M => 1,
|
|
||||||
PageSize::Size1G => 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Walk pagetable levels
|
|
||||||
for lvl in (lookup_depth + 1..5).rev() {
|
|
||||||
let entry = (*current_pt)
|
|
||||||
.table
|
|
||||||
.get_unchecked_mut(addr_extract_index(target, lvl));
|
|
||||||
|
|
||||||
let ptr = entry.ptr();
|
|
||||||
match entry.permission() {
|
|
||||||
// Still not on target and already seeing empty entry?
|
|
||||||
// No worries! Let's create one (allocates).
|
|
||||||
Permission::Empty => {
|
|
||||||
// Increase children count
|
|
||||||
(*current_pt).childen += 1;
|
|
||||||
|
|
||||||
let table = Box::into_raw(Box::new(paging::PtPointedData {
|
|
||||||
pt: PageTable::default(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
core::ptr::write(entry, PtEntry::new(table, Permission::Node));
|
|
||||||
current_pt = table as _;
|
|
||||||
}
|
|
||||||
// Continue walking
|
|
||||||
Permission::Node => current_pt = ptr as _,
|
|
||||||
|
|
||||||
// There is some entry on place of node
|
|
||||||
_ => return Err(MapError::PageOnNode),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let node = (*current_pt)
|
|
||||||
.table
|
|
||||||
.get_unchecked_mut(addr_extract_index(target, lookup_depth));
|
|
||||||
|
|
||||||
// Check if node is not mapped
|
|
||||||
if node.permission() != Permission::Empty {
|
|
||||||
return Err(MapError::AlreadyMapped);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write entry
|
|
||||||
(*current_pt).childen += 1;
|
|
||||||
core::ptr::write(node, PtEntry::new(host.cast(), perm));
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unmaps pages from VM's memory
|
|
||||||
///
|
|
||||||
/// If errors, it only means there is no entry to unmap and in most cases
|
|
||||||
/// just should be ignored.
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
pub fn unmap(&mut self, addr: u64) -> Result<(), NothingToUnmap> {
|
|
||||||
let mut current_pt = self.root_pt;
|
|
||||||
let mut page_tables = [core::ptr::null_mut(); 5];
|
|
||||||
|
|
||||||
// Walk page table in reverse
|
|
||||||
for lvl in (0..5).rev() {
|
|
||||||
let entry = unsafe {
|
|
||||||
(*current_pt)
|
|
||||||
.table
|
|
||||||
.get_unchecked_mut(addr_extract_index(addr, lvl))
|
|
||||||
};
|
|
||||||
|
|
||||||
let ptr = entry.ptr();
|
|
||||||
match entry.permission() {
|
|
||||||
// Nothing is there, throw an error, not critical!
|
|
||||||
Permission::Empty => return Err(NothingToUnmap),
|
|
||||||
// Node – Save to visited pagetables and continue walking
|
|
||||||
Permission::Node => {
|
|
||||||
page_tables[lvl as usize] = entry;
|
|
||||||
current_pt = ptr as _
|
|
||||||
}
|
|
||||||
// Page entry – zero it out!
|
|
||||||
// Zero page entry is completely valid entry with
|
|
||||||
// empty permission - no UB here!
|
|
||||||
_ => unsafe {
|
|
||||||
core::ptr::write_bytes(entry, 0, 1);
|
|
||||||
break;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now walk in order visited page tables
|
|
||||||
for entry in page_tables.into_iter() {
|
|
||||||
// Level not visited, skip.
|
|
||||||
if entry.is_null() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let children = &mut (*(*entry).ptr()).pt.childen;
|
|
||||||
*children -= 1; // Decrease children count
|
|
||||||
|
|
||||||
// If there are no children, deallocate.
|
|
||||||
if *children == 0 {
|
|
||||||
let _ = Box::from_raw((*entry).ptr() as *mut PageTable);
|
|
||||||
|
|
||||||
// Zero visited entry
|
|
||||||
core::ptr::write_bytes(entry, 0, 1);
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Load value from an address
|
/// Load value from an address
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Applies same conditions as for [`core::ptr::copy_nonoverlapping`]
|
/// Applies same conditions as for [`core::ptr::copy_nonoverlapping`]
|
||||||
pub unsafe fn load(
|
unsafe fn load(&mut self, addr: u64, target: *mut u8, count: usize) -> Result<(), LoadError> {
|
||||||
&mut self,
|
|
||||||
addr: u64,
|
|
||||||
target: *mut u8,
|
|
||||||
count: usize,
|
|
||||||
traph: &mut impl HandlePageFault,
|
|
||||||
) -> Result<(), LoadError> {
|
|
||||||
self.memory_access(
|
self.memory_access(
|
||||||
MemoryAccessReason::Load,
|
MemoryAccessReason::Load,
|
||||||
addr,
|
addr,
|
||||||
|
@ -188,7 +33,6 @@ impl Memory {
|
||||||
count,
|
count,
|
||||||
perm_check::readable,
|
perm_check::readable,
|
||||||
|src, dst, count| core::ptr::copy_nonoverlapping(src, dst, count),
|
|src, dst, count| core::ptr::copy_nonoverlapping(src, dst, count),
|
||||||
traph,
|
|
||||||
)
|
)
|
||||||
.map_err(LoadError)
|
.map_err(LoadError)
|
||||||
}
|
}
|
||||||
|
@ -197,12 +41,11 @@ impl Memory {
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// Applies same conditions as for [`core::ptr::copy_nonoverlapping`]
|
/// Applies same conditions as for [`core::ptr::copy_nonoverlapping`]
|
||||||
pub unsafe fn store(
|
unsafe fn store(
|
||||||
&mut self,
|
&mut self,
|
||||||
addr: u64,
|
addr: u64,
|
||||||
source: *const u8,
|
source: *const u8,
|
||||||
count: usize,
|
count: usize,
|
||||||
traph: &mut impl HandlePageFault,
|
|
||||||
) -> Result<(), StoreError> {
|
) -> Result<(), StoreError> {
|
||||||
self.memory_access(
|
self.memory_access(
|
||||||
MemoryAccessReason::Store,
|
MemoryAccessReason::Store,
|
||||||
|
@ -211,11 +54,12 @@ impl Memory {
|
||||||
count,
|
count,
|
||||||
perm_check::writable,
|
perm_check::writable,
|
||||||
|dst, src, count| core::ptr::copy_nonoverlapping(src, dst, count),
|
|dst, src, count| core::ptr::copy_nonoverlapping(src, dst, count),
|
||||||
traph,
|
|
||||||
)
|
)
|
||||||
.map_err(StoreError)
|
.map_err(StoreError)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PfHandler: HandlePageFault> SoftPagedMem<PfHandler> {
|
||||||
// Everyone behold, the holy function, the god of HBVM memory accesses!
|
// Everyone behold, the holy function, the god of HBVM memory accesses!
|
||||||
|
|
||||||
/// Split address to pages, check their permissions and feed pointers with offset
|
/// Split address to pages, check their permissions and feed pointers with offset
|
||||||
|
@ -231,7 +75,6 @@ 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 HandlePageFault,
|
|
||||||
) -> Result<(), u64> {
|
) -> Result<(), u64> {
|
||||||
// Create new splitter
|
// Create new splitter
|
||||||
let mut pspl = AddrPageLookuper::new(src, len, self.root_pt);
|
let mut pspl = AddrPageLookuper::new(src, len, self.root_pt);
|
||||||
|
@ -255,7 +98,13 @@ impl Memory {
|
||||||
// No page found
|
// No page found
|
||||||
Some(Err(AddrPageLookupError { addr, size })) => {
|
Some(Err(AddrPageLookupError { addr, size })) => {
|
||||||
// Attempt to execute page fault handler
|
// Attempt to execute page fault handler
|
||||||
if traph.page_fault(reason, self, addr, size, dst) {
|
if self.pf_handler.page_fault(
|
||||||
|
reason,
|
||||||
|
unsafe { &mut *self.root_pt },
|
||||||
|
addr,
|
||||||
|
size,
|
||||||
|
dst,
|
||||||
|
) {
|
||||||
// Shift the splitter address
|
// Shift the splitter address
|
||||||
pspl.bump(size);
|
pspl.bump(size);
|
||||||
|
|
||||||
|
@ -389,6 +238,154 @@ impl Iterator for AddrPageLookuper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
impl<PfHandler: Default> Default for SoftPagedMem<PfHandler> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
root_pt: Box::into_raw(Default::default()),
|
||||||
|
pf_handler: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
impl<A> Drop for SoftPagedMem<A> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let _ = unsafe { Box::from_raw(self.root_pt) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
impl<A> SoftPagedMem<A> {
|
||||||
|
/// Maps host's memory into VM's memory
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// - Your faith in the gods of UB
|
||||||
|
/// - Addr-san claims it's fine but who knows is she isn't lying :ferrisSus:
|
||||||
|
/// - Alright, Miri-sama is also fine with this, who knows why
|
||||||
|
pub unsafe fn map(
|
||||||
|
&mut self,
|
||||||
|
host: *mut u8,
|
||||||
|
target: u64,
|
||||||
|
perm: Permission,
|
||||||
|
pagesize: PageSize,
|
||||||
|
) -> Result<(), MapError> {
|
||||||
|
let mut current_pt = self.root_pt;
|
||||||
|
|
||||||
|
// Decide on what level depth are we going
|
||||||
|
let lookup_depth = match pagesize {
|
||||||
|
PageSize::Size4K => 0,
|
||||||
|
PageSize::Size2M => 1,
|
||||||
|
PageSize::Size1G => 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Walk pagetable levels
|
||||||
|
for lvl in (lookup_depth + 1..5).rev() {
|
||||||
|
let entry = (*current_pt)
|
||||||
|
.table
|
||||||
|
.get_unchecked_mut(addr_extract_index(target, lvl));
|
||||||
|
|
||||||
|
let ptr = entry.ptr();
|
||||||
|
match entry.permission() {
|
||||||
|
// Still not on target and already seeing empty entry?
|
||||||
|
// No worries! Let's create one (allocates).
|
||||||
|
Permission::Empty => {
|
||||||
|
// Increase children count
|
||||||
|
(*current_pt).childen += 1;
|
||||||
|
|
||||||
|
let table = Box::into_raw(Box::new(paging::PtPointedData {
|
||||||
|
pt: PageTable::default(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
core::ptr::write(entry, PtEntry::new(table, Permission::Node));
|
||||||
|
current_pt = table as _;
|
||||||
|
}
|
||||||
|
// Continue walking
|
||||||
|
Permission::Node => current_pt = ptr as _,
|
||||||
|
|
||||||
|
// There is some entry on place of node
|
||||||
|
_ => return Err(MapError::PageOnNode),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let node = (*current_pt)
|
||||||
|
.table
|
||||||
|
.get_unchecked_mut(addr_extract_index(target, lookup_depth));
|
||||||
|
|
||||||
|
// Check if node is not mapped
|
||||||
|
if node.permission() != Permission::Empty {
|
||||||
|
return Err(MapError::AlreadyMapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write entry
|
||||||
|
(*current_pt).childen += 1;
|
||||||
|
core::ptr::write(node, PtEntry::new(host.cast(), perm));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unmaps pages from VM's memory
|
||||||
|
///
|
||||||
|
/// If errors, it only means there is no entry to unmap and in most cases
|
||||||
|
/// just should be ignored.
|
||||||
|
pub fn unmap(&mut self, addr: u64) -> Result<(), NothingToUnmap> {
|
||||||
|
let mut current_pt = self.root_pt;
|
||||||
|
let mut page_tables = [core::ptr::null_mut(); 5];
|
||||||
|
|
||||||
|
// Walk page table in reverse
|
||||||
|
for lvl in (0..5).rev() {
|
||||||
|
let entry = unsafe {
|
||||||
|
(*current_pt)
|
||||||
|
.table
|
||||||
|
.get_unchecked_mut(addr_extract_index(addr, lvl))
|
||||||
|
};
|
||||||
|
|
||||||
|
let ptr = entry.ptr();
|
||||||
|
match entry.permission() {
|
||||||
|
// Nothing is there, throw an error, not critical!
|
||||||
|
Permission::Empty => return Err(NothingToUnmap),
|
||||||
|
// Node – Save to visited pagetables and continue walking
|
||||||
|
Permission::Node => {
|
||||||
|
page_tables[lvl as usize] = entry;
|
||||||
|
current_pt = ptr as _
|
||||||
|
}
|
||||||
|
// Page entry – zero it out!
|
||||||
|
// Zero page entry is completely valid entry with
|
||||||
|
// empty permission - no UB here!
|
||||||
|
_ => unsafe {
|
||||||
|
core::ptr::write_bytes(entry, 0, 1);
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now walk in order visited page tables
|
||||||
|
for entry in page_tables.into_iter() {
|
||||||
|
// Level not visited, skip.
|
||||||
|
if entry.is_null() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let children = &mut (*(*entry).ptr()).pt.childen;
|
||||||
|
*children -= 1; // Decrease children count
|
||||||
|
|
||||||
|
// If there are no children, deallocate.
|
||||||
|
if *children == 0 {
|
||||||
|
let _ = Box::from_raw((*entry).ptr() as *mut PageTable);
|
||||||
|
|
||||||
|
// Zero visited entry
|
||||||
|
core::ptr::write_bytes(entry, 0, 1);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Extract index in page table on specified level
|
/// Extract index in page table on specified level
|
||||||
///
|
///
|
||||||
/// The level shall not be larger than 4, otherwise
|
/// The level shall not be larger than 4, otherwise
|
||||||
|
@ -423,44 +420,6 @@ impl PageSize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unhandled load access trap
|
|
||||||
#[derive(Clone, Copy, Display, Debug, PartialEq, Eq)]
|
|
||||||
#[display(fmt = "Load access error at address {_0:#x}")]
|
|
||||||
pub struct LoadError(u64);
|
|
||||||
|
|
||||||
/// Unhandled store access trap
|
|
||||||
#[derive(Clone, Copy, Display, Debug, PartialEq, Eq)]
|
|
||||||
#[display(fmt = "Store access error at address {_0:#x}")]
|
|
||||||
pub struct StoreError(u64);
|
|
||||||
|
|
||||||
/// There was no entry in page table to unmap
|
|
||||||
///
|
|
||||||
/// No worry, don't panic, nothing bad has happened,
|
|
||||||
/// but if you are 120% sure there should be something,
|
|
||||||
/// double-check your addresses.
|
|
||||||
#[derive(Clone, Copy, Display, Debug)]
|
|
||||||
#[display(fmt = "There was no entry to unmap")]
|
|
||||||
pub struct NothingToUnmap;
|
|
||||||
|
|
||||||
/// Reason to access memory
|
|
||||||
#[derive(Clone, Copy, Display, Debug, PartialEq, Eq)]
|
|
||||||
pub enum MemoryAccessReason {
|
|
||||||
Load,
|
|
||||||
Store,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<LoadError> for VmRunError {
|
|
||||||
fn from(value: LoadError) -> Self {
|
|
||||||
Self::LoadAccessEx(value.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<StoreError> for VmRunError {
|
|
||||||
fn from(value: StoreError) -> Self {
|
|
||||||
Self::StoreAccessEx(value.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Error mapping
|
/// Error mapping
|
||||||
#[derive(Clone, Copy, Display, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Display, Debug, PartialEq, Eq)]
|
||||||
pub enum MapError {
|
pub enum MapError {
|
||||||
|
@ -473,13 +432,22 @@ pub enum MapError {
|
||||||
PageOnNode,
|
PageOnNode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// There was no entry in page table to unmap
|
||||||
|
///
|
||||||
|
/// No worry, don't panic, nothing bad has happened,
|
||||||
|
/// but if you are 120% sure there should be something,
|
||||||
|
/// double-check your addresses.
|
||||||
|
#[derive(Clone, Copy, Display, Debug)]
|
||||||
|
#[display(fmt = "There was no entry to unmap")]
|
||||||
|
pub struct NothingToUnmap;
|
||||||
|
|
||||||
/// Permisison checks
|
/// Permisison checks
|
||||||
pub mod perm_check {
|
pub mod perm_check {
|
||||||
use super::paging::Permission;
|
use super::paging::Permission;
|
||||||
|
|
||||||
/// Page is readable
|
/// Page is readable
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn readable(perm: Permission) -> bool {
|
pub const fn readable(perm: Permission) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
perm,
|
perm,
|
||||||
Permission::Readonly | Permission::Write | Permission::Exec
|
Permission::Readonly | Permission::Write | Permission::Exec
|
||||||
|
@ -488,7 +456,26 @@ pub mod perm_check {
|
||||||
|
|
||||||
/// Page is writable
|
/// Page is writable
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn writable(perm: Permission) -> bool {
|
pub const fn writable(perm: Permission) -> bool {
|
||||||
perm == Permission::Write
|
matches!(perm, Permission::Write)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handle VM traps
|
||||||
|
pub trait HandlePageFault {
|
||||||
|
/// Handle page fault
|
||||||
|
///
|
||||||
|
/// Return true if handling was sucessful,
|
||||||
|
/// otherwise the program will be interrupted and will
|
||||||
|
/// yield an error.
|
||||||
|
fn page_fault(
|
||||||
|
&mut self,
|
||||||
|
reason: MemoryAccessReason,
|
||||||
|
pagetable: &mut PageTable,
|
||||||
|
vaddr: u64,
|
||||||
|
size: PageSize,
|
||||||
|
dataptr: *mut u8,
|
||||||
|
) -> bool
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
}
|
|
@ -59,7 +59,9 @@ impl Debug for PtEntry {
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
#[repr(align(4096))]
|
#[repr(align(4096))]
|
||||||
pub struct PageTable {
|
pub struct PageTable {
|
||||||
|
/// How much entries are in use
|
||||||
pub childen: u8,
|
pub childen: u8,
|
||||||
|
/// Entries
|
||||||
pub table: [PtEntry; 256],
|
pub table: [PtEntry; 256],
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,10 @@ macro_rules! value_def {
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
#[repr(packed)]
|
#[repr(packed)]
|
||||||
pub union Value {
|
pub union Value {
|
||||||
$(pub $ty: $ty),*
|
$(
|
||||||
|
#[doc = concat!(stringify!($ty), " type")]
|
||||||
|
pub $ty: $ty
|
||||||
|
),*
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,10 +40,22 @@ macro_rules! value_def {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
|
/// Byte reinterpret value to target variant
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn cast<Variant: ValueVariant>(self) -> Variant {
|
pub fn cast<V: ValueVariant>(self) -> V {
|
||||||
|
/// Evil.
|
||||||
|
///
|
||||||
|
/// Transmute cannot be performed with generic type
|
||||||
|
/// as size is unknown, so union is used.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// If [`ValueVariant`] implemented correctly, it's fine :)
|
||||||
|
///
|
||||||
|
/// :ferrisClueless:
|
||||||
union Transmute<Variant: ValueVariant> {
|
union Transmute<Variant: ValueVariant> {
|
||||||
|
/// Self
|
||||||
src: Value,
|
src: Value,
|
||||||
|
/// Target variant
|
||||||
variant: Variant,
|
variant: Variant,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
spec.md
2
spec.md
|
@ -260,13 +260,13 @@
|
||||||
|
|
||||||
# Memory
|
# Memory
|
||||||
- Addresses are 64 bit
|
- Addresses are 64 bit
|
||||||
|
- Address `0x0` is invalid and acessing it traps
|
||||||
- Memory implementation is arbitrary
|
- Memory implementation is arbitrary
|
||||||
- In case of accessing invalid address:
|
- In case of accessing invalid address:
|
||||||
- Program shall trap (LoadAccessEx, StoreAccessEx) with parameter of accessed address
|
- Program shall trap (LoadAccessEx, StoreAccessEx) with parameter of accessed address
|
||||||
- Value of register when trapped is undefined
|
- Value of register when trapped is undefined
|
||||||
|
|
||||||
## Recommendations
|
## Recommendations
|
||||||
- Leave address `0x0` as invalid
|
|
||||||
- If paging used:
|
- If paging used:
|
||||||
- Leave first page invalid
|
- Leave first page invalid
|
||||||
- Pages should be at least 4 KiB
|
- Pages should be at least 4 KiB
|
||||||
|
|
Loading…
Reference in a new issue