From 8a3e9e3afcb61536a99cab6eeff3852fb0b95fee Mon Sep 17 00:00:00 2001 From: zxq5 Date: Fri, 28 Feb 2025 04:18:35 +0000 Subject: [PATCH] - implemented a custom allocator (fixed size block) with a fallback (linked list allocator) for larger block sizes - apic code still not working (commented out, check lib.rs) --- kernel/src/arch/x86_64/apic/mod.rs | 39 ++-- kernel/src/arch/x86_64/interrupts.rs | 8 +- kernel/src/lib.rs | 4 +- kernel/src/main.rs | 7 + libk/src/drivers/kalloc/allocator.rs | 24 ++- libk/src/drivers/kalloc/foundry_kalloc.rs | 231 ++++++++++++++++++++++ libk/src/drivers/kalloc/mod.rs | 1 + 7 files changed, 280 insertions(+), 34 deletions(-) create mode 100644 libk/src/drivers/kalloc/foundry_kalloc.rs diff --git a/kernel/src/arch/x86_64/apic/mod.rs b/kernel/src/arch/x86_64/apic/mod.rs index 9199a09..0c57961 100644 --- a/kernel/src/arch/x86_64/apic/mod.rs +++ b/kernel/src/arch/x86_64/apic/mod.rs @@ -1,6 +1,6 @@ use core::arch::x86_64::__cpuid; -use libk::drivers::memory::FoundryOSFrameAllocator; +use libk::drivers::memory::{FoundryOSFrameAllocator, FRAME_ALLOCATOR, OFFSET_PAGE_TABLE}; use spin::Lazy; use x86_64::{ PhysAddr, VirtAddr, @@ -68,37 +68,28 @@ pub fn check_apic() -> bool { #[inline(always)] unsafe fn phys_to_virt(phys: PhysAddr) -> VirtAddr { let phys = phys.as_u64(); - match phys.checked_add(*PHYSICAL_MEMORY_OFFSET) { - Some(virt) => { - serial_print!("map worked!"); - VirtAddr::new(virt) - } - None => { - serial_print!("THIS IS A PROBLEM"); - panic!("overflow") - } - } + phys.checked_add(*PHYSICAL_MEMORY_OFFSET).map_or_else(|| panic!(" overflow"), VirtAddr::new) } -pub fn enable_apic( - mapper: &mut impl Mapper, - frame_allocator: &mut FoundryOSFrameAllocator, -) { +pub fn enable_apic() { + let mut mapper = OFFSET_PAGE_TABLE.get().unwrap().lock(); + let mut frame_allocator = FRAME_ALLOCATOR.get().unwrap().lock(); + let apic_phys_addr = get_apic_base(); set_apic_base_enable(apic_phys_addr); // map virt address of apic let apic_virt = unsafe { phys_to_virt(apic_phys_addr) }; - // let page: Page = Page::containing_address(apic_virt); - // let frame: PhysFrame = PhysFrame::containing_address(apic_phys_addr); - // let flags: PageTableFlags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; + let page: Page = Page::containing_address(apic_virt); + let frame: PhysFrame = PhysFrame::containing_address(apic_phys_addr); + let flags: PageTableFlags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; - // unsafe { - // match mapper.map_to(page, frame, flags, frame_allocator) { - // Ok(_) => {} - // Err(why) => panic!("failed to map apic: {:?}", why), - // } - // } + unsafe { + match mapper.map_to(page, frame, flags, &mut *frame_allocator) { + Ok(_) => {} + Err(why) => panic!("failed to map apic: {:?}", why), + } + } // // FIXME: this causes a page fault // // TODO: map to virtual memor diff --git a/kernel/src/arch/x86_64/interrupts.rs b/kernel/src/arch/x86_64/interrupts.rs index 31c4190..1320b21 100644 --- a/kernel/src/arch/x86_64/interrupts.rs +++ b/kernel/src/arch/x86_64/interrupts.rs @@ -127,10 +127,10 @@ extern "x86-interrupt" fn page_fault_handler( _stack_frame: InterruptStackFrame, _error_code: PageFaultErrorCode, ) { - // serial_println!("Exception: Page Fault"); - // serial_println!("Accessed Address: {:?}", Cr2::read()); - // serial_println!("Error Code: {:?}", error_code); - // serial_println!("{:#?}", _stack_frame); + serial_println!("Exception: Page Fault"); + serial_println!("Accessed Address: {:?}", Cr2::read()); + serial_println!("Error Code: {:?}", _error_code); + serial_println!("{:#?}", _stack_frame); if let Some(frame_allocator) = FRAME_ALLOCATOR.get() { let mut f = frame_allocator.lock(); diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 3a08569..ed53657 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -99,9 +99,9 @@ pub fn boot() -> Result<(), &'static str> { // print_log!(" Disabling PICs... "); // interrupts::disable_pic(); // println_log!("[Success]"); - // + // print_log!(" Initialising APIC"); - // enable_apic(&mut l4_table, &mut frame_allocator); + // enable_apic(); // println_log!("[Success]"); print_log!(" Enabling Interrupts... "); diff --git a/kernel/src/main.rs b/kernel/src/main.rs index d5839dc..fb9ba94 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -28,6 +28,13 @@ extern "C" fn kmain() -> ! { println!("Dimensions: {}x{} (px)", dimensions2.0, dimensions2.1); println!("Dimensions: {}x{} (chars)", dimensions.0, dimensions.1); + // println!("TESTING :: Allocation"); + // let somevec = vec![0; 1_000_000]; + // println!("{:?}", somevec); + // println!("{}", somevec.len()); + // println!("PASSED!"); + + let mut executor = Executor::new(); executor.spawn(Task::new(shell())); executor.run(); diff --git a/libk/src/drivers/kalloc/allocator.rs b/libk/src/drivers/kalloc/allocator.rs index 086a8c0..187c176 100644 --- a/libk/src/drivers/kalloc/allocator.rs +++ b/libk/src/drivers/kalloc/allocator.rs @@ -1,15 +1,15 @@ use linked_list_allocator::LockedHeap; +use spin::{Mutex, MutexGuard}; use x86_64::structures::paging::{ mapper::MapToError, Size4KiB, }; +use crate::drivers::kalloc::foundry_kalloc::FoundryAllocator; /// We are currently using a linked list heap allocator which uses our underlying page allocator. -pub type FoundryAllocator = LockedHeap; - #[global_allocator] /// This is now Rust's global allocator, so we can use stuff requiring heap allocations. -static ALLOCATOR: FoundryAllocator = FoundryAllocator::empty(); +static ALLOCATOR: Locked = Locked::new(FoundryAllocator::new()); pub const HEAP_START: usize = 0x4444_4444_0000; pub const HEAP_SIZE: usize = 1024 * 1024 * 1024; @@ -19,8 +19,24 @@ pub fn init_heap() -> Result<(), MapToError> { // code to allocate frames is now done in the page fault interrupt handler! unsafe { - ALLOCATOR.lock().init(HEAP_START as *mut u8, HEAP_SIZE); + ALLOCATOR.lock().init(HEAP_START, HEAP_SIZE); } Ok(()) } + +pub struct Locked { + inner: Mutex, +} + +impl Locked { + pub const fn new(inner: T) -> Self { + Locked { + inner: Mutex::new(inner) + } + } + + pub fn lock(&self) -> MutexGuard { + self.inner.lock() + } +} \ No newline at end of file diff --git a/libk/src/drivers/kalloc/foundry_kalloc.rs b/libk/src/drivers/kalloc/foundry_kalloc.rs new file mode 100644 index 0000000..4e24e2d --- /dev/null +++ b/libk/src/drivers/kalloc/foundry_kalloc.rs @@ -0,0 +1,231 @@ +use alloc::collections::LinkedList; +use core::alloc::{GlobalAlloc, Layout}; +use core::{mem, ptr}; +use core::ptr::NonNull; +use crate::drivers::kalloc::allocator::Locked; +const BLOCK_SIZES: &[usize] = &[8, 16, 32, 64, 128, 256, 512, 1024, 2048]; + +struct ListNode { + next: Option<&'static mut ListNode>, +} + +pub struct FoundryAllocator { + list_heads: [Option<&'static mut ListNode>; BLOCK_SIZES.len()], + fallback: Locked, +} + +impl Default for FoundryAllocator { + fn default() -> Self { + Self::new() + } +} + +impl FoundryAllocator { + pub const fn new() -> Self { + const EMPTY: Option<&'static mut ListNode> = None; + Self { + list_heads: [EMPTY; BLOCK_SIZES.len()], + fallback: Locked::new(FoundryFallbackAllocator::new()), + } + } + pub unsafe fn init(&mut self, heap_start: usize, heap_size: usize) { unsafe { + self.fallback.lock().init(heap_start, heap_size); + }} + + unsafe fn fallback_alloc(&mut self, layout: Layout) -> *mut u8 { unsafe { + self.fallback.alloc(layout) + }} + + fn block_size(&self, layout: &Layout) -> Option { + let required_block_size = layout.size().max(layout.align()); + BLOCK_SIZES.iter().position(|&s| s >= required_block_size) + } +} + +unsafe impl GlobalAlloc for Locked { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let mut allocator = self.lock(); + + match allocator.block_size(&layout) { + Some(index) => { + match allocator.list_heads[index].take() { + Some(node) => { + allocator.list_heads[index] = node.next.take(); + node as *mut ListNode as *mut u8 + } + None => { + // no block exists in list => allocate new block + let block_size = BLOCK_SIZES[index]; + // only works if all block sizes are a power of 2 + let block_align = block_size; + let layout = Layout::from_size_align(block_size, block_align) + .unwrap(); + unsafe { allocator.fallback_alloc(layout) } + } + } + } + None => unsafe { allocator.fallback_alloc(layout) }, + } + } + + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + let mut allocator = self.lock(); + match allocator.block_size(&layout) { + Some(idx) => { + let new_node = ListNode { + next: allocator.list_heads[idx].take(), + }; + + let new_ptr = ptr as *mut ListNode; + unsafe { new_ptr.write(new_node) }; + allocator.list_heads[idx] = Some( unsafe { &mut *new_ptr }); + } + None => {; + unsafe { allocator.fallback.dealloc(ptr, layout) }; + } + } + } +} + +struct FallbackListNode { + size: usize, + next: Option<&'static mut FallbackListNode>, +} + +impl FallbackListNode { + const fn new(size: usize) -> Self { + Self { + size, + next: None, + } + } + + fn start_addr(&self) -> usize { + self as *const Self as usize + } + + fn end_addr(&self) -> usize { + self.start_addr() + self.size + } +} + +pub struct FoundryFallbackAllocator { + head: FallbackListNode, +} + +impl FoundryFallbackAllocator { + pub const fn new() -> Self { + Self { + head: FallbackListNode::new(0), + } + } + + pub unsafe fn init(&mut self, heap_start: usize, heap_size: usize) { + unsafe { self.add_region(heap_start, heap_size) }; + } + + unsafe fn add_region(&mut self, addr: usize, size: usize) { unsafe { + let mut node = FallbackListNode::new(size); + node.next = self.head.next.take(); + let node_ptr = addr as *mut FallbackListNode; + node_ptr.write(node); + self.head.next = Some(&mut *node_ptr); + }} + + fn find_region(&mut self, size: usize, align: usize) -> Option<(&'static mut FallbackListNode, usize)> { + let mut current = &mut self.head; + // look for a large enough memory region in linked list + while let Some(ref mut region) = current.next { + if let Ok(alloc_start) = Self::alloc_from_region(®ion, size, align) { + // region suitable for allocation -> remove node from list + let next = region.next.take(); + let ret = Some((current.next.take().unwrap(), alloc_start)); + current.next = next; + return ret; + } else { + // region not suitable -> continue with next region + current = current.next.as_mut().unwrap(); + } + } + None + } + const fn align_up(addr: usize, align: usize) -> usize { + (addr + align - 1) & !(align - 1) + } + + fn size_align(layout: Layout) -> (usize, usize) { + let layout = layout + .align_to(align_of::()) + .expect("adjusting alignment failed") + .pad_to_align(); + let size = layout.size().max(size_of::()); + (size, layout.align()) + } + + fn alloc_from_region(region: &FallbackListNode, size: usize, align: usize) -> Result + { + let alloc_start = Self::align_up(region.start_addr(), align); + let alloc_end = alloc_start.checked_add(size).ok_or(())?; + + if alloc_end > region.end_addr() { + return Err(()); + } + + let excess_size = region.end_addr() - alloc_end; + if excess_size > 0 && excess_size < size_of::() { + return Err(()); + } + + Ok(alloc_start) + } +} + +unsafe impl GlobalAlloc for Locked { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let mut allocator = self.lock(); + // perform layout adjustments + let (size, align) = FoundryFallbackAllocator::size_align(layout); + + if let Some((region, alloc_start)) = allocator.find_region(size, align) { + let alloc_end = alloc_start.checked_add(size).expect("overflow"); + let excess_size = region.end_addr() - alloc_end; + if excess_size > 0 { + unsafe { allocator.add_region(alloc_end, excess_size) }; + } + alloc_start as *mut u8 + } else { + ptr::null_mut() + } + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + let mut allocator = self.lock(); + + // perform layout adjustments + let (size, _) = FoundryFallbackAllocator::size_align(layout); + + unsafe { + allocator.add_region(ptr as usize, size) + } + } +} + + + + + + + + + + + + + + + + + + + diff --git a/libk/src/drivers/kalloc/mod.rs b/libk/src/drivers/kalloc/mod.rs index 98fe5c3..184e946 100644 --- a/libk/src/drivers/kalloc/mod.rs +++ b/libk/src/drivers/kalloc/mod.rs @@ -1 +1,2 @@ pub mod allocator; +mod foundry_kalloc;