38 Commits

Author SHA1 Message Date
Lauri Kenttä
b9e23c91a3 Update change log and tag 2.4.1 2024-04-11 17:48:55 +03:00
Lauri Kenttä
697c57355b Disable old version before copying files
Apparently some people still have the obsolete v1.5.1 installed
and manage to break things by copying v2.x config in place.
2024-04-11 17:46:13 +03:00
Lauri Kenttä
da16365508 Report if BCDEdit is not working
One possible cause for BCDEdit failure is Windows error 0x800703EE.
"The volume for a file has been externally altered so that the opened file is no longer valid."
Suggested solutions include disabling antivirus or backup software.
2024-04-11 17:46:13 +03:00
Lauri Kenttä
665a4732ca Detect device paths which contain extra data
Apparently some firmware may add extra data to the device path,
so exit the loop on the first end-of-path record.
Fixes GH issue #181.
2024-03-24 17:27:02 +02:00
Lauri Kenttä
39596aadfc Don't try to remove non-existent dir. 2024-01-20 12:43:00 +02:00
Lauri Kenttä
1a5b1df064 Update change log and tag 2.4.0 2023-12-31 18:35:35 +02:00
Lauri Kenttä
ea70f3ce79 Disable before enabling, and default to BCDEdit 2023-12-31 18:29:48 +02:00
Lauri Kenttä
a44b929012 Fix a shim error caused by bad load options data
Shim expects a filename or nothing in the load options.

To avoid an error message during boot, do several things:

When creating the NVRAM entry, use empty load options. The current
string ("HackBGRT\0") was just a decoration, and it's luckily ignored
by shim because the length is odd.

When creating the entry with BCDEdit, manually fix the load options.
The load options in BCDEdit entries start with "WINDOWS\0" followed
by UINT32 version, as seen in ReactOS struct BL_WINDOWS_LOAD_OPTIONS.
The version is 1, but BCDEdit seems to be happy with a higher number.
By setting this version to 'X' (0x58), the string becomes a valid
UCS-2 file name. Update the installer so that the HackBGRT loader is
installed with this weird file name.

The reason why the load options cannot be deleted completely is that
BCDEdit will recreate the entry on next boot if it doesn't find the
entry it just tried to create.

See: https://github.com/rhboot/shim/pull/621
See: https://github.com/reactos/reactos/blob/v0.4.7/boot/environ/include/bl.h#L911
2023-12-31 18:20:27 +02:00
Lauri Kenttä
9948e5a306 Fix BCDEdit dry run and add more logging 2023-12-31 17:50:14 +02:00
Lauri Kenttä
518d7c8a97 Show proper message if boot log is empty 2023-12-20 18:29:34 +02:00
Lauri Kenttä
c6108ffd62 Warn about old .Net version if methods are missing 2023-12-20 18:29:25 +02:00
Lauri Kenttä
6dc447a8ce Try to avoid some .Net Framework 4.8 features 2023-12-20 18:29:25 +02:00
Lauri Kenttä
5ec17a49e8 Detect ARM arch from MS loader 2023-12-20 18:29:25 +02:00
Lauri Kenttä
7b7309a255 Explain why the original logo is still visible 2023-12-20 18:29:25 +02:00
Lauri Kenttä
a82646a822 Wipe the vendor logo as soon as possible
Loading the image might take some minimal time. Optimize experience.
Hide cursor as well but restore it in ReadKey.
2023-12-20 18:27:19 +02:00
Lauri Kenttä
294da9c069 Get rid of \EFI\HackBGRT\ in config.txt 2023-12-16 15:13:05 +02:00
Lauri Kenttä
4096002eb2 Update issue templates 2023-12-16 15:03:05 +02:00
Lauri Kenttä
835cc1b2f2 Update change log and tag v2.3.1 2023-11-27 18:57:20 +02:00
Lauri Kenttä
74a143e723 Make BitLocker detection more reliable 2023-11-27 18:57:08 +02:00
Lauri Kenttä
b75dbe13c7 Update change log and tag v2.3.0 2023-11-27 15:33:31 +02:00
Lauri Kenttä
ba72b90082 Add logging to verify entry creation 2023-11-27 15:19:46 +02:00
Lauri Kenttä
6b724d5590 Don't create entries higher than Boot00FF
Apparently Boot20## are sometimes reserved for firmware even if
they seem to be free. Boot00## is the normal place to be.
2023-11-27 15:19:06 +02:00
Lauri Kenttä
9ebe4e2476 Add run-qemu-<arch> to Makefile
The exact command may vary by OS, this is for Arch Linux.
2023-11-25 20:04:54 +02:00
Lauri Kenttä
5111897fae Update Makefile (instructions, release) 2023-11-25 20:04:54 +02:00
Lauri Kenttä
aac8a38cbb Don't print HackBGRT on every output line 2023-11-25 20:02:53 +02:00
Lauri Kenttä
77dd2bd699 If GOP is missing, use config.txt resolution or 1024x768 2023-11-25 20:02:01 +02:00
Lauri Kenttä
8dfa456a7b Fix ARM (32-bit) build
Use -O, because -O2 causes Undefined OpCode in QEMU / UEFI Shell.

Remove -mno-red-zone which triggers -Wunused-command-line-argument.

Set architecture in PE file manually:
EFI uses IMAGE_FILE_MACHINE_ARMTHUMB_MIXED = 0x01C2,
while the default is IMAGE_FILE_MACHINE_ARMNT = 0x01C4.
2023-11-25 19:16:43 +02:00
Lauri Kenttä
7ccdcc4a77 Add ARM (32-bit) target (without shim, though)
Remove some integer divisions because they emit function calls.
2023-11-20 19:45:28 +02:00
Lauri Kenttä
50e84e8c90 Add AArch64 target 2023-11-20 19:45:28 +02:00
Lauri Kenttä
24c7e2b316 Switch to Clang
Clang can cross-compile to PE and is easier to setup than MinGW.
2023-11-20 19:45:28 +02:00
Lauri Kenttä
4379f9cbeb Add gnu-efi submodule for headers, don't link, just re-implement some functions 2023-11-20 19:42:23 +02:00
Lauri Kenttä
2a0f2a7757 Get shell arguments without gnu-efi 2023-11-20 19:31:25 +02:00
Lauri Kenttä
ebbacb72bb Open ESP root dir without gnu-efi 2023-11-20 19:31:25 +02:00
Lauri Kenttä
a908036ee8 Implement WaitKey without gnu-efi 2023-11-20 19:31:25 +02:00
Lauri Kenttä
57ce3ae33e Avoid using gnu-efi static global GUIDs 2023-11-20 19:31:25 +02:00
Lauri Kenttä
7dd048346d Use EFI BS FreePool, SetMem, CopyMem 2023-11-20 19:31:25 +02:00
Lauri Kenttä
0dfc49c800 Re-implement string formatting (%s, %d, %x) 2023-11-20 19:31:25 +02:00
Lauri Kenttä
4e50b33636 Check BMP pixel data size when loading 2023-11-20 19:31:25 +02:00
19 changed files with 679 additions and 194 deletions

21
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,21 @@
---
name: Bug report
about: Create a report if you encounter a bug
title: ''
labels: ''
assignees: ''
---
**What happens?**
Describe the bug.
**How this happens?**
Explain exactly the steps you did:
1. Download HackBGRT-(version).zip and extract all files.
2. Start setup.
3. Select ...
4. ...
**Log file**
Run the setup again and select menu option `L` (or add parameter `show-boot-log` on command line). Attach the `setup.log` to this report.

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "gnu-efi"]
path = gnu-efi
url = https://github.com/rhboot/gnu-efi.git

View File

@@ -2,6 +2,36 @@
All notable changes to this project will be documented in this file.
## 2.4.1 - 2024-04-11
### Fixed
- Report better if BCDEdit is unable to operate.
- Improve support for non-BCDEdit boot entries.
- Remove old version before copying any new files.
## 2.4.0 - 2023-12-31
### Fixed
- Fix BCDEdit boot entries to avoid *shim* error messages.
- Combine BCDEdit and own code to create boot entries more reliably.
### Changed
- Clear the screen to wipe the vendor logo as soon as possible.
- Image paths in `config.txt` may be relative (just file names).
## 2.3.1 - 2023-11-27
### Fixed
- BitLocker detection is more reliable.
## 2.3.0 - 2023-11-27
### Added
- AArch64 and ARM builds, and *shim* for AArch64.
### Fixed
- Boot entry is more reliable, avoids conflicts with firmware entries.
## 2.2.0 - 2023-11-17
### Added

115
Makefile
View File

