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

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>,
}