Skip to content

Commit 7cb20fe

Browse files
reclaim bootloader reclaimable space, and relocate lowheap if needed
1 parent 999e0f0 commit 7cb20fe

File tree

3 files changed

+170
-45
lines changed

3 files changed

+170
-45
lines changed

include/kernel.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,15 @@
9090
kprintf("Assertion failure at %s:%d: %s\n", __FILE__, __LINE__, message); \
9191
__asm__ volatile("int3"); }
9292

93+
struct reqset {
94+
const uintptr_t *ptrs;
95+
size_t count;
96+
};
97+
9398
void network_up();
9499
void network_down();
95100
void validate_limine_page_tables_and_gdt(void);
101+
struct reqset request_addresses(void);
96102

97103
#ifdef PROFILE_KERNEL
98104
typedef struct profile_entry {

src/gdt.c

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@
55
#define NX (1ULL << 63)
66
#define PS (1ULL << 7)
77

8+
static uint64_t limine_gdtr;
9+
static uint64_t limine_cr3;
10+
11+
struct {
12+
uint16_t limit;
13+
uint64_t base;
14+
} __attribute__((packed)) gdtr;
15+
816
volatile struct limine_stack_size_request stack_size_request = {
917
.id = LIMINE_STACK_SIZE_REQUEST,
1018
.revision = 0,
@@ -21,10 +29,36 @@ volatile struct limine_kernel_address_request address_request = {
2129
.revision = 0,
2230
};
2331

24-
struct {
25-
uint16_t limit;
26-
uint64_t base;
27-
} __attribute__((packed)) gdtr;
32+
extern volatile struct limine_smp_request smp_request;
33+
extern volatile struct limine_rsdp_request rsdp_request;
34+
extern volatile struct limine_module_request module_request;
35+
extern volatile struct limine_memmap_request memory_map_request;
36+
extern volatile struct limine_framebuffer_request framebuffer_request;
37+
extern volatile struct limine_kernel_file_request rr_kfile_req;
38+
39+
struct reqset request_addresses(void) {
40+
static struct {
41+
uintptr_t arr[12];
42+
} buf; /* backing storage, static so it survives return */
43+
44+
buf.arr[0] = (uintptr_t)limine_gdtr;
45+
buf.arr[1] = (uintptr_t)limine_cr3;
46+
buf.arr[2] = (uintptr_t)smp_request.response - hhdm_request.response->offset;
47+
buf.arr[3] = (uintptr_t)rsdp_request.response - hhdm_request.response->offset;
48+
buf.arr[4] = (uintptr_t)module_request.response - hhdm_request.response->offset;
49+
buf.arr[5] = (uintptr_t)memory_map_request.response - hhdm_request.response->offset;
50+
buf.arr[6] = (uintptr_t)framebuffer_request.response - hhdm_request.response->offset;
51+
buf.arr[7] = (uintptr_t)rr_kfile_req.response - hhdm_request.response->offset;
52+
buf.arr[8] = (uintptr_t)address_request.response - hhdm_request.response->offset;
53+
buf.arr[9] = (uintptr_t)hhdm_request.response - hhdm_request.response->offset;
54+
buf.arr[10] = (uintptr_t)stack_size_request.response - hhdm_request.response->offset;
55+
56+
return (struct reqset){
57+
buf.arr,
58+
sizeof(buf.arr) / sizeof(buf.arr[0])
59+
};
60+
}
61+
2862

2963
static void dump_mapping(uint64_t virt, uint64_t phys, uint64_t flags, int level) {
3064
const char *sizes[] = { "4K", "2M", "1G" };
@@ -65,12 +99,16 @@ void validate_limine_page_tables_and_gdt(void) {
6599
__asm__ volatile("sgdt %0" : "=m"(gdtr));
66100
uint64_t *gdt = (uint64_t *)gdtr.base;
67101
int count = (gdtr.limit + 1) / 8;
102+
limine_gdtr = gdtr.base;
68103

104+
dprintf("gdtr.base=%p gdtr.limit=%u count=%u\n", (void*)gdtr.base, gdtr.limit, count);
69105
for (int i = 0; i < count; i++) {
70106
dprintf("GDT[%d] = %016lx\n", i, gdt[i]);
71107
}
72108

73109
uint64_t cr3;
74110
__asm__ volatile("mov %%cr3, %0" : "=r"(cr3));
111+
dprintf("cr3=%p\n", (void*)cr3);
112+
limine_cr3 = cr3;
75113
walk_page_tables((uint64_t *)cr3, 0, 0);
76114
}

src/kmalloc.c

Lines changed: 122 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
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

1414
static uint64_t heaplen = 0;
1515
static uint64_t allocated = 0;
16-
static uint32_t low_mem_cur = LOW_HEAP_START;
16+
static uint32_t low_mem_cur = 0;
1717
static spinlock_t allocator_lock = 0;
1818

1919
volatile 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+
186267
void* kmalloc(uint64_t size) {
187268
uint64_t flags;
188269
lock_spinlock_irq(&allocator_lock, &flags);

0 commit comments

Comments
 (0)