:: commit 95272c5f1d0873efcd92403c7f9ec645246decce

mintsuki <mintsuki@protonmail.com> — 2022-07-10 22:15

parents: 1ac1343059

protos: Add chainload_next protocol. Closes #191

diff --git a/CONFIG.md b/CONFIG.md
index 6248f674..5dd6c19e 100644
--- a/CONFIG.md
+++ b/CONFIG.md
@@ -97,7 +97,7 @@ Editor control options.
 
 *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`, `limine`, `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: `multiboot2 -> multiboot1 -> limine -> linux -> failure`.
+* `PROTOCOL` - The boot protocol that will be used to boot the kernel. Valid protocols are: `linux`, `limine`, `chainload`, `chainload_next`, `multiboot` or `multiboot1` and `multiboot2`. If the protocol is omitted, Limine will try to autodetect it, following this list of protocols, in this order: `multiboot2 -> multiboot1 -> limine -> linux -> failure`.
 * `CMDLINE` - The command line string to be passed to the kernel. Can be omitted.
 * `KERNEL_CMDLINE` - Alias of `CMDLINE`.
 
@@ -110,6 +110,7 @@ Editor control options.
   modules.
   * `RESOLUTION` - The resolution to be used. This setting takes the form of `<width>x<height>x<bpp>`. If the resolution is not available, Limine will pick another one automatically. Omitting `<bpp>` will default to 32.
   * `TEXTMODE` - If set to `yes`, prefer text mode. (BIOS only)
+
 * Limine protocol:
   * `KERNEL_PATH` - The URI path of the kernel.
   * `MODULE_PATH` - The URI path to a module.
@@ -122,22 +123,28 @@ Editor control options.
 
   * `RESOLUTION` - The resolution to be used. This setting takes the form of `<width>x<height>x<bpp>`. If the resolution is not available, Limine will pick another one automatically. Omitting `<bpp>` will default to 32.
   * `KASLR` - For relocatable kernels, if set to `no`, disable kernel address space layout randomisation. KASLR is enabled by default.
+
 * Chainload protocol on BIOS:
   * `DRIVE` - The 1-based BIOS drive to chainload, if omitted, assume boot drive.
   * `PARTITION` - The 1-based BIOS partition to chainload, if omitted, chainload drive (MBR).
+
 * Chainload protocol on UEFI:
   * `IMAGE_PATH` - URI of the EFI application to chainload.
   * `RESOLUTION` - The resolution to be used. This setting takes the form of `<width>x<height>x<bpp>`. If the resolution is not available, Limine will pick another one automatically. Omitting `<bpp>` will default to 32.
 
+* chainload_next protocol:
+  This protocol does not specify any locally assignable key. Will boot the next bootable drive found in the system, if there is one.
+
 * multiboot1 and multiboot2 protocols:
   * `KERNEL_PATH` - The URI path of the kernel.
   * `MODULE_PATH` - The URI path to a module.
   * `MODULE_STRING` - A string to be passed to a module.
 
-  Note that one can define these 2 last variable multiple times to specify multiple
+  **Note:** One can define these 2 last variable multiple times to specify multiple
   modules.
   The entries will be matched in order. E.g.: the 1st module path entry will be matched
   to the 1st module string entry that appear, and so on.
+
   * `RESOLUTION` - The resolution to be used should the kernel request a graphical framebuffer. This setting takes the form of `<width>x<height>x<bpp>` and *overrides* any resolution requested by the kernel. If the resolution is not available, Limine will pick another one automatically. Omitting `<bpp>` will default to 32.
 
 ## URIs
diff --git a/common/menu.c b/common/menu.c
index f2e3f756..f2e19678 100644
--- a/common/menu.c
+++ b/common/menu.c
@@ -17,6 +17,7 @@
 #include <console.h>
 #include <protos/linux.h>
 #include <protos/chainload.h>
+#include <protos/chainload_next.h>
 #include <protos/multiboot1.h>
 #include <protos/multiboot2.h>
 #include <protos/limine.h>
@@ -929,6 +930,8 @@ noreturn void boot(char *config) {
         multiboot1_load(config, cmdline);
     } else if (!strcmp(proto, "multiboot2")) {
         multiboot2_load(config, cmdline);
+    } else if (!strcmp(proto, "chainload_next")) {
+        chainload_next(config);
     } else if (!strcmp(proto, "chainload")) {
         chainload(config);
     }
