Paging should work ish, need to complete page reservation for kernel app. need to set stack pointer, then we are ready to hit it.

This commit is contained in:
ivajon
2026-05-11 18:16:33 +02:00
parent c68d3fab14
commit 4c2a5b1068
8 changed files with 970 additions and 30 deletions

28
kernel/link.x Normal file
View File

@@ -0,0 +1,28 @@
ENTRY(efi_main)
SECTIONS {
. = 0x100000; /* or whatever uefi loads you at */
__kernel_start = .;
. = ALIGN(4K);
.text : { *(.text .text.*) }
. = ALIGN(4K);
.rodata : { *(.rodata .rodata.*) }
. = ALIGN(4K);
.data : { *(.data .data.*) }
. = ALIGN(4K);
.bss : { *(.bss .bss.*) }
. = ALIGN(4K);
__kernel_end = .;
/DISCARD/ : {
*(.eh_frame)
*(.eh_frame_hdr)
*(.note.*)
*(.comment)
}
}

View File

@@ -4,7 +4,13 @@
use uefi::{EfiMemoryType, EfiSystemTable, ExitBootServicesKey, Handle, UefiStatus};
use kernel::{handler, paging::create_region_map, print};
use kernel::{
handler,
paging::{
self, RegionAllocator, RegionRequest, create_region_map, kernel::reserve_kernel_pages,
},
print,
};
use x86_drivers::gdt::{DPL, GdtEntry, GdtPointer, GdtSelector};
pub use x86_drivers::prelude::*;
@@ -34,7 +40,7 @@ extern "efiapi" fn efi_main(handle: Handle, table: *mut EfiSystemTable) -> UefiS
.expect("memory map failed");
let map = create_region_map::<1024>(map);
for el in map {
for el in &map {
print!("Map element {:?}\r\n", el);
}
@@ -42,6 +48,9 @@ extern "efiapi" fn efi_main(handle: Handle, table: *mut EfiSystemTable) -> UefiS
print!("Default {:?}", default);
let boot_services = table.boot_services();
let image_info = boot_services.get_image_info(handle).unwrap();
print!("Kernel @ {image_info:?}");
boot_services
.exit_boot_services(handle, arena)
.expect("exit boot services failed");
@@ -110,6 +119,12 @@ extern "efiapi" fn efi_main(handle: Handle, table: *mut EfiSystemTable) -> UefiS
)
.unwrap();
print!("Set all ISRs :)");
let mut alloc = RegionAllocator::new(map);
let res = alloc.request_pages(&RegionRequest::new(2));
print!("Region request returned ({res:?})\r\n");
let sp = reserve_kernel_pages(image_info.0, image_info.1, 4096, &mut alloc);
print!("Potential new stack pointer: {sp:?}\r\n");
#[allow(clippy::empty_loop)]
loop {}

View File

