]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
refactor: make pmm.c fully architecture-independent
authorTulio A M Mendes <[email protected]>
Tue, 10 Feb 2026 06:13:16 +0000 (03:13 -0300)
committerTulio A M Mendes <[email protected]>
Tue, 10 Feb 2026 06:13:16 +0000 (03:13 -0300)
Extract all Multiboot2 x86-specific code from src/mm/pmm.c into
src/arch/x86/pmm_boot.c as pmm_arch_init().

Design:
- pmm.h now exposes pmm_mark_region(), pmm_set_limits(), pmm_arch_init()
- pmm_init() calls pmm_arch_init() (arch-specific) which discovers
  memory and calls pmm_set_limits() + pmm_mark_region()
- pmm.c provides a weak default pmm_arch_init() for archs without one
- Kernel protection uses hal_mm_virt_to_phys() (no #if MIPS/x86)
- x86 pmm_boot.c handles Multiboot2 parsing, module protection,
  and boot info protection
- Zero #if guards remain in pmm.c

Passes: make, cppcheck, QEMU smoke test.

include/pmm.h
src/arch/x86/pmm_boot.c [new file with mode: 0644]
src/mm/pmm.c

index 90b87743c7345a97a79d9daabaa45e2d9636dd1a..855e83359e4af46165bd26fb64dbcec35454cfc5 100644 (file)
@@ -9,6 +9,19 @@
 // boot_info is architecture dependent (Multiboot info on x86)
 void pmm_init(void* boot_info);
 
+// Mark a range of physical memory as used (1) or free (0).
+// Called by arch-specific boot code to describe the memory map.
+void pmm_mark_region(uint64_t base, uint64_t size, int used);
+
+// Set total memory size and max frame count.
+// Must be called by arch boot code before marking regions.
+void pmm_set_limits(uint64_t total_mem, uint64_t max_fr);
+
+// Architecture-specific boot info parser.
+// Implemented per-arch (e.g. Multiboot2 on x86, DTB on ARM).
+// Called by pmm_init(). Must call pmm_set_limits() then pmm_mark_region().
+void pmm_arch_init(void* boot_info);
+
 // Allocate a single physical page
 void* pmm_alloc_page(void);
 
diff --git a/src/arch/x86/pmm_boot.c b/src/arch/x86/pmm_boot.c
new file mode 100644 (file)
index 0000000..bef7f19
--- /dev/null
@@ -0,0 +1,155 @@
+#include "pmm.h"
+#include "arch/x86/multiboot2.h"
+#include "uart_console.h"
+#include "utils.h"
+#include "hal/mm.h"
+
+#include <stdint.h>
+
+static uint64_t align_up_local(uint64_t value, uint64_t align) {
+    return (value + align - 1) & ~(align - 1);
+}
+
+static uint64_t align_down_local(uint64_t value, uint64_t align) {
+    return value & ~(align - 1);
+}
+
+void pmm_arch_init(void* boot_info) {
+    if (!boot_info) {
+        uart_print("[PMM] Error: boot_info is NULL!\n");
+        return;
+    }
+
+    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;
+
+    uart_print("[PMM] Parsing Multiboot2 info...\n");
+
+    // First pass: determine total 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)))
+    {
+        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);
+        }
+        else 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))
+            {
+                uint64_t end = entry->addr + entry->len;
+                if (end > highest_addr) highest_addr = end;
+            }
+        }
+    }
+
+    if (highest_addr > total_memory) {
+        total_memory = highest_addr;
+    }
+    if (total_memory == 0) {
+        total_memory = 16 * 1024 * 1024;
+    }
+
+    // pmm_set_limits clamps and configures the bitmap
+    pmm_set_limits(total_memory, 0);
+
+    // Second pass: 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)))
+    {
+        if (tag->type == MULTIBOOT_TAG_TYPE_MMAP) {
+            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))
+            {
+                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);
+                }
+            }
+        }
+    }
+
+    // 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);
+        if (len) {
+            pmm_mark_region(base, len, 0);
+            freed_frames += (len / PAGE_SIZE);
+        }
+    }
+
+    // Reserve low memory and frame 0
+    pmm_mark_region(0, 0x00100000, 1);
+
+    uart_print("[PMM] total_memory bytes: ");
+    char tmp[11];
+    itoa_hex((uint32_t)total_memory, tmp);
+    uart_print(tmp);
+    uart_print("\n");
+    uart_print("[PMM] freed_frames: ");
+    itoa_hex((uint32_t)freed_frames, tmp);
+    uart_print(tmp);
+    uart_print("\n");
+
+    if (freed_frames == 0) {
+        uart_print("[PMM] WARN: no free frames detected (MMAP missing or parse failed).\n");
+    }
+
+    // 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)))
+    {
+        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);
+        }
+    }
+
+    // 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) {
+        pmm_mark_region((uint64_t)bi_ptr, 4096, 1);
+    }
+}
index 0806119efafc70c035ece122c110732b679167b6..c6a34b1a5a5bdc5eee58e42f7c0f4541689a245a 100644 (file)
@@ -1,7 +1,4 @@
 #include "pmm.h"
