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:
90
x86_drivers/src/cpuid.rs
Normal file
90
x86_drivers/src/cpuid.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
@@ -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
448
x86_drivers/src/paging.rs
Normal 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>,
|
||||
}
|
||||
Reference in New Issue
Block a user