:: commit 9f74289bd38415d9360f08690fce5f1bf992a694

mintsuki <mintsuki@protonmail.com> — 2021-10-28 23:15

parents: 2138fe42a4

stivale2: Introduce fully virtual kernel mappings

diff --git a/stage23/lib/elf.c b/stage23/lib/elf.c
index 360b40c2..80af3823 100644
--- a/stage23/lib/elf.c
+++ b/stage23/lib/elf.c
@@ -430,7 +430,7 @@ static void elf64_get_ranges(uint8_t *elf, uint64_t slide, bool use_paddr, struc
     *_ranges = ranges;
 }
 
-int elf64_load(uint8_t *elf, uint64_t *entry_point, uint64_t *top, uint64_t *_slide, uint32_t alloc_type, bool kaslr, bool use_paddr, struct elf_range **ranges, uint64_t *ranges_count) {
+int elf64_load(uint8_t *elf, uint64_t *entry_point, uint64_t *top, uint64_t *_slide, uint32_t alloc_type, bool kaslr, bool use_paddr, struct elf_range **ranges, uint64_t *ranges_count, bool fully_virtual, uint64_t *physical_base, uint64_t *virtual_base) {
     struct elf64_hdr hdr;
     memcpy(&hdr, elf + (0), sizeof(struct elf64_hdr));
 
@@ -457,6 +457,41 @@ int elf64_load(uint8_t *elf, uint64_t *entry_point, uint64_t *top, uint64_t *_sl
 
     uint64_t max_align = elf64_max_align(elf);
 
+    uint64_t base_load_addr;
+
+    if (fully_virtual) {
+        simulation = false;
+
+        uint64_t min_vaddr = (uint64_t)-1;
+        uint64_t max_vaddr = 0;
+        for (uint16_t i = 0; i < hdr.ph_num; i++) {
+            struct elf64_phdr phdr;
+            memcpy(&phdr, elf + (hdr.phoff + i * sizeof(struct elf64_phdr)),
+                       sizeof(struct elf64_phdr));
+
+            if (phdr.p_type != PT_LOAD)
+                continue;
+
+            if (phdr.p_type != PT_LOAD)
+                continue;
+
+            if (phdr.p_vaddr < min_vaddr) {
+                min_vaddr = phdr.p_vaddr;
+            }
+
+            if (phdr.p_vaddr + phdr.p_memsz > max_vaddr) {
+                max_vaddr = phdr.p_vaddr + phdr.p_memsz;
+            }
+        }
+
+        uint64_t image_size = max_vaddr - min_vaddr;
+
+        base_load_addr = (uintptr_t)ext_mem_alloc_type_aligned(image_size, alloc_type, max_align);
+
+        *physical_base = base_load_addr;
+        *virtual_base = min_vaddr;
+    }
+
     if (!elf64_is_relocatable(elf, &hdr)) {
         simulation = false;
         goto final;
@@ -490,6 +525,10 @@ final:
             if (load_addr & ((uint64_t)1 << 63)) {
                 higher_half = true;
                 load_addr -= FIXED_HIGHER_HALF_OFFSET_64;
+
+                if (fully_virtual) {
+                    load_addr += base_load_addr;
+                }
             }
         }
 
@@ -497,7 +536,9 @@ final:
             panic("elf: Higher half executable trying to load too high");
         }
 
-        load_addr += slide;
+        if (!fully_virtual) {
+            load_addr += slide;
+        }
 
         uint64_t this_top = load_addr + phdr.p_memsz;
 
@@ -522,7 +563,8 @@ final:
             mem_size = phdr.p_memsz;
         }
 
-        if (!memmap_alloc_range((size_t)mem_base, (size_t)mem_size, alloc_type, true, false, simulation, false)) {
+        if (!fully_virtual &&
+            !memmap_alloc_range((size_t)mem_base, (size_t)mem_size, alloc_type, true, false, simulation, false)) {
             if (++try_count == max_simulated_tries || simulation == false) {
                 panic("elf: Failed to allocate necessary memory ranges");
             }
diff --git a/stage23/lib/elf.h b/stage23/lib/elf.h
index 2d300f11..8db57f91 100644
--- a/stage23/lib/elf.h
+++ b/stage23/lib/elf.h
@@ -27,7 +27,7 @@ struct elf_section_hdr_info {
 
 int elf_bits(uint8_t *elf);
 
-int elf64_load(uint8_t *elf, uint64_t *entry_point, uint64_t *top, uint64_t *_slide, uint32_t alloc_type, bool kaslr, bool use_paddr, struct elf_range **ranges, uint64_t *ranges_count);
+int elf64_load(uint8_t *elf, uint64_t *entry_point, uint64_t *top, uint64_t *_slide, uint32_t alloc_type, bool kaslr, bool use_paddr, struct elf_range **ranges, uint64_t *ranges_count, bool fully_virtual, uint64_t *physical_base, uint64_t *virtual_base);
 int elf64_load_section(uint8_t *elf, void *buffer, const char *name, size_t limit, uint64_t slide);
 struct elf_section_hdr_info* elf64_section_hdr_info(uint8_t *elf);
 
diff --git a/stage23/mm/pmm.h b/stage23/mm/pmm.h
index fce753e6..5426e334 100644
--- a/stage23/mm/pmm.h
+++ b/stage23/mm/pmm.h
@@ -46,6 +46,7 @@ void pmm_randomise_memory(void);
 
 void *ext_mem_alloc(size_t count);
 void *ext_mem_alloc_type(size_t count, uint32_t type);
+void *ext_mem_alloc_type_aligned(size_t count, uint32_t type, size_t alignment);
 
 void *conv_mem_alloc(size_t count);
 
diff --git a/stage23/mm/pmm.s2.c b/stage23/mm/pmm.s2.c
index 984d68f6..f023c7f2 100644
--- a/stage23/mm/pmm.s2.c
+++ b/stage23/mm/pmm.s2.c
@@ -574,9 +574,13 @@ void *ext_mem_alloc(size_t count) {
     return ext_mem_alloc_type(count, MEMMAP_BOOTLOADER_RECLAIMABLE);
 }
 
-// Allocate memory top down, hopefully without bumping into kernel or modules
 void *ext_mem_alloc_type(size_t count, uint32_t type) {
-    count = ALIGN_UP(count, 4096);
+    return ext_mem_alloc_type_aligned(count, type, 4096);
+}
+
+// Allocate memory top down, hopefully without bumping into kernel or modules
+void *ext_mem_alloc_type_aligned(size_t count, uint32_t type, size_t alignment) {
+    count = ALIGN_UP(count, alignment);
 
     if (allocations_disallowed)
         panic("Memory allocations disallowed");
@@ -595,7 +599,7 @@ void *ext_mem_alloc_type(size_t count, uint32_t type) {
                 continue;
         }
 
-        int64_t alloc_base = ALIGN_DOWN(entry_top - (int64_t)count, 4096);
+        int64_t alloc_base = ALIGN_DOWN(entry_top - (int64_t)count, alignment);
 
         // This entry is too small for us.
         if (alloc_base < entry_base)
diff --git a/stage23/protos/multiboot1.c b/stage23/protos/multiboot1.c
index ce3582e2..3a67d274 100644
--- a/stage23/protos/multiboot1.c
+++ b/stage23/protos/multiboot1.c
@@ -104,7 +104,7 @@ void multiboot1_load(char *config, char *cmdline) {
                 break;
             case 64: {
                 uint64_t e, t;
-                if (elf64_load(kernel, &e, &t, NULL, MEMMAP_KERNEL_AND_MODULES, false, true, NULL, NULL))
+                if (elf64_load(kernel, &e, &t, NULL, MEMMAP_KERNEL_AND_MODULES, false, true, NULL, NULL, false, NULL, NULL))
                     panic("multiboot1: ELF64 load failure");
                 entry_point = e;
                 kernel_top = t;
diff --git a/stage23/protos/multiboot2.c b/stage23/protos/multiboot2.c
index 3b0d29ec..efcc8f74 100644
--- a/stage23/protos/multiboot2.c
+++ b/stage23/protos/multiboot2.c
@@ -225,7 +225,7 @@ void multiboot2_load(char *config, char* cmdline) {
 
                 break;
             case 64: {
-                if (elf64_load(kernel, &e, &t, NULL, MEMMAP_KERNEL_AND_MODULES, false, true, NULL, NULL))
+                if (elf64_load(kernel, &e, &t, NULL, MEMMAP_KERNEL_AND_MODULES, false, true, NULL, NULL, false, NULL, NULL))
                     panic("multiboot2: ELF64 load failure");
 
                 break;
diff --git a/stage23/protos/stivale.c b/stage23/protos/stivale.c
index a29ca68b..c302401d 100644
--- a/stage23/protos/stivale.c
+++ b/stage23/protos/stivale.c
@@ -127,7 +127,7 @@ void stivale_load(char *config, char *cmdline) {
             if (!loaded_by_anchor) {
                 if (elf64_load(kernel, &entry_point, NULL, &slide,
                                STIVALE_MMAP_KERNEL_AND_MODULES, kaslr, false,
-                               NULL, NULL))
+                               NULL, NULL, false, NULL, NULL))
                     panic("stivale: ELF64 load failure");
 
                 ret = elf64_load_section(kernel, &stivale_hdr, ".stivalehdr",
@@ -300,7 +300,7 @@ void stivale_load(char *config, char *cmdline) {
 
     pagemap_t pagemap = {0};
     if (bits == 64)
-        pagemap = stivale_build_pagemap(want_5lv, false, NULL, 0);
+        pagemap = stivale_build_pagemap(want_5lv, false, NULL, 0, false, 0);
 
     // Reserve 32K at 0x70000
     memmap_alloc_range(0x70000, 0x8000, MEMMAP_USABLE, true, true, false, false);
@@ -324,7 +324,8 @@ void stivale_load(char *config, char *cmdline) {
                    stivale_hdr.stack, false);
 }
 
-pagemap_t stivale_build_pagemap(bool level5pg, bool unmap_null, struct elf_range *ranges, size_t ranges_count) {
+pagemap_t stivale_build_pagemap(bool level5pg, bool unmap_null, struct elf_range *ranges, size_t ranges_count,
+                                bool want_fully_virtual, uint64_t physical_base) {
     pagemap_t pagemap = new_pagemap(level5pg ? 5 : 4);
     uint64_t higher_half_base = level5pg ? 0xff00000000000000 : 0xffff800000000000;
 
@@ -336,10 +337,14 @@ pagemap_t stivale_build_pagemap(bool level5pg, bool unmap_null, struct elf_range
     } 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)) {
-                phys -= FIXED_HIGHER_HALF_OFFSET_64;
+            uint64_t phys;
+
+            if (virt & ((uint64_t)1 << 63)) {
+                if (want_fully_virtual) {
+                    phys = physical_base + (virt - FIXED_HIGHER_HALF_OFFSET_64);
+                } else {
+                    phys = virt - FIXED_HIGHER_HALF_OFFSET_64;
+                }
             } else {
                 panic("stivale2: Protected memory ranges are only supported for higher half kernels");
             }
diff --git a/stage23/protos/stivale.h b/stage23/protos/stivale.h
index 5379dc3b..b4da17e0 100644
--- a/stage23/protos/stivale.h
+++ b/stage23/protos/stivale.h
@@ -10,7 +10,8 @@ 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, struct elf_range *ranges, size_t ranges_count);
+pagemap_t stivale_build_pagemap(bool level5pg, bool unmap_null, struct elf_range *ranges, size_t ranges_count,
+                                bool want_fully_virtual, uint64_t physical_base);
 __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 90675f56..d91aa686 100644
--- a/stage23/protos/stivale2.c
+++ b/stage23/protos/stivale2.c
@@ -32,22 +32,36 @@
 
 struct stivale2_struct stivale2_struct = {0};
 
-inline static size_t get_phys_addr(uint64_t addr) {
-    if (addr & ((uint64_t)1 << 63))
-        return addr - FIXED_HIGHER_HALF_OFFSET_64;
-    return addr;
-}
+#define get_phys_addr(addr) ({ \
+    uintptr_t r1; \
+    if ((addr) & ((uint64_t)1 << 63)) { \
+        if (want_fully_virtual) { \
+            r1 = physical_base + ((addr) - virtual_base); \
+        } else { \
+            r1 = (addr) - FIXED_HIGHER_HALF_OFFSET_64; \
+        } \
+    } else { \
+        r1 = addr; \
+    } \
+    r1; \
+})
 
-static void *get_tag(struct stivale2_header *s, uint64_t id) {
-    struct stivale2_tag *tag = (void*)get_phys_addr(s->tags);
-    for (;;) {
-        if (tag == NULL)
-            return NULL;
-        if (tag->identifier == id)
-            return tag;
-        tag = (void*)get_phys_addr(tag->next);
-    }
-}
+#define get_tag(s, id) ({ \
+    void *r; \
+    struct stivale2_tag *tag = (void *)get_phys_addr((s)->tags); \
+    for (;;) { \
+        if (tag == NULL) { \
+            r = NULL; \
+            break; \
+        } \
+        if (tag->identifier == (id)) { \
+            r = tag; \
+            break; \
+        } \
+        tag = (void *)get_phys_addr(tag->next); \
+    } \
+    r; \
+})
 
 #define append_tag(S, TAG) ({                              \
     (TAG)->next = (S)->tags;                               \
@@ -110,6 +124,9 @@ void stivale2_load(char *config, char *cmdline, bool pxe, void *efi_system_table
     }
 
     bool want_pmrs = false;
+    bool want_fully_virtual = false;
+
+    uint64_t physical_base, virtual_base;
 
     int ret = 0;
     switch (bits) {
@@ -141,10 +158,15 @@ void stivale2_load(char *config, char *cmdline, bool pxe, void *efi_system_table
                     want_pmrs = true;
                 }
 
+                if (want_pmrs && (stivale2_hdr.flags & (1 << 3))) {
+                    want_fully_virtual = true;
+                }
+
                 if (elf64_load(kernel, &entry_point, NULL, &slide,
                                STIVALE2_MMAP_KERNEL_AND_MODULES, kaslr, false,
                                want_pmrs ? &ranges : NULL,
-                               want_pmrs ? &ranges_count : NULL))
+                               want_pmrs ? &ranges_count : NULL,
+                               want_fully_virtual, &physical_base, &virtual_base))
                     panic("stivale2: ELF64 load failure");
 
                 ret = elf64_load_section(kernel, &stivale2_hdr, ".stivale2hdr",
@@ -579,6 +601,23 @@ have_tm_tag:;
     }
     }
 
+    //////////////////////////////////////////////
+    // Create PMRs struct tag
+    //////////////////////////////////////////////
+    {
+    if (want_fully_virtual) {
+        struct stivale2_struct_tag_kernel_base_address *tag =
+            ext_mem_alloc(sizeof(struct stivale2_struct_tag_kernel_base_address));
+
+        tag->tag.identifier = STIVALE2_STRUCT_TAG_KERNEL_BASE_ADDRESS_ID;
+
+        tag->physical_base_address = physical_base;
+        tag->virtual_base_address = virtual_base;
+
+        append_tag(&stivale2_struct, (struct stivale2_tag *)tag);
+    }
+    }
+
     //////////////////////////////////////////////
     // Create EFI system table struct tag
     //////////////////////////////////////////////
@@ -599,7 +638,8 @@ have_tm_tag:;
     if (bits == 64)
         pagemap = stivale_build_pagemap(want_5lv, unmap_null,
                                         want_pmrs ? ranges : NULL,
-                                        want_pmrs ? ranges_count : 0);
+                                        want_pmrs ? ranges_count : 0,
+                                        want_fully_virtual, physical_base);
 
 #if uefi == 1
     efi_exit_boot_services();
diff --git a/test/Makefile b/test/Makefile
index 808862cc..45cb828f 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -1,5 +1,5 @@
 CC              = cc
-CFLAGS          = -O2
+CFLAGS          = -O2 -g
 LDFLAGS         =
 LD              = ld
 QEMU            = qemu-system-x86_64
@@ -9,10 +9,7 @@ INTERNALLDFLAGS :=         \
 	-Tlinker.ld            \
 	-nostdlib              \
 	-zmax-page-size=0x1000 \
-	-static                \
-	-pie                   \
-	--no-dynamic-linker    \
-	-ztext
+	-static
 
 INTERNALCFLAGS  :=       \
 	-I../stivale         \
@@ -20,14 +17,15 @@ INTERNALCFLAGS  :=       \
 	-std=gnu11           \
 	-ffreestanding       \
 	-fno-stack-protector \
-	-fno-pic -fpie       \
+	-fno-pic -fno-pie       \
 	-mabi=sysv           \
 	-mno-80387           \
 	-mno-mmx             \
 	-mno-3dnow           \
 	-mno-sse             \
 	-mno-sse2            \
-	-mno-red-zone
+	-mno-red-zone \
+	-mcmodel=kernel
 
 all: test.elf
 
diff --git a/test/linker.ld b/test/linker.ld
index 8c2ea098..18222830 100644
--- a/test/linker.ld
+++ b/test/linker.ld
@@ -6,12 +6,11 @@ PHDRS
     text    PT_LOAD    FLAGS((1 << 0) | (1 << 2)) ; /* Execute + Read */
     rodata  PT_LOAD    FLAGS((1 << 2)) ;            /* Read only */
     data    PT_LOAD    FLAGS((1 << 1) | (1 << 2)) ; /* Write + Read */
-    dynamic PT_DYNAMIC FLAGS((1 << 1) | (1 << 2)) ; /* Dynamic segment needed for PIE */
 }
 
 SECTIONS
 {
-    . = 0xffffffff80200000;
+    . = 0xffffffff80000000;
 
     .text : {
         *(.text*)
@@ -37,10 +36,6 @@ SECTIONS
         *(.data*)
     } :data
 
-    .dynamic : {
-        *(.dynamic)
-    } :data :dynamic
-
     .bss : {
         *(COMMON)
         *(.bss*)
diff --git a/test/stivale2.c b/test/stivale2.c
index a69577a2..8a3d19ae 100644
--- a/test/stivale2.c
+++ b/test/stivale2.c
@@ -40,7 +40,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) | (1 << 2),
+    .flags       = (1 << 1) | (1 << 2) | (1 << 3),
     .tags        = (uint64_t)&any_video_request
 };
 
@@ -204,6 +204,13 @@ void stivale2_main(struct stivale2_struct *info) {
                 }
                 break;
             }
+            case STIVALE2_STRUCT_TAG_KERNEL_BASE_ADDRESS_ID: {
+                struct stivale2_struct_tag_kernel_base_address *t = (void *)tag;
+                e9_puts("Kernel base address:");
+                e9_printf("\tPhysical base address: %x", t->physical_base_address);
+                e9_printf("\tVirtual base address:  %x", t->virtual_base_address);
+                break;
+            }
             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