:: commit de3c7eed679fde24060434dcc369ea22836d9654

mintsuki <mintsuki@protonmail.com> — 2021-07-15 14:20

parents: 79b8c38c8c

stivale2: Implement PMRs

diff --git a/stage23/lib/elf.c b/stage23/lib/elf.c
index 7eabee7c..5b7e3913 100644
--- a/stage23/lib/elf.c
+++ b/stage23/lib/elf.c
@@ -8,7 +8,7 @@
 #include <mm/pmm.h>
 #include <fs/file.h>
 
-#define KASLR_SLIDE_BITMASK ((uintptr_t)0x3ffff000)
+#define KASLR_SLIDE_BITMASK ((uintptr_t)0x8ffff000)
 
 #define PT_LOAD     0x00000001
 #define PT_INTERP   0x00000003
@@ -420,7 +420,7 @@ int elf64_load(uint8_t *elf, uint64_t *entry_point, uint64_t *top, uint64_t *_sl
 
 again:
     if (kaslr)
-        slide = rand64() & KASLR_SLIDE_BITMASK;
+        slide = (rand64() & KASLR_SLIDE_BITMASK) & (min_align - 1);
 
 final:
     if (top)
diff --git a/stage23/protos/stivale.32.c b/stage23/protos/stivale.32.c
index e104aae0..ad9c1a23 100644
--- a/stage23/protos/stivale.32.c
+++ b/stage23/protos/stivale.32.c
@@ -4,7 +4,7 @@
 #include <mm/vmm.h>
 
 __attribute__((noreturn)) void stivale_spinup_32(
-                 int bits, bool level5pg, uint32_t pagemap_top_lv,
+                 int bits, bool level5pg, bool enable_nx, uint32_t pagemap_top_lv,
                  uint32_t entry_point_lo, uint32_t entry_point_hi,
                  uint32_t stivale_struct_lo, uint32_t stivale_struct_hi,
                  uint32_t stack_lo, uint32_t stack_hi) {
@@ -15,6 +15,16 @@ __attribute__((noreturn)) void stivale_spinup_32(
     };
 
     if (bits == 64) {
+        if (enable_nx) {
+            asm volatile (
+                "movl $0xc0000080, %%ecx\n\t"
+                "rdmsr\n\t"
+                "btsl $11, %%eax\n\t"
+                "wrmsr\n\t"
+                ::: "eax", "ecx", "edx", "memory"
+            );
+        }
+
         if (level5pg) {
             // Enable CR4.LA57
             asm volatile (
diff --git a/stage23/protos/stivale.c b/stage23/protos/stivale.c
index 347d1a3f..aa2b0284 100644
--- a/stage23/protos/stivale.c
+++ b/stage23/protos/stivale.c
@@ -295,7 +295,7 @@ void stivale_load(char *config, char *cmdline) {
 
     stivale_spinup(bits, want_5lv, &pagemap,
                    entry_point, REPORTED_ADDR((uint64_t)(uintptr_t)&stivale_struct),
-                   stivale_hdr.stack);
+                   stivale_hdr.stack, false);
 }
 
 pagemap_t stivale_build_pagemap(bool level5pg, bool unmap_null, struct elf_range *ranges, size_t ranges_count) {
@@ -307,20 +307,16 @@ pagemap_t stivale_build_pagemap(bool level5pg, bool unmap_null, struct elf_range
         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;
 
-            if (phys & ((uint64_t)1 << 63))
+            if (phys & ((uint64_t)1 << 63)) {
                 phys -= FIXED_HIGHER_HALF_OFFSET_64;
+            } else {
+                panic("stivale2: Protected memory ranges are only supported for higher half kernels");
+            }
 
             uint64_t pf = VMM_FLAG_PRESENT |
                 (ranges[i].permissions & ELF_PF_X ? 0 : VMM_FLAG_NOEXEC) |
@@ -332,6 +328,13 @@ pagemap_t stivale_build_pagemap(bool level5pg, bool unmap_null, struct elf_range
         }
     }
 
+    // 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);
+    }
+
     // Map 2MiB to 4GiB at higher half base and 0
     for (uint64_t i = 0x200000; i < 0x100000000; i += 0x200000) {
         map_page(pagemap, i, i, 0x03, true);
@@ -382,7 +385,8 @@ __attribute__((noreturn)) void stivale_spinup_32(
 
 __attribute__((noreturn)) void stivale_spinup(
                  int bits, bool level5pg, pagemap_t *pagemap,
-                 uint64_t entry_point, uint64_t _stivale_struct, uint64_t stack) {
+                 uint64_t entry_point, uint64_t _stivale_struct, uint64_t stack,
+                 bool enable_nx) {
 #if bios == 1
     if (bits == 64) {
         // If we're going 64, we might as well call this BIOS interrupt
@@ -395,11 +399,15 @@ __attribute__((noreturn)) void stivale_spinup(
     }
 #endif
 
+    if (enable_nx) {
+        vmm_assert_nx();
+    }
+
     pic_mask_all();
     pic_flush();
 
     common_spinup(stivale_spinup_32, 9,
-        bits, level5pg, (uint32_t)(uintptr_t)pagemap->top_level,
+        bits, level5pg, enable_nx, (uint32_t)(uintptr_t)pagemap->top_level,
         (uint32_t)entry_point, (uint32_t)(entry_point >> 32),
         (uint32_t)_stivale_struct, (uint32_t)(_stivale_struct >> 32),
         (uint32_t)stack, (uint32_t)(stack >> 32));
diff --git a/stage23/protos/stivale.h b/stage23/protos/stivale.h
index b0ea8809..5379dc3b 100644
--- a/stage23/protos/stivale.h
+++ b/stage23/protos/stivale.h
@@ -13,6 +13,7 @@ bool stivale_load_by_anchor(void **_anchor, const char *magic,
 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);
+                 uint64_t entry_point, uint64_t stivale_struct, uint64_t stack,
+                 bool enable_nx);
 
 #endif
diff --git a/stage23/protos/stivale2.c b/stage23/protos/stivale2.c
index 2cc8077b..9b7c75d4 100644
--- a/stage23/protos/stivale2.c
+++ b/stage23/protos/stivale2.c
@@ -81,6 +81,9 @@ void stivale2_load(char *config, char *cmdline, bool pxe, void *efi_system_table
     uint64_t slide = 0;
     uint64_t entry_point = 0;
 
+    struct elf_range *ranges;
+    uint64_t ranges_count;
+
     uint8_t *kernel = freadall(kernel_file, STIVALE2_MMAP_BOOTLOADER_RECLAIMABLE);
     int bits = elf_bits(kernel);
     bool loaded_by_anchor = false;
@@ -99,6 +102,8 @@ void stivale2_load(char *config, char *cmdline, bool pxe, void *efi_system_table
         loaded_by_anchor = true;
     }
 
+    bool want_pmrs = false;
+
     int ret = 0;
     switch (bits) {
         case 64: {
@@ -114,9 +119,25 @@ void stivale2_load(char *config, char *cmdline, bool pxe, void *efi_system_table
             }
 
             if (!loaded_by_anchor) {
+                ret = elf64_load_section(kernel, &stivale2_hdr, ".stivale2hdr",
+                                         sizeof(struct stivale2_header), 0);
+                if (ret) {
+                    goto failed_to_load_header_section;
+                }
+
+                if ((stivale2_hdr.flags & (1 << 2))) {
+                    if (bits == 32) {
+                        panic("stivale2: PMRs are not supported for 32-bit kernels");
+                    } else if (loaded_by_anchor) {
+                        panic("stivale2: PMRs are not supported for anchored kernels");
+                    }
+                    want_pmrs = true;
+                }
+
                 if (elf64_load(kernel, &entry_point, NULL, &slide,
                                STIVALE2_MMAP_KERNEL_AND_MODULES, kaslr, false,
-                               NULL, NULL))
+                               want_pmrs ? &ranges : NULL,
+                               want_pmrs ? &ranges_count : NULL))
                     panic("stivale2: ELF64 load failure");
 
                 ret = elf64_load_section(kernel, &stivale2_hdr, ".stivale2hdr",
@@ -142,6 +163,7 @@ void stivale2_load(char *config, char *cmdline, bool pxe, void *efi_system_table
 
     printv("stivale2: %u-bit kernel detected\n", bits);
 
+failed_to_load_header_section:
     switch (ret) {
         case 1:
             panic("stivale2: File is not a valid ELF.");
@@ -462,6 +484,25 @@ skip_modeset:;
     (void)pxe;
 #endif
 
+    //////////////////////////////////////////////
+    // Create PMRs struct tag
+    //////////////////////////////////////////////
+    {
+    if (want_pmrs) {
+        struct stivale2_struct_tag_pmrs *tag =
+            ext_mem_alloc(sizeof(struct stivale2_struct_tag_pmrs)
+                          + ranges_count * sizeof(struct stivale2_pmr));
+
+        tag->tag.identifier = STIVALE2_STRUCT_TAG_PMRS_ID;
+
+        tag->entries = ranges_count;
+
+        memcpy(tag->pmrs, ranges, ranges_count * sizeof(struct stivale2_pmr));
+
+        append_tag(&stivale2_struct, (struct stivale2_tag *)tag);
+    }
+    }
+
     //////////////////////////////////////////////
     // Create EFI system table struct tag
     //////////////////////////////////////////////
@@ -480,7 +521,9 @@ skip_modeset:;
 
     pagemap_t pagemap = {0};
     if (bits == 64)
-        pagemap = stivale_build_pagemap(want_5lv, unmap_null, NULL, 0);
+        pagemap = stivale_build_pagemap(want_5lv, unmap_null,
+                                        want_pmrs ? ranges : NULL,
+                                        want_pmrs ? ranges_count : 0);
 
 #if uefi == 1
     efi_exit_boot_services();
@@ -499,7 +542,7 @@ skip_modeset:;
         smp_info = init_smp(sizeof(struct stivale2_struct_tag_smp), (void **)&tag,
                             &cpu_count, &bsp_lapic_id,
                             bits == 64, want_5lv,
-                            pagemap, smp_hdr_tag->flags & 1);
+                            pagemap, smp_hdr_tag->flags & 1, want_pmrs);
 
         if (smp_info != NULL) {
             tag->tag.identifier = STIVALE2_STRUCT_TAG_SMP_ID;
@@ -557,5 +600,5 @@ skip_modeset:;
 
     stivale_spinup(bits, want_5lv, &pagemap, entry_point,
                    REPORTED_ADDR((uint64_t)(uintptr_t)&stivale2_struct),
-                   stivale2_hdr.stack);
+                   stivale2_hdr.stack, want_pmrs);
 }
diff --git a/stage23/sys/smp.c b/stage23/sys/smp.c
index 5abeb993..79f463e9 100644
--- a/stage23/sys/smp.c
+++ b/stage23/sys/smp.c
@@ -58,7 +58,7 @@ struct trampoline_passed_info {
 static bool smp_start_ap(uint32_t lapic_id, struct gdtr *gdtr,
                          struct smp_information *info_struct,
                          bool longmode, bool lv5, uint32_t pagemap,
-                         bool x2apic) {
+                         bool x2apic, bool nx) {
     size_t trampoline_size = (size_t)_binary_smp_trampoline_bin_end
                            - (size_t)_binary_smp_trampoline_bin_start;
 
@@ -81,6 +81,7 @@ static bool smp_start_ap(uint32_t lapic_id, struct gdtr *gdtr,
     passed_info->smp_tpl_pagemap     = pagemap;
     passed_info->smp_tpl_target_mode = ((uint32_t)x2apic << 2)
                         | ((uint32_t)lv5 << 1)
+                        | ((uint32_t)nx << 3)
                         | (uint32_t)longmode;
     passed_info->smp_tpl_gdt         = *gdtr;
     passed_info->smp_tpl_booted_flag = 0;
@@ -122,7 +123,8 @@ struct smp_information *init_smp(size_t    header_hack_size,
                                  bool      longmode,
                                  bool      lv5,
                                  pagemap_t pagemap,
-                                 bool      x2apic) {
+                                 bool      x2apic,
+                                 bool      nx) {
     if (!lapic_check())
         return NULL;
 
@@ -227,7 +229,7 @@ struct smp_information *init_smp(size_t    header_hack_size,
                 // Try to start the AP
                 if (!smp_start_ap(lapic->lapic_id, &gdtr, info_struct,
                                   longmode, lv5, (uintptr_t)pagemap.top_level,
-                                  x2apic)) {
+                                  x2apic, nx)) {
                     print("smp: FAILED to bring-up AP\n");
                     continue;
                 }
@@ -264,7 +266,7 @@ struct smp_information *init_smp(size_t    header_hack_size,
                 // Try to start the AP
                 if (!smp_start_ap(x2lapic->x2apic_id, &gdtr, info_struct,
                                   longmode, lv5, (uintptr_t)pagemap.top_level,
-                                  true)) {
+                                  true, nx)) {
                     print("smp: FAILED to bring-up AP\n");
                     continue;
                 }
diff --git a/stage23/sys/smp.h b/stage23/sys/smp.h
index 0e36df14..efc0f7c6 100644
--- a/stage23/sys/smp.h
+++ b/stage23/sys/smp.h
@@ -21,6 +21,7 @@ struct smp_information *init_smp(size_t    header_hack_size,
                                  bool      longmode,
                                  bool      lv5,
                                  pagemap_t pagemap,
-                                 bool      x2apic);
+                                 bool      x2apic,
+                                 bool      nx);
 
 #endif
diff --git a/stage23/sys/smp_trampoline.real b/stage23/sys/smp_trampoline.real
index 86968c20..fa8b173e 100644
--- a/stage23/sys/smp_trampoline.real
+++ b/stage23/sys/smp_trampoline.real
@@ -56,6 +56,11 @@ smp_trampoline:
     bts eax, 5
     mov cr4, eax
 
+    mov ecx, 0xc0000080
+    mov eax, 0x100
+    xor edx, edx
+    wrmsr
+
     test dword [ebx + passed_info.target_mode], (1 << 1)
     jz .no5lv
 
@@ -67,11 +72,6 @@ smp_trampoline:
     mov eax, dword [ebx + passed_info.pagemap]
     mov cr3, eax
 
-    mov ecx, 0xc0000080
-    mov eax, 0x100
-    xor edx, edx
-    wrmsr
-
     mov eax, cr0
     bts eax, 31
     mov cr0, eax
@@ -91,6 +91,16 @@ smp_trampoline:
     mov gs, ax
     mov ss, ax
 
+    mov ebx, ebx
+    test dword [rbx + passed_info.target_mode], (1 << 3)
+    jz .nonx
+
+    mov ecx, 0xc0000080
+    rdmsr
+    bts eax, 11
+    wrmsr
+
+  .nonx:
     jmp parking64
 
 bits 32
diff --git a/test/stivale2.c b/test/stivale2.c
index eef221d9..834c494c 100644
--- a/test/stivale2.c
+++ b/test/stivale2.c
@@ -42,7 +42,7 @@ __attribute__((section(".stivale2hdr"), used))
 struct stivale2_header header2 = {
     .entry_point = (uint64_t)stivale2_main,
     .stack       = (uintptr_t)stacks[0] + sizeof(stack),
-    .flags       = 1 << 1,
+    .flags       = (1 << 1) | (1 << 2),
     .tags        = (uint64_t)&framebuffer_request
 };
 
@@ -184,6 +184,15 @@ void stivale2_main(struct stivale2_struct *info) {
                 e9_printf("Kernel slide: %x", t->kernel_slide);
                 break;
             }
+            case STIVALE2_STRUCT_TAG_PMRS_ID: {
+                struct stivale2_struct_tag_pmrs *t = (struct stivale2_struct_tag_pmrs *)tag;
+                e9_puts("PMRs tag:");
+                e9_printf("\t%d ranges:", t->entries);
+                for (size_t i = 0; i < t->entries; i++) {
+                    e9_printf("\t\tBase: %x  Length: %x  Perms: %x",
+                          t->pmrs[i].base, t->pmrs[i].length, t->pmrs[i].permissions);
+                }
+            }
             case STIVALE2_STRUCT_TAG_SMP_ID: {
                 struct stivale2_struct_tag_smp *s = (struct stivale2_struct_tag_smp *)tag;
                 e9_puts("SMP tag:");
tab: 248 wrap: offon