88 * 12mb kmalloc_low space for drivers that require space
99 * guaranteed below 4gb
1010 */
11- #define LOW_HEAP_START 0x0800000 // Starts at 8mb
12- #define LOW_HEAP_MAX 0x1400000 // Ends at 20mb
11+ static uint32_t LOW_HEAP_START ;
12+ static uint32_t LOW_HEAP_MAX ;
1313
1414static uint64_t heaplen = 0 ;
1515static uint64_t allocated = 0 ;
16- static uint32_t low_mem_cur = LOW_HEAP_START ;
16+ static uint32_t low_mem_cur = 0 ;
1717static spinlock_t allocator_lock = 0 ;
1818
1919volatile struct limine_memmap_request memory_map_request = {
@@ -76,65 +76,100 @@ void init_heap(void) {
7676 uint64_t best_len = 0 ;
7777 uint64_t best_addr = 0 ;
7878 int64_t best_idx = -1 ;
79- bool lowheap_contained = false;
79+
80+ /* Find a 12 MB USABLE window below 4 GB for LOW_HEAP */
81+ const uint64_t LOW_HEAP_SIZE = 1024 * 1024 * 12 ;
82+ const uint64_t FOUR_GB_BOUNDARY = (1ull << 32 );
83+ bool lowheap_found = false;
8084
8185 init_spinlock (& allocator_lock );
8286 dump_limine_memmap ();
8387
84- /* Find the largest usable entry, and validate LOW_HEAP_START/LOW_HEAP_MAX */
88+ /* Pick LOW_HEAP window (any LOW_HEAP_SIZE USABLE below 4 GB) */
8589 for (uint64_t i = 0 ; i < memory_map_request .response -> entry_count ; ++ i ) {
8690 struct limine_memmap_entry * e = memory_map_request .response -> entries [i ];
91+ if (e -> type != LIMINE_MEMMAP_USABLE ) {
92+ continue ;
93+ }
8794 uint64_t base = e -> base ;
8895 uint64_t end = e -> base + e -> length ;
89- uint64_t ovl_lo = (LOW_HEAP_START > base ) ? LOW_HEAP_START : base ;
90- uint64_t ovl_hi = (LOW_HEAP_MAX < end ) ? LOW_HEAP_MAX : end ;
91- if (ovl_lo < ovl_hi ) {
92- if (e -> type == LIMINE_MEMMAP_USABLE ) {
93- if (LOW_HEAP_START >= base && LOW_HEAP_MAX <= end ) {
94- lowheap_contained = true;
95- }
96- } else {
97- dprintf ("heap: ERROR low-heap overlaps non-usable [0x%016lx..0x%016lx] type=%s\n" , base , end , limine_memmap_type_str (e -> type ));
98- preboot_fail ("LOW_HEAP overlaps a non-usable memmap entry" );
96+ if (base >= FOUR_GB_BOUNDARY ) {
97+ continue ;
98+ }
99+ if (end > FOUR_GB_BOUNDARY ) {
100+ end = FOUR_GB_BOUNDARY ;
101+ }
102+ if (end > base && (end - base ) >= LOW_HEAP_SIZE ) {
103+ uint64_t start = (base + 0xFFF ) & ~0xFFFull ; /* 4 KB align */
104+ if ((end - start ) >= LOW_HEAP_SIZE ) {
105+ LOW_HEAP_START = start ;
106+ LOW_HEAP_MAX = start + LOW_HEAP_SIZE ;
107+ lowheap_found = true;
108+ dprintf ("heap: LOW_HEAP selected at [0x%08x..0x%08x] (%lu MB)\n" , LOW_HEAP_START , LOW_HEAP_MAX , LOW_HEAP_SIZE / 1024 / 1024 );
109+ break ;
99110 }
100111 }
101- if (e -> type == LIMINE_MEMMAP_USABLE && e -> length > best_len ) {
112+ }
113+
114+ if (!lowheap_found ) {
115+ char error [256 ];
116+ snprintf (error , 255 , "Unable to find %lu MB USABLE below 4 GB for LOW_HEAP" , LOW_HEAP_SIZE / 1024 / 1024 );
117+ preboot_fail (error );
118+ }
119+
120+ /* Find the largest USABLE entry for primary ta_init() */
121+ for (uint64_t i = 0 ; i < memory_map_request .response -> entry_count ; ++ i ) {
122+ struct limine_memmap_entry * e = memory_map_request .response -> entries [i ];
123+ if (e -> type != LIMINE_MEMMAP_USABLE ) {
124+ continue ;
125+ }
126+ if (e -> length > best_len ) {
102127 best_len = e -> length ;
103128 best_addr = e -> base ;
104129 best_idx = (int64_t )i ;
105130 }
106131 }
107132
108- if (!lowheap_contained ) {
109- dprintf ("heap: ERROR low-heap [0x%016lx..0x%016lx] not fully inside any USABLE entry\n" , (unsigned long )LOW_HEAP_START , (unsigned long )LOW_HEAP_MAX );
110- preboot_fail ("LOW_HEAP region outside usable RAM" );
111- }
112-
113133 if (best_idx < 0 || !best_addr || best_len < 0x800000 ) {
114134 preboot_fail ("No usable RAM found for heap" );
115135 }
116136
117- /* Preserve existing behaviour for the primary heap:
118- carve the low heap bump area out with LOW_HEAP_MAX. */
119- uint64_t heapstart = best_addr + LOW_HEAP_MAX ;
120- heaplen = (best_len > LOW_HEAP_MAX ) ? (best_len - LOW_HEAP_MAX ) : 0 ;
137+ low_mem_cur = LOW_HEAP_START ;
138+
139+ /* Build the primary heap region, excluding LOW_HEAP if it overlaps. */
140+ uint64_t bbase = best_addr ;
141+ uint64_t bend = best_addr + best_len ;
142+
143+ uint64_t left_lo = bbase ;
144+ uint64_t left_hi = (LOW_HEAP_START > bbase ) ? (LOW_HEAP_START < bend ? LOW_HEAP_START : bend ) : bbase ;
145+ uint64_t right_lo = (LOW_HEAP_MAX > bbase ) ? (LOW_HEAP_MAX < bend ? LOW_HEAP_MAX : bend ) : bbase ;
146+ uint64_t right_hi = bend ;
147+
148+ uint64_t left_len = (left_hi > left_lo ) ? (left_hi - left_lo ) : 0 ;
149+ uint64_t right_len = (right_hi > right_lo ) ? (right_hi - right_lo ) : 0 ;
150+
151+ uint64_t heapstart , heaplen_local ;
152+ if (left_len >= right_len ) {
153+ heapstart = left_lo ;
154+ heaplen_local = left_len ;
155+ } else {
156+ heapstart = right_lo ;
157+ heaplen_local = right_len ;
158+ }
121159
122- if (heaplen <= sizeof (ta_header )) {
123- preboot_fail ("Primary heap too small after LOW_HEAP_MAX carve-out " );
160+ if (heaplen_local <= sizeof (ta_header )) {
161+ preboot_fail ("Primary heap too small after LOW_HEAP exclusion " );
124162 }
125163
126- ta_init ((void * )heapstart , heaplen , 8 );
164+ ta_init ((void * )heapstart , heaplen_local , 8 );
127165
128- /* Log the primary region and start accumulating total usable bytes
129- (exclude allocator headers so this reflects payload capacity) */
130- uint64_t total_usable = heaplen > sizeof (ta_header ) ? (heaplen - sizeof (ta_header )) : 0 ;
131- dprintf ("heap: add primary region 0x%016lx..0x%016lx (%lu bytes)\n" , heapstart , (heapstart + heaplen ), heaplen );
166+ uint64_t total_usable = heaplen_local > sizeof (ta_header ) ? (heaplen_local - sizeof (ta_header )) : 0 ;
167+ dprintf ("heap: add primary region 0x%016lx..0x%016lx (%lu bytes)\n" , heapstart , (heapstart + heaplen_local ), heaplen_local );
132168
133- /* Pass 2: roll in every other usable region via ta_add_region(),
134- clipping out the low-heap window [LOW_HEAP_START, LOW_HEAP_END] */
169+ /* Add other USABLE regions, clipping out LOW_HEAP */
135170 for (uint64_t i = 0 ; i < memory_map_request .response -> entry_count ; ++ i ) {
136171 if ((int64_t )i == best_idx ) {
137- continue ; /* skip the one we already used for ta_init() */
172+ continue ;
138173 }
139174
140175 struct limine_memmap_entry * entry = memory_map_request .response -> entries [i ];
@@ -145,7 +180,6 @@ void init_heap(void) {
145180 uint64_t base = entry -> base ;
146181 uint64_t end = entry -> base + entry -> length ;
147182
148- /* Left segment: [base, min(end, LOW_HEAP_START)) */
149183 if (base < LOW_HEAP_START ) {
150184 uint64_t seg_end = end < LOW_HEAP_START ? end : LOW_HEAP_START ;
151185 if (seg_end > base ) {
@@ -159,14 +193,10 @@ void init_heap(void) {
159193 }
160194 }
161195
162- /* Right segment: [max(base, LOW_HEAP_END), end) */
163196 if (end > LOW_HEAP_MAX ) {
164197 uint64_t seg_base = base > LOW_HEAP_MAX ? base : LOW_HEAP_MAX ;
165198 if (end > seg_base ) {
166199 uint64_t seg_len = end - seg_base ;
167- /* If this happens to match the primary region exactly,
168- ta_add_region() will just link it; but we’ve already
169- skipped best_idx so overlap shouldn't occur here. */
170200 if (seg_len > sizeof (ta_header )) {
171201 if (ta_add_region ((void * )seg_base , (size_t )seg_len )) {
172202 dprintf ("heap: add region 0x%016lx..0x%016lx (%lu bytes)\n" , seg_base , end , seg_len );
@@ -177,12 +207,63 @@ void init_heap(void) {
177207 }
178208 }
179209
210+ /* Selectively reclaim Bootloader Reclaimable */
211+ struct reqset rs = request_addresses ();
212+ const size_t BLR_MIN_RECLAIM = (1u << 20 ); /* 1 MB */
213+ size_t remaining_blr = 0 ;
214+
215+ for (uint64_t i = 0 ; i < memory_map_request .response -> entry_count ; ++ i ) {
216+ struct limine_memmap_entry * e = memory_map_request .response -> entries [i ];
217+ if (e -> type != LIMINE_MEMMAP_BOOTLOADER_RECLAIMABLE ) {
218+ continue ;
219+ }
220+
221+ uint64_t rbase = e -> base ;
222+ uint64_t rend = e -> base + e -> length ;
223+
224+ if (e -> length < BLR_MIN_RECLAIM ) {
225+ dprintf ("heap: keep BLR 0x%016lx..0x%016lx (small: %lu bytes)\n" , rbase , rend , e -> length );
226+ remaining_blr += e -> length ;
227+ continue ;
228+ }
229+
230+ uintptr_t hit = 0 ;
231+ for (size_t j = 0 ; j < rs .count ; ++ j ) {
232+ uintptr_t p = rs .ptrs [j ];
233+ if (p && p >= rbase && p < rend ) {
234+ hit = p ;
235+ break ;
236+ }
237+ }
238+
239+ if (hit ) {
240+ dprintf ("heap: keep BLR 0x%016lx..0x%016lx (contains 0x%016lx)\n" , rbase , rend , hit );
241+ remaining_blr += e -> length ;
242+ continue ;
243+ }
244+
245+ if (e -> length > sizeof (ta_header )) {
246+ if (ta_add_region ((void * )rbase , (size_t )e -> length )) {
247+ dprintf ("heap: reclaim BLR 0x%016lx..0x%016lx (%lu bytes)\n" , rbase , rend , e -> length );
248+ total_usable += (e -> length - sizeof (ta_header ));
249+ } else {
250+ dprintf ("heap: WARNING failed to add BLR 0x%016lx..0x%016lx\n" , rbase , rend );
251+ remaining_blr += e -> length ;
252+ }
253+ } else {
254+ dprintf ("heap: skip tiny BLR 0x%016lx..0x%016lx (%lu bytes)\n" , rbase , rend , e -> length );
255+ remaining_blr += e -> length ;
256+ }
257+ }
258+
259+ /* Now we're done. We've clawed back every last byte we can */
180260 heaplen = total_usable ;
181- dprintf ("heap: total usable RAM for allocator: %lu bytes (%lu MB)\n" , total_usable , total_usable / 1024 / 1024 );
261+ dprintf ("heap: total usable RAM for allocator: %lu bytes (%lu MB); remaining bootloader reserved: %lu MB \n" , total_usable , total_usable / 1024 / 1024 , remaining_blr / 1024 / 1024 );
182262
183263 dprintf_buffer_init (0 );
184264}
185265
266+
186267void * kmalloc (uint64_t size ) {
187268 uint64_t flags ;
188269 lock_spinlock_irq (& allocator_lock , & flags );
0 commit comments