:: commit b2e614e7c26c9792f24c7c7082c2d87f693f3736

mintsuki <mintsuki@protonmail.com> — 2022-03-12 18:40

parents: c2b1be81dc

limine: Introduce basic Limine protocol concept

diff --git a/common/limine.h b/common/limine.h
new file mode 100644
index 00000000..689d6359
--- /dev/null
+++ b/common/limine.h
@@ -0,0 +1,65 @@
+#ifndef _LIMINE_H
+#define _LIMINE_H 1
+
+#include <stdint.h>
+
+#ifdef LIMINE_NO_POINTERS
+#  define LIMINE_CHARPTR uint64_t
+#  define LIMINE_VOIDPTR uint64_t
+#  define LIMINE_VOIDPTRPTR uint64_t
+#else
+#  define LIMINE_CHARPTR char *
+#  define LIMINE_VOIDPTR void *
+#  define LIMINE_VOIDPTRPTR void **
+#endif
+
+#define LIMINE_MAGIC { 0xc7b1dd30df4c8b88, 0x0a82e883a194f07b }
+
+struct limine_header {
+    uint64_t magic[2];
+    LIMINE_VOIDPTR entry;
+    uint64_t features_count;
+    LIMINE_VOIDPTRPTR features;
+};
+
+// Boot info
+
+#define LIMINE_BOOT_INFO_REQUEST ((LIMINE_VOIDPTR) 1 )
+
+struct limine_boot_info_response {
+    uint64_t flags;
+    LIMINE_CHARPTR loader;
+};
+
+// Framebuffer
+
+#define LIMINE_FRAMEBUFFER_REQUEST ((LIMINE_VOIDPTR) 2 )
+
+struct limine_framebuffer_request {
+    LIMINE_VOIDPTR id;
+
+#define LIMINE_FRAMEBUFFER_PREFER_LFB 0
+#define LIMINE_FRAMEBUFFER_PREFER_TEXT 1
+#define LIMINE_FRAMEBUFFER_ENFORCE_PREFER (1 << 8)
+    uint64_t flags;
+
+    uint16_t width;
+    uint16_t height;
+    uint16_t bpp;
+
+    uint16_t unused;
+};
+
+// 5-level paging
+
+#define LIMINE_5_LEVEL_PAGING_REQUEST ((LIMINE_VOIDPTR) 3 )
+
+struct limine_5_level_paging_response {
+    uint64_t flags;
+};
+
+// PMRs
+
+#define LIMINE_PMR_REQUEST ((LIMINE_VOIDPTR) 4 )
+
+#endif
diff --git a/common/menu.c b/common/menu.c
index c2f4a4f4..4be1b45a 100644
--- a/common/menu.c
+++ b/common/menu.c
@@ -21,6 +21,7 @@
 #include <protos/chainload.h>
 #include <protos/multiboot1.h>
 #include <protos/multiboot2.h>
+#include <protos/limine.h>
 
 static char *menu_branding = NULL;
 static char *menu_branding_colour = NULL;
