limine: Introduce basic Limine protocol concept
This commit is contained in:
parent
c2b1be81dc
commit
b2e614e7c2
65
common/limine.h
Normal file
65
common/limine.h
Normal file
@ -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
|
@ -21,6 +21,7 @@
|
|||||||
#include <protos/chainload.h>
|
#include <protos/chainload.h>
|
||||||
#include <protos/multiboot1.h>
|
#include <protos/multiboot1.h>
|
||||||
#include <protos/multiboot2.h>
|
#include <protos/multiboot2.h>
|
||||||
|
#include <protos/limine.h>
|
||||||
|
|
||||||
static char *menu_branding = NULL;
|
static char *menu_branding = NULL;
|
||||||
static char *menu_branding_colour = NULL;
|
static char *menu_branding_colour = NULL;
|
||||||
@ -948,6 +949,8 @@ autodetect:
|
|||||||
ret = stivale_load(config, cmdline);
|
ret = stivale_load(config, cmdline);
|
||||||
} else if (!strcmp(proto, "stivale2")) {
|
} else if (!strcmp(proto, "stivale2")) {
|
||||||
ret = stivale2_load(config, cmdline);
|
ret = stivale2_load(config, cmdline);
|
||||||
|
} else if (!strcmp(proto, "limine")) {
|
||||||
|
ret = limine_load(config, cmdline);
|
||||||
} else if (!strcmp(proto, "linux")) {
|
} else if (!strcmp(proto, "linux")) {
|
||||||
ret = linux_load(config, cmdline);
|
ret = linux_load(config, cmdline);
|
||||||
} else if (!strcmp(proto, "multiboot1") || !strcmp(proto, "multiboot")) {
|
} else if (!strcmp(proto, "multiboot1") || !strcmp(proto, "multiboot")) {
|
||||||
|
251
common/protos/limine.c
Normal file
251
common/protos/limine.c
Normal file
@ -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();
|
||||||
|
}
|
8
common/protos/limine.h
Normal file
8
common/protos/limine.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#ifndef __PROTOS__LIMINE_H__
|
||||||
|
#define __PROTOS__LIMINE_H__
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
bool limine_load(char *config, char *cmdline);
|
||||||
|
|
||||||
|
#endif
|
@ -31,6 +31,7 @@ INTERNAL_LD_FLAGS_MULTIBOOT1 := \
|
|||||||
INTERNALCFLAGS := \
|
INTERNALCFLAGS := \
|
||||||
-I../stivale \
|
-I../stivale \
|
||||||
-I. \
|
-I. \
|
||||||
|
-I../common \
|
||||||
-std=gnu11 \
|
-std=gnu11 \
|
||||||
-ffreestanding \
|
-ffreestanding \
|
||||||
-fno-stack-protector \
|
-fno-stack-protector \
|
||||||
@ -45,7 +46,7 @@ INTERNALCFLAGS := \
|
|||||||
|
|
||||||
all: test.elf multiboot2.elf multiboot.elf
|
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 $@
|
$(LD) $^ $(LDFLAGS) $(INTERNALLDFLAGS) -o $@
|
||||||
|
|
||||||
multiboot2.elf: multiboot2_trampoline.o
|
multiboot2.elf: multiboot2_trampoline.o
|
||||||
|
48
test/limine.c
Normal file
48
test/limine.c
Normal file
@ -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 (;;);
|
||||||
|
}
|
@ -11,13 +11,11 @@ BACKGROUND_PATH=${BACKGROUND_PATH}
|
|||||||
BACKGROUND_STYLE=stretched
|
BACKGROUND_STYLE=stretched
|
||||||
BACKDROP_COLOUR=008080
|
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=limine
|
||||||
#PROTOCOL=stivale2
|
|
||||||
RESOLUTION=800x600
|
|
||||||
KERNEL_PATH=${STIVALE_KERNEL}
|
KERNEL_PATH=${STIVALE_KERNEL}
|
||||||
KERNEL_CMDLINE=Woah! Another example!
|
KERNEL_CMDLINE=Woah! Another example!
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user