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); + } + + boot_volume = disk_volume_from_efi_handle(loaded_image->DeviceHandle); + + if (boot_volume != NULL) + stage3_common(); + + if (loaded_image->ParentHandle == 0) + panic("Can't determine boot disk"); + + current_handle = loaded_image->ParentHandle; } - - // Invalid return address of 0 to end stacktraces here - asm volatile ( - "push 0\n\t" - "jmp stage3_common\n\t" - ); - - __builtin_unreachable(); } #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 #include #include @@ -8,8 +6,17 @@ #include #include #include +#include +#include +#include #include #include +#include +#if defined (uefi) +# include +#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