summaryrefslogtreecommitdiff
path: root/na/mm/memblock.cc
diff options
context:
space:
mode:
Diffstat (limited to 'na/mm/memblock.cc')
-rw-r--r--na/mm/memblock.cc70
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);
}