@@ -948,6 +949,8 @@ autodetect:
         ret = stivale_load(config, cmdline);
     } else if (!strcmp(proto, "stivale2")) {
         ret = stivale2_load(config, cmdline);
+    } else if (!strcmp(proto, "limine")) {
+        ret = limine_load(config, cmdline);
     } else if (!strcmp(proto, "linux")) {
         ret = linux_load(config, cmdline);
     } else if (!strcmp(proto, "multiboot1") || !strcmp(proto, "multiboot")) {
diff --git a/common/protos/limine.c b/common/protos/limine.c
new file mode 100644
index 00000000..7bdc2102
--- /dev/null
+++ b/common/protos/limine.c
@@ -0,0 +1,251 @@
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <config.h>
+#include <protos/stivale.h>
+#include <protos/stivale2.h>
+#include <lib/elf.h>
+#include <lib/blib.h>
+#include <lib/acpi.h>
+#include <lib/config.h>
+#include <lib/time.h>
+#include <lib/print.h>
+#include <lib/real.h>
+#include <lib/libc.h>
+#include <lib/gterm.h>
+#include <lib/uri.h>
+#include <sys/smp.h>
+#include <sys/cpu.h>
+#include <sys/gdt.h>
+#include <lib/fb.h>
+#include <lib/term.h>
+#include <sys/pic.h>
+#include <sys/lapic.h>
+#include <fs/file.h>
+#include <mm/pmm.h>
+#include <stivale2.h>
+#include <pxe/tftp.h>
+#include <drivers/edid.h>
+#include <drivers/vga_textmode.h>
+#include <lib/rand.h>
+#define LIMINE_NO_POINTERS
+#include <protos/limine.h>
+#include <limine.h>
+
+static uint64_t features_count, physical_base, virtual_base, slide, direct_map_offset;
+static uint64_t *features, *features_orig;
+
+static uint64_t reported_addr(void *addr) {
+    return (uint64_t)(uintptr_t)addr + direct_map_offset;
+}
+
+static uintptr_t get_phys_addr(uint64_t addr) {
+    return physical_base + (addr - virtual_base);
+}
+
+struct feature {
+    bool found;
+    size_t index;
+    void *request;
+};
+
+static struct feature get_feature(uint64_t id) {
+    for (size_t i = 0; i < features_count; i++) {
+        if (features[i] < 0xffffffff80000000 && features[i] == id) {
+            return (struct feature){
+                .found = true,
+                .index = i,
+                .request = NULL
+            };
+        } else {
+            uint64_t *id_ptr = (void *)get_phys_addr(features[i] + slide);
+            if (*id_ptr == id) {
+                return (struct feature){
+                    .found = true,
+                    .index = i,
+                    .request = (void *)id_ptr
+                };
+            }
+        }
+    }
+
+    return (struct feature){
+        .found = false,
+        .index = 0,
+        .request = NULL
+    };
+}
+
+#define FEAT_START do {
+#define FEAT_END } while (0);
+
+bool limine_load(char *config, char *cmdline) {
+    (void)cmdline;
+
+    uint32_t eax, ebx, ecx, edx;
+
+    char *kernel_path = config_get_value(config, 0, "KERNEL_PATH");
+    if (kernel_path == NULL)
+        panic(true, "limine: KERNEL_PATH not specified");
+
+    print("limine: Loading kernel `%s`...\n", kernel_path);
+
+    struct file_handle *kernel_file;
+    if ((kernel_file = uri_open(kernel_path)) == NULL)
+        panic(true, "limine: Failed to open kernel with path `%s`. Is the path correct?", kernel_path);
+
+    uint8_t *kernel = freadall(kernel_file, MEMMAP_BOOTLOADER_RECLAIMABLE);
+
+    size_t kernel_file_size = kernel_file->size;
+
+    //struct volume *kernel_volume = kernel_file->vol;
+
+    fclose(kernel_file);
+
+    // Search for header
+    struct limine_header *limine_header = NULL;
+    uint64_t limine_magic[2] = LIMINE_MAGIC;
+    for (size_t i = 0; i < kernel_file_size; i += 16) {
+        if (memcmp(kernel + i, limine_magic, 16) == 0) {
+            limine_header = (void *)(kernel + i);
+        }
+    }
+
+    if (limine_header == NULL) {
+        panic(true, "limine: Magic number not found");
+    }
+
+    printv("limine: Header found at %p\n", (size_t)limine_header - (size_t)kernel);
+
+    // Check if 64 bit CPU
+    if (!cpuid(0x80000001, 0, &eax, &ebx, &ecx, &edx) || !(edx & (1 << 29))) {
+        panic(true, "limine: This CPU does not support 64-bit mode.");
+    }
+
+    char *kaslr_s = config_get_value(config, 0, "KASLR");
+    bool kaslr = true;
+    if (kaslr_s != NULL && strcmp(kaslr_s, "no") == 0)
+        kaslr = false;
+
+    int bits = elf_bits(kernel);
+
+    if (bits == -1 || bits == 32) {
+        panic(true, "limine: Kernel in unrecognised format");
+    }
+
+    uint64_t entry_point = 0;
+    struct elf_range *ranges;
+    uint64_t ranges_count;
+
+    if (elf64_load(kernel, &entry_point, NULL, &slide,
+                   MEMMAP_KERNEL_AND_MODULES, kaslr, false,
+                   &ranges, &ranges_count,
+                   true, &physical_base, &virtual_base)) {
+        panic(true, "limine: ELF64 load failure");
+    }
+
+    if (limine_header->entry != 0) {
+        entry_point = limine_header->entry + slide;
+    }
+
+    printv("limine: Physical base: %X\n", physical_base);
+    printv("limine: Virtual base:  %X\n", virtual_base);
+    printv("limine: Slide:         %X\n", slide);
+    printv("limine: Entry point:   %X\n", entry_point);
+
+    // Prepare features
+
+    features_count = limine_header->features_count;
+    features_orig = (void *)get_phys_addr(limine_header->features + slide);
+
+    features = ext_mem_alloc(features_count * sizeof(uint64_t));
+    memcpy(features, features_orig, features_count * sizeof(uint64_t));
+
+    for (size_t i = 0; i < features_count; i++) {
+        features_orig[i] = 0;
+    }
+
+    printv("limine: Features count: %U\n", features_count);
+    printv("limine: Features list at %X (%p)\n", limine_header->features, features_orig);
+
+    // 5 level paging feature & HHDM slide
+    bool want_5lv;
+FEAT_START
+    // Check if 5-level paging is available
+    bool level5pg = false;
+    if (cpuid(0x00000007, 0, &eax, &ebx, &ecx, &edx) && (ecx & (1 << 16))) {
+        printv("limine: CPU has 5-level paging support\n");
+        level5pg = true;
+    }
+
+    struct feature lv5pg_feat = get_feature(LIMINE_5_LEVEL_PAGING_REQUEST);
+    want_5lv = lv5pg_feat.found && level5pg;
+
+    direct_map_offset = want_5lv ? 0xff00000000000000 : 0xffff800000000000;
+
+    if (kaslr) {
+        direct_map_offset += (rand64() & ~((uint64_t)0x40000000 - 1)) & 0xfffffffffff;
+    }
+
+    if (want_5lv) {
+        void *lv5pg_response = ext_mem_alloc(sizeof(struct limine_5_level_paging_response));
+        features_orig[lv5pg_feat.index] = reported_addr(lv5pg_response);
+    }
+FEAT_END
+
+    // Boot info feature
+FEAT_START
+    struct feature boot_info_feat = get_feature(LIMINE_BOOT_INFO_REQUEST);
+    if (boot_info_feat.found == false) {
+        break; // next feature
+    }
+
+    struct limine_boot_info_response *boot_info_response =
+        ext_mem_alloc(sizeof(struct limine_boot_info_response));
+
+    boot_info_response->loader = reported_addr("Limine " LIMINE_VERSION);
+
+    features_orig[boot_info_feat.index] = reported_addr(boot_info_response);
+FEAT_END
+
+    // Framebuffer feature
+FEAT_START
+    term_deinit();
+
+    struct fb_info *fb = NULL;
+    fb_init(fb, 0, 0, 0);
+FEAT_END
+
+    // Wrap-up stuff before memmap close
+    struct gdtr *local_gdt = ext_mem_alloc(sizeof(struct gdtr));
+    local_gdt->limit = gdt.limit;
+    uint64_t local_gdt_base = (uint64_t)gdt.ptr;
+    local_gdt_base += direct_map_offset;
+    local_gdt->ptr = local_gdt_base;
+#if defined (__i386__)
+    local_gdt->ptr_hi = local_gdt_base >> 32;
+#endif
+
+    void *stack = ext_mem_alloc(8192) + 8192;
+
+    pagemap_t pagemap = {0};
+    pagemap = stivale_build_pagemap(want_5lv, true, ranges, ranges_count, true,
+                                    physical_base, virtual_base, direct_map_offset);
+
+    // Memmap
+FEAT_START
+    size_t mmap_entries;
+    struct e820_entry_t *mmap = get_memmap(&mmap_entries);
+    (void)mmap;
+FEAT_END
+
+    // Final wrap-up
+#if uefi == 1
+    efi_exit_boot_services();
+#endif
+
+    stivale_spinup(64, want_5lv, &pagemap, entry_point, 0,
+                   reported_addr(stack), true, (uintptr_t)local_gdt);
+
+    __builtin_unreachable();
+}
diff --git a/common/protos/limine.h b/common/protos/limine.h
new file mode 100644
index 00000000..64bcdbae
--- /dev/null
+++ b/common/protos/limine.h
@@ -0,0 +1,8 @@
+#ifndef __PROTOS__LIMINE_H__
+#define __PROTOS__LIMINE_H__
+
+#include <stdbool.h>
+
+bool limine_load(char *config, char *cmdline);
+
+#endif
diff --git a/test/Makefile b/test/Makefile
index 0c509eaa..39e273c6 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -31,6 +31,7 @@ INTERNAL_LD_FLAGS_MULTIBOOT1 := \
 INTERNALCFLAGS  :=       \
 	-I../stivale         \
 	-I.                  \
+	-I../common          \
 	-std=gnu11           \
 	-ffreestanding       \
 	-fno-stack-protector \
@@ -45,7 +46,7 @@ INTERNALCFLAGS  :=       \
 
 all: test.elf multiboot2.elf multiboot.elf
 
-test.elf: stivale.o stivale2.o e9print.o memory.o
+test.elf: stivale.o stivale2.o limine.o e9print.o memory.o
 	$(LD) $^ $(LDFLAGS) $(INTERNALLDFLAGS) -o $@
 
 multiboot2.elf: multiboot2_trampoline.o
diff --git a/test/limine.c b/test/limine.c
new file mode 100644
index 00000000..357fbc2d
--- /dev/null
+++ b/test/limine.c
@@ -0,0 +1,48 @@
+#include <stdint.h>
+#include <stddef.h>
+#include <limine.h>
+#include <e9print.h>
+
+static struct limine_framebuffer_request framebuffer_request = {
+    .id = LIMINE_FRAMEBUFFER_REQUEST,
+
+    .flags = LIMINE_FRAMEBUFFER_PREFER_LFB | LIMINE_FRAMEBUFFER_ENFORCE_PREFER,
+
+    .height = 0, .width = 0, .bpp = 0
+};
+
+static void *features_array[] = {
+    LIMINE_BOOT_INFO_REQUEST,
+    &framebuffer_request,
+    LIMINE_5_LEVEL_PAGING_REQUEST,
+    LIMINE_PMR_REQUEST
+};
+
+static void limine_main(void);
+
+__attribute__((used, aligned(16)))
+static struct limine_header limine_header = {
+    .magic = LIMINE_MAGIC,
+    .entry = limine_main,
+    .features_count = sizeof(features_array) / sizeof(void *),
+    .features = features_array
+};
+
+#define FEAT_START do {
+#define FEAT_END } while (0);
+
+static void limine_main(void) {
+    e9_printf("We're alive");
+
+FEAT_START
+    if (features_array[0] == NULL) {
+        break;
+    }
+    struct limine_boot_info_response *boot_info_response = features_array[0];
+    e9_printf("Boot info response:");
+    e9_printf("Bootloader name: %s", boot_info_response->loader);
+
+FEAT_END
+
+    for (;;);
+}
diff --git a/test/limine.cfg b/test/limine.cfg
index b5cf2e5d..828a9b5f 100644
--- a/test/limine.cfg
+++ b/test/limine.cfg
@@ -11,13 +11,11 @@ BACKGROUND_PATH=${BACKGROUND_PATH}
 BACKGROUND_STYLE=stretched
 BACKDROP_COLOUR=008080
 
-:Stivale2 Test
+:Limine Test
 
-COMMENT=Test of the stivale2 boot protocol.
+COMMENT=Test of the Limine boot protocol.
 
-# Let's use autodetection
-#PROTOCOL=stivale2
-RESOLUTION=800x600
+PROTOCOL=limine
 KERNEL_PATH=${STIVALE_KERNEL}
 KERNEL_CMDLINE=Woah! Another example!
 
tab: 248 wrap: offon