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)
|
||||
}
|
||||
Reference in New Issue
Block a user