From 95272c5f1d0873efcd92403c7f9ec645246decce Mon Sep 17 00:00:00 2001 From: mintsuki Date: Mon, 11 Jul 2022 00:15:56 +0200 Subject: [PATCH] protos: Add chainload_next protocol. Closes #191 --- CONFIG.md | 11 +++++- common/menu.c | 3 ++ common/protos/chainload.c | 20 +++++++++- common/protos/chainload.h | 10 +++++ common/protos/chainload_next.c | 69 ++++++++++++++++++++++++++++++++++ common/protos/chainload_next.h | 6 +++ 6 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 common/protos/chainload_next.c create mode 100644 common/protos/chainload_next.h 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 `xx`. If the resolution is not available, Limine will pick another one automatically. Omitting `` 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 `xx`. If the resolution is not available, Limine will pick another one automatically. Omitting `` 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 `xx`. If the resolution is not available, Limine will pick another one automatically. Omitting `` 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 `xx` and *overrides* any resolution requested by the kernel. If the resolution is not available, Limine will pick another one automatically. Omitting `` 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 #include #include +#include #include #include #include @@ -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 +void efi_chainload_file(struct file_handle *image); +#endif + +#if bios == 1 +#include +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 +#include +#include +#include +#include +#include +#include + +#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