:: commit db3a41bc4c842f9e29a2e73990b12702c84d77a5

mintsuki <mintsuki@protonmail.com> — 2021-05-06 02:31

parents: 96b987273d

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