protos: Implement autodetection

This commit is contained in:
mintsuki 2021-11-24 13:23:02 +01:00
parent ad3e17569c
commit 6bcbfe9b48
13 changed files with 112 additions and 60 deletions

View File

@ -88,7 +88,7 @@ Some keys take *URIs* as values; these are described in the next section.
*Locally assignable (non protocol specific)* keys are:
* `COMMENT` - An optional comment string that will be displayed by the bootloader on the menu when an entry is selected.
* `PROTOCOL` - The boot protocol that will be used to boot the kernel. Valid protocols are: `linux`, `stivale`, `stivale2`, `chainload`, `multiboot` or `multiboot1` and `multiboot2`.
* `PROTOCOL` - The boot protocol that will be used to boot the kernel. Valid protocols are: `linux`, `stivale`, `stivale2`, `chainload`, `multiboot` or `multiboot1` and `multiboot2`. If the protocol is omitted, Limine will try to autodetect it, following this list of protocols, in this order: `stivale2 -> stivale1 -> multiboot2 -> multiboot1 -> linux -> failure`.
* `CMDLINE` - The command line string to be passed to the kernel. Can be omitted.
* `KERNEL_CMDLINE` - Alias of `CMDLINE`.

View File

@ -164,22 +164,38 @@ void stage3_common(void) {
char *proto = config_get_value(config, 0, "PROTOCOL");
if (proto == NULL) {
panic("PROTOCOL not specified");
printv("PROTOCOL not specified, using autodetection...\n");
autodetect:
stivale2_load(config, cmdline);
stivale_load(config, cmdline);
multiboot2_load(config, cmdline);
multiboot1_load(config, cmdline);
linux_load(config, cmdline);
panic("Kernel protocol autodetection failed");
}
bool ret = true;
if (!strcmp(proto, "stivale1") || !strcmp(proto, "stivale")) {
stivale_load(config, cmdline);
ret = stivale_load(config, cmdline);
} else if (!strcmp(proto, "stivale2")) {
stivale2_load(config, cmdline);
ret = stivale2_load(config, cmdline);
} else if (!strcmp(proto, "linux")) {
linux_load(config, cmdline);
ret = linux_load(config, cmdline);
} else if (!strcmp(proto, "multiboot1") || !strcmp(proto, "multiboot")) {
ret = multiboot1_load(config, cmdline);
} else if (!strcmp(proto, "multiboot2")) {
ret = multiboot2_load(config, cmdline);
} else if (!strcmp(proto, "chainload")) {
chainload(config);
} else if (!strcmp(proto, "multiboot1") || !strcmp(proto, "multiboot")) {
multiboot1_load(config, cmdline);
} else if (!strcmp(proto, "multiboot2")) {
multiboot2_load(config, cmdline);
}
panic("Invalid protocol specified");
if (ret) {
print("WARNING: Unsupported protocol specified: %s.\n", proto);
} else {
print("WARNING: Incorrect protocol specified for kernel.\n");
}
print(" Attempting autodetection.\n");
goto autodetect;
}

View File

