From 74b421ba89133eadf4c58a08f0fc82c72d84e8da Mon Sep 17 00:00:00 2001 From: a1ive <10670106+a1ive@users.noreply.github.com> Date: Tue, 18 Feb 2025 21:24:11 +0900 Subject: [PATCH] text mode --- disk/efidisk.c | 2 +- docs/cmdline.md | 6 ++++ include/cmdline.h | 1 + include/efi.h | 3 +- kern/cmdline.c | 5 +++ kern/efi.c | 9 +++-- kern/efiboot.c | 83 +++++++++++++++++++++++++++++++++++++++-------- kern/efifile.c | 4 +-- kern/main.c | 13 ++++++-- 9 files changed, 104 insertions(+), 22 deletions(-) diff --git a/disk/efidisk.c b/disk/efidisk.c index e52544b..a5d45fd 100644 --- a/disk/efidisk.c +++ b/disk/efidisk.c @@ -426,7 +426,7 @@ efidisk_read (void *disk, uint64_t sector, size_t len, void *buf) EFI_BLOCK_IO_PROTOCOL *bio; EFI_STATUS status; UINTN pages = BYTES_TO_PAGES (len); - void *mem = efi_allocate_pages (pages); + void *mem = efi_allocate_pages (pages, EfiLoaderData); bio = d->bio; diff --git a/docs/cmdline.md b/docs/cmdline.md index cd2e840..30b2c0d 100644 --- a/docs/cmdline.md +++ b/docs/cmdline.md @@ -43,6 +43,12 @@ file=/path/to/xxx.vhd ``` The path to the WIM/VHD/VHDX file. Bootloader will automatically detect the file type. +### text +``` +text +``` +Force the Windows boot manager to display error messages in text mode. + ### testmode ``` testmode=yes|no diff --git a/include/cmdline.h b/include/cmdline.h index 7a94130..0e5bb3c 100644 --- a/include/cmdline.h +++ b/include/cmdline.h @@ -40,6 +40,7 @@ struct nt_args { + uint8_t textmode; uint8_t testmode; uint8_t hires; uint8_t hal; diff --git a/include/efi.h b/include/efi.h index ab8cb39..28e3a31 100644 --- a/include/efi.h +++ b/include/efi.h @@ -74,10 +74,11 @@ extern EFI_GUID efi_device_path_protocol_guid; extern EFI_GUID efi_loaded_image_protocol_guid; extern EFI_GUID efi_simple_file_system_protocol_guid; extern EFI_GUID efi_load_file2_protocol_guid; +extern EFI_GUID efi_gop_guid; extern void *efi_malloc (size_t size); extern void efi_free (void *ptr); extern void efi_free_pages (void *ptr, UINTN pages); -extern void *efi_allocate_pages (UINTN pages); +extern void *efi_allocate_pages (UINTN pages, EFI_MEMORY_TYPE type); #endif /* _EFI_H */ diff --git a/kern/cmdline.c b/kern/cmdline.c index 0793e2c..6fb0ca2 100644 --- a/kern/cmdline.c +++ b/kern/cmdline.c @@ -29,6 +29,7 @@ static struct nt_args args = { + .textmode = NTARG_BOOL_FALSE, .testmode = NTARG_BOOL_FALSE, .hires = NTARG_BOOL_UNSET, // wim = yes .hal = NTARG_BOOL_TRUE, @@ -185,6 +186,10 @@ void process_cmdline (char *cmdline) else args.boottype = NTBOOT_VHD; } + else if (strcmp (key, "text") == 0) + { + args.textmode = NTARG_BOOL_TRUE; + } else if (strcmp (key, "testmode") == 0) { args.testmode = convert_bool (value); diff --git a/kern/efi.c b/kern/efi.c index 15d3205..c6be3b3 100644 --- a/kern/efi.c +++ b/kern/efi.c @@ -23,6 +23,7 @@ #include "efi/Protocol/SimpleFileSystem.h" #include "efi/Protocol/LoadFile2.h" #include "efi/Protocol/LoadedImage.h" +#include "efi/Protocol/GraphicsOutput.h" /** EFI system table */ EFI_SYSTEM_TABLE *efi_systab; @@ -50,6 +51,10 @@ EFI_GUID efi_simple_file_system_protocol_guid EFI_GUID efi_load_file2_protocol_guid = EFI_LOAD_FILE2_PROTOCOL_GUID; +/** Graphics output protocol GUID */ +EFI_GUID efi_gop_guid += EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; + void *efi_malloc (size_t size) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; @@ -73,13 +78,13 @@ void efi_free_pages (void *ptr, UINTN pages) efi_systab->BootServices->FreePages (addr, pages); } -void *efi_allocate_pages (UINTN pages) +void *efi_allocate_pages (UINTN pages, EFI_MEMORY_TYPE type) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; EFI_STATUS efirc; EFI_PHYSICAL_ADDRESS addr = 0; efirc = bs->AllocatePages (AllocateAnyPages, - EfiLoaderData, pages, &addr); + type, pages, &addr); if (efirc != EFI_SUCCESS) die ("Could not allocate memory.\n"); return (void *) (intptr_t) addr; diff --git a/kern/efiboot.c b/kern/efiboot.c index c8f6e19..f4d2fe0 100644 --- a/kern/efiboot.c +++ b/kern/efiboot.c @@ -31,6 +31,68 @@ #include "vdisk.h" #include "efi.h" #include "efiboot.h" +#include "efi/Protocol/GraphicsOutput.h" + +/** Original OpenProtocol() method */ +static EFI_OPEN_PROTOCOL orig_open_protocol; + +/** Dummy "opening gop blocked once" protocol GUID */ +static EFI_GUID efi_gop_blocked_guid = +{ + 0xbd1598bf, 0x8e65, 0x47e0, + { 0x80, 0x01, 0xe4, 0x62, 0x4c, 0xab, 0xa4, 0x7f } +}; + +/** Dummy "opening gop blocked once" protocol instance */ +static uint8_t efi_gop_blocked; + +/** + * Intercept OpenProtocol() + * + * @v handle EFI handle + * @v protocol Protocol GUID + * @v interface Opened interface + * @v agent_handle Agent handle + * @v controller_handle Controller handle + * @v attributes Attributes + * @ret efirc EFI status code + */ +static EFI_STATUS EFIAPI +efi_open_protocol_wrapper (EFI_HANDLE handle, EFI_GUID *protocol, + VOID **interface, EFI_HANDLE agent_handle, + EFI_HANDLE controller_handle, + UINT32 attributes) +{ + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_STATUS efirc; + + /* Open the protocol */ + efirc = orig_open_protocol (handle, protocol, interface, + agent_handle, controller_handle, + attributes); + if (efirc != 0) + return efirc; + + /* Block first attempt by bootmgfw.efi to open each + * EFI_GRAPHICS_OUTPUT_PROTOCOL. This forces error messages + * to be displayed in text mode (thereby avoiding the totally + * blank error screen if the fonts are missing). We must + * allow subsequent attempts to succeed, otherwise the OS will + * fail to boot. + */ + if ((memcmp (protocol, &efi_gop_guid, sizeof (*protocol)) == 0) && + (bs->InstallMultipleProtocolInterfaces (&handle, + &efi_gop_blocked_guid, + &efi_gop_blocked, + NULL) == 0) && + (nt_cmdline->textmode)) + { + DBG ("Forcing text mode output\n"); + return EFI_INVALID_PARAMETER; + } + + return 0; +} /** * Boot from EFI device @@ -47,24 +109,13 @@ void efi_boot (EFI_DEVICE_PATH_PROTOCOL *path, EFI_LOADED_IMAGE_PROTOCOL *image; void *intf; } loaded; - EFI_PHYSICAL_ADDRESS phys; void *data; - unsigned int pages; EFI_HANDLE handle; EFI_STATUS efirc; /* Allocate memory */ - pages = ((nt_cmdline->bootmgr_length + PAGE_SIZE - 1) / PAGE_SIZE); - efirc = bs->AllocatePages (AllocateAnyPages, - EfiBootServicesData, pages, - &phys); - if (efirc != 0) - { - die ("Could not allocate %d pages: %#lx\n", - pages, ((unsigned long) efirc)); - } - data = ((void *) (intptr_t) phys); - + data = efi_allocate_pages ( + BYTES_TO_PAGES (nt_cmdline->bootmgr_length), EfiLoaderCode); /* Copy image */ memcpy (data, nt_cmdline->bootmgr, nt_cmdline->bootmgr_length); @@ -98,6 +149,12 @@ void efi_boot (EFI_DEVICE_PATH_PROTOCOL *path, loaded.image->DeviceHandle = device; } + /* Intercept calls to OpenProtocol() */ + orig_open_protocol = + loaded.image->SystemTable->BootServices->OpenProtocol; + loaded.image->SystemTable->BootServices->OpenProtocol = + efi_open_protocol_wrapper; + /* Start image */ if ((efirc = bs->StartImage (handle, NULL, NULL)) != 0) { diff --git a/kern/efifile.c b/kern/efifile.c index e7fbaab..ea6c9a2 100644 --- a/kern/efifile.c +++ b/kern/efifile.c @@ -95,7 +95,7 @@ efi_load_sfs_initrd (UINTN *len, EFI_HANDLE handle) die ("Could not get file size\n"); DBG ("...found %ls size 0x%llx\n", wpath, size); *len = size; - initrd = efi_allocate_pages (BYTES_TO_PAGES (*len)); + initrd = efi_allocate_pages (BYTES_TO_PAGES (*len), EfiLoaderData); efirc = file->Read (file, len, initrd); if (efirc != EFI_SUCCESS) die ("Could not read from file.\n"); @@ -140,7 +140,7 @@ efi_load_lf2_initrd (UINTN *len) die ("Could not get initrd size\n"); /* Allocate memory */ - initrd = efi_allocate_pages (BYTES_TO_PAGES (*len)); + initrd = efi_allocate_pages (BYTES_TO_PAGES (*len), EfiLoaderData); /* Read initrd */ efirc = lf2->LoadFile (lf2, dp, FALSE, len, initrd); diff --git a/kern/main.c b/kern/main.c index 9db3e40..86b4fe8 100644 --- a/kern/main.c +++ b/kern/main.c @@ -73,11 +73,11 @@ enum static void call_interrupt_wrapper (struct bootapp_callback_params *params) { struct paging_state state; + uint16_t *attributes; /* Handle/modify/pass-through interrupt as required */ if (params->vector.interrupt == 0x13) { - /* Enable paging */ enable_paging (&state); @@ -86,11 +86,18 @@ static void call_interrupt_wrapper (struct bootapp_callback_params *params) /* Disable paging */ disable_paging (&state); - + } + else if ((params->vector.interrupt == 0x10) && + (params->ax == 0x4f01) && + (nt_cmdline->textmode)) + { + /* Mark all VESA video modes as unsupported */ + attributes = REAL_PTR ( params->es, params->di ); + call_interrupt ( params ); + *attributes &= ~0x0001; } else { - /* Pass through interrupt */ call_interrupt (params); }