]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
fix: PMM total_memory overflow — MMAP reserved regions near 4GB inflated highest_addr
authorTulio A M Mendes <[email protected]>
Mon, 16 Feb 2026 22:08:36 +0000 (19:08 -0300)
committerTulio A M Mendes <[email protected]>
Mon, 16 Feb 2026 22:08:36 +0000 (19:08 -0300)
Root cause: Multiboot2 MMAP includes a BIOS reserved region at
0xFFFC0000-0x100000000. The end address (0x100000000) overflows
uint32_t when stored in a uint64_t local variable, and (unsigned)
truncation yields 0 — hence '[PMM] total_memory bytes: 0x0'.

Fixes:
- Use uint32_t locals (32-bit x86 caps RAM at 512 MB anyway)
- Clamp MMAP end addresses to 0xFFFFFFFF before comparison
- Only track highest_avail from AVAILABLE regions, not reserved
- Use 'if' instead of 'else if' so both BASIC_MEMINFO and MMAP
  are processed in the same pass
- Print total_memory and freed_frames in decimal with MB suffix

Before: [PMM] total_memory bytes: 0x0
After:  [PMM] total_memory: 134086656 bytes (127 MB)

83/83 smoke tests pass, cppcheck clean.

src/arch/x86/pmm_boot.c

index 050d1d064c806bd29e971d522f837f3d72343d42..7b7456a6e25ba4bd7bfe29f01c5e42f0ca934498 100644 (file)
@@ -6,14 +6,20 @@
 
 #include <stdint.h>
 
