rulimine/common/protos/stivale.c

521 lines
18 KiB
C
Raw Normal View History

#include <stdint.h>
#include <stddef.h>
2020-05-03 23:37:24 +03:00
#include <stdbool.h>
2021-12-31 12:58:05 +03:00
#include <stdnoreturn.h>
#include <protos/stivale.h>
#include <lib/libc.h>
#include <lib/elf.h>
2020-03-26 02:46:35 +03:00
#include <lib/blib.h>
2020-03-26 03:37:56 +03:00
#include <lib/acpi.h>
2020-03-30 23:27:15 +03:00
#include <lib/config.h>
2020-04-30 22:19:12 +03:00
#include <lib/time.h>
2020-05-10 01:38:27 +03:00
#include <lib/print.h>
#include <lib/real.h>
2020-11-02 11:20:34 +03:00
#include <lib/uri.h>
2021-03-02 08:21:05 +03:00
#include <lib/fb.h>
2020-09-02 10:55:56 +03:00
#include <lib/term.h>
2020-09-18 21:02:10 +03:00
#include <sys/pic.h>
2020-10-12 22:49:17 +03:00
#include <sys/cpu.h>
#include <sys/gdt.h>
#include <sys/idt.h>
#include <sys/lapic.h>
2020-04-14 06:20:55 +03:00
#include <fs/file.h>
2020-09-21 13:15:55 +03:00
#include <mm/vmm.h>
2020-09-20 13:03:44 +03:00
#include <mm/pmm.h>
2021-10-22 21:08:11 +03:00
#include <stivale.h>
#include <drivers/vga_textmode.h>
#include <drivers/gop.h>
2020-03-26 02:46:35 +03:00
#define REPORTED_ADDR(PTR) \
((PTR) + ((stivale_hdr.flags & (1 << 3)) ? \
direct_map_offset : 0))
bool stivale_load_by_anchor(void **_anchor, const char *magic,
uint8_t *file, uint64_t filesize) {
struct stivale_anchor *anchor = NULL;
size_t magiclen = strlen(magic);
for (size_t i = 0; i < filesize; i += 16) {
if (memcmp(file + i, magic, magiclen) == 0) {
anchor = (void *)(file + i);
}
}
if (anchor == NULL) {
return false;
}
memmap_alloc_range(anchor->phys_load_addr, filesize, MEMMAP_KERNEL_AND_MODULES,
true, true, false, false);
memcpy((void *)(uintptr_t)anchor->phys_load_addr, file, filesize);
size_t bss_size = anchor->phys_bss_end - anchor->phys_bss_start;
memmap_alloc_range(anchor->phys_bss_start, bss_size, MEMMAP_KERNEL_AND_MODULES,
true, true, false, false);
memset((void *)(uintptr_t)anchor->phys_bss_start, 0, bss_size);
*_anchor = anchor;
return true;
}
noreturn void stivale_load(char *config, char *cmdline) {
2022-08-09 16:03:51 +03:00
char *depr_warn = config_get_value(config, 0, "DEPRECATION_WARNING");
if (depr_warn == NULL || strcmp(depr_warn, "no") != 0) {
print("WARNING: The stivale protocol is deprecated in Limine and will be removed as of version 4.x.\n");
print(" It is recommended to move to either the Limine boot protocol or multiboot2.\n");
print(" To silence this warning add DEPRECATION_WARNING=no to the boot entry.\n");
print("\n");
print("Press any key or wait 5 seconds to continue booting...\n");
pit_sleep_and_quit_on_keypress(5);
}
struct stivale_struct *stivale_struct = ext_mem_alloc(sizeof(struct stivale_struct));
2021-03-12 02:04:37 +03:00
// BIOS or UEFI?
#if bios == 1
stivale_struct->flags |= (1 << 0);
2021-03-12 02:04:37 +03:00
#endif
stivale_struct->flags |= (1 << 1); // we give colour information
stivale_struct->flags |= (1 << 2); // we give SMBIOS information
2020-05-15 06:47:38 +03:00
2021-10-21 02:27:05 +03:00
struct file_handle *kernel_file;
char *kernel_path = config_get_value(config, 0, "KERNEL_PATH");
if (kernel_path == NULL)
2021-12-11 21:58:00 +03:00
panic(true, "stivale: KERNEL_PATH not specified");
print("stivale: Loading kernel `%s`...\n", kernel_path);
2021-10-21 02:27:05 +03:00
if ((kernel_file = uri_open(kernel_path)) == NULL)
2021-12-11 21:58:00 +03:00
panic(true, "stivale: Failed to open kernel with path `%s`. Is the path correct?", kernel_path);
2020-05-06 17:38:45 +03:00
char *kaslr_s = config_get_value(config, 0, "KASLR");
bool kaslr = true;
if (kaslr_s != NULL && strcmp(kaslr_s, "no") == 0)
kaslr = false;
2020-03-26 02:46:35 +03:00
struct stivale_header stivale_hdr;
2020-04-18 19:01:29 +03:00
bool level5pg = false;
uint64_t slide = 0;
uint64_t entry_point = 0;
2021-03-28 16:46:59 +03:00
uint8_t *kernel = freadall(kernel_file, STIVALE_MMAP_BOOTLOADER_RECLAIMABLE);
int bits = elf_bits(kernel);
bool loaded_by_anchor = false;
2020-04-18 19:01:29 +03:00
2021-10-22 17:37:17 +03:00
size_t kernel_file_size = kernel_file->size;
2021-10-21 02:27:05 +03:00
fclose(kernel_file);
if (bits == -1) {
struct stivale_anchor *anchor;
2021-10-22 17:37:17 +03:00
if (!stivale_load_by_anchor((void **)&anchor, "STIVALE1 ANCHOR", kernel, kernel_file_size)) {
panic(true, "stivale: Failed to load kernel by anchor");
}
2020-04-18 19:01:29 +03:00
bits = anchor->bits;
memcpy(&stivale_hdr, (void *)(uintptr_t)anchor->phys_stivalehdr,
sizeof(struct stivale_header));
2021-04-15 05:32:49 +03:00
loaded_by_anchor = true;
2021-11-24 15:23:02 +03:00
} else {
switch (bits) {
case 64:
if (elf64_load_section(kernel, &stivale_hdr, ".stivalehdr",
sizeof(struct stivale_header), slide)) {
panic(true, "stivale: Failed to load .stivalehdr section");
2021-11-24 15:23:02 +03:00
}
break;
case 32:
if (elf32_load_section(kernel, &stivale_hdr, ".stivalehdr",
sizeof(struct stivale_header))) {
panic(true, "stivale: Failed to load .stivalehdr section");
2021-11-24 15:23:02 +03:00
}
break;
}
}
int ret = 0;
2020-04-18 19:01:29 +03:00
switch (bits) {
2020-05-03 23:37:24 +03:00
case 64: {
2020-04-30 14:03:04 +03:00
// Check if 64 bit CPU
2020-05-03 23:37:24 +03:00
uint32_t eax, ebx, ecx, edx;
if (!cpuid(0x80000001, 0, &eax, &ebx, &ecx, &edx) || !(edx & (1 << 29))) {
2021-12-11 21:58:00 +03:00
panic(true, "stivale: This CPU does not support 64-bit mode.");
2020-05-03 23:37:24 +03:00
}
// Check if 5-level paging is available
if (cpuid(0x00000007, 0, &eax, &ebx, &ecx, &edx) && (ecx & (1 << 16))) {
2021-05-11 07:46:42 +03:00
printv("stivale: CPU has 5-level paging support\n");
2020-05-03 23:37:24 +03:00
level5pg = true;
2020-04-30 14:03:04 +03:00
}
if (!loaded_by_anchor) {
if (elf64_load(kernel, &entry_point, NULL, &slide,
STIVALE_MMAP_KERNEL_AND_MODULES, kaslr,
2022-03-28 06:13:47 +03:00
NULL, NULL, false, NULL, NULL, NULL, NULL))
2021-12-11 21:58:00 +03:00
panic(true, "stivale: ELF64 load failure");
ret = elf64_load_section(kernel, &stivale_hdr, ".stivalehdr",
sizeof(struct stivale_header), slide);
}
2020-12-28 01:11:11 +03:00
2020-04-18 19:01:29 +03:00
break;
2020-05-03 23:37:24 +03:00
}
case 32: {
if (!loaded_by_anchor) {
if (elf32_load(kernel, (uint32_t *)&entry_point, NULL, STIVALE_MMAP_KERNEL_AND_MODULES))
2021-12-11 21:58:00 +03:00
panic(true, "stivale: ELF32 load failure");
ret = elf32_load_section(kernel, &stivale_hdr, ".stivalehdr",
sizeof(struct stivale_header));
}
2020-04-18 19:01:29 +03:00
break;
}
2020-04-29 17:53:05 +03:00
default:
2021-12-11 21:58:00 +03:00
panic(true, "stivale: Not 32 nor 64-bit kernel. What is this?");
2020-04-18 19:01:29 +03:00
}
printv("stivale: %u-bit kernel detected\n", bits);
2020-03-26 02:46:35 +03:00
switch (ret) {
case 1:
2021-12-11 21:58:00 +03:00
panic(true, "stivale: File is not a valid ELF.");
2020-03-26 02:46:35 +03:00
case 2:
2021-12-11 21:58:00 +03:00
panic(true, "stivale: Section .stivalehdr not found.");
2020-03-26 02:46:35 +03:00
case 3:
2021-12-11 21:58:00 +03:00
panic(true, "stivale: Section .stivalehdr exceeds the size of the struct.");
case 4:
2021-12-11 21:58:00 +03:00
panic(true, "stivale: Section .stivalehdr is smaller than size of the struct.");
2020-03-26 02:46:35 +03:00
}
if ((stivale_hdr.flags & (1 << 3)) && bits == 32) {
2021-12-11 21:58:00 +03:00
panic(true, "stivale: Higher half addresses header flag not supported in 32-bit mode.");
}
bool want_5lv = level5pg && (stivale_hdr.flags & (1 << 1));
uint64_t direct_map_offset = want_5lv ? 0xff00000000000000 : 0xffff800000000000;
struct gdtr *local_gdt = ext_mem_alloc(sizeof(struct gdtr));
local_gdt->limit = gdt.limit;
uint64_t local_gdt_base = (uint64_t)gdt.ptr;
if (stivale_hdr.flags & (1 << 3)) {
local_gdt_base += direct_map_offset;
}
local_gdt->ptr = local_gdt_base;
2021-12-08 20:20:45 +03:00
#if defined (__i386__)
local_gdt->ptr_hi = local_gdt_base >> 32;
#endif
if (stivale_hdr.entry_point != 0)
entry_point = stivale_hdr.entry_point;
2021-05-11 07:46:42 +03:00
if (verbose) {
print("stivale: Kernel slide: %X\n", slide);
2021-05-11 07:46:42 +03:00
print("stivale: Entry point at: %X\n", entry_point);
print("stivale: Requested stack at: %X\n", stivale_hdr.stack);
}
// The spec says the stack has to be 16-byte aligned
if ((stivale_hdr.stack & (16 - 1)) != 0) {
print("stivale: WARNING: Requested stack is not 16-byte aligned\n");
}
// It also says the stack cannot be NULL for 32-bit kernels
if (bits == 32 && stivale_hdr.stack == 0) {
2021-12-11 21:58:00 +03:00
panic(true, "stivale: The stack cannot be 0 for 32-bit kernels");
}
stivale_struct->module_count = 0;
uint64_t *prev_mod_ptr = &stivale_struct->modules;
2020-03-30 23:27:15 +03:00
for (int i = 0; ; i++) {
struct conf_tuple conf_tuple =
config_get_tuple(config, i, "MODULE_PATH", "MODULE_STRING");
char *module_path = conf_tuple.value1;
char *module_string = conf_tuple.value2;
if (module_path == NULL)
2020-03-30 23:27:15 +03:00
break;
stivale_struct->module_count++;
2020-03-30 23:27:15 +03:00
struct stivale_module *m = ext_mem_alloc(sizeof(struct stivale_module));
2020-03-30 23:27:15 +03:00
// TODO: perhaps change the module string to to be a pointer.
//
// NOTE: By default, the module string is the file name.
if (module_string == NULL) {
size_t str_len = strlen(module_path);
if (str_len > 127)
str_len = 127;
memcpy(m->string, module_path, str_len);
} else {
// TODO perhaps change this to be a pointer
size_t str_len = strlen(module_string);
if (str_len > 127)
str_len = 127;
memcpy(m->string, module_string, str_len);
2020-06-05 18:51:33 +03:00
}
2020-03-30 23:27:15 +03:00
print("stivale: Loading module `%s`...\n", module_path);
2021-10-21 02:27:05 +03:00
struct file_handle *f;
if ((f = uri_open(module_path)) == NULL)
2021-12-11 21:58:00 +03:00
panic(true, "stivale: Failed to open module with path `%s`. Is the path correct?", module_path);
2020-03-30 23:27:15 +03:00
2021-10-21 02:27:05 +03:00
m->begin = REPORTED_ADDR((uint64_t)(size_t)freadall(f, STIVALE_MMAP_KERNEL_AND_MODULES));
m->end = m->begin + f->size;
2020-03-30 23:27:15 +03:00
m->next = 0;
*prev_mod_ptr = REPORTED_ADDR((uint64_t)(size_t)m);
2020-03-30 23:27:15 +03:00
prev_mod_ptr = &m->next;
2021-10-21 02:27:05 +03:00
fclose(f);
2021-05-11 07:46:42 +03:00
if (verbose) {
print("stivale: Requested module %u:\n", i);
print(" Path: %s\n", module_path);
print(" String: %s\n", m->string);
print(" Begin: %X\n", m->begin);
print(" End: %X\n", m->end);
}
2020-03-30 23:27:15 +03:00
}
uint64_t rsdp = (uint64_t)(size_t)acpi_get_rsdp();
if (rsdp)
stivale_struct->rsdp = REPORTED_ADDR(rsdp);
2020-03-26 03:37:56 +03:00
uint64_t smbios_entry_32 = 0, smbios_entry_64 = 0;
acpi_get_smbios((void **)&smbios_entry_32, (void **)&smbios_entry_64);
if (smbios_entry_32)
stivale_struct->smbios_entry_32 = REPORTED_ADDR(smbios_entry_32);
if (smbios_entry_64)
stivale_struct->smbios_entry_64 = REPORTED_ADDR(smbios_entry_64);
stivale_struct->cmdline = REPORTED_ADDR((uint64_t)(size_t)cmdline);
stivale_struct->epoch = time();
printv("stivale: Current epoch: %U\n", stivale_struct->epoch);
2020-04-30 22:19:12 +03:00
term_deinit();
2020-05-03 23:37:24 +03:00
if (stivale_hdr.flags & (1 << 0)) {
size_t req_width = stivale_hdr.framebuffer_width;
size_t req_height = stivale_hdr.framebuffer_height;
size_t req_bpp = stivale_hdr.framebuffer_bpp;
char *resolution = config_get_value(config, 0, "RESOLUTION");
if (resolution != NULL)
parse_resolution(&req_width, &req_height, &req_bpp, resolution);
2021-03-02 08:21:05 +03:00
struct fb_info fbinfo;
#if uefi == 1
gop_force_16 = true;
#endif
2021-03-02 08:21:05 +03:00
if (!fb_init(&fbinfo, req_width, req_height, req_bpp))
2021-12-11 21:58:00 +03:00
panic(true, "stivale: Unable to set video mode");
memmap_alloc_range(fbinfo.framebuffer_addr,
(uint64_t)fbinfo.framebuffer_pitch * fbinfo.framebuffer_height,
MEMMAP_FRAMEBUFFER, false, false, false, true);
stivale_struct->framebuffer_addr = REPORTED_ADDR((uint64_t)fbinfo.framebuffer_addr);
stivale_struct->framebuffer_width = fbinfo.framebuffer_width;
stivale_struct->framebuffer_height = fbinfo.framebuffer_height;
stivale_struct->framebuffer_bpp = fbinfo.framebuffer_bpp;
stivale_struct->framebuffer_pitch = fbinfo.framebuffer_pitch;
stivale_struct->fb_memory_model = STIVALE_FBUF_MMODEL_RGB;
stivale_struct->fb_red_mask_size = fbinfo.red_mask_size;
stivale_struct->fb_red_mask_shift = fbinfo.red_mask_shift;
stivale_struct->fb_green_mask_size = fbinfo.green_mask_size;
stivale_struct->fb_green_mask_shift = fbinfo.green_mask_shift;
stivale_struct->fb_blue_mask_size = fbinfo.blue_mask_size;
stivale_struct->fb_blue_mask_shift = fbinfo.blue_mask_shift;
} else {
#if uefi == 1
2021-12-11 21:58:00 +03:00
panic(true, "stivale: Cannot use text mode with UEFI.");
#elif bios == 1
size_t rows, cols;
init_vga_textmode(&rows, &cols, false);
#endif
2020-03-26 05:13:19 +03:00
}
#if uefi == 1
efi_exit_boot_services();
#endif
pagemap_t pagemap = {0};
if (bits == 64)
pagemap = stivale_build_pagemap(want_5lv, false, NULL, 0, false, 0, 0, direct_map_offset);
2021-11-03 03:52:55 +03:00
// Reserve 32K at 0x70000 if possible
if (!memmap_alloc_range(0x70000, 0x8000, MEMMAP_USABLE, true, false, false, false)) {
if ((stivale_hdr.flags & (1 << 4)) == 0) {
2021-12-11 21:58:00 +03:00
panic(false, "stivale: Could not allocate low memory area");
2021-11-03 03:52:55 +03:00
}
}
struct memmap_entry *mmap_copy = ext_mem_alloc(256 * sizeof(struct memmap_entry));
2021-07-06 06:17:18 +03:00
size_t mmap_entries;
struct memmap_entry *mmap = get_memmap(&mmap_entries);
2020-06-05 18:51:33 +03:00
if (mmap_entries > 256) {
2021-12-11 21:58:00 +03:00
panic(false, "stivale: Too many memory map entries!");
}
memcpy(mmap_copy, mmap, mmap_entries * sizeof(struct memmap_entry));
stivale_struct->memory_map_entries = (uint64_t)mmap_entries;
stivale_struct->memory_map_addr = REPORTED_ADDR((uint64_t)(size_t)mmap_copy);
stivale_spinup(bits, want_5lv, &pagemap,
entry_point, REPORTED_ADDR((uint64_t)(uintptr_t)stivale_struct),
stivale_hdr.stack, false, false, (uintptr_t)local_gdt);
2020-09-02 10:55:56 +03:00
}
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, uint64_t virtual_base,
uint64_t direct_map_offset) {
2020-09-18 15:39:29 +03:00
pagemap_t pagemap = new_pagemap(level5pg ? 5 : 4);
if (ranges_count == 0) {
// Map 0 to 2GiB at 0xffffffff80000000
for (uint64_t i = 0; i < 0x80000000; i += 0x40000000) {
map_page(pagemap, 0xffffffff80000000 + i, i, 0x03, Size1GiB);
}
} else {
for (size_t i = 0; i < ranges_count; i++) {
uint64_t virt = ranges[i].base;
uint64_t phys;
if (virt & ((uint64_t)1 << 63)) {
if (want_fully_virtual) {
phys = physical_base + (virt - virtual_base);
} else {
phys = virt - FIXED_HIGHER_HALF_OFFSET_64;
}
2021-07-15 17:20:29 +03:00
} else {
2021-12-11 21:58:00 +03:00
panic(false, "stivale2: Protected memory ranges are only supported for higher half kernels");
2021-07-15 17:20:29 +03:00
}
uint64_t pf = VMM_FLAG_PRESENT |
(ranges[i].permissions & ELF_PF_X ? 0 : VMM_FLAG_NOEXEC) |
(ranges[i].permissions & ELF_PF_W ? VMM_FLAG_WRITE : 0);
for (uint64_t j = 0; j < ranges[i].length; j += 0x1000) {
map_page(pagemap, virt + j, phys + j, pf, Size4KiB);
}
}
}
2021-07-15 17:20:29 +03:00
// Sub 2MiB mappings
for (uint64_t i = 0; i < 0x200000; i += 0x1000) {
if (!(i == 0 && unmap_null))
map_page(pagemap, i, i, 0x03, Size4KiB);
map_page(pagemap, direct_map_offset + i, i, 0x03, Size4KiB);
2021-07-15 17:20:29 +03:00
}
// Map 2MiB to 4GiB at higher half base and 0
//
// NOTE: We cannot just directly map from 2MiB to 4GiB with 1GiB
// pages because if you do the math.
//
// start = 0x200000
// end = 0x40000000
2022-01-28 13:06:22 +03:00
//
// pages_required = (end - start) / (4096 * 512 * 512)
//
// So we map 2MiB to 1GiB with 2MiB pages and then map the rest
// with 1GiB pages :^)
for (uint64_t i = 0x200000; i < 0x40000000; i += 0x200000) {
map_page(pagemap, i, i, 0x03, Size2MiB);
map_page(pagemap, direct_map_offset + i, i, 0x03, Size2MiB);
2020-09-18 15:39:29 +03:00
}
for (uint64_t i = 0x40000000; i < 0x100000000; i += 0x40000000) {
map_page(pagemap, i, i, 0x03, Size1GiB);
map_page(pagemap, direct_map_offset + i, i, 0x03, Size1GiB);
}
size_t _memmap_entries = memmap_entries;
struct memmap_entry *_memmap =
ext_mem_alloc(_memmap_entries * sizeof(struct memmap_entry));
for (size_t i = 0; i < _memmap_entries; i++)
_memmap[i] = memmap[i];
2020-09-18 15:39:29 +03:00
// Map any other region of memory from the memmap
for (size_t i = 0; i < _memmap_entries; i++) {
uint64_t base = _memmap[i].base;
uint64_t length = _memmap[i].length;
2020-09-18 15:39:29 +03:00
uint64_t top = base + length;
2021-04-14 12:06:14 +03:00
if (base < 0x100000000)
base = 0x100000000;
if (base >= top)
continue;
2022-01-28 13:06:22 +03:00
uint64_t aligned_base = ALIGN_DOWN(base, 0x40000000);
uint64_t aligned_top = ALIGN_UP(top, 0x40000000);
2020-09-18 15:39:29 +03:00
uint64_t aligned_length = aligned_top - aligned_base;
2022-01-28 13:06:22 +03:00
for (uint64_t j = 0; j < aligned_length; j += 0x40000000) {
2021-07-06 06:17:18 +03:00
uint64_t page = aligned_base + j;
2022-01-28 13:06:22 +03:00
map_page(pagemap, page, page, 0x03, Size1GiB);
map_page(pagemap, direct_map_offset + page, page, 0x03, Size1GiB);
2020-09-18 15:39:29 +03:00
}
}
return pagemap;
}
2021-12-31 12:58:05 +03:00
noreturn void stivale_spinup_32(
int bits, bool level5pg, 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, uint32_t local_gdt);
2021-12-31 12:58:05 +03:00
noreturn void stivale_spinup(
int bits, bool level5pg, pagemap_t *pagemap,
2021-07-15 17:20:29 +03:00
uint64_t entry_point, uint64_t _stivale_struct, uint64_t stack,
bool enable_nx, bool wp, uint32_t local_gdt) {
#if bios == 1
if (bits == 64) {
// If we're going 64, we might as well call this BIOS interrupt
// to tell the BIOS that we are entering Long Mode, since it is in
// the specification.
struct rm_regs r = {0};
r.eax = 0xec00;
r.ebx = 0x02; // Long mode only
rm_int(0x15, &r, &r);
}
2021-03-02 12:23:43 +03:00
#endif
2021-07-15 17:20:29 +03:00
if (enable_nx) {
vmm_assert_nx();
}
2020-09-02 03:32:04 +03:00
pic_mask_all();
io_apic_mask_all();
irq_flush_type = IRQ_PIC_APIC_FLUSH;
common_spinup(stivale_spinup_32, 12,
bits, level5pg, enable_nx, wp, (uint32_t)(uintptr_t)pagemap->top_level,
(uint32_t)entry_point, (uint32_t)(entry_point >> 32),
2021-07-06 06:17:18 +03:00
(uint32_t)_stivale_struct, (uint32_t)(_stivale_struct >> 32),
(uint32_t)stack, (uint32_t)(stack >> 32), local_gdt);
}