:: commit f6c8097c784535d746ddcbc6ece27c533af3c281

mintsuki <mintsuki@protonmail.com> — 2024-07-20 03:25

parents: 64385a9d86

protos/limine: Change minimum supported paging mode depending on HHDM span, fail if span too large

diff --git a/common/protos/limine.c b/common/protos/limine.c
index afc9846b..f2c37a4d 100644
--- a/common/protos/limine.c
+++ b/common/protos/limine.c
@@ -34,6 +34,37 @@
 
 #define MAX_REQUESTS 128
 
+static uint64_t get_hhdm_span_top(int base_revision) {
+    uint64_t ret = 0x100000000;
+    for (size_t i = 0; i < memmap_entries; i++) {
+        if (base_revision >= 1 && (
+            memmap[i].type == MEMMAP_RESERVED
+         || memmap[i].type == MEMMAP_BAD_MEMORY)) {
+            continue;
+        }
+
+        uint64_t base = memmap[i].base;
+        uint64_t length = memmap[i].length;
+        uint64_t top = base + length;
+
+        if (base < 0x100000000) {
+            base = 0x100000000;
+        }
+
+        if (base >= top) {
+            continue;
+        }
+
+        uint64_t aligned_top = ALIGN_UP(top, 0x40000000);
+
+        if (aligned_top > ret) {
+            ret = aligned_top;
+        }
+    }
+
+    return ret;
+}
+
 static pagemap_t build_pagemap(int base_revision,
                                int paging_mode, bool nx, struct elf_range *ranges, size_t ranges_count,
                                uint64_t physical_base, uint64_t virtual_base,
@@ -204,7 +235,8 @@ static void **requests;
 static void set_paging_mode(int paging_mode, bool kaslr) {
     direct_map_offset = paging_mode_higher_half(paging_mode);
     if (kaslr) {
-        uint64_t mask = ((uint64_t)1 << (paging_mode_va_bits(paging_mode) - 4)) - 1;
+        // Half of the higher half of wiggle room for KASLR, align to 1GiB steps.
+        uint64_t mask = ((uint64_t)1 << (paging_mode_va_bits(paging_mode) - 2)) - 1;
         direct_map_offset += (rand64() & ~((uint64_t)0x40000000 - 1)) & mask;
     }
 }
@@ -446,12 +478,15 @@ noreturn void limine_load(char *config, char *cmdline) {
     }
 #endif
 
+    uint64_t hhdm_span_top = get_hhdm_span_top(base_revision);
+
     printv("limine: Physical base:   %X\n", physical_base);
     printv("limine: Virtual base:    %X\n", virtual_base);
     printv("limine: Slide:           %X\n", slide);
     printv("limine: ELF entry point: %X\n", entry_point);
     printv("limine: Base revision:   %u\n", base_revision);
     printv("limine: Requests count:  %u\n", requests_count);
+    printv("limine: Top of HHDM:     %X\n", hhdm_span_top);
 
     // Paging Mode
     int paging_mode, max_supported_paging_mode, min_supported_paging_mode;
@@ -463,17 +498,49 @@ noreturn void limine_load(char *config, char *cmdline) {
         max_supported_paging_mode = PAGING_MODE_X86_64_5LVL;
     }
     min_supported_paging_mode = PAGING_MODE_X86_64_4LVL;
+    if (hhdm_span_top >= ((uint64_t)1 << paging_mode_va_bits(min_supported_paging_mode)) - (kaslr ? 2 : 1)) {
+        min_supported_paging_mode = PAGING_MODE_X86_64_5LVL;
+        if (min_supported_paging_mode > max_supported_paging_mode) {
+            goto hhdm_fail;
+        }
+    }
+    if (hhdm_span_top >= ((uint64_t)1 << paging_mode_va_bits(min_supported_paging_mode)) - (kaslr ? 2 : 1)) {
+        goto hhdm_fail;
+    }
 #elif defined (__aarch64__)
     max_supported_paging_mode = PAGING_MODE_AARCH64_4LVL;
     min_supported_paging_mode = PAGING_MODE_AARCH64_4LVL;
+    if (hhdm_span_top >= ((uint64_t)1 << paging_mode_va_bits(min_supported_paging_mode)) - (kaslr ? 2 : 1)) {
+        goto hhdm_fail;
+    }
     // TODO(qookie): aarch64 also has optional 5 level paging when using 4K pages
 #elif defined (__riscv64)
     max_supported_paging_mode = vmm_max_paging_mode();
     min_supported_paging_mode = PAGING_MODE_RISCV_SV39;
+    if (hhdm_span_top >= ((uint64_t)1 << paging_mode_va_bits(min_supported_paging_mode)) - (kaslr ? 2 : 1)) {
+        min_supported_paging_mode = PAGING_MODE_RISCV_SV48;
+        if (min_supported_paging_mode > max_supported_paging_mode) {
+            goto hhdm_fail;
+        }
+    }
+    if (hhdm_span_top >= ((uint64_t)1 << paging_mode_va_bits(min_supported_paging_mode)) - (kaslr ? 2 : 1)) {
+        min_supported_paging_mode = PAGING_MODE_RISCV_SV57;
+        if (min_supported_paging_mode > max_supported_paging_mode) {
+            goto hhdm_fail;
+        }
+    }
+    if (hhdm_span_top >= ((uint64_t)1 << paging_mode_va_bits(min_supported_paging_mode)) - (kaslr ? 2 : 1)) {
+        goto hhdm_fail;
+    }
 #else
 #error Unknown architecture
 #endif
 
+    if (0) {
+hhdm_fail:
+        panic(true, "limine: Unable to allocate higher half direct map (too much memory?)");
+    }
+
     char *user_paging_mode_s = config_get_value(config, 0, "PAGING_MODE");
 
     int user_max_paging_mode = PAGING_MODE_MAX;
tab: 248 wrap: offon