diff --git a/common/protos/chainload.c b/common/protos/chainload.c
index 15960789..b6f50bc2 100644
--- a/common/protos/chainload.c
+++ b/common/protos/chainload.c
@@ -99,19 +99,29 @@ void chainload(char *config) {
 
     struct volume *p = volume_get_by_coord(false, drive, part);
 
+    bios_chainload_volume(p);
+
+    panic(true, "chainload: Volume is not bootable");
+}
+
+void bios_chainload_volume(struct volume *p) {
     size_t rows, cols;
     init_vga_textmode(&rows, &cols, false);
 
     volume_read(p, (void *)0x7c00, 0, 512);
 
+    volatile uint16_t *boot_sig = (volatile uint16_t *)0x7dfe;
+
+    if (*boot_sig != 0xaa55) {
+        return;
+    }
+
     spinup(p->drive);
 }
 
 #elif uefi == 1
 
 void chainload(char *config) {
-    EFI_STATUS status;
-
     char *image_path = config_get_value(config, 0, "IMAGE_PATH");
     if (image_path == NULL)
         panic(true, "chainload: IMAGE_PATH not specified");
@@ -120,6 +130,12 @@ void chainload(char *config) {
     if ((image = uri_open(image_path)) == NULL)
         panic(true, "chainload: Failed to open image with path `%s`. Is the path correct?", image_path);
 
+    efi_chainload_file(image);
+}
+
+void efi_chainload_file(struct file_handle *image) {
+    EFI_STATUS status;
+
     EFI_HANDLE efi_part_handle = image->efi_part_handle;
 
     void *_ptr = freadall(image, MEMMAP_RESERVED);
diff --git a/common/protos/chainload.h b/common/protos/chainload.h
index 13e2d9e6..a462b8a4 100644
--- a/common/protos/chainload.h
+++ b/common/protos/chainload.h
@@ -3,4 +3,14 @@
 
 void chainload(char *config);
 
+#if uefi == 1
+#include <fs/file.h>
+void efi_chainload_file(struct file_handle *image);
+#endif
+
+#if bios == 1
+#include <lib/part.h>
+void bios_chainload_volume(struct volume *v);
+#endif
+
 #endif
diff --git a/common/protos/chainload_next.c b/common/protos/chainload_next.c
new file mode 100644
index 00000000..669d0c1d
--- /dev/null
+++ b/common/protos/chainload_next.c
@@ -0,0 +1,69 @@
+#include <stddef.h>
+#include <stdint.h>
+#include <protos/chainload_next.h>
+#include <protos/chainload.h>
+#include <lib/blib.h>
+#include <lib/print.h>
+#include <lib/part.h>
+
+#if bios == 1
+static void try(struct volume *v) {
+    bios_chainload_volume(v);
+}
+#endif
+
+#if uefi == 1
+static void try(struct volume *v) {
+    for (int i = 0; i < v->max_partition + 1; i++) {
+        struct file_handle *image;
+
+        if ((image = fopen(v, "/EFI/BOOT/BOOTX64.EFI")) == NULL
+         && (image = fopen(v, "/efi/boot/bootx64.efi")) == NULL
+         && (image = fopen(v, "/EFI/BOOT/BOOTx64.efi")) == NULL) {
+            continue;
+        }
+
+        uefi_chainload_file(image);
+    }
+}
+#endif
+
+void chainload_next(char *config) {
+    (void)config;
+
+    bool wrap = false;
+    for (int i = boot_volume->is_optical ? 0 : (wrap = true, boot_volume->index + 1);
+         boot_volume->is_optical ? true : i != boot_volume->index; i++) {
+        struct volume *v = volume_get_by_coord(false, i, 0);
+        if (v == NULL) {
+            if (wrap) {
+                i = 0;
+                continue;
+            } else {
+                break;
+            }
+        }
+
+        try(v);
+    }
+
+    wrap = false;
+    for (int i = boot_volume->is_optical ? (wrap = true, boot_volume->index + 1) : 0;
+         boot_volume->is_optical ? i != boot_volume->index : true; i++) {
+        struct volume *v = volume_get_by_coord(true, i, 0);
+        if (v == NULL) {
+            if (wrap) {
+                i = 0;
+                continue;
+            } else {
+                break;
+            }
+        }
+
+        try(v);
+    }
+
+    panic(true, "chainload_next: No other bootable device");
+
+    __builtin_unreachable();
+}
diff --git a/common/protos/chainload_next.h b/common/protos/chainload_next.h
new file mode 100644
index 00000000..226a8d97
--- /dev/null
+++ b/common/protos/chainload_next.h
@@ -0,0 +1,6 @@
+#ifndef __PROTOS__CHAINLOAD_NEXT_H__
+#define __PROTOS__CHAINLOAD_NEXT_H__
+
+void chainload_next(char *config);
+
+#endif
tab: 248 wrap: offon