protos: Implement autodetection
diff --git a/CONFIG.md b/CONFIG.md
index e0b49970..b47d7587 100644
--- a/CONFIG.md
+++ b/CONFIG.md
@@ -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`.
diff --git a/stage23/entry.s3.c b/stage23/entry.s3.c
index ef07aff8..f44e3bff 100644
--- a/stage23/entry.s3.c
+++ b/stage23/entry.s3.c
@@ -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);
- } else if (!strcmp(proto, "chainload")) {
- chainload(config);
+ ret = linux_load(config, cmdline);
} else if (!strcmp(proto, "multiboot1") || !strcmp(proto, "multiboot")) {
- multiboot1_load(config, cmdline);
+ ret = multiboot1_load(config, cmdline);
} else if (!strcmp(proto, "multiboot2")) {
- multiboot2_load(config, cmdline);
+ ret = multiboot2_load(config, cmdline);
+ } else if (!strcmp(proto, "chainload")) {
+ chainload(config);
+ }
+
+ if (ret) {
+ print("WARNING: Unsupported protocol specified: %s.\n", proto);
+ } else {
+ print("WARNING: Incorrect protocol specified for kernel.\n");
}
- panic("Invalid protocol specified");
+ print(" Attempting autodetection.\n");
+ goto autodetect;
}
diff --git a/stage23/protos/linux.c b/stage23/protos/linux.c
index 21ba635c..4d5cb0d4 100644
--- a/stage23/protos/linux.c
+++ b/stage23/protos/linux.c
@@ -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;
diff --git a/stage23/protos/linux.h b/stage23/protos/linux.h
index 395fd3b7..fadd321e 100644
--- a/stage23/protos/linux.h
+++ b/stage23/protos/linux.h
@@ -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
diff --git a/stage23/protos/multiboot1.c b/stage23/protos/multiboot1.c
index 3a67d274..f5947106 100644
--- a/stage23/protos/multiboot1.c
+++ b/stage23/protos/multiboot1.c
@@ -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");
diff --git a/stage23/protos/multiboot1.h b/stage23/protos/multiboot1.h
index acc0ba17..e282f9d1 100644
--- a/stage23/protos/multiboot1.h
+++ b/stage23/protos/multiboot1.h
@@ -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
diff --git a/stage23/protos/multiboot2.c b/stage23/protos/multiboot2.c
index efcc8f74..8c60925b 100644
--- a/stage23/protos/multiboot2.c
+++ b/stage23/protos/multiboot2.c
@@ -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;
diff --git a/stage23/protos/multiboot2.h b/stage23/protos/multiboot2.h
index 4140edbe..d02cfcca 100644
--- a/stage23/protos/multiboot2.h
+++ b/stage23/protos/multiboot2.h
@@ -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
diff --git a/stage23/protos/stivale.c b/stage23/protos/stivale.c
index e2913349..f11935bc 100644
--- a/stage23/protos/stivale.c
+++ b/stage23/protos/stivale.c
@@ -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,
diff --git a/stage23/protos/stivale.h b/stage23/protos/stivale.h
index c6123843..a52b75b3 100644
--- a/stage23/protos/stivale.h
+++ b/stage23/protos/stivale.h
@@ -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);
diff --git a/stage23/protos/stivale2.c b/stage23/protos/stivale2.c
index f36be7e6..3c79f4fc 100644
--- a/stage23/protos/stivale2.c
+++ b/stage23/protos/stivale2.c
@@ -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;
}
diff --git a/stage23/protos/stivale2.h b/stage23/protos/stivale2.h
index bc1b519d..9674fcff 100644
--- a/stage23/protos/stivale2.h
+++ b/stage23/protos/stivale2.h
@@ -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
diff --git a/test/limine.cfg b/test/limine.cfg
index 6aa90749..23ae6778 100644
--- a/test/limine.cfg
+++ b/test/limine.cfg
@@ -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!
