Basic paging
This commit is contained in:
parent
2144c055d1
commit
2e6e6b7939
60
Cargo.lock
generated
60
Cargo.lock
generated
|
@ -29,6 +29,36 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||
name = "compiler"
|
||||
version = "0.1.0"
|
||||
|
||||
[[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"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d358e0ec5c59a5e1603b933def447096886121660fc680dc1e64a0753981fe3c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"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"
|
||||
|
@ -62,6 +92,8 @@ version = "0.1.0"
|
|||
name = "hbvm"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"delegate",
|
||||
"derive_more",
|
||||
"hashbrown",
|
||||
"hbbytecode",
|
||||
"log",
|
||||
|
@ -106,7 +138,7 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
"quote",
|
||||
"regex-syntax",
|
||||
"syn",
|
||||
"syn 2.0.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -154,12 +186,38 @@ 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"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.18"
|
||||
|
|
|
@ -7,6 +7,8 @@ edition = "2021"
|
|||
lto = true
|
||||
|
||||
[dependencies]
|
||||
delegate = "0.9"
|
||||
derive_more = "0.99"
|
||||
hashbrown = "0.13"
|
||||
hbbytecode.path = "../hbbytecode"
|
||||
log = "0.4"
|
||||
|
|
|
@ -1,109 +0,0 @@
|
|||
// HACK: This is temporary implementation so we can have memory instructions working
|
||||
|
||||
use {
|
||||
crate::vm::value::Value, alloc::boxed::Box, core::mem::MaybeUninit, hashbrown::HashMap,
|
||||
ma_size::MemAccessSize,
|
||||
};
|
||||
|
||||
pub const PAGE_SIZE: usize = 8192;
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Memory {
|
||||
pages: HashMap<u64, Box<[u8; PAGE_SIZE]>>,
|
||||
}
|
||||
|
||||
impl Memory {
|
||||
// HACK: Just for allocation testing, will be removed when proper memory interfaces
|
||||
// implemented.
|
||||
pub fn insert_test_page(&mut self) {
|
||||
self.pages.insert(0, unsafe {
|
||||
use alloc::alloc::{alloc_zeroed, handle_alloc_error, Layout};
|
||||
let layout = Layout::new::<[u8; PAGE_SIZE]>();
|
||||
let ptr = alloc_zeroed(layout);
|
||||
if ptr.is_null() {
|
||||
handle_alloc_error(layout);
|
||||
}
|
||||
Box::from_raw(ptr.cast())
|
||||
});
|
||||
}
|
||||
|
||||
/// Load value from an address
|
||||
pub fn load<S: MemAccessSize>(&self, addr: u64) -> Option<Value> {
|
||||
let (page, offset) = split_addr(addr);
|
||||
|
||||
// Check if copy won't get over page boundary (TODO: make it go over)
|
||||
if offset + S::BYTES <= PAGE_SIZE - 1 {
|
||||
let mut value = MaybeUninit::<Value>::zeroed();
|
||||
unsafe {
|
||||
core::ptr::copy_nonoverlapping(
|
||||
self.pages.get(&page)?.as_ptr().add(offset),
|
||||
value.as_mut_ptr().cast(),
|
||||
S::BYTES,
|
||||
);
|
||||
|
||||
// Even zeroed [`Value`] if holding valid variants as defined is valid,
|
||||
// this is always valid.
|
||||
Some(value.assume_init())
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Store value to an address
|
||||
pub fn store<S: MemAccessSize>(&mut self, addr: u64, value: Value) -> Result<(), ()> {
|
||||
let (page, offset) = split_addr(addr);
|
||||
|
||||
// Check if copy won't get over page boundary (TODO: make it go over)
|
||||
if offset + S::BYTES <= PAGE_SIZE - 1 {
|
||||
unsafe {
|
||||
core::ptr::copy_nonoverlapping(
|
||||
(&value as *const Value).cast::<u8>(),
|
||||
self.pages
|
||||
.get_mut(&page)
|
||||
.ok_or(())?
|
||||
.as_mut_ptr()
|
||||
.add(offset),
|
||||
S::BYTES,
|
||||
)
|
||||
};
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Split address into page number and in-page offset
|
||||
#[inline]
|
||||
pub const fn split_addr(addr: u64) -> (u64, usize) {
|
||||
(addr >> PAGE_SIZE.count_ones(), (addr as usize & PAGE_SIZE))
|
||||
}
|
||||
|
||||
macro_rules! size_markers {
|
||||
($($name:ident = $size:expr),* $(,)?) => {
|
||||
pub mod ma_size {
|
||||
/// # Safety
|
||||
/// Implementor has to assure that [`MemAccessSize::BYTES`] won't be larger than
|
||||
/// size of [`Value`]
|
||||
pub unsafe trait MemAccessSize {
|
||||
const BYTES: usize;
|
||||
}
|
||||
|
||||
$(
|
||||
pub struct $name;
|
||||
unsafe impl MemAccessSize for $name {
|
||||
const BYTES: usize = $size;
|
||||
}
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
size_markers! {
|
||||
Byte = 1,
|
||||
Doublet = 2,
|
||||
Quadlet = 4,
|
||||
Octlet = 8,
|
||||
}
|
129
hbvm/src/vm/mem/mod.rs
Normal file
129
hbvm/src/vm/mem/mod.rs
Normal file
|
@ -0,0 +1,129 @@
|
|||
// HACK: This is temporary implementation so we can have memory instructions working
|
||||
|
||||
mod paging;
|
||||
|
||||
use self::paging::{PageTable, Permission, PtEntry};
|
||||
use alloc::boxed::Box;
|
||||
use core::mem::MaybeUninit;
|
||||
use {crate::vm::value::Value, ma_size::MemAccessSize};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Memory {
|
||||
root_pt: *mut PageTable,
|
||||
}
|
||||
|
||||
impl Default for Memory {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
root_pt: Box::into_raw(Box::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Memory {
|
||||
fn drop(&mut self) {
|
||||
let _ = unsafe { Box::from_raw(self.root_pt) };
|
||||
}
|
||||
}
|
||||
|
||||
impl Memory {
|
||||
// HACK: Just for allocation testing, will be removed when proper memory interfaces
|
||||
// implemented.
|
||||
pub fn insert_test_page(&mut self) {
|
||||
unsafe {
|
||||
let mut entry = PtEntry::new(
|
||||
{
|
||||
let layout = alloc::alloc::Layout::from_size_align_unchecked(4096, 4096);
|
||||
let ptr = alloc::alloc::alloc(layout);
|
||||
if ptr.is_null() {
|
||||
alloc::alloc::handle_alloc_error(layout);
|
||||
}
|
||||
|
||||
core::ptr::write_bytes(ptr, 69, 10);
|
||||
ptr.cast()
|
||||
},
|
||||
Permission::Write,
|
||||
);
|
||||
|
||||
for _ in 0..4 {
|
||||
let mut pt = Box::<PageTable>::default();
|
||||
pt[0] = entry;
|
||||
entry = PtEntry::new(Box::into_raw(pt) as _, Permission::Node);
|
||||
}
|
||||
|
||||
self.root_pt_mut()[0] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
/// Load value from an address
|
||||
pub fn load<S: MemAccessSize>(&self, addr: u64) -> Option<Value> {
|
||||
let mut current_pt = self.root_pt;
|
||||
for lvl in (0..5).rev() {
|
||||
unsafe {
|
||||
let entry = (*current_pt).get_unchecked(
|
||||
usize::try_from((addr >> (lvl * 9 + 12)) & ((1 << 9) - 1))
|
||||
.expect("?conradluget a better CPU"),
|
||||
);
|
||||
|
||||
let ptr = entry.ptr();
|
||||
match entry.permission() {
|
||||
Permission::Empty => return None,
|
||||
Permission::Node => current_pt = ptr as _,
|
||||
Permission::Readonly | Permission::Write | Permission::Exec => {
|
||||
let mut value = MaybeUninit::<Value>::zeroed();
|
||||
core::ptr::copy_nonoverlapping::<u8>(
|
||||
entry.ptr() as _,
|
||||
value.as_mut_ptr().cast(),
|
||||
1,
|
||||
);
|
||||
return Some(value.assume_init());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Store value to an address
|
||||
pub fn store<S: MemAccessSize>(&mut self, addr: u64, value: Value) -> Result<(), ()> {
|
||||
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 }
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! size_markers {
|
||||
($($name:ident = $size:expr),* $(,)?) => {
|
||||
pub mod ma_size {
|
||||
/// # Safety
|
||||
/// Implementor has to assure that [`MemAccessSize::BYTES`] won't be larger than
|
||||
/// size of [`Value`]
|
||||
pub unsafe trait MemAccessSize {
|
||||
const BYTES: usize;
|
||||
}
|
||||
|
||||
$(
|
||||
pub struct $name;
|
||||
unsafe impl MemAccessSize for $name {
|
||||
const BYTES: usize = $size;
|
||||
}
|
||||
)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
size_markers! {
|
||||
Byte = 1,
|
||||
Doublet = 2,
|
||||
Quadlet = 4,
|
||||
Octlet = 8,
|
||||
}
|
107
hbvm/src/vm/mem/paging.rs
Normal file
107
hbvm/src/vm/mem/paging.rs
Normal file
|
@ -0,0 +1,107 @@
|
|||
use core::{
|
||||
fmt::Debug,
|
||||
mem::MaybeUninit,
|
||||
ops::{Index, IndexMut},
|
||||
slice::SliceIndex,
|
||||
};
|
||||
use delegate::delegate;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub enum Permission {
|
||||
#[default]
|
||||
Empty,
|
||||
Node,
|
||||
Readonly,
|
||||
Write,
|
||||
Exec,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default, PartialEq, Eq)]
|
||||
pub struct PtEntry(u64);
|
||||
impl PtEntry {
|
||||
#[inline]
|
||||
pub unsafe fn new(ptr: *mut PtPointedData, permission: Permission) -> Self {
|
||||
Self(ptr as u64 | permission as u64)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn permission(&self) -> Permission {
|
||||
unsafe { core::mem::transmute(self.0 as u8 & 0b111) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn ptr(&self) -> *mut PtPointedData {
|
||||
(self.0 & !((1 << 12) - 1)) as _
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for PtEntry {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("PtEntry")
|
||||
.field("ptr", &self.ptr())
|
||||
.field("permission", &self.permission())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[repr(align(4096))]
|
||||
pub struct PageTable([PtEntry; 512]);
|
||||
|
||||
impl PageTable {
|
||||
delegate!(to self.0 {
|
||||
pub unsafe fn get<I>(&self, ix: I) -> Option<&I::Output>
|
||||
where I: SliceIndex<[PtEntry]>;
|
||||
|
||||
pub unsafe fn get_mut<I>(&mut self, ix: I) -> Option<&mut I::Output>
|
||||
where I: SliceIndex<[PtEntry]>;
|
||||
|
||||
pub unsafe fn get_unchecked<I>(&self, index: I) -> &I::Output
|
||||
where I: SliceIndex<[PtEntry]>;
|
||||
|
||||
pub unsafe fn get_unchecked_mut<I>(&mut self, index: I) -> &mut I::Output
|
||||
where I: SliceIndex<[PtEntry]>;
|
||||
});
|
||||
}
|
||||
|
||||
impl<Idx> Index<Idx> for PageTable
|
||||
where
|
||||
Idx: SliceIndex<[PtEntry]>,
|
||||
{
|
||||
type Output = Idx::Output;
|
||||
|
||||
#[inline(always)]
|
||||
fn index(&self, index: Idx) -> &Self::Output {
|
||||
&self.0[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl<Idx> IndexMut<Idx> for PageTable
|
||||
where
|
||||
Idx: SliceIndex<[PtEntry]>,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn index_mut(&mut self, index: Idx) -> &mut Self::Output {
|
||||
&mut self.0[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PageTable {
|
||||
fn default() -> Self {
|
||||
Self(unsafe { MaybeUninit::zeroed().assume_init() })
|
||||
}
|
||||
}
|
||||
|
||||
/// This byte is special. It has a whole page reserved just for it.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Deref, DerefMut)]
|
||||
#[repr(align(4096))]
|
||||
pub struct SpecialByte(u8);
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(C)]
|
||||
pub union PtPointedData {
|
||||
pub pt: PageTable,
|
||||
pub page: SpecialByte,
|
||||
}
|
Loading…
Reference in a new issue