@ -345,7 +345,7 @@ struct boot_params {
// End of Linux code
void linux_load(char *config, char *cmdline) {
bool linux_load(char *config, char *cmdline) {
struct file_handle *kernel_file;
char *kernel_path = config_get_value(config, 0, "KERNEL_PATH");
@ -360,7 +360,8 @@ void linux_load(char *config, char *cmdline) {
// validate signature
if (signature != 0x53726448) {
panic("linux: Invalid Linux kernel signature");
fclose(kernel_file);
return false;
}
size_t setup_code_size = 0;

View File

@ -1,6 +1,8 @@
#ifndef __PROTOS__LINUX_H__
#define __PROTOS__LINUX_H__
void linux_load(char *config, char *cmdline);
#include <stdbool.h>
bool linux_load(char *config, char *cmdline);
#endif

View File

@ -23,7 +23,7 @@ __attribute__((noreturn)) void multiboot1_spinup_32(
struct multiboot1_info multiboot1_info = {0};
void multiboot1_load(char *config, char *cmdline) {
bool multiboot1_load(char *config, char *cmdline) {
struct file_handle *kernel_file;
char *kernel_path = config_get_value(config, 0, "KERNEL_PATH");
@ -54,8 +54,10 @@ void multiboot1_load(char *config, char *cmdline) {
}
}
if (header.magic != MULTIBOOT1_HEADER_MAGIC)
panic("multiboot1: Could not find header");
if (header.magic != MULTIBOOT1_HEADER_MAGIC) {
pmm_free(kernel_file, kernel_file_size);
return false;
}
if (header.magic + header.flags + header.checksum)
panic("multiboot1: Header checksum is invalid");

View File

@ -3,6 +3,7 @@
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#define MULTIBOOT1_HEADER_MAGIC 0x1BADB002
@ -93,9 +94,6 @@ struct multiboot1_mmap_entry {
uint32_t type;
} __attribute__((packed));
void multiboot1_load(char *config, char *cmdline);
__attribute__((noreturn)) void multiboot1_spinup(
uint32_t entry_point, uint32_t multiboot1_info);
bool multiboot1_load(char *config, char *cmdline);
#endif

View File

@ -19,32 +19,6 @@
#include <lib/blib.h>
#include <drivers/vga_textmode.h>
static struct multiboot_header *load_multiboot2_header(uint8_t *kernel) {
struct multiboot_header *ptr = NULL;
size_t header_offset;
for (header_offset = 0; header_offset < MULTIBOOT_SEARCH; header_offset += MULTIBOOT_HEADER_ALIGN) {
uint32_t v;
memcpy(&v, kernel + header_offset, 4);
if (v == MULTIBOOT2_HEADER_MAGIC) {
ptr = (void *)(kernel + header_offset);
break;
}
}
if (ptr->magic != MULTIBOOT2_HEADER_MAGIC) {
panic("multiboot2: Could not find header");
}
if (ptr->magic + ptr->architecture + ptr->checksum + ptr->header_length) {
panic("mutliboot2: Header checksum is invalid");
}
return ptr;
}
/// Returns the size required to store the multiboot2 info.
static size_t get_multiboot2_info_size(
char *cmdline,
@ -78,7 +52,7 @@ static size_t get_multiboot2_info_size(
#define append_tag(P, TAG) ({ (P) += ALIGN_UP((TAG)->size, MULTIBOOT_TAG_ALIGN); })
void multiboot2_load(char *config, char* cmdline) {
bool multiboot2_load(char *config, char* cmdline) {
struct file_handle *kernel_file;
char *kernel_path = config_get_value(config, 0, "KERNEL_PATH");
@ -96,7 +70,24 @@ void multiboot2_load(char *config, char* cmdline) {
fclose(kernel_file);
struct multiboot_header *header = load_multiboot2_header(kernel);
struct multiboot_header *header;
for (size_t header_offset = 0; header_offset < MULTIBOOT_SEARCH; header_offset += MULTIBOOT_HEADER_ALIGN) {
header = (void *)(kernel + header_offset);
if (header->magic == MULTIBOOT2_HEADER_MAGIC) {
break;
}
}
if (header->magic != MULTIBOOT2_HEADER_MAGIC) {
pmm_free(kernel_file, kernel_file_size);
return false;
}
if (header->magic + header->architecture + header->checksum + header->header_length) {
panic("multiboot2: Header checksum is invalid");
}
struct multiboot_header_tag_address *addresstag = NULL;
struct multiboot_header_tag_framebuffer *fbtag = NULL;

View File

@ -23,8 +23,9 @@
#define __PROTOS__MULTIBOOT2_H__
#include <stdint.h>
#include <stdbool.h>
void multiboot2_load(char *config, char* cmdline);
bool multiboot2_load(char *config, char* cmdline);
/* How many bytes from the start of the file we search for the header. */
#define MULTIBOOT_SEARCH 32768

View File

@ -57,7 +57,7 @@ bool stivale_load_by_anchor(void **_anchor, const char *magic,
struct stivale_struct stivale_struct = {0};
void stivale_load(char *config, char *cmdline) {
bool stivale_load(char *config, char *cmdline) {
// BIOS or UEFI?
#if bios == 1
stivale_struct.flags |= (1 << 0);
@ -72,8 +72,6 @@ void stivale_load(char *config, char *cmdline) {
if (kernel_path == NULL)
panic("stivale: KERNEL_PATH not specified");
print("stivale: Loading kernel `%s`...\n", kernel_path);
if ((kernel_file = uri_open(kernel_path)) == NULL)
panic("stivale: Failed to open kernel with path `%s`. Is the path correct?", kernel_path);
@ -99,7 +97,7 @@ void stivale_load(char *config, char *cmdline) {
if (bits == -1) {
struct stivale_anchor *anchor;
if (!stivale_load_by_anchor((void **)&anchor, "STIVALE1 ANCHOR", kernel, kernel_file_size)) {
panic("stivale: Not a valid ELF or anchored file.");
goto fail;
}
bits = anchor->bits;
@ -108,8 +106,25 @@ void stivale_load(char *config, char *cmdline) {
sizeof(struct stivale_header));
loaded_by_anchor = true;
} else {
switch (bits) {
case 64:
if (elf64_load_section(kernel, &stivale_hdr, ".stivalehdr",
sizeof(struct stivale_header), slide)) {
goto fail;
}
break;
case 32:
if (elf32_load_section(kernel, &stivale_hdr, ".stivalehdr",
sizeof(struct stivale_header))) {
goto fail;
}
break;
}
}
print("stivale: Loading kernel `%s`...\n", kernel_path);
int ret = 0;
switch (bits) {
case 64: {
@ -340,6 +355,10 @@ void stivale_load(char *config, char *cmdline) {
stivale_spinup(bits, want_5lv, &pagemap,
entry_point, REPORTED_ADDR((uint64_t)(uintptr_t)&stivale_struct),
stivale_hdr.stack, false);
fail:
pmm_free(kernel, kernel_file_size);
return false;
}
pagemap_t stivale_build_pagemap(bool level5pg, bool unmap_null, struct elf_range *ranges, size_t ranges_count,

View File

@ -6,7 +6,7 @@
#include <mm/vmm.h>
#include <lib/elf.h>
void stivale_load(char *config, char *cmdline);
bool stivale_load(char *config, char *cmdline);
bool stivale_load_by_anchor(void **_anchor, const char *magic,
uint8_t *file, uint64_t filesize);

View File

@ -75,15 +75,13 @@ uint64_t stivale2_term_callback_ptr = 0;
void stivale2_term_callback(uint64_t, uint64_t, uint64_t, uint64_t);
#endif
void stivale2_load(char *config, char *cmdline) {
bool stivale2_load(char *config, char *cmdline) {
struct file_handle *kernel_file;
char *kernel_path = config_get_value(config, 0, "KERNEL_PATH");
if (kernel_path == NULL)
panic("stivale2: KERNEL_PATH not specified");
print("stivale2: Loading kernel `%s`...\n", kernel_path);
if ((kernel_file = uri_open(kernel_path)) == NULL)
panic("stivale2: Failed to open kernel with path `%s`. Is the path correct?", kernel_path);
@ -114,7 +112,7 @@ void stivale2_load(char *config, char *cmdline) {
if (bits == -1) {
struct stivale2_anchor *anchor;
if (!stivale_load_by_anchor((void **)&anchor, "STIVALE2 ANCHOR", kernel, kernel_file_size)) {
panic("stivale2: Not a valid ELF or anchored file.");
goto fail;
}
bits = anchor->bits;
@ -123,8 +121,25 @@ void stivale2_load(char *config, char *cmdline) {
sizeof(struct stivale2_header));
loaded_by_anchor = true;
} else {
switch (bits) {
case 64:
if (elf64_load_section(kernel, &stivale2_hdr, ".stivale2hdr",
sizeof(struct stivale2_header), slide)) {
goto fail;
}
break;
case 32:
if (elf32_load_section(kernel, &stivale2_hdr, ".stivale2hdr",
sizeof(struct stivale2_header))) {
goto fail;
}
break;
}
}
print("stivale2: Loading kernel `%s`...\n", kernel_path);
bool want_pmrs = false;
bool want_fully_virtual = false;
@ -766,4 +781,8 @@ have_tm_tag:;
stivale_spinup(bits, want_5lv, &pagemap, entry_point,
REPORTED_ADDR((uint64_t)(uintptr_t)&stivale2_struct),
stivale2_hdr.stack, want_pmrs);
fail:
pmm_free(kernel, kernel_file_size);
return false;
}

View File

@ -1,6 +1,8 @@
#ifndef __PROTOS__STIVALE2_H__
#define __PROTOS__STIVALE2_H__
void stivale2_load(char *config, char *cmdline);
#include <stdbool.h>
bool stivale2_load(char *config, char *cmdline);
#endif

View File

@ -13,7 +13,8 @@ BACKDROP_COLOUR=008080
COMMENT=Test of the stivale2 boot protocol.
PROTOCOL=stivale2
# Let's use autodetection
#PROTOCOL=stivale2
RESOLUTION=800x600
KERNEL_PATH=boot:///boot/test.elf
KERNEL_CMDLINE=Woah! Another example!