From b2e614e7c26c9792f24c7c7082c2d87f693f3736 Mon Sep 17 00:00:00 2001 From: mintsuki Date: Sat, 12 Mar 2022 19:40:49 +0100 Subject: [PATCH] limine: Introduce basic Limine protocol concept --- common/limine.h | 65 +++++++++++ common/menu.c | 3 + common/protos/limine.c | 251 +++++++++++++++++++++++++++++++++++++++++ common/protos/limine.h | 8 ++ test/Makefile | 3 +- test/limine.c | 48 ++++++++ test/limine.cfg | 8 +- 7 files changed, 380 insertions(+), 6 deletions(-) create mode 100644 common/limine.h create mode 100644 common/protos/limine.c create mode 100644 common/protos/limine.h create mode 100644 test/limine.c 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 + +#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 #include #include +#include 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LIMINE_NO_POINTERS +#include +#include + +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 + +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 +#include +#include +#include + +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!