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