-#if defined(__i386__) || defined(__x86_64__)
-#include "arch/x86/multiboot2.h"
-#endif
 #include "utils.h"
 #include "uart_console.h"
 #include "hal/cpu.h"
@@ -47,8 +44,7 @@ static int bitmap_test(uint64_t bit) {
     return memory_bitmap[bit / 8] & (1 << (bit % 8));
 }
 
-// Mark a range of physical memory as used (1) or free (0)
-static void pmm_mark_region(uint64_t base, uint64_t size, int used) {
+void pmm_mark_region(uint64_t base, uint64_t size, int used) {
     uint64_t start_frame = base / PAGE_SIZE;
     uint64_t frames_count = size / PAGE_SIZE;
 
@@ -72,192 +68,50 @@ static void pmm_mark_region(uint64_t base, uint64_t size, int used) {
     }
 }
 
+void pmm_set_limits(uint64_t total_mem, uint64_t max_fr) {
+    if (total_mem > MAX_RAM_SIZE) total_mem = MAX_RAM_SIZE;
+    total_mem = align_down(total_mem, PAGE_SIZE);
+    total_memory = total_mem;
+    max_frames = max_fr ? max_fr : (total_mem / PAGE_SIZE);
+    used_memory = max_frames * PAGE_SIZE;
+}
+
+// Weak default: architectures that don't implement pmm_arch_init yet
+__attribute__((weak))
+void pmm_arch_init(void* boot_info) {
+    (void)boot_info;
+    uart_print("[PMM] No arch-specific memory init. Assuming 16MB.\n");
+    pmm_set_limits(16 * 1024 * 1024, 0);
+}
+
 void pmm_init(void* boot_info) {
     // 1. Mark EVERYTHING as used initially to be safe
     for (int i = 0; i < BITMAP_SIZE; i++) {
         memory_bitmap[i] = 0xFF;
     }
 
-#if defined(__i386__) || defined(__x86_64__)
-    // Parse Multiboot2 Info
-    if (boot_info) {
-        struct multiboot_tag *tag;
-        (void)*(uint32_t *)boot_info;
-        uint64_t highest_addr = 0;
-        int saw_mmap = 0;
-        uint64_t freed_frames = 0;
-        
-        uart_print("[PMM] Parsing Multiboot2 info...\n");
-
-        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)))
-        {
-            if (tag->type == MULTIBOOT_TAG_TYPE_BASIC_MEMINFO) {
-                 struct multiboot_tag_basic_meminfo *meminfo = (struct multiboot_tag_basic_meminfo *)tag;
-                 // Basic info just gives lower/upper KB
-                 uint64_t mem_kb = meminfo->mem_upper; // Upper memory only
-                 total_memory = (mem_kb * 1024) + (1024*1024); // +1MB low mem
-            }
-            else 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))
-                {
-                    uint64_t end = entry->addr + entry->len;
-                    if (end > highest_addr) highest_addr = end;
-
-                    // Only mark AVAILABLE regions as free
-                    if (entry->type == MULTIBOOT_MEMORY_AVAILABLE) {
-                        pmm_mark_region(entry->addr, entry->len, 0); // 0 = Free
-                    }
-                }
-            }
-        }
-
-        // Finalize memory limits (must be done BEFORE pmm_mark_region is meaningful)
-        if (highest_addr > total_memory) {
-            total_memory = highest_addr;
-        }
-
-        if (total_memory == 0) {
-            total_memory = 16 * 1024 * 1024; // Fallback to 16MB
-        }
-
-        if (total_memory > MAX_RAM_SIZE) {
-            total_memory = MAX_RAM_SIZE;
-        }
-
-        total_memory = align_down(total_memory, PAGE_SIZE);
-        max_frames = total_memory / PAGE_SIZE;
-
-        // After "everything used", used_memory should reflect that state.
-        used_memory = max_frames * PAGE_SIZE;
-
-        // Re-run map processing to free AVAILABLE regions now that max_frames is valid.
-        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)))
-        {
-            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))
-                {
-                    if (entry->type == MULTIBOOT_MEMORY_AVAILABLE) {
-                        // Clamp to our supported range
-                        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(base, PAGE_SIZE);
-                        len = align_down(len, PAGE_SIZE);
-                        if (len == 0) continue;
-
-                        pmm_mark_region(base, len, 0);
-                        freed_frames += (len / PAGE_SIZE);
-                    }
-                }
-            }
-        }
-
-        // Fallback: if we didn't manage to see an MMAP tag, assume everything above 1MB
-        // up to total_memory is usable. This is less accurate, but prevents "all used"
-        // which breaks early-boot allocators.
-        if (!saw_mmap) {
-            uint64_t base = 0x00100000;
-            uint64_t len = (total_memory > base) ? (total_memory - base) : 0;
-            base = align_up(base, PAGE_SIZE);
-            len = align_down(len, PAGE_SIZE);
-            if (len) {
-                pmm_mark_region(base, len, 0);
-                freed_frames += (len / PAGE_SIZE);
-            }
-        }
-
-        // Reserve low memory and frame 0.
-        // Frame 0 must never be returned because it aliases NULL in C.
-        // Also keep the first 1MB used (BIOS/real-mode areas, multiboot scratch, etc.).
-        pmm_mark_region(0, 0x00100000, 1);
-
-        uart_print("[PMM] total_memory bytes: ");
-        char tmp[11];
-        itoa_hex((uint32_t)total_memory, tmp);
-        uart_print(tmp);
-        uart_print("\n");
-        uart_print("[PMM] max_frames: ");
-        itoa_hex((uint32_t)max_frames, tmp);
-        uart_print(tmp);
-        uart_print("\n");
-        uart_print("[PMM] freed_frames: ");
-        itoa_hex((uint32_t)freed_frames, tmp);
-        uart_print(tmp);
-        uart_print("\n");
-
-        if (freed_frames == 0) {
-            uart_print("[PMM] WARN: no free frames detected (MMAP missing or parse failed).\n");
-        }
-    } else {
-        uart_print("[PMM] Error: boot_info is NULL!\n");
-    }
-#else
-    // Manual setup for ARM/RISC-V (assuming fixed RAM for now)
-    // TODO: Parse Device Tree (DTB)
-    uart_print("[PMM] Manual memory config (ARM/RISC-V)\n");
-    
-    uint64_t ram_base = 0;
-    #ifdef __aarch64__
-        ram_base = 0x40000000;
-    #elif defined(__riscv)
-        ram_base = 0x80000000;
-    #endif
-
-    uint64_t ram_size = 128 * 1024 * 1024; // Assume 128MB
-    if (ram_size > MAX_RAM_SIZE) ram_size = MAX_RAM_SIZE;
-    ram_size = align_down(ram_size, PAGE_SIZE);
-    max_frames = ram_size / PAGE_SIZE;
-    used_memory = max_frames * PAGE_SIZE;
-    
-    // Mark all RAM as free
-    pmm_mark_region(ram_base, ram_size, 0);
-
-#endif
+    // 2. Let arch-specific code discover memory and call
+    //    pmm_set_limits() + pmm_mark_region()
+    pmm_arch_init(boot_info);
 
