:: commit 79b8c38c8cb7246160b9211c8853974623483699

mintsuki <mintsuki@protonmail.com> — 2021-07-15 12:09

parents: 178f3cea0e

stivale: Initial infrastructure to support ELF permissions

diff --git a/stage23/mm/vmm.c b/stage23/mm/vmm.c
index d44cc24e..76f51394 100644
--- a/stage23/mm/vmm.c
+++ b/stage23/mm/vmm.c
@@ -3,11 +3,19 @@
 #include <mm/vmm.h>
 #include <mm/pmm.h>
 #include <lib/blib.h>
+#include <sys/cpu.h>
 
 #define PT_SIZE ((uint64_t)0x1000)
 
 typedef uint64_t pt_entry_t;
 
+void vmm_assert_nx(void) {
+    uint32_t a, b, c, d;
+    if (!cpuid(0x80000001, 0, &a, &b, &c, &d) || !(d & (1 << 20))) {
+        panic("vmm: NX functionality not available on this CPU.");
+    }
+}
+
 static pt_entry_t *get_next_level(pt_entry_t *current_level, size_t entry) {
     pt_entry_t *ret;
 
diff --git a/stage23/mm/vmm.h b/stage23/mm/vmm.h
index 9aa096c5..d7cbcb37 100644
--- a/stage23/mm/vmm.h
+++ b/stage23/mm/vmm.h
@@ -4,11 +4,16 @@
 #include <stdint.h>
 #include <stdbool.h>
 
+#define VMM_FLAG_PRESENT ((uint64_t)1 << 0)
+#define VMM_FLAG_WRITE   ((uint64_t)1 << 1)
+#define VMM_FLAG_NOEXEC  ((uint64_t)1 << 63)
+
 typedef struct {
     int   levels;
     void *top_level;
 } pagemap_t;
 
+void vmm_assert_nx(void);
 pagemap_t new_pagemap(int lv);
 void map_page(pagemap_t pagemap, uint64_t virt_addr, uint64_t phys_addr, uint64_t flags, bool hugepages);
 
diff --git a/stage23/protos/stivale.c b/stage23/protos/stivale.c
index 79e4a975..347d1a3f 100644
--- a/stage23/protos/stivale.c
+++ b/stage23/protos/stivale.c
@@ -282,7 +282,7 @@ void stivale_load(char *config, char *cmdline) {
 
     pagemap_t pagemap = {0};
     if (bits == 64)
-        pagemap = stivale_build_pagemap(want_5lv, false);
+        pagemap = stivale_build_pagemap(want_5lv, false, NULL, 0);
 
     // Reserve 32K at 0x70000
     memmap_alloc_range(0x70000, 0x8000, MEMMAP_USABLE, true, true, false, false);
@@ -298,20 +298,38 @@ void stivale_load(char *config, char *cmdline) {
                    stivale_hdr.stack);
 }
 
-pagemap_t stivale_build_pagemap(bool level5pg, bool unmap_null) {
+pagemap_t stivale_build_pagemap(bool level5pg, bool unmap_null, struct elf_range *ranges, size_t ranges_count) {
     pagemap_t pagemap = new_pagemap(level5pg ? 5 : 4);
     uint64_t higher_half_base = level5pg ? 0xff00000000000000 : 0xffff800000000000;
 
-    // Map 0 to 2GiB at 0xffffffff80000000
-    for (uint64_t i = 0; i < 0x80000000; i += 0x200000) {
-        map_page(pagemap, 0xffffffff80000000 + i, i, 0x03, true);
-    }
+    if (ranges_count == 0) {
+        // Map 0 to 2GiB at 0xffffffff80000000
+        for (uint64_t i = 0; i < 0x80000000; i += 0x200000) {
+            map_page(pagemap, 0xffffffff80000000 + i, i, 0x03, true);
+        }
+
+        // Sub 2MiB mappings
+        for (uint64_t i = 0; i < 0x200000; i += 0x1000) {
+            if (!(i == 0 && unmap_null))
+                map_page(pagemap, i, i, 0x03, false);
+            map_page(pagemap, higher_half_base + i, i, 0x03, false);
+        }
+    } else {
+        for (size_t i = 0; i < ranges_count; i++) {
+            uint64_t virt = ranges[i].base;
+            uint64_t phys = virt;
 
-    // Sub 2MiB mappings
-    for (uint64_t i = 0; i < 0x200000; i += 0x1000) {
-        if (!(i == 0 && unmap_null))
-            map_page(pagemap, i, i, 0x03, false);
-        map_page(pagemap, higher_half_base + i, i, 0x03, false);
+            if (phys & ((uint64_t)1 << 63))
+                phys -= FIXED_HIGHER_HALF_OFFSET_64;
+
+            uint64_t pf = VMM_FLAG_PRESENT |
+                (ranges[i].permissions & ELF_PF_X ? 0 : VMM_FLAG_NOEXEC) |
+                (ranges[i].permissions & ELF_PF_W ? VMM_FLAG_WRITE : 0);
+
+            for (uint64_t j = 0; j < ranges[i].length; j += 0x1000) {
+                map_page(pagemap, virt + j, phys + j, pf, false);
+            }
+        }
     }
 
     // Map 2MiB to 4GiB at higher half base and 0
diff --git a/stage23/protos/stivale.h b/stage23/protos/stivale.h
index e09708cb..b0ea8809 100644
--- a/stage23/protos/stivale.h
+++ b/stage23/protos/stivale.h
@@ -4,12 +4,13 @@
 #include <stdbool.h>
 #include <stdint.h>
 #include <mm/vmm.h>
+#include <lib/elf.h>
 
 void stivale_load(char *config, char *cmdline);
 
 bool stivale_load_by_anchor(void **_anchor, const char *magic,
                             uint8_t *file, uint64_t filesize);
-pagemap_t stivale_build_pagemap(bool level5pg, bool unmap_null);
+pagemap_t stivale_build_pagemap(bool level5pg, bool unmap_null, struct elf_range *ranges, size_t ranges_count);
 __attribute__((noreturn)) void stivale_spinup(
                  int bits, bool level5pg, pagemap_t *pagemap,
                  uint64_t entry_point, uint64_t stivale_struct, uint64_t stack);
diff --git a/stage23/protos/stivale2.c b/stage23/protos/stivale2.c
index af06d931..2cc8077b 100644
--- a/stage23/protos/stivale2.c
+++ b/stage23/protos/stivale2.c
@@ -480,7 +480,7 @@ skip_modeset:;
 
     pagemap_t pagemap = {0};
     if (bits == 64)
-        pagemap = stivale_build_pagemap(want_5lv, unmap_null);
+        pagemap = stivale_build_pagemap(want_5lv, unmap_null, NULL, 0);
 
 #if uefi == 1
     efi_exit_boot_services();
tab: 248 wrap: offon