diff options
Diffstat (limited to 'na/arch/riscv/config.cc')
| -rw-r--r-- | na/arch/riscv/config.cc | 207 |
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 |
