From: Tulio A M Mendes Date: Sun, 19 Apr 2026 01:25:23 +0000 (-0300) Subject: Fix initrd LZ4 OOM: use page-level allocation instead of kmalloc X-Git-Url: https://projects.tadryanom.me/?a=commitdiff_plain;h=267ba8074e567232bfff3f748dd7abda5407a8c9;p=AdrOS.git Fix initrd LZ4 OOM: use page-level allocation instead of kmalloc initrd_init used kmalloc() to allocate the decompression buffer for LZ4-compressed initrd images. The kernel buddy heap is only 8 MB, so a 4 MB allocation (the default content size) can easily fail due to fragmentation from earlier allocations, producing: [HEAP] OOM: kmalloc failed. [INITRD] OOM decompressing LZ4 (4194304 bytes) This was especially likely with custom initrd images compressed with lz4 -1 or -9, which can declare larger content sizes. Replace kmalloc(orig_sz) with initrd_alloc_pages(), which allocates individual physical pages via pmm_alloc_page() and maps them into a dedicated virtual region (0xD0800000, above the 8MB heap). This bypasses the buddy allocator entirely, using the physical memory manager directly. On OOM, already-mapped pages are rolled back. Both LZ4 Frame and legacy LZ4B paths are updated. --- diff --git a/src/drivers/initrd.c b/src/drivers/initrd.c index 64041a7e..b0609562 100644 --- a/src/drivers/initrd.c +++ b/src/drivers/initrd.c @@ -13,6 +13,8 @@ #include "console.h" #include "errno.h" #include "lz4.h" +#include "pmm.h" +#include "vmm.h" #define TAR_BLOCK 512 @@ -46,8 +48,40 @@ typedef struct { int next_sibling; } initrd_entry_t; +/* Virtual address base for initrd decompression buffer. + * Placed above the kernel heap (0xD0000000 + 8MB = 0xD0800000) + * to avoid collisions. Pages are allocated via pmm_alloc_page() + * and mapped one-by-one, bypassing the buddy heap allocator. */ +#define INITRD_DECOMP_BASE 0xD0800000U + static uint32_t initrd_location_base = 0; +/* Allocate a contiguous virtual region of `size` bytes backed by + * individual physical pages. Returns the virtual address, or NULL + * on OOM. Pages are zeroed. */ +static uint8_t* initrd_alloc_pages(uint32_t size) { + uint32_t npages = (size + PAGE_SIZE - 1) / PAGE_SIZE; + uintptr_t va = INITRD_DECOMP_BASE; + for (uint32_t i = 0; i < npages; i++) { + void* phys = pmm_alloc_page(); + if (!phys) { + /* Rollback: unmap + free pages already mapped */ + for (uint32_t j = 0; j < i; j++) { + uintptr_t page_va = INITRD_DECOMP_BASE + (uintptr_t)j * PAGE_SIZE; + uintptr_t p = vmm_virt_to_phys(page_va); + vmm_unmap_page(page_va); + if (p) pmm_free_blocks((void*)p, 1); + } + return NULL; + } + memset(phys, 0, PAGE_SIZE); + vmm_map_page((uint64_t)(uintptr_t)phys, (uint64_t)va, + VMM_FLAG_PRESENT | VMM_FLAG_RW); + va += PAGE_SIZE; + } + return (uint8_t*)INITRD_DECOMP_BASE; +} + static initrd_entry_t* entries = NULL; static fs_node_t* nodes = NULL; static int entry_count = 0; @@ -302,7 +336,7 @@ fs_node_t* initrd_init(uint32_t location, uint32_t size) { orig_sz = 4U * 1024U * 1024U; } - decomp_buf = (uint8_t*)kmalloc(orig_sz); + decomp_buf = initrd_alloc_pages(orig_sz); if (!decomp_buf) { kprintf("[INITRD] OOM decompressing LZ4 (%u bytes)\n", orig_sz); return NULL; @@ -311,7 +345,6 @@ fs_node_t* initrd_init(uint32_t location, uint32_t size) { int ret = lz4_decompress_frame(raw, size, decomp_buf, orig_sz); if (ret < 0) { kprintf("[INITRD] LZ4 Frame decompress failed (ret=%d)\n", ret); - kfree(decomp_buf); return NULL; } @@ -324,7 +357,7 @@ fs_node_t* initrd_init(uint32_t location, uint32_t size) { uint32_t comp_sz = (uint32_t)raw[8] | ((uint32_t)raw[9] << 8) | ((uint32_t)raw[10] << 16) | ((uint32_t)raw[11] << 24); - decomp_buf = (uint8_t*)kmalloc(orig_sz); + decomp_buf = initrd_alloc_pages(orig_sz); if (!decomp_buf) { kprintf("[INITRD] OOM decompressing LZ4 (%u bytes)\n", orig_sz); return NULL; @@ -335,7 +368,6 @@ fs_node_t* initrd_init(uint32_t location, uint32_t size) { if (ret < 0 || (uint32_t)ret != orig_sz) { kprintf("[INITRD] LZ4 decompress failed (ret=%d, expected=%u)\n", ret, orig_sz); - kfree(decomp_buf); return NULL; } @@ -349,7 +381,7 @@ fs_node_t* initrd_init(uint32_t location, uint32_t size) { entry_count = 0; int root = entry_alloc(); - if (root < 0) { kfree(decomp_buf); return NULL; } + if (root < 0) return NULL; strcpy(entries[root].name, ""); entries[root].flags = FS_DIRECTORY; entries[root].data_offset = 0;