@@ -1,10 +1,16 @@
use core::alloc::Layout;
use uefi::{EfiMemoryType, PageIter};
use crate::print;
pub mod kernel;
const PAGE_SIZE: usize = 4096;
#[repr(C, align(4096))]
pub struct Page {
data: [u8; 4096],
data: [u8; PAGE_SIZE],
}
#[derive(Clone, Copy, Debug)]
@@ -21,6 +27,11 @@ impl Region {
}
}
pub const fn skip_page(&mut self) {
unsafe { self.start = self.start.add(1) };
self.count -= 1;
}
pub const fn new(start: *const Page, count: usize) -> Self {
Self { start, count }
}
@@ -61,20 +72,7 @@ pub fn create_region_map<const BUFFER_SIZE: usize>(
})
.map(|t| Region::new(t.physical_start.raw_ptr(), t.number_of_pages as usize)),
);
vec.sort_unstable_by_key(|f| f.start);
for start in (0..(vec.len().saturating_sub(1))).rev() {
let potential_join = start + 1;
let possible = vec[start].can_coalesce(&vec[potential_join]);
if !possible {
continue;
}
let joinable = vec.remove(potential_join);
vec[start] = vec[start].coalesce(joinable);
}
coalesce(&mut vec, usize::MAX);
vec
}
@@ -83,25 +81,189 @@ pub struct RegionAllocator<const MAX_REGIONS: usize> {
allocators: heapless::Vec<Zone, MAX_REGIONS>,
}
const BUDDY_N: usize = 8;
pub struct Zone {
region: Region,
allocator: buddyalloc::Heap<24>,
allocator: buddyalloc::Heap<BUDDY_N>,
}
#[derive(Debug)]
pub enum Error {
/// The allocator could not allocate the requested buffer.
InsufficientMemory,
}
pub fn split_in_to_power_of_twos<const MAX_REGIONS: usize>(
regions: &mut heapless::Vec<Region, MAX_REGIONS>,
) {
let mut idx = 0;
while idx < regions.len() {
let region = &mut regions[idx];
idx += 1;
// TODO: Split in to regions of power of two, this should be binary searchable ish
if region.start.is_null() {
region.skip_page();
}
if region.size().is_power_of_two() {
continue;
}
// TODO: Handle this case in some sound way.
let new_count = find_power_of_two(region.count, PAGE_SIZE).unwrap();
// RIGHT
let other_region_start = unsafe { region.start.add(new_count) };
let other_region_size = region.count - new_count;
region.count = new_count;
regions
.push(Region::new(other_region_start, other_region_size))
.unwrap();
}
}
pub fn coalesce<const BUFFER_SIZE: usize>(
vec: &mut heapless::Vec<Region, BUFFER_SIZE>,
max_size: usize,
) -> bool {
vec.sort_unstable_by_key(|f| f.start);
let mut merged = false;
for start in (0..(vec.len().saturating_sub(1))).rev() {
let potential_join = start + 1;
let possible = vec[start].can_coalesce(&vec[potential_join]) && vec[start].count < max_size;
if !possible {
continue;
}
let joinable = vec.remove(potential_join);
vec[start] = vec[start].coalesce(joinable);
merged = true;
}
merged
}
pub fn pre_process<const BUFFER_SIZE: usize>(vec: &mut heapless::Vec<Region, BUFFER_SIZE>) {
let mut sizes: heapless::Vec<_, BUFFER_SIZE> =
heapless::Vec::from_iter(vec.iter().map(|el| el.count));
sizes.sort_unstable();
split_in_to_power_of_twos(vec);
for size in sizes.iter().rev() {
let merged = coalesce(vec, *size);
print!("Did merge {:?}, @ {size}", merged);
split_in_to_power_of_twos(vec);
if !merged {
return;
}
}
}
const fn find_power_of_two(mut count: usize, size: usize) -> Option<usize> {
if (count * size).is_power_of_two() {
return Some(count);
}
while count != 0 {
count -= 1;
if (count * size).is_power_of_two() {
return Some(count);
}
}
None
}
impl<const MAX_REGIONS: usize> RegionAllocator<MAX_REGIONS> {
pub fn new(regions: heapless::Vec<Region, MAX_REGIONS>) -> Self {
let allocators = heapless::Vec::from_iter(regions.into_iter().map(|region| Zone {
allocator: unsafe {
buddyalloc::Heap::new(
core::ptr::NonNull::new(region.start.cast_mut().cast()).unwrap(),
region.size(),
)
.unwrap()
},
region,
pub fn new(mut regions: heapless::Vec<Region, MAX_REGIONS>) -> Self {
pre_process(&mut regions);
// TODO: Buddy allocator is not optimal at all here, we need to runtime dispatcher to select
// the "best" alloc `N` for that size.
let allocators = heapless::Vec::from_iter(regions.into_iter().map(|mut region| {
print!(
"Creating region for {:?}, size {}, expected_min_block {}\r\n",
region,
region.size(),
region.size() >> (BUDDY_N - 1)
);
Zone {
allocator: unsafe {
buddyalloc::Heap::new(
core::ptr::NonNull::new(region.start.cast_mut().cast()).unwrap(),
region.size(),
)
.unwrap()
},
region,
}
}));
Self { allocators }
}
pub fn request_pages
pub fn request_pages(&mut self, request: &RegionRequest) -> Result<Region, Error> {
for alloc in &mut self.allocators {
if !(request.min_address..request.max_address).contains(&(alloc.region.start as u64)) {
continue;
}
let Ok(val) = alloc.try_alloc(request) else {
continue;
};
return Ok(val);
}
Err(Error::InsufficientMemory)
}
}
pub struct RegionRequest {
count: usize,
min_address: u64,
max_address: u64,
}
impl Zone {
fn try_alloc(&mut self, region_request: &RegionRequest) -> Result<Region, Error> {
let layout = Layout::from_size_align(region_request.size_in_bytes(), PAGE_SIZE).unwrap();
let Ok(ptr) = self.allocator.allocate(layout) else {
return Err(Error::InsufficientMemory);
};
Ok(Region {
start: ptr.cast(),
count: region_request.count(),
})
}
}
impl RegionRequest {
pub const fn new(count: usize) -> Self {
Self {
count,
max_address: u64::MAX,
min_address: u64::MIN,
}
}
pub const fn new_with_address_filter(count: usize, min_address: u64, max_address: u64) -> Self {
Self {
count,
min_address,
max_address,
}
}
pub const fn count(&self) -> usize {
self.count
}
pub const fn size_in_bytes(&self) -> usize {
self.count * PAGE_SIZE
}
pub const fn alignment_in_bytes(&self) -> usize {
PAGE_SIZE
}
}

141
kernel/src/paging/kernel.rs Normal file
View File

@@ -0,0 +1,141 @@
use core::{
char::MAX,
sync::atomic::{AtomicBool, AtomicU8},
};
use x86_drivers::paging::{
BanExecution, Cache, CacheWriteThrough, KernelMode, Page, PageAttributeTable, PageDirectory,
PageDirectoryPointerTable, PageMapLevel4, PageTable, PdEntry, PdptEntry, Pml4Entry,
};
use crate::{
paging::{PAGE_SIZE, RegionAllocator},
print,
};
static mut PML4: PageMapLevel4 = PageMapLevel4::null();
static mut KERNEL_PHYSMAP_PDPT: PageDirectoryPointerTable<KernelMode> =
PageDirectoryPointerTable::null();
static mut KERNEL_PHYSMAP_PAGE_DIRECTORY: PageDirectory<KernelMode> = PageDirectory::null();
static mut KERNEL_PAGE_TABLE: PageTable<KernelMode> = PageTable::null();
static MAX_ADDR: AtomicU8 = AtomicU8::new(0);
#[allow(static_mut_refs)]
pub fn get_max_addr() -> u8 {
let old_val = MAX_ADDR.load(core::sync::atomic::Ordering::Relaxed);
if old_val != 0 {
return old_val;
}
let max_addr = x86_drivers::cpuid::MaxAddrInfo::new().max_physical_address();
MAX_ADDR.store(max_addr, core::sync::atomic::Ordering::Release);
max_addr
}
#[allow(static_mut_refs)]
pub fn reserve_kernel_pages<const MAX_REGIONS: usize>(
app_ptr: *const u8,
app_size: u64,
stack_size: usize,
allocator: &mut RegionAllocator<MAX_REGIONS>,
) -> Result<*const super::Page, super::Error> {
// TODO: This is going to bite me, need to track the addresses in some in memory abstraction.
let page_ptr = (app_ptr as u64 % PAGE_SIZE as u64) as *const [u8; PAGE_SIZE];
let pages = app_size as usize / PAGE_SIZE;
for page in 0..pages {
unsafe {
KERNEL_PAGE_TABLE
.register(
page as usize,
Page::new(
page_ptr.add(page) as *const _,
x86_drivers::paging::Mutable::Yes,
// Think this is a good idea, we should keep memory in sync?
CacheWriteThrough::On,
// Generally we do not
Cache::On,
PageAttributeTable::On,
get_max_addr().into(),
BanExecution::No,
),
)
.unwrap();
}
}
let stack_pages = stack_size / PAGE_SIZE;
// TODO: Make some better upper bound here :(
let result = allocator.request_pages(&super::RegionRequest::new_with_address_filter(
stack_pages,
unsafe { page_ptr.add(pages) as u64 },
u64::MAX / 2,
))?;
for page in 0..stack_pages {
let offset_in_table = pages + page;
print!("IDX {offset_in_table}");
unsafe {
KERNEL_PAGE_TABLE
.register(
offset_in_table,
Page::new(
result.start.add(page) as *const _,
x86_drivers::paging::Mutable::Yes,
// Not sure if we should have write through on here? a bunch of invalidation
CacheWriteThrough::On,
Cache::On,
PageAttributeTable::On,
get_max_addr().into(),
BanExecution::Yes,
),
)
.unwrap();
}
}
unsafe {
KERNEL_PHYSMAP_PAGE_DIRECTORY
.register_nested(
0,
PdEntry::new(
core::ptr::addr_of!(KERNEL_PAGE_TABLE).cast(),
CacheWriteThrough::Off,
Cache::On,
get_max_addr().into(),
),
)
.unwrap();
}
unsafe {
KERNEL_PHYSMAP_PDPT
.register_nested(
0,
PdptEntry::new(
core::ptr::addr_of!(KERNEL_PAGE_TABLE).cast(),
CacheWriteThrough::Off,
Cache::On,
get_max_addr().into(),
),
)
.unwrap();
}
unsafe {
PML4.register(
511,
Pml4Entry::<KernelMode>::new(
core::ptr::addr_of!(KERNEL_PAGE_TABLE).cast(),
CacheWriteThrough::Off,
Cache::On,
get_max_addr().into(),
),
)
.unwrap();
}
Ok(result.start)
}

View File

@@ -73,6 +73,20 @@ pub struct Handle(*mut ());
#[derive(Debug)]
pub struct ExitBootServicesKey(usize);
#[repr(C, packed)]
pub struct Guid {
_1: u32,
_2: u16,
_3: u16,
_4: [u8; 8],
}
const EFI_LOADED_IMAGE_PROTOCOL_GUID: Guid = Guid {
_1: 0x5B1B31A1,
_2: 0x9562,
_3: 0x11d2,
_4: [0x8E, 0x3F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B],
};
#[repr(C)]
pub struct EfiBootServices {
hdr: EfiTableHeader,
@@ -111,7 +125,12 @@ pub struct EfiBootServices {
install_protocol_interface: *const core::ffi::c_void,
reinstall_protocol_interface: *const core::ffi::c_void,
uninstall_protocol_interface: *const core::ffi::c_void,
handle_protocol: *const core::ffi::c_void,
// TODO: Handle interface here
handle_protocol: extern "efiapi" fn(
efi_handle: Handle,
protocol: *const Guid,
interface: *const *const core::ffi::c_void,
) -> RawUefiStatus,
reserved: *const core::ffi::c_void,
register_protocol_notify: *const core::ffi::c_void,
locate_handle: *const core::ffi::c_void,
@@ -233,6 +252,20 @@ impl<'a> Iterator for PageIter<'a> {
}
}
#[repr(C)]
pub struct EfiLoadedImageProtocol {
pub revision: u32,
pub parent_handle: Handle,
pub system_table: *mut EfiSystemTable,
pub device_handle: Handle,
pub file_path: *mut core::ffi::c_void, // EFI_DEVICE_PATH_PROTOCOL*
pub reserved: *mut core::ffi::c_void,
pub load_options_size: u32,
pub load_options: *mut core::ffi::c_void,
pub image_base: *mut u8, // ← what you want
pub image_size: u64, // ← what you want
}
impl EfiBootServices {
pub fn memory_map<'a, 'b>(
&mut self,
@@ -287,6 +320,27 @@ impl EfiBootServices {
}
Err(Some(res))
}
pub fn get_image_info(&self, handle: Handle) -> Result<(*const u8, u64), Option<UefiStatus>> {
let protocol = EFI_LOADED_IMAGE_PROTOCOL_GUID;
let mut interface: *mut EfiLoadedImageProtocol = core::ptr::null_mut();
let Some(ret) = (self.handle_protocol)(
handle,
core::ptr::from_ref(&protocol),
core::ptr::from_mut(&mut interface).cast(),
)
.interpret() else {
return Err(None);
};
if ret.is_error() {
return Err(Some(ret));
}
let result = (unsafe { interface.read() });
Ok((result.image_base, result.image_size))
}
}
impl EfiSystemTable {

90
x86_drivers/src/cpuid.rs Normal file
View File

@@ -0,0 +1,90 @@
#[allow(unused)]
pub struct CpuIdStatus {
source_call: (u32, u32),
eax: u32,
ebx: u32,
ecx: u32,
edx: u32,
}
/// Gets the cpuid data.
///
/// # Safety
///
/// This function is safe to use in a race free context, it must be the only caller of `cpuid`
/// at the same time. this will likely allways be the case.
fn cpuid_with_ebx(leaf: u32, subleaf: u32) -> CpuIdStatus {
let eax: u32;
let ebx: u32;
let ecx: u32;
let edx: u32;
unsafe {
core::arch::asm!(
// Apparently rust assumes no violence happends to rbx
"push rbx",
"cpuid",
"mov {ebx:e}, ebx",
"pop rbx",
inout("eax") leaf => eax,
inout("ecx") subleaf => ecx,
out ("edx") edx,
ebx = out(reg) ebx,
options(nostack, preserves_flags),
);
}
CpuIdStatus {
source_call: (leaf, subleaf),
eax,
ebx,
ecx,
edx,
}
}
fn cpuid(leaf: u32, subleaf: Option<u32>) -> CpuIdStatus {
let subleaf = subleaf.unwrap_or_default();
cpuid_with_ebx(leaf, subleaf)
}
#[repr(C, packed)]
pub struct MaxAddrInfo {
phys_address_size: u8,
linear_address_size: u8,
guest_phys_addr_size: u8,
_padding: u8,
}
impl MaxAddrInfo {
#[must_use]
/// Fetches the cpus max address info.
pub fn new() -> Self {
// SAFETY:
//
// This is safe assuming that the rust compiler compiler does not break the C abi.
unsafe { core::mem::transmute::<u32, Self>(cpuid(0x8000_0008, None).eax) }
}
#[must_use]
pub const fn max_physical_address(&self) -> u8 {
self.phys_address_size
}
#[must_use]
pub const fn max_linear_address(&self) -> u8 {
self.linear_address_size
}
#[must_use]
pub const fn max_guest_address(&self) -> u8 {
if self.guest_phys_addr_size == 0 {
return self.max_physical_address();
}
self.guest_phys_addr_size
}
}
impl Default for MaxAddrInfo {
fn default() -> Self {
Self::new()
}
}

View File

@@ -28,8 +28,10 @@
)]
pub mod uart;
pub mod cpuid;
pub mod gdt;
pub mod idt;
pub mod paging;
pub mod core;

448
x86_drivers/src/paging.rs Normal file
View File

@@ -0,0 +1,448 @@
use core::marker::PhantomData;
pub trait Mode {
const MODE: UserOrSupervisor;
}
pub trait PageKind {
const PAGE_TYPE: PageType;
}
#[repr(transparent)]
#[derive(Clone, Copy)]
#[must_use]
pub struct PageDirectoryEntry(u64);
#[repr(transparent)]
#[derive(Clone, Copy)]
#[must_use]
pub struct Pml4Entry<M: Mode>(PageDirectoryEntry, PhantomData<M>); // only points to PDPT
#[repr(transparent)]
#[derive(Clone, Copy)]
#[must_use]
pub struct PdptEntry<M: Mode>(PageDirectoryEntry, PhantomData<M>); // points to PD OR is 1GB page
#[repr(transparent)]
#[derive(Clone, Copy)]
#[must_use]
pub struct PdEntry<M: Mode>(PageDirectoryEntry, PhantomData<M>); // points to PT OR is 2MB page
#[repr(transparent)]
#[derive(Clone, Copy)]
#[must_use]
pub struct PtEntry<M: Mode>(PageDirectoryEntry, PhantomData<M>); // always 4KB page
#[repr(transparent)]
#[derive(Clone, Copy)]
#[must_use]
pub struct Page<M: Mode, S: PageKind>(PageDirectoryEntry, PhantomData<M>, PhantomData<S>); // always 4KB page
pub struct K4;
pub struct M2;
pub struct G1;
pub struct UserMode;
pub struct KernelMode;
impl PageKind for K4 {
const PAGE_TYPE: PageType = PageType::Page4K;
}
impl PageKind for M2 {
const PAGE_TYPE: PageType = PageType::Page2M;
}
impl PageKind for G1 {
const PAGE_TYPE: PageType = PageType::Page1G;
}
impl Mode for KernelMode {
const MODE: UserOrSupervisor = UserOrSupervisor::Supervisor;
}
impl Mode for UserMode {
const MODE: UserOrSupervisor = UserOrSupervisor::User;
}
#[repr(u8)]
#[derive(Debug, PartialEq, Eq)]
pub enum UserOrSupervisor {
User = 1,
Supervisor = 0,
}
#[repr(u8)]
#[derive(Debug, PartialEq, Eq)]
pub enum Mutable {
Yes = 1,
No = 0,
}
#[repr(u8)]
#[derive(Debug, PartialEq, Eq)]
pub enum CacheWriteThrough {
/// This forces write to RAM and CACHE every time, costly in time.
/// But saves cache flushing.
On = 1,
/// This writes to RAM on every write, will force cache flushing after write.
Off = 0,
}
#[repr(u8)]
#[derive(Debug, PartialEq, Eq)]
pub enum Cache {
/// Disables caching for this address space.
On = 0,
/// Disables caching for this address space.
Off = 1,
}
// PS is hard pulled to 0 on 64 bit.
// GLOBAL: this is omitted for now, but could be really neat for things like system timers since TLB
// will not be reloaded there, could also be really good to have things like VTABLE for kernel
// fuinctions etc etc
// AVL: pulled to 0 on 64 bit, might want to handle them for offloading to disk?
#[repr(u8)]
#[derive(Debug, PartialEq, Eq)]
pub enum PageAttributeTable {
/// Allows setting cache config
On = 1,
/// Disallows cache config
Off = 0,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum PageType {
Page1G,
Page2M,
Page4K,
}
#[repr(u8)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum BanExecution {
Yes = 1,
No = 0,
}
impl PageDirectoryEntry {
// TODO: Make the compiler show us that lifetimes are fine here
const fn new_super(
next_page: u64,
mutable: Mutable,
supervisor: UserOrSupervisor,
write_through: CacheWriteThrough,
cache: Cache,
max_addr: u64,
executable: BanExecution,
) -> Self {
let mut ret = 0b1;
ret |= (mutable as u64) << 1;
ret |= (supervisor as u64) << 2;
ret |= (write_through as u64) << 3;
ret |= (cache as u64) << 4;
// A: hard pulled to 0 at init.
// D: hard pulled to 0 at init.
let mut addr = next_page >> 12;
addr &= (1 << (max_addr - 12)) - 1;
ret |= addr << 12;
ret |= (executable as u64) << 63;
Self(ret)
}
const fn new_terminal_node(
page: u64,
mutable: Mutable,
supervisor: UserOrSupervisor,
write_through: CacheWriteThrough,
cache: Cache,
pat: PageAttributeTable,
page_ty: PageType,
max_addr: u64,
executable: BanExecution,
) -> Self {
let mut ret = 0b1;
ret |= (mutable as u64) << 1;
ret |= (supervisor as u64) << 2;
ret |= (write_through as u64) << 3;
ret |= (cache as u64) << 4;
// A: hard pulled to 0 at init.
// D: hard pulled to 0 at init.
// Only sets if page type is huge page
ret |= page_ty.ps() << 7;
let (pat_shift, addr_lsb) = match page_ty {
PageType::Page1G => (12, 30),
PageType::Page2M => (12, 21),
PageType::Page4K => (7, 12),
};
ret |= (pat as u64) << pat_shift;
let mut addr = page >> addr_lsb;
addr &= (1 << (max_addr - addr_lsb)) - 1;
ret |= addr << addr_lsb;
ret |= (executable as u64) << 63;
Self(ret)
}
}
impl PageType {
const fn ps(&self) -> u64 {
match self {
Self::Page4K => 0,
Self::Page1G | Self::Page2M => 1,
}
}
}
impl<S: PageKind, M: Mode> Page<M, S> {
pub fn new(
page: *const u8,
mutable: Mutable,
write_through: CacheWriteThrough,
cache: Cache,
pat: PageAttributeTable,
max_addr: u64,
executable: BanExecution,
) -> Self {
Self(
PageDirectoryEntry::new_terminal_node(
page as u64,
mutable,
M::MODE,
write_through,
cache,
pat,
S::PAGE_TYPE,
max_addr,
executable,
),
PhantomData,
PhantomData,
)
}
}
impl<M: Mode> Pml4Entry<M> {
// Do no think that we will have a top level that controls mutability of all subpages, seems
// odd.
pub fn new(
next_page: *const PageDirectoryPointerTable<M>,
write_through: CacheWriteThrough,
cache: Cache,
max_addr: u64,
) -> Self {
Self(
PageDirectoryEntry::new_super(
next_page as u64,
Mutable::Yes,
M::MODE,
write_through,
cache,
max_addr,
BanExecution::No,
),
PhantomData,
)
}
}
impl<M: Mode> PdptEntry<M> {
// Do no think that we will have a top level that controls mutability of all subpages, seems
// odd.
pub fn new(
next_page: *const PageDirectory<M>,
write_through: CacheWriteThrough,
cache: Cache,
max_addr: u64,
) -> Self {
Self(
PageDirectoryEntry::new_super(
next_page as u64,
Mutable::Yes,
M::MODE,
write_through,
cache,
max_addr,
BanExecution::No,
),
PhantomData,
)
}
}
impl<M: Mode> PdEntry<M> {
// Do no think that we will have a top level that controls mutability of all subpages, seems
// odd.
pub fn new(
next_page: *const PageTable<M>,
write_through: CacheWriteThrough,
cache: Cache,
max_addr: u64,
) -> Self {
Self(
PageDirectoryEntry::new_super(
next_page as u64,
Mutable::Yes,
M::MODE,
write_through,
cache,
max_addr,
BanExecution::No,
),
PhantomData,
)
}
}
#[derive(Debug)]
pub enum RegisterError {
IndexOutOfBounds,
InvalidIndexForPrivilegeLevel,
}
impl PageMapLevel4 {
pub fn register<M: Mode>(
&mut self,
offset: usize,
page: Pml4Entry<M>,
) -> Result<&mut Self, RegisterError> {
let Some(val) = self.table.get_mut(offset) else {
return Err(RegisterError::IndexOutOfBounds);
};
match (M::MODE, offset) {
(UserOrSupervisor::Supervisor, 256..) => (),
(UserOrSupervisor::User, ..256) => (),
_ => return Err(RegisterError::InvalidIndexForPrivilegeLevel),
}
*val = page.0;
Ok(self)
}
pub const fn null() -> Self {
Self {
table: [PageDirectoryEntry(0); _],
}
}
}
impl<M: Mode> PageDirectoryPointerTable<M> {
pub fn register_page(
&mut self,
offset: usize,
page: Page<M, G1>,
) -> Result<&mut Self, RegisterError> {
let Some(val) = self.table.get_mut(offset) else {
return Err(RegisterError::IndexOutOfBounds);
};
*val = page.0;
Ok(self)
}
pub fn register_nested(
&mut self,
offset: usize,
page: PdptEntry<M>,
) -> Result<&mut Self, RegisterError> {
let Some(val) = self.table.get_mut(offset) else {
return Err(RegisterError::IndexOutOfBounds);
};
*val = page.0;
Ok(self)
}
pub const fn null() -> Self {
Self {
table: [PageDirectoryEntry(0); _],
_0: PhantomData,
}
}
}
impl<M: Mode> PageDirectory<M> {
pub fn register_page(
&mut self,
offset: usize,
page: Page<M, M2>,
) -> Result<&mut Self, RegisterError> {
let Some(val) = self.table.get_mut(offset) else {
return Err(RegisterError::IndexOutOfBounds);
};
*val = page.0;
Ok(self)
}
pub fn register_nested(
&mut self,
offset: usize,
page: PdEntry<M>,
) -> Result<&mut Self, RegisterError> {
let Some(val) = self.table.get_mut(offset) else {
return Err(RegisterError::IndexOutOfBounds);
};
*val = page.0;
Ok(self)
}
pub const fn null() -> Self {
Self {
table: [PageDirectoryEntry(0); _],
_0: PhantomData,
}
}
}
impl<M: Mode> PageTable<M> {
pub fn register(
&mut self,
offset: usize,
page: Page<M, K4>,
) -> Result<&mut Self, RegisterError> {
let Some(val) = self.table.get_mut(offset) else {
return Err(RegisterError::IndexOutOfBounds);
};
*val = page.0;
Ok(self)
}
pub const fn null() -> Self {
Self {
table: [PageDirectoryEntry(0); _],
_0: PhantomData,
}
}
}
#[repr(C, align(4096))]
pub struct PageMapLevel4 {
// This seems it should be 512 * 8 bytes
table: [PageDirectoryEntry; 512],
}
#[repr(C, align(4096))]
pub struct PageDirectoryPointerTable<M: Mode> {
// This seems it should be 512 * 8 bytes
table: [PageDirectoryEntry; 512],
_0: PhantomData<M>,
}
#[repr(C, align(4096))]
pub struct PageDirectory<M: Mode> {
// This seems it should be 512 * 8 bytes
table: [PageDirectoryEntry; 512],
_0: PhantomData<M>,
}
#[repr(C, align(4096))]
pub struct PageTable<M: Mode> {
// This seems it should be 512 * 8 bytes
table: [PageDirectoryEntry; 512],
_0: PhantomData<M>,
}