11//! Heap allocator module
22//!
33//! This module implements the heap allocator for the kernel.
4- //! It uses the `linked_list_allocator ` crate to manage heap memory.
4+ //! It uses the `talc ` crate to manage heap memory with dynamic growth support .
55
66use crate :: config:: KERNEL_DEFAULT_HEAP_SIZE ;
7- use talc:: { ClaimOnOom , Span , Talc , Talck } ;
7+ use talc:: { Span , Talc , Talck } ;
88use x86_64:: {
99 structures:: paging:: {
1010 mapper:: MapToError , FrameAllocator , Mapper , Page , PageTableFlags , Size4KiB ,
@@ -15,31 +15,77 @@ use x86_64::{
1515/// The starting virtual address of the heap
1616pub const HEAP_START : usize = 0x_4444_4444_0000 ;
1717
18+ /// OOM handler for the kernel heap
19+ pub struct KernelOomHandler ;
20+
21+ impl talc:: OomHandler for KernelOomHandler {
22+ fn handle_oom ( talc : & mut Talc < Self > , _layout : core:: alloc:: Layout ) -> Result < ( ) , ( ) > {
23+ // Expand by 1MB at least
24+ let expand_size = 1024 * 1024 ;
25+
26+ let mut ms_lock = crate :: memory:: vmm:: KERNEL_MEMORY_SET . lock ( ) ;
27+ let memory_set = ms_lock. as_mut ( ) . ok_or ( ( ) ) ?;
28+
29+ // Find heap area
30+ let ( old_end, new_end) = {
31+ let heap_area = memory_set. areas . iter_mut ( ) . find ( |a| a. name == "heap" ) . ok_or ( ( ) ) ?;
32+ let old_end = heap_area. end ;
33+ let new_end = old_end + expand_size;
34+ heap_area. end = new_end;
35+ ( old_end, new_end)
36+ } ;
37+
38+ // Map the new pages MANUALLY to avoid deadlock via #PF
39+ let page_range = {
40+ let start_page = Page :: containing_address ( old_end) ;
41+ let end_page = Page :: containing_address ( new_end - 1u64 ) ;
42+ Page :: range_inclusive ( start_page, end_page)
43+ } ;
44+
45+ let memory_map_response = crate :: MEMORY_MAP_REQUEST
46+ . get_response ( )
47+ . expect ( "Failed to get memory map response" ) ;
48+ let mut frame_allocator = unsafe { crate :: memory:: paging:: init_frame_allocator ( memory_map_response) } ;
49+
50+ for page in page_range {
51+ let frame = frame_allocator. allocate_frame ( ) . ok_or ( ( ) ) ?;
52+ let flags = PageTableFlags :: PRESENT | PageTableFlags :: WRITABLE | PageTableFlags :: NO_EXECUTE ;
53+ unsafe {
54+ memory_set. page_table . map_to ( page, frame, flags, & mut frame_allocator) . map_err ( |_| ( ) ) ?. flush ( ) ;
55+ }
56+ }
57+
58+ drop ( ms_lock) ;
59+
60+ unsafe {
61+ talc. claim ( Span :: new ( old_end. as_mut_ptr ( ) , new_end. as_mut_ptr ( ) ) ) . map_err ( |_| ( ) ) ?;
62+ }
63+
64+ Ok ( ( ) )
65+ }
66+ }
67+
1868#[ global_allocator]
19- pub static ALLOCATOR : Talck < spin:: Mutex < ( ) > , ClaimOnOom > = Talc :: new ( unsafe {
20- // if we're in a hosted environment, the Rust runtime may allocate before
21- // main() is called, so we need to initialize the arena automatically
22- ClaimOnOom :: new ( Span :: empty ( ) )
23- } )
24- . lock ( ) ;
69+ pub static ALLOCATOR : Talck < spin:: Mutex < ( ) > , KernelOomHandler > = Talc :: new ( KernelOomHandler ) . lock ( ) ;
2570
2671/// Initialize the heap
2772///
28- /// This function maps the heap memory region and initializes the global allocator .
73+ /// This function initializes the global allocator with a small pre-mapped area .
2974///
3075/// # Arguments
3176/// * `mapper` - The page table mapper
3277/// * `frame_allocator` - The frame allocator
3378///
3479/// # Returns
3580/// * `Ok(())` on success
36- /// * `Err(MapToError)` on failure
3781pub fn init_heap (
3882 mapper : & mut impl Mapper < Size4KiB > ,
3983 frame_allocator : & mut impl FrameAllocator < Size4KiB > ,
4084) -> Result < ( ) , MapToError < Size4KiB > > {
4185 let heap_start = VirtAddr :: new ( HEAP_START as u64 ) ;
42- let heap_end = heap_start + KERNEL_DEFAULT_HEAP_SIZE ;
86+ // Map initial 64KB for boot-strapping VMM
87+ let initial_size = 64 * 1024 ;
88+ let heap_end = heap_start + initial_size;
4389
4490 let page_range = {
4591 let heap_start_page = Page :: containing_address ( heap_start) ;
@@ -51,7 +97,7 @@ pub fn init_heap(
5197 let frame = frame_allocator
5298 . allocate_frame ( )
5399 . ok_or ( MapToError :: FrameAllocationFailed ) ?;
54- let flags = PageTableFlags :: PRESENT | PageTableFlags :: WRITABLE ;
100+ let flags = PageTableFlags :: PRESENT | PageTableFlags :: WRITABLE | PageTableFlags :: NO_EXECUTE ;
55101 unsafe {
56102 mapper. map_to ( page, frame, flags, frame_allocator) ?. flush ( ) ;
57103 }
0 commit comments