Initial release to GitHub

This commit is contained in:
Lauri Kenttä
2016-05-11 22:53:54 +03:00
parent e89bafd392
commit cc44e6eb9b
14 changed files with 981 additions and 0 deletions

12
Doxyfile Normal file
View File

@@ -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

27
Makefile Normal file
View File

@@ -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/, $^

31
README.efilib Normal file
View File

@@ -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.

48
README.md Normal file
View File

@@ -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

BIN
config.txt Executable file

Binary file not shown.

120
install.bat Executable file
View File

@@ -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

BIN
splash.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

102
src/config.c Normal file
View File

@@ -0,0 +1,102 @@
#include "config.h"
#include "util.h"
#include <efilib.h>
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);
}

51
src/config.h Normal file
View File

@@ -0,0 +1,51 @@
#pragma once
#include <efi.h>
/**
* 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);

311
src/main.c Normal file
View File

@@ -0,0 +1,311 @@
#include <efi.h>
#include <efilib.h>
#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_);
}

54
src/types.h Normal file
View File

@@ -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)

115
src/util.c Normal file
View File

@@ -0,0 +1,115 @@
#include "util.h"
#include <efilib.h>
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;
}

101
src/util.h Normal file
View File

@@ -0,0 +1,101 @@
#pragma once
#include <efi.h>
/**
* 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);
}

9
uninstall.bat Executable file
View File

@@ -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