uefi: Implement chainloading support
diff --git a/stage23/drivers/disk.s2.c b/stage23/drivers/disk.s2.c
index de658555..84385b8b 100644
--- a/stage23/drivers/disk.s2.c
+++ b/stage23/drivers/disk.s2.c
@@ -213,10 +213,14 @@ struct volume *disk_volume_from_efi_handle(EFI_HANDLE *efi_handle) {
EFI_DISK_IO *disk_io = NULL;
EFI_BLOCK_IO *block_io = NULL;
- uefi_call_wrapper(gBS->HandleProtocol, 3, efi_handle, &disk_io_guid,
- &disk_io);
- uefi_call_wrapper(gBS->HandleProtocol, 3, efi_handle, &block_io_guid,
- &block_io);
+ status = uefi_call_wrapper(gBS->HandleProtocol, 3, efi_handle, &disk_io_guid,
+ &disk_io);
+ if (status)
+ return NULL;
+ status = uefi_call_wrapper(gBS->HandleProtocol, 3, efi_handle, &block_io_guid,
+ &block_io);
+ if (status)
+ return NULL;
uint64_t signature = BUILD_ID;
uint64_t orig;
@@ -233,6 +237,7 @@ struct volume *disk_volume_from_efi_handle(EFI_HANDLE *efi_handle) {
if (volume_index[i]->drive == 0xe0)
return volume_index[i];
}
+
return NULL;
}
diff --git a/stage23/entry.s3.c b/stage23/entry.s3.c
index a584739f..2f3889da 100644
--- a/stage23/entry.s3.c
+++ b/stage23/entry.s3.c
@@ -24,11 +24,28 @@ void stage3_common(void);
#if defined (uefi)
EFI_STATUS EFIAPI efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) {
+ // Invalid return address of 0 to end stacktraces here
+ asm volatile (
+ "push 0\n\t"
+ "xor eax, eax\n\t"
+ "jmp uefi_entry\n\t"
+ :
+ : "D" (ImageHandle), "S" (SystemTable)
+ : "memory"
+ );
+
+ __builtin_unreachable();
+}
+
+__attribute__((noreturn))
+void uefi_entry(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable) {
gST = SystemTable;
gBS = SystemTable->BootServices;
gRT = SystemTable->RuntimeServices;
efi_image_handle = ImageHandle;
+ EFI_STATUS status;
+
init_memmap();
term_vbe(0, 0);
@@ -38,24 +55,29 @@ EFI_STATUS EFIAPI efi_main(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable
disk_create_index();
- EFI_GUID loaded_img_prot_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
- EFI_LOADED_IMAGE_PROTOCOL *loaded_image = NULL;
+ EFI_HANDLE current_handle = ImageHandle;
+ for (;;) {
+ EFI_GUID loaded_img_prot_guid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
+ EFI_LOADED_IMAGE_PROTOCOL *loaded_image = NULL;
- uefi_call_wrapper(gBS->HandleProtocol, 3, ImageHandle, &loaded_img_prot_guid,
- &loaded_image);
+ status = uefi_call_wrapper(gBS->HandleProtocol, 3,
+ current_handle, &loaded_img_prot_guid,
+ &loaded_image);
- boot_volume = disk_volume_from_efi_handle(loaded_image->DeviceHandle);
- if (boot_volume == NULL) {
- panic("Can't determine boot disk");
- }
+ if (status) {
+ panic("HandleProtocol failure (%x)\n", status);
+ }
- // Invalid return address of 0 to end stacktraces here
- asm volatile (
- "push 0\n\t"
- "jmp stage3_common\n\t"
- );
+ boot_volume = disk_volume_from_efi_handle(loaded_image->DeviceHandle);
- __builtin_unreachable();
+ if (boot_volume != NULL)
+ stage3_common();
+
+ if (loaded_image->ParentHandle == 0)
+ panic("Can't determine boot disk");
+
+ current_handle = loaded_image->ParentHandle;
+ }
}
#endif
@@ -99,11 +121,7 @@ void stage3_common(void) {
} else if (!strcmp(proto, "linux")) {
linux_load(config, cmdline);
} else if (!strcmp(proto, "chainload")) {
-#if defined (bios)
chainload(config);
-#elif defined (uefi)
- panic("UEFI Limine does not support the chainload boot protocol");
-#endif
}
panic("Invalid protocol specified");
diff --git a/stage23/fs/file.s2.c b/stage23/fs/file.s2.c
index 503490b4..ed7a0e8d 100644
--- a/stage23/fs/file.s2.c
+++ b/stage23/fs/file.s2.c
@@ -113,7 +113,7 @@ int fread(struct file_handle *fd, void *buf, uint64_t loc, uint64_t count) {
void *freadall(struct file_handle *fd, uint32_t type) {
if (fd->is_memfile) {
- memmap_alloc_range((uint64_t)(size_t)fd->fd, fd->size, type, false, true, false, false);
+ memmap_alloc_range((uint64_t)(size_t)fd->fd, ALIGN_UP(fd->size, 4096), type, false, true, false, false);
return fd->fd;
} else {
void *ret = ext_mem_alloc_type(fd->size, type);
diff --git a/stage23/mm/pmm.h b/stage23/mm/pmm.h
index a5ccda61..6abe338f 100644
--- a/stage23/mm/pmm.h
+++ b/stage23/mm/pmm.h
@@ -15,6 +15,7 @@
#define MEMMAP_KERNEL_AND_MODULES 0x1001
#define MEMMAP_FRAMEBUFFER 0x1002
#define MEMMAP_EFI_RECLAIMABLE 0x2000
+#define MEMMAP_EFI_LOADER 0x2001
extern struct e820_entry_t memmap[];
extern size_t memmap_entries;
@@ -31,6 +32,7 @@ void *conv_mem_alloc(size_t count);
#if defined (uefi)
void pmm_reclaim_uefi_mem(void);
+void pmm_release_uefi_mem(void);
#endif
#endif
diff --git a/stage23/mm/pmm.s2.c b/stage23/mm/pmm.s2.c
index 8fab15ae..76508f3b 100644
--- a/stage23/mm/pmm.s2.c
+++ b/stage23/mm/pmm.s2.c
@@ -69,6 +69,8 @@ static const char *memmap_type(uint32_t type) {
return "Kernel/Modules";
case MEMMAP_EFI_RECLAIMABLE:
return "EFI reclaimable";
+ case MEMMAP_EFI_LOADER:
+ return "EFI loader";
default:
return "???";
}
@@ -280,7 +282,7 @@ void init_memmap(void) {
our_type = MEMMAP_EFI_RECLAIMABLE; break;
case EfiLoaderCode:
case EfiLoaderData:
- our_type = MEMMAP_BOOTLOADER_RECLAIMABLE; break;
+ our_type = MEMMAP_EFI_LOADER; break;
case EfiACPIReclaimMemory:
our_type = MEMMAP_ACPI_RECLAIMABLE; break;
case EfiACPIMemoryNVS:
@@ -323,13 +325,14 @@ void init_memmap(void) {
AllocateAddress, EfiLoaderData, memmap[i].length / 4096, &base);
if (status)
- panic("AllocatePages %x", status);
+ panic("pmm: AllocatePages failure (%x)", status);
}
}
void pmm_reclaim_uefi_mem(void) {
for (size_t i = 0; i < memmap_entries; i++) {
- if (memmap[i].type != MEMMAP_EFI_RECLAIMABLE)
+ if (memmap[i].type != MEMMAP_EFI_RECLAIMABLE
+ && memmap[i].type != MEMMAP_EFI_LOADER)
continue;
memmap[i].type = MEMMAP_USABLE;
@@ -337,6 +340,25 @@ void pmm_reclaim_uefi_mem(void) {
sanitise_entries(false);
}
+
+void pmm_release_uefi_mem(void) {
+ EFI_STATUS status;
+
+ for (size_t i = 0; i < memmap_entries; i++) {
+ if (memmap[i].type != MEMMAP_USABLE
+ && memmap[i].type != MEMMAP_BOOTLOADER_RECLAIMABLE) {
+ continue;
+ }
+
+ status = uefi_call_wrapper(gBS->FreePages, 2,
+ memmap[i].base, memmap[i].length / 4096);
+
+ if (status)
+ panic("pmm: FreePages failure (%x)", status);
+ }
+
+ allocations_disallowed = true;
+}
#endif
void *ext_mem_alloc(size_t count) {
diff --git a/stage23/protos/chainload.c b/stage23/protos/chainload.c
index bc15f036..5744ef87 100644
--- a/stage23/protos/chainload.c
+++ b/stage23/protos/chainload.c
@@ -1,5 +1,3 @@
-#if defined (bios)
-
#include <stddef.h>
#include <stdint.h>
#include <protos/chainload.h>
@@ -8,8 +6,17 @@
#include <lib/blib.h>
#include <drivers/disk.h>
#include <lib/term.h>
+#include <lib/fb.h>
+#include <lib/uri.h>
+#include <lib/print.h>
#include <sys/idt.h>
#include <drivers/vga_textmode.h>
+#include <mm/pmm.h>
+#if defined (uefi)
+# include <efi.h>
+#endif
+
+#if defined (bios)
__attribute__((noinline))
__attribute__((section(".realmode")))
@@ -95,4 +102,65 @@ void chainload(char *config) {
spinup(drive);
}
+#elif defined (uefi)
+
+void chainload(char *config) {
+ EFI_STATUS status;
+
+ char *image_path = config_get_value(config, 0, "IMAGE_PATH");
+ if (image_path == NULL)
+ panic("chainload: IMAGE_PATH not specified");
+
+ struct file_handle *image = ext_mem_alloc(sizeof(struct file_handle));
+ if (!uri_open(image, image_path))
+ panic("chainload: Could not open image");
+
+ void *ptr = freadall(image, MEMMAP_RESERVED);
+
+ term_deinit();
+
+ int req_width = 0, req_height = 0, req_bpp = 0;
+
+ char *resolution = config_get_value(config, 0, "RESOLUTION");
+ if (resolution != NULL)
+ parse_resolution(&req_width, &req_height, &req_bpp, resolution);
+
+ struct fb_info fbinfo;
+ if (!fb_init(&fbinfo, req_width, req_height, req_bpp))
+ panic("chainload: Unable to set video mode");
+
+ pmm_release_uefi_mem();
+
+ MEMMAP_DEVICE_PATH memdev_path[2];
+
+ memdev_path[0].Header.Type = HARDWARE_DEVICE_PATH;
+ memdev_path[0].Header.SubType = HW_MEMMAP_DP;
+ memdev_path[0].Header.Length[0] = sizeof(MEMMAP_DEVICE_PATH);
+ memdev_path[0].Header.Length[1] = sizeof(MEMMAP_DEVICE_PATH) >> 8;
+
+ memdev_path[0].MemoryType = EfiLoaderData;
+ memdev_path[0].StartingAddress = (uintptr_t)ptr;
+ memdev_path[0].EndingAddress = (uintptr_t)ptr + image->size;
+
+ memdev_path[1].Header.Type = END_DEVICE_PATH_TYPE;
+ memdev_path[1].Header.SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE;
+ memdev_path[1].Header.Length[0] = sizeof(MEMMAP_DEVICE_PATH);
+ memdev_path[1].Header.Length[1] = sizeof(MEMMAP_DEVICE_PATH) >> 8;
+
+ EFI_HANDLE new_handle = 0;
+
+ status = uefi_call_wrapper(gBS->LoadImage, 6, 0, efi_image_handle, memdev_path,
+ ptr, image->size, &new_handle);
+ if (status) {
+ panic("chainload: LoadImage failure (%x)", status);
+ }
+
+ status = uefi_call_wrapper(gBS->StartImage, 3, new_handle, NULL, NULL);
+ if (status) {
+ panic("chainload: StartImage failure (%x)", status);
+ }
+
+ __builtin_unreachable();
+}
+
#endif
diff --git a/stage23/protos/linux.c b/stage23/protos/linux.c
index 89fb3715..983bd6ec 100644
--- a/stage23/protos/linux.c
+++ b/stage23/protos/linux.c
@@ -564,6 +564,7 @@ void linux_load(char *config, char *cmdline) {
switch (e820_table[i].type) {
case MEMMAP_BOOTLOADER_RECLAIMABLE:
case MEMMAP_EFI_RECLAIMABLE:
+ case MEMMAP_EFI_LOADER:
e820_table[i].type = MEMMAP_USABLE;
break;
}
diff --git a/test/limine.cfg b/test/limine.cfg
index 0c44931a..5c46ad67 100644
--- a/test/limine.cfg
+++ b/test/limine.cfg
@@ -20,6 +20,11 @@ KERNEL_CMDLINE=Woah! Another example!
MODULE_PATH=boot:///boot/bg.bmp
MODULE_STRING=yooooo
+:EFI Chainloading
+
+PROTOCOL=chainload
+IMAGE_PATH=boot:///EFI/BOOT/BOOTX64.EFI
+
:+Legacy
::Stivale Test
