summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.x@gmail.com>2026-05-13 15:33:27 +0800
committerRichard Qin <richard.qin.x@gmail.com>2026-05-13 15:33:27 +0800
commite5d267d35504a831855d73d8d49014f8c20c9a8a (patch)
treef7e0cb38e4df6762e0d3de2f2e3150147329e710
parent29c979d3a99495807cf98f635c1ef283f1a02582 (diff)
downloadlume-e5d267d35504a831855d73d8d49014f8c20c9a8a.tar.gz
lume-e5d267d35504a831855d73d8d49014f8c20c9a8a.tar.xz
feat: refactor KASLR computation and enhance validation checks
-rw-r--r--na/arch/riscv/config.cc207
1 files changed, 145 insertions, 62 deletions
diff --git a/na/arch/riscv/config.cc b/na/arch/riscv/config.cc
index 421817d..a18a825 100644
--- a/na/arch/riscv/config.cc
+++ b/na/arch/riscv/config.cc
@@ -6,14 +6,14 @@
*
* Provides the runtime (post-KASLR) values of the higher-half
* virtual address bases. kaslr_init() is called once during
- * early boot, before vmm_init(), to slide these bases by a
- * random, 1GB-aligned offset.
+ * early boot, before vmm_init(), to randomise the layout and
+ * fill the unified KaslrLayout structure.
*
* KASLR slide budget:
* The default Direct Map starts at 0xFFFFFFC0_0000_0000 (VPN[2]=256).
* The default Vmemmap starts at 0xFFFFFFD0_0000_0000 (VPN[2]=320).
* A 1GB superpage occupies one VPN[2] slot. We allow a slide of
- * 0..63 slots (0..63 GB) downward, which keeps both regions inside
+ * 0..63 slots (0..63 GB) upward, which keeps both regions inside
* the upper canonical half and avoids colliding with the kernel
* text region (linked at VPN[2] = 258 + 2 = ~0xFFFFFFC0_8020_0000).
*
@@ -23,7 +23,7 @@
#include <arch/config.h>
#include <lume/fdt.h>
-#include <lume/frame.h>
+#include <lume/page.h>
#include <lume/config.h>
#include <lume/klog.h>
#include <lume/kprintf.h>
@@ -33,16 +33,23 @@ extern "C" char _stext[], _kernel_end[];
namespace arch {
-/* Runtime bases — default to the compile-time constants. */
+/* Global KASLR Layout Structure*/
+KaslrLayout g_kaslr_layout = {};
+
+/* Runtime bases — updated by kaslr_layout_compute() */
uint64 g_direct_map_base = kDirectMapBaseDefault;
uint64 g_vmemmap_base = kVmemmapBaseDefault;
+/* VA Range for validation */
struct VaRange {
const char* name;
uint64 start;
uint64 end; // exclusive
};
+/* ================================================================
+ * Utilities
+ * ================================================================ */
static inline uint64 align_down_page(uint64 v)
{
return v & ~(kPageSize - 1);
@@ -61,6 +68,15 @@ static uint64 checked_add_u64(uint64 a, uint64 b, const char* detail)
return a + b;
}
+/* Splitmix64: non-plaintext hash derived from seed (used for seed_hash) */
+static uint64 splitmix64(uint64 x)
+{
+ x += 0x9e3779b97f4a7c15ULL;
+ x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL;
+ x = x ^ (x >> 27);
+ return x * 0x94d049bb133111ebULL;
+}
+
/* SV39 canonical VA check: bits [63:39] must equal sign bit 38. */
static bool is_sv39_canonical(uint64 va)
{
@@ -79,16 +95,84 @@ static bool ranges_overlap(const VaRange& a, const VaRange& b)
return (a.start < b.end) && (b.start < a.end);
}
-static void kaslr_self_check(uint64 slot)
+/* ================================================================
+ * KASLR Computation (Phase A: Centralized in single function)
+ *
+ * This function:
+ * 1. Generates or derives random seed
+ * 2. Selects 1GB-aligned slot [0..63]
+ * 3. Computes seed_hash via splitmix64
+ * 4. Fills KaslrLayout structure completely
+ * 5. Validates ranges (canonical, overlaps)
+ * 6. Logs according to CONFIG_KASLR_LOG_LEVEL
+ * ================================================================ */
+static void kaslr_layout_compute()
{
- uint64 mem_base = 0;
- uint64 mem_size = 0;
+#ifdef CONFIG_KASLR
+ constexpr uint64 k1GB = 1ULL << 30;
+ constexpr uint64 kMaxSlots = 64;
+
+ /* 1. Get seed and source */
+ uint64 seed = random_early_seed_value();
+ RandomSeedSource rsrc = random_early_seed_source();
+ KaslrSeedSource source;
+ switch (rsrc) {
+ case RandomSeedSource::FdtProvided:
+ source = KaslrSeedSource::kFdt;
+ break;
+ case RandomSeedSource::HwRandom:
+ source = KaslrSeedSource::kHardware;
+ break;
+ case RandomSeedSource::CycleFallback:
+ case RandomSeedSource::Unknown:
+ default:
+ source = KaslrSeedSource::kCycle;
+ break;
+ }
+
+ /* 2. Select random slot and compute slide */
+ uint64 raw = get_random_u64();
+ uint64 slot = raw % kMaxSlots;
+ if (slot == 0) {
+ slot = 1; /* slot=0 causes direct_map to overlap with kernel image VA */
+ }
+ uint64 slide = slot * k1GB;
+
+ /* 3. Compute non-plaintext hash */
+ uint64 seed_hash = splitmix64(seed ^ 0xdeadbeefdeadbeefULL);
+
+ /* 4. Compute randomised bases */
+ uint64 direct_map_base = kDirectMapBaseDefault + slide;
+ uint64 vmemmap_base = kVmemmapBaseDefault + slide;
+
+ /* 5. Fill KaslrLayout */
+ g_kaslr_layout.seed = seed;
+ g_kaslr_layout.seed_hash = seed_hash;
+ g_kaslr_layout.source = source;
+ g_kaslr_layout.slot = slot;
+ g_kaslr_layout.slide_bytes = slide;
+ g_kaslr_layout.policy_id = kKaslrPolicyId;
+ g_kaslr_layout.direct_map_base = direct_map_base;
+ g_kaslr_layout.vmemmap_base = vmemmap_base;
+ g_kaslr_layout.canonical_ok = false;
+ g_kaslr_layout.overlap_ok = false;
+
+ /* Update global runtime bases */
+ g_direct_map_base = direct_map_base;
+ g_vmemmap_base = vmemmap_base;
+
+ /* 6. Self-check: canonical VA validation and overlap detection */
+ uint64 mem_base = 0, mem_size = 0;
fdt_early_get_mem_info(&mem_base, &mem_size);
if (mem_size == 0) {
- kernel_panic("kaslr: invalid memory size from FDT");
+ kernel_panic("kaslr_layout_compute: invalid memory from FDT");
}
- /* Direct-map covers the discovered physical memory window. */
+ /* Construct VA ranges */
+ uint64 num_pages = mem_size / kPageSize;
+ uint64 page_bytes = num_pages * sizeof(Page);
+ uint64 page_span = align_up_page(page_bytes);
+
VaRange direct_map {
"direct-map",
checked_add_u64(g_direct_map_base, mem_base, "direct_map start"),
@@ -97,23 +181,19 @@ static void kaslr_self_check(uint64 slot)
mem_size, "direct_map end"),
};
- /* Vmemmap covers the Frame descriptor array span. */
- uint64 num_frames = mem_size / kPageSize;
- uint64 frame_bytes = num_frames * sizeof(Frame);
- uint64 frame_span = align_up_page(frame_bytes);
VaRange vmemmap {
"vmemmap",
g_vmemmap_base,
- checked_add_u64(g_vmemmap_base, frame_span, "vmemmap end"),
+ checked_add_u64(g_vmemmap_base, page_span, "vmemmap end"),
};
- /* Kernel image window is linked at fixed higher-half addresses. */
VaRange kernel_img {
"kernel-image",
align_down_page(reinterpret_cast<uint64>(_stext)),
align_up_page(reinterpret_cast<uint64>(_kernel_end)),
};
+ /* Validate each range is canonical */
auto check_kernel_half = [](const VaRange& r) {
if (r.start >= r.end) {
kernel_panic("kaslr: invalid VA range", r.name);
@@ -127,6 +207,7 @@ static void kaslr_self_check(uint64 slot)
check_kernel_half(vmemmap);
check_kernel_half(kernel_img);
+ /* Validate no overlaps */
if (ranges_overlap(direct_map, kernel_img)) {
kernel_panic("kaslr: direct-map overlaps kernel image");
}
@@ -137,57 +218,59 @@ static void kaslr_self_check(uint64 slot)
kernel_panic("kaslr: direct-map overlaps vmemmap");
}
- kprintf("[boot][kaslr] seed=0x%llx source=%s slot=%llu slide_gb=%llu\n",
- random_early_seed_value(),
- random_seed_source_name(random_early_seed_source()),
- slot, slot);
- kprintf("[boot][kaslr] direct-map=[0x%llx,0x%llx)\n",
- direct_map.start, direct_map.end);
- kprintf("[boot][kaslr] vmemmap=[0x%llx,0x%llx)\n",
- vmemmap.start, vmemmap.end);
- kprintf("[boot][kaslr] kernel-image=[0x%llx,0x%llx)\n",
- kernel_img.start, kernel_img.end);
-}
+ /* Mark validation success */
+ g_kaslr_layout.canonical_ok = true;
+ g_kaslr_layout.overlap_ok = true;
-void kaslr_init()
-{
-#ifdef CONFIG_KASLR
- /*
- * Generate a random 1GB-aligned slide.
- *
- * Strategy (matching Linux's approach):
- * 1. Pull 64 random bits from the early PRNG.
- * 2. Reduce to a slot number in [0, kMaxSlots).
- * 3. Convert to a byte offset (slot * 1GB).
- * 4. Add to both bases (slide upward in VA space).
- *
- * Why 1GB alignment?
- * SV39 L2 (root) PTEs map 1GB superpages. Keeping the slide
- * 1GB-aligned means the direct-map can still use gigapages,
- * avoiding TLB pressure. This matches Linux's KASLR on RISC-V.
- */
- constexpr uint64 k1GB = 1ULL << 30;
- constexpr uint64 kMaxSlots = 64; /* 0..63 GB slide range */
+ /* 7. Logging according to CONFIG_KASLR_LOG_LEVEL */
+ if (kKaslrLogLevel >= 1) {
+ /* Minimal logging: just the seed and slot (release mode) */
+ kprintf("[boot][kaslr] policy_id=%u source=%s slot=%llu\n",
+ g_kaslr_layout.policy_id,
+ random_seed_source_name(rsrc),
+ slot);
+ }
+
+ if (kKaslrLogLevel >= 2) {
+ /* Debug logging: full ranges and hashes */
+ kprintf("[boot][kaslr] seed=0x%llx seed_hash=0x%llx\n",
+ seed, seed_hash);
+ kprintf("[boot][kaslr] direct-map=[0x%llx,0x%llx)\n",
+ direct_map.start, direct_map.end);
+ kprintf("[boot][kaslr] vmemmap=[0x%llx,0x%llx)\n",
+ vmemmap.start, vmemmap.end);
+ kprintf("[boot][kaslr] kernel-image=[0x%llx,0x%llx)\n",
+ kernel_img.start, kernel_img.end);
+ }
- uint64 raw = get_random_u64();
- uint64 slot = raw % kMaxSlots;
- uint64 slide = slot * k1GB;
+#else /* CONFIG_KASLR disabled */
+ g_kaslr_layout.seed = 0;
+ g_kaslr_layout.seed_hash = 0;
+ g_kaslr_layout.source = KaslrSeedSource::kCycle;
+ g_kaslr_layout.slot = 0;
+ g_kaslr_layout.slide_bytes = 0;
+ g_kaslr_layout.policy_id = kKaslrPolicyId;
+ g_kaslr_layout.direct_map_base = kDirectMapBaseDefault;
+ g_kaslr_layout.vmemmap_base = kVmemmapBaseDefault;
+ g_kaslr_layout.canonical_ok = true;
+ g_kaslr_layout.overlap_ok = true;
- g_direct_map_base = kDirectMapBaseDefault + slide;
- g_vmemmap_base = kVmemmapBaseDefault + slide;
+ g_direct_map_base = kDirectMapBaseDefault;
+ g_vmemmap_base = kVmemmapBaseDefault;
- kaslr_self_check(slot);
-#else
- g_direct_map_base = kDirectMapBaseDefault;
- g_vmemmap_base = kVmemmapBaseDefault;
- kprintf("[boot][kaslr] disabled source=%s seed=0x%llx slot=0 slide_gb=0\n",
- random_seed_source_name(random_early_seed_source()),
- random_early_seed_value());
- kprintf("[boot][kaslr] direct-map=[0x%llx,0x%llx)\n",
- g_direct_map_base, g_direct_map_base);
- kprintf("[boot][kaslr] vmemmap=[0x%llx,0x%llx)\n",
- g_vmemmap_base, g_vmemmap_base);
+ if (kKaslrLogLevel >= 1) {
+ kprintf("[boot][kaslr] disabled policy_id=%u\n",
+ g_kaslr_layout.policy_id);
+ }
#endif
}
+/* ================================================================
+ * Public entry point
+ * ================================================================ */
+void kaslr_init()
+{
+ kaslr_layout_compute();
+}
+
} // namespace arch