mirror of
https://github.com/Metabolix/HackBGRT.git
synced 2025-12-07 09:36:10 -08:00
Compare commits
151 Commits
v1.0.0
...
79ee253108
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79ee253108 | ||
|
|
82abb0c120 | ||
|
|
830db410ea | ||
|
|
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 | ||
|
|
da9909bbdd | ||
|
|
0ce904f133 | ||
|
|
62c892009a | ||
|
|
00bbfd6d82 | ||
|
|
1e566a05a0 | ||
|
|
1058f3e2b4 | ||
|
|
661758ba58 | ||
|
|
93eec1a250 | ||
|
|
33e4450d19 | ||
|
|
99bdf5a310 | ||
|
|
d2bac39680 | ||
|
|
a4c917294c | ||
|
|
d6da4b7cee | ||
|
|
f2185f624d | ||
|
|
75e64a7ef3 | ||
|
|
e5616c6cd2 | ||
|
|
ad0b71c49b | ||
|
|
e44ce9f5ee | ||
|
|
5f3c6afc23 | ||
|
|
1fa53f3f06 | ||
|
|
8301a16ca8 | ||
|
|
de9d0d984a | ||
|
|
7dbdf33ea8 | ||
|
|
dfb5b916ed | ||
|
|
267af0bd9c | ||
|
|
be5894b387 | ||
|
|
199650a567 | ||
|
|
31172f71ca | ||
|
|
990f245ac9 | ||
|
|
dfadf67a21 | ||
|
|
691fbd164b | ||
|
|
7d5f4eac17 | ||
|
|
0b5899e801 | ||
|
|
4cf12f26b2 | ||
|
|
3396a4799d | ||
|
|
d35a9abb0a | ||
|
|
85811d62a6 | ||
|
|
449dc6acc6 | ||
|
|
1980e5c05c | ||
|
|
9a59f69a28 | ||
|
|
1fffbcff2c | ||
|
|
bd7a5f3eea | ||
|
|
32643fab96 | ||
|
|
ecbca09419 | ||
|
|
fa0f846f79 | ||
|
|
b469b600ba | ||
|
|
f7fa54cfee | ||
|
|
e092c4768c | ||
|
|
7ad4762a3d | ||
|
|
cea656631a | ||
|
|
1b6b17ec9a | ||
|
|
91aad3b971 | ||
|
|
cbcb630697 | ||
|
|
8531c728e8 | ||
|
|
f255b13027 | ||
|
|
054f8cc751 | ||
|
|
24c4a8aa0c | ||
|
|
b3cc80b37b | ||
|
|
c01cf121a0 | ||
|
|
c12bd7a859 | ||
|
|
ce44c3dcb3 | ||
|
|
3b0253f6fc | ||
|
|
8921bafa90 | ||
|
|
0347a1d921 | ||
|
|
b78e5cd977 | ||
|
|
e1d51be11b | ||
|
|
19203eceed | ||
|
|
3da9e1818a | ||
|
|
b7fd08c978 | ||
|
|
ff838ec0f6 | ||
|
|
a627895bfb | ||
|
|
733acccc42 | ||
|
|
efdd91a6d8 | ||
|
|
dae1a9abce | ||
|
|
e9ccec0d76 | ||
|
|
51ccb0255e | ||
|
|
a633eeb781 | ||
|
|
b5006f7771 | ||
|
|
c49f0f6cbc | ||
|
|
8ed61047dd | ||
|
|
31323a5111 | ||
|
|
9891039b06 |
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.
|
||||
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
boot*.efi
|
||||
setup.exe
|
||||
src/GIT_DESCRIBE.cs
|
||||
html
|
||||
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
|
||||
143
CHANGELOG.md
Normal file
143
CHANGELOG.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## 2.5.1 - 2024-08-18
|
||||
|
||||
### Changed
|
||||
- Update *shim* to 15.8.
|
||||
|
||||
## 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
|
||||
|
||||
### Added
|
||||
- Check image size, crop if it's bigger than the screen.
|
||||
- Check BitLocker status to avoid unbootable machine.
|
||||
|
||||
## 2.0.0 - 2023-09-10
|
||||
|
||||
### Added
|
||||
- Log to `setup.log`.
|
||||
- Image conversion (GIF, EXIF, JPG, PNG, TIFF) to BMP during setup.
|
||||
- Quiet (batch) setup.
|
||||
- Dry run in setup.
|
||||
- EFI boot entry support in setup.
|
||||
- Orientation parameter (o=0|90|180|270) for images.
|
||||
|
||||
### Changed
|
||||
- Configure (edit config and images) before installing.
|
||||
- Escalate privileges only when needed (after the menu).
|
||||
- Try to detect and avoid some configuration errors.
|
||||
- Wait at most 15 seconds for key presses during boot.
|
||||
- Image coordinates are now relative to the center.
|
||||
|
||||
## 1.5.1 - 2018-08-11
|
||||
|
||||
### Fixed
|
||||
- Clarify the default config.txt.
|
||||
- Fix an exception in some cases when trying to boot to UEFI setup.
|
||||
|
||||
## 1.5.0 - 2017-09-30
|
||||
|
||||
### Added
|
||||
- Support for rebooting to UEFI setup.
|
||||
|
||||
### Changed
|
||||
- Minor enhancements to installer.
|
||||
|
||||
## 1.4.0 - 2017-08-29
|
||||
|
||||
### Added
|
||||
- Use UTF-8 in the configuration file.
|
||||
- Use the default boot loader path if the configured one doesn't work.
|
||||
|
||||
## 1.3.0 - 2016-12-22
|
||||
|
||||
### Added
|
||||
- Check Secure Boot status before installing.
|
||||
|
||||
## 1.2.0 - 2016-06-05
|
||||
|
||||
### Added
|
||||
- Better installer, setup.exe.
|
||||
- Support for low-end machines with 32-bit IA-32 UEFI.
|
||||
- Support for changing resolution.
|
||||
- Version information in the program.
|
||||
- Change log.
|
||||
|
||||
### Removed
|
||||
- Removed old install scripts, install.bat and uninstall.bat.
|
||||
|
||||
## 1.1.0 - 2016-05-14
|
||||
|
||||
### Changed
|
||||
- Wait for input before booting if debug=1 is set.
|
||||
|
||||
### Fixed
|
||||
- Fix handling of multiple BGRT entries.
|
||||
- Fix ACPI table checksums.
|
||||
|
||||
## 1.0.0 - 2016-05-11
|
||||
|
||||
### Added
|
||||
- Easy-to-use installation script.
|
||||
- Git repository for the project.
|
||||
|
||||
## 0.2.0 - 2016-04-26
|
||||
|
||||
### Added
|
||||
- Support for randomly alternating images.
|
||||
- Support for black background.
|
||||
- Support for the native Windows logo.
|
||||
|
||||
### Changed
|
||||
- New configuration file format for images.
|
||||
|
||||
## 0.1.0 - 2016-01-15
|
||||
|
||||
### Added
|
||||
- Support for loading a bitmap and updating the BGRT.
|
||||
- Support for loading the next boot loader.
|
||||
- Support for a configuration file.
|
||||
3
Doxyfile
3
Doxyfile
@@ -1,8 +1,9 @@
|
||||
INPUT = src
|
||||
FILE_PATTERNS = *.c *.h
|
||||
FILE_PATTERNS = *.c *.h *.cs
|
||||
JAVADOC_AUTOBRIEF = YES
|
||||
EXTRACT_ALL = YES
|
||||
EXTRACT_STATIC = YES
|
||||
EXTRACT_PRIVATE = YES
|
||||
STRIP_CODE_COMMENTS = NO
|
||||
INLINE_SOURCES = NO
|
||||
GENERATE_HTML = YES
|
||||
|
||||
147
Makefile
147
Makefile
@@ -1,27 +1,136 @@
|
||||
CC = $(CC_PREFIX)-gcc
|
||||
CFLAGS = -std=c11 -O2 -ffreestanding -mno-red-zone -fno-stack-protector -Wshadow -Wall -Wunused -Werror-implicit-function-declaration -Werror
|
||||
CC = clang
|
||||
CFLAGS = -target $(CLANG_TARGET) -ffreestanding -fshort-wchar
|
||||
CFLAGS += -std=c17 -Wshadow -Wall -Wunused -Werror-implicit-function-declaration
|
||||
CFLAGS += -I$(GNUEFI_INC) -I$(GNUEFI_INC)/$(GNUEFI_ARCH) -I$(GNUEFI_INC)/protocol
|
||||
LDFLAGS = -nostdlib -shared -Wl,-dll -Wl,--subsystem,10 -e _EfiMain
|
||||
LIBS = -L$(GNUEFI_LIB) -lefi -lgcc
|
||||
CFLAGS += $(ARCH_CFLAGS)
|
||||
LDFLAGS = -target $(CLANG_TARGET) -nostdlib -Wl,-entry:efi_main -Wl,-subsystem:efi_application -fuse-ld=lld
|
||||
ARCH_CFLAGS = -O2 -mno-red-zone
|
||||
|
||||
GNUEFI_INC = /usr/$(CC_PREFIX)/include/efi
|
||||
GNUEFI_LIB = /usr/$(CC_PREFIX)/lib
|
||||
GNUEFI_INC = gnu-efi/inc
|
||||
|
||||
FILES_C = src/main.c src/util.c src/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_CS = src/Setup.cs src/Esp.cs src/Efi.cs src/EfiBootEntries.cs
|
||||
|
||||
.PHONY: all
|
||||
all: bootx64.efi
|
||||
# Generate version number from git describe.
|
||||
# In the numeric form, add the number of commits as the last part.
|
||||
# (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))
|
||||
|
||||
bootx64.efi: CC_PREFIX = x86_64-w64-mingw32
|
||||
bootx64.efi: GNUEFI_ARCH = x86_64
|
||||
bootx64.efi: $(FILES_C)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) -s
|
||||
define GIT_DESCRIBE_CS
|
||||
public class GIT_DESCRIBE {
|
||||
public const string data = "$(GIT_DESCRIBE)";
|
||||
public const string numeric = "$(GIT_DESCRIBE_NUMERIC)";
|
||||
}
|
||||
endef
|
||||
|
||||
bootia32.efi: CC_PREFIX = i686-w64-mingw32
|
||||
bootia32.efi: GNUEFI_ARCH = ia32
|
||||
bootia32.efi: $(FILES_C)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) -s
|
||||
CFLAGS += '-DGIT_DESCRIBE_W=L"$(GIT_DESCRIBE)"' '-DGIT_DESCRIBE="$(GIT_DESCRIBE)"'
|
||||
RELEASE_NAME = HackBGRT-$(GIT_DESCRIBE:v%=%)
|
||||
|
||||
HackBGRT.tar.xz: bootx64.efi config.txt splash.bmp install.bat uninstall.bat README.md README.efilib LICENSE
|
||||
tar cJf $@ --transform=s,^,HackBGRT/, $^
|
||||
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."
|
||||
|
||||
setup: setup.exe
|
||||
|
||||
release: release/$(RELEASE_NAME).zip
|
||||
@echo "Current version is packaged: $<"
|
||||
|
||||
release/$(RELEASE_NAME): $(EFI_SIGNED_FILES) certificate.cer config.txt splash.bmp setup.exe README.md CHANGELOG.md README.efilib LICENSE shim-signed/* shim.md
|
||||
rm -rf $@
|
||||
tar c --transform=s,^,$@/, $^ | tar x
|
||||
|
||||
release/$(RELEASE_NAME).zip: release/$(RELEASE_NAME)
|
||||
rm -rf $@
|
||||
(cd release; 7z a -mx=9 "$(RELEASE_NAME).zip" "$(RELEASE_NAME)" -bd -bb1)
|
||||
|
||||
src/GIT_DESCRIBE.cs: $(FILES_CS) $(FILES_C) $(FILES_H)
|
||||
$(file > $@,$(GIT_DESCRIBE_CS))
|
||||
|
||||
setup.exe: $(FILES_CS) src/GIT_DESCRIBE.cs
|
||||
csc -nologo -define:GIT_DESCRIBE -out:$@ $^
|
||||
|
||||
certificate.cer pki:
|
||||
@echo
|
||||
@echo "You need proper keys to sign the EFI executables."
|
||||
@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
|
||||
|
||||
efi-signed/%.efi: efi/%.efi pki
|
||||
@mkdir -p efi-signed
|
||||
pesign --force -n pki -i $< -o $@ -c HackBGRT-signer -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)
|
||||
|
||||
132
README.md
132
README.md
@@ -4,45 +4,131 @@ HackBGRT is intended as a boot logo changer for UEFI-based Windows systems.
|
||||
|
||||
## Summary
|
||||
|
||||
When booting on a UEFI-based computer, Windows may show a vendor-defined logo which is stored on the UEFI firmware in a section called Boot Graphics Resource Table (BGRT). It's usually very difficult to change the image permamently, but a custom UEFI application may be used to overwrite it during the boot. HackBGRT does exactly that.
|
||||
When booting on a UEFI-based computer, Windows may show a vendor-defined logo which is stored on the UEFI firmware in a section called Boot Graphics Resource Table (BGRT). It's usually very difficult to change the image permanently, but a custom UEFI application may be used to overwrite it during the boot. HackBGRT does exactly that.
|
||||
|
||||
**Note:** The original logo is often visible for a moment before HackBGRT is started. This is expected, please do not report this "bug". This can't be changed without modifying computer firmware, which this project will not do.
|
||||
|
||||
## Usage
|
||||
|
||||
**Important:** If you mess up the installation, your system may become unbootable! Create a rescue disk before use. This software comes with no warranty. Use at your own risk.
|
||||
|
||||
* Make sure that your computer is booting with UEFI.
|
||||
* Make sure that you have a 64-bit x86-64 processor.
|
||||
* Make sure that Secure Boot is disabled, or learn to sign EFI applications.
|
||||
* Simple Windows installation:
|
||||
* Get at least these files: `bootx64.efi`, `config.txt`, `install.bat`, `splash.bmp`.
|
||||
* Run Command Prompt as Administrator.
|
||||
* Run `install.bat` from the Command Prompt.
|
||||
* The installer will launch Paint for creating the image(s).
|
||||
* The installer will launch Notepad for modifying the configuration.
|
||||
* If Windows later reinstalls the original boot loader, run `install.bat` again.
|
||||
* Installation for Windows with another boot loader (e.g. GRUB):
|
||||
* Copy the mentioned files to `[EFI System Partition]\EFI\HackBGRT\`.
|
||||
* Set `boot=\EFI\Microsoft\Boot\bootmgfw.efi` in `config.txt`.
|
||||
* Point your boot loader to `\EFI\HackBGRT\bootx64.efi`.
|
||||
* Installation for all operating systems:
|
||||
* Copy the mentioned files to `[EFI System Partition]\EFI\HackBGRT\`.
|
||||
* Set `boot=` to your preferred boot loader in `config.txt`.
|
||||
* Set `\EFI\HackBGRT\bootx64.efi` as your default boot loader with `efibootmgr` or some other EFI boot manager tool.
|
||||
* Make sure that you have read the Secure Boot instructions.
|
||||
* 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
|
||||
|
||||
* Get the latest release from the Releases page.
|
||||
* Start `setup.exe` and follow the instructions.
|
||||
* The installer will launch Paint for editing the image, or you can edit it otherwise.
|
||||
* 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
|
||||
|
||||
* 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:
|
||||
* `install` – copy the files but don't enable.
|
||||
* `enable-bcdedit` – use `bcdedit` to create a new 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.
|
||||
* `disable-overwrite` – restore the MS boot loader.
|
||||
* `skip-shim` – skip *shim* when installing.
|
||||
* `allow-secure-boot` – ignore Secure Boot in subsequent commands.
|
||||
* `allow-bitlocker` – ignore BitLocker in subsequent commands.
|
||||
* `allow-bad-loader` – ignore bad boot loader configuration in subsequent commands.
|
||||
* `disable` – run all relevant `disable-*` commands.
|
||||
* `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.
|
||||
|
||||
### Multi-boot configurations
|
||||
|
||||
If you only need HackBGRT for Windows:
|
||||
|
||||
* Run `setup.exe`, install files without enabling.
|
||||
* Configure your boot loader to start `\EFI\HackBGRT\loader.efi`.
|
||||
|
||||
If you need it for other systems as well:
|
||||
|
||||
* Configure HackBGRT to start your boot loader (such as systemd-boot): `boot=\EFI\systemd\systemd-bootx64.efi`.
|
||||
* Run `setup.exe`, install as a new EFI boot entry.
|
||||
|
||||
To install purely on Linux, you can install with `setup.exe dry-run` and then manually copy files from `dry-run/EFI` to your `[EFI System Partition]/EFI`. For further instructions, consult the documentation of your own Linux system.
|
||||
|
||||
## Configuration
|
||||
|
||||
The configuration options are described in `config.txt`, which should be stored in `[EFI System Partition]\EFI\HackBGRT\config.txt`.
|
||||
The configuration options are described in `config.txt`, which the installer copies into `[EFI System Partition]\EFI\HackBGRT\config.txt`.
|
||||
|
||||
## Images
|
||||
|
||||
The image path can be changed in the configuration file. The default path is `[EFI System Partition]\EFI\HackBGRT\splash.bmp`.
|
||||
If you only need one image, just edit `splash.bmp` to your needs.
|
||||
|
||||
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. 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.
|
||||
|
||||
Multiple images may be specified, in which case one is picked at random.
|
||||
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.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Verification failed, Security violation
|
||||
|
||||
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
|
||||
|
||||
* Compiler: GCC targeting w64-mingw32
|
||||
* Compiler: Clang
|
||||
* Compiler flags: see Makefile
|
||||
* Libraries: gnu-efi
|
||||
|
||||
BIN
config.txt
Executable file → Normal file
BIN
config.txt
Executable file → Normal file
Binary file not shown.
1
gnu-efi
Submodule
1
gnu-efi
Submodule
Submodule gnu-efi added at 74bd9b60ba
120
install.bat
120
install.bat
@@ -1,120 +0,0 @@
|
||||
@ECHO OFF
|
||||
CD %~dp0
|
||||
|
||||
IF NOT "%1" == "uninstall" (
|
||||
IF NOT EXIST bootx64.efi (
|
||||
ECHO Missing bootx64.efi, you're doing something wrong.
|
||||
GOTO fail_before_esp
|
||||
)
|
||||
)
|
||||
|
||||
SET ESP_UNMOUNT=1
|
||||
SET ESP=-
|
||||
FOR /F "delims=" %%I IN ('CMD /C "MOUNTVOL | FINDSTR /C:EFI | FINDSTR /C::"') DO (
|
||||
ECHO %%I
|
||||
SET ESP_STR=%%I
|
||||
SET ESP=%ESP_STR:~-3,2%
|
||||
SET ESP_UNMOUNT=0
|
||||
)
|
||||
IF %ESP% == - MOUNTVOL S: /S >NUL && SET ESP=S:
|
||||
IF %ESP% == - MOUNTVOL B: /S >NUL && SET ESP=B:
|
||||
IF %ESP% == - MOUNTVOL A: /S >NUL && SET ESP=A:
|
||||
IF %ESP% == - MOUNTVOL X: /S >NUL && SET ESP=X:
|
||||
|
||||
IF %ESP% == - (
|
||||
ECHO The EFI System Partition is not mounted.
|
||||
GOTO fail_before_esp
|
||||
)
|
||||
|
||||
SET HackBGRT=%ESP%\EFI\HackBGRT
|
||||
SET MSBOOT=%ESP%\EFI\Microsoft\Boot
|
||||
|
||||
IF NOT EXIST %MSBOOT% (
|
||||
ECHO %MSBOOT% does not exist.
|
||||
ECHO If the path seems incorrect, report a bug.
|
||||
GOTO fail
|
||||
)
|
||||
|
||||
IF "%1" == "uninstall" (
|
||||
IF NOT EXIST %HackBGRT%\bootmgfw-original.efi (
|
||||
ECHO Missing %HackBGRT%\bootmgfw-original.efi!
|
||||
GOTO fail
|
||||
)
|
||||
COPY %HackBGRT%\bootmgfw-original.efi %MSBOOT%\bootmgfw.efi >NUL || (
|
||||
ECHO Failed to restore the original bootmgfw.efi.
|
||||
GOTO fail
|
||||
)
|
||||
ECHO The original bootmgfw.efi has been restored.
|
||||
IF EXIST %HackBGRT% (
|
||||
DEL /P %HackBGRT%
|
||||
)
|
||||
EXIT /B
|
||||
)
|
||||
|
||||
IF NOT EXIST %HackBGRT% (
|
||||
MKDIR %HackBGRT%
|
||||
)
|
||||
IF NOT EXIST %HackBGRT%\bootmgfw-original.efi (
|
||||
COPY %MSBOOT%\bootmgfw.efi %HackBGRT%\bootmgfw-original.efi >NUL || (
|
||||
ECHO Couldn't copy the original bootmgfw.efi.
|
||||
GOTO fail
|
||||
)
|
||||
)
|
||||
|
||||
ECHO Copying files...
|
||||
COPY /Y LICENSE %HackBGRT%\ >NUL
|
||||
COPY /Y README.md %HackBGRT%\ >NUL
|
||||
COPY /Y README.efilib %HackBGRT%\ >NUL
|
||||
COPY /Y install.bat %HackBGRT%\ >NUL
|
||||
COPY /Y uninstall.bat %HackBGRT%\ >NUL
|
||||
COPY /Y bootx64.efi %HackBGRT%\ >NUL || GOTO fail
|
||||
IF NOT EXIST %HackBGRT%\splash.bmp (
|
||||
COPY splash.bmp %HackBGRT%\ >NUL || GOTO fail
|
||||
)
|
||||
IF EXIST %HackBGRT%\config.txt (
|
||||
ECHO Copying configuration as config-new.txt.
|
||||
ECHO Be sure to check for any format changes!
|
||||
COPY /Y config.txt %HackBGRT%\config-new.txt >NUL || GOTO fail
|
||||
) ELSE (
|
||||
COPY /Y config.txt %HackBGRT%\config.txt >NUL || GOTO fail
|
||||
)
|
||||
|
||||
ECHO Draw or copy your preferred image to splash.bmp.
|
||||
START /WAIT mspaint %HackBGRT%\splash.bmp
|
||||
|
||||
ECHO Check the configuration in config.txt.
|
||||
IF EXIST %HackBGRT%\config-new.txt (
|
||||
ECHO See config-new.txt for reference.
|
||||
START notepad %HackBGRT%\config-new.txt
|
||||
)
|
||||
START /WAIT notepad %HackBGRT%\config.txt
|
||||
|
||||
ECHO Replacing bootmgfw.efi.
|
||||
COPY /Y bootx64.efi %MSBOOT%\bootmgfw.efi >NUL || (
|
||||
ECHO Failed to copy the boot loader!
|
||||
ECHO Restoring the original bootmgfw.efi...
|
||||
COPY %HackBGRT%\bootmgfw-original.efi %MSBOOT%\bootmgfw.efi >NUL || (
|
||||
ECHO Restoration failed You will need to fix this!
|
||||
)
|
||||
GOTO fail
|
||||
)
|
||||
|
||||
IF %ESP_UNMOUNT% == 1 (
|
||||
MOUNTVOL %ESP% /D
|
||||
)
|
||||
|
||||
ECHO Installation is ready.
|
||||
ECHO If your CPU is not x86-64, you should definitely uninstall now.
|
||||
ECHO Remember to disable Secure Boot, or HackBGRT will not boot.
|
||||
PAUSE
|
||||
EXIT /B
|
||||
|
||||
:fail
|
||||
IF %ESP_UNMOUNT% == 1 (
|
||||
MOUNTVOL %ESP% /D
|
||||
)
|
||||
|
||||
:fail_before_esp
|
||||
ECHO Exiting due to errors.
|
||||
PAUSE
|
||||
EXIT /B 1
|
||||
30
shim-signed/COPYRIGHT
Normal file
30
shim-signed/COPYRIGHT
Normal file
@@ -0,0 +1,30 @@
|
||||
Copyright 2012 Red Hat, Inc <mjg@redhat.com>
|
||||
|
||||
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.
|
||||
|
||||
Significant portions of this code are derived from Tianocore
|
||||
(http://tianocore.sf.net) and are Copyright 2009-2012 Intel
|
||||
Corporation.
|
||||
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.
|
||||
298
src/Efi.cs
Normal file
298
src/Efi.cs
Normal file
@@ -0,0 +1,298 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using Microsoft.Win32;
|
||||
|
||||
/**
|
||||
* Methods for handling EFI variables.
|
||||
*/
|
||||
public class Efi {
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern UInt32 GetFirmwareEnvironmentVariableEx(string lpName, string lpGuid, [MarshalAs(UnmanagedType.LPArray)] byte[] pBuffer, UInt32 nSize, out UInt32 pdwAttributes);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern UInt32 SetFirmwareEnvironmentVariableEx(string lpName, string lpGuid, [MarshalAs(UnmanagedType.LPArray)] byte[] pBuffer, UInt32 nSize, UInt32 pdwAttributes);
|
||||
|
||||
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
|
||||
private static extern bool AdjustTokenPrivileges(IntPtr htoken, bool disable, ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);
|
||||
|
||||
[DllImport("kernel32.dll", ExactSpelling = true)]
|
||||
private static extern IntPtr GetCurrentProcess();
|
||||
|
||||
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
|
||||
private static extern bool OpenProcessToken(IntPtr h, int acc, out IntPtr phtok);
|
||||
|
||||
[DllImport("advapi32.dll", SetLastError = true)]
|
||||
private static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool CloseHandle(IntPtr hObject);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern UInt32 GetSystemFirmwareTable(UInt32 provider, UInt32 table, [MarshalAs(UnmanagedType.LPArray)] byte[] buffer, UInt32 len);
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
private struct TokPriv1Luid {
|
||||
public int Count;
|
||||
public long Luid;
|
||||
public int Attr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Information about an EFI variable.
|
||||
*/
|
||||
public class Variable {
|
||||
public string Name, Guid;
|
||||
public UInt32 Attributes;
|
||||
public byte[] Data;
|
||||
|
||||
/**
|
||||
* Convert to string.
|
||||
*
|
||||
* @return String representation of this object.
|
||||
*/
|
||||
public override string ToString() {
|
||||
if (Data == null) {
|
||||
return $"{Name} Guid={Guid} Attributes={Attributes} Data=null";
|
||||
}
|
||||
var hex = BitConverter.ToString(Data).Replace("-", " ");
|
||||
var text = new string(Data.Select(c => 0x20 <= c && c <= 0x7f ? (char) c : ' ').ToArray());
|
||||
return $"{Name} Guid={Guid} Attributes={Attributes} Text='{text}' Bytes='{hex}'";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GUID of the global EFI variables.
|
||||
*/
|
||||
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";
|
||||
|
||||
/**
|
||||
* Enable the privilege to access EFI variables.
|
||||
*/
|
||||
public static void EnablePrivilege() {
|
||||
if (Directory.Exists(LinuxEfiDir)) {
|
||||
var linuxEfiFile = $"{LinuxEfiDir}/BootOrder-8be4df61-93ca-11d2-aa0d-00e098032b8c";
|
||||
if (File.Exists(linuxEfiFile)) {
|
||||
using (FileStream fs = File.OpenWrite(linuxEfiFile)) {
|
||||
// OpenWrite throws an exception on error.
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const int SE_PRIVILEGE_ENABLED = 0x00000002;
|
||||
const int TOKEN_QUERY = 0x00000008;
|
||||
const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
|
||||
const string SE_SYSTEM_ENVIRONMENT_NAME = "SeSystemEnvironmentPrivilege";
|
||||
IntPtr htoken = IntPtr.Zero;
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, out htoken)) {
|
||||
throw new Exception("EnablePrivilege: OpenProcessToken failed: " + Marshal.GetLastWin32Error());
|
||||
}
|
||||
try {
|
||||
TokPriv1Luid tp;
|
||||
tp.Count = 1;
|
||||
tp.Luid = 0;
|
||||
tp.Attr = SE_PRIVILEGE_ENABLED;
|
||||
if (!LookupPrivilegeValue(null, SE_SYSTEM_ENVIRONMENT_NAME, ref tp.Luid)) {
|
||||
throw new Exception("EnablePrivilege: LookupPrivilegeValue failed: " + Marshal.GetLastWin32Error());
|
||||
}
|
||||
if (!AdjustTokenPrivileges(htoken, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero)) {
|
||||
throw new Exception("EnablePrivilege: AdjustTokenPrivileges failed: " + Marshal.GetLastWin32Error());
|
||||
}
|
||||
} finally {
|
||||
CloseHandle(htoken);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an EFI variable.
|
||||
*
|
||||
* @param name Name of the EFI variable.
|
||||
* @param guid GUID of the EFI variable.
|
||||
* @return Information about the EFI variable.
|
||||
*/
|
||||
public static Variable GetVariable(string name, string guid = EFI_GLOBAL_GUID) {
|
||||
Variable result = new Variable();
|
||||
result.Name = name;
|
||||
result.Guid = guid;
|
||||
result.Data = null;
|
||||
result.Attributes = 0;
|
||||
|
||||
if (Directory.Exists(LinuxEfiDir)) {
|
||||
var linuxEfiFile = $"{LinuxEfiDir}/{name}-{guid.Substring(1, guid.Length - 2)}";
|
||||
if (File.Exists(linuxEfiFile)) {
|
||||
var d = File.ReadAllBytes(linuxEfiFile);
|
||||
result.Attributes = (UInt32)(d[0] + 0x100 * d[1] + 0x10000 * d[2] + 0x1000000 * d[3]);
|
||||
result.Data = d.Skip(4).ToArray();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
for (UInt32 i = 4096; i <= 1024*1024; i *= 2) {
|
||||
byte[] buf = new byte[i];
|
||||
UInt32 len = GetFirmwareEnvironmentVariableEx(name, guid, buf, (UInt32) buf.Length, out result.Attributes);
|
||||
if (len == buf.Length) {
|
||||
continue;
|
||||
}
|
||||
if (len > 0 || Marshal.GetLastWin32Error() == 0) {
|
||||
result.Data = new byte[len];
|
||||
Array.Copy(buf, 0, result.Data, 0, len);
|
||||
return result;
|
||||
}
|
||||
switch (len != 0 ? 0 : Marshal.GetLastWin32Error()) {
|
||||
case 203:
|
||||
// Not found.
|
||||
return result;
|
||||
case 87:
|
||||
throw new Exception("GetVariable: Invalid parameter");
|
||||
case 1314:
|
||||
throw new Exception("GetVariable: Privilege not held");
|
||||
default:
|
||||
throw new Exception("GetVariable: error " + Marshal.GetLastWin32Error());
|
||||
}
|
||||
}
|
||||
throw new Exception("GetFirmwareEnvironmentVariable: too big data");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an EFI variable.
|
||||
*
|
||||
* @param v Information of the variable.
|
||||
* @param dryRun Don't actually set the variable.
|
||||
*/
|
||||
public static void SetVariable(Variable v, bool dryRun = false) {
|
||||
Setup.WriteLine($"Writing EFI variable {v.Name} (see log for details)");
|
||||
Setup.Log($"Writing EFI variable: {v}");
|
||||
if (dryRun) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Directory.Exists(LinuxEfiDir)) {
|
||||
var linuxEfiFile = $"{LinuxEfiDir}/{v.Name}-{v.Guid.Substring(1, v.Guid.Length - 2)}";
|
||||
var a = v.Attributes;
|
||||
var b = new byte[] { (byte) a, (byte) (a >> 8), (byte) (a >> 16), (byte) (a >> 24) };
|
||||
// FIXME: Just writing won't work: File.WriteAllBytes(linuxEfiFile, b.Concat(v.Data).ToArray());
|
||||
Setup.WriteLine("FIXME: Can't yet write EFI variables in Linux.");
|
||||
return;
|
||||
}
|
||||
|
||||
UInt32 r = SetFirmwareEnvironmentVariableEx(v.Name, v.Guid, v.Data, (UInt32) v.Data.Length, v.Attributes);
|
||||
if (r == 0) {
|
||||
switch (Marshal.GetLastWin32Error()) {
|
||||
case 87:
|
||||
throw new Exception("SetVariable: Invalid parameter");
|
||||
case 1314:
|
||||
throw new Exception("SetVariable: Privilege not held");
|
||||
default:
|
||||
throw new Exception("SetVariable: error " + Marshal.GetLastWin32Error());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Secure Boot is enabled.
|
||||
*
|
||||
* @return 0 for disabled, 1 for enabled, other for unknown.
|
||||
*/
|
||||
public static int GetSecureBootStatus() {
|
||||
// GetVariable("SecureBoot") reports always 1 (on Lenovo E335).
|
||||
// Windows registry seems to work better, though.
|
||||
try {
|
||||
return (int) Registry.GetValue(
|
||||
"HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\SecureBoot\\State",
|
||||
"UEFISecureBootEnabled",
|
||||
-1
|
||||
);
|
||||
} catch {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if it's possible to reboot to EFI setup.
|
||||
*
|
||||
* @return True, if it's possible to reboot to EFI setup.
|
||||
*/
|
||||
public static bool CanBootToFW() {
|
||||
try {
|
||||
Variable tmp = GetVariable("OsIndicationsSupported");
|
||||
return tmp.Data != null && (tmp.Data[0] & 1) != 0;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark that the next reboot should go to EFI setup.
|
||||
*/
|
||||
public static void SetBootToFW() {
|
||||
Variable tmp = GetVariable("OsIndications");
|
||||
if (tmp.Data == null) {
|
||||
tmp.Data = new byte[8];
|
||||
tmp.Attributes = 7;
|
||||
}
|
||||
tmp.Data[0] |= 1;
|
||||
SetVariable(tmp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert bytes into UInt16 values.
|
||||
*
|
||||
* @param bytes The byte array.
|
||||
* @return An enumeration of UInt16 values.
|
||||
*/
|
||||
public static IEnumerable<UInt16> BytesToUInt16s(byte[] bytes) {
|
||||
// TODO: return bytes.Chunk(2).Select(b => (UInt16) (b[0] + 0x100 * b[1])).ToArray();
|
||||
return Enumerable.Range(0, bytes.Length / 2).Select(i => (UInt16) (bytes[2 * i] + 0x100 * bytes[2 * i + 1]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve HackBGRT log collected during boot.
|
||||
*/
|
||||
public static string GetHackBGRTLog() {
|
||||
try {
|
||||
var log = GetVariable("HackBGRTLog", EFI_HACKBGRT_GUID);
|
||||
if (log.Data == null) {
|
||||
return "Log is empty.";
|
||||
}
|
||||
return new string(BytesToUInt16s(log.Data).Select(i => (char)i).ToArray());
|
||||
} catch (Exception e) {
|
||||
return $"Log not found: {e.ToString()}";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the BGRT table (for debugging).
|
||||
*/
|
||||
public static void LogBGRT() {
|
||||
try {
|
||||
const UInt32 acpiBE = 0x41435049, bgrtLE = 0x54524742;
|
||||
UInt32 size = GetSystemFirmwareTable(acpiBE, bgrtLE, null, 0);
|
||||
byte[] buf = new byte[size];
|
||||
var ret = GetSystemFirmwareTable(acpiBE, bgrtLE, buf, size);
|
||||
if (ret == size) {
|
||||
var hex = BitConverter.ToString(buf).Replace("-", " ");
|
||||
Setup.Log($"LogBGRT: {size} bytes: {hex}");
|
||||
} else if (ret == 0) {
|
||||
Setup.Log($"LogBGRT: Win32Error {Marshal.GetLastWin32Error()}");
|
||||
} else {
|
||||
Setup.Log($"LogBGRT: Size problems: spec {0x38}, buf {size}, ret {ret}");
|
||||
}
|
||||
} catch (Exception 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()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
129
src/Esp.cs
Normal file
129
src/Esp.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
/**
|
||||
* EFI System Partition mounter.
|
||||
*/
|
||||
public sealed class Esp {
|
||||
/** The singleton instance of this class, if ESP is mounted. */
|
||||
private static Esp MountInstance;
|
||||
|
||||
/** EFI System Partition location. */
|
||||
public static string Location { get; private set; }
|
||||
|
||||
/** MS boot loader path on ESP. */
|
||||
public static string MsLoaderPath {
|
||||
get {
|
||||
return Path.Combine(new string[] { Location, "EFI", "Microsoft", "Boot", "bootmgfw.efi"});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor: do nothing.
|
||||
*/
|
||||
private Esp() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor: unmount.
|
||||
*/
|
||||
~Esp() {
|
||||
if (this == MountInstance) {
|
||||
Unmount();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to find ESP at a path.
|
||||
*
|
||||
* @param tryPath The new path to try.
|
||||
* @param requireMsLoader Look for MS boot loader specifically?
|
||||
* @return true if the path was given and seems valid, false otherwise.
|
||||
*/
|
||||
public static bool TryPath(string tryPath, bool requireMsLoader = true) {
|
||||
if (MountInstance != null && Location != null) {
|
||||
Unmount();
|
||||
}
|
||||
Location = tryPath;
|
||||
if (Location != null && Location != "") {
|
||||
if (File.Exists(MsLoaderPath)) {
|
||||
Setup.Log($"Esp.TryPath: {Location} has MS boot loader");
|
||||
return true;
|
||||
}
|
||||
if (Directory.Exists(Path.Combine(Location, "EFI"))) {
|
||||
Setup.Log($"Esp.TryPath: {Location} has EFI directory but no loader");
|
||||
if (!requireMsLoader) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Location = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the EFI System Partition, if it's already mounted.
|
||||
*
|
||||
* @return true if the drive was found.
|
||||
*/
|
||||
public static bool Find() {
|
||||
if (MountInstance != null) {
|
||||
return true;
|
||||
}
|
||||
Setup.Log("Esp.Find()");
|
||||
try {
|
||||
// Match "The EFI System Partition is mounted at E:\" with some language support.
|
||||
var re = new Regex(" EFI[^\n]*(?:\n[ \t]*)?([A-Z]:\\\\)");
|
||||
var m = re.Match(Setup.Execute("mountvol", "", false));
|
||||
if (m.Success && TryPath(m.Groups[1].Captures[0].Value)) {
|
||||
return true;
|
||||
}
|
||||
Setup.Log("Esp.Find: no match");
|
||||
} catch (Exception e) {
|
||||
Setup.Log($"Esp.Find: {e.ToString()}");
|
||||
}
|
||||
for (char c = 'A'; c <= 'Z'; ++c) {
|
||||
if (TryPath(c + ":\\")) {
|
||||
Setup.Log($"Esp.Find: found {c}");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Setup.Log("Esp.Find: not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mount the EFI System Partition.
|
||||
*
|
||||
* @return true if the drive was mounted, false otherwise.
|
||||
*/
|
||||
public static bool Mount() {
|
||||
if (MountInstance != null) {
|
||||
return true;
|
||||
}
|
||||
for (char c = 'A'; c <= 'Z'; ++c) {
|
||||
Setup.Log($"Esp.Mount: {c}");
|
||||
if (Setup.Execute("mountvol", c + ": /S", true) != null) {
|
||||
MountInstance = new Esp();
|
||||
if (TryPath(c + ":\\", false)) {
|
||||
return true;
|
||||
} else {
|
||||
throw new Exception("Mounted ESP at " + c + ":\\ but it seems to be invalid!");
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmount the EFI System Partition, if necessary.
|
||||
*/
|
||||
private static void Unmount() {
|
||||
if (MountInstance != null) {
|
||||
Setup.Execute("mountvol", Location + " /D", true);
|
||||
Location = null;
|
||||
MountInstance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
1101
src/Setup.cs
Normal file
1101
src/Setup.cs
Normal file
File diff suppressed because it is too large
Load Diff
117
src/config.c
117
src/config.c
@@ -1,17 +1,56 @@
|
||||
#include "config.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <efilib.h>
|
||||
|
||||
BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, const CHAR16* path) {
|
||||
CHAR16* str = 0;
|
||||
UINTN str_bytes = 0;
|
||||
str = LoadFileWithPadding(root_dir, path, &str_bytes, sizeof(*str));
|
||||
if (!str) {
|
||||
Print(L"HackBGRT: Failed to load configuration (%s)!\n", path);
|
||||
BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE base_dir, const CHAR16* path) {
|
||||
void* data = 0;
|
||||
UINTN data_bytes = 0;
|
||||
data = LoadFileWithPadding(base_dir, path, &data_bytes, 4);
|
||||
if (!data) {
|
||||
Log(1, L"Failed to load configuration (%s)!\n", path);
|
||||
return FALSE;
|
||||
}
|
||||
UINTN str_len = str_bytes / sizeof(*str);
|
||||
CHAR16* str;
|
||||
UINTN str_len;
|
||||
if (*(CHAR16*)data == 0xfeff) {
|
||||
// UCS-2
|
||||
str = data;
|
||||
str_len = data_bytes / sizeof(*str);
|
||||
} else {
|
||||
// UTF-8 -> UCS-2
|
||||
EFI_STATUS e = BS->AllocatePool(EfiBootServicesData, data_bytes * 2 + 2, (void**)&str);
|
||||
if (EFI_ERROR(e)) {
|
||||
BS->FreePool(data);
|
||||
return FALSE;
|
||||
}
|
||||
UINT8* str0 = data;
|
||||
for (UINTN i = str_len = 0; i < data_bytes;) {
|
||||
UINTN unicode = 0xfffd;
|
||||
if (str0[i] < 0x80) {
|
||||
unicode = str0[i];
|
||||
i += 1;
|
||||
} else if (str0[i] < 0xc0) {
|
||||
i += 1;
|
||||
} else if (str0[i] < 0xe0) {
|
||||
unicode = ((str0[i] & 0x1f) << 6) | ((str0[i+1] & 0x3f) << 0);
|
||||
i += 2;
|
||||
} else if (str0[i] < 0xf0) {
|
||||
unicode = ((str0[i] & 0x0f) << 12) | ((str0[i+1] & 0x3f) << 6) | ((str0[i+2] & 0x3f) << 0);
|
||||
i += 3;
|
||||
} else if (str0[i] < 0xf8) {
|
||||
unicode = ((str0[i] & 0x07) << 18) | ((str0[i+1] & 0x3f) << 12) | ((str0[i+2] & 0x3f) << 6) | ((str0[i+3] & 0x3f) << 0);
|
||||
i += 4;
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
if (unicode <= 0xffff) {
|
||||
str[str_len++] = unicode;
|
||||
} else {
|
||||
str[str_len++] = 0xfffd;
|
||||
}
|
||||
}
|
||||
str[str_len] = 0;
|
||||
BS->FreePool(data);
|
||||
}
|
||||
|
||||
for (int i = 0; i < str_len;) {
|
||||
int j = i;
|
||||
@@ -22,42 +61,42 @@ BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir,
|
||||
str[j] = 0;
|
||||
++j;
|
||||
}
|
||||
ReadConfigLine(config, root_dir, &str[i]);
|
||||
ReadConfigLine(config, base_dir, &str[i]);
|
||||
i = j;
|
||||
}
|
||||
// NOTICE: string is not freed, because paths are not copied.
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void SetBMPWithRandom(struct HackBGRT_config* config, int weight, enum HackBGRT_action action, int x, int y, 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;
|
||||
UINT32 random = Random();
|
||||
UINT32 limit = 0xfffffffful / config->image_weight_sum * weight;
|
||||
if (config->debug) {
|
||||
Print(L"HackBGRT: weight %d, action %d, x %d, y %d, path %s, random = %08x, limit = %08x\n", weight, action, x, y, path, random, limit);
|
||||
}
|
||||
if (!config->image_weight_sum || random <= limit) {
|
||||
UINT32 random = (((UINT64) Random() & 0xffffffff) * config->image_weight_sum) >> 32;
|
||||
UINT32 limit = ((UINT64) 0xffffffff * weight) >> 32;
|
||||
Log(config->debug, L"%s n=%d, action=%d, x=%d, y=%d, o=%d, path=%s, rand=%x/%x\n", random <= limit ? L"Using" : L"Skipping", weight, action, x, y, o, path, random, limit);
|
||||
if (random <= limit) {
|
||||
config->action = action;
|
||||
config->image_path = path;
|
||||
config->orientation = o;
|
||||
config->image_x = x;
|
||||
config->image_y = y;
|
||||
}
|
||||
}
|
||||
|
||||
static int ParseCoordinate(const CHAR16* str, enum HackBGRT_action action) {
|
||||
if (str && L'0' <= str[0] && str[0] <= L'9') {
|
||||
return Atoi(str);
|
||||
if (str && ((L'0' <= str[0] && str[0] <= L'9') || str[0] == L'-')) {
|
||||
return str[0] == L'-' ? -(int)Atoi(str+1) : (int)Atoi(str);
|
||||
}
|
||||
if ((str && StrnCmp(str, L"native", 6) == 0) || action == HackBGRT_KEEP) {
|
||||
return HackBGRT_coord_native;
|
||||
if ((str && StrnCmp(str, L"keep", 4) == 0) || action == HackBGRT_KEEP) {
|
||||
return HackBGRT_coord_keep;
|
||||
}
|
||||
return HackBGRT_coord_auto;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ReadConfigImage(struct HackBGRT_config* config, const CHAR16* line) {
|
||||
const CHAR16* n = StrStrAfter(line, L"n=");
|
||||
const CHAR16* x = StrStrAfter(line, L"x=");
|
||||
const CHAR16* y = StrStrAfter(line, L"y=");
|
||||
const CHAR16* o = StrStrAfter(line, L"o=");
|
||||
const CHAR16* f = StrStrAfter(line, L"path=");
|
||||
enum HackBGRT_action action = HackBGRT_KEEP;
|
||||
if (f) {
|
||||
@@ -65,18 +104,32 @@ static void ReadConfigImage(struct HackBGRT_config* config, const CHAR16* line)
|
||||
} else if (StrStr(line, L"remove")) {
|
||||
action = HackBGRT_REMOVE;
|
||||
} else if (StrStr(line, L"black")) {
|
||||
action = HackBGRT_BLACK;
|
||||
action = HackBGRT_REPLACE;
|
||||
} else if (StrStr(line, L"keep")) {
|
||||
action = HackBGRT_KEEP;
|
||||
} else {
|
||||
Print(L"HackBGRT: Invalid image line: %s\n", line);
|
||||
Log(1, L"Invalid image line: %s\n", line);
|
||||
return;
|
||||
}
|
||||
int weight = n && (!f || n < f) ? Atoi(n) : 1;
|
||||
SetBMPWithRandom(config, weight, action, ParseCoordinate(x, action), ParseCoordinate(y, action), f);
|
||||
int x_val = ParseCoordinate(x, action);
|
||||
int y_val = ParseCoordinate(y, action);
|
||||
int o_val = o ? ParseCoordinate(o, action) : HackBGRT_coord_keep;
|
||||
SetBMPWithRandom(config, weight, action, x_val, y_val, o_val, f);
|
||||
}
|
||||
|
||||
void ReadConfigLine(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, const CHAR16* line) {
|
||||
static void ReadConfigResolution(struct HackBGRT_config* config, const CHAR16* line) {
|
||||
const CHAR16* x = line;
|
||||
const CHAR16* y = StrStrAfter(line, L"x");
|
||||
if (x && *x && y && *y) {
|
||||
config->resolution_x = *x == '-' ? -(int)Atoi(x+1) : (int)Atoi(x);
|
||||
config->resolution_y = *y == '-' ? -(int)Atoi(y+1) : (int)Atoi(y);
|
||||
} else {
|
||||
Log(1, L"Invalid resolution line: %s\n", line);
|
||||
}
|
||||
}
|
||||
|
||||
void ReadConfigLine(struct HackBGRT_config* config, EFI_FILE_HANDLE base_dir, const CHAR16* line) {
|
||||
line = TrimLeft(line);
|
||||
if (line[0] == L'#' || line[0] == 0) {
|
||||
return;
|
||||
@@ -86,6 +139,10 @@ void ReadConfigLine(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, co
|
||||
config->debug = (StrCmp(line, L"debug=1") == 0);
|
||||
return;
|
||||
}
|
||||
if (StrnCmp(line, L"log=", 4) == 0) {
|
||||
config->log = (StrCmp(line, L"log=1") == 0);
|
||||
return;
|
||||
}
|
||||
if (StrnCmp(line, L"image=", 6) == 0) {
|
||||
ReadConfigImage(config, line + 6);
|
||||
return;
|
||||
@@ -95,8 +152,12 @@ void ReadConfigLine(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, co
|
||||
return;
|
||||
}
|
||||
if (StrnCmp(line, L"config=", 7) == 0) {
|
||||
ReadConfigFile(config, root_dir, line + 7);
|
||||
ReadConfigFile(config, base_dir, line + 7);
|
||||
return;
|
||||
}
|
||||
Print(L"Unknown configuration directive: %s\n", line);
|
||||
if (StrnCmp(line, L"resolution=", 11) == 0) {
|
||||
ReadConfigResolution(config, line + 11);
|
||||
return;
|
||||
}
|
||||
Log(1, L"Unknown configuration directive: %s\n", line);
|
||||
}
|
||||
|
||||
22
src/config.h
22
src/config.h
@@ -1,12 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <efi.h>
|
||||
#include "efi.h"
|
||||
|
||||
/**
|
||||
* Possible actions to perform on the BGRT.
|
||||
*/
|
||||
enum HackBGRT_action {
|
||||
HackBGRT_KEEP = 0, HackBGRT_REPLACE, HackBGRT_REMOVE, HackBGRT_BLACK
|
||||
HackBGRT_KEEP = 0, HackBGRT_REPLACE, HackBGRT_REMOVE
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -14,20 +14,24 @@ enum HackBGRT_action {
|
||||
* @see struct HackBGRT_config
|
||||
*/
|
||||
enum HackBGRT_coordinate {
|
||||
HackBGRT_coord_auto = 0x10000001,
|
||||
HackBGRT_coord_native = 0x10000002
|
||||
HackBGRT_coord_keep = -1000001
|
||||
};
|
||||
|
||||
/**
|
||||
* The configuration.
|
||||
*/
|
||||
struct HackBGRT_config {
|
||||
int debug;
|
||||
int debug, log;
|
||||
enum HackBGRT_action action;
|
||||
const CHAR16* image_path;
|
||||
int image_x;
|
||||
int image_y;
|
||||
int image_weight_sum;
|
||||
int orientation;
|
||||
int resolution_x;
|
||||
int resolution_y;
|
||||
int old_resolution_x;
|
||||
int old_resolution_y;
|
||||
const CHAR16* boot_path;
|
||||
};
|
||||
|
||||
@@ -35,17 +39,17 @@ struct HackBGRT_config {
|
||||
* Read a configuration parameter. (May recursively read config files.)
|
||||
*
|
||||
* @param config The configuration to modify.
|
||||
* @param root_dir The root directory, in case the parameter contains an include.
|
||||
* @param base_dir The base directory, in case the parameter contains an include.
|
||||
* @param line The configuration line to parse.
|
||||
*/
|
||||
extern void ReadConfigLine(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, const CHAR16* line);
|
||||
extern void ReadConfigLine(struct HackBGRT_config* config, EFI_FILE_HANDLE base_dir, const CHAR16* line);
|
||||
|
||||
/**
|
||||
* Read a configuration file. (May recursively read more files.)
|
||||
*
|
||||
* @param config The configuration to modify.
|
||||
* @param root_dir The root directory.
|
||||
* @param base_dir The base directory.
|
||||
* @param path The path to the file.
|
||||
* @return FALSE, if the file couldn't be read, TRUE otherwise.
|
||||
*/
|
||||
extern BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, const CHAR16* path);
|
||||
extern BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE base_dir, const CHAR16* path);
|
||||
|
||||
159
src/efi.c
Normal file
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>
|
||||
604
src/main.c
604
src/main.c
@@ -1,25 +1,27 @@
|
||||
#include <efi.h>
|
||||
#include <efilib.h>
|
||||
|
||||
#include "efi.h"
|
||||
#include "types.h"
|
||||
#include "config.h"
|
||||
#include "util.h"
|
||||
|
||||
/**
|
||||
* The Print function signature.
|
||||
* The version.
|
||||
*/
|
||||
typedef UINTN print_t(IN CHAR16 *fmt, ...);
|
||||
#ifdef GIT_DESCRIBE_W
|
||||
const CHAR16 version[] = GIT_DESCRIBE_W;
|
||||
#else
|
||||
const CHAR16 version[] = L"unknown; not an official release?";
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The function for debug printing; either Print or NullPrint.
|
||||
*/
|
||||
print_t* Debug = NullPrint;
|
||||
EFI_SYSTEM_TABLE *ST;
|
||||
EFI_BOOT_SERVICES *BS;
|
||||
EFI_RUNTIME_SERVICES *RT;
|
||||
|
||||
/**
|
||||
* The configuration.
|
||||
*/
|
||||
static struct HackBGRT_config config = {
|
||||
.action = HackBGRT_KEEP
|
||||
.log = 1,
|
||||
.action = HackBGRT_KEEP,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -28,131 +30,147 @@ static struct HackBGRT_config config = {
|
||||
static EFI_GRAPHICS_OUTPUT_PROTOCOL* GOP(void) {
|
||||
static EFI_GRAPHICS_OUTPUT_PROTOCOL* gop;
|
||||
if (!gop) {
|
||||
EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
|
||||
LibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **)&gop);
|
||||
LibLocateProtocol(TmpGuidPtr((EFI_GUID) EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID), (void**) &gop);
|
||||
}
|
||||
return gop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the correct coordinate (manual, automatic, native)
|
||||
* Set screen resolution. If there is no exact match, try to find a bigger one.
|
||||
*
|
||||
* @param value The configured coordinate value; has special values for automatic and native.
|
||||
* @param automatic The automatically calculated alternative.
|
||||
* @param native The original coordinate.
|
||||
* @see enum HackBGRT_coordinate
|
||||
* @param w Horizontal resolution. 0 for max, -1 for current.
|
||||
* @param h Vertical resolution. 0 for max, -1 for current.
|
||||
*/
|
||||
static int SelectCoordinate(int value, int automatic, int native) {
|
||||
if (value == HackBGRT_coord_auto) {
|
||||
return automatic;
|
||||
static void SetResolution(int w, int h) {
|
||||
EFI_GRAPHICS_OUTPUT_PROTOCOL* gop = GOP();
|
||||
if (!gop) {
|
||||
if (config.resolution_x <= 0 || config.resolution_y <= 0) {
|
||||
config.resolution_x = 1024;
|
||||
config.resolution_y = 768;
|
||||
}
|
||||
if (value == HackBGRT_coord_native) {
|
||||
return native;
|
||||
config.old_resolution_x = config.resolution_x;
|
||||
config.old_resolution_y = config.resolution_y;
|
||||
Log(config.debug, L"GOP not found! Assuming resolution %dx%d.\n", config.resolution_x, config.resolution_y);
|
||||
return;
|
||||
}
|
||||
UINTN best_i = gop->Mode->Mode;
|
||||
int best_w = config.old_resolution_x = gop->Mode->Info->HorizontalResolution;
|
||||
int best_h = config.old_resolution_y = gop->Mode->Info->VerticalResolution;
|
||||
w = (w <= 0 ? w < 0 ? best_w : 999999 : w);
|
||||
h = (h <= 0 ? h < 0 ? best_h : 999999 : h);
|
||||
|
||||
Log(config.debug, L"Looking for resolution %dx%d...\n", w, h);
|
||||
for (UINT32 i = gop->Mode->MaxMode; i--;) {
|
||||
int new_w = 0, new_h = 0;
|
||||
|
||||
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION* info = 0;
|
||||
UINTN info_size;
|
||||
if (EFI_ERROR(gop->QueryMode(gop, i, &info_size, &info))) {
|
||||
continue;
|
||||
}
|
||||
if (info_size < sizeof(*info)) {
|
||||
BS->FreePool(info);
|
||||
continue;
|
||||
}
|
||||
new_w = info->HorizontalResolution;
|
||||
new_h = info->VerticalResolution;
|
||||
BS->FreePool(info);
|
||||
|
||||
// Sum of missing w/h should be minimal.
|
||||
int new_missing = max(w - new_w, 0) + max(h - new_h, 0);
|
||||
int best_missing = max(w - best_w, 0) + max(h - best_h, 0);
|
||||
if (new_missing > best_missing) {
|
||||
continue;
|
||||
}
|
||||
// Sum of extra w/h should be minimal.
|
||||
int new_over = max(-w + new_w, 0) + max(-h + new_h, 0);
|
||||
int best_over = max(-w + best_w, 0) + max(-h + best_h, 0);
|
||||
if (new_missing == best_missing && new_over >= best_over) {
|
||||
continue;
|
||||
}
|
||||
best_w = new_w;
|
||||
best_h = new_h;
|
||||
best_i = i;
|
||||
}
|
||||
Log(config.debug, L"Found resolution %dx%d.\n", best_w, best_h);
|
||||
config.resolution_x = best_w;
|
||||
config.resolution_y = best_h;
|
||||
if (best_i != gop->Mode->Mode) {
|
||||
gop->SetMode(gop, best_i);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize (clear) a BGRT.
|
||||
* Create a new XSDT with the given number of entries.
|
||||
*
|
||||
* @param bgrt The BGRT to initialize.
|
||||
* @param xsdt0 The old XSDT.
|
||||
* @param entries The number of SDT entries.
|
||||
* @return Pointer to a new XSDT.
|
||||
*/
|
||||
static void InitBGRT(ACPI_BGRT* bgrt) {
|
||||
const char data[0x38] = "BGRT" "\x38\x00\x00\x00" "\x00" "\xd6" "Mtblx*" "HackBGRT" "\x20\x17\x00\x00" "PTL " "\x02\x00\x00\x00" "\x01\x00" "\x00" "\x00";
|
||||
CopyMem(bgrt, data, sizeof(data));
|
||||
ACPI_SDT_HEADER* CreateXsdt(ACPI_SDT_HEADER* xsdt0, UINTN entries) {
|
||||
ACPI_SDT_HEADER* xsdt = 0;
|
||||
UINT32 xsdt_len = sizeof(ACPI_SDT_HEADER) + entries * sizeof(UINT64);
|
||||
BS->AllocatePool(EfiACPIReclaimMemory, xsdt_len, (void**)&xsdt);
|
||||
if (!xsdt) {
|
||||
Log(1, L"Failed to allocate memory for XSDT.\n");
|
||||
return 0;
|
||||
}
|
||||
BS->SetMem(xsdt, xsdt_len, 0);
|
||||
BS->CopyMem(xsdt, xsdt0, min(xsdt0->length, xsdt_len));
|
||||
xsdt->length = xsdt_len;
|
||||
SetAcpiSdtChecksum(xsdt);
|
||||
return xsdt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill a BGRT as specified by the parameters.
|
||||
* Update the ACPI tables as needed for the desired BGRT change.
|
||||
*
|
||||
* @param bgrt The BGRT to fill.
|
||||
* @param new_bmp The BMP to use.
|
||||
* @param new_x The x coordinate to use.
|
||||
* @param new_y The y coordinate to use.
|
||||
*/
|
||||
static void FillBGRT(ACPI_BGRT* bgrt, BMP* new_bmp, int new_x, int new_y) {
|
||||
BMP* old_bmp = (BMP*) (UINTN) bgrt->image_address;
|
||||
ACPI_BGRT bgrt0 = *bgrt;
|
||||
InitBGRT(bgrt);
|
||||
|
||||
if (new_bmp) {
|
||||
bgrt->image_address = (UINTN) new_bmp;
|
||||
}
|
||||
BMP* bmp = (BMP*) (UINTN) bgrt->image_address;
|
||||
|
||||
// Calculate the automatically centered position for the image.
|
||||
int x_auto, y_auto;
|
||||
if (GOP()) {
|
||||
x_auto = max(0, ((int)GOP()->Mode->Info->HorizontalResolution - (int)bmp->width) / 2);
|
||||
y_auto = max(0, ((int)GOP()->Mode->Info->VerticalResolution * 2/3 - (int)bmp->height) / 2);
|
||||
} else {
|
||||
x_auto = max(0, (int)bgrt0.image_offset_x + ((int)old_bmp->width - (int)bmp->width) / 2);
|
||||
y_auto = max(0, (int)bgrt0.image_offset_y + ((int)old_bmp->height - (int)bmp->height) / 2);
|
||||
}
|
||||
|
||||
// Set the position (manual, automatic, original).
|
||||
bgrt->image_offset_x = SelectCoordinate(new_x, x_auto, bgrt0.image_offset_x);
|
||||
bgrt->image_offset_y = SelectCoordinate(new_y, y_auto, bgrt0.image_offset_y);
|
||||
Debug(L"HackBGRT: BMP at (%d, %d).\n", (int) bgrt->image_offset_x, (int) bgrt->image_offset_y);
|
||||
|
||||
bgrt->header.checksum = 0;
|
||||
bgrt->header.checksum = CalculateAcpiChecksum(bgrt, sizeof(*bgrt));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the BGRT and optionally destroy it or create if missing.
|
||||
* If action is REMOVE, all BGRT entries will be removed.
|
||||
* If action is KEEP, the first BGRT entry will be returned.
|
||||
* If action is REPLACE, the given BGRT entry will be stored in each XSDT.
|
||||
*
|
||||
* @param action The intended action.
|
||||
* @param bgrt The BGRT, if action is REPLACE.
|
||||
* @return Pointer to the BGRT, or 0 if not found (or destroyed).
|
||||
*/
|
||||
static ACPI_BGRT* FindBGRT(enum HackBGRT_action action) {
|
||||
ACPI_20_RSDP* good_rsdp = 0;
|
||||
ACPI_BGRT* bgrt = 0;
|
||||
|
||||
static ACPI_BGRT* HandleAcpiTables(enum HackBGRT_action action, ACPI_BGRT* bgrt) {
|
||||
for (int i = 0; i < ST->NumberOfTableEntries; i++) {
|
||||
EFI_GUID Acpi20TableGuid = ACPI_20_TABLE_GUID;
|
||||
EFI_GUID* vendor_guid = &ST->ConfigurationTable[i].VendorGuid;
|
||||
if (!CompareGuid(vendor_guid, &AcpiTableGuid) && !CompareGuid(vendor_guid, &Acpi20TableGuid)) {
|
||||
if (CompareMem(vendor_guid, TmpGuidPtr((EFI_GUID) ACPI_TABLE_GUID), sizeof(EFI_GUID)) != 0 && CompareMem(vendor_guid, TmpGuidPtr((EFI_GUID) ACPI_20_TABLE_GUID), sizeof(EFI_GUID)) != 0) {
|
||||
continue;
|
||||
}
|
||||
EFI_CONFIGURATION_TABLE *ect = &ST->ConfigurationTable[i];
|
||||
if (CompareMem(ect->VendorTable, "RSD PTR ", 8) != 0) {
|
||||
ACPI_20_RSDP* rsdp = (ACPI_20_RSDP *) ST->ConfigurationTable[i].VendorTable;
|
||||
if (CompareMem(rsdp->signature, "RSD PTR ", 8) != 0 || rsdp->revision < 2 || !VerifyAcpiRsdp2Checksums(rsdp)) {
|
||||
continue;
|
||||
}
|
||||
ACPI_20_RSDP* rsdp = (ACPI_20_RSDP *)ect->VendorTable;
|
||||
Debug(L"RSDP: revision = %d, OEM ID = %s\n", rsdp->revision, TmpStr(rsdp->oem_id, 6));
|
||||
Log(config.debug, L"RSDP @%x: revision = %d, OEM ID = %s\n", (UINTN)rsdp, rsdp->revision, TmpStr(rsdp->oem_id, 6));
|
||||
|
||||
if (rsdp->revision < 2) {
|
||||
Debug(L"* XSDT: N/A (revision < 2)\n");
|
||||
continue;
|
||||
}
|
||||
ACPI_SDT_HEADER* xsdt = (ACPI_SDT_HEADER *) (UINTN) rsdp->xsdt_address;
|
||||
if (!xsdt) {
|
||||
Debug(L"* XSDT: N/A (null)\n");
|
||||
if (!xsdt || CompareMem(xsdt->signature, "XSDT", 4) != 0 || !VerifyAcpiSdtChecksum(xsdt)) {
|
||||
Log(config.debug, L"* XSDT: missing or invalid\n");
|
||||
continue;
|
||||
}
|
||||
if (CompareMem(xsdt->signature, "XSDT", 4) != 0) {
|
||||
Debug(L"* XSDT: N/A (invalid signature)\n");
|
||||
continue;
|
||||
}
|
||||
good_rsdp = rsdp;
|
||||
UINT64* entry_arr = (UINT64*)&xsdt[1];
|
||||
UINT32 entry_arr_length = (xsdt->length - sizeof(*xsdt)) / sizeof(UINT64);
|
||||
|
||||
Debug(L"* XSDT: OEM ID = %s, entry count = %d\n", TmpStr(xsdt->oem_id, 6), entry_arr_length);
|
||||
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;
|
||||
for (int j = 0; j < entry_arr_length; j++) {
|
||||
ACPI_SDT_HEADER *entry = (ACPI_SDT_HEADER *)((UINTN)entry_arr[j]);
|
||||
Debug(L" - ACPI table: %s, revision = %d, OEM ID = %s\n", TmpStr(entry->signature, 4), entry->revision, TmpStr(entry->oem_id, 6));
|
||||
if (CompareMem(entry->signature, "BGRT", 4) == 0) {
|
||||
if (!bgrt && action != HackBGRT_REMOVE) {
|
||||
bgrt = (void*) entry;
|
||||
} else {
|
||||
if (bgrt) {
|
||||
Debug(L" -> Deleting; BGRT was already found!\n");
|
||||
} else {
|
||||
Debug(L" -> Deleting.\n");
|
||||
if (CompareMem(entry->signature, "BGRT", 4) != 0) {
|
||||
continue;
|
||||
}
|
||||
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) {
|
||||
case HackBGRT_KEEP:
|
||||
if (!bgrt) {
|
||||
Log(config.debug, L" -> Returning this one for later use.\n");
|
||||
bgrt = (ACPI_BGRT*) entry;
|
||||
}
|
||||
break;
|
||||
case HackBGRT_REMOVE:
|
||||
Log(config.debug, L" -> Deleting.\n");
|
||||
for (int k = j+1; k < entry_arr_length; ++k) {
|
||||
entry_arr[k-1] = entry_arr[k];
|
||||
}
|
||||
@@ -160,152 +178,348 @@ static ACPI_BGRT* FindBGRT(enum HackBGRT_action action) {
|
||||
entry_arr[entry_arr_length] = 0;
|
||||
xsdt->length -= sizeof(entry_arr[0]);
|
||||
--j;
|
||||
break;
|
||||
case HackBGRT_REPLACE:
|
||||
Log(config.debug, L" -> Replacing.\n");
|
||||
entry_arr[j] = (UINTN) bgrt;
|
||||
}
|
||||
bgrt_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (action == HackBGRT_REMOVE) {
|
||||
return 0;
|
||||
}
|
||||
if (!good_rsdp) {
|
||||
Print(L"HackBGRT: RSDP or XSDT not found.\n");
|
||||
return 0;
|
||||
}
|
||||
if (!bgrt) {
|
||||
if (action == HackBGRT_KEEP) {
|
||||
Print(L"HackBGRT: BGRT not found.\n");
|
||||
return 0;
|
||||
}
|
||||
Debug(L"HackBGRT: BGRT not found, creating.\n");
|
||||
ACPI_20_RSDP* rsdp = good_rsdp;
|
||||
ACPI_SDT_HEADER* xsdt0 = (ACPI_SDT_HEADER *) (UINTN) rsdp->xsdt_address;
|
||||
ACPI_SDT_HEADER* xsdt = 0;
|
||||
UINT32 xsdt_len = xsdt0->length + sizeof(UINT64);
|
||||
BS->AllocatePool(EfiACPIReclaimMemory, xsdt_len, (void**)&xsdt);
|
||||
BS->AllocatePool(EfiACPIReclaimMemory, sizeof(*bgrt), (void**)&bgrt);
|
||||
if (!xsdt || !bgrt) {
|
||||
Print(L"HackBGRT: Failed to allocate memory for XSDT and BGRT.\n");
|
||||
return 0;
|
||||
}
|
||||
if (!bgrt_count && action == HackBGRT_REPLACE && bgrt) {
|
||||
Log(config.debug, L" - Adding missing BGRT.\n");
|
||||
xsdt = CreateXsdt(xsdt, entry_arr_length + 1);
|
||||
entry_arr = (UINT64*)&xsdt[1];
|
||||
entry_arr[entry_arr_length++] = (UINTN) bgrt;
|
||||
rsdp->xsdt_address = (UINTN) xsdt;
|
||||
CopyMem(xsdt, xsdt0, xsdt0->length);
|
||||
*(UINT64*)((char*)xsdt + xsdt->length) = (UINTN) bgrt;
|
||||
xsdt->length = xsdt_len;
|
||||
InitBGRT(bgrt);
|
||||
SetAcpiRsdp2Checksums(rsdp);
|
||||
}
|
||||
SetAcpiSdtChecksum(xsdt);
|
||||
}
|
||||
return bgrt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a bitmap or generate one, or return 0 if not applicable.
|
||||
* Generate a BMP with the given size and color.
|
||||
*
|
||||
* @param action Tells what to do.
|
||||
* @param root_dir The root directory for loading a BMP.
|
||||
* @param path The BMP path within the root directory.
|
||||
* @return The loaded BMP, or 0 if not needed or not available.
|
||||
* @param w The width.
|
||||
* @param h The height.
|
||||
* @param r The red component.
|
||||
* @param g The green component.
|
||||
* @param b The blue component.
|
||||
* @return The generated BMP, or 0 on failure.
|
||||
*/
|
||||
static BMP* LoadBMP(enum HackBGRT_action action, EFI_FILE_HANDLE root_dir, const CHAR16* path) {
|
||||
static BMP* MakeBMP(int w, int h, UINT8 r, UINT8 g, UINT8 b) {
|
||||
BMP* bmp = 0;
|
||||
if (action == HackBGRT_KEEP || action == HackBGRT_REMOVE) {
|
||||
return 0;
|
||||
}
|
||||
if (action == HackBGRT_BLACK) {
|
||||
BS->AllocatePool(EfiBootServicesData, 58, (void**) &bmp);
|
||||
BS->AllocatePool(EfiBootServicesData, 54 + w * h * 4, (void**) &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);
|
||||
return 0;
|
||||
}
|
||||
CopyMem(
|
||||
bmp,
|
||||
"\x42\x4d\x3a\x00\x00\x00\x00\x00\x00\x00\x36\x00\x00\x00\x28\x00"
|
||||
"\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x18\x00\x00\x00"
|
||||
"\x00\x00\x04\x00\x00\x00\x13\x0b\x00\x00\x13\x0b\x00\x00\x00\x00"
|
||||
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
|
||||
58
|
||||
);
|
||||
*bmp = (BMP) {
|
||||
.magic_BM = { 'B', 'M' },
|
||||
.file_size = 54 + w * h * 4,
|
||||
.pixel_data_offset = 54,
|
||||
.dib_header_size = 40,
|
||||
.width = w,
|
||||
.height = h,
|
||||
.planes = 1,
|
||||
.bpp = 32,
|
||||
};
|
||||
UINT8* data = (UINT8*) bmp + bmp->pixel_data_offset;
|
||||
for (int y = 0; y < h; ++y) for (int x = 0; x < w; ++x) {
|
||||
*data++ = b;
|
||||
*data++ = g;
|
||||
*data++ = r;
|
||||
*data++ = 0;
|
||||
}
|
||||
return bmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a bitmap or generate a black one.
|
||||
*
|
||||
* @param base_dir The directory for loading a BMP.
|
||||
* @param path The BMP path within the directory; NULL for a black BMP.
|
||||
* @return The loaded BMP, or 0 if not available.
|
||||
*/
|
||||
static BMP* LoadBMP(EFI_FILE_HANDLE base_dir, const CHAR16* path) {
|
||||
if (!path) {
|
||||
Print(L"HackBGRT: Missing BMP path. REPORT THIS BUG!");
|
||||
return 0;
|
||||
}
|
||||
Debug(L"HackBGRT: Loading %s.\n", path);
|
||||
bmp = LoadFile(root_dir, path, 0);
|
||||
if (!bmp) {
|
||||
Print(L"HackBGRT: Failed to load BMP (%s)!\n", path);
|
||||
BS->Stall(1000000);
|
||||
return 0;
|
||||
return MakeBMP(1, 1, 0, 0, 0); // empty path = black image
|
||||
}
|
||||
Log(config.debug, L"Loading %s.\n", path);
|
||||
UINTN size = 0;
|
||||
BMP* bmp = LoadFile(base_dir, path, &size);
|
||||
if (bmp) {
|
||||
if (size >= bmp->file_size
|
||||
&& CompareMem(bmp, "BM", 2) == 0
|
||||
&& bmp->file_size > bmp->pixel_data_offset
|
||||
&& 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;
|
||||
}
|
||||
BS->FreePool(bmp);
|
||||
Log(1, L"Invalid BMP (%s)!\n", path);
|
||||
} else {
|
||||
Log(1, L"Failed to load BMP (%s)!\n", path);
|
||||
}
|
||||
BS->Stall(1000000);
|
||||
return MakeBMP(16, 16, 255, 0, 0); // error = red image
|
||||
}
|
||||
|
||||
/**
|
||||
* Crop a BMP to the given size.
|
||||
*
|
||||
* @param bmp The BMP to crop.
|
||||
* @param w The maximum width.
|
||||
* @param h The maximum height.
|
||||
*/
|
||||
static void CropBMP(BMP* bmp, int w, int h) {
|
||||
const int old_pitch = -(-(bmp->width * (bmp->bpp / 8)) & ~3);
|
||||
bmp->image_size = 0;
|
||||
bmp->width = min(bmp->width, w);
|
||||
bmp->height = min(bmp->height, h);
|
||||
const int new_pitch = -(-(bmp->width * (bmp->bpp / 8)) & ~3);
|
||||
|
||||
if (new_pitch < old_pitch) {
|
||||
for (int i = 1; i < bmp->height; ++i) {
|
||||
BS->CopyMem(
|
||||
(UINT8*) bmp + bmp->pixel_data_offset + i * new_pitch,
|
||||
(UINT8*) bmp + bmp->pixel_data_offset + i * old_pitch,
|
||||
new_pitch
|
||||
);
|
||||
}
|
||||
}
|
||||
bmp->file_size = bmp->pixel_data_offset + bmp->height * new_pitch;
|
||||
}
|
||||
|
||||
/**
|
||||
* The main logic for BGRT modification.
|
||||
*
|
||||
* @param base_dir The directory for loading a BMP.
|
||||
*/
|
||||
void HackBgrt(EFI_FILE_HANDLE base_dir) {
|
||||
// REMOVE: simply delete all BGRT entries.
|
||||
if (config.action == HackBGRT_REMOVE) {
|
||||
HandleAcpiTables(config.action, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// KEEP/REPLACE: first get the old BGRT entry.
|
||||
ACPI_BGRT* bgrt = HandleAcpiTables(HackBGRT_KEEP, 0);
|
||||
|
||||
// Get the old BMP and position (relative to screen center), if possible.
|
||||
const int old_valid = bgrt && VerifyAcpiSdtChecksum(bgrt);
|
||||
BMP* old_bmp = old_valid ? (BMP*) (UINTN) bgrt->image_address : 0;
|
||||
const int old_orientation = old_valid ? ((bgrt->status >> 1) & 3) : 0;
|
||||
const int old_swap = old_orientation & 1;
|
||||
const int old_reso_x = old_swap ? config.old_resolution_y : config.old_resolution_x;
|
||||
const int old_reso_y = old_swap ? config.old_resolution_x : config.old_resolution_y;
|
||||
const int old_x = old_bmp ? bgrt->image_offset_x + (old_bmp->width - old_reso_x) / 2 : 0;
|
||||
const int old_y = old_bmp ? bgrt->image_offset_y + (old_bmp->height - old_reso_y) / 2 : 0;
|
||||
|
||||
// Missing BGRT?
|
||||
if (!bgrt) {
|
||||
// Keep missing = do nothing.
|
||||
if (config.action == HackBGRT_KEEP) {
|
||||
return;
|
||||
}
|
||||
// Replace missing = allocate new.
|
||||
BS->AllocatePool(EfiACPIReclaimMemory, sizeof(*bgrt), (void**)&bgrt);
|
||||
if (!bgrt) {
|
||||
Log(1, L"Failed to allocate memory for BGRT.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
*bgrt = (ACPI_BGRT) {
|
||||
.header = {
|
||||
.signature = "BGRT",
|
||||
.length = sizeof(*bgrt),
|
||||
.revision = 1,
|
||||
.oem_id = "Mtblx*",
|
||||
.oem_table_id = "HackBGRT",
|
||||
.oem_revision = 1,
|
||||
.asl_compiler_id = *(const UINT32*) "None",
|
||||
.asl_compiler_revision = 1,
|
||||
},
|
||||
.version = 1,
|
||||
};
|
||||
|
||||
// Get the image (either old or new).
|
||||
BMP* new_bmp = old_bmp;
|
||||
if (config.action == HackBGRT_REPLACE) {
|
||||
new_bmp = LoadBMP(base_dir, config.image_path);
|
||||
}
|
||||
|
||||
// No image = no need for BGRT.
|
||||
if (!new_bmp) {
|
||||
HandleAcpiTables(HackBGRT_REMOVE, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Crop the image to screen.
|
||||
CropBMP(new_bmp, config.resolution_x, config.resolution_y);
|
||||
|
||||
// Set the image address and orientation.
|
||||
bgrt->image_address = (UINTN) new_bmp;
|
||||
const int new_orientation = config.orientation == HackBGRT_coord_keep ? old_orientation : ((config.orientation / 90) & 3);
|
||||
bgrt->status = new_orientation << 1;
|
||||
|
||||
// New center coordinates.
|
||||
const int new_x = config.image_x == HackBGRT_coord_keep ? old_x : config.image_x;
|
||||
const int new_y = config.image_y == HackBGRT_coord_keep ? old_y : config.image_y;
|
||||
const int new_swap = new_orientation & 1;
|
||||
const int new_reso_x = new_swap ? config.resolution_y : config.resolution_x;
|
||||
const int new_reso_y = new_swap ? config.resolution_x : config.resolution_y;
|
||||
|
||||
// Calculate absolute position.
|
||||
const int max_x = new_reso_x - new_bmp->width;
|
||||
const int max_y = new_reso_y - new_bmp->height;
|
||||
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));
|
||||
|
||||
Log(config.debug,
|
||||
L"BMP at (%d, %d), center (%d, %d), resolution (%d, %d), orientation %d.\n",
|
||||
(int) bgrt->image_offset_x, (int) bgrt->image_offset_y,
|
||||
new_x, new_y, new_reso_x, new_reso_y,
|
||||
new_orientation * 90
|
||||
);
|
||||
|
||||
// Store this BGRT in the ACPI tables.
|
||||
SetAcpiSdtChecksum(bgrt);
|
||||
HandleAcpiTables(HackBGRT_REPLACE, bgrt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an application.
|
||||
*/
|
||||
static EFI_HANDLE LoadApp(int print_failure, EFI_HANDLE image_handle, EFI_LOADED_IMAGE* image, const CHAR16* path) {
|
||||
EFI_DEVICE_PATH* boot_dp = FileDevicePath(image->DeviceHandle, (CHAR16*) path);
|
||||
EFI_HANDLE result = 0;
|
||||
Log(config.debug, L"Loading application %s.\n", path);
|
||||
if (EFI_ERROR(BS->LoadImage(0, image_handle, boot_dp, 0, 0, &result))) {
|
||||
Log(config.debug || print_failure, L"Failed to load application %s.\n", path);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* The main program.
|
||||
*/
|
||||
EFI_STATUS EFIAPI EfiMain(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *ST_) {
|
||||
InitializeLib(image_handle, ST_);
|
||||
EFI_STATUS EFIAPI efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *ST_) {
|
||||
ST = ST_;
|
||||
BS = ST_->BootServices;
|
||||
RT = ST_->RuntimeServices;
|
||||
|
||||
// Clear the screen to wipe the vendor logo.
|
||||
ST->ConOut->EnableCursor(ST->ConOut, 0);
|
||||
ST->ConOut->ClearScreen(ST->ConOut);
|
||||
|
||||
Log(0, L"HackBGRT version: %s\n", version);
|
||||
|
||||
EFI_LOADED_IMAGE* image;
|
||||
if (EFI_ERROR(BS->HandleProtocol(image_handle, &LoadedImageProtocol, (void**) &image))) {
|
||||
Debug(L"HackBGRT: LOADED_IMAGE_PROTOCOL failed.\n");
|
||||
if (EFI_ERROR(BS->HandleProtocol(image_handle, TmpGuidPtr((EFI_GUID) EFI_LOADED_IMAGE_PROTOCOL_GUID), (void**) &image))) {
|
||||
Log(config.debug, L"LOADED_IMAGE_PROTOCOL failed.\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
EFI_FILE_HANDLE root_dir = LibOpenRoot(image->DeviceHandle);
|
||||
|
||||
CHAR16 **argv;
|
||||
int argc = GetShellArgcArgv(image_handle, &argv);
|
||||
|
||||
if (argc <= 1) {
|
||||
const CHAR16* config_path = L"\\EFI\\HackBGRT\\config.txt";
|
||||
if (!ReadConfigFile(&config, root_dir, config_path)) {
|
||||
Print(L"HackBGRT: No config, no command line!\n", config_path);
|
||||
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;
|
||||
}
|
||||
|
||||
EFI_FILE_HANDLE root_dir;
|
||||
if (EFI_ERROR(io->OpenVolume(io, &root_dir))) {
|
||||
Log(config.debug, L"Failed to open root directory.\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
EFI_FILE_HANDLE base_dir;
|
||||
if (EFI_ERROR(root_dir->Open(root_dir, &base_dir, L"\\EFI\\HackBGRT", EFI_FILE_MODE_READ, 0))) {
|
||||
Log(config.debug, L"Failed to HackBGRT directory.\n");
|
||||
base_dir = root_dir;
|
||||
}
|
||||
|
||||
EFI_SHELL_PARAMETERS_PROTOCOL *shell_param_proto = NULL;
|
||||
if (EFI_ERROR(BS->OpenProtocol(image_handle, TmpGuidPtr((EFI_GUID) EFI_SHELL_PARAMETERS_PROTOCOL_GUID), (void**) &shell_param_proto, 0, 0, EFI_OPEN_PROTOCOL_GET_PROTOCOL)) || shell_param_proto->Argc <= 1) {
|
||||
const CHAR16* config_path = L"config.txt";
|
||||
if (!ReadConfigFile(&config, base_dir, config_path)) {
|
||||
Log(1, L"No config, no command line!\n", config_path);
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
CHAR16 **argv = shell_param_proto->Argv;
|
||||
int argc = shell_param_proto->Argc;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
ReadConfigLine(&config, root_dir, argv[i]);
|
||||
ReadConfigLine(&config, base_dir, argv[i]);
|
||||
}
|
||||
Debug = config.debug ? Print : NullPrint;
|
||||
|
||||
BMP* new_bmp = LoadBMP(config.action, root_dir, config.image_path);
|
||||
ACPI_BGRT* bgrt = FindBGRT(config.action);
|
||||
if (bgrt) {
|
||||
FillBGRT(bgrt, new_bmp, config.image_x, config.image_y);
|
||||
}
|
||||
|
||||
if (!config.boot_path) {
|
||||
Print(L"HackBGRT: Boot path not specified.\n");
|
||||
if (config.debug) {
|
||||
Log(-1, L"HackBGRT version: %s\n", version);
|
||||
}
|
||||
|
||||
SetResolution(config.resolution_x, config.resolution_y);
|
||||
HackBgrt(base_dir);
|
||||
|
||||
EFI_HANDLE next_image_handle = 0;
|
||||
static CHAR16 backup_boot_path[] = L"\\EFI\\HackBGRT\\bootmgfw-original.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) {
|
||||
next_image_handle = LoadApp(1, image_handle, image, config.boot_path);
|
||||
try_ms_quietly = 0;
|
||||
}
|
||||
if (!next_image_handle) {
|
||||
config.boot_path = backup_boot_path;
|
||||
next_image_handle = LoadApp(!try_ms_quietly, image_handle, image, config.boot_path);
|
||||
if (!next_image_handle) {
|
||||
config.boot_path = ms_boot_path;
|
||||
next_image_handle = LoadApp(!try_ms_quietly, image_handle, image, config.boot_path);
|
||||
if (!next_image_handle) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
Debug(L"HackBGRT: Loading and booting %s.\n", config.boot_path);
|
||||
EFI_DEVICE_PATH* boot_dp = FileDevicePath(image->DeviceHandle, (CHAR16*) config.boot_path);
|
||||
EFI_HANDLE next_image_handle;
|
||||
if (EFI_ERROR(BS->LoadImage(0, image_handle, boot_dp, 0, 0, &next_image_handle))) {
|
||||
Print(L"HackBGRT: LoadImage for new image (%s) failed.\n", config.boot_path);
|
||||
}
|
||||
if (try_ms_quietly) {
|
||||
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) {
|
||||
goto fail;
|
||||
}
|
||||
} else ready_to_boot: if (config.debug) {
|
||||
Log(-1, L"Ready to boot.\n");
|
||||
Log(-1, L"If all goes well, you can set debug=0 and log=0 in config.txt.\n");
|
||||
Log(-1, L"Press escape to cancel or any other key (or wait 15 seconds) to boot.\n");
|
||||
if (ReadKey(15000).ScanCode == SCAN_ESC) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (!config.log) {
|
||||
ClearLogVariable();
|
||||
}
|
||||
if (EFI_ERROR(BS->StartImage(next_image_handle, 0, 0))) {
|
||||
Print(L"HackBGRT: StartImage for %s failed.\n", config.boot_path);
|
||||
Log(1, L"Failed to start %s.\n", config.boot_path);
|
||||
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);
|
||||
Log(-1, L"Please check that %s is not actually HackBGRT!\n", config.boot_path);
|
||||
goto fail;
|
||||
|
||||
fail: {
|
||||
Print(L"HackBGRT has failed. Use parameter debug=1 for details.\nPress any key to exit.\n");
|
||||
WaitKey();
|
||||
Log(1, L"HackBGRT has failed.\n");
|
||||
Log(-1, L"Dumping log:\n\n");
|
||||
DumpLog();
|
||||
Log(-1, L"If you can't boot into Windows, get install/recovery disk to fix your boot.\n");
|
||||
Log(-1, L"Press any key (or wait 15 seconds) to exit.\n");
|
||||
ReadKey(15000);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward to EfiMain.
|
||||
*
|
||||
* Some compilers and architectures differ in underscore handling. This helps.
|
||||
*/
|
||||
EFI_STATUS EFIAPI _EfiMain(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *ST_) {
|
||||
return EfiMain(image_handle, ST_);
|
||||
}
|
||||
|
||||
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"
|
||||
;
|
||||
37
src/types.c
Normal file
37
src/types.c
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "types.h"
|
||||
|
||||
UINT8 SumBytes(const UINT8* arr, UINTN size) {
|
||||
UINT8 sum = 0;
|
||||
for (UINTN i = 0; i < size; ++i) {
|
||||
sum += arr[i];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
int VerifyAcpiRsdp2Checksums(const void* data) {
|
||||
const UINT8* arr = data;
|
||||
UINTN size = *(const UINT32*)&arr[20];
|
||||
return SumBytes(arr, 20) == 0 && SumBytes(arr, size) == 0;
|
||||
}
|
||||
|
||||
void SetAcpiRsdp2Checksums(void* data) {
|
||||
UINT8* arr = data;
|
||||
UINTN size = *(const UINT32*)&arr[20];
|
||||
arr[9] = 0;
|
||||
arr[32] = 0;
|
||||
arr[9] = -SumBytes(arr, 20);
|
||||
arr[32] = -SumBytes(arr, size);
|
||||
}
|
||||
|
||||
int VerifyAcpiSdtChecksum(const void* data) {
|
||||
const UINT8* arr = data;
|
||||
UINTN size = *(const UINT32*)&arr[4];
|
||||
return SumBytes(arr, size) == 0;
|
||||
}
|
||||
|
||||
void SetAcpiSdtChecksum(void* data) {
|
||||
UINT8* arr = data;
|
||||
UINTN size = *(const UINT32*)&arr[4];
|
||||
arr[9] = 0;
|
||||
arr[9] = -SumBytes(arr, size);
|
||||
}
|
||||
39
src/types.h
39
src/types.h
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "efi.h"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
/** RSDP (Root System Description Pointer) */
|
||||
@@ -50,5 +52,42 @@ typedef struct {
|
||||
UINT32 height;
|
||||
UINT16 planes;
|
||||
UINT16 bpp;
|
||||
UINT32 compression;
|
||||
UINT32 image_size;
|
||||
UINT32 x_pixels_per_meter;
|
||||
UINT32 y_pixels_per_meter;
|
||||
UINT32 colors_used;
|
||||
UINT32 important_colors;
|
||||
} BMP;
|
||||
|
||||
/**
|
||||
* Verify the checksums of an ACPI RSDP version 2.
|
||||
*
|
||||
* @param data Pointer to the table.
|
||||
* @return 1 if the checksum is correct, 0 otherwise.
|
||||
*/
|
||||
extern int VerifyAcpiRsdp2Checksums(const void* data);
|
||||
|
||||
/**
|
||||
* Set the correct checksums of an ACPI RSDP version 2.
|
||||
*
|
||||
* @param data Pointer to the table.
|
||||
*/
|
||||
extern void SetAcpiRsdp2Checksums(void* data);
|
||||
|
||||
/**
|
||||
* Verify the checksum of an ACPI SDT.
|
||||
*
|
||||
* @param data Pointer to the table.
|
||||
* @return 1 if the checksum is correct, 0 otherwise.
|
||||
*/
|
||||
extern int VerifyAcpiSdtChecksum(const void* data);
|
||||
|
||||
/**
|
||||
* Set the correct checksum for an ACPI SDT.
|
||||
*
|
||||
* @param data Pointer to the table.
|
||||
*/
|
||||
extern void SetAcpiSdtChecksum(void* data);
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
139
src/util.c
139
src/util.c
@@ -1,7 +1,5 @@
|
||||
#include "util.h"
|
||||
|
||||
#include <efilib.h>
|
||||
|
||||
const CHAR16* TmpStr(CHAR8 *src, int length) {
|
||||
static CHAR16 arr[4][16];
|
||||
static int j;
|
||||
@@ -14,17 +12,99 @@ const CHAR16* TmpStr(CHAR8 *src, int length) {
|
||||
return dest;
|
||||
}
|
||||
|
||||
UINTN NullPrint(IN CHAR16 *fmt, ...) {
|
||||
return 0;
|
||||
const CHAR16* TmpIntToStr(UINT32 x) {
|
||||
static CHAR16 buf[20];
|
||||
int i = 20 - 1;
|
||||
buf[i] = 0;
|
||||
if (!x) {
|
||||
buf[--i] = '0';
|
||||
}
|
||||
while (x && i) {
|
||||
buf[--i] = '0' + (x % 10);
|
||||
x /= 10;
|
||||
}
|
||||
return &buf[i];
|
||||
}
|
||||
|
||||
UINT8 CalculateAcpiChecksum(void* data, UINTN size) {
|
||||
UINT8 sum = 0;
|
||||
UINT8* arr = data;
|
||||
for (UINTN i = 0; i < size; ++i) {
|
||||
sum += arr[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;
|
||||
}
|
||||
return 256 - sum;
|
||||
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) {
|
||||
@@ -77,9 +157,30 @@ void RandomSeedAuto(void) {
|
||||
RandomSeed(a, b), Random(), Random();
|
||||
}
|
||||
|
||||
void WaitKey(void) {
|
||||
EFI_STATUS WaitKey(UINT64 timeout_ms) {
|
||||
ST->ConIn->Reset(ST->ConIn, FALSE);
|
||||
WaitForSingleEvent(ST->ConIn->WaitForKey, 0);
|
||||
const int ms_to_100ns = 10000;
|
||||
|
||||
EFI_EVENT events[2] = {ST->ConIn->WaitForKey};
|
||||
EFI_STATUS status = BS->CreateEvent(EVT_TIMER, 0, NULL, NULL, &events[1]);
|
||||
if (!EFI_ERROR(status)) {
|
||||
BS->SetTimer(events[1], TimerRelative, timeout_ms * ms_to_100ns);
|
||||
UINTN index;
|
||||
status = BS->WaitForEvent(2, events, &index);
|
||||
BS->CloseEvent(events[1]);
|
||||
if (!EFI_ERROR(status) && index == 1) {
|
||||
status = EFI_TIMEOUT;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
EFI_INPUT_KEY ReadKey(UINT64 timeout_ms) {
|
||||
EFI_INPUT_KEY key = {0};
|
||||
ST->ConOut->EnableCursor(ST->ConOut, 1);
|
||||
WaitKey(timeout_ms);
|
||||
ST->ConIn->ReadKeyStroke(ST->ConIn, &key);
|
||||
return key;
|
||||
}
|
||||
|
||||
void* LoadFileWithPadding(EFI_FILE_HANDLE dir, const CHAR16* path, UINTN* size_ptr, UINTN padding) {
|
||||
@@ -91,9 +192,11 @@ void* LoadFileWithPadding(EFI_FILE_HANDLE dir, const CHAR16* path, UINTN* size_p
|
||||
return 0;
|
||||
}
|
||||
|
||||
EFI_FILE_INFO *info = LibFileInfo(handle);
|
||||
UINTN size = info->FileSize;
|
||||
FreePool(info);
|
||||
UINT64 get_size = 0;
|
||||
handle->SetPosition(handle, ~(UINT64)0);
|
||||
handle->GetPosition(handle, &get_size);
|
||||
handle->SetPosition(handle, 0);
|
||||
UINTN size = (UINTN) get_size;
|
||||
|
||||
void* data = 0;
|
||||
e = BS->AllocatePool(EfiBootServicesData, size + padding, &data);
|
||||
@@ -102,10 +205,12 @@ void* LoadFileWithPadding(EFI_FILE_HANDLE dir, const CHAR16* path, UINTN* size_p
|
||||
return 0;
|
||||
}
|
||||
e = handle->Read(handle, &size, data);
|
||||
*(UINT32*)((char*)data + size) = 0;
|
||||
for (int i = 0; i < padding; ++i) {
|
||||
*((char*)data + size + i) = 0;
|
||||
}
|
||||
handle->Close(handle);
|
||||
if (EFI_ERROR(e)) {
|
||||
FreePool(data);
|
||||
BS->FreePool(data);
|
||||
return 0;
|
||||
}
|
||||
if (size_ptr) {
|
||||
|
||||
51
src/util.h
51
src/util.h
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <efi.h>
|
||||
#include "efi.h"
|
||||
|
||||
/**
|
||||
* Convert a short ASCII string to UCS2, store in a static array.
|
||||
@@ -12,18 +12,22 @@
|
||||
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 CHAR16 *fmt, ...);
|
||||
extern void Log(int mode, IN CONST CHAR16 *fmt, ...);
|
||||
|
||||
/**
|
||||
* Calculate the checksum for an ACPI table.
|
||||
*
|
||||
* @param data Pointer to the table.
|
||||
* @param size Table length in bytes.
|
||||
* @return Checksum.
|
||||
* Dump the log buffer to the screen.
|
||||
*/
|
||||
extern UINT8 CalculateAcpiChecksum(void* data, UINTN size);
|
||||
extern void DumpLog(void);
|
||||
|
||||
/**
|
||||
* Clear the log EFI variable, for minor RAM savings.
|
||||
*/
|
||||
extern void ClearLogVariable(void);
|
||||
|
||||
/**
|
||||
* Return the greater of two numbers.
|
||||
@@ -32,6 +36,13 @@ static inline int max(int a, int b) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the smaller of two numbers.
|
||||
*/
|
||||
static inline int min(int a, int b) {
|
||||
return a < b ? a : b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim BOM, spaces and tabs from the beginning of a string.
|
||||
*
|
||||
@@ -83,9 +94,19 @@ extern void RandomSeed(UINT64 a, UINT64 b);
|
||||
extern void RandomSeedAuto(void);
|
||||
|
||||
/**
|
||||
* Wait for a key press.
|
||||
* Wait for a key press. It will still remain in the buffer.
|
||||
*
|
||||
* @param timeout_ms The timeout in milliseconds, or 0 for no timeout.
|
||||
*/
|
||||
extern void WaitKey(void);
|
||||
extern EFI_STATUS WaitKey(UINT64 timeout_ms);
|
||||
|
||||
/**
|
||||
* Wait for a key press and read it.
|
||||
*
|
||||
* @param timeout_ms The timeout in milliseconds, or 0 for no timeout.
|
||||
* @return The pressed key.
|
||||
*/
|
||||
extern EFI_INPUT_KEY ReadKey(UINT64 timeout_ms);
|
||||
|
||||
/**
|
||||
* Load a file, allocate some extra bytes as well.
|
||||
@@ -99,3 +120,11 @@ static inline void* LoadFile(EFI_FILE_HANDLE dir, const CHAR16* path, UINTN* siz
|
||||
return LoadFileWithPadding(dir, path, size_ptr, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a temporary pointer to GUID.
|
||||
*/
|
||||
static inline EFI_GUID* TmpGuidPtr(EFI_GUID guid) {
|
||||
static EFI_GUID g;
|
||||
g = guid;
|
||||
return &g;
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
@ECHO OFF
|
||||
CD %~dp0
|
||||
|
||||
IF NOT EXIST install.bat (
|
||||
ECHO The uninstaller needs install.bat!
|
||||
EXIT /B 1
|
||||
)
|
||||
|
||||
CALL install.bat uninstall
|
||||
Reference in New Issue
Block a user