mirror of
https://github.com/Metabolix/HackBGRT.git
synced 2025-12-07 09:36:10 -08:00
Compare commits
66 Commits
v2.1.0
...
1e36d7e388
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e36d7e388 | ||
|
|
9038e20cd2 | ||
|
|
9a0d4737e1 | ||
|
|
fa6fae3aa3 | ||
|
|
90fb8e47c1 | ||
|
|
c826149183 | ||
|
|
af4f99aab6 | ||
|
|
8a97382a2e | ||
|
|
8e6466990a | ||
|
|
6f94f6bc28 | ||
|
|
bc600a6c2f | ||
|
|
022ea9b93b | ||
|
|
7d7d4c2aa4 | ||
|
|
f1c8b11d6b | ||
|
|
a0553856f0 | ||
|
|
ffa29f6ffc | ||
|
|
b9e23c91a3 | ||
|
|
697c57355b | ||
|
|
da16365508 | ||
|
|
665a4732ca | ||
|
|
39596aadfc | ||
|
|
1a5b1df064 | ||
|
|
ea70f3ce79 | ||
|
|
a44b929012 | ||
|
|
9948e5a306 | ||
|
|
518d7c8a97 | ||
|
|
c6108ffd62 | ||
|
|
6dc447a8ce | ||
|
|
5ec17a49e8 | ||
|
|
7b7309a255 | ||
|
|
a82646a822 | ||
|
|
294da9c069 | ||
|
|
4096002eb2 | ||
|
|
835cc1b2f2 | ||
|
|
74a143e723 | ||
|
|
b75dbe13c7 | ||
|
|
ba72b90082 | ||
|
|
6b724d5590 | ||
|
|
9ebe4e2476 | ||
|
|
5111897fae | ||
|
|
aac8a38cbb | ||
|
|
77dd2bd699 | ||
|
|
8dfa456a7b | ||
|
|
7ccdcc4a77 | ||
|
|
50e84e8c90 | ||
|
|
24c7e2b316 | ||
|
|
4379f9cbeb | ||
|
|
2a0f2a7757 | ||
|
|
ebbacb72bb | ||
|
|
a908036ee8 | ||
|
|
57ce3ae33e | ||
|
|
7dd048346d | ||
|
|
0dfc49c800 | ||
|
|
4e50b33636 | ||
|
|
5856d25b5b | ||
|
|
466ab69c48 | ||
|
|
14aa79929a | ||
|
|
5e32a3f880 | ||
|
|
db934099df | ||
|
|
e93ed54cb2 | ||
|
|
9b3b045a21 | ||
|
|
be8a5d35d2 | ||
|
|
2366fc8b98 | ||
|
|
e04ba3e451 | ||
|
|
f40f2bc9dc | ||
|
|
2f572b24d4 |
21
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
21
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal 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
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "gnu-efi"]
|
||||||
|
path = gnu-efi
|
||||||
|
url = https://github.com/rhboot/gnu-efi.git
|
||||||
43
CHANGELOG.md
43
CHANGELOG.md
@@ -2,6 +2,49 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
## 2.5.0 - 2024-06-21
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Properly handle skip-shim with enable-overwrite.
|
||||||
|
- Improve instructions (documentation).
|
||||||
|
- Improve error reporting and logging.
|
||||||
|
|
||||||
|
## 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
|
||||||
|
- Support Secure Boot with *shim* boot loader.
|
||||||
|
- Gather debug log during boot and read it with setup.exe.
|
||||||
|
|
||||||
## 2.1.0 - 2023-10-04
|
## 2.1.0 - 2023-10-04
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
154
Makefile
154
Makefile
@@ -1,44 +1,136 @@
|
|||||||
CC = $(CC_PREFIX)-gcc
|
CC = clang
|
||||||
CFLAGS = -std=c11 -O2 -ffreestanding -mno-red-zone -fno-stack-protector -Wshadow -Wall -Wunused -Werror-implicit-function-declaration -Werror
|
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
|
CFLAGS += -I$(GNUEFI_INC) -I$(GNUEFI_INC)/$(GNUEFI_ARCH) -I$(GNUEFI_INC)/protocol
|
||||||
LDFLAGS = -nostdlib -shared -Wl,-dll -Wl,--subsystem,10 -e _EfiMain
|
CFLAGS += $(ARCH_CFLAGS)
|
||||||
LIBS = -L$(GNUEFI_LIB) -lefi -lgcc
|
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_INC = gnu-efi/inc
|
||||||
GNUEFI_LIB = /usr/$(CC_PREFIX)/lib
|
|
||||||
|
|
||||||
FILES_C = src/main.c src/util.c src/types.c src/config.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_H = $(wildcard src/*.h)
|
||||||
FILES_CS = src/Setup.cs src/Esp.cs src/Efi.cs
|
FILES_CS = src/Setup.cs src/Esp.cs src/Efi.cs src/EfiBootEntries.cs
|
||||||
GIT_DESCRIBE = $(firstword $(shell git describe --tags) unknown)
|
|
||||||
CFLAGS += '-DGIT_DESCRIBE=L"$(GIT_DESCRIBE)"'
|
# Generate version number from git describe.
|
||||||
ZIPDIR = HackBGRT-$(GIT_DESCRIBE:v%=%)
|
# In the numeric form, add the number of commits as the last part.
|
||||||
ZIP = $(ZIPDIR).zip
|
# (Add .1 for uncommitted changes.)
|
||||||
|
GIT_DESCRIBE := $(firstword $(GIT_DESCRIBE) $(shell git describe --tags --dirty=-1-dirty) unknown)
|
||||||
|
GIT_DESCRIBE_PARTS := $(subst -, ,$(patsubst v%,%,$(GIT_DESCRIBE))) 0
|
||||||
|
GIT_DESCRIBE_NUMERIC := $(firstword $(GIT_DESCRIBE_PARTS)).$(word 2,$(GIT_DESCRIBE_PARTS))
|
||||||
|
|
||||||
|
define GIT_DESCRIBE_CS
|
||||||
|
public class GIT_DESCRIBE {
|
||||||
|
public const string data = "$(GIT_DESCRIBE)";
|
||||||
|
public const string numeric = "$(GIT_DESCRIBE_NUMERIC)";
|
||||||
|
}
|
||||||
|
endef
|
||||||
|
|
||||||
|
CFLAGS += '-DGIT_DESCRIBE_W=L"$(GIT_DESCRIBE)"' '-DGIT_DESCRIBE="$(GIT_DESCRIBE)"'
|
||||||
|
RELEASE_NAME = HackBGRT-$(GIT_DESCRIBE:v%=%)
|
||||||
|
|
||||||
|
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
|
||||||
|
@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."
|
||||||
|
|
||||||
all: efi setup zip
|
|
||||||
efi: bootx64.efi bootia32.efi
|
|
||||||
setup: setup.exe
|
setup: setup.exe
|
||||||
|
|
||||||
zip: $(ZIP)
|
release: release/$(RELEASE_NAME).zip
|
||||||
$(ZIP): bootx64.efi bootia32.efi config.txt splash.bmp setup.exe README.md CHANGELOG.md README.efilib LICENSE
|
@echo "Current version is packaged: $<"
|
||||||
test ! -d "$(ZIPDIR)"
|
|
||||||
mkdir "$(ZIPDIR)"
|
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
|
||||||
cp -a $^ "$(ZIPDIR)" || (rm -rf "$(ZIPDIR)"; exit 1)
|
rm -rf $@
|
||||||
7z a -mx=9 "$(ZIP)" "$(ZIPDIR)" || (rm -rf "$(ZIPDIR)"; exit 1)
|
tar c --transform=s,^,$@/, $^ | tar x
|
||||||
rm -rf "$(ZIPDIR)"
|
|
||||||
|
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)
|
src/GIT_DESCRIBE.cs: $(FILES_CS) $(FILES_C) $(FILES_H)
|
||||||
echo 'public class GIT_DESCRIBE { public const string data = "$(GIT_DESCRIBE)"; }' > $@
|
$(file > $@,$(GIT_DESCRIBE_CS))
|
||||||
|
|
||||||
setup.exe: $(FILES_CS) src/GIT_DESCRIBE.cs
|
setup.exe: $(FILES_CS) src/GIT_DESCRIBE.cs
|
||||||
csc /define:GIT_DESCRIBE /out:$@ $^
|
csc -nologo -define:GIT_DESCRIBE -out:$@ $^
|
||||||
|
|
||||||
bootx64.efi: CC_PREFIX = x86_64-w64-mingw32
|
certificate.cer pki:
|
||||||
bootx64.efi: GNUEFI_ARCH = x86_64
|
@echo
|
||||||
bootx64.efi: $(FILES_C)
|
@echo "You need proper keys to sign the EFI executables."
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) -s
|
@echo "Example:"
|
||||||
|
@echo "mkdir -p pki"
|
||||||
|
@echo "certutil --empty-password -N -d pki"
|
||||||
|
@echo "efikeygen -d pki -n HackBGRT-signer -S -k -c 'CN=HackBGRT Secure Boot Signer,OU=HackBGRT,O=Unknown,MAIL=unknown@example.com' -u 'URL'"
|
||||||
|
@echo "certutil -d pki -n HackBGRT-signer -Lr > certificate.cer"
|
||||||
|
@echo "Modify and run the commands yourself."
|
||||||
|
@echo
|
||||||
|
@false
|
||||||
|
|
||||||
bootia32.efi: CC_PREFIX = i686-w64-mingw32
|
efi-signed/%.efi: efi/%.efi pki
|
||||||
bootia32.efi: GNUEFI_ARCH = ia32
|
@mkdir -p efi-signed
|
||||||
bootia32.efi: $(FILES_C)
|
pesign --force -n pki -i $< -o $@ -c HackBGRT-signer -s
|
||||||
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) -s
|
|
||||||
|
efi/bootx64.efi: CLANG_TARGET = x86_64-pc-windows-msvc
|
||||||
|
efi/bootx64.efi: GNUEFI_ARCH = x86_64
|
||||||
|
|
||||||
|
efi/bootia32.efi: CLANG_TARGET = i386-pc-windows-msvc
|
||||||
|
efi/bootia32.efi: GNUEFI_ARCH = ia32
|
||||||
|
|
||||||
|
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 $@
|
||||||
|
|
||||||
|
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)
|
||||||
|
|||||||
81
README.md
81
README.md
@@ -6,40 +6,52 @@ 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.
|
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
|
## 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.
|
**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 your computer is booting with UEFI.
|
||||||
* Make sure that Secure Boot is disabled, unless you know how to sign EFI applications.
|
* Make sure that you have read the Secure Boot instructions.
|
||||||
* Make sure that BitLocker is disabled, or find your recovery key.
|
* Make sure that BitLocker is disabled, or find your recovery key.
|
||||||
|
|
||||||
|
### Secure Boot instructions
|
||||||
|
|
||||||
|
HackBGRT is not approved by Microsoft. Instead, HackBGRT comes with the *shim* boot loader, which allows to manually select HackBGRT as a trusted program. After installing HackBGRT and rebooting your computer, you have to **follow the instructions in [shim.md](shim.md)** to achieve this. These steps cannot be automated, that's the whole point of Secure Boot. Although HackBGRT is self-signed with a certificate, it's not advisable to enroll foreign certificates directly into your firmware.
|
||||||
|
|
||||||
|
The *shim* boot loader is maintained by Red Hat, Inc, and the included signed copy of *shim* is extracted from Debian GNU/Linux – many thanks to the maintainers! For copyright information, see [shim-signed/COPYRIGHT](shim-signed/COPYRIGHT).
|
||||||
|
|
||||||
### Windows installation
|
### Windows installation
|
||||||
|
|
||||||
* Get the latest release from the Releases page.
|
* Get the latest release from the Releases page.
|
||||||
* Start `setup.exe` and follow the instructions.
|
* Start `setup.exe` and follow the instructions.
|
||||||
* You may need to manually disable Secure Boot and then retry.
|
* The installer will launch Paint for editing the image, or you can edit it otherwise.
|
||||||
* The installer will launch Paint for editing the image.
|
|
||||||
* If Windows later restores the original boot loader, just reinstall.
|
|
||||||
* If you wish to change the image or other configuration, just reinstall.
|
|
||||||
* For advanced settings, edit `config.txt` before installing. No extra support provided!
|
* For advanced settings, edit `config.txt` before installing. No extra support provided!
|
||||||
|
* Read the instructions in [shim.md](shim.md).
|
||||||
|
* Check the common [troubleshooting](#troubleshooting) to be prepared.
|
||||||
|
* Reboot your computer.
|
||||||
|
* If Windows later restores the original boot loader, just reinstall.
|
||||||
|
* If you wish to change the image or configuration later, choose the option to only install files.
|
||||||
|
|
||||||
### Quiet (batch) installation
|
### Quiet (batch) installation
|
||||||
|
|
||||||
* Edit the `config.txt` and `splash.bmp` (or any other images) to your needs.
|
* Edit the `config.txt` and `splash.bmp` (or any other images) to your needs.
|
||||||
* Run `setup.exe batch COMMANDS` as administrator, with some of the following commands:
|
* Run `setup.exe batch COMMANDS` as administrator, with some of the following commands:
|
||||||
* `install` – copy the files but don't enable.
|
* `install` – copy the files but don't enable.
|
||||||
* `enable-entry` – create a new EFI boot entry.
|
|
||||||
* `disable-entry` – disable the EFI boot entry.
|
|
||||||
* `enable-bcdedit` – use `bcdedit` to create a new EFI boot entry.
|
* `enable-bcdedit` – use `bcdedit` to create a new EFI boot entry.
|
||||||
* `disable-bootmgr` – use `bcdedit` to disable the EFI boot entry.
|
* `disable-bcdedit` – use `bcdedit` to disable the EFI boot entry.
|
||||||
|
* `enable-entry` – write NVRAM to create a new EFI boot entry.
|
||||||
|
* `disable-entry` – write NVRAM to disable the EFI boot entry.
|
||||||
* `enable-overwrite` – overwrite the MS boot loader.
|
* `enable-overwrite` – overwrite the MS boot loader.
|
||||||
* `disable-overwrite` – restore the MS boot loader.
|
* `disable-overwrite` – restore the MS boot loader.
|
||||||
|
* `skip-shim` – skip *shim* when installing.
|
||||||
* `allow-secure-boot` – ignore Secure Boot in subsequent commands.
|
* `allow-secure-boot` – ignore Secure Boot in subsequent commands.
|
||||||
* `allow-bitlocker` – ignore BitLocker in subsequent commands.
|
* `allow-bitlocker` – ignore BitLocker in subsequent commands.
|
||||||
* `allow-bad-loader` – ignore bad boot loader configuration in subsequent commands.
|
* `allow-bad-loader` – ignore bad boot loader configuration in subsequent commands.
|
||||||
* `disable` – run all relevant `disable-*` commands.
|
* `disable` – run all relevant `disable-*` commands.
|
||||||
* `uninstall` – disable and remove completely.
|
* `uninstall` – disable and remove completely.
|
||||||
|
* `show-boot-log` – show the debug log collected during boot (if `log=1` is set in `config.txt`).
|
||||||
* For example, run `setup.exe batch install allow-secure-boot enable-overwrite` to copy files and overwrite the MS boot loader regardless of Secure Boot status.
|
* For example, run `setup.exe batch install allow-secure-boot enable-overwrite` to copy files and overwrite the MS boot loader regardless of Secure Boot status.
|
||||||
|
|
||||||
### Multi-boot configurations
|
### Multi-boot configurations
|
||||||
@@ -62,20 +74,61 @@ The configuration options are described in `config.txt`, which the installer cop
|
|||||||
|
|
||||||
## Images
|
## 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.
|
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.
|
## Troubleshooting
|
||||||
|
|
||||||
## Recovery
|
### Verification failed, Security violation
|
||||||
|
|
||||||
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.
|
This is part of the setup on first boot. Make sure you have read and understood [shim.md](shim.md).
|
||||||
|
|
||||||
|
### Boot is slow, boot is stuck, just spinning
|
||||||
|
|
||||||
|
Sometimes the first boot is very slow (multiple minutes) for an unknown reason. Wait patiently until you get into Windows. Try to reboot at least a few times to see if it gets any better. It it does not, there's not much else to do than give up.
|
||||||
|
|
||||||
|
### Image is not visible, "nothing happens"
|
||||||
|
|
||||||
|
Run the setup again and select the option to check the boot log. Continue troubleshooting according to the log contents:
|
||||||
|
|
||||||
|
#### Log is empty
|
||||||
|
|
||||||
|
If the log is empty, then HackBGRT is not in use. Many computers now have a security feature which causes this problem: the computer resets some settings on reboot and skips the newly-installed HackBGRT.
|
||||||
|
|
||||||
|
You have to fix this manually. (After all, the security feature is specifically designed to prevent automatic changes.)
|
||||||
|
|
||||||
|
1. Run the setup again.
|
||||||
|
2. Select the option "boot to UEFI setup".
|
||||||
|
3. After a reboot, you should get into your computer's own setup utility (UEFI or Firmware settings, or so-called "BIOS").
|
||||||
|
4. Find boot options and the list of boot entries.
|
||||||
|
5. Select HackBGRT as the default boot entry (before Windows Boot Loader).
|
||||||
|
|
||||||
|
The setup utility is different for each computer and manufacturer, so search online for "[computer model] UEFI setup" or "firmware setup" for images and instructions.
|
||||||
|
|
||||||
|
Some people report that HackBGRT is not visible in the computer settings. That's unfortunately a problem with your computer, and you should ask your computer manufacturer how to edit boot entries inside your computer settings. HackBGRT needs to boot `\EFI\HackBGRT\loader.efi`.
|
||||||
|
|
||||||
|
If all else fails and you are sure about your computer skills, you can try the legacy installation method. The method bypasses this particular problem but may cause very serious problems if configured incorrectly.
|
||||||
|
|
||||||
|
#### Log is not empty
|
||||||
|
|
||||||
|
Try to reinstall HackBGRT with the default configuration and image.
|
||||||
|
|
||||||
|
If the default logo works, try again with your custom image. Make sure that the image has a reasonable size and position and that you haven't messed up `config.txt`.
|
||||||
|
|
||||||
|
If the default logo does not work, check the boot log again.
|
||||||
|
You may report an issue and attach the `setup.log` file.
|
||||||
|
|
||||||
|
### Impossible to boot at all
|
||||||
|
|
||||||
|
If you used the default installation method, then your Windows boot loader is still in place and you should be able to access UEFI Setup ("BIOS setup") or boot loader list by some key combination right after powering on your computer. There you can choose the `Windows Boot Loader` and continue as usual to uninstall HackBGRT.
|
||||||
|
|
||||||
|
If you selected the legacy installation method which overwrites Windows boot loader, then you need to use the Windows installation disk (or recovery disk) to fix boot issues.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
* Compiler: GCC targeting w64-mingw32
|
* Compiler: Clang
|
||||||
* Compiler flags: see Makefile
|
* Compiler flags: see Makefile
|
||||||
* Libraries: gnu-efi
|
* Libraries: gnu-efi
|
||||||
|
|||||||
19
config.txt
19
config.txt
@@ -16,16 +16,14 @@ boot=MS
|
|||||||
# - "keep" to keep the firmware logo. Also keeps coordinates by default.
|
# - "keep" to keep the firmware logo. Also keeps coordinates by default.
|
||||||
# - "remove" to remove the BGRT. Makes x and y meaningless.
|
# - "remove" to remove the BGRT. Makes x and y meaningless.
|
||||||
# - "black" to use only a black image. Makes x and y meaningless.
|
# - "black" to use only a black image. Makes x and y meaningless.
|
||||||
# - "path=..." to read a BMP file.
|
# - "path=file.bmp" to read an image file.
|
||||||
# * NOTE: For path=\EFI\HackBGRT\*, the installer will copy and convert the file if necessary.
|
# * NOTE: The installer can copy and convert BMP, PNG, JPEG, GIF.
|
||||||
# * 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!
|
|
||||||
# Examples:
|
# Examples:
|
||||||
# - image=remove
|
# - image=remove
|
||||||
# - image=black
|
# - image=black
|
||||||
# - image= x=0 y=-200 path=\EFI\HackBGRT\topimage.bmp
|
# - image= x=0 y=-200 path=topimage.bmp
|
||||||
# - image= n=1 o=90 path=\EFI\HackBGRT\sideways.bmp
|
# - image= n=1 o=90 path=sideways.bmp
|
||||||
# - image= n=50 y=999999 o=keep path=\EFI\HackBGRT\probable.bmp
|
# - image= n=50 y=999999 o=keep path=probable.bmp
|
||||||
# The above examples together would produce
|
# The above examples together would produce
|
||||||
# - 1/54 chance for the default OS logo
|
# - 1/54 chance for the default OS logo
|
||||||
# - 1/54 chance for black screen
|
# - 1/54 chance for black screen
|
||||||
@@ -33,11 +31,16 @@ boot=MS
|
|||||||
# - 1/54 chance for splash.bmp, centered, orientation set to 90 degrees
|
# - 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
|
# - 50/54 chance for probable.bmp, at the bottom edge, explicitly default orientation
|
||||||
# Default: just one image.
|
# 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.
|
# Preferred resolution. Use 0x0 for maximum and -1x-1 for original.
|
||||||
resolution=0x0
|
resolution=0x0
|
||||||
|
|
||||||
|
# Logging (0 for disabled, 1 for enabled).
|
||||||
|
# When logging is enabled, setup.exe can show debug information about the current boot.
|
||||||
|
# The log might occupy a few kilobytes of RAM.
|
||||||
|
log=1
|
||||||
|
|
||||||
# Debug mode (0 for disabled, 1 for enabled).
|
# Debug mode (0 for disabled, 1 for enabled).
|
||||||
# Shows debug information and prompts for keypress before booting.
|
# Shows debug information and prompts for keypress before booting.
|
||||||
debug=0
|
debug=0
|
||||||
|
|||||||
1
gnu-efi
Submodule
1
gnu-efi
Submodule
Submodule gnu-efi added at 74bd9b60ba
55
shim-signed/COPYRIGHT
Normal file
55
shim-signed/COPYRIGHT
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||||
|
Upstream-Name: shim
|
||||||
|
Upstream-Contact: Peter Jones <pjones@redhat.com>
|
||||||
|
Source: https://github.com/rhboot/shim
|
||||||
|
|
||||||
|
Files: *
|
||||||
|
Copyright: 2012 Red Hat, Inc
|
||||||
|
2009-2012 Intel Corporation
|
||||||
|
License: BSD-2-Clause
|
||||||
|
|
||||||
|
Files: debian/po/cs.po
|
||||||
|
Copyright: 2018 Michal Simunek <michal.simunek@gmail.com>
|
||||||
|
License: BSD-2-Clause
|
||||||
|
|
||||||
|
Files: debian/po/de.po
|
||||||
|
Copyright: 2017, 2018 Markus Hiereth <markus.hiereth@freenet.de>
|
||||||
|
License: BSD-2-Clause
|
||||||
|
|
||||||
|
Files: debian/po/fr.po
|
||||||
|
Copyright: 2017, 2018 Alban Vidal <alban.vidal@zordhak.fr>
|
||||||
|
License: BSD-2-Clause
|
||||||
|
|
||||||
|
Files: debian/po/nl.po
|
||||||
|
Copyright: 2017, 2018 Frans Spiesschaert <Frans.Spiesschaert@yucom.be>
|
||||||
|
License: BSD-2-Clause
|
||||||
|
|
||||||
|
Files: debian/po/pt.po
|
||||||
|
Copyright: 2017, 2018 Rui Branco <ruipb@debianpt.org>
|
||||||
|
License: BSD-2-Clause
|
||||||
|
|
||||||
|
License: BSD-2-Clause
|
||||||
|
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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"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 THE
|
||||||
|
COPYRIGHT HOLDER OR CONTRIBUTORS 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.
|
||||||
BIN
shim-signed/mmaa64.efi
Normal file
BIN
shim-signed/mmaa64.efi
Normal file
Binary file not shown.
BIN
shim-signed/mmia32.efi
Normal file
BIN
shim-signed/mmia32.efi
Normal file
Binary file not shown.
BIN
shim-signed/mmx64.efi
Normal file
BIN
shim-signed/mmx64.efi
Normal file
Binary file not shown.
BIN
shim-signed/shimaa64.efi
Normal file
BIN
shim-signed/shimaa64.efi
Normal file
Binary file not shown.
BIN
shim-signed/shimia32.efi
Normal file
BIN
shim-signed/shimia32.efi
Normal file
Binary file not shown.
BIN
shim-signed/shimx64.efi
Normal file
BIN
shim-signed/shimx64.efi
Normal file
Binary file not shown.
286
shim.md
Normal file
286
shim.md
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
# Secure Boot and *shim*
|
||||||
|
|
||||||
|
Secure Boot accepts only trusted files during boot. The *shim* boot loader is a tool which allows you to select which files to trust. HackBGRT installs *shim* for you, but you need to configure it with these instructions.
|
||||||
|
|
||||||
|
On the first boot after installing HackBGRT, you will see an error message saying "Verification failed". You need to press a key to enter the setup tool (MOKManager) where you can choose to trust HackBGRT. After that, use arrow keys to navigate and *Enter* to continue as described below.
|
||||||
|
|
||||||
|
## 1. Verification failed
|
||||||
|
|
||||||
|
This is the start of *shim* configuration.
|
||||||
|
|
||||||
|
```
|
||||||
|
ERROR
|
||||||
|
Verification failed: (0x1A) Security Violation
|
||||||
|
+----+
|
||||||
|
| OK |
|
||||||
|
+----+
|
||||||
|
```
|
||||||
|
|
||||||
|
Select `OK`, *Enter*.
|
||||||
|
|
||||||
|
```
|
||||||
|
Shim UEFI key management
|
||||||
|
Press any key to perform MOK management
|
||||||
|
Booting in 5 seconds
|
||||||
|
```
|
||||||
|
|
||||||
|
Press a key quickly to enter *MOK management* or the *MOKManager* program.
|
||||||
|
|
||||||
|
## 2. MOK management
|
||||||
|
|
||||||
|
```
|
||||||
|
Perform MOK management
|
||||||
|
|
||||||
|
Continue to boot
|
||||||
|
Enroll key from disk
|
||||||
|
Enroll hash from disk
|
||||||
|
```
|
||||||
|
|
||||||
|
Select `Enroll hash from disk`, *Enter*. This is the safest option where you choose to trust only a specific version of HackBGRT.
|
||||||
|
|
||||||
|
You can also choose to `Enroll key from disk`, which means that you choose to trust anything signed with the same certificate. How do you know if it's safe? You don't – that's why you should rather use the other option or build your own version of HackBGRT with your own certificate.
|
||||||
|
|
||||||
|
## 3a. Enroll hash from disk
|
||||||
|
|
||||||
|
```
|
||||||
|
Select Binary
|
||||||
|
|
||||||
|
The Selected Binary will have its hash Enrolled
|
||||||
|
This means it will subsequently Boot with no prompting
|
||||||
|
Remember to make sure it is a genuine binary before enrolling its hash
|
||||||
|
|
||||||
|
+----------------+
|
||||||
|
| YOUR DISK NAME |
|
||||||
|
+----------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
Select the disk, *Enter*.
|
||||||
|
|
||||||
|
```
|
||||||
|
+---------------+
|
||||||
|
| EFI/ |
|
||||||
|
| loader/ |
|
||||||
|
| vmlinuz-linux |
|
||||||
|
+---------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
Select `EFI/`, *Enter*.
|
||||||
|
|
||||||
|
```
|
||||||
|
+------------+
|
||||||
|
| ../ |
|
||||||
|
| Boot/ |
|
||||||
|
| HackBGRT/ |
|
||||||
|
| Microsoft/ |
|
||||||
|
+------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
Select `HackBGRT/`, *Enter*.
|
||||||
|
|
||||||
|
```
|
||||||
|
+-----------------+
|
||||||
|
| ../ |
|
||||||
|
| grubx64.efi |
|
||||||
|
| loader.efi |
|
||||||
|
| mmx64.efi |
|
||||||
|
| certificate.cer |
|
||||||
|
| splash.bmp |
|
||||||
|
| config.txt |
|
||||||
|
+-----------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
Select `grubx64.efi`, *Enter*.
|
||||||
|
|
||||||
|
```
|
||||||
|
[Enroll MOK]
|
||||||
|
|
||||||
|
+------------+
|
||||||
|
| View key 0 |
|
||||||
|
| Continue |
|
||||||
|
+------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
To verify the key contents, select `View key 0`, *Enter*.
|
||||||
|
|
||||||
|
```
|
||||||
|
SHA256 hash
|
||||||
|
(some hexadecimal values)
|
||||||
|
```
|
||||||
|
|
||||||
|
Press *Enter* to continue.
|
||||||
|
|
||||||
|
```
|
||||||
|
[Enroll MOK]
|
||||||
|
|
||||||
|
+------------+
|
||||||
|
| View key 0 |
|
||||||
|
| Continue |
|
||||||
|
+------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
Select `Continue`, *Enter*.
|
||||||
|
|
||||||
|
```
|
||||||
|
Enroll the key(s)?
|
||||||
|
|
||||||
|
+-----+
|
||||||
|
| No |
|
||||||
|
| Yes |
|
||||||
|
+-----+
|
||||||
|
```
|
||||||
|
|
||||||
|
Select `Yes`, *Enter*.
|
||||||
|
|
||||||
|
```
|
||||||
|
Perform MOK management
|
||||||
|
|
||||||
|
+-----------------------+
|
||||||
|
| Reboot |
|
||||||
|
| Enroll key from disk |
|
||||||
|
| Enroll hash from disk |
|
||||||
|
+-----------------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
Select `Reboot`, *Enter*.
|
||||||
|
|
||||||
|
You are now ready to boot using HackBGRT.
|
||||||
|
|
||||||
|
## 3b. Enroll key from disk
|
||||||
|
|
||||||
|
```
|
||||||
|
Select Key
|
||||||
|
|
||||||
|
The selected key will be enrolled into the MOK database
|
||||||
|
This means any binaries signed with it will be run without prompting
|
||||||
|
Remember to make sure it is a genuine key before Enrolling it
|
||||||
|
|
||||||
|
+----------------+
|
||||||
|
| YOUR DISK NAME |
|
||||||
|
+----------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
Select the disk, *Enter*.
|
||||||
|
|
||||||
|
```
|
||||||
|
+---------------+
|
||||||
|
| EFI/ |
|
||||||
|
| loader/ |
|
||||||
|
| vmlinuz-linux |
|
||||||
|
+---------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
Select `EFI/`, *Enter*.
|
||||||
|
|
||||||
|
```
|
||||||
|
+------------+
|
||||||
|
| ../ |
|
||||||
|
| Boot/ |
|
||||||
|
| HackBGRT/ |
|
||||||
|
| Microsoft/ |
|
||||||
|
+------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
Select `HackBGRT/`, *Enter*.
|
||||||
|
|
||||||
|
```
|
||||||
|
+-----------------+
|
||||||
|
| ../ |
|
||||||
|
| grubx64.efi |
|
||||||
|
| loader.efi |
|
||||||
|
| mmx64.efi |
|
||||||
|
| certificate.cer |
|
||||||
|
| splash.bmp |
|
||||||
|
| config.txt |
|
||||||
|
+-----------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
Select `certificate.cer`, *Enter*.
|
||||||
|
|
||||||
|
```
|
||||||
|
[Enroll MOK]
|
||||||
|
|
||||||
|
+------------+
|
||||||
|
| View key 0 |
|
||||||
|
| Continue |
|
||||||
|
+------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
To verify the key contents, select `View key 0`, *Enter*.
|
||||||
|
|
||||||
|
```
|
||||||
|
[Extended Key Usage]
|
||||||
|
OID: Code Signing
|
||||||
|
|
||||||
|
[Serial Number]
|
||||||
|
6B:24:52:E9:3B:84:41:73:B0:22:92:E8:BE:8E:38:85:
|
||||||
|
|
||||||
|
[Issuer]
|
||||||
|
CN=HackBGRT Secure Boot Signer, O=Metabolix
|
||||||
|
|
||||||
|
[Subject]
|
||||||
|
CN=HackBGRT Secure Boot Signer, O=Metabolix
|
||||||
|
|
||||||
|
[Valid Not Before]
|
||||||
|
Nov 9 13:43:56 2023 GMT
|
||||||
|
|
||||||
|
[Valid Not After]
|
||||||
|
Jan 19 03:14:07 2037 GMT
|
||||||
|
|
||||||
|
[Fingerprint]
|
||||||
|
79 8E 64 40 D1 D1 F4 53 30 8D
|
||||||
|
A0 83 A4 77 FE 57 45 30 36 60
|
||||||
|
```
|
||||||
|
|
||||||
|
Press *Enter* to continue.
|
||||||
|
|
||||||
|
```
|
||||||
|
[Enroll MOK]
|
||||||
|
|
||||||
|
+------------+
|
||||||
|
| View key 0 |
|
||||||
|
| Continue |
|
||||||
|
+------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
Select `Continue`, *Enter*.
|
||||||
|
|
||||||
|
```
|
||||||
|
Enroll the key(s)?
|
||||||
|
|
||||||
|
+-----+
|
||||||
|
| No |
|
||||||
|
| Yes |
|
||||||
|
+-----+
|
||||||
|
```
|
||||||
|
|
||||||
|
Select `Yes`, *Enter*.
|
||||||
|
|
||||||
|
```
|
||||||
|
Perform MOK management
|
||||||
|
|
||||||
|
+-----------------------+
|
||||||
|
| Reboot |
|
||||||
|
| Enroll key from disk |
|
||||||
|
| Enroll hash from disk |
|
||||||
|
+-----------------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
Select `Reboot`, *Enter*.
|
||||||
|
|
||||||
|
You are now ready to boot using HackBGRT.
|
||||||
|
|
||||||
|
## Tutorial: *shim* for dummies
|
||||||
|
|
||||||
|
To install *shim* manually, follow these steps (assuming x64 architecture):
|
||||||
|
|
||||||
|
1. Get *shim*, preferably *shim-signed*.
|
||||||
|
2. Rename your boot loader to `grubx64.efi`.
|
||||||
|
3. Copy `shimx64.efi` where your loader used to be.
|
||||||
|
4. Copy `mmx64.efi` to the same folder.
|
||||||
|
|
||||||
|
The *shim* boot process is as follows:
|
||||||
|
|
||||||
|
1. Your computer starts `your-loader-name.efi`, which is now really *shim*.
|
||||||
|
2. Next, *shim* tries to load `grubx64.efi`.
|
||||||
|
3. If `grubx64.efi` is trusted, the boot process continues.
|
||||||
|
4. Otherwise, *shim* offers to launch *MOKManager* `mmx64.efi`, and you can try again after that.
|
||||||
219
src/Efi.cs
219
src/Efi.cs
@@ -55,6 +55,9 @@ public class Efi {
|
|||||||
* @return String representation of this object.
|
* @return String representation of this object.
|
||||||
*/
|
*/
|
||||||
public override string ToString() {
|
public override string ToString() {
|
||||||
|
if (Data == null) {
|
||||||
|
return $"{Name} Guid={Guid} Attributes={Attributes} Data=null";
|
||||||
|
}
|
||||||
var hex = BitConverter.ToString(Data).Replace("-", " ");
|
var hex = BitConverter.ToString(Data).Replace("-", " ");
|
||||||
var text = new string(Data.Select(c => 0x20 <= c && c <= 0x7f ? (char) c : ' ').ToArray());
|
var text = new string(Data.Select(c => 0x20 <= c && c <= 0x7f ? (char) c : ' ').ToArray());
|
||||||
return $"{Name} Guid={Guid} Attributes={Attributes} Text='{text}' Bytes='{hex}'";
|
return $"{Name} Guid={Guid} Attributes={Attributes} Text='{text}' Bytes='{hex}'";
|
||||||
@@ -62,82 +65,18 @@ public class Efi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Information about an EFI boot entry.
|
* GUID of the global EFI variables.
|
||||||
*/
|
*/
|
||||||
public class BootEntryData {
|
|
||||||
public UInt32 Attributes;
|
|
||||||
public string Label;
|
|
||||||
public class DevicePathNode {
|
|
||||||
public byte Type, SubType;
|
|
||||||
public byte[] Data;
|
|
||||||
public DevicePathNode(byte[] data) {
|
|
||||||
Type = data[0];
|
|
||||||
SubType = data[1];
|
|
||||||
Data = data.Skip(4).ToArray();
|
|
||||||
}
|
|
||||||
public byte[] ToBytes() {
|
|
||||||
var len = Data.Length + 4;
|
|
||||||
return new byte[] { Type, SubType, (byte)(len & 0xff), (byte)(len >> 8) }.Concat(Data).ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public List<DevicePathNode> DevicePathNodes;
|
|
||||||
public byte[] Arguments;
|
|
||||||
|
|
||||||
public BootEntryData(byte[] data) {
|
|
||||||
Attributes = BitConverter.ToUInt32(data, 0);
|
|
||||||
var pathNodesLength = BitConverter.ToUInt16(data, 4);
|
|
||||||
Label = new string(BytesToUInt16s(data).Skip(3).TakeWhile(i => i != 0).Select(i => (char)i).ToArray());
|
|
||||||
var pos = 6 + 2 * (Label.Length + 1);
|
|
||||||
var pathNodesEnd = pos + pathNodesLength;
|
|
||||||
DevicePathNodes = new List<DevicePathNode>();
|
|
||||||
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()));
|
|
||||||
pos += len;
|
|
||||||
}
|
|
||||||
Arguments = data.Skip(pathNodesEnd).ToArray();
|
|
||||||
}
|
|
||||||
public byte[] ToBytes() {
|
|
||||||
return new byte[0]
|
|
||||||
.Concat(BitConverter.GetBytes((UInt32) Attributes))
|
|
||||||
.Concat(BitConverter.GetBytes((UInt16) DevicePathNodes.Sum(n => n.Data.Length + 4)))
|
|
||||||
.Concat(Encoding.Unicode.GetBytes(Label + "\0"))
|
|
||||||
.Concat(DevicePathNodes.SelectMany(n => n.ToBytes()))
|
|
||||||
.Concat(Arguments)
|
|
||||||
.ToArray();
|
|
||||||
}
|
|
||||||
public DevicePathNode FileNameNode {
|
|
||||||
get {
|
|
||||||
var d = DevicePathNodes;
|
|
||||||
return d.Count > 1 && d[d.Count - 1].Type == 0x7F && d[d.Count - 2].Type == 0x04 ? d[d.Count - 2] : null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public bool HasFileName {
|
|
||||||
get {
|
|
||||||
return FileNameNode != null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public string FileName {
|
|
||||||
get {
|
|
||||||
if (!HasFileName) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return new string(Encoding.Unicode.GetChars(FileNameNode.Data).TakeWhile(c => c != '\0').ToArray());
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
if (!HasFileName) {
|
|
||||||
throw new Exception("Logic error: Setting FileName on a bad boot entry.");
|
|
||||||
}
|
|
||||||
FileNameNode.Data = Encoding.Unicode.GetBytes(value + "\0");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public const string EFI_GLOBAL_GUID = "{8be4df61-93ca-11d2-aa0d-00e098032b8c}";
|
public const string EFI_GLOBAL_GUID = "{8be4df61-93ca-11d2-aa0d-00e098032b8c}";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GUID for HackBGRT EFI variables.
|
||||||
|
*/
|
||||||
|
public const string EFI_HACKBGRT_GUID = "{03c64761-075f-4dba-abfb-2ed89e18b236}";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directory containing EFI variables in Linux.
|
||||||
|
*/
|
||||||
public const string LinuxEfiDir = "/sys/firmware/efi/efivars";
|
public const string LinuxEfiDir = "/sys/firmware/efi/efivars";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -185,7 +124,7 @@ public class Efi {
|
|||||||
* @param guid GUID of the EFI variable.
|
* @param guid GUID of the EFI variable.
|
||||||
* @return Information about the EFI variable.
|
* @return Information about the EFI variable.
|
||||||
*/
|
*/
|
||||||
private static Variable GetVariable(string name, string guid = EFI_GLOBAL_GUID) {
|
public static Variable GetVariable(string name, string guid = EFI_GLOBAL_GUID) {
|
||||||
Variable result = new Variable();
|
Variable result = new Variable();
|
||||||
result.Name = name;
|
result.Name = name;
|
||||||
result.Guid = guid;
|
result.Guid = guid;
|
||||||
@@ -234,7 +173,7 @@ public class Efi {
|
|||||||
* @param v Information of the variable.
|
* @param v Information of the variable.
|
||||||
* @param dryRun Don't actually set the variable.
|
* @param dryRun Don't actually set the variable.
|
||||||
*/
|
*/
|
||||||
private static void SetVariable(Variable v, bool dryRun = false) {
|
public static void SetVariable(Variable v, bool dryRun = false) {
|
||||||
Setup.WriteLine($"Writing EFI variable {v.Name} (see log for details)");
|
Setup.WriteLine($"Writing EFI variable {v.Name} (see log for details)");
|
||||||
Setup.Log($"Writing EFI variable: {v}");
|
Setup.Log($"Writing EFI variable: {v}");
|
||||||
if (dryRun) {
|
if (dryRun) {
|
||||||
@@ -321,127 +260,17 @@ public class Efi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disable the said boot entry from BootOrder.
|
* Retrieve HackBGRT log collected during boot.
|
||||||
*
|
|
||||||
* @param label Label of the boot entry.
|
|
||||||
* @param fileName File name of the boot entry.
|
|
||||||
* @param dryRun Don't actually disable the entry.
|
|
||||||
* @return True, if the entry was found in BootOrder.
|
|
||||||
*/
|
*/
|
||||||
public static bool DisableBootEntry(string label, string fileName, bool dryRun = false) {
|
public static string GetHackBGRTLog() {
|
||||||
Variable bootOrder;
|
|
||||||
try {
|
try {
|
||||||
bootOrder = GetVariable("BootOrder");
|
var log = GetVariable("HackBGRTLog", EFI_HACKBGRT_GUID);
|
||||||
} catch {
|
if (log.Data == null) {
|
||||||
return false;
|
return "Log is empty.";
|
||||||
}
|
|
||||||
if (bootOrder.Data == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Setup.Log($"Read EFI variable: {bootOrder}");
|
|
||||||
var found = false;
|
|
||||||
var bootOrderInts = new List<UInt16>();
|
|
||||||
foreach (var num in BytesToUInt16s(bootOrder.Data)) {
|
|
||||||
var entry = GetVariable(String.Format("Boot{0:X04}", num));
|
|
||||||
if (entry.Data != null) {
|
|
||||||
var entryData = new BootEntryData(entry.Data);
|
|
||||||
if (entryData.Label == label && entryData.FileName == fileName) {
|
|
||||||
found = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
bootOrderInts.Add(num);
|
return new string(BytesToUInt16s(log.Data).Select(i => (char)i).ToArray());
|
||||||
}
|
} catch (Exception e) {
|
||||||
if (found) {
|
return $"Log not found: {e.ToString()}";
|
||||||
bootOrder.Data = bootOrderInts.SelectMany(num => new byte[] { (byte)(num & 0xff), (byte)(num >> 8) }).ToArray();
|
|
||||||
SetVariable(bootOrder, dryRun);
|
|
||||||
}
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create and enable the said boot entry from BootOrder.
|
|
||||||
*
|
|
||||||
* @param label Label of the boot entry.
|
|
||||||
* @param fileName File name of the boot entry.
|
|
||||||
* @param dryRun Don't actually create the entry.
|
|
||||||
*/
|
|
||||||
public static void MakeAndEnableBootEntry(string label, string fileName, bool dryRun = false) {
|
|
||||||
Variable msEntry = null, ownEntry = null;
|
|
||||||
UInt16 msNum = 0, ownNum = 0;
|
|
||||||
|
|
||||||
// Find a free entry and the MS bootloader entry.
|
|
||||||
Variable bootOrder = null;
|
|
||||||
try {
|
|
||||||
bootOrder = GetVariable("BootOrder");
|
|
||||||
} catch {
|
|
||||||
if (dryRun) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (bootOrder == null || bootOrder.Data == null) {
|
|
||||||
throw new Exception("MakeBootEntry: Could not read BootOrder. Maybe your computer is defective.");
|
|
||||||
}
|
|
||||||
var bootCurrent = GetVariable("BootCurrent");
|
|
||||||
if (bootCurrent.Data == null) {
|
|
||||||
throw new Exception("MakeBootEntry: Could not read BootCurrent. Maybe your computer is defective.");
|
|
||||||
}
|
|
||||||
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))) {
|
|
||||||
var entry = GetVariable(String.Format("Boot{0:X04}", num));
|
|
||||||
if (entry.Data == null) {
|
|
||||||
if (ownEntry == null) {
|
|
||||||
ownNum = num;
|
|
||||||
ownEntry = entry;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var entryData = new BootEntryData(entry.Data);
|
|
||||||
if (!entryData.HasFileName) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (entryData.Label == label && entryData.FileName == fileName) {
|
|
||||||
ownNum = num;
|
|
||||||
ownEntry = entry;
|
|
||||||
}
|
|
||||||
if (msEntry == null && entryData.FileName.StartsWith("\\EFI\\Microsoft\\Boot\\bootmgfw.efi", StringComparison.OrdinalIgnoreCase)) {
|
|
||||||
msNum = num;
|
|
||||||
msEntry = entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ownEntry != null && msEntry != null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ownEntry == null) {
|
|
||||||
throw new Exception("MakeBootEntry: Boot entry list is full.");
|
|
||||||
} else if (msEntry == null) {
|
|
||||||
throw new Exception("MakeBootEntry: Windows Boot Manager not found.");
|
|
||||||
} else {
|
|
||||||
Setup.Log($"Read EFI variable: {msEntry}");
|
|
||||||
// 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");
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
var msPos = bootOrderInts.IndexOf(msNum);
|
|
||||||
var ownPos = bootOrderInts.IndexOf(ownNum);
|
|
||||||
var mustAdd = ownPos == -1;
|
|
||||||
var mustMove = 0 <= msPos && msPos <= ownPos;
|
|
||||||
if (mustAdd || mustMove) {
|
|
||||||
if (mustMove) {
|
|
||||||
bootOrderInts.RemoveAt(ownPos);
|
|
||||||
}
|
|
||||||
bootOrderInts.Insert(msPos < 0 ? 0 : msPos, ownNum);
|
|
||||||
bootOrder.Data = bootOrderInts.SelectMany(num => new byte[] { (byte)(num & 0xff), (byte)(num >> 8) }).ToArray();
|
|
||||||
SetVariable(bootOrder, dryRun);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -456,14 +285,14 @@ public class Efi {
|
|||||||
var ret = GetSystemFirmwareTable(acpiBE, bgrtLE, buf, size);
|
var ret = GetSystemFirmwareTable(acpiBE, bgrtLE, buf, size);
|
||||||
if (ret == size) {
|
if (ret == size) {
|
||||||
var hex = BitConverter.ToString(buf).Replace("-", " ");
|
var hex = BitConverter.ToString(buf).Replace("-", " ");
|
||||||
Setup.Log($"LogBGRT: {hex}");
|
Setup.Log($"LogBGRT: {size} bytes: {hex}");
|
||||||
} else if (ret == 0) {
|
} else if (ret == 0) {
|
||||||
Setup.Log($"LogBGRT: Win32Error {Marshal.GetLastWin32Error()}");
|
Setup.Log($"LogBGRT: Win32Error {Marshal.GetLastWin32Error()}");
|
||||||
} else {
|
} else {
|
||||||
Setup.Log($"LogBGRT: Size problems: spec {0x38}, buf {size}, ret {ret}");
|
Setup.Log($"LogBGRT: Size problems: spec {0x38}, buf {size}, ret {ret}");
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Setup.Log($"LogBGRT: {e}");
|
Setup.Log($"LogBGRT failed: {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
309
src/EfiBootEntries.cs
Normal file
309
src/EfiBootEntries.cs
Normal file
@@ -0,0 +1,309 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class for handling EFI boot entries. Lazy access with cache.
|
||||||
|
* Notice: Data is not updated after the first read.
|
||||||
|
*/
|
||||||
|
public class EfiBootEntries {
|
||||||
|
/**
|
||||||
|
* Information about an EFI boot entry.
|
||||||
|
*/
|
||||||
|
public class BootEntryData {
|
||||||
|
public UInt32 Attributes;
|
||||||
|
public string Label;
|
||||||
|
public class DevicePathNode {
|
||||||
|
public byte Type, SubType;
|
||||||
|
public byte[] Data;
|
||||||
|
public DevicePathNode(byte[] data) {
|
||||||
|
Type = data[0];
|
||||||
|
SubType = data[1];
|
||||||
|
Data = data.Skip(4).ToArray();
|
||||||
|
}
|
||||||
|
public byte[] ToBytes() {
|
||||||
|
var len = Data.Length + 4;
|
||||||
|
return new byte[] { Type, SubType, (byte)(len & 0xff), (byte)(len >> 8) }.Concat(Data).ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public List<DevicePathNode> DevicePathNodes;
|
||||||
|
public byte[] Arguments;
|
||||||
|
|
||||||
|
public BootEntryData(byte[] data) {
|
||||||
|
Attributes = BitConverter.ToUInt32(data, 0);
|
||||||
|
var pathNodesLength = BitConverter.ToUInt16(data, 4);
|
||||||
|
Label = new string(Efi.BytesToUInt16s(data).Skip(3).TakeWhile(i => i != 0).Select(i => (char)i).ToArray());
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
public byte[] ToBytes() {
|
||||||
|
return new byte[0]
|
||||||
|
.Concat(BitConverter.GetBytes((UInt32) Attributes))
|
||||||
|
.Concat(BitConverter.GetBytes((UInt16) DevicePathNodes.Sum(n => n.Data.Length + 4)))
|
||||||
|
.Concat(Encoding.Unicode.GetBytes(Label + "\0"))
|
||||||
|
.Concat(DevicePathNodes.SelectMany(n => n.ToBytes()))
|
||||||
|
.Concat(Arguments)
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
public DevicePathNode FileNameNode {
|
||||||
|
get {
|
||||||
|
var d = DevicePathNodes;
|
||||||
|
return d.Count > 1 && d[d.Count - 1].Type == 0x7F && d[d.Count - 2].Type == 0x04 ? d[d.Count - 2] : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public bool HasFileName {
|
||||||
|
get {
|
||||||
|
return FileNameNode != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public string FileName {
|
||||||
|
get {
|
||||||
|
if (!HasFileName) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return new string(Encoding.Unicode.GetChars(FileNameNode.Data).TakeWhile(c => c != '\0').ToArray());
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if (!HasFileName) {
|
||||||
|
throw new Exception("Logic error: Setting FileName on a bad boot entry.");
|
||||||
|
}
|
||||||
|
FileNameNode.Data = Encoding.Unicode.GetBytes(value + "\0");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path to the Windows boot loader.
|
||||||
|
*/
|
||||||
|
public const string WindowsLoaderPath = "\\EFI\\Microsoft\\Boot\\bootmgfw.efi";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path to the HackBGRT loader.
|
||||||
|
*/
|
||||||
|
public const string OwnLoaderPath = "\\EFI\\HackBGRT\\loader.efi";
|
||||||
|
|
||||||
|
private readonly Dictionary<UInt16, (Efi.Variable, BootEntryData)> cache;
|
||||||
|
private readonly Efi.Variable BootOrder;
|
||||||
|
private readonly Efi.Variable BootCurrent;
|
||||||
|
private readonly List<UInt16> BootOrderInts;
|
||||||
|
private readonly List<UInt16> BootCurrentInts;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor. Reads BootOrder and BootCurrent.
|
||||||
|
*/
|
||||||
|
public EfiBootEntries() {
|
||||||
|
cache = new Dictionary<UInt16, (Efi.Variable, BootEntryData)>();
|
||||||
|
BootOrder = Efi.GetVariable("BootOrder");
|
||||||
|
BootCurrent = Efi.GetVariable("BootCurrent");
|
||||||
|
if (BootOrder.Data == null) {
|
||||||
|
throw new Exception("Could not read BootOrder.");
|
||||||
|
}
|
||||||
|
BootCurrentInts = new List<UInt16>(Efi.BytesToUInt16s(BootCurrent.Data ?? new byte[0]));
|
||||||
|
BootOrderInts = new List<UInt16>(Efi.BytesToUInt16s(BootOrder.Data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the boot entry with the given number.
|
||||||
|
*
|
||||||
|
* @param num Number of the boot entry.
|
||||||
|
* @return The boot entry.
|
||||||
|
*/
|
||||||
|
public (Efi.Variable, BootEntryData) GetEntry(UInt16 num) {
|
||||||
|
if (!cache.ContainsKey(num)) {
|
||||||
|
var v = Efi.GetVariable(String.Format("Boot{0:X04}", num));
|
||||||
|
cache[num] = (v, v.Data == null ? null : new BootEntryData(v.Data));
|
||||||
|
}
|
||||||
|
return cache[num];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find entry by file name.
|
||||||
|
*
|
||||||
|
* @param fileName File name of the boot entry.
|
||||||
|
* @return The boot entry.
|
||||||
|
*/
|
||||||
|
public (UInt16, Efi.Variable, BootEntryData) FindEntry(string fileName) {
|
||||||
|
var rest = Enumerable.Range(0, 0xff).Select(i => (UInt16) i);
|
||||||
|
var entryAccessOrder = BootCurrentInts.Concat(BootOrderInts).Concat(rest);
|
||||||
|
foreach (var num in entryAccessOrder) {
|
||||||
|
var (v, e) = GetEntry(num);
|
||||||
|
if (fileName == null ? e == null : (e != null && e.FileName.Equals(fileName, StringComparison.OrdinalIgnoreCase))) {
|
||||||
|
return (num, v, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (0xffff, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Windows boot entry.
|
||||||
|
*/
|
||||||
|
public (UInt16, Efi.Variable, BootEntryData) WindowsEntry {
|
||||||
|
get { return FindEntry(WindowsLoaderPath); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the HackBGRT boot entry.
|
||||||
|
*/
|
||||||
|
public (UInt16, Efi.Variable, BootEntryData) OwnEntry {
|
||||||
|
get { return FindEntry(OwnLoaderPath); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a free boot entry.
|
||||||
|
*/
|
||||||
|
public (UInt16, Efi.Variable, BootEntryData) FreeEntry {
|
||||||
|
get { return FindEntry(null); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable the said boot entry from BootOrder.
|
||||||
|
*
|
||||||
|
* @param dryRun Don't actually write to NVRAM.
|
||||||
|
* @return True, if the entry was found in BootOrder.
|
||||||
|
*/
|
||||||
|
public bool DisableOwnEntry(bool dryRun = false) {
|
||||||
|
var (ownNum, ownVar, _) = OwnEntry;
|
||||||
|
if (ownVar == null) {
|
||||||
|
Setup.Log("Own entry not found.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Setup.Log($"Old boot order: {BootOrder}");
|
||||||
|
if (!BootOrderInts.Contains(ownNum)) {
|
||||||
|
Setup.Log("Own entry not in BootOrder.");
|
||||||
|
} else {
|
||||||
|
Setup.Log($"Disabling own entry: {ownNum:X04}");
|
||||||
|
BootOrderInts.Remove(ownNum);
|
||||||
|
BootOrder.Data = BootOrderInts.SelectMany(num => new byte[] { (byte)(num & 0xff), (byte)(num >> 8) }).ToArray();
|
||||||
|
Efi.SetVariable(BootOrder, dryRun);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the boot entry.
|
||||||
|
*
|
||||||
|
* @param alwaysCopyFromMS If true, do not preserve any existing data.
|
||||||
|
* @param dryRun Don't actually write to NVRAM.
|
||||||
|
*/
|
||||||
|
public void MakeOwnEntry(bool alwaysCopyFromMS, bool dryRun = false) {
|
||||||
|
var (msNum, msVar, msEntry) = WindowsEntry;
|
||||||
|
if (msEntry == null) {
|
||||||
|
throw new Exception("MakeOwnEntry: Windows Boot Manager not found.");
|
||||||
|
}
|
||||||
|
var (ownNum, ownVar, ownEntry) = OwnEntry;
|
||||||
|
if (ownVar == null) {
|
||||||
|
(ownNum, ownVar, ownEntry) = FreeEntry;
|
||||||
|
if (ownVar == null) {
|
||||||
|
throw new Exception("MakeOwnEntry: No free entry.");
|
||||||
|
}
|
||||||
|
Setup.Log($"Creating own entry {ownNum:X4}.");
|
||||||
|
} else {
|
||||||
|
Setup.Log($"Updating own entry {ownNum:X4}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Setup.Log($"Read EFI variable: {msVar}");
|
||||||
|
Setup.Log($"Read EFI variable: {ownVar}");
|
||||||
|
// Make a new boot entry using the MS entry as a starting point.
|
||||||
|
if (!alwaysCopyFromMS && ownEntry != null) {
|
||||||
|
// 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(ownEntry.Arguments.Take(12).Select(c => (char) c).ToArray());
|
||||||
|
if (str == "WINDOWS\0\x01\0\0\0") {
|
||||||
|
ownEntry.Arguments[8] = (byte) 'X';
|
||||||
|
} else if (str != "WINDOWS\0\x58\0\0\0") {
|
||||||
|
// Not recognized. Clear the arguments.
|
||||||
|
ownEntry.Arguments = new byte[0];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ownEntry = msEntry;
|
||||||
|
ownEntry.Arguments = new byte[0];
|
||||||
|
ownEntry.Label = "HackBGRT";
|
||||||
|
ownEntry.FileName = OwnLoaderPath;
|
||||||
|
}
|
||||||
|
ownEntry.Attributes = 1; // LOAD_OPTION_ACTIVE
|
||||||
|
ownVar.Attributes = 7; // EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS
|
||||||
|
ownVar.Data = ownEntry.ToBytes();
|
||||||
|
Efi.SetVariable(ownVar, dryRun);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable the own boot entry.
|
||||||
|
*
|
||||||
|
* @param dryRun Don't actually write to NVRAM.
|
||||||
|
*/
|
||||||
|
public void EnableOwnEntry(bool dryRun = false) {
|
||||||
|
var (ownNum, ownVar, _) = OwnEntry;
|
||||||
|
if (ownVar == null) {
|
||||||
|
Setup.Log("Own entry not found.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var (msNum, _, _) = WindowsEntry;
|
||||||
|
var msPos = BootOrderInts.IndexOf(msNum);
|
||||||
|
var ownPos = BootOrderInts.IndexOf(ownNum);
|
||||||
|
var mustAdd = ownPos == -1;
|
||||||
|
var mustMove = 0 <= msPos && msPos <= ownPos;
|
||||||
|
Setup.Log($"Old boot order: {BootOrder}");
|
||||||
|
if (mustAdd || mustMove) {
|
||||||
|
Setup.Log($"Enabling own entry: {ownNum:X04}");
|
||||||
|
if (mustMove) {
|
||||||
|
BootOrderInts.RemoveAt(ownPos);
|
||||||
|
}
|
||||||
|
BootOrderInts.Insert(msPos < 0 ? 0 : msPos, ownNum);
|
||||||
|
BootOrder.Data = BootOrderInts.SelectMany(num => new byte[] { (byte)(num & 0xff), (byte)(num >> 8) }).ToArray();
|
||||||
|
Efi.SetVariable(BootOrder, dryRun);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log the boot entries.
|
||||||
|
*/
|
||||||
|
public void LogEntries() {
|
||||||
|
Setup.Log($"LogEntries: {BootOrder}");
|
||||||
|
Setup.Log($"LogEntries: {BootCurrent}");
|
||||||
|
// Windows can't enumerate EFI variables, and trying them all is too slow.
|
||||||
|
// BootOrder + BootCurrent + the first 0xff entries should be enough.
|
||||||
|
var seen = new HashSet<UInt16>();
|
||||||
|
foreach (var num in BootOrderInts.Concat(BootCurrentInts).Concat(Enumerable.Range(0, 0xff).Select(i => (UInt16) i))) {
|
||||||
|
if (seen.Contains(num)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
seen.Add(num);
|
||||||
|
var (v, e) = GetEntry(num);
|
||||||
|
if (e != null) {
|
||||||
|
Setup.Log($"LogEntries: {v}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to log the boot entries.
|
||||||
|
*/
|
||||||
|
public static void TryLogEntries() {
|
||||||
|
try {
|
||||||
|
new EfiBootEntries().LogEntries();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Setup.Log($"LogEntries failed: {e.ToString()}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
374
src/Setup.cs
374
src/Setup.cs
@@ -10,9 +10,15 @@ using System.Diagnostics;
|
|||||||
using System.Security.Principal;
|
using System.Security.Principal;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Management;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
|
||||||
[assembly: AssemblyInformationalVersionAttribute(GIT_DESCRIBE.data)]
|
#if GIT_DESCRIBE
|
||||||
[assembly: AssemblyProductAttribute("HackBGRT")]
|
[assembly: AssemblyVersion(GIT_DESCRIBE.numeric)]
|
||||||
|
#endif
|
||||||
|
[assembly: AssemblyProduct("HackBGRT")]
|
||||||
|
[assembly: AssemblyTitle("HackBGRT Installer")]
|
||||||
|
[assembly: AssemblyDescription("HackBGRT boot logo changer for UEFI")]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HackBGRT Setup.
|
* HackBGRT Setup.
|
||||||
@@ -51,22 +57,31 @@ public class Setup {
|
|||||||
public enum BootLoaderType {
|
public enum BootLoaderType {
|
||||||
None,
|
None,
|
||||||
Own,
|
Own,
|
||||||
|
Shim,
|
||||||
Microsoft,
|
Microsoft,
|
||||||
Other,
|
Other,
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var The privileged actions. */
|
/** @var The privileged actions. */
|
||||||
protected static readonly string[] privilegedActions = new string[] {
|
protected static readonly string[] PrivilegedActions = new string[] {
|
||||||
"install",
|
"install",
|
||||||
"allow-secure-boot",
|
|
||||||
"allow-bitlocker",
|
|
||||||
"allow-bad-loader",
|
|
||||||
"enable-entry", "disable-entry",
|
"enable-entry", "disable-entry",
|
||||||
"enable-bcdedit", "disable-bcdedit",
|
"enable-bcdedit", "disable-bcdedit",
|
||||||
"enable-overwrite", "disable-overwrite",
|
"enable-overwrite", "disable-overwrite",
|
||||||
"disable",
|
"disable",
|
||||||
"uninstall",
|
"uninstall",
|
||||||
"boot-to-fw",
|
"boot-to-fw",
|
||||||
|
"show-boot-log",
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @var Forwardable arguments. */
|
||||||
|
protected static readonly string[] ForwardableArguments = new string[] {
|
||||||
|
"dry-run",
|
||||||
|
"batch",
|
||||||
|
"skip-shim",
|
||||||
|
"allow-secure-boot",
|
||||||
|
"allow-bitlocker",
|
||||||
|
"allow-bad-loader",
|
||||||
};
|
};
|
||||||
|
|
||||||
/** @var The target directory. */
|
/** @var The target directory. */
|
||||||
@@ -94,6 +109,21 @@ public class Setup {
|
|||||||
/** @var Run in batch mode? */
|
/** @var Run in batch mode? */
|
||||||
protected bool Batch;
|
protected bool Batch;
|
||||||
|
|
||||||
|
/** @var Skip the shim? */
|
||||||
|
protected bool SkipShim;
|
||||||
|
|
||||||
|
/** @var Allow Secure Boot to be enabled? */
|
||||||
|
protected bool AllowSecureBoot;
|
||||||
|
|
||||||
|
/** @var Allow BitLocker to be enabled? */
|
||||||
|
protected bool AllowBitLocker;
|
||||||
|
|
||||||
|
/** @var Allow bad loader in config file? */
|
||||||
|
protected bool AllowBadLoader;
|
||||||
|
|
||||||
|
/** @var Is the loader signed? */
|
||||||
|
protected bool LoaderIsSigned = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Output a line.
|
* Output a line.
|
||||||
*/
|
*/
|
||||||
@@ -136,10 +166,9 @@ public class Setup {
|
|||||||
*
|
*
|
||||||
* @param app Path to the application.
|
* @param app Path to the application.
|
||||||
* @param args The argument string.
|
* @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 and exit code.
|
||||||
* @return The output, or null if the execution failed.
|
|
||||||
*/
|
*/
|
||||||
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}");
|
Log($"Execute: {app} {args}");
|
||||||
try {
|
try {
|
||||||
var info = new ProcessStartInfo(app, args);
|
var info = new ProcessStartInfo(app, args);
|
||||||
@@ -149,13 +178,26 @@ public class Setup {
|
|||||||
string output = p.StandardOutput.ReadToEnd();
|
string output = p.StandardOutput.ReadToEnd();
|
||||||
p.WaitForExit();
|
p.WaitForExit();
|
||||||
Log($"Exit code: {p.ExitCode}, output:\n{output}\n\n");
|
Log($"Exit code: {p.ExitCode}, output:\n{output}\n\n");
|
||||||
return (nullOnFail && p.ExitCode != 0) ? null : output;
|
return (output, p.ExitCode);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log($"Execute failed: {e.ToString()}");
|
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).
|
* Check for required privileges (access to ESP and EFI vars).
|
||||||
*
|
*
|
||||||
@@ -204,6 +246,8 @@ public class Setup {
|
|||||||
string tmp = System.Text.Encoding.ASCII.GetString(data);
|
string tmp = System.Text.Encoding.ASCII.GetString(data);
|
||||||
if (tmp.IndexOf("HackBGRT") >= 0 || tmp.IndexOf("HackBgrt") >= 0) {
|
if (tmp.IndexOf("HackBGRT") >= 0 || tmp.IndexOf("HackBgrt") >= 0) {
|
||||||
return BootLoaderType.Own;
|
return BootLoaderType.Own;
|
||||||
|
} else if (tmp.IndexOf("UEFI shim") >= 0) {
|
||||||
|
return BootLoaderType.Shim;
|
||||||
} else if (tmp.IndexOf("Microsoft Corporation") >= 0) {
|
} else if (tmp.IndexOf("Microsoft Corporation") >= 0) {
|
||||||
return BootLoaderType.Microsoft;
|
return BootLoaderType.Microsoft;
|
||||||
} else {
|
} else {
|
||||||
@@ -229,6 +273,8 @@ public class Setup {
|
|||||||
0x0200 => "ia64",
|
0x0200 => "ia64",
|
||||||
0x8664 => "x64",
|
0x8664 => "x64",
|
||||||
0xaa64 => "aa64",
|
0xaa64 => "aa64",
|
||||||
|
0x01c2 => "arm",
|
||||||
|
0x01c4 => "arm",
|
||||||
_ => $"unknown-{peArch:x4}"
|
_ => $"unknown-{peArch:x4}"
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
@@ -297,20 +343,40 @@ public class Setup {
|
|||||||
g.DrawImageUnscaledAndClipped(img, new Rectangle(Point.Empty, img.Size));
|
g.DrawImageUnscaledAndClipped(img, new Rectangle(Point.Empty, img.Size));
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
bmp.Save(newName, ImageFormat.Bmp);
|
var ms = new MemoryStream();
|
||||||
} catch {
|
bmp.Save(ms, ImageFormat.Bmp);
|
||||||
|
var bytes = ms.ToArray();
|
||||||
|
File.WriteAllBytes(newName, bytes);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log($"InstallImageFile failed: {e.ToString()}");
|
||||||
throw new SetupException($"Failed to install image {name} to {newName}.");
|
throw new SetupException($"Failed to install image {name} to {newName}.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WriteLine($"Installed image {name} to {newName}.");
|
WriteLine($"Installed image {name} to {newName}, size {img.Width}x{img.Height}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Install files to ESP.
|
* Install files to ESP.
|
||||||
*/
|
*/
|
||||||
protected void InstallFiles() {
|
protected void InstallFiles() {
|
||||||
if (!File.Exists($"boot{EfiArch}.efi")) {
|
var loaderSource = Path.Combine("efi-signed", $"boot{EfiArch}.efi");
|
||||||
throw new SetupException($"Missing boot{EfiArch}.efi, {EfiArch} is not supported!");
|
LoaderIsSigned = true;
|
||||||
|
if (!File.Exists(loaderSource)) {
|
||||||
|
loaderSource = Path.Combine("efi", $"boot{EfiArch}.efi");
|
||||||
|
LoaderIsSigned = false;
|
||||||
|
if (!File.Exists(loaderSource)) {
|
||||||
|
throw new SetupException($"Missing boot{EfiArch}.efi, {EfiArch} is not supported!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var shimSource = Path.Combine("shim-signed", $"shim{EfiArch}.efi");
|
||||||
|
var mmSource = Path.Combine("shim-signed", $"mm{EfiArch}.efi");
|
||||||
|
if (!SkipShim) {
|
||||||
|
if (!File.Exists(shimSource)) {
|
||||||
|
throw new SetupException($"Missing shim ({shimSource}), can't install shim for {EfiArch}!");
|
||||||
|
}
|
||||||
|
if (!File.Exists(mmSource)) {
|
||||||
|
throw new SetupException($"Missing MokManager ({mmSource}), can't install shim for {EfiArch}!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (!Directory.Exists(InstallPath)) {
|
if (!Directory.Exists(InstallPath)) {
|
||||||
@@ -324,30 +390,91 @@ public class Setup {
|
|||||||
var lines = File.ReadAllLines("config.txt");
|
var lines = File.ReadAllLines("config.txt");
|
||||||
Log($"config.txt:\n{String.Join("\n", lines)}");
|
Log($"config.txt:\n{String.Join("\n", lines)}");
|
||||||
foreach (var line in lines.Where(s => s.StartsWith("image="))) {
|
foreach (var line in lines.Where(s => s.StartsWith("image="))) {
|
||||||
var delim = "path=\\EFI\\HackBGRT\\";
|
var delim = "path=";
|
||||||
var i = line.IndexOf(delim);
|
var i = line.IndexOf(delim);
|
||||||
if (i > 0) {
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InstallFile($"boot{EfiArch}.efi", "loader.efi");
|
var loaderDest = "loader.efi";
|
||||||
|
if (!SkipShim) {
|
||||||
|
InstallFile(shimSource, loaderDest);
|
||||||
|
InstallFile(mmSource, $"mm{EfiArch}.efi");
|
||||||
|
InstallFile(loaderSource, "\u4957\u444e\u574f\u0053\u0058"); // bytes "WINDOWS\0X\0" as UTF-16
|
||||||
|
loaderDest = $"grub{EfiArch}.efi";
|
||||||
|
}
|
||||||
|
InstallFile(loaderSource, loaderDest);
|
||||||
|
if (LoaderIsSigned) {
|
||||||
|
InstallFile("certificate.cer");
|
||||||
|
}
|
||||||
WriteLine($"HackBGRT has been copied to {InstallPath}.");
|
WriteLine($"HackBGRT has been copied to {InstallPath}.");
|
||||||
|
|
||||||
|
var enrollHashPath = $"EFI\\HackBGRT\\{loaderDest}";
|
||||||
|
var enrollKeyPath = "EFI\\HackBGRT\\certificate.cer";
|
||||||
|
if (SkipShim) {
|
||||||
|
if (LoaderIsSigned) {
|
||||||
|
WriteLine($"Remember to enroll {enrollKeyPath} in your firmware!");
|
||||||
|
} else {
|
||||||
|
WriteLine("This HackBGRT build is not signed. You may need to disable Secure Boot.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
WriteLine($"On first boot, select 'Enroll hash from disk' and enroll {enrollHashPath}.");
|
||||||
|
if (LoaderIsSigned) {
|
||||||
|
WriteLine($"Alternatively, select 'Enroll key from disk' and enroll {enrollKeyPath}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable HackBGRT with bcdedit.
|
* Enable HackBGRT with bcdedit.
|
||||||
*/
|
*/
|
||||||
protected void EnableBCDEdit() {
|
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 {
|
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 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} device partition={Esp.Location}", true);
|
||||||
Execute("bcdedit", $"/set {guid} path \\EFI\\HackBGRT\\loader.efi", true);
|
Execute("bcdedit", $"/set {guid} path {EfiBootEntries.OwnLoaderPath}", true);
|
||||||
foreach (var arg in new string[] { "locale", "inherit", "default", "resumeobject", "displayorder", "toolsdisplayorder", "timeout" }) {
|
foreach (var arg in new string[] { "locale", "inherit", "default", "resumeobject", "displayorder", "toolsdisplayorder", "timeout" }) {
|
||||||
Execute("bcdedit", $"/deletevalue {guid} {arg}", true);
|
Execute("bcdedit", $"/deletevalue {guid} {arg}", true);
|
||||||
}
|
}
|
||||||
var fwbootmgr = "{fwbootmgr}";
|
var fwbootmgr = "{fwbootmgr}";
|
||||||
Execute("bcdedit", $"/set {fwbootmgr} displayorder {guid} /addfirst", true);
|
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);
|
||||||
|
var e = new EfiBootEntries();
|
||||||
|
e.MakeOwnEntry(false, DryRun); // Fix load options for shim.
|
||||||
|
e.EnableOwnEntry(DryRun);
|
||||||
|
Execute("bcdedit", $"/enum {guid}", true);
|
||||||
|
e.LogEntries();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log($"EnableBCDEdit failed: {e.ToString()}");
|
Log($"EnableBCDEdit failed: {e.ToString()}");
|
||||||
throw new SetupException("Failed to enable HackBGRT with BCDEdit!");
|
throw new SetupException("Failed to enable HackBGRT with BCDEdit!");
|
||||||
@@ -372,14 +499,18 @@ public class Setup {
|
|||||||
} else if (entry.IndexOf("HackBGRT") >= 0) {
|
} else if (entry.IndexOf("HackBGRT") >= 0) {
|
||||||
found = true;
|
found = true;
|
||||||
Log($"Disabling HackBGRT entry {guid}.");
|
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}.");
|
Log($"DisableBCDEdit failed to delete {guid}.");
|
||||||
|
} else {
|
||||||
|
disabled = true;
|
||||||
}
|
}
|
||||||
disabled = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (found && !disabled) {
|
if (found) {
|
||||||
throw new SetupException("Failed to disable HackBGRT with BCDEdit!");
|
if (!disabled) {
|
||||||
|
throw new SetupException("Failed to disable HackBGRT with BCDEdit!");
|
||||||
|
}
|
||||||
|
WriteLine("Disabled NVRAM entry for HackBGRT with BCDEdit.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,25 +518,40 @@ public class Setup {
|
|||||||
* Enable HackBGRT boot entry.
|
* Enable HackBGRT boot entry.
|
||||||
*/
|
*/
|
||||||
protected void EnableEntry() {
|
protected void EnableEntry() {
|
||||||
Efi.MakeAndEnableBootEntry("HackBGRT", "\\EFI\\HackBGRT\\loader.efi", DryRun);
|
var e = new EfiBootEntries();
|
||||||
|
e.MakeOwnEntry(true, DryRun);
|
||||||
|
e.EnableOwnEntry(DryRun);
|
||||||
WriteLine("Enabled NVRAM entry for HackBGRT.");
|
WriteLine("Enabled NVRAM entry for HackBGRT.");
|
||||||
|
// Verify that the entry was created.
|
||||||
|
e.LogEntries();
|
||||||
|
Execute("bcdedit", "/enum firmware", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disable HackBGRT boot entry.
|
* Disable HackBGRT boot entry.
|
||||||
*/
|
*/
|
||||||
protected void DisableEntry() {
|
protected void DisableEntry() {
|
||||||
Efi.DisableBootEntry("HackBGRT", "\\EFI\\HackBGRT\\loader.efi", DryRun);
|
new EfiBootEntries().DisableOwnEntry(DryRun);
|
||||||
WriteLine("Disabled NVRAM entry for HackBGRT.");
|
WriteLine("Disabled NVRAM entry for HackBGRT.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get paths related to MS boot loader.
|
||||||
|
*/
|
||||||
|
protected (string Ms, string MsDir, string MsGrub, string MsMokManager) GetMsLoaderPaths() {
|
||||||
|
var ms = Esp.MsLoaderPath;
|
||||||
|
var msDir = Path.GetDirectoryName(ms);
|
||||||
|
var msGrub = Path.Combine(msDir, $"grub{EfiArch}.efi");
|
||||||
|
var msMm = Path.Combine(msDir, $"mm{EfiArch}.efi");
|
||||||
|
return (ms, msDir, msGrub, msMm);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable HackBGRT by overwriting the MS boot loader.
|
* Enable HackBGRT by overwriting the MS boot loader.
|
||||||
*/
|
*/
|
||||||
protected void OverwriteMsLoader() {
|
protected void OverwriteMsLoader() {
|
||||||
var ms = Esp.MsLoaderPath;
|
var (ms, msDir, msGrub, msMm) = GetMsLoaderPaths();
|
||||||
var backup = BackupLoaderPath;
|
var backup = BackupLoaderPath;
|
||||||
var own = Path.Combine(InstallPath, "loader.efi");
|
|
||||||
|
|
||||||
if (DetectLoader(ms) == BootLoaderType.Microsoft) {
|
if (DetectLoader(ms) == BootLoaderType.Microsoft) {
|
||||||
InstallFile(ms, backup, false);
|
InstallFile(ms, backup, false);
|
||||||
@@ -414,8 +560,16 @@ public class Setup {
|
|||||||
// Duplicate check, but better to be sure...
|
// Duplicate check, but better to be sure...
|
||||||
throw new SetupException("Missing MS boot loader backup!");
|
throw new SetupException("Missing MS boot loader backup!");
|
||||||
}
|
}
|
||||||
|
var loader = Path.Combine(InstallPath, "loader.efi");
|
||||||
|
if (SkipShim == (DetectLoader(loader) == BootLoaderType.Shim)) {
|
||||||
|
throw new SetupException("Bad skip-shim usage. Install and enable with consistent options.");
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
InstallFile(own, ms, false);
|
InstallFile(loader, ms, false);
|
||||||
|
if (!SkipShim) {
|
||||||
|
InstallFile(Path.Combine(InstallPath, $"grub{EfiArch}.efi"), msGrub, false);
|
||||||
|
InstallFile(Path.Combine(InstallPath, $"mm{EfiArch}.efi"), msMm, false);
|
||||||
|
}
|
||||||
} catch (SetupException e) {
|
} catch (SetupException e) {
|
||||||
WriteLine(e.Message);
|
WriteLine(e.Message);
|
||||||
if (DetectLoader(ms) != BootLoaderType.Microsoft) {
|
if (DetectLoader(ms) != BootLoaderType.Microsoft) {
|
||||||
@@ -426,6 +580,7 @@ public class Setup {
|
|||||||
throw new SetupException("Rollback failed, your system may be unbootable! Create a rescue disk IMMEADIATELY!");
|
throw new SetupException("Rollback failed, your system may be unbootable! Create a rescue disk IMMEADIATELY!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,11 +588,13 @@ public class Setup {
|
|||||||
* Restore the MS boot loader if it was previously replaced.
|
* Restore the MS boot loader if it was previously replaced.
|
||||||
*/
|
*/
|
||||||
protected void RestoreMsLoader() {
|
protected void RestoreMsLoader() {
|
||||||
var ms = Esp.MsLoaderPath;
|
var (ms, msDir, msGrub, msMm) = GetMsLoaderPaths();
|
||||||
if (DetectLoader(ms) == BootLoaderType.Own) {
|
if (DetectLoader(ms) == BootLoaderType.Own || DetectLoader(ms) == BootLoaderType.Shim) {
|
||||||
WriteLine("Disabling an old version of HackBGRT.");
|
WriteLine("Disabling an old version of HackBGRT.");
|
||||||
InstallFile(BackupLoaderPath, ms, false);
|
InstallFile(BackupLoaderPath, ms, false);
|
||||||
WriteLine($"{ms} has been restored.");
|
WriteLine($"{ms} has been restored.");
|
||||||
|
File.Delete(msGrub);
|
||||||
|
File.Delete(msMm);
|
||||||
}
|
}
|
||||||
if (DetectLoader(BackupLoaderPath) == BootLoaderType.Own) {
|
if (DetectLoader(BackupLoaderPath) == BootLoaderType.Own) {
|
||||||
File.Delete(BackupLoaderPath);
|
File.Delete(BackupLoaderPath);
|
||||||
@@ -450,7 +607,7 @@ public class Setup {
|
|||||||
*/
|
*/
|
||||||
protected void VerifyLoaderConfig() {
|
protected void VerifyLoaderConfig() {
|
||||||
var lines = File.ReadAllLines("config.txt");
|
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) {
|
if (loader == null) {
|
||||||
throw new SetupException("config.txt does not contain a boot=... line!");
|
throw new SetupException("config.txt does not contain a boot=... line!");
|
||||||
}
|
}
|
||||||
@@ -514,7 +671,9 @@ public class Setup {
|
|||||||
protected void Uninstall() {
|
protected void Uninstall() {
|
||||||
Disable();
|
Disable();
|
||||||
try {
|
try {
|
||||||
Directory.Delete(InstallPath, true);
|
if (Directory.Exists(InstallPath)) {
|
||||||
|
Directory.Delete(InstallPath, true);
|
||||||
|
}
|
||||||
WriteLine($"HackBGRT has been removed from {InstallPath}.");
|
WriteLine($"HackBGRT has been removed from {InstallPath}.");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log($"Uninstall failed: {e.ToString()}");
|
Log($"Uninstall failed: {e.ToString()}");
|
||||||
@@ -524,10 +683,8 @@ public class Setup {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Check Secure Boot status and inform the user.
|
* Check Secure Boot status and inform the user.
|
||||||
*
|
|
||||||
* @param allowSecureBoot Allow Secure Boot to be enabled?
|
|
||||||
*/
|
*/
|
||||||
protected void HandleSecureBoot(bool allowSecureBoot) {
|
protected void HandleSecureBoot() {
|
||||||
int secureBoot = Efi.GetSecureBootStatus();
|
int secureBoot = Efi.GetSecureBootStatus();
|
||||||
if (secureBoot == 0) {
|
if (secureBoot == 0) {
|
||||||
WriteLine("Secure Boot is disabled, good!");
|
WriteLine("Secure Boot is disabled, good!");
|
||||||
@@ -538,9 +695,12 @@ public class Setup {
|
|||||||
WriteLine("Secure Boot status could not be determined.");
|
WriteLine("Secure Boot status could not be determined.");
|
||||||
}
|
}
|
||||||
WriteLine("It's very important to disable Secure Boot before installing.");
|
WriteLine("It's very important to disable Secure Boot before installing.");
|
||||||
|
if (LoaderIsSigned) {
|
||||||
|
WriteLine("Alternatively, you can enroll the certificate.cer in your firmware.");
|
||||||
|
}
|
||||||
WriteLine("Otherwise your machine may become unbootable.");
|
WriteLine("Otherwise your machine may become unbootable.");
|
||||||
if (Batch) {
|
if (Batch) {
|
||||||
if (allowSecureBoot) {
|
if (AllowSecureBoot) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new SetupException("Aborting because of Secure Boot.");
|
throw new SetupException("Aborting because of Secure Boot.");
|
||||||
@@ -564,17 +724,15 @@ public class Setup {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Check BitLocker status and inform the user.
|
* Check BitLocker status and inform the user.
|
||||||
*
|
|
||||||
* @param allowBitLocker Allow BitLocker to be enabled?
|
|
||||||
*/
|
*/
|
||||||
protected void HandleBitLocker(bool allowBitLocker) {
|
protected void HandleBitLocker() {
|
||||||
var output = Execute("manage-bde", "-status", true);
|
var output = Execute("manage-bde", "-status", true);
|
||||||
if (output == null) {
|
if (output == null) {
|
||||||
WriteLine("BitLocker status could not be determined.");
|
WriteLine("BitLocker status could not be determined.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var reOn = new Regex(@"Conversion Status:\s*(Encr|Fully Encr)|Protection Status:\s*Protection On");
|
var reOn = new Regex(@"Conversion Status:\s*.*Encr|AES");
|
||||||
var reOff = new Regex(@"Conversion Status:\s*(Fully Decrypted)|Protection Status:\s*Protection Off");
|
var reOff = new Regex(@"Conversion Status:\s*(Fully Decrypted)|\s0[.,]0\s*%");
|
||||||
var isOn = reOn.Match(output).Success;
|
var isOn = reOn.Match(output).Success;
|
||||||
var isOff = reOff.Match(output).Success;
|
var isOff = reOff.Match(output).Success;
|
||||||
if (!isOn && isOff) {
|
if (!isOn && isOff) {
|
||||||
@@ -587,7 +745,7 @@ public class Setup {
|
|||||||
WriteLine("BitLocker status is unclear. Run manage-bde -status to check.");
|
WriteLine("BitLocker status is unclear. Run manage-bde -status to check.");
|
||||||
}
|
}
|
||||||
if (Batch) {
|
if (Batch) {
|
||||||
if (allowBitLocker) {
|
if (AllowBitLocker) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw new SetupException("Aborting because of BitLocker.");
|
throw new SetupException("Aborting because of BitLocker.");
|
||||||
@@ -644,8 +802,13 @@ public class Setup {
|
|||||||
* @param arch The architecture.
|
* @param arch The architecture.
|
||||||
*/
|
*/
|
||||||
protected void SetArch(string arch) {
|
protected void SetArch(string arch) {
|
||||||
var detectedArch = DetectArchFromOS();
|
var detectedArch = Environment.Is64BitOperatingSystem ? "x64" : "ia32";
|
||||||
if (arch == "") {
|
try {
|
||||||
|
detectedArch = DetectArchFromOS();
|
||||||
|
} catch {
|
||||||
|
WriteLine($"Failed to detect OS architecture, assuming {detectedArch}.");
|
||||||
|
}
|
||||||
|
if (arch == "" || arch == null) {
|
||||||
EfiArch = detectedArch;
|
EfiArch = detectedArch;
|
||||||
WriteLine($"Your OS uses arch={EfiArch}. This will be checked again during installation.");
|
WriteLine($"Your OS uses arch={EfiArch}. This will be checked again during installation.");
|
||||||
} else {
|
} else {
|
||||||
@@ -674,6 +837,40 @@ public class Setup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log the boot time.
|
||||||
|
*
|
||||||
|
* @return The boot time, or null if it couldn't be determined.
|
||||||
|
*/
|
||||||
|
protected DateTime? GetBootTime() {
|
||||||
|
try {
|
||||||
|
var query = new ObjectQuery("SELECT LastBootUpTime FROM Win32_OperatingSystem WHERE Primary='true'");
|
||||||
|
foreach (var m in new ManagementObjectSearcher(query).Get()) {
|
||||||
|
return ManagementDateTimeConverter.ToDateTime(m["LastBootUpTime"].ToString());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log($"GetBootTime failed: {e.ToString()}");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if Hiberboot is enabled.
|
||||||
|
*
|
||||||
|
* @return True, if Hiberboot is enabled.
|
||||||
|
*/
|
||||||
|
protected bool IsHiberbootEnabled() {
|
||||||
|
try {
|
||||||
|
return (int) Registry.GetValue(
|
||||||
|
"HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Power",
|
||||||
|
"HiberbootEnabled",
|
||||||
|
0
|
||||||
|
) != 0;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ask for user's choice and install/uninstall.
|
* Ask for user's choice and install/uninstall.
|
||||||
*/
|
*/
|
||||||
@@ -682,19 +879,14 @@ public class Setup {
|
|||||||
WriteLine("Choose action (press a key):");
|
WriteLine("Choose action (press a key):");
|
||||||
WriteLine(" I = install");
|
WriteLine(" I = install");
|
||||||
WriteLine(" - creates a new EFI boot entry for HackBGRT");
|
WriteLine(" - creates a new EFI boot entry for HackBGRT");
|
||||||
WriteLine(" - sometimes needs to be enabled in firmware settings");
|
|
||||||
WriteLine(" - should fall back to MS boot loader if HackBGRT fails");
|
|
||||||
WriteLine(" J = install (alternative)");
|
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(" - always sets HackBGRT as the first boot option");
|
WriteLine(" - try this if the first option doesn't work");
|
||||||
WriteLine(" - sometimes shows up as \"Windows Boot Manager\"");
|
|
||||||
WriteLine(" - should fall back to MS boot loader if HackBGRT fails");
|
|
||||||
WriteLine(" O = install (legacy)");
|
WriteLine(" O = install (legacy)");
|
||||||
WriteLine(" - overwrites the MS boot loader");
|
WriteLine(" - overwrites the MS boot loader; gets removed by Windows updates");
|
||||||
WriteLine(" - may require re-install after Windows updates");
|
WriteLine(" - use as last resort; may brick your system if configured incorrectly");
|
||||||
WriteLine(" - could brick your system if configured incorrectly");
|
|
||||||
WriteLine(" F = install files only");
|
WriteLine(" F = install files only");
|
||||||
WriteLine(" - needs to be enabled somehow");
|
WriteLine(" - ok for updating config, doesn't touch boot entries");
|
||||||
WriteLine(" D = disable");
|
WriteLine(" D = disable");
|
||||||
WriteLine(" - removes created entries, restores MS boot loader");
|
WriteLine(" - removes created entries, restores MS boot loader");
|
||||||
WriteLine(" R = remove completely");
|
WriteLine(" R = remove completely");
|
||||||
@@ -702,28 +894,31 @@ public class Setup {
|
|||||||
WriteLine(" B = boot to UEFI setup");
|
WriteLine(" B = boot to UEFI setup");
|
||||||
WriteLine(" - lets you disable Secure Boot");
|
WriteLine(" - lets you disable Secure Boot");
|
||||||
WriteLine(" - lets you move HackBGRT before Windows in boot order");
|
WriteLine(" - lets you move HackBGRT before Windows in boot order");
|
||||||
|
WriteLine(" L = show boot log (what HackBGRT did during boot)");
|
||||||
WriteLine(" C = cancel");
|
WriteLine(" C = cancel");
|
||||||
|
|
||||||
var k = Console.ReadKey().Key;
|
var k = Console.ReadKey().Key;
|
||||||
Log($"User input: {k}");
|
Log($"User input: {k}");
|
||||||
WriteLine();
|
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();
|
Configure();
|
||||||
}
|
}
|
||||||
if (k == ConsoleKey.I) {
|
if (k == ConsoleKey.I) {
|
||||||
RunPrivilegedActions(new string[] { "install", "enable-entry" });
|
RunPrivilegedActions(new string[] { "disable", "install", "enable-bcdedit" });
|
||||||
} else if (k == ConsoleKey.J) {
|
} else if (k == ConsoleKey.J) {
|
||||||
RunPrivilegedActions(new string[] { "install", "enable-bcdedit" });
|
RunPrivilegedActions(new string[] { "disable", "install", "enable-entry" });
|
||||||
} else if (k == ConsoleKey.O) {
|
} else if (k == ConsoleKey.O) {
|
||||||
RunPrivilegedActions(new string[] { "install", "enable-overwrite" });
|
RunPrivilegedActions(new string[] { "disable", "install", "enable-overwrite" });
|
||||||
} else if (k == ConsoleKey.F) {
|
} else if (k == ConsoleKey.F) {
|
||||||
RunPrivilegedActions(new string[] { "install" });
|
RunPrivilegedActions(new string[] { "disable-overwrite", "install" });
|
||||||
} else if (k == ConsoleKey.D) {
|
} else if (k == ConsoleKey.D) {
|
||||||
RunPrivilegedActions(new string[] { "disable" });
|
RunPrivilegedActions(new string[] { "disable" });
|
||||||
} else if (k == ConsoleKey.R) {
|
} else if (k == ConsoleKey.R) {
|
||||||
RunPrivilegedActions(new string[] { "uninstall" });
|
RunPrivilegedActions(new string[] { "uninstall" });
|
||||||
} else if (k == ConsoleKey.B) {
|
} else if (k == ConsoleKey.B) {
|
||||||
RunPrivilegedActions(new string[] { "boot-to-fw" });
|
RunPrivilegedActions(new string[] { "boot-to-fw" });
|
||||||
|
} else if (k == ConsoleKey.L) {
|
||||||
|
RunPrivilegedActions(new string[] { "show-boot-log" });
|
||||||
} else if (k == ConsoleKey.C) {
|
} else if (k == ConsoleKey.C) {
|
||||||
throw new ExitSetup(1);
|
throw new ExitSetup(1);
|
||||||
} else {
|
} else {
|
||||||
@@ -757,15 +952,25 @@ public class Setup {
|
|||||||
|
|
||||||
InitEspPath();
|
InitEspPath();
|
||||||
InitEspInfo();
|
InitEspInfo();
|
||||||
|
var bootLog = $"\n--- BOOT LOG START ---\n{Efi.GetHackBGRTLog()}\n--- BOOT LOG END ---";
|
||||||
|
Setup.Log(bootLog);
|
||||||
Efi.LogBGRT();
|
Efi.LogBGRT();
|
||||||
bool allowSecureBoot = false;
|
EfiBootEntries.TryLogEntries();
|
||||||
bool allowBitLocker = false;
|
if (GetBootTime() is DateTime bootTime) {
|
||||||
bool allowBadLoader = false;
|
var configTime = new[] { File.GetCreationTime("config.txt"), File.GetLastWriteTime("config.txt") }.Max();
|
||||||
|
Log($"Boot time = {bootTime}, config.txt changed = {configTime}");
|
||||||
|
if (configTime > bootTime) {
|
||||||
|
WriteLine($"Windows was booted at {bootTime}. Remember to reboot after installing!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (IsHiberbootEnabled()) {
|
||||||
|
WriteLine("You may have to disable Fast Startup (Hiberboot) to reboot properly.");
|
||||||
|
}
|
||||||
Action<Action> verify = (Action revert) => {
|
Action<Action> verify = (Action revert) => {
|
||||||
try {
|
try {
|
||||||
VerifyLoaderConfig();
|
VerifyLoaderConfig();
|
||||||
} catch (SetupException e) {
|
} catch (SetupException e) {
|
||||||
if (allowBadLoader) {
|
if (AllowBadLoader) {
|
||||||
WriteLine($"Warning: {e.Message}");
|
WriteLine($"Warning: {e.Message}");
|
||||||
} else {
|
} else {
|
||||||
WriteLine($"Error: {e.Message}");
|
WriteLine($"Error: {e.Message}");
|
||||||
@@ -776,8 +981,10 @@ public class Setup {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
Action<Action, Action> enable = (Action enable, Action revert) => {
|
Action<Action, Action> enable = (Action enable, Action revert) => {
|
||||||
HandleSecureBoot(allowSecureBoot);
|
if (SkipShim) {
|
||||||
HandleBitLocker(allowBitLocker);
|
HandleSecureBoot();
|
||||||
|
}
|
||||||
|
HandleBitLocker();
|
||||||
enable();
|
enable();
|
||||||
verify(revert);
|
verify(revert);
|
||||||
};
|
};
|
||||||
@@ -785,12 +992,6 @@ public class Setup {
|
|||||||
Log($"Running action '{arg}'.");
|
Log($"Running action '{arg}'.");
|
||||||
if (arg == "install") {
|
if (arg == "install") {
|
||||||
InstallFiles();
|
InstallFiles();
|
||||||
} else if (arg == "allow-secure-boot") {
|
|
||||||
allowSecureBoot = true;
|
|
||||||
} else if (arg == "allow-bitlocker") {
|
|
||||||
allowBitLocker = true;
|
|
||||||
} else if (arg == "allow-bad-loader") {
|
|
||||||
allowBadLoader = true;
|
|
||||||
} else if (arg == "enable-entry") {
|
} else if (arg == "enable-entry") {
|
||||||
enable(() => EnableEntry(), () => DisableEntry());
|
enable(() => EnableEntry(), () => DisableEntry());
|
||||||
} else if (arg == "disable-entry") {
|
} else if (arg == "disable-entry") {
|
||||||
@@ -809,6 +1010,8 @@ public class Setup {
|
|||||||
Uninstall();
|
Uninstall();
|
||||||
} else if (arg == "boot-to-fw") {
|
} else if (arg == "boot-to-fw") {
|
||||||
BootToFW();
|
BootToFW();
|
||||||
|
} else if (arg == "show-boot-log") {
|
||||||
|
WriteLine(bootLog);
|
||||||
} else {
|
} else {
|
||||||
throw new SetupException($"Invalid action: '{arg}'!");
|
throw new SetupException($"Invalid action: '{arg}'!");
|
||||||
}
|
}
|
||||||
@@ -837,14 +1040,23 @@ public class Setup {
|
|||||||
protected int Run(string[] args) {
|
protected int Run(string[] args) {
|
||||||
DryRun = args.Contains("dry-run");
|
DryRun = args.Contains("dry-run");
|
||||||
Batch = args.Contains("batch");
|
Batch = args.Contains("batch");
|
||||||
ForwardArguments = String.Join(" ", args.Where(s => s == "dry-run" || s == "batch" || s.StartsWith("arch=")));
|
AllowBadLoader = args.Contains("allow-bad-loader");
|
||||||
|
AllowSecureBoot = args.Contains("allow-secure-boot");
|
||||||
|
AllowBitLocker = args.Contains("allow-bitlocker");
|
||||||
|
SkipShim = args.Contains("skip-shim");
|
||||||
|
ForwardArguments = String.Join(" ", args.Where(s => ForwardableArguments.Contains(s) || s.StartsWith("arch=")));
|
||||||
try {
|
try {
|
||||||
SetArch(args.Prepend("arch=").Last(s => s.StartsWith("arch=")).Substring(5));
|
if (!(Directory.Exists("efi") || Directory.Exists("efi-signed")) || !File.Exists("config.txt")) {
|
||||||
|
WriteLine("This setup program is not in the correct directory!");
|
||||||
|
WriteLine("Please extract the zip file and run the setup program from there.");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
SetArch(args.Where(s => s.StartsWith("arch=")).Select(s => s.Substring(5)).LastOrDefault());
|
||||||
if (args.Contains("is-elevated") && !HasPrivileges() && !DryRun) {
|
if (args.Contains("is-elevated") && !HasPrivileges() && !DryRun) {
|
||||||
WriteLine("This installer needs to be run as administrator!");
|
WriteLine("This installer needs to be run as administrator!");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
var actions = args.Where(s => privilegedActions.Contains(s));
|
var actions = args.Where(s => PrivilegedActions.Contains(s));
|
||||||
if (actions.Count() > 0) {
|
if (actions.Count() > 0) {
|
||||||
RunPrivilegedActions(actions);
|
RunPrivilegedActions(actions);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -867,14 +1079,20 @@ public class Setup {
|
|||||||
WriteLine();
|
WriteLine();
|
||||||
WriteLine($"Unexpected error: {e.Message}");
|
WriteLine($"Unexpected error: {e.Message}");
|
||||||
Log(e.ToString());
|
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;
|
return 1;
|
||||||
} finally {
|
} finally {
|
||||||
if (DryRun) {
|
if (DryRun) {
|
||||||
WriteLine("This was a dry run, your system was not actually modified.");
|
WriteLine("This was a dry run, your system was not actually modified.");
|
||||||
}
|
}
|
||||||
if (!Batch) {
|
if (!Batch) {
|
||||||
WriteLine("If you need to report a bug, please include the setup.log file.");
|
WriteLine("If you need to report a bug,\n - run this setup again with menu option L (show-boot-log)\n - then include the setup.log file with your report.");
|
||||||
WriteLine("Press any key to quit.");
|
WriteLine("Press any key to quit.");
|
||||||
Console.ReadKey();
|
Console.ReadKey();
|
||||||
}
|
}
|
||||||
|
|||||||
38
src/config.c
38
src/config.c
@@ -1,14 +1,12 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#include <efilib.h>
|
BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE base_dir, const CHAR16* path) {
|
||||||
|
|
||||||
BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, const CHAR16* path) {
|
|
||||||
void* data = 0;
|
void* data = 0;
|
||||||
UINTN data_bytes = 0;
|
UINTN data_bytes = 0;
|
||||||
data = LoadFileWithPadding(root_dir, path, &data_bytes, 4);
|
data = LoadFileWithPadding(base_dir, path, &data_bytes, 4);
|
||||||
if (!data) {
|
if (!data) {
|
||||||
Print(L"HackBGRT: Failed to load configuration (%s)!\n", path);
|
Log(1, L"Failed to load configuration (%s)!\n", path);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
CHAR16* str;
|
CHAR16* str;
|
||||||
@@ -21,7 +19,7 @@ BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir,
|
|||||||
// UTF-8 -> UCS-2
|
// UTF-8 -> UCS-2
|
||||||
EFI_STATUS e = BS->AllocatePool(EfiBootServicesData, data_bytes * 2 + 2, (void**)&str);
|
EFI_STATUS e = BS->AllocatePool(EfiBootServicesData, data_bytes * 2 + 2, (void**)&str);
|
||||||
if (EFI_ERROR(e)) {
|
if (EFI_ERROR(e)) {
|
||||||
FreePool(data);
|
BS->FreePool(data);
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
UINT8* str0 = data;
|
UINT8* str0 = data;
|
||||||
@@ -51,7 +49,7 @@ BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
str[str_len] = 0;
|
str[str_len] = 0;
|
||||||
FreePool(data);
|
BS->FreePool(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < str_len;) {
|
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;
|
str[j] = 0;
|
||||||
++j;
|
++j;
|
||||||
}
|
}
|
||||||
ReadConfigLine(config, root_dir, &str[i]);
|
ReadConfigLine(config, base_dir, &str[i]);
|
||||||
i = j;
|
i = j;
|
||||||
}
|
}
|
||||||
// NOTICE: string is not freed, because paths are not copied.
|
// NOTICE: string is not freed, because paths are not copied.
|
||||||
@@ -72,12 +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) {
|
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;
|
config->image_weight_sum += weight;
|
||||||
UINT32 random = Random();
|
UINT32 random = (((UINT64) Random() & 0xffffffff) * config->image_weight_sum) >> 32;
|
||||||
UINT32 limit = 0xfffffffful / config->image_weight_sum * weight;
|
UINT32 limit = ((UINT64) 0xffffffff * weight) >> 32;
|
||||||
if (config->debug) {
|
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);
|
||||||
Print(L"HackBGRT: weight %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 (random <= limit) {
|
||||||
}
|
|
||||||
if (!config->image_weight_sum || random <= limit) {
|
|
||||||
config->action = action;
|
config->action = action;
|
||||||
config->image_path = path;
|
config->image_path = path;
|
||||||
config->orientation = o;
|
config->orientation = o;
|
||||||
@@ -112,7 +108,7 @@ static void ReadConfigImage(struct HackBGRT_config* config, const CHAR16* line)
|
|||||||
} else if (StrStr(line, L"keep")) {
|
} else if (StrStr(line, L"keep")) {
|
||||||
action = HackBGRT_KEEP;
|
action = HackBGRT_KEEP;
|
||||||
} else {
|
} else {
|
||||||
Print(L"HackBGRT: Invalid image line: %s\n", line);
|
Log(1, L"Invalid image line: %s\n", line);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int weight = n && (!f || n < f) ? Atoi(n) : 1;
|
int weight = n && (!f || n < f) ? Atoi(n) : 1;
|
||||||
@@ -129,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_x = *x == '-' ? -(int)Atoi(x+1) : (int)Atoi(x);
|
||||||
config->resolution_y = *y == '-' ? -(int)Atoi(y+1) : (int)Atoi(y);
|
config->resolution_y = *y == '-' ? -(int)Atoi(y+1) : (int)Atoi(y);
|
||||||
} else {
|
} else {
|
||||||
Print(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);
|
line = TrimLeft(line);
|
||||||
if (line[0] == L'#' || line[0] == 0) {
|
if (line[0] == L'#' || line[0] == 0) {
|
||||||
return;
|
return;
|
||||||
@@ -143,6 +139,10 @@ void ReadConfigLine(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, co
|
|||||||
config->debug = (StrCmp(line, L"debug=1") == 0);
|
config->debug = (StrCmp(line, L"debug=1") == 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (StrnCmp(line, L"log=", 4) == 0) {
|
||||||
|
config->log = (StrCmp(line, L"log=1") == 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (StrnCmp(line, L"image=", 6) == 0) {
|
if (StrnCmp(line, L"image=", 6) == 0) {
|
||||||
ReadConfigImage(config, line + 6);
|
ReadConfigImage(config, line + 6);
|
||||||
return;
|
return;
|
||||||
@@ -152,12 +152,12 @@ void ReadConfigLine(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, co
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (StrnCmp(line, L"config=", 7) == 0) {
|
if (StrnCmp(line, L"config=", 7) == 0) {
|
||||||
ReadConfigFile(config, root_dir, line + 7);
|
ReadConfigFile(config, base_dir, line + 7);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (StrnCmp(line, L"resolution=", 11) == 0) {
|
if (StrnCmp(line, L"resolution=", 11) == 0) {
|
||||||
ReadConfigResolution(config, line + 11);
|
ReadConfigResolution(config, line + 11);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Print(L"Unknown configuration directive: %s\n", line);
|
Log(1, L"Unknown configuration directive: %s\n", line);
|
||||||
}
|
}
|
||||||
|
|||||||
14
src/config.h
14
src/config.h
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <efi.h>
|
#include "efi.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Possible actions to perform on the BGRT.
|
* Possible actions to perform on the BGRT.
|
||||||
@@ -14,14 +14,14 @@ enum HackBGRT_action {
|
|||||||
* @see struct HackBGRT_config
|
* @see struct HackBGRT_config
|
||||||
*/
|
*/
|
||||||
enum HackBGRT_coordinate {
|
enum HackBGRT_coordinate {
|
||||||
HackBGRT_coord_keep = -1000000001
|
HackBGRT_coord_keep = -1000001
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The configuration.
|
* The configuration.
|
||||||
*/
|
*/
|
||||||
struct HackBGRT_config {
|
struct HackBGRT_config {
|
||||||
int debug;
|
int debug, log;
|
||||||
enum HackBGRT_action action;
|
enum HackBGRT_action action;
|
||||||
const CHAR16* image_path;
|
const CHAR16* image_path;
|
||||||
int image_x;
|
int image_x;
|
||||||
@@ -39,17 +39,17 @@ struct HackBGRT_config {
|
|||||||
* Read a configuration parameter. (May recursively read config files.)
|
* Read a configuration parameter. (May recursively read config files.)
|
||||||
*
|
*
|
||||||
* @param config The configuration to modify.
|
* @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.
|
* @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.)
|
* Read a configuration file. (May recursively read more files.)
|
||||||
*
|
*
|
||||||
* @param config The configuration to modify.
|
* @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.
|
* @param path The path to the file.
|
||||||
* @return FALSE, if the file couldn't be read, TRUE otherwise.
|
* @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
159
src/efi.c
Normal 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
5
src/efi.h
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <efi.h>
|
||||||
|
#include <efilib.h>
|
||||||
234
src/main.c
234
src/main.c
@@ -1,25 +1,27 @@
|
|||||||
#include <efi.h>
|
#include "efi.h"
|
||||||
#include <efilib.h>
|
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Print function signature.
|
* The version.
|
||||||
*/
|
*/
|
||||||
typedef UINTN print_t(IN CONST CHAR16 *fmt, ...);
|
#ifdef GIT_DESCRIBE_W
|
||||||
|
const CHAR16 version[] = GIT_DESCRIBE_W;
|
||||||
|
#else
|
||||||
|
const CHAR16 version[] = L"unknown; not an official release?";
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
EFI_SYSTEM_TABLE *ST;
|
||||||
* The function for debug printing; either Print or NullPrint.
|
EFI_BOOT_SERVICES *BS;
|
||||||
*/
|
EFI_RUNTIME_SERVICES *RT;
|
||||||
print_t* Debug = NullPrint;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The configuration.
|
* The configuration.
|
||||||
*/
|
*/
|
||||||
static struct HackBGRT_config config = {
|
static struct HackBGRT_config config = {
|
||||||
.action = HackBGRT_KEEP
|
.log = 1,
|
||||||
|
.action = HackBGRT_KEEP,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,8 +30,7 @@ static struct HackBGRT_config config = {
|
|||||||
static EFI_GRAPHICS_OUTPUT_PROTOCOL* GOP(void) {
|
static EFI_GRAPHICS_OUTPUT_PROTOCOL* GOP(void) {
|
||||||
static EFI_GRAPHICS_OUTPUT_PROTOCOL* gop;
|
static EFI_GRAPHICS_OUTPUT_PROTOCOL* gop;
|
||||||
if (!gop) {
|
if (!gop) {
|
||||||
EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
|
LibLocateProtocol(TmpGuidPtr((EFI_GUID) EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID), (void**) &gop);
|
||||||
LibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **)&gop);
|
|
||||||
}
|
}
|
||||||
return gop;
|
return gop;
|
||||||
}
|
}
|
||||||
@@ -43,9 +44,13 @@ static EFI_GRAPHICS_OUTPUT_PROTOCOL* GOP(void) {
|
|||||||
static void SetResolution(int w, int h) {
|
static void SetResolution(int w, int h) {
|
||||||
EFI_GRAPHICS_OUTPUT_PROTOCOL* gop = GOP();
|
EFI_GRAPHICS_OUTPUT_PROTOCOL* gop = GOP();
|
||||||
if (!gop) {
|
if (!gop) {
|
||||||
config.old_resolution_x = config.resolution_x = 0;
|
if (config.resolution_x <= 0 || config.resolution_y <= 0) {
|
||||||
config.old_resolution_y = config.resolution_y = 0;
|
config.resolution_x = 1024;
|
||||||
Debug(L"GOP not found!\n");
|
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;
|
return;
|
||||||
}
|
}
|
||||||
UINTN best_i = gop->Mode->Mode;
|
UINTN best_i = gop->Mode->Mode;
|
||||||
@@ -54,7 +59,7 @@ static void SetResolution(int w, int h) {
|
|||||||
w = (w <= 0 ? w < 0 ? best_w : 999999 : w);
|
w = (w <= 0 ? w < 0 ? best_w : 999999 : w);
|
||||||
h = (h <= 0 ? h < 0 ? best_h : 999999 : h);
|
h = (h <= 0 ? h < 0 ? best_h : 999999 : h);
|
||||||
|
|
||||||
Debug(L"Looking for resolution %dx%d...\n", w, h);
|
Log(config.debug, L"Looking for resolution %dx%d...\n", w, h);
|
||||||
for (UINT32 i = gop->Mode->MaxMode; i--;) {
|
for (UINT32 i = gop->Mode->MaxMode; i--;) {
|
||||||
int new_w = 0, new_h = 0;
|
int new_w = 0, new_h = 0;
|
||||||
|
|
||||||
@@ -64,12 +69,12 @@ static void SetResolution(int w, int h) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (info_size < sizeof(*info)) {
|
if (info_size < sizeof(*info)) {
|
||||||
FreePool(info);
|
BS->FreePool(info);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
new_w = info->HorizontalResolution;
|
new_w = info->HorizontalResolution;
|
||||||
new_h = info->VerticalResolution;
|
new_h = info->VerticalResolution;
|
||||||
FreePool(info);
|
BS->FreePool(info);
|
||||||
|
|
||||||
// Sum of missing w/h should be minimal.
|
// Sum of missing w/h should be minimal.
|
||||||
int new_missing = max(w - new_w, 0) + max(h - new_h, 0);
|
int new_missing = max(w - new_w, 0) + max(h - new_h, 0);
|
||||||
@@ -87,7 +92,7 @@ static void SetResolution(int w, int h) {
|
|||||||
best_h = new_h;
|
best_h = new_h;
|
||||||
best_i = i;
|
best_i = i;
|
||||||
}
|
}
|
||||||
Debug(L"Found resolution %dx%d.\n", best_w, best_h);
|
Log(config.debug, L"Found resolution %dx%d.\n", best_w, best_h);
|
||||||
config.resolution_x = best_w;
|
config.resolution_x = best_w;
|
||||||
config.resolution_y = best_h;
|
config.resolution_y = best_h;
|
||||||
if (best_i != gop->Mode->Mode) {
|
if (best_i != gop->Mode->Mode) {
|
||||||
@@ -107,11 +112,11 @@ ACPI_SDT_HEADER* CreateXsdt(ACPI_SDT_HEADER* xsdt0, UINTN entries) {
|
|||||||
UINT32 xsdt_len = sizeof(ACPI_SDT_HEADER) + entries * sizeof(UINT64);
|
UINT32 xsdt_len = sizeof(ACPI_SDT_HEADER) + entries * sizeof(UINT64);
|
||||||
BS->AllocatePool(EfiACPIReclaimMemory, xsdt_len, (void**)&xsdt);
|
BS->AllocatePool(EfiACPIReclaimMemory, xsdt_len, (void**)&xsdt);
|
||||||
if (!xsdt) {
|
if (!xsdt) {
|
||||||
Print(L"HackBGRT: Failed to allocate memory for XSDT.\n");
|
Log(1, L"Failed to allocate memory for XSDT.\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
ZeroMem(xsdt, xsdt_len);
|
BS->SetMem(xsdt, xsdt_len, 0);
|
||||||
CopyMem(xsdt, xsdt0, min(xsdt0->length, xsdt_len));
|
BS->CopyMem(xsdt, xsdt0, min(xsdt0->length, xsdt_len));
|
||||||
xsdt->length = xsdt_len;
|
xsdt->length = xsdt_len;
|
||||||
SetAcpiSdtChecksum(xsdt);
|
SetAcpiSdtChecksum(xsdt);
|
||||||
return xsdt;
|
return xsdt;
|
||||||
@@ -130,26 +135,25 @@ ACPI_SDT_HEADER* CreateXsdt(ACPI_SDT_HEADER* xsdt0, UINTN entries) {
|
|||||||
*/
|
*/
|
||||||
static ACPI_BGRT* HandleAcpiTables(enum HackBGRT_action action, ACPI_BGRT* bgrt) {
|
static ACPI_BGRT* HandleAcpiTables(enum HackBGRT_action action, ACPI_BGRT* bgrt) {
|
||||||
for (int i = 0; i < ST->NumberOfTableEntries; i++) {
|
for (int i = 0; i < ST->NumberOfTableEntries; i++) {
|
||||||
EFI_GUID Acpi20TableGuid = ACPI_20_TABLE_GUID;
|
|
||||||
EFI_GUID* vendor_guid = &ST->ConfigurationTable[i].VendorGuid;
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
ACPI_20_RSDP* rsdp = (ACPI_20_RSDP *) ST->ConfigurationTable[i].VendorTable;
|
ACPI_20_RSDP* rsdp = (ACPI_20_RSDP *) ST->ConfigurationTable[i].VendorTable;
|
||||||
if (CompareMem(rsdp->signature, "RSD PTR ", 8) != 0 || rsdp->revision < 2 || !VerifyAcpiRsdp2Checksums(rsdp)) {
|
if (CompareMem(rsdp->signature, "RSD PTR ", 8) != 0 || rsdp->revision < 2 || !VerifyAcpiRsdp2Checksums(rsdp)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Debug(L"RSDP @%x: revision = %d, OEM ID = %s\n", (UINTN)rsdp, rsdp->revision, TmpStr(rsdp->oem_id, 6));
|
Log(config.debug, L"RSDP @%x: revision = %d, OEM ID = %s\n", (UINTN)rsdp, rsdp->revision, TmpStr(rsdp->oem_id, 6));
|
||||||
|
|
||||||
ACPI_SDT_HEADER* xsdt = (ACPI_SDT_HEADER *) (UINTN) rsdp->xsdt_address;
|
ACPI_SDT_HEADER* xsdt = (ACPI_SDT_HEADER *) (UINTN) rsdp->xsdt_address;
|
||||||
if (!xsdt || CompareMem(xsdt->signature, "XSDT", 4) != 0 || !VerifyAcpiSdtChecksum(xsdt)) {
|
if (!xsdt || CompareMem(xsdt->signature, "XSDT", 4) != 0 || !VerifyAcpiSdtChecksum(xsdt)) {
|
||||||
Debug(L"* XSDT: missing or invalid\n");
|
Log(config.debug, L"* XSDT: missing or invalid\n");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
UINT64* entry_arr = (UINT64*)&xsdt[1];
|
UINT64* entry_arr = (UINT64*)&xsdt[1];
|
||||||
UINT32 entry_arr_length = (xsdt->length - sizeof(*xsdt)) / sizeof(UINT64);
|
UINT32 entry_arr_length = (xsdt->length - sizeof(*xsdt)) / sizeof(UINT64);
|
||||||
|
|
||||||
Debug(L"* XSDT @%x: OEM ID = %s, entry count = %d\n", (UINTN)xsdt, TmpStr(xsdt->oem_id, 6), entry_arr_length);
|
Log(config.debug, L"* XSDT @%x: OEM ID = %s, entry count = %d\n", (UINTN)xsdt, TmpStr(xsdt->oem_id, 6), entry_arr_length);
|
||||||
|
|
||||||
int bgrt_count = 0;
|
int bgrt_count = 0;
|
||||||
for (int j = 0; j < entry_arr_length; j++) {
|
for (int j = 0; j < entry_arr_length; j++) {
|
||||||
@@ -157,16 +161,16 @@ static ACPI_BGRT* HandleAcpiTables(enum HackBGRT_action action, ACPI_BGRT* bgrt)
|
|||||||
if (CompareMem(entry->signature, "BGRT", 4) != 0) {
|
if (CompareMem(entry->signature, "BGRT", 4) != 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Debug(L" - ACPI table @%x: %s, revision = %d, OEM ID = %s\n", (UINTN)entry, TmpStr(entry->signature, 4), entry->revision, TmpStr(entry->oem_id, 6));
|
Log(config.debug, L" - ACPI table @%x: %s, revision = %d, OEM ID = %s\n", (UINTN)entry, TmpStr(entry->signature, 4), entry->revision, TmpStr(entry->oem_id, 6));
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case HackBGRT_KEEP:
|
case HackBGRT_KEEP:
|
||||||
if (!bgrt) {
|
if (!bgrt) {
|
||||||
Debug(L" -> Returning this one for later use.\n");
|
Log(config.debug, L" -> Returning this one for later use.\n");
|
||||||
bgrt = (ACPI_BGRT*) entry;
|
bgrt = (ACPI_BGRT*) entry;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case HackBGRT_REMOVE:
|
case HackBGRT_REMOVE:
|
||||||
Debug(L" -> Deleting.\n");
|
Log(config.debug, L" -> Deleting.\n");
|
||||||
for (int k = j+1; k < entry_arr_length; ++k) {
|
for (int k = j+1; k < entry_arr_length; ++k) {
|
||||||
entry_arr[k-1] = entry_arr[k];
|
entry_arr[k-1] = entry_arr[k];
|
||||||
}
|
}
|
||||||
@@ -176,13 +180,13 @@ static ACPI_BGRT* HandleAcpiTables(enum HackBGRT_action action, ACPI_BGRT* bgrt)
|
|||||||
--j;
|
--j;
|
||||||
break;
|
break;
|
||||||
case HackBGRT_REPLACE:
|
case HackBGRT_REPLACE:
|
||||||
Debug(L" -> Replacing.\n");
|
Log(config.debug, L" -> Replacing.\n");
|
||||||
entry_arr[j] = (UINTN) bgrt;
|
entry_arr[j] = (UINTN) bgrt;
|
||||||
}
|
}
|
||||||
bgrt_count += 1;
|
bgrt_count += 1;
|
||||||
}
|
}
|
||||||
if (!bgrt_count && action == HackBGRT_REPLACE && bgrt) {
|
if (!bgrt_count && action == HackBGRT_REPLACE && bgrt) {
|
||||||
Debug(L" - Adding missing BGRT.\n");
|
Log(config.debug, L" - Adding missing BGRT.\n");
|
||||||
xsdt = CreateXsdt(xsdt, entry_arr_length + 1);
|
xsdt = CreateXsdt(xsdt, entry_arr_length + 1);
|
||||||
entry_arr = (UINT64*)&xsdt[1];
|
entry_arr = (UINT64*)&xsdt[1];
|
||||||
entry_arr[entry_arr_length++] = (UINTN) bgrt;
|
entry_arr[entry_arr_length++] = (UINTN) bgrt;
|
||||||
@@ -208,7 +212,7 @@ static BMP* MakeBMP(int w, int h, UINT8 r, UINT8 g, UINT8 b) {
|
|||||||
BMP* bmp = 0;
|
BMP* bmp = 0;
|
||||||
BS->AllocatePool(EfiBootServicesData, 54 + w * h * 4, (void**) &bmp);
|
BS->AllocatePool(EfiBootServicesData, 54 + w * h * 4, (void**) &bmp);
|
||||||
if (!bmp) {
|
if (!bmp) {
|
||||||
Print(L"HackBGRT: Failed to allocate a blank BMP!\n");
|
Log(1, L"Failed to allocate a blank BMP!\n");
|
||||||
BS->Stall(1000000);
|
BS->Stall(1000000);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -235,24 +239,32 @@ static BMP* MakeBMP(int w, int h, UINT8 r, UINT8 g, UINT8 b) {
|
|||||||
/**
|
/**
|
||||||
* Load a bitmap or generate a black one.
|
* Load a bitmap or generate a black one.
|
||||||
*
|
*
|
||||||
* @param root_dir The root directory for loading a BMP.
|
* @param base_dir The directory for loading a BMP.
|
||||||
* @param path The BMP path within the root directory; NULL for a black BMP.
|
* @param path The BMP path within the directory; NULL for a black BMP.
|
||||||
* @return The loaded BMP, or 0 if not available.
|
* @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) {
|
if (!path) {
|
||||||
return MakeBMP(1, 1, 0, 0, 0); // empty path = black image
|
return MakeBMP(1, 1, 0, 0, 0); // empty path = black image
|
||||||
}
|
}
|
||||||
Debug(L"HackBGRT: Loading %s.\n", path);
|
Log(config.debug, L"Loading %s.\n", path);
|
||||||
UINTN size = 0;
|
UINTN size = 0;
|
||||||
BMP* bmp = LoadFile(root_dir, path, &size);
|
BMP* bmp = LoadFile(base_dir, path, &size);
|
||||||
if (bmp) {
|
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;
|
return bmp;
|
||||||
}
|
}
|
||||||
Print(L"HackBGRT: Invalid BMP (%s)!\n", path);
|
BS->FreePool(bmp);
|
||||||
|
Log(1, L"Invalid BMP (%s)!\n", path);
|
||||||
} else {
|
} else {
|
||||||
Print(L"HackBGRT: Failed to load BMP (%s)!\n", path);
|
Log(1, L"Failed to load BMP (%s)!\n", path);
|
||||||
}
|
}
|
||||||
BS->Stall(1000000);
|
BS->Stall(1000000);
|
||||||
return MakeBMP(16, 16, 255, 0, 0); // error = red image
|
return MakeBMP(16, 16, 255, 0, 0); // error = red image
|
||||||
@@ -270,13 +282,11 @@ static void CropBMP(BMP* bmp, int w, int h) {
|
|||||||
bmp->image_size = 0;
|
bmp->image_size = 0;
|
||||||
bmp->width = min(bmp->width, w);
|
bmp->width = min(bmp->width, w);
|
||||||
bmp->height = min(bmp->height, h);
|
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);
|
const int new_pitch = -(-(bmp->width * (bmp->bpp / 8)) & ~3);
|
||||||
|
|
||||||
if (new_pitch < old_pitch) {
|
if (new_pitch < old_pitch) {
|
||||||
for (int i = 1; i < bmp->height; ++i) {
|
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 * new_pitch,
|
||||||
(UINT8*) bmp + bmp->pixel_data_offset + i * old_pitch,
|
(UINT8*) bmp + bmp->pixel_data_offset + i * old_pitch,
|
||||||
new_pitch
|
new_pitch
|
||||||
@@ -289,9 +299,9 @@ static void CropBMP(BMP* bmp, int w, int h) {
|
|||||||
/**
|
/**
|
||||||
* The main logic for BGRT modification.
|
* 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.
|
// REMOVE: simply delete all BGRT entries.
|
||||||
if (config.action == HackBGRT_REMOVE) {
|
if (config.action == HackBGRT_REMOVE) {
|
||||||
HandleAcpiTables(config.action, 0);
|
HandleAcpiTables(config.action, 0);
|
||||||
@@ -320,7 +330,7 @@ void HackBgrt(EFI_FILE_HANDLE root_dir) {
|
|||||||
// Replace missing = allocate new.
|
// Replace missing = allocate new.
|
||||||
BS->AllocatePool(EfiACPIReclaimMemory, sizeof(*bgrt), (void**)&bgrt);
|
BS->AllocatePool(EfiACPIReclaimMemory, sizeof(*bgrt), (void**)&bgrt);
|
||||||
if (!bgrt) {
|
if (!bgrt) {
|
||||||
Print(L"HackBGRT: Failed to allocate memory for BGRT.\n");
|
Log(1, L"Failed to allocate memory for BGRT.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -342,7 +352,7 @@ void HackBgrt(EFI_FILE_HANDLE root_dir) {
|
|||||||
// Get the image (either old or new).
|
// Get the image (either old or new).
|
||||||
BMP* new_bmp = old_bmp;
|
BMP* new_bmp = old_bmp;
|
||||||
if (config.action == HackBGRT_REPLACE) {
|
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.
|
// No image = no need for BGRT.
|
||||||
@@ -372,8 +382,8 @@ void HackBgrt(EFI_FILE_HANDLE root_dir) {
|
|||||||
bgrt->image_offset_x = max(0, min(max_x, new_x + (new_reso_x - new_bmp->width) / 2));
|
bgrt->image_offset_x = max(0, min(max_x, new_x + (new_reso_x - new_bmp->width) / 2));
|
||||||
bgrt->image_offset_y = max(0, min(max_y, new_y + (new_reso_y - new_bmp->height) / 2));
|
bgrt->image_offset_y = max(0, min(max_y, new_y + (new_reso_y - new_bmp->height) / 2));
|
||||||
|
|
||||||
Debug(
|
Log(config.debug,
|
||||||
L"HackBGRT: BMP at (%d, %d), center (%d, %d), resolution (%d, %d) with orientation %d applied.\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,
|
(int) bgrt->image_offset_x, (int) bgrt->image_offset_y,
|
||||||
new_x, new_y, new_reso_x, new_reso_y,
|
new_x, new_y, new_reso_x, new_reso_y,
|
||||||
new_orientation * 90
|
new_orientation * 90
|
||||||
@@ -387,12 +397,12 @@ void HackBgrt(EFI_FILE_HANDLE root_dir) {
|
|||||||
/**
|
/**
|
||||||
* Load an application.
|
* Load an application.
|
||||||
*/
|
*/
|
||||||
static EFI_HANDLE LoadApp(print_t* print_failure, EFI_HANDLE image_handle, EFI_LOADED_IMAGE* image, const CHAR16* path) {
|
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_DEVICE_PATH* boot_dp = FileDevicePath(image->DeviceHandle, (CHAR16*) path);
|
||||||
EFI_HANDLE result = 0;
|
EFI_HANDLE result = 0;
|
||||||
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))) {
|
if (EFI_ERROR(BS->LoadImage(0, image_handle, boot_dp, 0, 0, &result))) {
|
||||||
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;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -400,98 +410,116 @@ static EFI_HANDLE LoadApp(print_t* print_failure, EFI_HANDLE image_handle, EFI_L
|
|||||||
/**
|
/**
|
||||||
* The main program.
|
* The main program.
|
||||||
*/
|
*/
|
||||||
EFI_STATUS EFIAPI EfiMain(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *ST_) {
|
EFI_STATUS EFIAPI efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *ST_) {
|
||||||
InitializeLib(image_handle, 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;
|
EFI_LOADED_IMAGE* image;
|
||||||
if (EFI_ERROR(BS->HandleProtocol(image_handle, &LoadedImageProtocol, (void**) &image))) {
|
if (EFI_ERROR(BS->HandleProtocol(image_handle, TmpGuidPtr((EFI_GUID) EFI_LOADED_IMAGE_PROTOCOL_GUID), (void**) &image))) {
|
||||||
Debug(L"HackBGRT: LOADED_IMAGE_PROTOCOL failed.\n");
|
Log(config.debug, L"LOADED_IMAGE_PROTOCOL failed.\n");
|
||||||
goto fail;
|
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;
|
EFI_FILE_HANDLE root_dir;
|
||||||
int argc = GetShellArgcArgv(image_handle, &argv);
|
if (EFI_ERROR(io->OpenVolume(io, &root_dir))) {
|
||||||
|
Log(config.debug, L"Failed to open root directory.\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
if (argc <= 1) {
|
EFI_FILE_HANDLE base_dir;
|
||||||
const CHAR16* config_path = L"\\EFI\\HackBGRT\\config.txt";
|
if (EFI_ERROR(root_dir->Open(root_dir, &base_dir, L"\\EFI\\HackBGRT", EFI_FILE_MODE_READ, 0))) {
|
||||||
if (!ReadConfigFile(&config, root_dir, config_path)) {
|
Log(config.debug, L"Failed to HackBGRT directory.\n");
|
||||||
Print(L"HackBGRT: No config, no command line!\n", config_path);
|
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;
|
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) {
|
||||||
|
Log(-1, L"HackBGRT version: %s\n", version);
|
||||||
}
|
}
|
||||||
Debug = config.debug ? Print : NullPrint;
|
|
||||||
|
|
||||||
SetResolution(config.resolution_x, config.resolution_y);
|
SetResolution(config.resolution_x, config.resolution_y);
|
||||||
HackBgrt(root_dir);
|
HackBgrt(base_dir);
|
||||||
|
|
||||||
EFI_HANDLE next_image_handle = 0;
|
EFI_HANDLE next_image_handle = 0;
|
||||||
static CHAR16 backup_boot_path[] = L"\\EFI\\HackBGRT\\bootmgfw-original.efi";
|
static CHAR16 backup_boot_path[] = L"\\EFI\\HackBGRT\\bootmgfw-original.efi";
|
||||||
static CHAR16 ms_boot_path[] = L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi";
|
static CHAR16 ms_boot_path[] = L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi";
|
||||||
|
int try_ms_quietly = 1;
|
||||||
|
|
||||||
if (config.boot_path && StriCmp(config.boot_path, L"MS") != 0) {
|
if (config.boot_path && StriCmp(config.boot_path, L"MS") != 0) {
|
||||||
next_image_handle = LoadApp(Print, image_handle, image, config.boot_path);
|
next_image_handle = LoadApp(1, image_handle, image, config.boot_path);
|
||||||
} else {
|
try_ms_quietly = 0;
|
||||||
config.boot_path = backup_boot_path;
|
|
||||||
next_image_handle = LoadApp(Debug, image_handle, image, config.boot_path);
|
|
||||||
if (!next_image_handle) {
|
|
||||||
config.boot_path = ms_boot_path;
|
|
||||||
next_image_handle = LoadApp(Debug, image_handle, image, config.boot_path);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!next_image_handle) {
|
if (!next_image_handle) {
|
||||||
config.boot_path = backup_boot_path;
|
config.boot_path = backup_boot_path;
|
||||||
next_image_handle = LoadApp(Print, image_handle, image, config.boot_path);
|
next_image_handle = LoadApp(!try_ms_quietly, image_handle, image, config.boot_path);
|
||||||
if (!next_image_handle) {
|
if (!next_image_handle) {
|
||||||
config.boot_path = ms_boot_path;
|
config.boot_path = ms_boot_path;
|
||||||
next_image_handle = LoadApp(Print, image_handle, image, config.boot_path);
|
next_image_handle = LoadApp(!try_ms_quietly, image_handle, image, config.boot_path);
|
||||||
if (!next_image_handle) {
|
if (!next_image_handle) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Print(L"HackBGRT: Reverting to %s.\n", config.boot_path);
|
if (try_ms_quietly) {
|
||||||
Print(L"Press escape to cancel or any other key (or wait 15 seconds) to boot.\n");
|
goto ready_to_boot;
|
||||||
|
}
|
||||||
|
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) {
|
if (ReadKey(15000).ScanCode == SCAN_ESC) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
} else if (config.debug) {
|
} else ready_to_boot: if (config.debug) {
|
||||||
Print(L"HackBGRT: Ready to boot. Disable debug mode to skip this screen.\n");
|
Log(-1, L"Ready to boot.\n");
|
||||||
Print(L"Press escape to cancel or any other key (or wait 15 seconds) 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) {
|
if (ReadKey(15000).ScanCode == SCAN_ESC) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!config.log) {
|
||||||
|
ClearLogVariable();
|
||||||
|
}
|
||||||
if (EFI_ERROR(BS->StartImage(next_image_handle, 0, 0))) {
|
if (EFI_ERROR(BS->StartImage(next_image_handle, 0, 0))) {
|
||||||
Print(L"HackBGRT: Failed to start %s.\n", config.boot_path);
|
Log(1, L"Failed to start %s.\n", config.boot_path);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
Print(L"HackBGRT: Started %s. Why are we still here?!\n", config.boot_path);
|
Log(1, L"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"Please check that %s is not actually HackBGRT!\n", config.boot_path);
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
fail: {
|
fail: {
|
||||||
Print(L"HackBGRT has failed. Use parameter debug=1 for details.\n");
|
Log(1, L"HackBGRT has failed.\n");
|
||||||
Print(L"Get a Windows install disk or a recovery disk to fix your boot.\n");
|
Log(-1, L"Dumping log:\n\n");
|
||||||
#ifdef GIT_DESCRIBE
|
DumpLog();
|
||||||
Print(L"HackBGRT version: " GIT_DESCRIBE L"\n");
|
Log(-1, L"If you can't boot into Windows, get install/recovery disk to fix your boot.\n");
|
||||||
#else
|
Log(-1, L"Press any key (or wait 15 seconds) to exit.\n");
|
||||||
Print(L"HackBGRT version: unknown; not an official release?\n");
|
|
||||||
#endif
|
|
||||||
Print(L"Press any key (or wait 15 seconds) to exit.\n");
|
|
||||||
ReadKey(15000);
|
ReadKey(15000);
|
||||||
return 1;
|
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_);
|
|
||||||
}
|
|
||||||
|
|||||||
10
src/sbat.c
Normal file
10
src/sbat.c
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#ifdef GIT_DESCRIBE
|
||||||
|
#define SBAT_READABLE_VERSION GIT_DESCRIBE
|
||||||
|
#else
|
||||||
|
#define SBAT_READABLE_VERSION "unknown"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const char sbat[] __attribute__ ((section (".sbat"))) =
|
||||||
|
"sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md\n"
|
||||||
|
"hackbgrt,1,Metabolix,HackBGRT," SBAT_READABLE_VERSION ",https://github.com/Metabolix/HackBGRT\n"
|
||||||
|
;
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <efi.h>
|
#include "efi.h"
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
|
|
||||||
|
|||||||
122
src/util.c
122
src/util.c
@@ -1,7 +1,5 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#include <efilib.h>
|
|
||||||
|
|
||||||
const CHAR16* TmpStr(CHAR8 *src, int length) {
|
const CHAR16* TmpStr(CHAR8 *src, int length) {
|
||||||
static CHAR16 arr[4][16];
|
static CHAR16 arr[4][16];
|
||||||
static int j;
|
static int j;
|
||||||
@@ -14,8 +12,99 @@ const CHAR16* TmpStr(CHAR8 *src, int length) {
|
|||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
UINTN NullPrint(IN CONST CHAR16 *fmt, ...) {
|
const CHAR16* TmpIntToStr(UINT32 x) {
|
||||||
return 0;
|
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 mode, IN CONST CHAR16 *fmt, ...) {
|
||||||
|
va_list args;
|
||||||
|
CHAR16 buf[256] = {0};
|
||||||
|
int buf_i = 0;
|
||||||
|
#define putchar(c) { if (buf_i < 255) { buf[buf_i++] = c; } }
|
||||||
|
va_start(args, fmt);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DumpLog(void) {
|
||||||
|
ST->ConOut->OutputString(ST->ConOut, log_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearLogVariable(void) {
|
||||||
|
RT->SetVariable(LogVarName, &LogVarGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const CHAR16* TrimLeft(const CHAR16* s) {
|
const CHAR16* TrimLeft(const CHAR16* s) {
|
||||||
@@ -71,11 +160,24 @@ void RandomSeedAuto(void) {
|
|||||||
EFI_STATUS WaitKey(UINT64 timeout_ms) {
|
EFI_STATUS WaitKey(UINT64 timeout_ms) {
|
||||||
ST->ConIn->Reset(ST->ConIn, FALSE);
|
ST->ConIn->Reset(ST->ConIn, FALSE);
|
||||||
const int ms_to_100ns = 10000;
|
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 ReadKey(UINT64 timeout_ms) {
|
||||||
EFI_INPUT_KEY key = {0};
|
EFI_INPUT_KEY key = {0};
|
||||||
|
ST->ConOut->EnableCursor(ST->ConOut, 1);
|
||||||
WaitKey(timeout_ms);
|
WaitKey(timeout_ms);
|
||||||
ST->ConIn->ReadKeyStroke(ST->ConIn, &key);
|
ST->ConIn->ReadKeyStroke(ST->ConIn, &key);
|
||||||
return key;
|
return key;
|
||||||
@@ -90,9 +192,11 @@ void* LoadFileWithPadding(EFI_FILE_HANDLE dir, const CHAR16* path, UINTN* size_p
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
EFI_FILE_INFO *info = LibFileInfo(handle);
|
UINT64 get_size = 0;
|
||||||
UINTN size = info->FileSize;
|
handle->SetPosition(handle, ~(UINT64)0);
|
||||||
FreePool(info);
|
handle->GetPosition(handle, &get_size);
|
||||||
|
handle->SetPosition(handle, 0);
|
||||||
|
UINTN size = (UINTN) get_size;
|
||||||
|
|
||||||
void* data = 0;
|
void* data = 0;
|
||||||
e = BS->AllocatePool(EfiBootServicesData, size + padding, &data);
|
e = BS->AllocatePool(EfiBootServicesData, size + padding, &data);
|
||||||
@@ -106,7 +210,7 @@ void* LoadFileWithPadding(EFI_FILE_HANDLE dir, const CHAR16* path, UINTN* size_p
|
|||||||
}
|
}
|
||||||
handle->Close(handle);
|
handle->Close(handle);
|
||||||
if (EFI_ERROR(e)) {
|
if (EFI_ERROR(e)) {
|
||||||
FreePool(data);
|
BS->FreePool(data);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (size_ptr) {
|
if (size_ptr) {
|
||||||
|
|||||||
27
src/util.h
27
src/util.h
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <efi.h>
|
#include "efi.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a short ASCII string to UCS2, store in a static array.
|
* Convert a short ASCII string to UCS2, store in a static array.
|
||||||
@@ -12,9 +12,22 @@
|
|||||||
extern const CHAR16* TmpStr(CHAR8 *src, int length);
|
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 UINTN NullPrint(IN CONST CHAR16 *fmt, ...);
|
extern void Log(int mode, IN CONST CHAR16 *fmt, ...);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump the log buffer to the screen.
|
||||||
|
*/
|
||||||
|
extern void DumpLog(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the log EFI variable, for minor RAM savings.
|
||||||
|
*/
|
||||||
|
extern void ClearLogVariable(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the greater of two numbers.
|
* Return the greater of two numbers.
|
||||||
@@ -107,3 +120,11 @@ static inline void* LoadFile(EFI_FILE_HANDLE dir, const CHAR16* path, UINTN* siz
|
|||||||
return LoadFileWithPadding(dir, path, size_ptr, 0);
|
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;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user