diff options
Diffstat (limited to 'na/arch/riscv')
| -rw-r--r-- | na/arch/riscv/config.cc | 59 | ||||
| -rw-r--r-- | na/arch/riscv/include/arch/config.h | 40 | ||||
| -rw-r--r-- | na/arch/riscv/mm/pmap.cc | 12 |
3 files changed, 104 insertions, 7 deletions
diff --git a/na/arch/riscv/config.cc b/na/arch/riscv/config.cc index 139d932..9b4cc6e 100644 --- a/na/arch/riscv/config.cc +++ b/na/arch/riscv/config.cc @@ -1,10 +1,69 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* Copyright (C) 2026 Richard Qin */ +/* + * RISC-V Architecture Configuration — runtime state and KASLR + * + * 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. + * + * 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 + * the upper canonical half and avoids colliding with the kernel + * text region (linked at VPN[2] = 258 + 2 = ~0xFFFFFFC0_8020_0000). + * + * In practice, with 128MB RAM the kernel+direct map only touches + * VPN[2] 258-259, so a slide of up to 63GB is safe. + */ + #include <arch/config.h> +#include <lume/random.h> +#include <lume/kprintf.h> namespace arch { +/* Runtime bases — default to the compile-time constants. */ uint64 g_direct_map_base = kDirectMapBaseDefault; +uint64 g_vmemmap_base = kVmemmapBaseDefault; + +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 */ + + uint64 raw = get_random_u64(); + uint64 slot = raw % kMaxSlots; + uint64 slide = slot * k1GB; + + g_direct_map_base = kDirectMapBaseDefault + slide; + g_vmemmap_base = kVmemmapBaseDefault + slide; + + kprintf("[kaslr] slide = %llu GB (slot %llu/64)\n", slide >> 30, slot); + kprintf("[kaslr] direct_map_base = 0x%llx\n", g_direct_map_base); + kprintf("[kaslr] vmemmap_base = 0x%llx\n", g_vmemmap_base); +#else + kprintf("[kaslr] CONFIG_KASLR disabled, using fixed layout\n"); +#endif +} } // namespace arch diff --git a/na/arch/riscv/include/arch/config.h b/na/arch/riscv/include/arch/config.h index c407b25..e7b1027 100644 --- a/na/arch/riscv/include/arch/config.h +++ b/na/arch/riscv/include/arch/config.h @@ -8,21 +8,49 @@ * * These constants define the architecture-specific memory layout. * Each arch/ implementation must provide this header with the same symbols. + * + * KASLR: At boot, kaslr_init() randomises g_direct_map_base and + * g_vmemmap_base. All MI code must read these runtime variables + * (via pa_to_va / vmm helpers) rather than the static defaults. */ #include <lume/types.h> namespace arch { -/* Direct-map virtual base (higher-half base). - * SV39: top of 39-bit VA space. VA = PA + direct_map_base. */ +/* ================================================================ + * Default (compile-time) virtual addresses + * + * SV39 gives a 512GB virtual address space. + * Higher-half layout (defaults before KASLR): + * + * 0xFFFFFFC0_00000000 (256) Direct map (PA+offset) + * 0xFFFFFFD0_00000000 (320) Vmemmap (Frame array) + * + * The 64GB gap between them is the KASLR slide range. + * ================================================================ */ inline constexpr uint64 kDirectMapBaseDefault = 0xFFFFFFC000000000ULL; -extern uint64 g_direct_map_base; - -/* Dedicated VA base for the Frame descriptor array (vmemmap). */ -inline constexpr uint64 kVmemmapBase = 0xFFFFFFD000000000ULL; +inline constexpr uint64 kVmemmapBaseDefault = 0xFFFFFFD000000000ULL; /* Physical memory base (where RAM starts on RISC-V virt platform) */ inline constexpr uint64 kPhysBase = 0x80000000ULL; +/* ================================================================ + * Runtime (post-KASLR) virtual addresses + * + * These start equal to the defaults above. If CONFIG_KASLR is + * enabled, kaslr_init() adds a random, 1GB-aligned offset + * to each, sliding both regions upward in VA space. + * ================================================================ */ +extern uint64 g_direct_map_base; +extern uint64 g_vmemmap_base; + +/* ================================================================ + * KASLR entry point + * + * Must be called AFTER random_early_init() and BEFORE vmm_init(). + * When CONFIG_KASLR is disabled this is a no-op. + * ================================================================ */ +void kaslr_init(); + } // namespace arch diff --git a/na/arch/riscv/mm/pmap.cc b/na/arch/riscv/mm/pmap.cc index 4ed3bca..dafbeaf 100644 --- a/na/arch/riscv/mm/pmap.cc +++ b/na/arch/riscv/mm/pmap.cc @@ -24,6 +24,12 @@ namespace pmap { +/* + * Before the first pmap::activate(), CPU runs on entry.S bootstrap page table + * (fixed direct-map base). After activation, runtime (KASLR) base is valid. + */ +static bool g_runtime_mapping_ready = false; + /* PTE bit definitions */ inline constexpr uint64 PTE_V = 1ULL << 0; inline constexpr uint64 PTE_R = 1ULL << 1; @@ -59,7 +65,10 @@ static inline uint64 pa_to_pte(uint64 pa, uint64 flags) /* Convert PA to kernel VA for accessing page table contents */ static inline uint64 *pa_to_ptr(uint64 pa) { - return reinterpret_cast<uint64 *>(pa_to_va(phys_addr(pa)).raw); + uint64 va = g_runtime_mapping_ready + ? pa_to_va(phys_addr(pa)).raw + : boot_pa_to_va(phys_addr(pa)).raw; + return reinterpret_cast<uint64 *>(va); } /* Allocate a zeroed page for a page table level. @@ -203,6 +212,7 @@ void activate(uint64 root_pa) { arch::mmu::set_page_table(root_pa); arch::mmu::flush_tlb_all(); + g_runtime_mapping_ready = true; } uint64 vm_perm_to_pte(uint64 vm_perm) |
