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:
28
kernel/link.x
Normal file
28
kernel/link.x
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -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 {}
|
||||
|
||||
@@ -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
141
kernel/src/paging/kernel.rs
Normal 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)
|
||||
}
|
||||
@@ -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
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