-    // 2. Protect Kernel Memory (Critical!)
-    // We must ensure the kernel code itself is marked USED
-    
-    // Check if we are Higher Half (start > 3GB)
+    // 3. Protect Kernel Memory (Critical!)
     uintptr_t virt_start_ptr = (uintptr_t)&_start;
     uintptr_t virt_end_ptr = (uintptr_t)&_end;
-    
-    uint64_t phys_start = (uint64_t)virt_start_ptr;
-    uint64_t phys_end = (uint64_t)virt_end_ptr;
 
-    uintptr_t kvbase = hal_mm_kernel_virt_base();
-    if (kvbase && virt_start_ptr >= kvbase) {
-        phys_start -= kvbase;
-        phys_end -= kvbase;
-        uart_print("[PMM] Detected Higher Half Kernel. Adjusting protection range.\n");
-    }
-
-#if defined(__mips__)
-    // MIPS KSEG0 virtual addresses are 0x80000000..0x9FFFFFFF mapped to physical 0x00000000..
-    if (virt_start >= 0x80000000ULL && virt_start < 0xA0000000ULL) {
-        phys_start = virt_start - 0x80000000ULL;
-        phys_end = virt_end - 0x80000000ULL;
+    uint64_t phys_start = (uint64_t)hal_mm_virt_to_phys(virt_start_ptr);
+    uint64_t phys_end = (uint64_t)hal_mm_virt_to_phys(virt_end_ptr);
+
+    // Fallback: if hal_mm_virt_to_phys returns 0 (not implemented),
+    // try subtracting kernel virtual base manually
+    if (phys_start == 0 && virt_start_ptr != 0) {
+        phys_start = (uint64_t)virt_start_ptr;
+        phys_end = (uint64_t)virt_end_ptr;
+        uintptr_t kvbase = hal_mm_kernel_virt_base();
+        if (kvbase && virt_start_ptr >= kvbase) {
+            phys_start -= kvbase;
+            phys_end -= kvbase;
+        }
     }
-#endif
 
     uint64_t phys_start_aligned = align_down(phys_start, PAGE_SIZE);
     uint64_t phys_end_aligned = align_up(phys_end, PAGE_SIZE);
@@ -266,41 +120,7 @@ void pmm_init(void* boot_info) {
     }
     uint64_t kernel_size = phys_end_aligned - phys_start_aligned;
 
-    pmm_mark_region(phys_start_aligned, kernel_size, 1); // Mark Used
-
-#if defined(__i386__) || defined(__x86_64__)
-    // 3. Protect Multiboot2 modules (e.g. initrd)
-    // The initrd is loaded by GRUB into physical memory. If we don't reserve it,
-    // the PMM may allocate those frames and overwrite the initrd header/data.
-    if (boot_info) {
-        struct multiboot_tag *tag;
-        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)))
-        {
-            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(mod_start, PAGE_SIZE);
-                uint64_t mod_end_aligned = align_up(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);
-            }
-        }
-
-        // 4. Protect Multiboot info (if x86)
-        uintptr_t bi_ptr = (uintptr_t)boot_info;
-        if (!kvbase || bi_ptr < kvbase) {
-            pmm_mark_region((uint64_t)bi_ptr, 4096, 1); // Protect at least 1 page
-        }
-    }
-#endif
+    pmm_mark_region(phys_start_aligned, kernel_size, 1);
 
     uart_print("[PMM] Initialized.\n");
 }