From: Tulio A M Mendes Date: Tue, 10 Feb 2026 06:13:16 +0000 (-0300) Subject: refactor: make pmm.c fully architecture-independent X-Git-Url: https://projects.tadryanom.me/sitemap.xml?a=commitdiff_plain;h=96a10e4840e08ffc9a4765d77b55e3254888398d;p=AdrOS.git refactor: make pmm.c fully architecture-independent 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. --- diff --git a/include/pmm.h b/include/pmm.h index 90b8774..855e833 100644 --- a/include/pmm.h +++ b/include/pmm.h @@ -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 index 0000000..bef7f19 --- /dev/null +++ b/src/arch/x86/pmm_boot.c @@ -0,0 +1,155 @@ +#include "pmm.h" +#include "arch/x86/multiboot2.h" +#include "uart_console.h" +#include "utils.h" +#include "hal/mm.h" + +#include + +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); + } +} diff --git a/src/mm/pmm.c b/src/mm/pmm.c index 0806119..c6a34b1 100644 --- a/src/mm/pmm.c +++ b/src/mm/pmm.c @@ -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"); }