:: commit 692d2a03a892176a48d3e286706aa689c6a4931f

mintsuki <mintsuki@protonmail.com> — 2021-11-24 12:23

parents: 1621063536

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!
tab: 248 wrap: offon