@@ -1,40 +1,54 @@
CC = $(CC_PREFIX)-gcc
CFLAGS = -std=c11 -O2 -ffreestanding -mno-red-zone -fno-stack-protector -Wshadow -Wall -Wunused -Werror-implicit-function-declaration -Werror
CC = clang
CFLAGS = -target $(CLANG_TARGET) -ffreestanding -fshort-wchar
CFLAGS += -std=c17 -Wshadow -Wall -Wunused -Werror-implicit-function-declaration
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
CFLAGS += $(ARCH_CFLAGS)
LDFLAGS = -target $(CLANG_TARGET) -nostdlib -Wl,-entry:efi_main -Wl,-subsystem:efi_application -fuse-ld=lld
ARCH_CFLAGS = -O2 -mno-red-zone
GNUEFI_INC = /usr/$(CC_PREFIX)/include/efi
GNUEFI_LIB = /usr/$(CC_PREFIX)/lib
GNUEFI_INC = gnu-efi/inc
FILES_C = src/main.c src/util.c src/types.c src/config.c src/sbat.c
FILES_C = src/main.c src/util.c src/types.c src/config.c src/sbat.c src/efi.c
FILES_H = $(wildcard src/*.h)
FILES_CS = src/Setup.cs src/Esp.cs src/Efi.cs
GIT_DESCRIBE := $(firstword $(GIT_DESCRIBE) $(shell git describe --tags) unknown)
CFLAGS += '-DGIT_DESCRIBE_W=L"$(GIT_DESCRIBE)"' '-DGIT_DESCRIBE="$(GIT_DESCRIBE)"'
ZIPDIR = HackBGRT-$(GIT_DESCRIBE:v%=%)
ZIP = $(ZIPDIR).zip
RELEASE_NAME = HackBGRT-$(GIT_DESCRIBE:v%=%)
.PHONY: all efi efi-signed setup zip clean
EFI_ARCH_LIST = x64 ia32 aa64 arm
EFI_SIGNED_FILES = $(patsubst %,efi-signed/boot%.efi,$(EFI_ARCH_LIST))
.PHONY: all efi efi-signed setup release clean
all: efi setup
efi: efi/bootx64.efi efi/bootia32.efi
efi-signed: efi-signed/bootx64.efi efi-signed/bootia32.efi
@echo "Run 'make efi-signed' to sign the EFI executables."
@echo "Run 'make release' to build a release-ready ZIP archive."
@echo "Run 'make run-qemu-<arch>' to test the EFI executables with QEMU."
efi: $(patsubst %,efi/boot%.efi,$(EFI_ARCH_LIST))
@echo "EFI executables are in the efi/ directory."
efi-signed: $(patsubst %,efi-signed/boot%.efi,$(EFI_ARCH_LIST))
@echo "Signed EFI executables are in the efi-signed/ directory."
setup: setup.exe
zip: $(ZIP)
$(ZIP): efi-signed certificate.cer config.txt splash.bmp setup.exe README.md CHANGELOG.md README.efilib LICENSE shim-signed shim.md
test ! -d "$(ZIPDIR)"
mkdir "$(ZIPDIR)"
cp -a $^ "$(ZIPDIR)" || (rm -rf "$(ZIPDIR)"; exit 1)
7z a -mx=9 "$(ZIP)" "$(ZIPDIR)" || (rm -rf "$(ZIPDIR)"; exit 1)
rm -rf "$(ZIPDIR)"
release: release/$(RELEASE_NAME).zip
@echo "Current version is packaged: $<"
release/$(RELEASE_NAME): $(EFI_SIGNED_FILES) certificate.cer config.txt splash.bmp setup.exe README.md CHANGELOG.md README.efilib LICENSE shim-signed/* shim.md
rm -rf $@
tar c --transform=s,^,$@/, $^ | tar x
release/$(RELEASE_NAME).zip: release/$(RELEASE_NAME)
rm -rf $@
(cd release; 7z a -mx=9 "$(RELEASE_NAME).zip" "$(RELEASE_NAME)" -bd -bb1)
src/GIT_DESCRIBE.cs: $(FILES_CS) $(FILES_C) $(FILES_H)
echo 'public class GIT_DESCRIBE { public const string data = "$(GIT_DESCRIBE)"; }' > $@
setup.exe: $(FILES_CS) src/GIT_DESCRIBE.cs
csc /define:GIT_DESCRIBE /out:$@ $^
csc /nologo /define:GIT_DESCRIBE /out:$@ $^
certificate.cer pki:
@echo
@@ -48,24 +62,61 @@ certificate.cer pki:
@echo
@false
efi-signed/%.efi: efi/%.efi
mkdir -p efi-signed
efi-signed/%.efi: efi/%.efi pki
@mkdir -p efi-signed
pesign --force -n pki -i $< -o $@ -c HackBGRT-signer -s
efi-signed/bootx64.efi: pki
efi-signed/bootia32.efi: pki
efi/bootx64.efi: CC_PREFIX = x86_64-w64-mingw32
efi/bootx64.efi: CLANG_TARGET = x86_64-pc-windows-msvc
efi/bootx64.efi: GNUEFI_ARCH = x86_64
efi/bootx64.efi: $(FILES_C)
@mkdir -p efi
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) -s
efi/bootia32.efi: CC_PREFIX = i686-w64-mingw32
efi/bootia32.efi: CLANG_TARGET = i386-pc-windows-msvc
efi/bootia32.efi: GNUEFI_ARCH = ia32
efi/bootia32.efi: $(FILES_C)
efi/bootaa64.efi: CLANG_TARGET = aarch64-pc-windows-msvc
efi/bootaa64.efi: GNUEFI_ARCH = aa64
efi/boot%.efi: $(FILES_C)
@mkdir -p efi
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) -s
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@
efi/bootarm.efi: CLANG_TARGET = armv6-pc-windows-msvc
efi/bootarm.efi: GNUEFI_ARCH = arm
efi/bootarm.efi: ARCH_CFLAGS = -O # skip -O2 and -mno-red-zone
efi/bootarm.efi: $(FILES_C)
@mkdir -p efi
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@
@echo "Fix $@ architecture code (IMAGE_FILE_MACHINE_ARMTHUMB_MIXED = 0x01C2)"
echo -en "\xc2\x01" | dd of=$@ bs=1 seek=124 count=2 conv=notrunc status=none
clean:
rm -rf setup.exe efi efi-signed
rm -f src/GIT_DESCRIBE.cs
rm -rf release
rm -rf test
.PHONY: test $(patsubst %,run-qemu-%,$(EFI_ARCH_LIST))
test: run-qemu-x64
@echo "Run 'make run-qemu-<arch>' to test other architectures."
test/esp-%: efi/boot%.efi splash.bmp
rm -rf $@
mkdir -p $@/EFI/HackBGRT
cp efi/boot$*.efi splash.bmp $@/EFI/HackBGRT
echo -en "FS0:\n cd EFI\n cd HackBGRT\n boot$*.efi resolution=-1x-1 debug=1 image=path=splash.bmp" > $@/startup.nsh
QEMU_ARGS = -bios $(word 2, $^) -net none -drive media=disk,file=fat:rw:./$<,format=raw
run-qemu-x64: test/esp-x64 /usr/share/ovmf/x64/OVMF.fd
qemu-system-x86_64 $(QEMU_ARGS)
run-qemu-ia32: test/esp-ia32 /usr/share/ovmf/ia32/OVMF.fd
qemu-system-i386 $(QEMU_ARGS)
run-qemu-aa64: test/esp-aa64 /usr/share/ovmf/aarch64/QEMU_EFI.fd
@echo "Press Ctrl+Alt+2 to switch to QEMU console."
qemu-system-aarch64 -machine virt -cpu max $(QEMU_ARGS)
run-qemu-arm: test/esp-arm /usr/share/ovmf/arm/QEMU_EFI.fd
@echo "Press Ctrl+Alt+2 to switch to QEMU console."
qemu-system-arm -machine virt -cpu max $(QEMU_ARGS)

View File

@@ -6,6 +6,8 @@ HackBGRT is intended as a boot logo changer for UEFI-based Windows systems.
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 permanently, but a custom UEFI application may be used to overwrite it during the boot. HackBGRT does exactly that.
**Note:** The original logo is often visible for a moment before HackBGRT is started. This is expected, please do not report this "bug". This can't be changed without modifying computer firmware, which this project will not do.
## 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.
@@ -70,20 +72,18 @@ The configuration options are described in `config.txt`, which the installer cop
## Images
The image path can be changed in the configuration file. The default path is `[EFI System Partition]\EFI\HackBGRT\splash.bmp`.
If you only need one image, just edit `splash.bmp` to your needs.
The installer copies and converts files whose `path` starts with `\EFI\HackBGRT\`. For example, to use a file named `my.jpg`, copy it in the installer folder (same folder as `setup.exe`) and set the image path in `config.txt` to `path=\EFI\HackBGFT\my.jpg`.
Advanced users may edit the `config.txt` to define multiple images, in which case one is picked at random. The installer copies and converts the images. For example, to use a file named `my.jpg`, copy it in the installer folder (same folder as `setup.exe`) and set the image path in `config.txt` to `path=my.jpg` before running the installer.
If you copy an image file to ESP manually, note that 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.
Advanced users may edit the `config.txt` to define multiple images, in which case one is picked at random.
## Recovery
If something breaks and you can't boot to Windows, you need to use the Windows installation disk (or recovery disk) to fix boot issues.
## Building
* Compiler: GCC targeting w64-mingw32
* Compiler: Clang
* Compiler flags: see Makefile
* Libraries: gnu-efi

View File

@@ -16,16 +16,14 @@ boot=MS
# - "keep" to keep the firmware logo. Also keeps coordinates by default.
# - "remove" to remove the BGRT. Makes x and y meaningless.
# - "black" to use only a black image. Makes x and y meaningless.
# - "path=..." to read a BMP file.
# * NOTE: For path=\EFI\HackBGRT\*, the installer will copy and convert the file if necessary.
# * NOTE: For other paths, make sure that the file is a 24-bit BMP file with a 54-byte header.
# * NOTE: The file must be on the EFI System Partition. Do not add a drive letter!
# - "path=file.bmp" to read an image file.
# * NOTE: The installer can copy and convert BMP, PNG, JPEG, GIF.
# Examples:
# - image=remove
# - image=black
# - image= x=0 y=-200 path=\EFI\HackBGRT\topimage.bmp
# - image= n=1 o=90 path=\EFI\HackBGRT\sideways.bmp
# - image= n=50 y=999999 o=keep path=\EFI\HackBGRT\probable.bmp
# - image= x=0 y=-200 path=topimage.bmp
# - image= n=1 o=90 path=sideways.bmp
# - image= n=50 y=999999 o=keep path=probable.bmp
# The above examples together would produce
# - 1/54 chance for the default OS logo
# - 1/54 chance for black screen
@@ -33,7 +31,7 @@ boot=MS
# - 1/54 chance for splash.bmp, centered, orientation set to 90 degrees
# - 50/54 chance for probable.bmp, at the bottom edge, explicitly default orientation
# Default: just one image.
image= y=-200 path=\EFI\HackBGRT\splash.bmp
image= y=-200 path=splash.bmp
# Preferred resolution. Use 0x0 for maximum and -1x-1 for original.
resolution=0x0

1
gnu-efi Submodule

Submodule gnu-efi added at 965f557ab7

BIN
shim-signed/mmaa64.efi Normal file

Binary file not shown.

BIN
shim-signed/shimaa64.efi Normal file

Binary file not shown.

View File

@@ -93,12 +93,19 @@ public class Efi {
var pos = 6 + 2 * (Label.Length + 1);
var pathNodesEnd = pos + pathNodesLength;
DevicePathNodes = new List<DevicePathNode>();
Arguments = new byte[0];
while (pos + 4 <= pathNodesEnd) {
var len = BitConverter.ToUInt16(data, pos + 2);
if (len < 4 || pos + len > pathNodesEnd) {
return; // throw new Exception("Bad entry.");
}
DevicePathNodes.Add(new DevicePathNode(data.Skip(pos).Take(len).ToArray()));
var node = new DevicePathNode(data.Skip(pos).Take(len).ToArray());
DevicePathNodes.Add(node);
if (node.Type == 0x7f && node.SubType == 0xff) {
// End of entire device path.
// Apparently some firmwares produce paths with unused nodes at the end.
break;
}
pos += len;
}
Arguments = data.Skip(pathNodesEnd).ToArray();
@@ -378,9 +385,10 @@ public class Efi {
*
* @param label Label of the boot entry.
* @param fileName File name of the boot entry.
* @param alwaysCopyFromMS If true, do not preserve any existing data.
* @param dryRun Don't actually create the entry.
*/
public static void MakeAndEnableBootEntry(string label, string fileName, bool dryRun = false) {
public static void MakeAndEnableBootEntry(string label, string fileName, bool alwaysCopyFromMS, bool dryRun = false) {
Variable msEntry = null, ownEntry = null;
UInt16 msNum = 0, ownNum = 0;
@@ -403,10 +411,11 @@ public class Efi {
Setup.Log($"Read EFI variable: {bootOrder}");
Setup.Log($"Read EFI variable: {bootCurrent}");
var bootOrderInts = new List<UInt16>(BytesToUInt16s(bootOrder.Data));
foreach (var num in BytesToUInt16s(bootCurrent.Data).Concat(bootOrderInts).Concat(Enumerable.Range(0, 0xffff).Select(i => (UInt16) i))) {
foreach (var num in BytesToUInt16s(bootCurrent.Data).Concat(bootOrderInts).Concat(Enumerable.Range(0, 0xff).Select(i => (UInt16) i))) {
var entry = GetVariable(String.Format("Boot{0:X04}", num));
if (entry.Data == null) {
if (ownEntry == null) {
// Use only Boot0000 .. Boot00FF because some firmwares expect that.
if (ownEntry == null && num < 0x100) {
ownNum = num;
ownEntry = entry;
}
@@ -434,12 +443,28 @@ public class Efi {
throw new Exception("MakeBootEntry: Windows Boot Manager not found.");
} else {
Setup.Log($"Read EFI variable: {msEntry}");
Setup.Log($"Read EFI variable: {ownEntry}");
// Make a new boot entry using the MS entry as a starting point.
var entryData = new BootEntryData(msEntry.Data);
entryData.Arguments = Encoding.UTF8.GetBytes(label + "\0");
if (!alwaysCopyFromMS && ownEntry.Data != null) {
entryData = new BootEntryData(ownEntry.Data);
// Shim expects the arguments to be a filename or nothing.
// But BCDEdit expects some Microsoft-specific data.
// Modify the entry so that BCDEdit still recognises it
// but the data becomes a valid UCS-2 / UTF-16LE file name.
var str = new string(entryData.Arguments.Take(12).Select(c => (char) c).ToArray());
if (str == "WINDOWS\0\x01\0\0\0") {
entryData.Arguments[8] = (byte) 'X';
} else if (str != "WINDOWS\0\x58\0\0\0") {
// Not recognized. Clear the arguments.
entryData.Arguments = new byte[0];
}
} else {
entryData.Arguments = new byte[0];
entryData.Label = label;
entryData.FileName = fileName;
}
entryData.Attributes = 1; // LOAD_OPTION_ACTIVE
entryData.Label = label;
entryData.FileName = fileName;
ownEntry.Attributes = 7; // EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS
ownEntry.Data = entryData.ToBytes();
SetVariable(ownEntry, dryRun);
@@ -471,7 +496,12 @@ public class Efi {
var bootOrderInts = new List<UInt16>(BytesToUInt16s(bootOrder.Data));
// Windows can't enumerate EFI variables, and trying them all is too slow.
// BootOrder + BootCurrent + the first 0xff entries should be enough.
foreach (var num in BytesToUInt16s(bootCurrent.Data).Concat(bootOrderInts).Concat(Enumerable.Range(0, 0xff).Select(i => (UInt16) i))) {
var seen = new HashSet<UInt16>();
foreach (var num in bootOrderInts.Concat(BytesToUInt16s(bootCurrent.Data)).Concat(Enumerable.Range(0, 0xff).Select(i => (UInt16) i))) {
if (seen.Contains(num)) {
continue;
}
seen.Add(num);
var entry = GetVariable(String.Format("Boot{0:X04}", num));
if (entry.Data != null) {
Setup.Log($"LogBootOrder: {entry}");
@@ -488,6 +518,9 @@ public class Efi {
public static string GetHackBGRTLog() {
try {
var log = GetVariable("HackBGRTLog", EFI_HACKBGRT_GUID);
if (log.Data == null) {
return "(null)";
}
return new string(BytesToUInt16s(log.Data).Select(i => (char)i).ToArray());
} catch (Exception e) {
return $"Log not found: {e.ToString()}";

View File

@@ -144,10 +144,9 @@ public class Setup {
*
* @param app Path to the application.
* @param args The argument string.
* @param nullOnFail If set, return null if the program exits with a code other than 0, even if there was some output.
* @return The output, or null if the execution failed.
* @return The output and exit code.
*/
public static string Execute(string app, string args, bool nullOnFail) {
public static (string Output, int ExitCode) Execute(string app, string args) {
Log($"Execute: {app} {args}");
try {
var info = new ProcessStartInfo(app, args);
@@ -157,13 +156,26 @@ public class Setup {
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
Log($"Exit code: {p.ExitCode}, output:\n{output}\n\n");
return (nullOnFail && p.ExitCode != 0) ? null : output;
return (output, p.ExitCode);
} catch (Exception e) {
Log($"Execute failed: {e.ToString()}");
return null;
return (null, -1);
}
}
/**
* Execute another process, return the output.
*
* @param app Path to the application.
* @param args The argument string.
* @param nullOnFail If set, return null if the program exits with a code other than 0, even if there was some output.
* @return The output, or null if the execution failed.
*/
public static string Execute(string app, string args, bool nullOnFail) {
var result = Execute(app, args);
return (nullOnFail && result.ExitCode != 0) ? null : result.Output;
}
/**
* Check for required privileges (access to ESP and EFI vars).
*
@@ -239,6 +251,8 @@ public class Setup {
0x0200 => "ia64",
0x8664 => "x64",
0xaa64 => "aa64",
0x01c2 => "arm",
0x01c4 => "arm",
_ => $"unknown-{peArch:x4}"
};
} catch {
@@ -352,10 +366,16 @@ public class Setup {
var lines = File.ReadAllLines("config.txt");
Log($"config.txt:\n{String.Join("\n", lines)}");
foreach (var line in lines.Where(s => s.StartsWith("image="))) {
var delim = "path=\\EFI\\HackBGRT\\";
var delim = "path=";
var i = line.IndexOf(delim);
if (i > 0) {
InstallImageFile(line.Substring(i + delim.Length));
var dir = "\\EFI\\HackBGRT\\";
if (line.Substring(i + delim.Length).StartsWith(dir)) {
InstallImageFile(line.Substring(i + delim.Length + dir.Length));
}
if (!line.Substring(i + delim.Length).StartsWith("\\")) {
InstallImageFile(line.Substring(i + delim.Length));
}
}
}
var loaderDest = "loader.efi";
@@ -365,6 +385,7 @@ public class Setup {
loaderDest = $"grub{EfiArch}.efi";
}
InstallFile(loaderSource, loaderDest);
InstallFile(loaderSource, "\u4957\u444e\u574f\u0053\u0058"); // bytes "WINDOWS\0X\0" as UTF-16
if (LoaderIsSigned) {
InstallFile("certificate.cer");
}
@@ -390,9 +411,31 @@ public class Setup {
* Enable HackBGRT with bcdedit.
*/
protected void EnableBCDEdit() {
if (DryRun) {
WriteLine("Dry run, skip enabling with BCDEdit.");
return;
}
var bcdeditEnum = Execute("bcdedit", "/enum firmware");
if (bcdeditEnum.ExitCode != 0) {
WriteLine("BCDEdit is not working. Fix it or try another method.");
if (bcdeditEnum.Output != null) {
var lastLine = bcdeditEnum.Output.Split("\n".ToCharArray()).Last();
WriteLine($"BCDEdit output: {lastLine}");
WriteLine("Disable antivirus, check your hard disk, or search 'how to fix 0x800703EE'.");
}
throw new SetupException("Failed to enable HackBGRT with BCDEdit!");
}
try {
var re = new Regex("[{][0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}[}]");
var guid = re.Match(Execute("bcdedit", "/copy {bootmgr} /d HackBGRT", true)).Value;
var bcdCopy = Execute("bcdedit", "/copy {bootmgr} /d HackBGRT").Output;
if (bcdCopy == null) {
throw new SetupException("Failed to create a new BCDEdit entry for HackBGRT!");
}
var match = re.Match(bcdCopy);
if (!match.Success) {
throw new SetupException("Failed to get a GUID for the new BCDEdit entry for HackBGRT!");
}
var guid = match.Value;
Execute("bcdedit", $"/set {guid} device partition={Esp.Location}", true);
Execute("bcdedit", $"/set {guid} path \\EFI\\HackBGRT\\loader.efi", true);
foreach (var arg in new string[] { "locale", "inherit", "default", "resumeobject", "displayorder", "toolsdisplayorder", "timeout" }) {
@@ -400,6 +443,12 @@ public class Setup {
}
var fwbootmgr = "{fwbootmgr}";
Execute("bcdedit", $"/set {fwbootmgr} displayorder {guid} /addfirst", true);
WriteLine("Enabled NVRAM entry for HackBGRT with BCDEdit.");
// Verify that the entry was created.
Execute("bcdedit", "/enum firmware", true);
Efi.MakeAndEnableBootEntry("HackBGRT", "\\EFI\\HackBGRT\\loader.efi", false, DryRun);
Execute("bcdedit", $"/enum {guid}", true);
Efi.LogBootEntries();
} catch (Exception e) {
Log($"EnableBCDEdit failed: {e.ToString()}");
throw new SetupException("Failed to enable HackBGRT with BCDEdit!");
@@ -424,14 +473,18 @@ public class Setup {
} else if (entry.IndexOf("HackBGRT") >= 0) {
found = true;
Log($"Disabling HackBGRT entry {guid}.");
if (Execute("bcdedit", $"/delete {guid}", true) == null) {
if (!DryRun && Execute("bcdedit", $"/delete {guid}", true) == null) {
Log($"DisableBCDEdit failed to delete {guid}.");
} else {
disabled = true;
}
disabled = true;
}
}
if (found && !disabled) {
throw new SetupException("Failed to disable HackBGRT with BCDEdit!");
if (found) {
if (!disabled) {
throw new SetupException("Failed to disable HackBGRT with BCDEdit!");
}
WriteLine("Disabled NVRAM entry for HackBGRT with BCDEdit.");
}
}
@@ -439,8 +492,11 @@ public class Setup {
* Enable HackBGRT boot entry.
*/
protected void EnableEntry() {
Efi.MakeAndEnableBootEntry("HackBGRT", "\\EFI\\HackBGRT\\loader.efi", DryRun);
Efi.MakeAndEnableBootEntry("HackBGRT", "\\EFI\\HackBGRT\\loader.efi", true, DryRun);
WriteLine("Enabled NVRAM entry for HackBGRT.");
// Verify that the entry was created.
Efi.LogBootEntries();
Execute("bcdedit", "/enum firmware", true);
}
/**
@@ -506,7 +562,7 @@ public class Setup {
*/
protected void VerifyLoaderConfig() {
var lines = File.ReadAllLines("config.txt");
var loader = lines.Where(s => s.StartsWith("boot=")).Select(s => s.Substring(5)).Prepend("").Last();
var loader = lines.Where(s => s.StartsWith("boot=")).Select(s => s.Substring(5)).LastOrDefault();
if (loader == null) {
throw new SetupException("config.txt does not contain a boot=... line!");
}
@@ -570,7 +626,9 @@ public class Setup {
protected void Uninstall() {
Disable();
try {
Directory.Delete(InstallPath, true);
if (Directory.Exists(InstallPath)) {
Directory.Delete(InstallPath, true);
}
WriteLine($"HackBGRT has been removed from {InstallPath}.");
} catch (Exception e) {
Log($"Uninstall failed: {e.ToString()}");
@@ -632,8 +690,8 @@ public class Setup {
WriteLine("BitLocker status could not be determined.");
return;
}
var reOn = new Regex(@"Conversion Status:\s*(Encr|Fully Encr)|Protection Status:\s*Protection On");
var reOff = new Regex(@"Conversion Status:\s*(Fully Decrypted)|Protection Status:\s*Protection Off");
var reOn = new Regex(@"Conversion Status:\s*.*Encr|AES");
var reOff = new Regex(@"Conversion Status:\s*(Fully Decrypted)|\s0[.,]0\s*%");
var isOn = reOn.Match(output).Success;
var isOff = reOff.Match(output).Success;
if (!isOn && isOff) {
@@ -703,8 +761,13 @@ public class Setup {
* @param arch The architecture.
*/
protected void SetArch(string arch) {
var detectedArch = DetectArchFromOS();
if (arch == "") {
var detectedArch = Environment.Is64BitOperatingSystem ? "x64" : "ia32";
try {
detectedArch = DetectArchFromOS();
} catch {
WriteLine($"Failed to detect OS architecture, assuming {detectedArch}.");
}
if (arch == "" || arch == null) {
EfiArch = detectedArch;
WriteLine($"Your OS uses arch={EfiArch}. This will be checked again during installation.");
} else {
@@ -776,7 +839,7 @@ public class Setup {
WriteLine(" I = install");
WriteLine(" - creates a new EFI boot entry for HackBGRT");
WriteLine(" J = install (alternative)");
WriteLine(" - creates a new EFI boot entry with an alternative method (BCDEdit)");
WriteLine(" - creates a new EFI boot entry with an alternative method");
WriteLine(" - try this if the first option doesn't work");
WriteLine(" O = install (legacy)");
WriteLine(" - overwrites the MS boot loader; gets removed by Windows updates");
@@ -796,17 +859,17 @@ public class Setup {
var k = Console.ReadKey().Key;
Log($"User input: {k}");
WriteLine();
if (k == ConsoleKey.I || k == ConsoleKey.O || k == ConsoleKey.F) {
if (k == ConsoleKey.I || k == ConsoleKey.J || k == ConsoleKey.O || k == ConsoleKey.F) {
Configure();
}
if (k == ConsoleKey.I) {
RunPrivilegedActions(new string[] { "install", "enable-entry" });
RunPrivilegedActions(new string[] { "disable", "install", "enable-bcdedit" });
} else if (k == ConsoleKey.J) {
RunPrivilegedActions(new string[] { "install", "enable-bcdedit" });
RunPrivilegedActions(new string[] { "disable", "install", "enable-entry" });
} else if (k == ConsoleKey.O) {
RunPrivilegedActions(new string[] { "install", "enable-overwrite" });
RunPrivilegedActions(new string[] { "disable", "install", "enable-overwrite" });
} else if (k == ConsoleKey.F) {
RunPrivilegedActions(new string[] { "install" });
RunPrivilegedActions(new string[] { "disable-overwrite", "install" });
} else if (k == ConsoleKey.D) {
RunPrivilegedActions(new string[] { "disable" });
} else if (k == ConsoleKey.R) {
@@ -950,7 +1013,7 @@ public class Setup {
Batch = args.Contains("batch");
ForwardArguments = String.Join(" ", args.Where(s => s == "dry-run" || s == "batch" || s.StartsWith("arch=")));
try {
SetArch(args.Prepend("arch=").Last(s => s.StartsWith("arch=")).Substring(5));
SetArch(args.Where(s => s.StartsWith("arch=")).Select(s => s.Substring(5)).LastOrDefault());
if (args.Contains("is-elevated") && !HasPrivileges() && !DryRun) {
WriteLine("This installer needs to be run as administrator!");
return 1;
@@ -978,7 +1041,13 @@ public class Setup {
WriteLine();
WriteLine($"Unexpected error: {e.Message}");
Log(e.ToString());
WriteLine("If this is the most current release, please report this bug.");
if (e is MissingMemberException || e is TypeLoadException) {
WriteLine("This installer requires a recent version of .Net Framework.");
WriteLine("Use Windows Update or download manually:");
WriteLine("https://dotnet.microsoft.com/en-us/download/dotnet-framework");
} else {
WriteLine("If this is the most current release, please report this bug.");
}
return 1;
} finally {
if (DryRun) {

View File

@@ -1,14 +1,12 @@
#include "config.h"
#include "util.h"
#include <efilib.h>
BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, const CHAR16* path) {
BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE base_dir, const CHAR16* path) {
void* data = 0;
UINTN data_bytes = 0;
data = LoadFileWithPadding(root_dir, path, &data_bytes, 4);
data = LoadFileWithPadding(base_dir, path, &data_bytes, 4);
if (!data) {
Log(1, L"HackBGRT: Failed to load configuration (%s)!\n", path);
Log(1, L"Failed to load configuration (%s)!\n", path);
return FALSE;
}
CHAR16* str;
@@ -21,7 +19,7 @@ BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir,
// UTF-8 -> UCS-2
EFI_STATUS e = BS->AllocatePool(EfiBootServicesData, data_bytes * 2 + 2, (void**)&str);
if (EFI_ERROR(e)) {
FreePool(data);
BS->FreePool(data);
return FALSE;
}
UINT8* str0 = data;
@@ -51,7 +49,7 @@ BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir,
}
}
str[str_len] = 0;
FreePool(data);
BS->FreePool(data);
}
for (int i = 0; i < str_len;) {
@@ -63,7 +61,7 @@ BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir,
str[j] = 0;
++j;
}
ReadConfigLine(config, root_dir, &str[i]);
ReadConfigLine(config, base_dir, &str[i]);
i = j;
}
// NOTICE: string is not freed, because paths are not copied.
@@ -72,10 +70,10 @@ BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir,
static void SetBMPWithRandom(struct HackBGRT_config* config, int weight, enum HackBGRT_action action, int x, int y, int o, const CHAR16* path) {
config->image_weight_sum += weight;
UINT32 random = Random();
UINT32 limit = 0xfffffffful / config->image_weight_sum * weight;
Log(config->debug, L"HackBGRT: n=%d, action=%d, x=%d, y=%d, o=%d, path=%s, random = %08x, limit = %08x\n", weight, action, x, y, o, path, random, limit);
if (!config->image_weight_sum || random <= limit) {
UINT32 random = (((UINT64) Random() & 0xffffffff) * config->image_weight_sum) >> 32;
UINT32 limit = ((UINT64) 0xffffffff * weight) >> 32;
Log(config->debug, L"%s n=%d, action=%d, x=%d, y=%d, o=%d, path=%s, rand=%x/%x\n", random <= limit ? L"Using" : L"Skipping", weight, action, x, y, o, path, random, limit);
if (random <= limit) {
config->action = action;
config->image_path = path;
config->orientation = o;
@@ -110,7 +108,7 @@ static void ReadConfigImage(struct HackBGRT_config* config, const CHAR16* line)
} else if (StrStr(line, L"keep")) {
action = HackBGRT_KEEP;
} else {
Log(1, L"HackBGRT: Invalid image line: %s\n", line);
Log(1, L"Invalid image line: %s\n", line);
return;
}
int weight = n && (!f || n < f) ? Atoi(n) : 1;
@@ -127,11 +125,11 @@ static void ReadConfigResolution(struct HackBGRT_config* config, const CHAR16* l
config->resolution_x = *x == '-' ? -(int)Atoi(x+1) : (int)Atoi(x);
config->resolution_y = *y == '-' ? -(int)Atoi(y+1) : (int)Atoi(y);
} else {
Log(1, L"HackBGRT: Invalid resolution line: %s\n", line);
Log(1, L"Invalid resolution line: %s\n", line);
}
}
void ReadConfigLine(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, const CHAR16* line) {
void ReadConfigLine(struct HackBGRT_config* config, EFI_FILE_HANDLE base_dir, const CHAR16* line) {
line = TrimLeft(line);
if (line[0] == L'#' || line[0] == 0) {
return;
@@ -154,7 +152,7 @@ void ReadConfigLine(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, co
return;
}
if (StrnCmp(line, L"config=", 7) == 0) {
ReadConfigFile(config, root_dir, line + 7);
ReadConfigFile(config, base_dir, line + 7);
return;
}
if (StrnCmp(line, L"resolution=", 11) == 0) {

View File

@@ -1,6 +1,6 @@
#pragma once
#include <efi.h>
#include "efi.h"
/**
* Possible actions to perform on the BGRT.
@@ -14,7 +14,7 @@ enum HackBGRT_action {
* @see struct HackBGRT_config
*/
enum HackBGRT_coordinate {
HackBGRT_coord_keep = -1000000001
HackBGRT_coord_keep = -1000001
};
/**
@@ -39,17 +39,17 @@ struct HackBGRT_config {
* 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 base_dir The base 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);
extern void ReadConfigLine(struct HackBGRT_config* config, EFI_FILE_HANDLE base_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 base_dir The base 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);
extern BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE base_dir, const CHAR16* path);

159
src/efi.c Normal file
View File

@@ -0,0 +1,159 @@
#include "efi.h"
#include "util.h"
// New implementations of some functions in gnu-efi.
// These functions are designed to avoid other gnu-efi calls.
EFI_STATUS LibLocateProtocol(IN EFI_GUID *ProtocolGuid, OUT VOID **Interface) {
EFI_HANDLE buffer[256];
UINTN size = sizeof(buffer);
if (!EFI_ERROR(BS->LocateHandle(ByProtocol, ProtocolGuid, NULL, &size, buffer))) {
for (int i = 0; i < size / sizeof(EFI_HANDLE); ++i) {
if (!EFI_ERROR(BS->HandleProtocol(buffer[i], ProtocolGuid, Interface))) {
return EFI_SUCCESS;
}
}
}
return EFI_NOT_FOUND;
}
EFI_DEVICE_PATH *FileDevicePath(IN EFI_HANDLE Device OPTIONAL, IN CHAR16 *FileName) {
EFI_DEVICE_PATH *old_path = 0;
if (!Device || EFI_ERROR(BS->HandleProtocol(Device, TmpGuidPtr((EFI_GUID) EFI_DEVICE_PATH_PROTOCOL_GUID), (void**)&old_path))) {
static EFI_DEVICE_PATH end_path = {END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, {sizeof(end_path), 0}};
old_path = &end_path;
}
UINTN old_path_size = 0, instances = 0;
for (EFI_DEVICE_PATH *p0 = old_path;; p0 = NextDevicePathNode(p0)) {
old_path_size += DevicePathNodeLength(p0);
if (IsDevicePathEndType(p0)) {
instances += 1;
}
if (IsDevicePathEnd(p0)) {
break;
}
}
UINTN size_str = (StrLen(FileName) + 1) * sizeof(*FileName);
UINTN size_fdp = SIZE_OF_FILEPATH_DEVICE_PATH + size_str;
EFI_DEVICE_PATH *new_path;
if (EFI_ERROR(BS->AllocatePool(EfiBootServicesData, old_path_size + instances * size_fdp, (void**)&new_path))) {
return 0;
}
EFI_DEVICE_PATH *p1 = new_path;
for (EFI_DEVICE_PATH *p0 = old_path;; p0 = NextDevicePathNode(p0)) {
if (IsDevicePathEndType(p0)) {
*p1 = (EFI_DEVICE_PATH) {
.Type = MEDIA_DEVICE_PATH,
.SubType = MEDIA_FILEPATH_DP,
.Length = {size_fdp, size_fdp >> 8},
};
FILEPATH_DEVICE_PATH *f = (FILEPATH_DEVICE_PATH *) p1;
BS->CopyMem(f->PathName, FileName, size_str);
p1 = NextDevicePathNode(p1);
}
BS->CopyMem(p1, p0, DevicePathNodeLength(p0));
if (IsDevicePathEnd(p0)) {
break;
}
p1 = NextDevicePathNode(p1);
}
return new_path;
}
INTN CompareMem(IN CONST VOID *Dest, IN CONST VOID *Src, IN UINTN len) {
CONST UINT8 *d = Dest, *s = Src;
for (UINTN i = 0; i < len; ++i) {
if (d[i] != s[i]) {
return d[i] - s[i];
}
}
return 0;
}
void StrnCat(IN CHAR16* dest, IN CONST CHAR16* src, UINTN len) {
CHAR16* d = dest;
while (*d) {
++d;
}
while (len-- && *src) {
*d++ = *src++;
}
*d = 0;
}
UINTN StrLen(IN CONST CHAR16* s) {
UINTN i = 0;
while (*s++) {
++i;
}
return i;
}
INTN StriCmp(IN CONST CHAR16* s1, IN CONST CHAR16* s2) {
while (*s1 && *s2) {
CHAR16 c1 = *s1++, c2 = *s2++;
if (c1 >= 'A' && c1 <= 'Z') {
c1 += 'a' - 'A';
}
if (c2 >= 'A' && c2 <= 'Z') {
c2 += 'a' - 'A';
}
if (c1 != c2) {
return c1 - c2;
}
}
return *s1 - *s2;
}
INTN StrnCmp(IN CONST CHAR16* s1, IN CONST CHAR16* s2, UINTN len) {
while (*s1 && *s2 && len--) {
CHAR16 c1 = *s1++, c2 = *s2++;
if (c1 >= 'A' && c1 <= 'Z') {
c1 += 'a' - 'A';
}
if (c2 >= 'A' && c2 <= 'Z') {
c2 += 'a' - 'A';
}
if (c1 != c2) {
return c1 - c2;
}
}
return len ? *s1 - *s2 : 0;
}
INTN StrCmp(IN CONST CHAR16* s1, IN CONST CHAR16* s2) {
while (*s1 && *s2) {
if (*s1 != *s2) {
return *s1 - *s2;
}
++s1, ++s2;
}
return *s1 - *s2;
}
UINTN Atoi(IN CONST CHAR16* s) {
UINTN n = 0;
while (*s >= '0' && *s <= '9') {
n = n * 10 + *s++ - '0';
}
return n;
}
void *memset(void *s, int c, __SIZE_TYPE__ n) {
unsigned char *p = s;
while (n--)
*p++ = c;
return s;
}
void *memcpy(void *dest, const void *src, __SIZE_TYPE__ n) {
const unsigned char *q = src;
unsigned char *p = dest;
while (n--)
*p++ = *q++;
return dest;
}

5
src/efi.h Normal file
View File

@@ -0,0 +1,5 @@
#pragma once
#include <stdint.h>
#include <efi.h>
#include <efilib.h>

View File

@@ -1,6 +1,4 @@
#include <efi.h>
#include <efilib.h>
#include "efi.h"
#include "types.h"
#include "config.h"
#include "util.h"
@@ -14,6 +12,10 @@
const CHAR16 version[] = L"unknown; not an official release?";
#endif
EFI_SYSTEM_TABLE *ST;
EFI_BOOT_SERVICES *BS;
EFI_RUNTIME_SERVICES *RT;
/**
* The configuration.
*/
@@ -28,8 +30,7 @@ static struct HackBGRT_config config = {
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);
LibLocateProtocol(TmpGuidPtr((EFI_GUID) EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID), (void**) &gop);
}
return gop;
}
@@ -43,9 +44,13 @@ static EFI_GRAPHICS_OUTPUT_PROTOCOL* GOP(void) {
static void SetResolution(int w, int h) {
EFI_GRAPHICS_OUTPUT_PROTOCOL* gop = GOP();
if (!gop) {
config.old_resolution_x = config.resolution_x = 0;
config.old_resolution_y = config.resolution_y = 0;
Log(config.debug, L"GOP not found!\n");
if (config.resolution_x <= 0 || config.resolution_y <= 0) {
config.resolution_x = 1024;
config.resolution_y = 768;
}
config.old_resolution_x = config.resolution_x;
config.old_resolution_y = config.resolution_y;
Log(config.debug, L"GOP not found! Assuming resolution %dx%d.\n", config.resolution_x, config.resolution_y);
return;
}
UINTN best_i = gop->Mode->Mode;
@@ -64,12 +69,12 @@ static void SetResolution(int w, int h) {
continue;
}
if (info_size < sizeof(*info)) {
FreePool(info);
BS->FreePool(info);
continue;
}
new_w = info->HorizontalResolution;
new_h = info->VerticalResolution;
FreePool(info);
BS->FreePool(info);
// Sum of missing w/h should be minimal.
int new_missing = max(w - new_w, 0) + max(h - new_h, 0);
@@ -107,11 +112,11 @@ ACPI_SDT_HEADER* CreateXsdt(ACPI_SDT_HEADER* xsdt0, UINTN entries) {
UINT32 xsdt_len = sizeof(ACPI_SDT_HEADER) + entries * sizeof(UINT64);
BS->AllocatePool(EfiACPIReclaimMemory, xsdt_len, (void**)&xsdt);
if (!xsdt) {
Log(1, L"HackBGRT: Failed to allocate memory for XSDT.\n");
Log(1, L"Failed to allocate memory for XSDT.\n");
return 0;
}
ZeroMem(xsdt, xsdt_len);
CopyMem(xsdt, xsdt0, min(xsdt0->length, xsdt_len));
BS->SetMem(xsdt, xsdt_len, 0);
BS->CopyMem(xsdt, xsdt0, min(xsdt0->length, xsdt_len));
xsdt->length = xsdt_len;
SetAcpiSdtChecksum(xsdt);
return xsdt;
@@ -130,9 +135,8 @@ ACPI_SDT_HEADER* CreateXsdt(ACPI_SDT_HEADER* xsdt0, UINTN entries) {
*/
static ACPI_BGRT* HandleAcpiTables(enum HackBGRT_action action, ACPI_BGRT* bgrt) {
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)) {
if (CompareMem(vendor_guid, TmpGuidPtr((EFI_GUID) ACPI_TABLE_GUID), sizeof(EFI_GUID)) != 0 && CompareMem(vendor_guid, TmpGuidPtr((EFI_GUID) ACPI_20_TABLE_GUID), sizeof(EFI_GUID)) != 0) {
continue;
}
ACPI_20_RSDP* rsdp = (ACPI_20_RSDP *) ST->ConfigurationTable[i].VendorTable;
@@ -208,7 +212,7 @@ static BMP* MakeBMP(int w, int h, UINT8 r, UINT8 g, UINT8 b) {
BMP* bmp = 0;
BS->AllocatePool(EfiBootServicesData, 54 + w * h * 4, (void**) &bmp);
if (!bmp) {
Log(1, L"HackBGRT: Failed to allocate a blank BMP!\n");
Log(1, L"Failed to allocate a blank BMP!\n");
BS->Stall(1000000);
return 0;
}
@@ -235,25 +239,32 @@ static BMP* MakeBMP(int w, int h, UINT8 r, UINT8 g, UINT8 b) {
/**
* Load a bitmap or generate a black one.
*
* @param root_dir The root directory for loading a BMP.
* @param path The BMP path within the root directory; NULL for a black BMP.
* @param base_dir The directory for loading a BMP.
* @param path The BMP path within the directory; NULL for a black BMP.
* @return The loaded BMP, or 0 if not available.
*/
static BMP* LoadBMP(EFI_FILE_HANDLE root_dir, const CHAR16* path) {
static BMP* LoadBMP(EFI_FILE_HANDLE base_dir, const CHAR16* path) {
if (!path) {
return MakeBMP(1, 1, 0, 0, 0); // empty path = black image
}
Log(config.debug, L"HackBGRT: Loading %s.\n", path);
Log(config.debug, L"Loading %s.\n", path);
UINTN size = 0;
BMP* bmp = LoadFile(root_dir, path, &size);
BMP* bmp = LoadFile(base_dir, path, &size);
if (bmp) {
if (size >= bmp->file_size && CompareMem(bmp, "BM", 2) == 0 && bmp->file_size - bmp->pixel_data_offset > 4 && bmp->width && bmp->height && (bmp->bpp == 32 || bmp->bpp == 24) && bmp->compression == 0) {
if (size >= bmp->file_size
&& CompareMem(bmp, "BM", 2) == 0
&& bmp->file_size > bmp->pixel_data_offset
&& bmp->width > 0
&& bmp->height > 0
&& (bmp->bpp == 32 || bmp->bpp == 24)
&& bmp->height * (-(-(bmp->width * (bmp->bpp / 8)) & ~3)) <= bmp->file_size - bmp->pixel_data_offset
&& bmp->compression == 0) {
return bmp;
}
FreePool(bmp);
Log(1, L"HackBGRT: Invalid BMP (%s)!\n", path);
BS->FreePool(bmp);
Log(1, L"Invalid BMP (%s)!\n", path);
} else {
Log(1, L"HackBGRT: Failed to load BMP (%s)!\n", path);
Log(1, L"Failed to load BMP (%s)!\n", path);
}
BS->Stall(1000000);
return MakeBMP(16, 16, 255, 0, 0); // error = red image
@@ -271,13 +282,11 @@ static void CropBMP(BMP* bmp, int w, int h) {
bmp->image_size = 0;
bmp->width = min(bmp->width, w);
bmp->height = min(bmp->height, h);
const int h_max = (bmp->file_size - bmp->pixel_data_offset) / old_pitch;
bmp->height = min(bmp->height, h_max);
const int new_pitch = -(-(bmp->width * (bmp->bpp / 8)) & ~3);
if (new_pitch < old_pitch) {
for (int i = 1; i < bmp->height; ++i) {
CopyMem(
BS->CopyMem(
(UINT8*) bmp + bmp->pixel_data_offset + i * new_pitch,
(UINT8*) bmp + bmp->pixel_data_offset + i * old_pitch,
new_pitch
@@ -290,9 +299,9 @@ static void CropBMP(BMP* bmp, int w, int h) {
/**
* The main logic for BGRT modification.
*
* @param root_dir The root directory for loading a BMP.
* @param base_dir The directory for loading a BMP.
*/
void HackBgrt(EFI_FILE_HANDLE root_dir) {
void HackBgrt(EFI_FILE_HANDLE base_dir) {
// REMOVE: simply delete all BGRT entries.
if (config.action == HackBGRT_REMOVE) {
HandleAcpiTables(config.action, 0);
@@ -321,7 +330,7 @@ void HackBgrt(EFI_FILE_HANDLE root_dir) {
// Replace missing = allocate new.
BS->AllocatePool(EfiACPIReclaimMemory, sizeof(*bgrt), (void**)&bgrt);
if (!bgrt) {
Log(1, L"HackBGRT: Failed to allocate memory for BGRT.\n");
Log(1, L"Failed to allocate memory for BGRT.\n");
return;
}
}
@@ -343,7 +352,7 @@ void HackBgrt(EFI_FILE_HANDLE root_dir) {
// Get the image (either old or new).
BMP* new_bmp = old_bmp;
if (config.action == HackBGRT_REPLACE) {
new_bmp = LoadBMP(root_dir, config.image_path);
new_bmp = LoadBMP(base_dir, config.image_path);
}
// No image = no need for BGRT.
@@ -374,7 +383,7 @@ void HackBgrt(EFI_FILE_HANDLE root_dir) {
bgrt->image_offset_y = max(0, min(max_y, new_y + (new_reso_y - new_bmp->height) / 2));
Log(config.debug,
L"HackBGRT: BMP at (%d, %d), center (%d, %d), resolution (%d, %d), orientation %d.\n",
L"BMP at (%d, %d), center (%d, %d), resolution (%d, %d), orientation %d.\n",
(int) bgrt->image_offset_x, (int) bgrt->image_offset_y,
new_x, new_y, new_reso_x, new_reso_y,
new_orientation * 90
@@ -391,9 +400,9 @@ void HackBgrt(EFI_FILE_HANDLE root_dir) {
static EFI_HANDLE LoadApp(int print_failure, EFI_HANDLE image_handle, EFI_LOADED_IMAGE* image, const CHAR16* path) {
EFI_DEVICE_PATH* boot_dp = FileDevicePath(image->DeviceHandle, (CHAR16*) path);
EFI_HANDLE result = 0;
Log(config.debug, L"HackBGRT: Loading application %s.\n", path);
Log(config.debug, L"Loading application %s.\n", path);
if (EFI_ERROR(BS->LoadImage(0, image_handle, boot_dp, 0, 0, &result))) {
Log(config.debug || print_failure, L"HackBGRT: Failed to load application %s.\n", path);
Log(config.debug || print_failure, L"Failed to load application %s.\n", path);
}
return result;
}
@@ -401,37 +410,62 @@ static EFI_HANDLE LoadApp(int print_failure, EFI_HANDLE image_handle, EFI_LOADED
/**
* The main program.
*/
EFI_STATUS EFIAPI EfiMain(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *ST_) {
InitializeLib(image_handle, ST_);
EFI_STATUS EFIAPI efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *ST_) {
ST = ST_;
BS = ST_->BootServices;
RT = ST_->RuntimeServices;
// Clear the screen to wipe the vendor logo.
ST->ConOut->EnableCursor(ST->ConOut, 0);
ST->ConOut->ClearScreen(ST->ConOut);
Log(0, L"HackBGRT version: %s\n", version);
EFI_LOADED_IMAGE* image;
if (EFI_ERROR(BS->HandleProtocol(image_handle, &LoadedImageProtocol, (void**) &image))) {
Log(config.debug, L"HackBGRT: LOADED_IMAGE_PROTOCOL failed.\n");
if (EFI_ERROR(BS->HandleProtocol(image_handle, TmpGuidPtr((EFI_GUID) EFI_LOADED_IMAGE_PROTOCOL_GUID), (void**) &image))) {
Log(config.debug, L"LOADED_IMAGE_PROTOCOL failed.\n");
goto fail;
}
EFI_FILE_HANDLE root_dir = LibOpenRoot(image->DeviceHandle);
EFI_FILE_IO_INTERFACE* io;
if (EFI_ERROR(BS->HandleProtocol(image->DeviceHandle, TmpGuidPtr((EFI_GUID) EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID), (void**) &io))) {
Log(config.debug, L"FILE_SYSTEM_PROTOCOL failed.\n");
goto fail;
}
CHAR16 **argv;
int argc = GetShellArgcArgv(image_handle, &argv);
EFI_FILE_HANDLE root_dir;
if (EFI_ERROR(io->OpenVolume(io, &root_dir))) {
Log(config.debug, L"Failed to open root directory.\n");
goto fail;
}
if (argc <= 1) {
const CHAR16* config_path = L"\\EFI\\HackBGRT\\config.txt";
if (!ReadConfigFile(&config, root_dir, config_path)) {
Log(1, L"HackBGRT: No config, no command line!\n", config_path);
EFI_FILE_HANDLE base_dir;
if (EFI_ERROR(root_dir->Open(root_dir, &base_dir, L"\\EFI\\HackBGRT", EFI_FILE_MODE_READ, 0))) {
Log(config.debug, L"Failed to HackBGRT directory.\n");
base_dir = root_dir;
}
EFI_SHELL_PARAMETERS_PROTOCOL *shell_param_proto = NULL;
if (EFI_ERROR(BS->OpenProtocol(image_handle, TmpGuidPtr((EFI_GUID) EFI_SHELL_PARAMETERS_PROTOCOL_GUID), (void**) &shell_param_proto, 0, 0, EFI_OPEN_PROTOCOL_GET_PROTOCOL)) || shell_param_proto->Argc <= 1) {
const CHAR16* config_path = L"config.txt";
if (!ReadConfigFile(&config, base_dir, config_path)) {
Log(1, L"No config, no command line!\n", config_path);
goto fail;
}
} else {
CHAR16 **argv = shell_param_proto->Argv;
int argc = shell_param_proto->Argc;
for (int i = 1; i < argc; ++i) {
ReadConfigLine(&config, base_dir, argv[i]);
}
}
for (int i = 1; i < argc; ++i) {
ReadConfigLine(&config, root_dir, argv[i]);
}
if (config.debug) {
Print(L"HackBGRT version: %s\n", version);
Log(-1, L"HackBGRT version: %s\n", version);
}
SetResolution(config.resolution_x, config.resolution_y);
HackBgrt(root_dir);
HackBgrt(base_dir);
EFI_HANDLE next_image_handle = 0;
static CHAR16 backup_boot_path[] = L"\\EFI\\HackBGRT\\bootmgfw-original.efi";
@@ -455,15 +489,15 @@ EFI_STATUS EFIAPI EfiMain(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *ST_) {
if (try_ms_quietly) {
goto ready_to_boot;
}
Log(1, L"HackBGRT: Reverting to %s.\n", config.boot_path);
Print(L"Press escape to cancel or any other key (or wait 15 seconds) to boot.\n");
Log(1, L"Reverting to %s.\n", config.boot_path);
Log(-1, L"Press escape to cancel or any other key (or wait 15 seconds) to boot.\n");
if (ReadKey(15000).ScanCode == SCAN_ESC) {
goto fail;
}
} else ready_to_boot: if (config.debug) {
Print(L"HackBGRT: Ready to boot.\n");
Print(L"If all goes well, you can set debug=0 and log=0 in config.txt.\n");
Print(L"Press escape to cancel or any other key (or wait 15 seconds) to boot.\n");
Log(-1, L"Ready to boot.\n");
Log(-1, L"If all goes well, you can set debug=0 and log=0 in config.txt.\n");
Log(-1, L"Press escape to cancel or any other key (or wait 15 seconds) to boot.\n");
if (ReadKey(15000).ScanCode == SCAN_ESC) {
return 0;
}
@@ -472,29 +506,20 @@ EFI_STATUS EFIAPI EfiMain(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *ST_) {
ClearLogVariable();
}
if (EFI_ERROR(BS->StartImage(next_image_handle, 0, 0))) {
Log(1, L"HackBGRT: Failed to start %s.\n", config.boot_path);
Log(1, L"Failed to start %s.\n", config.boot_path);
goto fail;
}
Log(1, L"HackBGRT: Started %s. Why are we still here?!\n", config.boot_path);
Print(L"Please check that %s is not actually HackBGRT!\n", config.boot_path);
Log(1, L"Started %s. Why are we still here?!\n", config.boot_path);
Log(-1, L"Please check that %s is not actually HackBGRT!\n", config.boot_path);
goto fail;
fail: {
Log(1, L"HackBGRT has failed.\n");
Print(L"Dumping log:\n\n");
Log(-1, L"Dumping log:\n\n");
DumpLog();
Print(L"If you can't boot into Windows, get install/recovery disk to fix your boot.\n");
Print(L"Press any key (or wait 15 seconds) to exit.\n");
Log(-1, L"If you can't boot into Windows, get install/recovery disk to fix your boot.\n");
Log(-1, L"Press any key (or wait 15 seconds) to exit.\n");
ReadKey(15000);
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_);
}

View File

@@ -1,6 +1,6 @@
#pragma once
#include <efi.h>
#include "efi.h"
#pragma pack(push, 1)

View File

@@ -1,7 +1,5 @@
#include "util.h"
#include <efilib.h>
const CHAR16* TmpStr(CHAR8 *src, int length) {
static CHAR16 arr[4][16];
static int j;
@@ -14,31 +12,99 @@ const CHAR16* TmpStr(CHAR8 *src, int length) {
return dest;
}
const CHAR16* TmpIntToStr(UINT32 x) {
static CHAR16 buf[20];
int i = 20 - 1;
buf[i] = 0;
if (!x) {
buf[--i] = '0';
}
while (x && i) {
buf[--i] = '0' + (x % 10);
x /= 10;
}
return &buf[i];
}
#define log_buffer_size (65536)
CHAR16 log_buffer[log_buffer_size] = {0};
CHAR16 LogVarName[] = L"HackBGRTLog";
EFI_GUID LogVarGuid = {0x03c64761, 0x075f, 0x4dba, {0xab, 0xfb, 0x2e, 0xd8, 0x9e, 0x18, 0xb2, 0x36}}; // self-made: 03c64761-075f-4dba-abfb-2ed89e18b236
void Log(int print, IN CONST CHAR16 *fmt, ...) {
void Log(int mode, IN CONST CHAR16 *fmt, ...) {
va_list args;
CHAR16 buf[256];
CHAR16 buf[256] = {0};
int buf_i = 0;
#define putchar(c) { if (buf_i < 255) { buf[buf_i++] = c; } }
va_start(args, fmt);
VSPrint(buf, sizeof(buf), fmt, args); // size is in bytes, not CHAR16s
va_end(args);
if (print) {
Print(L"%s", buf);
for (int i = 0; fmt[i]; ++i) {
if (fmt[i] == '\n') {
putchar('\r');
putchar('\n');
continue;
}
if (fmt[i] != '%') {
putchar(fmt[i]);
continue;
}
++i;
switch (fmt[i]) {
case '%': putchar('%'); continue;
case 'd': goto fmt_decimal;
case 'x': goto fmt_hex;
case 's': goto fmt_string;
case 0: goto fmt_end;
}
putchar('%');
putchar(fmt[i]);
continue;
if (0) fmt_decimal: {
INT32 x = va_arg(args, INT32);
if (x < 0) {
putchar('-');
x = -x;
}
const CHAR16* s = TmpIntToStr(x);
while (*s) {
putchar(*s++);
}
}
if (0) fmt_hex: {
UINT32 x = va_arg(args, UINT32);
for (int pos = 8, started = 0; pos--;) {
int d = (x >> (4 * pos)) & 0xf;
if (d || started || pos == 0) {
putchar("0123456789abcdef"[d]);
started = 1;
}
}
}
if (0) fmt_string: {
CHAR16* s = va_arg(args, CHAR16*);
while (*s) {
putchar(*s++);
}
}
}
fmt_end:
va_end(args);
if (mode) {
ST->ConOut->OutputString(ST->ConOut, buf);
}
if (mode != -1) {
StrnCat(log_buffer, buf, log_buffer_size - StrLen(log_buffer) - 1);
RT->SetVariable(LogVarName, &LogVarGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, StrLen(log_buffer) * 2, log_buffer);
}
StrnCat(log_buffer, buf, log_buffer_size - StrLen(log_buffer) - 1);
LibSetVariable(LogVarName, &LogVarGuid, StrLen(log_buffer) * 2, log_buffer);
}
void DumpLog(void) {
Print(L"%s", log_buffer);
ST->ConOut->OutputString(ST->ConOut, log_buffer);
}
void ClearLogVariable(void) {
LibDeleteVariable(LogVarName, &LogVarGuid);
RT->SetVariable(LogVarName, &LogVarGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, 0, 0);
}
const CHAR16* TrimLeft(const CHAR16* s) {
@@ -94,11 +160,24 @@ void RandomSeedAuto(void) {
EFI_STATUS WaitKey(UINT64 timeout_ms) {
ST->ConIn->Reset(ST->ConIn, FALSE);
const int ms_to_100ns = 10000;
return WaitForSingleEvent(ST->ConIn->WaitForKey, timeout_ms * ms_to_100ns);
EFI_EVENT events[2] = {ST->ConIn->WaitForKey};
EFI_STATUS status = BS->CreateEvent(EVT_TIMER, 0, NULL, NULL, &events[1]);
if (!EFI_ERROR(status)) {
BS->SetTimer(events[1], TimerRelative, timeout_ms * ms_to_100ns);
UINTN index;
status = BS->WaitForEvent(2, events, &index);
BS->CloseEvent(events[1]);
if (!EFI_ERROR(status) && index == 1) {
status = EFI_TIMEOUT;
}
}
return status;
}
EFI_INPUT_KEY ReadKey(UINT64 timeout_ms) {
EFI_INPUT_KEY key = {0};
ST->ConOut->EnableCursor(ST->ConOut, 1);
WaitKey(timeout_ms);
ST->ConIn->ReadKeyStroke(ST->ConIn, &key);
return key;
@@ -113,9 +192,11 @@ void* LoadFileWithPadding(EFI_FILE_HANDLE dir, const CHAR16* path, UINTN* size_p
return 0;
}
EFI_FILE_INFO *info = LibFileInfo(handle);
UINTN size = info->FileSize;
FreePool(info);
UINT64 get_size = 0;
handle->SetPosition(handle, ~(UINT64)0);
handle->GetPosition(handle, &get_size);
handle->SetPosition(handle, 0);
UINTN size = (UINTN) get_size;
void* data = 0;
e = BS->AllocatePool(EfiBootServicesData, size + padding, &data);
@@ -129,7 +210,7 @@ void* LoadFileWithPadding(EFI_FILE_HANDLE dir, const CHAR16* path, UINTN* size_p
}
handle->Close(handle);
if (EFI_ERROR(e)) {
FreePool(data);
BS->FreePool(data);
return 0;
}
if (size_ptr) {

View File

@@ -1,6 +1,6 @@
#pragma once
#include <efi.h>
#include "efi.h"
/**
* Convert a short ASCII string to UCS2, store in a static array.
@@ -12,9 +12,12 @@
extern const CHAR16* TmpStr(CHAR8 *src, int length);
/**
* Empty function that has the same signature as Print.
* Print or log a string.
*
* @param mode -1 = print without logging, 0 = no, 1 = yes.
* @param fmt The format string. Supports %d, %x, %s.
*/
extern void Log(int print, IN CONST CHAR16 *fmt, ...);
extern void Log(int mode, IN CONST CHAR16 *fmt, ...);
/**
* Dump the log buffer to the screen.
@@ -117,3 +120,11 @@ static inline void* LoadFile(EFI_FILE_HANDLE dir, const CHAR16* path, UINTN* siz
return LoadFileWithPadding(dir, path, size_ptr, 0);
}
/**
* Get a temporary pointer to GUID.
*/
static inline EFI_GUID* TmpGuidPtr(EFI_GUID guid) {
static EFI_GUID g;
g = guid;
return &g;
}