protos: Add chainload_next protocol. Closes #191
diff --git a/CONFIG.md b/CONFIG.md
index e57786cf..728cfeb3 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`, `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 -> limine -> linux -> failure`.
+* `PROTOCOL` - The boot protocol that will be used to boot the kernel. Valid protocols are: `linux`, `limine`, `stivale`, `stivale2`, `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: `stivale2 -> stivale1 -> 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,21 +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 14472184..c6b169fd 100644
--- a/common/menu.c
+++ b/common/menu.c
@@ -19,6 +19,7 @@
#include <protos/stivale2.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>
@@ -941,6 +942,8 @@ autodetect:
ret = multiboot1_load(config, cmdline);
} else if (!strcmp(proto, "multiboot2")) {
ret = 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
