diff --git a/kernel/link.x b/kernel/link.x new file mode 100644 index 0000000..a9e94fb --- /dev/null +++ b/kernel/link.x @@ -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) + } +} diff --git a/kernel/src/main.rs b/kernel/src/main.rs index a159c48..425d7eb 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -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 {} diff --git a/kernel/src/paging.rs b/kernel/src/paging.rs index 8adc63d..d43539e 100644 --- a/kernel/src/paging.rs +++ b/kernel/src/paging.rs @@ -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( }) .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 { allocators: heapless::Vec, } +const BUDDY_N: usize = 8; pub struct Zone { region: Region, - allocator: buddyalloc::Heap<24>, + allocator: buddyalloc::Heap, +} + +#[derive(Debug)] +pub enum Error { + /// The allocator could not allocate the requested buffer. + InsufficientMemory, +} + +pub fn split_in_to_power_of_twos( + regions: &mut heapless::Vec, +) { + 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( + vec: &mut heapless::Vec, + 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(vec: &mut heapless::Vec) { + 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 { + 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 RegionAllocator { - pub fn new(regions: heapless::Vec) -> 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) -> 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 { + 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 { + 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 + } } diff --git a/kernel/src/paging/kernel.rs b/kernel/src/paging/kernel.rs new file mode 100644 index 0000000..7c656de --- /dev/null +++ b/kernel/src/paging/kernel.rs @@ -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 = + PageDirectoryPointerTable::null(); +static mut KERNEL_PHYSMAP_PAGE_DIRECTORY: PageDirectory = PageDirectory::null(); +static mut KERNEL_PAGE_TABLE: PageTable = 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( + app_ptr: *const u8, + app_size: u64, + stack_size: usize, + allocator: &mut RegionAllocator, +) -> 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::::new( + core::ptr::addr_of!(KERNEL_PAGE_TABLE).cast(), + CacheWriteThrough::Off, + Cache::On, + get_max_addr().into(), + ), + ) + .unwrap(); + } + Ok(result.start) +} diff --git a/uefi/src/lib.rs b/uefi/src/lib.rs index 05c16c3..f30484e 100644 --- a/uefi/src/lib.rs +++ b/uefi/src/lib.rs @@ -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> { + 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 { diff --git a/x86_drivers/src/cpuid.rs b/x86_drivers/src/cpuid.rs new file mode 100644 index 0000000..7df7bb7 --- /dev/null +++ b/x86_drivers/src/cpuid.rs @@ -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) -> 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::(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() + } +} diff --git a/x86_drivers/src/lib.rs b/x86_drivers/src/lib.rs index 214653b..225a4e2 100644 --- a/x86_drivers/src/lib.rs +++ b/x86_drivers/src/lib.rs @@ -28,8 +28,10 @@ )] pub mod uart; +pub mod cpuid; pub mod gdt; pub mod idt; +pub mod paging; pub mod core; diff --git a/x86_drivers/src/paging.rs b/x86_drivers/src/paging.rs new file mode 100644 index 0000000..20d5a2d --- /dev/null +++ b/x86_drivers/src/paging.rs @@ -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(PageDirectoryEntry, PhantomData); // only points to PDPT + +#[repr(transparent)] +#[derive(Clone, Copy)] +#[must_use] +pub struct PdptEntry(PageDirectoryEntry, PhantomData); // points to PD OR is 1GB page + +#[repr(transparent)] +#[derive(Clone, Copy)] +#[must_use] +pub struct PdEntry(PageDirectoryEntry, PhantomData); // points to PT OR is 2MB page + +#[repr(transparent)] +#[derive(Clone, Copy)] +#[must_use] +pub struct PtEntry(PageDirectoryEntry, PhantomData); // always 4KB page + +#[repr(transparent)] +#[derive(Clone, Copy)] +#[must_use] +pub struct Page(PageDirectoryEntry, PhantomData, PhantomData); // 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 Page { + 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 Pml4Entry { + // 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, + 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 PdptEntry { + // 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, + 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 PdEntry { + // 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, + 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( + &mut self, + offset: usize, + page: Pml4Entry, + ) -> 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 PageDirectoryPointerTable { + pub fn register_page( + &mut self, + offset: usize, + page: Page, + ) -> 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, + ) -> 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 PageDirectory { + pub fn register_page( + &mut self, + offset: usize, + page: Page, + ) -> 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, + ) -> 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 PageTable { + pub fn register( + &mut self, + offset: usize, + page: Page, + ) -> 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 { + // This seems it should be 512 * 8 bytes + table: [PageDirectoryEntry; 512], + _0: PhantomData, +} + +#[repr(C, align(4096))] +pub struct PageDirectory { + // This seems it should be 512 * 8 bytes + table: [PageDirectoryEntry; 512], + _0: PhantomData, +} + +#[repr(C, align(4096))] +pub struct PageTable { + // This seems it should be 512 * 8 bytes + table: [PageDirectoryEntry; 512], + _0: PhantomData, +}