From cc44e6eb9b169a159c8a86416af1266199f01c24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauri=20Kentt=C3=A4?= Date: Wed, 11 May 2016 22:53:54 +0300 Subject: [PATCH] Initial release to GitHub --- Doxyfile | 12 ++ Makefile | 27 +++++ README.efilib | 31 +++++ README.md | 48 ++++++++ config.txt | Bin 0 -> 3178 bytes install.bat | 120 +++++++++++++++++++ splash.bmp | Bin 0 -> 98358 bytes src/config.c | 102 +++++++++++++++++ src/config.h | 51 +++++++++ src/main.c | 311 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/types.h | 54 +++++++++ src/util.c | 115 +++++++++++++++++++ src/util.h | 101 ++++++++++++++++ uninstall.bat | 9 ++ 14 files changed, 981 insertions(+) create mode 100644 Doxyfile create mode 100644 Makefile create mode 100644 README.efilib create mode 100644 README.md create mode 100755 config.txt create mode 100755 install.bat create mode 100644 splash.bmp create mode 100644 src/config.c create mode 100644 src/config.h create mode 100644 src/main.c create mode 100644 src/types.h create mode 100644 src/util.c create mode 100644 src/util.h create mode 100755 uninstall.bat diff --git a/Doxyfile b/Doxyfile new file mode 100644 index 0000000..fdd2ff9 --- /dev/null +++ b/Doxyfile @@ -0,0 +1,12 @@ +INPUT = src +FILE_PATTERNS = *.c *.h +JAVADOC_AUTOBRIEF = YES +EXTRACT_ALL = YES +EXTRACT_STATIC = YES +STRIP_CODE_COMMENTS = NO +INLINE_SOURCES = NO +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_DYNAMIC_SECTIONS = YES +GENERATE_LATEX = NO diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1d72cf6 --- /dev/null +++ b/Makefile @@ -0,0 +1,27 @@ +CC = $(CC_PREFIX)-gcc +CFLAGS = -std=c11 -O2 -ffreestanding -mno-red-zone -fno-stack-protector -Wshadow -Wall -Wunused -Werror-implicit-function-declaration -Werror +CFLAGS += -I$(GNUEFI_INC) -I$(GNUEFI_INC)/$(GNUEFI_ARCH) -I$(GNUEFI_INC)/protocol +LDFLAGS = -nostdlib -shared -Wl,-dll -Wl,--subsystem,10 -e _EfiMain +LIBS = -L$(GNUEFI_LIB) -lefi -lgcc + +GNUEFI_INC = /usr/$(CC_PREFIX)/include/efi +GNUEFI_LIB = /usr/$(CC_PREFIX)/lib + +FILES_C = src/main.c src/util.c src/config.c +FILES_H = $(wildcard src/*.h) + +.PHONY: all +all: bootx64.efi + +bootx64.efi: CC_PREFIX = x86_64-w64-mingw32 +bootx64.efi: GNUEFI_ARCH = x86_64 +bootx64.efi: $(FILES_C) + $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) -s + +bootia32.efi: CC_PREFIX = i686-w64-mingw32 +bootia32.efi: GNUEFI_ARCH = ia32 +bootia32.efi: $(FILES_C) + $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) -s + +HackBGRT.tar.xz: bootx64.efi config.txt splash.bmp install.bat uninstall.bat README.md README.efilib LICENSE + tar cJf $@ --transform=s,^,HackBGRT/, $^ diff --git a/README.efilib b/README.efilib new file mode 100644 index 0000000..0d43424 --- /dev/null +++ b/README.efilib @@ -0,0 +1,31 @@ +# vim: set fileencoding=utf-8 + +HackBGRT uses the gnu-efi library, which in turn is using the EFI Application +Toolkit distributed by Intel at http://developer.intel.com/technology/efi + +This code is covered by the following agreement: + +Copyright (c) 1998-2000 Intel Corporation + +Redistribution and use in source and binary forms, with or without modification, are permitted +provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and +the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this list of conditions +and the following disclaimer in the documentation and/or other materials provided with the +distribution. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. THE EFI SPECIFICATION AND ALL OTHER INFORMATION +ON THIS WEB SITE ARE PROVIDED "AS IS" WITH NO WARRANTIES, AND ARE SUBJECT +TO CHANGE WITHOUT NOTICE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..16c3756 --- /dev/null +++ b/README.md @@ -0,0 +1,48 @@ +# HackBGRT + +HackBGRT is intended as a boot logo changer for UEFI-based Windows systems. + +## Summary + +When booting on a UEFI-based computer, Windows may show a vendor-defined logo which is stored on the UEFI firmware in a section called Boot Graphics Resource Table (BGRT). It's usually very difficult to change the image permamently, but a custom UEFI application may be used to overwrite it during the boot. HackBGRT does exactly that. + +## Usage + +**Important:** If you mess up the installation, your system may become unbootable! Create a rescue disk before use. This software comes with no warranty. Use at your own risk. + +* Make sure that your computer is booting with UEFI. +* Make sure that you have a 64-bit x86-64 processor. +* Make sure that Secure Boot is disabled, or learn to sign EFI applications. +* Simple Windows installation: + * Get at least these files: `bootx64.efi`, `config.txt`, `install.bat`, `splash.bmp`. + * Run Command Prompt as Administrator. + * Run `install.bat` from the Command Prompt. + * The installer will launch Paint for creating the image(s). + * The installer will launch Notepad for modifying the configuration. + * If Windows later reinstalls the original boot loader, run `install.bat` again. +* Installation for Windows with another boot loader (e.g. GRUB): + * Copy the mentioned files to `[EFI System Partition]\EFI\HackBGRT\`. + * Set `boot=\EFI\Microsoft\Boot\bootmgfw.efi` in `config.txt`. + * Point your boot loader to `\EFI\HackBGRT\bootx64.efi`. +* Installation for all operating systems: + * Copy the mentioned files to `[EFI System Partition]\EFI\HackBGRT\`. + * Set `boot=` to your preferred boot loader in `config.txt`. + * Set `\EFI\HackBGRT\bootx64.efi` as your default boot loader with `efibootmgr` or some other EFI boot manager tool. + +## Configuration + +The configuration options are described in `config.txt`, which should be stored in `[EFI System Partition]\EFI\HackBGRT\config.txt`. + +## Images + +The image path can be changed in the configuration file. The default path is `[EFI System Partition]\EFI\HackBGRT\splash.bmp`. + +The image must be a 24-bit BMP file with a 54-byte header. That's a TrueColor BMP3 in Imagemagick, or 24-bit BMP/DIB in Microsoft Paint. + +Multiple images may be specified, in which case one is picked at random. + +## Building + +* Compiler: GCC targeting w64-mingw32 +* Compiler flags: see Makefile +* Libraries: gnu-efi diff --git a/config.txt b/config.txt new file mode 100755 index 0000000000000000000000000000000000000000..5bb7962e33989aabd403286ecca700968fa4f129 GIT binary patch literal 3178 zcmb`J-A_|N5XI-&#Q)(29)$`h9wN2Z$ z7s2AOJu-Y8Sx?q@Xvr6&(nhi^^na+;Osg%s(EoPWJ=M-x@SkbDFN;!ptzb#SL9ibx za;9CP<8vg7v3`j#)$cgivzuw|2=?ubz1RFxnD=#hL$kSNg>va>T!d3I{mqrjOcrEZ z%3G#*xe(5jD+tNCVdNO|+>>3YSfDC}1bfbb3%LO}oEnR5edbUQL^q&XFG31#!bhsy6 ziNhfC9#;F~HH@gMl_>0oyv4f;`ms=USJ?V+vJhpx&c}#DROKp$@1X%E+Bcs!FY@eb^8W>nq$DGDpo)pBL*& zyn!Oo&4jVKns7#Cf-(b{46>9~;#$48@Ff8*JCz_+x^}1}^)4&YWtS1zF$HmtQKT&n-H$JmNDM(V3 z`}s_a;5)Drc*Gav{k$yeJ8vF2@CNhE1Hr)++#y?(WVq_WVoi#W(!hwf4kssOoJ4bDB4m7rFNIa@=)mZ>t!d F{sJNT2h;!n literal 0 HcmV?d00001 diff --git a/install.bat b/install.bat new file mode 100755 index 0000000..cc1cc41 --- /dev/null +++ b/install.bat @@ -0,0 +1,120 @@ +@ECHO OFF +CD %~dp0 + +IF NOT "%1" == "uninstall" ( + IF NOT EXIST bootx64.efi ( + ECHO Missing bootx64.efi, you're doing something wrong. + GOTO fail_before_esp + ) +) + +SET ESP_UNMOUNT=1 +SET ESP=- +FOR /F "delims=" %%I IN ('CMD /C "MOUNTVOL | FINDSTR /C:EFI | FINDSTR /C::"') DO ( + ECHO %%I + SET ESP_STR=%%I + SET ESP=%ESP_STR:~-3,2% + SET ESP_UNMOUNT=0 +) +IF %ESP% == - MOUNTVOL S: /S >NUL && SET ESP=S: +IF %ESP% == - MOUNTVOL B: /S >NUL && SET ESP=B: +IF %ESP% == - MOUNTVOL A: /S >NUL && SET ESP=A: +IF %ESP% == - MOUNTVOL X: /S >NUL && SET ESP=X: + +IF %ESP% == - ( + ECHO The EFI System Partition is not mounted. + GOTO fail_before_esp +) + +SET HackBGRT=%ESP%\EFI\HackBGRT +SET MSBOOT=%ESP%\EFI\Microsoft\Boot + +IF NOT EXIST %MSBOOT% ( + ECHO %MSBOOT% does not exist. + ECHO If the path seems incorrect, report a bug. + GOTO fail +) + +IF "%1" == "uninstall" ( + IF NOT EXIST %HackBGRT%\bootmgfw-original.efi ( + ECHO Missing %HackBGRT%\bootmgfw-original.efi! + GOTO fail + ) + COPY %HackBGRT%\bootmgfw-original.efi %MSBOOT%\bootmgfw.efi >NUL || ( + ECHO Failed to restore the original bootmgfw.efi. + GOTO fail + ) + ECHO The original bootmgfw.efi has been restored. + IF EXIST %HackBGRT% ( + DEL /P %HackBGRT% + ) + EXIT /B +) + +IF NOT EXIST %HackBGRT% ( + MKDIR %HackBGRT% +) +IF NOT EXIST %HackBGRT%\bootmgfw-original.efi ( + COPY %MSBOOT%\bootmgfw.efi %HackBGRT%\bootmgfw-original.efi >NUL || ( + ECHO Couldn't copy the original bootmgfw.efi. + GOTO fail + ) +) + +ECHO Copying files... +COPY /Y LICENSE %HackBGRT%\ >NUL +COPY /Y README.md %HackBGRT%\ >NUL +COPY /Y README.efilib %HackBGRT%\ >NUL +COPY /Y install.bat %HackBGRT%\ >NUL +COPY /Y uninstall.bat %HackBGRT%\ >NUL +COPY /Y bootx64.efi %HackBGRT%\ >NUL || GOTO fail +IF NOT EXIST %HackBGRT%\splash.bmp ( + COPY splash.bmp %HackBGRT%\ >NUL || GOTO fail +) +IF EXIST %HackBGRT%\config.txt ( + ECHO Copying configuration as config-new.txt. + ECHO Be sure to check for any format changes! + COPY /Y config.txt %HackBGRT%\config-new.txt >NUL || GOTO fail +) ELSE ( + COPY /Y config.txt %HackBGRT%\config.txt >NUL || GOTO fail +) + +ECHO Draw or copy your preferred image to splash.bmp. +START /WAIT mspaint %HackBGRT%\splash.bmp + +ECHO Check the configuration in config.txt. +IF EXIST %HackBGRT%\config-new.txt ( + ECHO See config-new.txt for reference. + START notepad %HackBGRT%\config-new.txt +) +START /WAIT notepad %HackBGRT%\config.txt + +ECHO Replacing bootmgfw.efi. +COPY /Y bootx64.efi %MSBOOT%\bootmgfw.efi >NUL || ( + ECHO Failed to copy the boot loader! + ECHO Restoring the original bootmgfw.efi... + COPY %HackBGRT%\bootmgfw-original.efi %MSBOOT%\bootmgfw.efi >NUL || ( + ECHO Restoration failed You will need to fix this! + ) + GOTO fail +) + +IF %ESP_UNMOUNT% == 1 ( + MOUNTVOL %ESP% /D +) + +ECHO Installation is ready. +ECHO If your CPU is not x86-64, you should definitely uninstall now. +ECHO Remember to disable Secure Boot, or HackBGRT will not boot. +PAUSE +EXIT /B + +:fail +IF %ESP_UNMOUNT% == 1 ( + MOUNTVOL %ESP% /D +) + +:fail_before_esp +ECHO Exiting due to errors. +PAUSE +EXIT /B 1 diff --git a/splash.bmp b/splash.bmp new file mode 100644 index 0000000000000000000000000000000000000000..3046a1d6787a3ccc0670983d9df6d516bdd6274f GIT binary patch literal 98358 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 literal 0 HcmV?d00001 diff --git a/src/config.c b/src/config.c new file mode 100644 index 0000000..60e13a4 --- /dev/null +++ b/src/config.c @@ -0,0 +1,102 @@ +#include "config.h" +#include "util.h" + +#include + +BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, const CHAR16* path) { + CHAR16* str = 0; + UINTN str_bytes = 0; + str = LoadFileWithPadding(root_dir, path, &str_bytes, sizeof(*str)); + if (!str) { + Print(L"HackBGRT: Failed to load configuration (%s)!\n", path); + return FALSE; + } + UINTN str_len = str_bytes / sizeof(*str); + + for (int i = 0; i < str_len;) { + int j = i; + while (j < str_len && str[j] != '\r' && str[j] != '\n') { + ++j; + } + while (j < str_len && (str[j] == '\r' || str[j] == '\n')) { + str[j] = 0; + ++j; + } + ReadConfigLine(config, root_dir, &str[i]); + i = j; + } + // NOTICE: string is not freed, because paths are not copied. + return TRUE; +} + +static void SetBMPWithRandom(struct HackBGRT_config* config, int weight, enum HackBGRT_action action, int x, int y, const CHAR16* path) { + config->image_weight_sum += weight; + UINT32 random = Random(); + UINT32 limit = 0xfffffffful / config->image_weight_sum * weight; + if (config->debug) { + Print(L"HackBGRT: weight %d, action %d, x %d, y %d, path %s, random = %08x, limit = %08x\n", weight, action, x, y, path, random, limit); + } + if (!config->image_weight_sum || random <= limit) { + config->action = action; + config->image_path = path; + config->image_x = x; + config->image_y = y; + } +} + +static int ParseCoordinate(const CHAR16* str, enum HackBGRT_action action) { + if (str && L'0' <= str[0] && str[0] <= L'9') { + return Atoi(str); + } + if ((str && StrnCmp(str, L"native", 6) == 0) || action == HackBGRT_KEEP) { + return HackBGRT_coord_native; + } + return HackBGRT_coord_auto; +} + +static void ReadConfigImage(struct HackBGRT_config* config, const CHAR16* line) { + const CHAR16* n = StrStrAfter(line, L"n="); + const CHAR16* x = StrStrAfter(line, L"x="); + const CHAR16* y = StrStrAfter(line, L"y="); + const CHAR16* f = StrStrAfter(line, L"path="); + enum HackBGRT_action action = HackBGRT_KEEP; + if (f) { + action = HackBGRT_REPLACE; + } else if (StrStr(line, L"remove")) { + action = HackBGRT_REMOVE; + } else if (StrStr(line, L"black")) { + action = HackBGRT_BLACK; + } else if (StrStr(line, L"keep")) { + action = HackBGRT_KEEP; + } else { + Print(L"HackBGRT: Invalid image line: %s\n", line); + return; + } + int weight = n && (!f || n < f) ? Atoi(n) : 1; + SetBMPWithRandom(config, weight, action, ParseCoordinate(x, action), ParseCoordinate(y, action), f); +} + +void ReadConfigLine(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, const CHAR16* line) { + line = TrimLeft(line); + if (line[0] == L'#' || line[0] == 0) { + return; + } + + if (StrnCmp(line, L"debug=", 6) == 0) { + config->debug = (StrCmp(line, L"debug=1") == 0); + return; + } + if (StrnCmp(line, L"image=", 6) == 0) { + ReadConfigImage(config, line + 6); + return; + } + if (StrnCmp(line, L"boot=", 5) == 0) { + config->boot_path = line + 5; + return; + } + if (StrnCmp(line, L"config=", 7) == 0) { + ReadConfigFile(config, root_dir, line + 7); + return; + } + Print(L"Unknown configuration directive: %s\n", line); +} diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..b8a0089 --- /dev/null +++ b/src/config.h @@ -0,0 +1,51 @@ +#pragma once + +#include + +/** + * Possible actions to perform on the BGRT. + */ +enum HackBGRT_action { + HackBGRT_KEEP = 0, HackBGRT_REPLACE, HackBGRT_REMOVE, HackBGRT_BLACK +}; + +/** + * Special values for the image coordinates. + * @see struct HackBGRT_config + */ +enum HackBGRT_coordinate { + HackBGRT_coord_auto = 0x10000001, + HackBGRT_coord_native = 0x10000002 +}; + +/** + * The configuration. + */ +struct HackBGRT_config { + int debug; + enum HackBGRT_action action; + const CHAR16* image_path; + int image_x; + int image_y; + int image_weight_sum; + const CHAR16* boot_path; +}; + +/** + * Read a configuration parameter. (May recursively read config files.) + * + * @param config The configuration to modify. + * @param root_dir The root directory, in case the parameter contains an include. + * @param line The configuration line to parse. + */ +extern void ReadConfigLine(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, const CHAR16* line); + +/** + * Read a configuration file. (May recursively read more files.) + * + * @param config The configuration to modify. + * @param root_dir The root directory. + * @param path The path to the file. + * @return FALSE, if the file couldn't be read, TRUE otherwise. + */ +extern BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, const CHAR16* path); diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..3108ea5 --- /dev/null +++ b/src/main.c @@ -0,0 +1,311 @@ +#include +#include + +#include "types.h" +#include "config.h" +#include "util.h" + +/** + * The Print function signature. + */ +typedef UINTN print_t(IN CHAR16 *fmt, ...); + +/** + * The function for debug printing; either Print or NullPrint. + */ +print_t* Debug = NullPrint; + +/** + * The configuration. + */ +static struct HackBGRT_config config = { + .action = HackBGRT_KEEP +}; + +/** + * Get the GOP (Graphics Output Protocol) pointer. + */ +static EFI_GRAPHICS_OUTPUT_PROTOCOL* GOP(void) { + static EFI_GRAPHICS_OUTPUT_PROTOCOL* gop; + if (!gop) { + EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; + LibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **)&gop); + } + return gop; +} + +/** + * Select the correct coordinate (manual, automatic, native) + * + * @param value The configured coordinate value; has special values for automatic and native. + * @param automatic The automatically calculated alternative. + * @param native The original coordinate. + * @see enum HackBGRT_coordinate + */ +static int SelectCoordinate(int value, int automatic, int native) { + if (value == HackBGRT_coord_auto) { + return automatic; + } + if (value == HackBGRT_coord_native) { + return native; + } + return value; +} + +/** + * Initialize (clear) a BGRT. + * + * @param bgrt The BGRT to initialize. + */ +static void InitBGRT(ACPI_BGRT* bgrt) { + const char data[0x38] = "BGRT" "\x38\x00\x00\x00" "\x00" "\xd6" "Mtblx*" "HackBGRT" "\x20\x17\x00\x00" "PTL " "\x02\x00\x00\x00" "\x01\x00" "\x00" "\x00"; + CopyMem(bgrt, data, sizeof(data)); +} + +/** + * Fill a BGRT as specified by the parameters. + * + * @param bgrt The BGRT to fill. + * @param new_bmp The BMP to use. + * @param new_x The x coordinate to use. + * @param new_y The y coordinate to use. + */ +static void FillBGRT(ACPI_BGRT* bgrt, BMP* new_bmp, int new_x, int new_y) { + BMP* old_bmp = (BMP*) (UINTN) bgrt->image_address; + ACPI_BGRT bgrt0 = *bgrt; + InitBGRT(bgrt); + + if (new_bmp) { + bgrt->image_address = (UINTN) new_bmp; + } + BMP* bmp = (BMP*) (UINTN) bgrt->image_address; + + // Calculate the automatically centered position for the image. + int x_auto, y_auto; + if (GOP()) { + x_auto = max(0, ((int)GOP()->Mode->Info->HorizontalResolution - (int)bmp->width) / 2); + y_auto = max(0, ((int)GOP()->Mode->Info->VerticalResolution * 2/3 - (int)bmp->height) / 2); + } else { + x_auto = max(0, (int)bgrt0.image_offset_x + ((int)old_bmp->width - (int)bmp->width) / 2); + y_auto = max(0, (int)bgrt0.image_offset_y + ((int)old_bmp->height - (int)bmp->height) / 2); + } + + // Set the position (manual, automatic, original). + bgrt->image_offset_x = SelectCoordinate(new_x, x_auto, bgrt0.image_offset_x); + bgrt->image_offset_y = SelectCoordinate(new_y, y_auto, bgrt0.image_offset_y); + Debug(L"HackBGRT: BMP at (%d, %d).\n", (int) bgrt->image_offset_x, (int) bgrt->image_offset_y); + + bgrt->header.checksum = 0; + bgrt->header.checksum = CalculateAcpiChecksum(bgrt, sizeof(*bgrt)); +} + +/** + * Find the BGRT and optionally destroy it or create if missing. + * + * @param action The intended action. + * @return Pointer to the BGRT, or 0 if not found (or destroyed). + */ +static ACPI_BGRT* FindBGRT(enum HackBGRT_action action) { + ACPI_20_RSDP* good_rsdp = 0; + ACPI_BGRT* bgrt = 0; + + for (int i = 0; i < ST->NumberOfTableEntries; i++) { + EFI_GUID Acpi20TableGuid = ACPI_20_TABLE_GUID; + EFI_GUID* vendor_guid = &ST->ConfigurationTable[i].VendorGuid; + if (!CompareGuid(vendor_guid, &AcpiTableGuid) && !CompareGuid(vendor_guid, &Acpi20TableGuid)) { + continue; + } + EFI_CONFIGURATION_TABLE *ect = &ST->ConfigurationTable[i]; + if (CompareMem(ect->VendorTable, "RSD PTR ", 8) != 0) { + continue; + } + ACPI_20_RSDP* rsdp = (ACPI_20_RSDP *)ect->VendorTable; + Debug(L"RSDP: revision = %d, OEM ID = %s\n", rsdp->revision, TmpStr(rsdp->oem_id, 6)); + + if (rsdp->revision < 2) { + Debug(L"* XSDT: N/A (revision < 2)\n"); + continue; + } + ACPI_SDT_HEADER* xsdt = (ACPI_SDT_HEADER *) (UINTN) rsdp->xsdt_address; + if (!xsdt) { + Debug(L"* XSDT: N/A (null)\n"); + continue; + } + if (CompareMem(xsdt->signature, "XSDT", 4) != 0) { + Debug(L"* XSDT: N/A (invalid signature)\n"); + continue; + } + good_rsdp = rsdp; + UINT64* entry_arr = (UINT64*)&xsdt[1]; + UINT32 entry_arr_length = (xsdt->length - sizeof(*xsdt)) / sizeof(UINT64); + + Debug(L"* XSDT: OEM ID = %s, entry count = %d\n", TmpStr(xsdt->oem_id, 6), entry_arr_length); + + for (int j = 0; j < entry_arr_length; j++) { + ACPI_SDT_HEADER *entry = (ACPI_SDT_HEADER *)((UINTN)entry_arr[j]); + Debug(L" - ACPI table: %s, revision = %d, OEM ID = %s\n", TmpStr(entry->signature, 4), entry->revision, TmpStr(entry->oem_id, 6)); + if (CompareMem(entry->signature, "BGRT", 4) == 0) { + if (!bgrt && action != HackBGRT_REMOVE) { + bgrt = (void*) entry; + } else { + if (bgrt) { + Debug(L" -> Deleting; BGRT was already found!\n"); + } else { + Debug(L" -> Deleting.\n"); + } + for (int k = j+1; k < entry_arr_length; ++k) { + entry_arr[k-1] = entry_arr[k]; + } + --entry_arr_length; + entry_arr[entry_arr_length] = 0; + xsdt->length -= sizeof(entry_arr[0]); + --j; + } + } + } + } + if (action == HackBGRT_REMOVE) { + return 0; + } + if (!good_rsdp) { + Print(L"HackBGRT: RSDP or XSDT not found.\n"); + return 0; + } + if (!bgrt) { + if (action == HackBGRT_KEEP) { + Print(L"HackBGRT: BGRT not found.\n"); + return 0; + } + Debug(L"HackBGRT: BGRT not found, creating.\n"); + ACPI_20_RSDP* rsdp = good_rsdp; + ACPI_SDT_HEADER* xsdt0 = (ACPI_SDT_HEADER *) (UINTN) rsdp->xsdt_address; + ACPI_SDT_HEADER* xsdt = 0; + UINT32 xsdt_len = xsdt0->length + sizeof(UINT64); + BS->AllocatePool(EfiACPIReclaimMemory, xsdt_len, (void**)&xsdt); + BS->AllocatePool(EfiACPIReclaimMemory, sizeof(*bgrt), (void**)&bgrt); + if (!xsdt || !bgrt) { + Print(L"HackBGRT: Failed to allocate memory for XSDT and BGRT.\n"); + return 0; + } + rsdp->xsdt_address = (UINTN) xsdt; + CopyMem(xsdt, xsdt0, xsdt0->length); + *(UINT64*)((char*)xsdt + xsdt->length) = (UINTN) bgrt; + xsdt->length = xsdt_len; + InitBGRT(bgrt); + } + return bgrt; +} + +/** + * Load a bitmap or generate one, or return 0 if not applicable. + * + * @param action Tells what to do. + * @param root_dir The root directory for loading a BMP. + * @param path The BMP path within the root directory. + * @return The loaded BMP, or 0 if not needed or not available. + */ +static BMP* LoadBMP(enum HackBGRT_action action, EFI_FILE_HANDLE root_dir, const CHAR16* path) { + BMP* bmp = 0; + if (action == HackBGRT_KEEP || action == HackBGRT_REMOVE) { + return 0; + } + if (action == HackBGRT_BLACK) { + BS->AllocatePool(EfiBootServicesData, 58, (void**) &bmp); + if (!bmp) { + Print(L"HackBGRT: Failed to allocate a blank BMP!\n"); + BS->Stall(1000000); + return 0; + } + CopyMem( + bmp, + "\x42\x4d\x3a\x00\x00\x00\x00\x00\x00\x00\x36\x00\x00\x00\x28\x00" + "\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x18\x00\x00\x00" + "\x00\x00\x04\x00\x00\x00\x13\x0b\x00\x00\x13\x0b\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", + 58 + ); + return bmp; + } + if (!path) { + Print(L"HackBGRT: Missing BMP path. REPORT THIS BUG!"); + return 0; + } + Debug(L"HackBGRT: Loading %s.\n", path); + bmp = LoadFile(root_dir, path, 0); + if (!bmp) { + Print(L"HackBGRT: Failed to load BMP (%s)!\n", path); + BS->Stall(1000000); + return 0; + } + return bmp; +} + +/** + * The main program. + */ +EFI_STATUS EFIAPI EfiMain(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *ST_) { + InitializeLib(image_handle, ST_); + + EFI_LOADED_IMAGE* image; + if (EFI_ERROR(BS->HandleProtocol(image_handle, &LoadedImageProtocol, (void**) &image))) { + Debug(L"HackBGRT: LOADED_IMAGE_PROTOCOL failed.\n"); + goto fail; + } + + EFI_FILE_HANDLE root_dir = LibOpenRoot(image->DeviceHandle); + + CHAR16 **argv; + int argc = GetShellArgcArgv(image_handle, &argv); + + if (argc <= 1) { + const CHAR16* config_path = L"\\EFI\\HackBGRT\\config.txt"; + if (!ReadConfigFile(&config, root_dir, config_path)) { + Print(L"HackBGRT: No config, no command line!\n", config_path); + goto fail; + } + } + for (int i = 1; i < argc; ++i) { + ReadConfigLine(&config, root_dir, argv[i]); + } + Debug = config.debug ? Print : NullPrint; + + BMP* new_bmp = LoadBMP(config.action, root_dir, config.image_path); + ACPI_BGRT* bgrt = FindBGRT(config.action); + if (bgrt) { + FillBGRT(bgrt, new_bmp, config.image_x, config.image_y); + } + + if (!config.boot_path) { + Print(L"HackBGRT: Boot path not specified.\n"); + goto fail; + } + + Debug(L"HackBGRT: Loading and booting %s.\n", config.boot_path); + EFI_DEVICE_PATH* boot_dp = FileDevicePath(image->DeviceHandle, (CHAR16*) config.boot_path); + EFI_HANDLE next_image_handle; + if (EFI_ERROR(BS->LoadImage(0, image_handle, boot_dp, 0, 0, &next_image_handle))) { + Print(L"HackBGRT: LoadImage for new image (%s) failed.\n", config.boot_path); + goto fail; + } + if (EFI_ERROR(BS->StartImage(next_image_handle, 0, 0))) { + Print(L"HackBGRT: StartImage for %s failed.\n", config.boot_path); + goto fail; + } + Print(L"HackBGRT: Started %s. Why are we still here?!\n", config.boot_path); + goto fail; + + fail: { + Print(L"HackBGRT has failed. Use parameter debug=1 for details.\nPress any key to exit.\n"); + WaitKey(); + return 1; + } +} + +/** + * Forward to EfiMain. + * + * Some compilers and architectures differ in underscore handling. This helps. + */ +EFI_STATUS EFIAPI _EfiMain(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *ST_) { + return EfiMain(image_handle, ST_); +} diff --git a/src/types.h b/src/types.h new file mode 100644 index 0000000..fb02b2c --- /dev/null +++ b/src/types.h @@ -0,0 +1,54 @@ +#pragma once + +#pragma pack(push, 1) + +/** RSDP (Root System Description Pointer) */ +typedef struct { + CHAR8 signature[8]; + UINT8 checksum; + CHAR8 oem_id[6]; + UINT8 revision; + UINT32 rsdt_address; + UINT32 length; + UINT64 xsdt_address; + UINT8 extended_checksum; + UINT8 reserved[3]; +} ACPI_20_RSDP; + +/** SDT (System Description Table) entry header */ +typedef struct { + CHAR8 signature[4]; + UINT32 length; + UINT8 revision; + UINT8 checksum; + CHAR8 oem_id[6]; + CHAR8 oem_table_id[8]; + UINT32 oem_revision; + UINT32 asl_compiler_id; + UINT32 asl_compiler_revision; +} ACPI_SDT_HEADER; + +/** BGRT structure */ +typedef struct { + ACPI_SDT_HEADER header; + UINT16 version; + UINT8 status; + UINT8 image_type; + UINT64 image_address; + UINT32 image_offset_x; + UINT32 image_offset_y; +} ACPI_BGRT; + +/** Bitmap file header */ +typedef struct { + UINT8 magic_BM[2]; + UINT32 file_size; + UINT8 unused_0x06[4]; + UINT32 pixel_data_offset; + UINT32 dib_header_size; + UINT32 width; + UINT32 height; + UINT16 planes; + UINT16 bpp; +} BMP; +#pragma pack(pop) diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..471ada8 --- /dev/null +++ b/src/util.c @@ -0,0 +1,115 @@ +#include "util.h" + +#include + +const CHAR16* TmpStr(CHAR8 *src, int length) { + static CHAR16 arr[4][16]; + static int j; + CHAR16* dest = arr[j = (j+1) % 4]; + int i; + for (i = 0; i < length && i < 16-1 && src[i]; ++i) { + dest[i] = src[i]; + } + dest[i] = 0; + return dest; +} + +UINTN NullPrint(IN CHAR16 *fmt, ...) { + return 0; +} + +UINT8 CalculateAcpiChecksum(void* data, UINTN size) { + UINT8 sum = 0; + UINT8* arr = data; + for (UINTN i = 0; i < size; ++i) { + sum += arr[i]; + } + return 256 - sum; +} + +const CHAR16* TrimLeft(const CHAR16* s) { + // Skip white-space and BOM. + while (s[0] == L'\xfeff' || s[0] == ' ' || s[0] == '\t') { + ++s; + } + return s; +} + +const CHAR16* StrStr(const CHAR16* haystack, const CHAR16* needle) { + int len = StrLen(needle); + while (haystack && haystack[0]) { + if (StrnCmp(haystack, needle, len) == 0) { + return haystack; + } + ++haystack; + } + return 0; +} + +const CHAR16* StrStrAfter(const CHAR16* haystack, const CHAR16* needle) { + return (haystack = StrStr(haystack, needle)) ? haystack + StrLen(needle) : 0; +} + +UINT64 Random_a, Random_b; + +UINT64 Random(void) { + // Implemented after xoroshiro128plus.c + if (!Random_a && !Random_b) { + RandomSeedAuto(); + } + UINT64 a = Random_a, b = Random_b, r = a + b; + b ^= a; + Random_a = rotl(a, 55) ^ b ^ (b << 14); + Random_b = rotl(b, 36); + return r; +} + +void RandomSeed(UINT64 a, UINT64 b) { + Random_a = a; + Random_b = b; +} + +void RandomSeedAuto(void) { + EFI_TIME t; + RT->GetTime(&t, 0); + UINT64 a, b = ((((((UINT64) t.Second * 100 + t.Minute) * 100 + t.Hour) * 100 + t.Day) * 100 + t.Month) * 10000 + t.Year) * 300000 + t.Nanosecond; + BS->GetNextMonotonicCount(&a); + RandomSeed(a, b), Random(), Random(); +} + +void WaitKey(void) { + ST->ConIn->Reset(ST->ConIn, FALSE); + WaitForSingleEvent(ST->ConIn->WaitForKey, 0); +} + +void* LoadFileWithPadding(EFI_FILE_HANDLE dir, const CHAR16* path, UINTN* size_ptr, UINTN padding) { + EFI_STATUS e; + EFI_FILE_HANDLE handle; + + e = dir->Open(dir, &handle, (CHAR16*) path, EFI_FILE_MODE_READ, 0); + if (EFI_ERROR(e)) { + return 0; + } + + EFI_FILE_INFO *info = LibFileInfo(handle); + UINTN size = info->FileSize; + FreePool(info); + + void* data = 0; + e = BS->AllocatePool(EfiBootServicesData, size + padding, &data); + if (EFI_ERROR(e)) { + handle->Close(handle); + return 0; + } + e = handle->Read(handle, &size, data); + *(UINT32*)((char*)data + size) = 0; + handle->Close(handle); + if (EFI_ERROR(e)) { + FreePool(data); + return 0; + } + if (size_ptr) { + *size_ptr = size; + } + return data; +} diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..ee7d00a --- /dev/null +++ b/src/util.h @@ -0,0 +1,101 @@ +#pragma once + +#include + +/** + * Convert a short ASCII string to UCS2, store in a static array. + * + * @param src The ASCII string. Will be truncated to 15 characters + null. + * @param length The maximum length, if the string is not null-terminated. + * @return The UCS2 string, statically allocated, null-terminated. + */ +extern const CHAR16* TmpStr(CHAR8 *src, int length); + +/** + * Empty function that has the same signature as Print. + */ +extern UINTN NullPrint(IN CHAR16 *fmt, ...); + +/** + * Calculate the checksum for an ACPI table. + * + * @param data Pointer to the table. + * @param size Table length in bytes. + * @return Checksum. + */ +extern UINT8 CalculateAcpiChecksum(void* data, UINTN size); + +/** + * Return the greater of two numbers. + */ +static inline int max(int a, int b) { + return a > b ? a : b; +} + +/** + * Trim BOM, spaces and tabs from the beginning of a string. + * + * @param s The string. + * @return Pointer to the first acceptable character. + */ +extern const CHAR16* TrimLeft(const CHAR16* s); + +/** + * Find the position of another string within a string. + * + * @param haystack The full text. + * @param needle The string to look for. + * @return Pointer to the first occurence of needle in the haystack, or 0. + */ +extern const CHAR16* StrStr(const CHAR16* haystack, const CHAR16* needle); + + +/** + * Find the position after another string within a string. + * + * @param haystack The full text. + * @param needle The string to look for. + * @return Pointer after the first occurence of needle in the haystack, or 0. + */ +extern const CHAR16* StrStrAfter(const CHAR16* haystack, const CHAR16* needle); + +/** + * Rotate left a 64-bit value. + */ +static inline UINT64 rotl(const UINT64 x, int k) { + return (x << k) | (x >> (64 - k)); +} + +/** + * Generate a random 64-bit number. + */ +extern UINT64 Random(void); + + +/** + * Seed the random number generator. Pass 0 and 0 to seed from the clock. + */ +extern void RandomSeed(UINT64 a, UINT64 b); + +/** + * Seed the random number generator automatically. + */ +extern void RandomSeedAuto(void); + +/** + * Wait for a key press. + */ +extern void WaitKey(void); + +/** + * Load a file, allocate some extra bytes as well. + */ +extern void* LoadFileWithPadding(EFI_FILE_HANDLE dir, const CHAR16* path, UINTN* size_ptr, UINTN padding); + +/** + * Load a file. + */ +static inline void* LoadFile(EFI_FILE_HANDLE dir, const CHAR16* path, UINTN* size_ptr) { + return LoadFileWithPadding(dir, path, size_ptr, 0); +} + diff --git a/uninstall.bat b/uninstall.bat new file mode 100755 index 0000000..c2fed88 --- /dev/null +++ b/uninstall.bat @@ -0,0 +1,9 @@ +@ECHO OFF +CD %~dp0 + +IF NOT EXIST install.bat ( + ECHO The uninstaller needs install.bat! + EXIT /B 1 +) + +CALL install.bat uninstall