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"
|
name = "compiler"
|
||||||
version = "0.1.0"
|
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]]
|
[[package]]
|
||||||
name = "fnv"
|
name = "fnv"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
|
@ -62,6 +92,8 @@ version = "0.1.0"
|
||||||
name = "hbvm"
|
name = "hbvm"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"delegate",
|
||||||
|
"derive_more",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
"hbbytecode",
|
"hbbytecode",
|
||||||
"log",
|
"log",
|
||||||
|
@ -106,7 +138,7 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"regex-syntax",
|
"regex-syntax",
|
||||||
"syn",
|
"syn 2.0.18",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -154,12 +186,38 @@ version = "0.6.29"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
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]]
|
[[package]]
|
||||||
name = "static_assertions"
|
name = "static_assertions"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
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]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.18"
|
version = "2.0.18"
|
||||||
|
|
|
@ -7,6 +7,8 @@ edition = "2021"
|
||||||
lto = true
|
lto = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
delegate = "0.9"
|
||||||
|
derive_more = "0.99"
|
||||||
hashbrown = "0.13"
|
hashbrown = "0.13"
|
||||||
hbbytecode.path = "../hbbytecode"
|
hbbytecode.path = "../hbbytecode"
|
||||||
log = "0.4"
|
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