-static uint64_t align_up_local(uint64_t value, uint64_t align) {
+/* 32-bit x86: we cap usable RAM at MAX_RAM_SIZE (defined in pmm.c). */
+#define PMM_MAX_RAM  (512U * 1024U * 1024U)
+
+static uint32_t align_up32(uint32_t value, uint32_t align) {
     return (value + align - 1) & ~(align - 1);
 }
 
-static uint64_t align_down_local(uint64_t value, uint64_t align) {
+static uint32_t align_down32(uint32_t value, uint32_t align) {
     return value & ~(align - 1);
 }
 
+#define MB2_TAG_NEXT(t) \
+    ((struct multiboot_tag *)((uint8_t *)(t) + (((t)->size + 7U) & ~7U)))
+
 void pmm_arch_init(void* boot_info) {
     if (!boot_info) {
         kprintf("[PMM] Error: boot_info is NULL!\n");
@@ -21,125 +27,129 @@ void pmm_arch_init(void* boot_info) {
     }
 
     struct multiboot_tag *tag;
-    (void)*(uint32_t *)boot_info;
-    uint64_t highest_addr = 0;
-    uint64_t total_memory = 0;
-    int saw_mmap = 0;
-    uint64_t freed_frames = 0;
+    uint32_t total_mem = 0;
+    uint32_t highest_avail = 0;
+    int      saw_mmap = 0;
+    uint32_t freed_frames = 0;
 
     kprintf("[PMM] Parsing Multiboot2 info...\n");
 
-    // First pass: determine total memory size
+    /* --- Pass 1: determine total usable memory size --- */
     for (tag = (struct multiboot_tag *)((uint8_t *)boot_info + 8);
-       tag->type != MULTIBOOT_TAG_TYPE_END;
-       tag = (struct multiboot_tag *)((uint8_t *)tag + ((tag->size + 7) & ~7)))
+         tag->type != MULTIBOOT_TAG_TYPE_END;
+         tag = MB2_TAG_NEXT(tag))
     {
         if (tag->type == MULTIBOOT_TAG_TYPE_BASIC_MEMINFO) {
-             struct multiboot_tag_basic_meminfo *meminfo = (struct multiboot_tag_basic_meminfo *)tag;
-             uint64_t mem_kb = meminfo->mem_upper;
-             total_memory = (mem_kb * 1024) + (1024*1024);
+            struct multiboot_tag_basic_meminfo *mi =
+                (struct multiboot_tag_basic_meminfo *)tag;
+            total_mem = (mi->mem_upper * 1024U) + (1024U * 1024U);
         }
-        else if (tag->type == MULTIBOOT_TAG_TYPE_MMAP) {
+        if (tag->type == MULTIBOOT_TAG_TYPE_MMAP) {
             saw_mmap = 1;
-            struct multiboot_tag_mmap *mmap = (struct multiboot_tag_mmap *)tag;
-            struct multiboot_mmap_entry *entry;
-
-            for (entry = mmap->entries;
-                 (uint8_t *)entry < (uint8_t *)mmap + mmap->size;
-                 entry = (struct multiboot_mmap_entry *)((uint32_t)entry + mmap->entry_size))
+            struct multiboot_tag_mmap *mmap =
+                (struct multiboot_tag_mmap *)tag;
+            struct multiboot_mmap_entry *e;
+            for (e = mmap->entries;
+                 (uint8_t *)e < (uint8_t *)mmap + mmap->size;
+                 e = (struct multiboot_mmap_entry *)
+                     ((uintptr_t)e + mmap->entry_size))
             {
-                uint64_t end = entry->addr + entry->len;
-                if (end > highest_addr) highest_addr = end;
+                if (e->type != MULTIBOOT_MEMORY_AVAILABLE)
+                    continue;
+                uint64_t end64 = e->addr + e->len;
+                /* Clamp to 32-bit address space */
+                uint32_t end = (end64 > 0xFFFFFFFFULL)
+                             ? 0xFFFFFFFFU : (uint32_t)end64;
+                if (end > highest_avail)
+                    highest_avail = end;
             }
         }
     }
 
-    if (highest_addr > total_memory) {
-        total_memory = highest_addr;
-    }
-    if (total_memory == 0) {
-        total_memory = 16 * 1024 * 1024;
-    }
+    if (highest_avail > total_mem)
+        total_mem = highest_avail;
+    if (total_mem == 0)
+        total_mem = 16U * 1024U * 1024U;
+    if (total_mem > PMM_MAX_RAM)
+        total_mem = PMM_MAX_RAM;
 
-    // pmm_set_limits clamps and configures the bitmap
-    pmm_set_limits(total_memory, 0);
+    pmm_set_limits((uint64_t)total_mem, 0);
 
-    // Second pass: free AVAILABLE regions
+    /* --- Pass 2: free AVAILABLE regions --- */
     for (tag = (struct multiboot_tag *)((uint8_t *)boot_info + 8);
-       tag->type != MULTIBOOT_TAG_TYPE_END;
-       tag = (struct multiboot_tag *)((uint8_t *)tag + ((tag->size + 7) & ~7)))
+         tag->type != MULTIBOOT_TAG_TYPE_END;
+         tag = MB2_TAG_NEXT(tag))
     {
-        if (tag->type == MULTIBOOT_TAG_TYPE_MMAP) {
-            struct multiboot_tag_mmap *mmap = (struct multiboot_tag_mmap *)tag;
-            struct multiboot_mmap_entry *entry;
+        if (tag->type != MULTIBOOT_TAG_TYPE_MMAP)
+            continue;
+
+        struct multiboot_tag_mmap *mmap =
+            (struct multiboot_tag_mmap *)tag;
+        struct multiboot_mmap_entry *e;
+
+        for (e = mmap->entries;
+             (uint8_t *)e < (uint8_t *)mmap + mmap->size;
+             e = (struct multiboot_mmap_entry *)
+                 ((uintptr_t)e + mmap->entry_size))
+        {
+            if (e->type != MULTIBOOT_MEMORY_AVAILABLE)
+                continue;
+
+            uint64_t b64 = e->addr;
+            uint64_t l64 = e->len;
+            if (b64 >= total_mem) continue;
+            if (b64 + l64 > total_mem)
+                l64 = total_mem - b64;
+
+            uint32_t base = align_up32((uint32_t)b64, PAGE_SIZE);
+            uint32_t len  = align_down32((uint32_t)l64, PAGE_SIZE);
+            if (len == 0) continue;
 
-            for (entry = mmap->entries;
-                 (uint8_t *)entry < (uint8_t *)mmap + mmap->size;
-                 entry = (struct multiboot_mmap_entry *)((uint32_t)entry + mmap->entry_size))
-            {
-                if (entry->type == MULTIBOOT_MEMORY_AVAILABLE) {
-                    uint64_t base = entry->addr;
-                    uint64_t len = entry->len;
-
-                    if (base >= total_memory) continue;
-                    if (base + len > total_memory) {
-                        len = total_memory - base;
-                    }
-                    base = align_up_local(base, PAGE_SIZE);
-                    len = align_down_local(len, PAGE_SIZE);
-                    if (len == 0) continue;
-
-                    pmm_mark_region(base, len, 0);
-                    freed_frames += (len / PAGE_SIZE);
-                }
-            }
+            pmm_mark_region(base, len, 0);
+            freed_frames += len / PAGE_SIZE;
         }
     }
 
-    // Fallback if no MMAP tag
+    /* Fallback if no MMAP tag */
     if (!saw_mmap) {
-        uint64_t base = 0x00100000;
-        uint64_t len = (total_memory > base) ? (total_memory - base) : 0;
-        base = align_up_local(base, PAGE_SIZE);
-        len = align_down_local(len, PAGE_SIZE);
+        uint32_t base = 0x00100000U;
+        uint32_t len = (total_mem > base) ? (total_mem - base) : 0;
+        base = align_up32(base, PAGE_SIZE);
+        len  = align_down32(len, PAGE_SIZE);
         if (len) {
             pmm_mark_region(base, len, 0);
-            freed_frames += (len / PAGE_SIZE);
+            freed_frames += len / PAGE_SIZE;
         }
     }
 
-    // Reserve low memory and frame 0
+    /* Reserve low memory (real-mode IVT, BDA, EBDA, BIOS ROM) */
     pmm_mark_region(0, 0x00100000, 1);
 
-    kprintf("[PMM] total_memory bytes: 0x%x\n", (unsigned)total_memory);
-    kprintf("[PMM] freed_frames: 0x%x\n", (unsigned)freed_frames);
+    kprintf("[PMM] total_memory: %u bytes (%u MB)\n",
+            total_mem, total_mem / (1024U * 1024U));
+    kprintf("[PMM] freed_frames: %u (%u MB usable)\n",
+            freed_frames, (freed_frames * PAGE_SIZE) / (1024U * 1024U));
 
     if (freed_frames == 0) {
         kprintf("[PMM] WARN: no free frames detected (MMAP missing or parse failed).\n");
     }
 
-    // Protect Multiboot2 modules (e.g. initrd)
+    /* Protect Multiboot2 modules (e.g. initrd) */
     for (tag = (struct multiboot_tag *)((uint8_t *)boot_info + 8);
          tag->type != MULTIBOOT_TAG_TYPE_END;
-         tag = (struct multiboot_tag *)((uint8_t *)tag + ((tag->size + 7) & ~7)))
+         tag = MB2_TAG_NEXT(tag))
     {
         if (tag->type == MULTIBOOT_TAG_TYPE_MODULE) {
-            struct multiboot_tag_module* mod = (struct multiboot_tag_module*)tag;
-            uint64_t mod_start = (uint64_t)mod->mod_start;
-            uint64_t mod_end = (uint64_t)mod->mod_end;
-            if (mod_end < mod_start) mod_end = mod_start;
-
-            uint64_t mod_start_aligned = align_down_local(mod_start, PAGE_SIZE);
-            uint64_t mod_end_aligned = align_up_local(mod_end, PAGE_SIZE);
-            if (mod_end_aligned < mod_start_aligned) {
-                mod_end_aligned = mod_start_aligned;
-            }
-
-            pmm_mark_region(mod_start_aligned, mod_end_aligned - mod_start_aligned, 1);
+            struct multiboot_tag_module* mod =
+                (struct multiboot_tag_module*)tag;
+            uint32_t ms = align_down32(mod->mod_start, PAGE_SIZE);
+            uint32_t me = align_up32(mod->mod_end, PAGE_SIZE);
+            if (me > ms)
+                pmm_mark_region(ms, me - ms, 1);
         }
     }
 
-    // Protect Multiboot info structure itself
+    /* Protect Multiboot info structure itself */
     uintptr_t kvbase = hal_mm_kernel_virt_base();
     uintptr_t bi_ptr = (uintptr_t)boot_info;
     if (!kvbase || bi_ptr < kvbase) {