diff options
Diffstat (limited to 'na/mm/memblock.cc')
| -rw-r--r-- | na/mm/memblock.cc | 70 |
1 files changed, 55 insertions, 15 deletions
diff --git a/na/mm/memblock.cc b/na/mm/memblock.cc index 4cd0b16..aee0245 100644 --- a/na/mm/memblock.cc +++ b/na/mm/memblock.cc @@ -16,6 +16,7 @@ #include <lume/fdt.h> #include <lume/klog.h> #include <lume/kprintf.h> +#include <lume/config.h> #include <lume/addr.h> extern "C" char _stext[], _kernel_end[]; @@ -24,7 +25,11 @@ extern "C" char _stext[], _kernel_end[]; /* Module state */ /* ------------------------------------------------------------------ */ -static struct { +namespace { + +constexpr uint64 kMB = 1024ULL * 1024ULL; + +struct { uint64 mem_base; uint64 mem_size; @@ -41,7 +46,7 @@ static struct { /* ------------------------------------------------------------------ */ /* Insert a reserved region, keeping the array sorted by base. */ -static void insert_reserved(uint64 base, uint64 size) +void insert_reserved(uint64 base, uint64 size) { if (g_mb.reserved_count >= kMemblockMaxRegions) { kernel_panic("memblock: too many reserved regions"); @@ -57,7 +62,7 @@ static void insert_reserved(uint64 base, uint64 size) } /* Check if [base, base+size) overlaps any reserved region. */ -static bool overlaps_reserved(uint64 base, uint64 size) +bool overlaps_reserved(uint64 base, uint64 size) { uint64 end = base + size; for (int i = 0; i < g_mb.reserved_count; i++) { @@ -71,7 +76,7 @@ static bool overlaps_reserved(uint64 base, uint64 size) } /* Find the end of the first reserved region that covers `addr`. */ -static uint64 reserved_end_covering(uint64 addr) +uint64 reserved_end_covering(uint64 addr) { for (int i = 0; i < g_mb.reserved_count; i++) { uint64 rb = g_mb.reserved[i].base; @@ -83,13 +88,14 @@ static uint64 reserved_end_covering(uint64 addr) return addr; } +} // namespace + /* ------------------------------------------------------------------ */ /* Public API */ /* ------------------------------------------------------------------ */ void memblock_init(uint64 fdt_paddr) { - (void)fdt_paddr; /* 1. Get memory layout from FDT */ uint64 mem_base = 0; @@ -104,21 +110,58 @@ void memblock_init(uint64 fdt_paddr) g_mb.reserved_count = 0; g_mb.active = true; - /* 2. Reserve firmware region [mem_base, kernel_start). - * OpenSBI occupies the first 2MB of RAM on RISC-V virt. */ - uint64 ks_pa = boot_va_to_pa(virt_addr( - reinterpret_cast<uint64>(_stext))).raw; - if (ks_pa > mem_base) { - memblock_reserve(mem_base, ks_pa - mem_base); + /* 2. Reserve firmware region [mem_base, kPhysStart). + * + * Everything below the kernel's linked load address is firmware + * territory (OpenSBI), which is PMP-protected. */ + { + uint64 fw_end = kPhysStart; + if (fw_end > mem_base) { + memblock_reserve(mem_base, fw_end - mem_base); + } + } + + /* 2b. Reserve stale pre-relocation kernel image. + * + * After physical KASLR, the kernel is relocated from its original + * load address (kPhysStart) to a new PA. The old copy at + * [kPhysStart, kPhysStart + kernel_size) must not be allocated — + * it contains stale code/data that would corrupt page tables or + * other structures if reused. */ + { + uint64 ks_pa = boot_va_to_pa(virt_addr( + reinterpret_cast<uint64>(_stext))).raw; + if (ks_pa != kPhysStart) { + /* Kernel was relocated — reserve the original location */ + uint64 kernel_size = boot_va_to_pa(virt_addr( + reinterpret_cast<uint64>(_kernel_end))).raw - ks_pa; + memblock_reserve(kPhysStart, kernel_size); + } } /* 3. Reserve kernel image [_stext, _kernel_end) */ + uint64 ks_pa = boot_va_to_pa(virt_addr( + reinterpret_cast<uint64>(_stext))).raw; uint64 ke_pa = boot_va_to_pa(virt_addr( reinterpret_cast<uint64>(_kernel_end))).raw; memblock_reserve(ks_pa, ke_pa - ks_pa); + /* 4. Reserve FDT blob — it lives in RAM and must not be overwritten + * by vmemmap/page_map or buddy before fdt_unflatten() copies it. */ + { + struct fdt_hdr { uint32 magic; uint32 totalsize; }; + const auto* fh = reinterpret_cast<const fdt_hdr*>(fdt_paddr); + uint32 fdt_size = ((fh->totalsize & 0xFF) << 24) | + (((fh->totalsize >> 8) & 0xFF) << 16) | + (((fh->totalsize >> 16) & 0xFF) << 8) | + ((fh->totalsize >> 24) & 0xFF); + memblock_reserve(fdt_paddr, fdt_size); + kprintf("[memblock] FDT reserved [0x%llx, 0x%llx)\n", + fdt_paddr, fdt_paddr + fdt_size); + } + kprintf("[memblock] RAM [0x%llx, 0x%llx) = %llu MB\n", - mem_base, mem_base + mem_size, mem_size / (1024 * 1024)); + mem_base, mem_base + mem_size, mem_size / kMB); kprintf("[memblock] kernel reserved [0x%llx, 0x%llx)\n", ks_pa, ke_pa); } @@ -196,7 +239,6 @@ void memblock_for_each_free(void (*cb)(uint64 base, uint64 size, void* ctx), for (int i = 0; i < g_mb.reserved_count && pos < mem_end; i++) { uint64 rb = g_mb.reserved[i].base; if (rb < pos) { - /* Overlapping/adjacent — skip */ uint64 re = g_mb.reserved[i].base + g_mb.reserved[i].size; if (re > pos) { pos = re; @@ -204,13 +246,11 @@ void memblock_for_each_free(void (*cb)(uint64 base, uint64 size, void* ctx), continue; } if (rb > pos) { - /* Free gap [pos, rb) */ cb(pos, rb - pos, ctx); } pos = g_mb.reserved[i].base + g_mb.reserved[i].size; } - /* Trailing free region after last reserved */ if (pos < mem_end) { cb(pos, mem_end - pos, ctx); } |
