mirror of
https://github.com/Metabolix/HackBGRT.git
synced 2025-12-07 09:36:10 -08:00
Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9e23c91a3 | ||
|
|
697c57355b | ||
|
|
da16365508 | ||
|
|
665a4732ca | ||
|
|
39596aadfc | ||
|
|
1a5b1df064 | ||
|
|
ea70f3ce79 | ||
|
|
a44b929012 | ||
|
|
9948e5a306 | ||
|
|
518d7c8a97 | ||
|
|
c6108ffd62 | ||
|
|
6dc447a8ce | ||
|
|
5ec17a49e8 | ||
|
|
7b7309a255 | ||
|
|
a82646a822 | ||
|
|
294da9c069 | ||
|
|
4096002eb2 | ||
|
|
835cc1b2f2 | ||
|
|
74a143e723 | ||
|
|
b75dbe13c7 | ||
|
|
ba72b90082 | ||
|
|
6b724d5590 | ||
|
|
9ebe4e2476 | ||
|
|
5111897fae | ||
|
|
aac8a38cbb | ||
|
|
77dd2bd699 | ||
|
|
8dfa456a7b | ||
|
|
7ccdcc4a77 | ||
|
|
50e84e8c90 | ||
|
|
24c7e2b316 | ||
|
|
4379f9cbeb | ||
|
|
2a0f2a7757 | ||
|
|
ebbacb72bb | ||
|
|
a908036ee8 | ||
|
|
57ce3ae33e | ||
|
|
7dd048346d | ||
|
|
0dfc49c800 | ||
|
|
4e50b33636 | ||
|
|
5856d25b5b | ||
|
|
466ab69c48 | ||
|
|
14aa79929a | ||
|
|
5e32a3f880 | ||
|
|
db934099df | ||
|
|
e93ed54cb2 | ||
|
|
9b3b045a21 | ||
|
|
be8a5d35d2 | ||
|
|
2366fc8b98 | ||
|
|
e04ba3e451 | ||
|
|
f40f2bc9dc | ||
|
|
2f572b24d4 |
21
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
21
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report if you encounter a bug
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**What happens?**
|
||||
Describe the bug.
|
||||
|
||||
**How this happens?**
|
||||
Explain exactly the steps you did:
|
||||
1. Download HackBGRT-(version).zip and extract all files.
|
||||
2. Start setup.
|
||||
3. Select ...
|
||||
4. ...
|
||||
|
||||
**Log file**
|
||||
Run the setup again and select menu option `L` (or add parameter `show-boot-log` on command line). Attach the `setup.log` to this report.
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "gnu-efi"]
|
||||
path = gnu-efi
|
||||
url = https://github.com/rhboot/gnu-efi.git
|
||||
36
CHANGELOG.md
36
CHANGELOG.md
@@ -2,6 +2,42 @@
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## 2.4.1 - 2024-04-11
|
||||
|
||||
### Fixed
|
||||
- Report better if BCDEdit is unable to operate.
|
||||
- Improve support for non-BCDEdit boot entries.
|
||||
- Remove old version before copying any new files.
|
||||
|
||||
## 2.4.0 - 2023-12-31
|
||||
|
||||
### Fixed
|
||||
- Fix BCDEdit boot entries to avoid *shim* error messages.
|
||||
- Combine BCDEdit and own code to create boot entries more reliably.
|
||||
|
||||
### Changed
|
||||
- Clear the screen to wipe the vendor logo as soon as possible.
|
||||
- Image paths in `config.txt` may be relative (just file names).
|
||||
|
||||
## 2.3.1 - 2023-11-27
|
||||
|
||||
### Fixed
|
||||
- BitLocker detection is more reliable.
|
||||
|
||||
## 2.3.0 - 2023-11-27
|
||||
|
||||
### Added
|
||||
- AArch64 and ARM builds, and *shim* for AArch64.
|
||||
|
||||
### Fixed
|
||||
- Boot entry is more reliable, avoids conflicts with firmware entries.
|
||||
|
||||
## 2.2.0 - 2023-11-17
|
||||
|
||||
### Added
|
||||
- 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
|
||||
|
||||
136
Makefile
136
Makefile
@@ -1,44 +1,122 @@
|
||||
CC = $(CC_PREFIX)-gcc
|
||||
CFLAGS = -std=c11 -O2 -ffreestanding -mno-red-zone -fno-stack-protector -Wshadow -Wall -Wunused -Werror-implicit-function-declaration -Werror
|
||||
CC = clang
|
||||
CFLAGS = -target $(CLANG_TARGET) -ffreestanding -fshort-wchar
|
||||
CFLAGS += -std=c17 -Wshadow -Wall -Wunused -Werror-implicit-function-declaration
|
||||
CFLAGS += -I$(GNUEFI_INC) -I$(GNUEFI_INC)/$(GNUEFI_ARCH) -I$(GNUEFI_INC)/protocol
|
||||
LDFLAGS = -nostdlib -shared -Wl,-dll -Wl,--subsystem,10 -e _EfiMain
|
||||
LIBS = -L$(GNUEFI_LIB) -lefi -lgcc
|
||||
CFLAGS += $(ARCH_CFLAGS)
|
||||
LDFLAGS = -target $(CLANG_TARGET) -nostdlib -Wl,-entry:efi_main -Wl,-subsystem:efi_application -fuse-ld=lld
|
||||
ARCH_CFLAGS = -O2 -mno-red-zone
|
||||
|
||||
GNUEFI_INC = /usr/$(CC_PREFIX)/include/efi
|
||||
GNUEFI_LIB = /usr/$(CC_PREFIX)/lib
|
||||
GNUEFI_INC = gnu-efi/inc
|
||||
|
||||
FILES_C = src/main.c src/util.c src/types.c src/config.c
|
||||
FILES_C = src/main.c src/util.c src/types.c src/config.c src/sbat.c src/efi.c
|
||||
FILES_H = $(wildcard src/*.h)
|
||||
FILES_CS = src/Setup.cs src/Esp.cs src/Efi.cs
|
||||
GIT_DESCRIBE = $(firstword $(shell git describe --tags) unknown)
|
||||
CFLAGS += '-DGIT_DESCRIBE=L"$(GIT_DESCRIBE)"'
|
||||
ZIPDIR = HackBGRT-$(GIT_DESCRIBE:v%=%)
|
||||
ZIP = $(ZIPDIR).zip
|
||||
GIT_DESCRIBE := $(firstword $(GIT_DESCRIBE) $(shell git describe --tags) unknown)
|
||||
CFLAGS += '-DGIT_DESCRIBE_W=L"$(GIT_DESCRIBE)"' '-DGIT_DESCRIBE="$(GIT_DESCRIBE)"'
|
||||
RELEASE_NAME = HackBGRT-$(GIT_DESCRIBE:v%=%)
|
||||
|
||||
EFI_ARCH_LIST = x64 ia32 aa64 arm
|
||||
EFI_SIGNED_FILES = $(patsubst %,efi-signed/boot%.efi,$(EFI_ARCH_LIST))
|
||||
|
||||
.PHONY: all efi efi-signed setup release clean
|
||||
|
||||
all: efi setup
|
||||
@echo "Run 'make efi-signed' to sign the EFI executables."
|
||||
@echo "Run 'make release' to build a release-ready ZIP archive."
|
||||
@echo "Run 'make run-qemu-<arch>' to test the EFI executables with QEMU."
|
||||
|
||||
efi: $(patsubst %,efi/boot%.efi,$(EFI_ARCH_LIST))
|
||||
@echo "EFI executables are in the efi/ directory."
|
||||
|
||||
efi-signed: $(patsubst %,efi-signed/boot%.efi,$(EFI_ARCH_LIST))
|
||||
@echo "Signed EFI executables are in the efi-signed/ directory."
|
||||
|
||||
all: efi setup zip
|
||||
efi: bootx64.efi bootia32.efi
|
||||
setup: setup.exe
|
||||
|
||||
zip: $(ZIP)
|
||||
$(ZIP): bootx64.efi bootia32.efi config.txt splash.bmp setup.exe README.md CHANGELOG.md README.efilib LICENSE
|
||||
test ! -d "$(ZIPDIR)"
|
||||
mkdir "$(ZIPDIR)"
|
||||
cp -a $^ "$(ZIPDIR)" || (rm -rf "$(ZIPDIR)"; exit 1)
|
||||
7z a -mx=9 "$(ZIP)" "$(ZIPDIR)" || (rm -rf "$(ZIPDIR)"; exit 1)
|
||||
rm -rf "$(ZIPDIR)"
|
||||
release: release/$(RELEASE_NAME).zip
|
||||
@echo "Current version is packaged: $<"
|
||||
|
||||
release/$(RELEASE_NAME): $(EFI_SIGNED_FILES) certificate.cer config.txt splash.bmp setup.exe README.md CHANGELOG.md README.efilib LICENSE shim-signed/* shim.md
|
||||
rm -rf $@
|
||||
tar c --transform=s,^,$@/, $^ | tar x
|
||||
|
||||
release/$(RELEASE_NAME).zip: release/$(RELEASE_NAME)
|
||||
rm -rf $@
|
||||
(cd release; 7z a -mx=9 "$(RELEASE_NAME).zip" "$(RELEASE_NAME)" -bd -bb1)
|
||||
|
||||
src/GIT_DESCRIBE.cs: $(FILES_CS) $(FILES_C) $(FILES_H)
|
||||
echo 'public class GIT_DESCRIBE { public const string data = "$(GIT_DESCRIBE)"; }' > $@
|
||||
|
||||
setup.exe: $(FILES_CS) src/GIT_DESCRIBE.cs
|
||||
csc /define:GIT_DESCRIBE /out:$@ $^
|
||||
csc /nologo /define:GIT_DESCRIBE /out:$@ $^
|
||||
|
||||
bootx64.efi: CC_PREFIX = x86_64-w64-mingw32
|
||||
bootx64.efi: GNUEFI_ARCH = x86_64
|
||||
bootx64.efi: $(FILES_C)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) -s
|
||||
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
|
||||
|
||||
bootia32.efi: CC_PREFIX = i686-w64-mingw32
|
||||
bootia32.efi: GNUEFI_ARCH = ia32
|
||||
bootia32.efi: $(FILES_C)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) -s
|
||||
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)
|
||||
|
||||
22
README.md
22
README.md
@@ -6,23 +6,31 @@ HackBGRT is intended as a boot logo changer for UEFI-based Windows systems.
|
||||
|
||||
When booting on a UEFI-based computer, Windows may show a vendor-defined logo which is stored on the UEFI firmware in a section called Boot Graphics Resource Table (BGRT). It's usually very difficult to change the image permanently, but a custom UEFI application may be used to overwrite it during the boot. HackBGRT does exactly that.
|
||||
|
||||
**Note:** The original logo is often visible for a moment before HackBGRT is started. This is expected, please do not report this "bug". This can't be changed without modifying computer firmware, which this project will not do.
|
||||
|
||||
## Usage
|
||||
|
||||
**Important:** If you mess up the installation, your system may become unbootable! Create a rescue disk before use. This software comes with no warranty. Use at your own risk.
|
||||
|
||||
* Make sure that your computer is booting with UEFI.
|
||||
* Make sure that Secure Boot is disabled, unless you know how to sign EFI applications.
|
||||
* Make sure that you have read the Secure Boot instructions.
|
||||
* Make sure that BitLocker is disabled, or find your recovery key.
|
||||
|
||||
### 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.
|
||||
* You may need to manually disable Secure Boot and then retry.
|
||||
* The installer will launch Paint for editing the image.
|
||||
* If Windows later restores the original boot loader, just reinstall.
|
||||
* If you wish to change the image or other configuration, just reinstall.
|
||||
* For advanced settings, edit `config.txt` before installing. No extra support provided!
|
||||
* After installing, read the instructions in [shim.md](shim.md) and reboot your computer.
|
||||
|
||||
### Quiet (batch) installation
|
||||
|
||||
@@ -35,11 +43,13 @@ When booting on a UEFI-based computer, Windows may show a vendor-defined logo wh
|
||||
* `disable-bootmgr` – use `bcdedit` 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
|
||||
@@ -62,20 +72,18 @@ The configuration options are described in `config.txt`, which the installer cop
|
||||
|
||||
## Images
|
||||
|
||||
The image path can be changed in the configuration file. The default path is `[EFI System Partition]\EFI\HackBGRT\splash.bmp`.
|
||||
If you only need one image, just edit `splash.bmp` to your needs.
|
||||
|
||||
The installer copies and converts files whose `path` starts with `\EFI\HackBGRT\`. For example, to use a file named `my.jpg`, copy it in the installer folder (same folder as `setup.exe`) and set the image path in `config.txt` to `path=\EFI\HackBGFT\my.jpg`.
|
||||
Advanced users may edit the `config.txt` to define multiple images, in which case one is picked at random. The installer copies and converts the images. For example, to use a file named `my.jpg`, copy it in the installer folder (same folder as `setup.exe`) and set the image path in `config.txt` to `path=my.jpg` before running the installer.
|
||||
|
||||
If you copy an image file to ESP manually, note that the image must be a 24-bit BMP file with a 54-byte header. That's a TrueColor BMP3 in Imagemagick, or 24-bit BMP/DIB in Microsoft Paint.
|
||||
|
||||
Advanced users may edit the `config.txt` to define multiple images, in which case one is picked at random.
|
||||
|
||||
## Recovery
|
||||
|
||||
If something breaks and you can't boot to Windows, you need to use the Windows installation disk (or recovery disk) to fix boot issues.
|
||||
|
||||
## Building
|
||||
|
||||
* Compiler: GCC targeting w64-mingw32
|
||||
* Compiler: Clang
|
||||
* Compiler flags: see Makefile
|
||||
* Libraries: gnu-efi
|
||||
|
||||
19
config.txt
19
config.txt
@@ -16,16 +16,14 @@ boot=MS
|
||||
# - "keep" to keep the firmware logo. Also keeps coordinates by default.
|
||||
# - "remove" to remove the BGRT. Makes x and y meaningless.
|
||||
# - "black" to use only a black image. Makes x and y meaningless.
|
||||
# - "path=..." to read a BMP file.
|
||||
# * NOTE: For path=\EFI\HackBGRT\*, the installer will copy and convert the file if necessary.
|
||||
# * NOTE: For other paths, make sure that the file is a 24-bit BMP file with a 54-byte header.
|
||||
# * NOTE: The file must be on the EFI System Partition. Do not add a drive letter!
|
||||
# - "path=file.bmp" to read an image file.
|
||||
# * NOTE: The installer can copy and convert BMP, PNG, JPEG, GIF.
|
||||
# Examples:
|
||||
# - image=remove
|
||||
# - image=black
|
||||
# - image= x=0 y=-200 path=\EFI\HackBGRT\topimage.bmp
|
||||
# - image= n=1 o=90 path=\EFI\HackBGRT\sideways.bmp
|
||||
# - image= n=50 y=999999 o=keep path=\EFI\HackBGRT\probable.bmp
|
||||
# - image= x=0 y=-200 path=topimage.bmp
|
||||
# - image= n=1 o=90 path=sideways.bmp
|
||||
# - image= n=50 y=999999 o=keep path=probable.bmp
|
||||
# The above examples together would produce
|
||||
# - 1/54 chance for the default OS logo
|
||||
# - 1/54 chance for black screen
|
||||
@@ -33,11 +31,16 @@ boot=MS
|
||||
# - 1/54 chance for splash.bmp, centered, orientation set to 90 degrees
|
||||
# - 50/54 chance for probable.bmp, at the bottom edge, explicitly default orientation
|
||||
# Default: just one image.
|
||||
image= y=-200 path=\EFI\HackBGRT\splash.bmp
|
||||
image= y=-200 path=splash.bmp
|
||||
|
||||
# Preferred resolution. Use 0x0 for maximum and -1x-1 for original.
|
||||
resolution=0x0
|
||||
|
||||
# Logging (0 for disabled, 1 for enabled).
|
||||
# When logging is enabled, setup.exe can show debug information about the current boot.
|
||||
# The log might occupy a few kilobytes of RAM.
|
||||
log=1
|
||||
|
||||
# Debug mode (0 for disabled, 1 for enabled).
|
||||
# Shows debug information and prompts for keypress before booting.
|
||||
debug=0
|
||||
|
||||
1
gnu-efi
Submodule
1
gnu-efi
Submodule
Submodule gnu-efi added at 965f557ab7
55
shim-signed/COPYRIGHT
Normal file
55
shim-signed/COPYRIGHT
Normal file
@@ -0,0 +1,55 @@
|
||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: shim
|
||||
Upstream-Contact: Peter Jones <pjones@redhat.com>
|
||||
Source: https://github.com/rhboot/shim
|
||||
|
||||
Files: *
|
||||
Copyright: 2012 Red Hat, Inc
|
||||
2009-2012 Intel Corporation
|
||||
License: BSD-2-Clause
|
||||
|
||||
Files: debian/po/cs.po
|
||||
Copyright: 2018 Michal Simunek <michal.simunek@gmail.com>
|
||||
License: BSD-2-Clause
|
||||
|
||||
Files: debian/po/de.po
|
||||
Copyright: 2017, 2018 Markus Hiereth <markus.hiereth@freenet.de>
|
||||
License: BSD-2-Clause
|
||||
|
||||
Files: debian/po/fr.po
|
||||
Copyright: 2017, 2018 Alban Vidal <alban.vidal@zordhak.fr>
|
||||
License: BSD-2-Clause
|
||||
|
||||
Files: debian/po/nl.po
|
||||
Copyright: 2017, 2018 Frans Spiesschaert <Frans.Spiesschaert@yucom.be>
|
||||
License: BSD-2-Clause
|
||||
|
||||
Files: debian/po/pt.po
|
||||
Copyright: 2017, 2018 Rui Branco <ruipb@debianpt.org>
|
||||
License: BSD-2-Clause
|
||||
|
||||
License: BSD-2-Clause
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
.
|
||||
Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
.
|
||||
Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the
|
||||
distribution.
|
||||
.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
BIN
shim-signed/mmaa64.efi
Normal file
BIN
shim-signed/mmaa64.efi
Normal file
Binary file not shown.
BIN
shim-signed/mmia32.efi
Normal file
BIN
shim-signed/mmia32.efi
Normal file
Binary file not shown.
BIN
shim-signed/mmx64.efi
Normal file
BIN
shim-signed/mmx64.efi
Normal file
Binary file not shown.
BIN
shim-signed/shimaa64.efi
Normal file
BIN
shim-signed/shimaa64.efi
Normal file
Binary file not shown.
BIN
shim-signed/shimia32.efi
Normal file
BIN
shim-signed/shimia32.efi
Normal file
Binary file not shown.
BIN
shim-signed/shimx64.efi
Normal file
BIN
shim-signed/shimx64.efi
Normal file
Binary file not shown.
286
shim.md
Normal file
286
shim.md
Normal file
@@ -0,0 +1,286 @@
|
||||
# Secure Boot and *shim*
|
||||
|
||||
Secure Boot accepts only trusted files during boot. The *shim* boot loader is a tool which allows you to select which files to trust. HackBGRT installs *shim* for you, but you need to configure it with these instructions.
|
||||
|
||||
On the first boot after installing HackBGRT, you will see an error message saying "Verification failed". You need to press a key to enter the setup tool (MOKManager) where you can choose to trust HackBGRT. After that, use arrow keys to navigate and *Enter* to continue as described below.
|
||||
|
||||
## 1. Verification failed
|
||||
|
||||
This is the start of *shim* configuration.
|
||||
|
||||
```
|
||||
ERROR
|
||||
Verification failed: (0x1A) Security Violation
|
||||
+----+
|
||||
| OK |
|
||||
+----+
|
||||
```
|
||||
|
||||
Select `OK`, *Enter*.
|
||||
|
||||
```
|
||||
Shim UEFI key management
|
||||
Press any key to perform MOK management
|
||||
Booting in 5 seconds
|
||||
```
|
||||
|
||||
Press a key quickly to enter *MOK management* or the *MOKManager* program.
|
||||
|
||||
## 2. MOK management
|
||||
|
||||
```
|
||||
Perform MOK management
|
||||
|
||||
Continue to boot
|
||||
Enroll key from disk
|
||||
Enroll hash from disk
|
||||
```
|
||||
|
||||
Select `Enroll hash from disk`, *Enter*. This is the safest option where you choose to trust only a specific version of HackBGRT.
|
||||
|
||||
You can also choose to `Enroll key from disk`, which means that you choose to trust anything signed with the same certificate. How do you know if it's safe? You don't – that's why you should rather use the other option or build your own version of HackBGRT with your own certificate.
|
||||
|
||||
## 3a. Enroll hash from disk
|
||||
|
||||
```
|
||||
Select Binary
|
||||
|
||||
The Selected Binary will have its hash Enrolled
|
||||
This means it will subsequently Boot with no prompting
|
||||
Remember to make sure it is a genuine binary before enrolling its hash
|
||||
|
||||
+----------------+
|
||||
| YOUR DISK NAME |
|
||||
+----------------+
|
||||
```
|
||||
|
||||
Select the disk, *Enter*.
|
||||
|
||||
```
|
||||
+---------------+
|
||||
| EFI/ |
|
||||
| loader/ |
|
||||
| vmlinuz-linux |
|
||||
+---------------+
|
||||
```
|
||||
|
||||
Select `EFI/`, *Enter*.
|
||||
|
||||
```
|
||||
+------------+
|
||||
| ../ |
|
||||
| Boot/ |
|
||||
| HackBGRT/ |
|
||||
| Microsoft/ |
|
||||
+------------+
|
||||
```
|
||||
|
||||
Select `HackBGRT/`, *Enter*.
|
||||
|
||||
```
|
||||
+-----------------+
|
||||
| ../ |
|
||||
| grubx64.efi |
|
||||
| loader.efi |
|
||||
| mmx64.efi |
|
||||
| certificate.cer |
|
||||
| splash.bmp |
|
||||
| config.txt |
|
||||
+-----------------+
|
||||
```
|
||||
|
||||
Select `grubx64.efi`, *Enter*.
|
||||
|
||||
```
|
||||
[Enroll MOK]
|
||||
|
||||
+------------+
|
||||
| View key 0 |
|
||||
| Continue |
|
||||
+------------+
|
||||
```
|
||||
|
||||
To verify the key contents, select `View key 0`, *Enter*.
|
||||
|
||||
```
|
||||
SHA256 hash
|
||||
(some hexadecimal values)
|
||||
```
|
||||
|
||||
Press *Enter* to continue.
|
||||
|
||||
```
|
||||
[Enroll MOK]
|
||||
|
||||
+------------+
|
||||
| View key 0 |
|
||||
| Continue |
|
||||
+------------+
|
||||
```
|
||||
|
||||
Select `Continue`, *Enter*.
|
||||
|
||||
```
|
||||
Enroll the key(s)?
|
||||
|
||||
+-----+
|
||||
| No |
|
||||
| Yes |
|
||||
+-----+
|
||||
```
|
||||
|
||||
Select `Yes`, *Enter*.
|
||||
|
||||
```
|
||||
Perform MOK management
|
||||
|
||||
+-----------------------+
|
||||
| Reboot |
|
||||
| Enroll key from disk |
|
||||
| Enroll hash from disk |
|
||||
+-----------------------+
|
||||
```
|
||||
|
||||
Select `Reboot`, *Enter*.
|
||||
|
||||
You are now ready to boot using HackBGRT.
|
||||
|
||||
## 3b. Enroll key from disk
|
||||
|
||||
```
|
||||
Select Key
|
||||
|
||||
The selected key will be enrolled into the MOK database
|
||||
This means any binaries signed with it will be run without prompting
|
||||
Remember to make sure it is a genuine key before Enrolling it
|
||||
|
||||
+----------------+
|
||||
| YOUR DISK NAME |
|
||||
+----------------+
|
||||
```
|
||||
|
||||
Select the disk, *Enter*.
|
||||
|
||||
```
|
||||
+---------------+
|
||||
| EFI/ |
|
||||
| loader/ |
|
||||
| vmlinuz-linux |
|
||||
+---------------+
|
||||
```
|
||||
|
||||
Select `EFI/`, *Enter*.
|
||||
|
||||
```
|
||||
+------------+
|
||||
| ../ |
|
||||
| Boot/ |
|
||||
| HackBGRT/ |
|
||||
| Microsoft/ |
|
||||
+------------+
|
||||
```
|
||||
|
||||
Select `HackBGRT/`, *Enter*.
|
||||
|
||||
```
|
||||
+-----------------+
|
||||
| ../ |
|
||||
| grubx64.efi |
|
||||
| loader.efi |
|
||||
| mmx64.efi |
|
||||
| certificate.cer |
|
||||
| splash.bmp |
|
||||
| config.txt |
|
||||
+-----------------+
|
||||
```
|
||||
|
||||
Select `certificate.cer`, *Enter*.
|
||||
|
||||
```
|
||||
[Enroll MOK]
|
||||
|
||||
+------------+
|
||||
| View key 0 |
|
||||
| Continue |
|
||||
+------------+
|
||||
```
|
||||
|
||||
To verify the key contents, select `View key 0`, *Enter*.
|
||||
|
||||
```
|
||||
[Extended Key Usage]
|
||||
OID: Code Signing
|
||||
|
||||
[Serial Number]
|
||||
6B:24:52:E9:3B:84:41:73:B0:22:92:E8:BE:8E:38:85:
|
||||
|
||||
[Issuer]
|
||||
CN=HackBGRT Secure Boot Signer, O=Metabolix
|
||||
|
||||
[Subject]
|
||||
CN=HackBGRT Secure Boot Signer, O=Metabolix
|
||||
|
||||
[Valid Not Before]
|
||||
Nov 9 13:43:56 2023 GMT
|
||||
|
||||
[Valid Not After]
|
||||
Jan 19 03:14:07 2037 GMT
|
||||
|
||||
[Fingerprint]
|
||||
79 8E 64 40 D1 D1 F4 53 30 8D
|
||||
A0 83 A4 77 FE 57 45 30 36 60
|
||||
```
|
||||
|
||||
Press *Enter* to continue.
|
||||
|
||||
```
|
||||
[Enroll MOK]
|
||||
|
||||
+------------+
|
||||
| View key 0 |
|
||||
| Continue |
|
||||
+------------+
|
||||
```
|
||||
|
||||
Select `Continue`, *Enter*.
|
||||
|
||||
```
|
||||
Enroll the key(s)?
|
||||
|
||||
+-----+
|
||||
| No |
|
||||
| Yes |
|
||||
+-----+
|
||||
```
|
||||
|
||||
Select `Yes`, *Enter*.
|
||||
|
||||
```
|
||||
Perform MOK management
|
||||
|
||||
+-----------------------+
|
||||
| Reboot |
|
||||
| Enroll key from disk |
|
||||
| Enroll hash from disk |
|
||||
+-----------------------+
|
||||
```
|
||||
|
||||
Select `Reboot`, *Enter*.
|
||||
|
||||
You are now ready to boot using HackBGRT.
|
||||
|
||||
## Tutorial: *shim* for dummies
|
||||
|
||||
To install *shim* manually, follow these steps (assuming x64 architecture):
|
||||
|
||||
1. Get *shim*, preferably *shim-signed*.
|
||||
2. Rename your boot loader to `grubx64.efi`.
|
||||
3. Copy `shimx64.efi` where your loader used to be.
|
||||
4. Copy `mmx64.efi` to the same folder.
|
||||
|
||||
The *shim* boot process is as follows:
|
||||
|
||||
1. Your computer starts `your-loader-name.efi`, which is now really *shim*.
|
||||
2. Next, *shim* tries to load `grubx64.efi`.
|
||||
3. If `grubx64.efi` is trusted, the boot process continues.
|
||||
4. Otherwise, *shim* offers to launch *MOKManager* `mmx64.efi`, and you can try again after that.
|
||||
100
src/Efi.cs
100
src/Efi.cs
@@ -55,6 +55,9 @@ public class Efi {
|
||||
* @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}'";
|
||||
@@ -90,12 +93,19 @@ public class Efi {
|
||||
var pos = 6 + 2 * (Label.Length + 1);
|
||||
var pathNodesEnd = pos + pathNodesLength;
|
||||
DevicePathNodes = new List<DevicePathNode>();
|
||||
Arguments = new byte[0];
|
||||
while (pos + 4 <= pathNodesEnd) {
|
||||
var len = BitConverter.ToUInt16(data, pos + 2);
|
||||
if (len < 4 || pos + len > pathNodesEnd) {
|
||||
return; // throw new Exception("Bad entry.");
|
||||
}
|
||||
DevicePathNodes.Add(new DevicePathNode(data.Skip(pos).Take(len).ToArray()));
|
||||
var node = new DevicePathNode(data.Skip(pos).Take(len).ToArray());
|
||||
DevicePathNodes.Add(node);
|
||||
if (node.Type == 0x7f && node.SubType == 0xff) {
|
||||
// End of entire device path.
|
||||
// Apparently some firmwares produce paths with unused nodes at the end.
|
||||
break;
|
||||
}
|
||||
pos += len;
|
||||
}
|
||||
Arguments = data.Skip(pathNodesEnd).ToArray();
|
||||
@@ -136,8 +146,19 @@ public class Efi {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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";
|
||||
|
||||
/**
|
||||
@@ -364,9 +385,10 @@ public class Efi {
|
||||
*
|
||||
* @param label Label of the boot entry.
|
||||
* @param fileName File name of the boot entry.
|
||||
* @param alwaysCopyFromMS If true, do not preserve any existing data.
|
||||
* @param dryRun Don't actually create the entry.
|
||||
*/
|
||||
public static void MakeAndEnableBootEntry(string label, string fileName, bool dryRun = false) {
|
||||
public static void MakeAndEnableBootEntry(string label, string fileName, bool alwaysCopyFromMS, bool dryRun = false) {
|
||||
Variable msEntry = null, ownEntry = null;
|
||||
UInt16 msNum = 0, ownNum = 0;
|
||||
|
||||
@@ -389,10 +411,11 @@ public class Efi {
|
||||
Setup.Log($"Read EFI variable: {bootOrder}");
|
||||
Setup.Log($"Read EFI variable: {bootCurrent}");
|
||||
var bootOrderInts = new List<UInt16>(BytesToUInt16s(bootOrder.Data));
|
||||
foreach (var num in BytesToUInt16s(bootCurrent.Data).Concat(bootOrderInts).Concat(Enumerable.Range(0, 0xffff).Select(i => (UInt16) i))) {
|
||||
foreach (var num in BytesToUInt16s(bootCurrent.Data).Concat(bootOrderInts).Concat(Enumerable.Range(0, 0xff).Select(i => (UInt16) i))) {
|
||||
var entry = GetVariable(String.Format("Boot{0:X04}", num));
|
||||
if (entry.Data == null) {
|
||||
if (ownEntry == null) {
|
||||
// Use only Boot0000 .. Boot00FF because some firmwares expect that.
|
||||
if (ownEntry == null && num < 0x100) {
|
||||
ownNum = num;
|
||||
ownEntry = entry;
|
||||
}
|
||||
@@ -420,12 +443,28 @@ public class Efi {
|
||||
throw new Exception("MakeBootEntry: Windows Boot Manager not found.");
|
||||
} else {
|
||||
Setup.Log($"Read EFI variable: {msEntry}");
|
||||
Setup.Log($"Read EFI variable: {ownEntry}");
|
||||
// Make a new boot entry using the MS entry as a starting point.
|
||||
var entryData = new BootEntryData(msEntry.Data);
|
||||
entryData.Arguments = Encoding.UTF8.GetBytes(label + "\0");
|
||||
if (!alwaysCopyFromMS && ownEntry.Data != null) {
|
||||
entryData = new BootEntryData(ownEntry.Data);
|
||||
// Shim expects the arguments to be a filename or nothing.
|
||||
// But BCDEdit expects some Microsoft-specific data.
|
||||
// Modify the entry so that BCDEdit still recognises it
|
||||
// but the data becomes a valid UCS-2 / UTF-16LE file name.
|
||||
var str = new string(entryData.Arguments.Take(12).Select(c => (char) c).ToArray());
|
||||
if (str == "WINDOWS\0\x01\0\0\0") {
|
||||
entryData.Arguments[8] = (byte) 'X';
|
||||
} else if (str != "WINDOWS\0\x58\0\0\0") {
|
||||
// Not recognized. Clear the arguments.
|
||||
entryData.Arguments = new byte[0];
|
||||
}
|
||||
} else {
|
||||
entryData.Arguments = new byte[0];
|
||||
entryData.Label = label;
|
||||
entryData.FileName = fileName;
|
||||
}
|
||||
entryData.Attributes = 1; // LOAD_OPTION_ACTIVE
|
||||
entryData.Label = label;
|
||||
entryData.FileName = fileName;
|
||||
ownEntry.Attributes = 7; // EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS
|
||||
ownEntry.Data = entryData.ToBytes();
|
||||
SetVariable(ownEntry, dryRun);
|
||||
@@ -445,6 +484,49 @@ public class Efi {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the boot entries.
|
||||
*/
|
||||
public static void LogBootEntries() {
|
||||
try {
|
||||
var bootOrder = GetVariable("BootOrder");
|
||||
var bootCurrent = GetVariable("BootCurrent");
|
||||
Setup.Log($"LogBootOrder: {bootOrder}");
|
||||
Setup.Log($"LogBootOrder: {bootCurrent}");
|
||||
var bootOrderInts = new List<UInt16>(BytesToUInt16s(bootOrder.Data));
|
||||
// Windows can't enumerate EFI variables, and trying them all is too slow.
|
||||
// BootOrder + BootCurrent + the first 0xff entries should be enough.
|
||||
var seen = new HashSet<UInt16>();
|
||||
foreach (var num in bootOrderInts.Concat(BytesToUInt16s(bootCurrent.Data)).Concat(Enumerable.Range(0, 0xff).Select(i => (UInt16) i))) {
|
||||
if (seen.Contains(num)) {
|
||||
continue;
|
||||
}
|
||||
seen.Add(num);
|
||||
var entry = GetVariable(String.Format("Boot{0:X04}", num));
|
||||
if (entry.Data != null) {
|
||||
Setup.Log($"LogBootOrder: {entry}");
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Setup.Log($"LogBootOrder failed: {e.ToString()}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve HackBGRT log collected during boot.
|
||||
*/
|
||||
public static string GetHackBGRTLog() {
|
||||
try {
|
||||
var log = GetVariable("HackBGRTLog", EFI_HACKBGRT_GUID);
|
||||
if (log.Data == null) {
|
||||
return "(null)";
|
||||
}
|
||||
return new string(BytesToUInt16s(log.Data).Select(i => (char)i).ToArray());
|
||||
} catch (Exception e) {
|
||||
return $"Log not found: {e.ToString()}";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the BGRT table (for debugging).
|
||||
*/
|
||||
@@ -456,14 +538,14 @@ public class Efi {
|
||||
var ret = GetSystemFirmwareTable(acpiBE, bgrtLE, buf, size);
|
||||
if (ret == size) {
|
||||
var hex = BitConverter.ToString(buf).Replace("-", " ");
|
||||
Setup.Log($"LogBGRT: {hex}");
|
||||
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: {e}");
|
||||
Setup.Log($"LogBGRT failed: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
272
src/Setup.cs
272
src/Setup.cs
@@ -10,6 +10,8 @@ using System.Diagnostics;
|
||||
using System.Security.Principal;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Management;
|
||||
using Microsoft.Win32;
|
||||
|
||||
[assembly: AssemblyInformationalVersionAttribute(GIT_DESCRIBE.data)]
|
||||
[assembly: AssemblyProductAttribute("HackBGRT")]
|
||||
@@ -51,6 +53,7 @@ public class Setup {
|
||||
public enum BootLoaderType {
|
||||
None,
|
||||
Own,
|
||||
Shim,
|
||||
Microsoft,
|
||||
Other,
|
||||
}
|
||||
@@ -61,12 +64,14 @@ public class Setup {
|
||||
"allow-secure-boot",
|
||||
"allow-bitlocker",
|
||||
"allow-bad-loader",
|
||||
"skip-shim",
|
||||
"enable-entry", "disable-entry",
|
||||
"enable-bcdedit", "disable-bcdedit",
|
||||
"enable-overwrite", "disable-overwrite",
|
||||
"disable",
|
||||
"uninstall",
|
||||
"boot-to-fw",
|
||||
"show-boot-log",
|
||||
};
|
||||
|
||||
/** @var The target directory. */
|
||||
@@ -94,6 +99,9 @@ public class Setup {
|
||||
/** @var Run in batch mode? */
|
||||
protected bool Batch;
|
||||
|
||||
/** @var Is the loader signed? */
|
||||
protected bool LoaderIsSigned = false;
|
||||
|
||||
/**
|
||||
* Output a line.
|
||||
*/
|
||||
@@ -136,10 +144,9 @@ public class Setup {
|
||||
*
|
||||
* @param app Path to the application.
|
||||
* @param args The argument string.
|
||||
* @param nullOnFail If set, return null if the program exits with a code other than 0, even if there was some output.
|
||||
* @return The output, or null if the execution failed.
|
||||
* @return The output and exit code.
|
||||
*/
|
||||
public static string Execute(string app, string args, bool nullOnFail) {
|
||||
public static (string Output, int ExitCode) Execute(string app, string args) {
|
||||
Log($"Execute: {app} {args}");
|
||||
try {
|
||||
var info = new ProcessStartInfo(app, args);
|
||||
@@ -149,13 +156,26 @@ public class Setup {
|
||||
string output = p.StandardOutput.ReadToEnd();
|
||||
p.WaitForExit();
|
||||
Log($"Exit code: {p.ExitCode}, output:\n{output}\n\n");
|
||||
return (nullOnFail && p.ExitCode != 0) ? null : output;
|
||||
return (output, p.ExitCode);
|
||||
} catch (Exception e) {
|
||||
Log($"Execute failed: {e.ToString()}");
|
||||
return null;
|
||||
return (null, -1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute another process, return the output.
|
||||
*
|
||||
* @param app Path to the application.
|
||||
* @param args The argument string.
|
||||
* @param nullOnFail If set, return null if the program exits with a code other than 0, even if there was some output.
|
||||
* @return The output, or null if the execution failed.
|
||||
*/
|
||||
public static string Execute(string app, string args, bool nullOnFail) {
|
||||
var result = Execute(app, args);
|
||||
return (nullOnFail && result.ExitCode != 0) ? null : result.Output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for required privileges (access to ESP and EFI vars).
|
||||
*
|
||||
@@ -204,6 +224,8 @@ public class Setup {
|
||||
string tmp = System.Text.Encoding.ASCII.GetString(data);
|
||||
if (tmp.IndexOf("HackBGRT") >= 0 || tmp.IndexOf("HackBgrt") >= 0) {
|
||||
return BootLoaderType.Own;
|
||||
} else if (tmp.IndexOf("UEFI shim") >= 0) {
|
||||
return BootLoaderType.Shim;
|
||||
} else if (tmp.IndexOf("Microsoft Corporation") >= 0) {
|
||||
return BootLoaderType.Microsoft;
|
||||
} else {
|
||||
@@ -229,6 +251,8 @@ public class Setup {
|
||||
0x0200 => "ia64",
|
||||
0x8664 => "x64",
|
||||
0xaa64 => "aa64",
|
||||
0x01c2 => "arm",
|
||||
0x01c4 => "arm",
|
||||
_ => $"unknown-{peArch:x4}"
|
||||
};
|
||||
} catch {
|
||||
@@ -307,10 +331,28 @@ public class Setup {
|
||||
|
||||
/**
|
||||
* Install files to ESP.
|
||||
*
|
||||
* @param skipShim Whether to skip installing shim.
|
||||
*/
|
||||
protected void InstallFiles() {
|
||||
if (!File.Exists($"boot{EfiArch}.efi")) {
|
||||
throw new SetupException($"Missing boot{EfiArch}.efi, {EfiArch} is not supported!");
|
||||
protected void InstallFiles(bool skipShim) {
|
||||
var loaderSource = Path.Combine("efi-signed", $"boot{EfiArch}.efi");
|
||||
LoaderIsSigned = true;
|
||||
if (!File.Exists(loaderSource)) {
|
||||
loaderSource = Path.Combine("efi", $"boot{EfiArch}.efi");
|
||||
LoaderIsSigned = false;
|
||||
if (!File.Exists(loaderSource)) {
|
||||
throw new SetupException($"Missing boot{EfiArch}.efi, {EfiArch} is not supported!");
|
||||
}
|
||||
}
|
||||
var shimSource = Path.Combine("shim-signed", $"shim{EfiArch}.efi");
|
||||
var mmSource = Path.Combine("shim-signed", $"mm{EfiArch}.efi");
|
||||
if (!skipShim) {
|
||||
if (!File.Exists(shimSource)) {
|
||||
throw new SetupException($"Missing shim ({shimSource}), can't install shim for {EfiArch}!");
|
||||
}
|
||||
if (!File.Exists(mmSource)) {
|
||||
throw new SetupException($"Missing MokManager ({mmSource}), can't install shim for {EfiArch}!");
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (!Directory.Exists(InstallPath)) {
|
||||
@@ -324,23 +366,76 @@ public class Setup {
|
||||
var lines = File.ReadAllLines("config.txt");
|
||||
Log($"config.txt:\n{String.Join("\n", lines)}");
|
||||
foreach (var line in lines.Where(s => s.StartsWith("image="))) {
|
||||
var delim = "path=\\EFI\\HackBGRT\\";
|
||||
var delim = "path=";
|
||||
var i = line.IndexOf(delim);
|
||||
if (i > 0) {
|
||||
InstallImageFile(line.Substring(i + delim.Length));
|
||||
var dir = "\\EFI\\HackBGRT\\";
|
||||
if (line.Substring(i + delim.Length).StartsWith(dir)) {
|
||||
InstallImageFile(line.Substring(i + delim.Length + dir.Length));
|
||||
}
|
||||
if (!line.Substring(i + delim.Length).StartsWith("\\")) {
|
||||
InstallImageFile(line.Substring(i + delim.Length));
|
||||
}
|
||||
}
|
||||
}
|
||||
InstallFile($"boot{EfiArch}.efi", "loader.efi");
|
||||
var loaderDest = "loader.efi";
|
||||
if (!skipShim) {
|
||||
InstallFile(shimSource, loaderDest);
|
||||
InstallFile(mmSource, $"mm{EfiArch}.efi");
|
||||
loaderDest = $"grub{EfiArch}.efi";
|
||||
}
|
||||
InstallFile(loaderSource, loaderDest);
|
||||
InstallFile(loaderSource, "\u4957\u444e\u574f\u0053\u0058"); // bytes "WINDOWS\0X\0" as UTF-16
|
||||
if (LoaderIsSigned) {
|
||||
InstallFile("certificate.cer");
|
||||
}
|
||||
WriteLine($"HackBGRT has been copied to {InstallPath}.");
|
||||
|
||||
var enrollHashPath = $"EFI\\HackBGRT\\{loaderDest}";
|
||||
var enrollKeyPath = "EFI\\HackBGRT\\certificate.cer";
|
||||
if (skipShim) {
|
||||
if (LoaderIsSigned) {
|
||||
WriteLine($"Remember to enroll {enrollKeyPath} in your firmware!");
|
||||
} else {
|
||||
WriteLine("This HackBGRT build is not signed. You may need to disable Secure Boot.");
|
||||
}
|
||||
} else {
|
||||
WriteLine($"On first boot, select 'Enroll hash from disk' and enroll {enrollHashPath}.");
|
||||
if (LoaderIsSigned) {
|
||||
WriteLine($"Alternatively, select 'Enroll key from disk' and enroll {enrollKeyPath}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable HackBGRT with bcdedit.
|
||||
*/
|
||||
protected void EnableBCDEdit() {
|
||||
if (DryRun) {
|
||||
WriteLine("Dry run, skip enabling with BCDEdit.");
|
||||
return;
|
||||
}
|
||||
var bcdeditEnum = Execute("bcdedit", "/enum firmware");
|
||||
if (bcdeditEnum.ExitCode != 0) {
|
||||
WriteLine("BCDEdit is not working. Fix it or try another method.");
|
||||
if (bcdeditEnum.Output != null) {
|
||||
var lastLine = bcdeditEnum.Output.Split("\n".ToCharArray()).Last();
|
||||
WriteLine($"BCDEdit output: {lastLine}");
|
||||
WriteLine("Disable antivirus, check your hard disk, or search 'how to fix 0x800703EE'.");
|
||||
}
|
||||
throw new SetupException("Failed to enable HackBGRT with BCDEdit!");
|
||||
}
|
||||
try {
|
||||
var re = new Regex("[{][0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}[}]");
|
||||
var guid = re.Match(Execute("bcdedit", "/copy {bootmgr} /d HackBGRT", true)).Value;
|
||||
var bcdCopy = Execute("bcdedit", "/copy {bootmgr} /d HackBGRT").Output;
|
||||
if (bcdCopy == null) {
|
||||
throw new SetupException("Failed to create a new BCDEdit entry for HackBGRT!");
|
||||
}
|
||||
var match = re.Match(bcdCopy);
|
||||
if (!match.Success) {
|
||||
throw new SetupException("Failed to get a GUID for the new BCDEdit entry for HackBGRT!");
|
||||
}
|
||||
var guid = match.Value;
|
||||
Execute("bcdedit", $"/set {guid} device partition={Esp.Location}", true);
|
||||
Execute("bcdedit", $"/set {guid} path \\EFI\\HackBGRT\\loader.efi", true);
|
||||
foreach (var arg in new string[] { "locale", "inherit", "default", "resumeobject", "displayorder", "toolsdisplayorder", "timeout" }) {
|
||||
@@ -348,6 +443,12 @@ public class Setup {
|
||||
}
|
||||
var fwbootmgr = "{fwbootmgr}";
|
||||
Execute("bcdedit", $"/set {fwbootmgr} displayorder {guid} /addfirst", true);
|
||||
WriteLine("Enabled NVRAM entry for HackBGRT with BCDEdit.");
|
||||
// Verify that the entry was created.
|
||||
Execute("bcdedit", "/enum firmware", true);
|
||||
Efi.MakeAndEnableBootEntry("HackBGRT", "\\EFI\\HackBGRT\\loader.efi", false, DryRun);
|
||||
Execute("bcdedit", $"/enum {guid}", true);
|
||||
Efi.LogBootEntries();
|
||||
} catch (Exception e) {
|
||||
Log($"EnableBCDEdit failed: {e.ToString()}");
|
||||
throw new SetupException("Failed to enable HackBGRT with BCDEdit!");
|
||||
@@ -372,14 +473,18 @@ public class Setup {
|
||||
} else if (entry.IndexOf("HackBGRT") >= 0) {
|
||||
found = true;
|
||||
Log($"Disabling HackBGRT entry {guid}.");
|
||||
if (Execute("bcdedit", $"/delete {guid}", true) == null) {
|
||||
if (!DryRun && Execute("bcdedit", $"/delete {guid}", true) == null) {
|
||||
Log($"DisableBCDEdit failed to delete {guid}.");
|
||||
} else {
|
||||
disabled = true;
|
||||
}
|
||||
disabled = true;
|
||||
}
|
||||
}
|
||||
if (found && !disabled) {
|
||||
throw new SetupException("Failed to disable HackBGRT with BCDEdit!");
|
||||
if (found) {
|
||||
if (!disabled) {
|
||||
throw new SetupException("Failed to disable HackBGRT with BCDEdit!");
|
||||
}
|
||||
WriteLine("Disabled NVRAM entry for HackBGRT with BCDEdit.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,8 +492,11 @@ public class Setup {
|
||||
* Enable HackBGRT boot entry.
|
||||
*/
|
||||
protected void EnableEntry() {
|
||||
Efi.MakeAndEnableBootEntry("HackBGRT", "\\EFI\\HackBGRT\\loader.efi", DryRun);
|
||||
Efi.MakeAndEnableBootEntry("HackBGRT", "\\EFI\\HackBGRT\\loader.efi", true, DryRun);
|
||||
WriteLine("Enabled NVRAM entry for HackBGRT.");
|
||||
// Verify that the entry was created.
|
||||
Efi.LogBootEntries();
|
||||
Execute("bcdedit", "/enum firmware", true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -405,7 +513,6 @@ public class Setup {
|
||||
protected void OverwriteMsLoader() {
|
||||
var ms = Esp.MsLoaderPath;
|
||||
var backup = BackupLoaderPath;
|
||||
var own = Path.Combine(InstallPath, "loader.efi");
|
||||
|
||||
if (DetectLoader(ms) == BootLoaderType.Microsoft) {
|
||||
InstallFile(ms, backup, false);
|
||||
@@ -414,8 +521,13 @@ public class Setup {
|
||||
// Duplicate check, but better to be sure...
|
||||
throw new SetupException("Missing MS boot loader backup!");
|
||||
}
|
||||
var msDir = Path.GetDirectoryName(ms);
|
||||
var msGrub = Path.Combine(msDir, $"grub{EfiArch}.efi");
|
||||
var msMm = Path.Combine(msDir, $"mm{EfiArch}.efi");
|
||||
try {
|
||||
InstallFile(own, ms, false);
|
||||
InstallFile(Path.Combine(InstallPath, "loader.efi"), ms, false);
|
||||
InstallFile(Path.Combine(InstallPath, $"grub{EfiArch}.efi"), msGrub, false);
|
||||
InstallFile(Path.Combine(InstallPath, $"mm{EfiArch}.efi"), msMm, false);
|
||||
} catch (SetupException e) {
|
||||
WriteLine(e.Message);
|
||||
if (DetectLoader(ms) != BootLoaderType.Microsoft) {
|
||||
@@ -434,7 +546,7 @@ public class Setup {
|
||||
*/
|
||||
protected void RestoreMsLoader() {
|
||||
var ms = Esp.MsLoaderPath;
|
||||
if (DetectLoader(ms) == BootLoaderType.Own) {
|
||||
if (DetectLoader(ms) == BootLoaderType.Own || DetectLoader(ms) == BootLoaderType.Shim) {
|
||||
WriteLine("Disabling an old version of HackBGRT.");
|
||||
InstallFile(BackupLoaderPath, ms, false);
|
||||
WriteLine($"{ms} has been restored.");
|
||||
@@ -450,7 +562,7 @@ public class Setup {
|
||||
*/
|
||||
protected void VerifyLoaderConfig() {
|
||||
var lines = File.ReadAllLines("config.txt");
|
||||
var loader = lines.Where(s => s.StartsWith("boot=")).Select(s => s.Substring(5)).Prepend("").Last();
|
||||
var loader = lines.Where(s => s.StartsWith("boot=")).Select(s => s.Substring(5)).LastOrDefault();
|
||||
if (loader == null) {
|
||||
throw new SetupException("config.txt does not contain a boot=... line!");
|
||||
}
|
||||
@@ -514,7 +626,9 @@ public class Setup {
|
||||
protected void Uninstall() {
|
||||
Disable();
|
||||
try {
|
||||
Directory.Delete(InstallPath, true);
|
||||
if (Directory.Exists(InstallPath)) {
|
||||
Directory.Delete(InstallPath, true);
|
||||
}
|
||||
WriteLine($"HackBGRT has been removed from {InstallPath}.");
|
||||
} catch (Exception e) {
|
||||
Log($"Uninstall failed: {e.ToString()}");
|
||||
@@ -538,6 +652,9 @@ public class Setup {
|
||||
WriteLine("Secure Boot status could not be determined.");
|
||||
}
|
||||
WriteLine("It's very important to disable Secure Boot before installing.");
|
||||
if (LoaderIsSigned) {
|
||||
WriteLine("Alternatively, you can enroll the certificate.cer in your firmware.");
|
||||
}
|
||||
WriteLine("Otherwise your machine may become unbootable.");
|
||||
if (Batch) {
|
||||
if (allowSecureBoot) {
|
||||
@@ -573,8 +690,8 @@ public class Setup {
|
||||
WriteLine("BitLocker status could not be determined.");
|
||||
return;
|
||||
}
|
||||
var reOn = new Regex(@"Conversion Status:\s*(Encr|Fully Encr)|Protection Status:\s*Protection On");
|
||||
var reOff = new Regex(@"Conversion Status:\s*(Fully Decrypted)|Protection Status:\s*Protection Off");
|
||||
var reOn = new Regex(@"Conversion Status:\s*.*Encr|AES");
|
||||
var reOff = new Regex(@"Conversion Status:\s*(Fully Decrypted)|\s0[.,]0\s*%");
|
||||
var isOn = reOn.Match(output).Success;
|
||||
var isOff = reOff.Match(output).Success;
|
||||
if (!isOn && isOff) {
|
||||
@@ -644,8 +761,13 @@ public class Setup {
|
||||
* @param arch The architecture.
|
||||
*/
|
||||
protected void SetArch(string arch) {
|
||||
var detectedArch = DetectArchFromOS();
|
||||
if (arch == "") {
|
||||
var detectedArch = Environment.Is64BitOperatingSystem ? "x64" : "ia32";
|
||||
try {
|
||||
detectedArch = DetectArchFromOS();
|
||||
} catch {
|
||||
WriteLine($"Failed to detect OS architecture, assuming {detectedArch}.");
|
||||
}
|
||||
if (arch == "" || arch == null) {
|
||||
EfiArch = detectedArch;
|
||||
WriteLine($"Your OS uses arch={EfiArch}. This will be checked again during installation.");
|
||||
} else {
|
||||
@@ -674,6 +796,40 @@ public class Setup {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the boot time.
|
||||
*
|
||||
* @return The boot time, or null if it couldn't be determined.
|
||||
*/
|
||||
protected DateTime? GetBootTime() {
|
||||
try {
|
||||
var query = new ObjectQuery("SELECT LastBootUpTime FROM Win32_OperatingSystem WHERE Primary='true'");
|
||||
foreach (var m in new ManagementObjectSearcher(query).Get()) {
|
||||
return ManagementDateTimeConverter.ToDateTime(m["LastBootUpTime"].ToString());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log($"GetBootTime failed: {e.ToString()}");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Hiberboot is enabled.
|
||||
*
|
||||
* @return True, if Hiberboot is enabled.
|
||||
*/
|
||||
protected bool IsHiberbootEnabled() {
|
||||
try {
|
||||
return (int) Registry.GetValue(
|
||||
"HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Power",
|
||||
"HiberbootEnabled",
|
||||
0
|
||||
) != 0;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask for user's choice and install/uninstall.
|
||||
*/
|
||||
@@ -682,19 +838,14 @@ public class Setup {
|
||||
WriteLine("Choose action (press a key):");
|
||||
WriteLine(" I = install");
|
||||
WriteLine(" - creates a new EFI boot entry for HackBGRT");
|
||||
WriteLine(" - sometimes needs to be enabled in firmware settings");
|
||||
WriteLine(" - should fall back to MS boot loader if HackBGRT fails");
|
||||
WriteLine(" J = install (alternative)");
|
||||
WriteLine(" - creates a new EFI boot entry with an alternative method (BCDEdit)");
|
||||
WriteLine(" - always sets HackBGRT as the first boot option");
|
||||
WriteLine(" - sometimes shows up as \"Windows Boot Manager\"");
|
||||
WriteLine(" - should fall back to MS boot loader if HackBGRT fails");
|
||||
WriteLine(" - creates a new EFI boot entry with an alternative method");
|
||||
WriteLine(" - try this if the first option doesn't work");
|
||||
WriteLine(" O = install (legacy)");
|
||||
WriteLine(" - overwrites the MS boot loader");
|
||||
WriteLine(" - may require re-install after Windows updates");
|
||||
WriteLine(" - could brick your system if configured incorrectly");
|
||||
WriteLine(" - overwrites the MS boot loader; gets removed by Windows updates");
|
||||
WriteLine(" - use as last resort; may brick your system if configured incorrectly");
|
||||
WriteLine(" F = install files only");
|
||||
WriteLine(" - needs to be enabled somehow");
|
||||
WriteLine(" - ok for updating config, doesn't touch boot entries");
|
||||
WriteLine(" D = disable");
|
||||
WriteLine(" - removes created entries, restores MS boot loader");
|
||||
WriteLine(" R = remove completely");
|
||||
@@ -702,28 +853,31 @@ public class Setup {
|
||||
WriteLine(" B = boot to UEFI setup");
|
||||
WriteLine(" - lets you disable Secure Boot");
|
||||
WriteLine(" - lets you move HackBGRT before Windows in boot order");
|
||||
WriteLine(" L = show boot log (what HackBGRT did during boot)");
|
||||
WriteLine(" C = cancel");
|
||||
|
||||
var k = Console.ReadKey().Key;
|
||||
Log($"User input: {k}");
|
||||
WriteLine();
|
||||
if (k == ConsoleKey.I || k == ConsoleKey.O || k == ConsoleKey.F) {
|
||||
if (k == ConsoleKey.I || k == ConsoleKey.J || k == ConsoleKey.O || k == ConsoleKey.F) {
|
||||
Configure();
|
||||
}
|
||||
if (k == ConsoleKey.I) {
|
||||
RunPrivilegedActions(new string[] { "install", "enable-entry" });
|
||||
RunPrivilegedActions(new string[] { "disable", "install", "enable-bcdedit" });
|
||||
} else if (k == ConsoleKey.J) {
|
||||
RunPrivilegedActions(new string[] { "install", "enable-bcdedit" });
|
||||
RunPrivilegedActions(new string[] { "disable", "install", "enable-entry" });
|
||||
} else if (k == ConsoleKey.O) {
|
||||
RunPrivilegedActions(new string[] { "install", "enable-overwrite" });
|
||||
RunPrivilegedActions(new string[] { "disable", "install", "enable-overwrite" });
|
||||
} else if (k == ConsoleKey.F) {
|
||||
RunPrivilegedActions(new string[] { "install" });
|
||||
RunPrivilegedActions(new string[] { "disable-overwrite", "install" });
|
||||
} else if (k == ConsoleKey.D) {
|
||||
RunPrivilegedActions(new string[] { "disable" });
|
||||
} else if (k == ConsoleKey.R) {
|
||||
RunPrivilegedActions(new string[] { "uninstall" });
|
||||
} else if (k == ConsoleKey.B) {
|
||||
RunPrivilegedActions(new string[] { "boot-to-fw" });
|
||||
} else if (k == ConsoleKey.L) {
|
||||
RunPrivilegedActions(new string[] { "show-boot-log" });
|
||||
} else if (k == ConsoleKey.C) {
|
||||
throw new ExitSetup(1);
|
||||
} else {
|
||||
@@ -757,10 +911,24 @@ public class Setup {
|
||||
|
||||
InitEspPath();
|
||||
InitEspInfo();
|
||||
var bootLog = $"\n--- BOOT LOG START ---\n{Efi.GetHackBGRTLog()}\n--- BOOT LOG END ---";
|
||||
Setup.Log(bootLog);
|
||||
Efi.LogBGRT();
|
||||
Efi.LogBootEntries();
|
||||
if (GetBootTime() is DateTime bootTime) {
|
||||
var configTime = new[] { File.GetCreationTime("config.txt"), File.GetLastWriteTime("config.txt") }.Max();
|
||||
Log($"Boot time = {bootTime}, config.txt changed = {configTime}");
|
||||
if (configTime > bootTime) {
|
||||
WriteLine($"Windows was booted at {bootTime}. Remember to reboot after installing!");
|
||||
}
|
||||
}
|
||||
if (IsHiberbootEnabled()) {
|
||||
WriteLine("You may have to disable Fast Startup (Hiberboot) to reboot properly.");
|
||||
}
|
||||
bool allowSecureBoot = false;
|
||||
bool allowBitLocker = false;
|
||||
bool allowBadLoader = false;
|
||||
bool skipShim = false;
|
||||
Action<Action> verify = (Action revert) => {
|
||||
try {
|
||||
VerifyLoaderConfig();
|
||||
@@ -776,7 +944,9 @@ public class Setup {
|
||||
}
|
||||
};
|
||||
Action<Action, Action> enable = (Action enable, Action revert) => {
|
||||
HandleSecureBoot(allowSecureBoot);
|
||||
if (skipShim) {
|
||||
HandleSecureBoot(allowSecureBoot);
|
||||
}
|
||||
HandleBitLocker(allowBitLocker);
|
||||
enable();
|
||||
verify(revert);
|
||||
@@ -784,13 +954,15 @@ public class Setup {
|
||||
foreach (var arg in actions) {
|
||||
Log($"Running action '{arg}'.");
|
||||
if (arg == "install") {
|
||||
InstallFiles();
|
||||
InstallFiles(skipShim);
|
||||
} else if (arg == "allow-secure-boot") {
|
||||
allowSecureBoot = true;
|
||||
} else if (arg == "allow-bitlocker") {
|
||||
allowBitLocker = true;
|
||||
} else if (arg == "allow-bad-loader") {
|
||||
allowBadLoader = true;
|
||||
} else if (arg == "skip-shim") {
|
||||
skipShim = true;
|
||||
} else if (arg == "enable-entry") {
|
||||
enable(() => EnableEntry(), () => DisableEntry());
|
||||
} else if (arg == "disable-entry") {
|
||||
@@ -809,6 +981,8 @@ public class Setup {
|
||||
Uninstall();
|
||||
} else if (arg == "boot-to-fw") {
|
||||
BootToFW();
|
||||
} else if (arg == "show-boot-log") {
|
||||
WriteLine(bootLog);
|
||||
} else {
|
||||
throw new SetupException($"Invalid action: '{arg}'!");
|
||||
}
|
||||
@@ -839,7 +1013,7 @@ public class Setup {
|
||||
Batch = args.Contains("batch");
|
||||
ForwardArguments = String.Join(" ", args.Where(s => s == "dry-run" || s == "batch" || s.StartsWith("arch=")));
|
||||
try {
|
||||
SetArch(args.Prepend("arch=").Last(s => s.StartsWith("arch=")).Substring(5));
|
||||
SetArch(args.Where(s => s.StartsWith("arch=")).Select(s => s.Substring(5)).LastOrDefault());
|
||||
if (args.Contains("is-elevated") && !HasPrivileges() && !DryRun) {
|
||||
WriteLine("This installer needs to be run as administrator!");
|
||||
return 1;
|
||||
@@ -867,14 +1041,20 @@ public class Setup {
|
||||
WriteLine();
|
||||
WriteLine($"Unexpected error: {e.Message}");
|
||||
Log(e.ToString());
|
||||
WriteLine("If this is the most current release, please report this bug.");
|
||||
if (e is MissingMemberException || e is TypeLoadException) {
|
||||
WriteLine("This installer requires a recent version of .Net Framework.");
|
||||
WriteLine("Use Windows Update or download manually:");
|
||||
WriteLine("https://dotnet.microsoft.com/en-us/download/dotnet-framework");
|
||||
} else {
|
||||
WriteLine("If this is the most current release, please report this bug.");
|
||||
}
|
||||
return 1;
|
||||
} finally {
|
||||
if (DryRun) {
|
||||
WriteLine("This was a dry run, your system was not actually modified.");
|
||||
}
|
||||
if (!Batch) {
|
||||
WriteLine("If you need to report a bug, please include the setup.log file.");
|
||||
WriteLine("If you need to report a bug,\n - run this setup again with menu option L (show-boot-log)\n - then include the setup.log file with your report.");
|
||||
WriteLine("Press any key to quit.");
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
38
src/config.c
38
src/config.c
@@ -1,14 +1,12 @@
|
||||
#include "config.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <efilib.h>
|
||||
|
||||
BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, const CHAR16* path) {
|
||||
BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE base_dir, const CHAR16* path) {
|
||||
void* data = 0;
|
||||
UINTN data_bytes = 0;
|
||||
data = LoadFileWithPadding(root_dir, path, &data_bytes, 4);
|
||||
data = LoadFileWithPadding(base_dir, path, &data_bytes, 4);
|
||||
if (!data) {
|
||||
Print(L"HackBGRT: Failed to load configuration (%s)!\n", path);
|
||||
Log(1, L"Failed to load configuration (%s)!\n", path);
|
||||
return FALSE;
|
||||
}
|
||||
CHAR16* str;
|
||||
@@ -21,7 +19,7 @@ BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir,
|
||||
// UTF-8 -> UCS-2
|
||||
EFI_STATUS e = BS->AllocatePool(EfiBootServicesData, data_bytes * 2 + 2, (void**)&str);
|
||||
if (EFI_ERROR(e)) {
|
||||
FreePool(data);
|
||||
BS->FreePool(data);
|
||||
return FALSE;
|
||||
}
|
||||
UINT8* str0 = data;
|
||||
@@ -51,7 +49,7 @@ BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir,
|
||||
}
|
||||
}
|
||||
str[str_len] = 0;
|
||||
FreePool(data);
|
||||
BS->FreePool(data);
|
||||
}
|
||||
|
||||
for (int i = 0; i < str_len;) {
|
||||
@@ -63,7 +61,7 @@ BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir,
|
||||
str[j] = 0;
|
||||
++j;
|
||||
}
|
||||
ReadConfigLine(config, root_dir, &str[i]);
|
||||
ReadConfigLine(config, base_dir, &str[i]);
|
||||
i = j;
|
||||
}
|
||||
// NOTICE: string is not freed, because paths are not copied.
|
||||
@@ -72,12 +70,10 @@ BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir,
|
||||
|
||||
static void SetBMPWithRandom(struct HackBGRT_config* config, int weight, enum HackBGRT_action action, int x, int y, int o, const CHAR16* path) {
|
||||
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, o %d, path %s, random = %08x, limit = %08x\n", weight, action, x, y, o, path, random, limit);
|
||||
}
|
||||
if (!config->image_weight_sum || random <= limit) {
|
||||
UINT32 random = (((UINT64) Random() & 0xffffffff) * config->image_weight_sum) >> 32;
|
||||
UINT32 limit = ((UINT64) 0xffffffff * weight) >> 32;
|
||||
Log(config->debug, L"%s n=%d, action=%d, x=%d, y=%d, o=%d, path=%s, rand=%x/%x\n", random <= limit ? L"Using" : L"Skipping", weight, action, x, y, o, path, random, limit);
|
||||
if (random <= limit) {
|
||||
config->action = action;
|
||||
config->image_path = path;
|
||||
config->orientation = o;
|
||||
@@ -112,7 +108,7 @@ static void ReadConfigImage(struct HackBGRT_config* config, const CHAR16* line)
|
||||
} 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;
|
||||
@@ -129,11 +125,11 @@ static void ReadConfigResolution(struct HackBGRT_config* config, const CHAR16* l
|
||||
config->resolution_x = *x == '-' ? -(int)Atoi(x+1) : (int)Atoi(x);
|
||||
config->resolution_y = *y == '-' ? -(int)Atoi(y+1) : (int)Atoi(y);
|
||||
} else {
|
||||
Print(L"HackBGRT: Invalid resolution line: %s\n", line);
|
||||
Log(1, L"Invalid resolution line: %s\n", line);
|
||||
}
|
||||
}
|
||||
|
||||
void ReadConfigLine(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, const CHAR16* line) {
|
||||
void ReadConfigLine(struct HackBGRT_config* config, EFI_FILE_HANDLE base_dir, const CHAR16* line) {
|
||||
line = TrimLeft(line);
|
||||
if (line[0] == L'#' || line[0] == 0) {
|
||||
return;
|
||||
@@ -143,6 +139,10 @@ void ReadConfigLine(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, co
|
||||
config->debug = (StrCmp(line, L"debug=1") == 0);
|
||||
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;
|
||||
@@ -152,12 +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;
|
||||
}
|
||||
if (StrnCmp(line, L"resolution=", 11) == 0) {
|
||||
ReadConfigResolution(config, line + 11);
|
||||
return;
|
||||
}
|
||||
Print(L"Unknown configuration directive: %s\n", line);
|
||||
Log(1, L"Unknown configuration directive: %s\n", line);
|
||||
}
|
||||
|
||||
14
src/config.h
14
src/config.h
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <efi.h>
|
||||
#include "efi.h"
|
||||
|
||||
/**
|
||||
* Possible actions to perform on the BGRT.
|
||||
@@ -14,14 +14,14 @@ enum HackBGRT_action {
|
||||
* @see struct HackBGRT_config
|
||||
*/
|
||||
enum HackBGRT_coordinate {
|
||||
HackBGRT_coord_keep = -1000000001
|
||||
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;
|
||||
@@ -39,17 +39,17 @@ struct HackBGRT_config {
|
||||
* Read a configuration parameter. (May recursively read config files.)
|
||||
*
|
||||
* @param config The configuration to modify.
|
||||
* @param root_dir The root directory, in case the parameter contains an include.
|
||||
* @param base_dir The base directory, in case the parameter contains an include.
|
||||
* @param line The configuration line to parse.
|
||||
*/
|
||||
extern void ReadConfigLine(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, const CHAR16* line);
|
||||
extern void ReadConfigLine(struct HackBGRT_config* config, EFI_FILE_HANDLE base_dir, const CHAR16* line);
|
||||
|
||||
/**
|
||||
* Read a configuration file. (May recursively read more files.)
|
||||
*
|
||||
* @param config The configuration to modify.
|
||||
* @param root_dir The root directory.
|
||||
* @param base_dir The base directory.
|
||||
* @param path The path to the file.
|
||||
* @return FALSE, if the file couldn't be read, TRUE otherwise.
|
||||
*/
|
||||
extern BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, const CHAR16* path);
|
||||
extern BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE base_dir, const CHAR16* path);
|
||||
|
||||
159
src/efi.c
Normal file
159
src/efi.c
Normal file
@@ -0,0 +1,159 @@
|
||||
#include "efi.h"
|
||||
#include "util.h"
|
||||
|
||||
// New implementations of some functions in gnu-efi.
|
||||
// These functions are designed to avoid other gnu-efi calls.
|
||||
|
||||
EFI_STATUS LibLocateProtocol(IN EFI_GUID *ProtocolGuid, OUT VOID **Interface) {
|
||||
EFI_HANDLE buffer[256];
|
||||
UINTN size = sizeof(buffer);
|
||||
if (!EFI_ERROR(BS->LocateHandle(ByProtocol, ProtocolGuid, NULL, &size, buffer))) {
|
||||
for (int i = 0; i < size / sizeof(EFI_HANDLE); ++i) {
|
||||
if (!EFI_ERROR(BS->HandleProtocol(buffer[i], ProtocolGuid, Interface))) {
|
||||
return EFI_SUCCESS;
|
||||
}
|
||||
}
|
||||
}
|
||||
return EFI_NOT_FOUND;
|
||||
}
|
||||
|
||||
EFI_DEVICE_PATH *FileDevicePath(IN EFI_HANDLE Device OPTIONAL, IN CHAR16 *FileName) {
|
||||
EFI_DEVICE_PATH *old_path = 0;
|
||||
if (!Device || EFI_ERROR(BS->HandleProtocol(Device, TmpGuidPtr((EFI_GUID) EFI_DEVICE_PATH_PROTOCOL_GUID), (void**)&old_path))) {
|
||||
static EFI_DEVICE_PATH end_path = {END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, {sizeof(end_path), 0}};
|
||||
old_path = &end_path;
|
||||
}
|
||||
UINTN old_path_size = 0, instances = 0;
|
||||
for (EFI_DEVICE_PATH *p0 = old_path;; p0 = NextDevicePathNode(p0)) {
|
||||
old_path_size += DevicePathNodeLength(p0);
|
||||
if (IsDevicePathEndType(p0)) {
|
||||
instances += 1;
|
||||
}
|
||||
if (IsDevicePathEnd(p0)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
UINTN size_str = (StrLen(FileName) + 1) * sizeof(*FileName);
|
||||
UINTN size_fdp = SIZE_OF_FILEPATH_DEVICE_PATH + size_str;
|
||||
|
||||
EFI_DEVICE_PATH *new_path;
|
||||
if (EFI_ERROR(BS->AllocatePool(EfiBootServicesData, old_path_size + instances * size_fdp, (void**)&new_path))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
EFI_DEVICE_PATH *p1 = new_path;
|
||||
for (EFI_DEVICE_PATH *p0 = old_path;; p0 = NextDevicePathNode(p0)) {
|
||||
if (IsDevicePathEndType(p0)) {
|
||||
*p1 = (EFI_DEVICE_PATH) {
|
||||
.Type = MEDIA_DEVICE_PATH,
|
||||
.SubType = MEDIA_FILEPATH_DP,
|
||||
.Length = {size_fdp, size_fdp >> 8},
|
||||
};
|
||||
FILEPATH_DEVICE_PATH *f = (FILEPATH_DEVICE_PATH *) p1;
|
||||
BS->CopyMem(f->PathName, FileName, size_str);
|
||||
p1 = NextDevicePathNode(p1);
|
||||
}
|
||||
BS->CopyMem(p1, p0, DevicePathNodeLength(p0));
|
||||
if (IsDevicePathEnd(p0)) {
|
||||
break;
|
||||
}
|
||||
p1 = NextDevicePathNode(p1);
|
||||
}
|
||||
|
||||
return new_path;
|
||||
}
|
||||
|
||||
INTN CompareMem(IN CONST VOID *Dest, IN CONST VOID *Src, IN UINTN len) {
|
||||
CONST UINT8 *d = Dest, *s = Src;
|
||||
for (UINTN i = 0; i < len; ++i) {
|
||||
if (d[i] != s[i]) {
|
||||
return d[i] - s[i];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void StrnCat(IN CHAR16* dest, IN CONST CHAR16* src, UINTN len) {
|
||||
CHAR16* d = dest;
|
||||
while (*d) {
|
||||
++d;
|
||||
}
|
||||
while (len-- && *src) {
|
||||
*d++ = *src++;
|
||||
}
|
||||
*d = 0;
|
||||
}
|
||||
|
||||
UINTN StrLen(IN CONST CHAR16* s) {
|
||||
UINTN i = 0;
|
||||
while (*s++) {
|
||||
++i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
INTN StriCmp(IN CONST CHAR16* s1, IN CONST CHAR16* s2) {
|
||||
while (*s1 && *s2) {
|
||||
CHAR16 c1 = *s1++, c2 = *s2++;
|
||||
if (c1 >= 'A' && c1 <= 'Z') {
|
||||
c1 += 'a' - 'A';
|
||||
}
|
||||
if (c2 >= 'A' && c2 <= 'Z') {
|
||||
c2 += 'a' - 'A';
|
||||
}
|
||||
if (c1 != c2) {
|
||||
return c1 - c2;
|
||||
}
|
||||
}
|
||||
return *s1 - *s2;
|
||||
}
|
||||
|
||||
INTN StrnCmp(IN CONST CHAR16* s1, IN CONST CHAR16* s2, UINTN len) {
|
||||
while (*s1 && *s2 && len--) {
|
||||
CHAR16 c1 = *s1++, c2 = *s2++;
|
||||
if (c1 >= 'A' && c1 <= 'Z') {
|
||||
c1 += 'a' - 'A';
|
||||
}
|
||||
if (c2 >= 'A' && c2 <= 'Z') {
|
||||
c2 += 'a' - 'A';
|
||||
}
|
||||
if (c1 != c2) {
|
||||
return c1 - c2;
|
||||
}
|
||||
}
|
||||
return len ? *s1 - *s2 : 0;
|
||||
}
|
||||
|
||||
INTN StrCmp(IN CONST CHAR16* s1, IN CONST CHAR16* s2) {
|
||||
while (*s1 && *s2) {
|
||||
if (*s1 != *s2) {
|
||||
return *s1 - *s2;
|
||||
}
|
||||
++s1, ++s2;
|
||||
}
|
||||
return *s1 - *s2;
|
||||
}
|
||||
|
||||
UINTN Atoi(IN CONST CHAR16* s) {
|
||||
UINTN n = 0;
|
||||
while (*s >= '0' && *s <= '9') {
|
||||
n = n * 10 + *s++ - '0';
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void *memset(void *s, int c, __SIZE_TYPE__ n) {
|
||||
unsigned char *p = s;
|
||||
while (n--)
|
||||
*p++ = c;
|
||||
return s;
|
||||
}
|
||||
|
||||
void *memcpy(void *dest, const void *src, __SIZE_TYPE__ n) {
|
||||
const unsigned char *q = src;
|
||||
unsigned char *p = dest;
|
||||
while (n--)
|
||||
*p++ = *q++;
|
||||
return dest;
|
||||
}
|
||||
5
src/efi.h
Normal file
5
src/efi.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <efi.h>
|
||||
#include <efilib.h>
|
||||
234
src/main.c
234
src/main.c
@@ -1,25 +1,27 @@
|
||||
#include <efi.h>
|
||||
#include <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 CONST 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,8 +30,7 @@ static struct HackBGRT_config config = {
|
||||
static EFI_GRAPHICS_OUTPUT_PROTOCOL* GOP(void) {
|
||||
static EFI_GRAPHICS_OUTPUT_PROTOCOL* gop;
|
||||
if (!gop) {
|
||||
EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
|
||||
LibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **)&gop);
|
||||
LibLocateProtocol(TmpGuidPtr((EFI_GUID) EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID), (void**) &gop);
|
||||
}
|
||||
return gop;
|
||||
}
|
||||
@@ -43,9 +44,13 @@ static EFI_GRAPHICS_OUTPUT_PROTOCOL* GOP(void) {
|
||||
static void SetResolution(int w, int h) {
|
||||
EFI_GRAPHICS_OUTPUT_PROTOCOL* gop = GOP();
|
||||
if (!gop) {
|
||||
config.old_resolution_x = config.resolution_x = 0;
|
||||
config.old_resolution_y = config.resolution_y = 0;
|
||||
Debug(L"GOP not found!\n");
|
||||
if (config.resolution_x <= 0 || config.resolution_y <= 0) {
|
||||
config.resolution_x = 1024;
|
||||
config.resolution_y = 768;
|
||||
}
|
||||
config.old_resolution_x = config.resolution_x;
|
||||
config.old_resolution_y = config.resolution_y;
|
||||
Log(config.debug, L"GOP not found! Assuming resolution %dx%d.\n", config.resolution_x, config.resolution_y);
|
||||
return;
|
||||
}
|
||||
UINTN best_i = gop->Mode->Mode;
|
||||
@@ -54,7 +59,7 @@ static void SetResolution(int w, int h) {
|
||||
w = (w <= 0 ? w < 0 ? best_w : 999999 : w);
|
||||
h = (h <= 0 ? h < 0 ? best_h : 999999 : h);
|
||||
|
||||
Debug(L"Looking for resolution %dx%d...\n", w, h);
|
||||
Log(config.debug, L"Looking for resolution %dx%d...\n", w, h);
|
||||
for (UINT32 i = gop->Mode->MaxMode; i--;) {
|
||||
int new_w = 0, new_h = 0;
|
||||
|
||||
@@ -64,12 +69,12 @@ static void SetResolution(int w, int h) {
|
||||
continue;
|
||||
}
|
||||
if (info_size < sizeof(*info)) {
|
||||
FreePool(info);
|
||||
BS->FreePool(info);
|
||||
continue;
|
||||
}
|
||||
new_w = info->HorizontalResolution;
|
||||
new_h = info->VerticalResolution;
|
||||
FreePool(info);
|
||||
BS->FreePool(info);
|
||||
|
||||
// Sum of missing w/h should be minimal.
|
||||
int new_missing = max(w - new_w, 0) + max(h - new_h, 0);
|
||||
@@ -87,7 +92,7 @@ static void SetResolution(int w, int h) {
|
||||
best_h = new_h;
|
||||
best_i = i;
|
||||
}
|
||||
Debug(L"Found resolution %dx%d.\n", best_w, best_h);
|
||||
Log(config.debug, L"Found resolution %dx%d.\n", best_w, best_h);
|
||||
config.resolution_x = best_w;
|
||||
config.resolution_y = best_h;
|
||||
if (best_i != gop->Mode->Mode) {
|
||||
@@ -107,11 +112,11 @@ ACPI_SDT_HEADER* CreateXsdt(ACPI_SDT_HEADER* xsdt0, UINTN entries) {
|
||||
UINT32 xsdt_len = sizeof(ACPI_SDT_HEADER) + entries * sizeof(UINT64);
|
||||
BS->AllocatePool(EfiACPIReclaimMemory, xsdt_len, (void**)&xsdt);
|
||||
if (!xsdt) {
|
||||
Print(L"HackBGRT: Failed to allocate memory for XSDT.\n");
|
||||
Log(1, L"Failed to allocate memory for XSDT.\n");
|
||||
return 0;
|
||||
}
|
||||
ZeroMem(xsdt, xsdt_len);
|
||||
CopyMem(xsdt, xsdt0, min(xsdt0->length, xsdt_len));
|
||||
BS->SetMem(xsdt, xsdt_len, 0);
|
||||
BS->CopyMem(xsdt, xsdt0, min(xsdt0->length, xsdt_len));
|
||||
xsdt->length = xsdt_len;
|
||||
SetAcpiSdtChecksum(xsdt);
|
||||
return xsdt;
|
||||
@@ -130,26 +135,25 @@ ACPI_SDT_HEADER* CreateXsdt(ACPI_SDT_HEADER* xsdt0, UINTN entries) {
|
||||
*/
|
||||
static ACPI_BGRT* HandleAcpiTables(enum HackBGRT_action action, ACPI_BGRT* bgrt) {
|
||||
for (int i = 0; i < ST->NumberOfTableEntries; i++) {
|
||||
EFI_GUID Acpi20TableGuid = ACPI_20_TABLE_GUID;
|
||||
EFI_GUID* vendor_guid = &ST->ConfigurationTable[i].VendorGuid;
|
||||
if (!CompareGuid(vendor_guid, &AcpiTableGuid) && !CompareGuid(vendor_guid, &Acpi20TableGuid)) {
|
||||
if (CompareMem(vendor_guid, TmpGuidPtr((EFI_GUID) ACPI_TABLE_GUID), sizeof(EFI_GUID)) != 0 && CompareMem(vendor_guid, TmpGuidPtr((EFI_GUID) ACPI_20_TABLE_GUID), sizeof(EFI_GUID)) != 0) {
|
||||
continue;
|
||||
}
|
||||
ACPI_20_RSDP* rsdp = (ACPI_20_RSDP *) ST->ConfigurationTable[i].VendorTable;
|
||||
if (CompareMem(rsdp->signature, "RSD PTR ", 8) != 0 || rsdp->revision < 2 || !VerifyAcpiRsdp2Checksums(rsdp)) {
|
||||
continue;
|
||||
}
|
||||
Debug(L"RSDP @%x: revision = %d, OEM ID = %s\n", (UINTN)rsdp, rsdp->revision, TmpStr(rsdp->oem_id, 6));
|
||||
Log(config.debug, L"RSDP @%x: revision = %d, OEM ID = %s\n", (UINTN)rsdp, rsdp->revision, TmpStr(rsdp->oem_id, 6));
|
||||
|
||||
ACPI_SDT_HEADER* xsdt = (ACPI_SDT_HEADER *) (UINTN) rsdp->xsdt_address;
|
||||
if (!xsdt || CompareMem(xsdt->signature, "XSDT", 4) != 0 || !VerifyAcpiSdtChecksum(xsdt)) {
|
||||
Debug(L"* XSDT: missing or invalid\n");
|
||||
Log(config.debug, L"* XSDT: missing or invalid\n");
|
||||
continue;
|
||||
}
|
||||
UINT64* entry_arr = (UINT64*)&xsdt[1];
|
||||
UINT32 entry_arr_length = (xsdt->length - sizeof(*xsdt)) / sizeof(UINT64);
|
||||
|
||||
Debug(L"* XSDT @%x: OEM ID = %s, entry count = %d\n", (UINTN)xsdt, TmpStr(xsdt->oem_id, 6), entry_arr_length);
|
||||
Log(config.debug, L"* XSDT @%x: OEM ID = %s, entry count = %d\n", (UINTN)xsdt, TmpStr(xsdt->oem_id, 6), entry_arr_length);
|
||||
|
||||
int bgrt_count = 0;
|
||||
for (int j = 0; j < entry_arr_length; j++) {
|
||||
@@ -157,16 +161,16 @@ static ACPI_BGRT* HandleAcpiTables(enum HackBGRT_action action, ACPI_BGRT* bgrt)
|
||||
if (CompareMem(entry->signature, "BGRT", 4) != 0) {
|
||||
continue;
|
||||
}
|
||||
Debug(L" - ACPI table @%x: %s, revision = %d, OEM ID = %s\n", (UINTN)entry, TmpStr(entry->signature, 4), entry->revision, TmpStr(entry->oem_id, 6));
|
||||
Log(config.debug, L" - ACPI table @%x: %s, revision = %d, OEM ID = %s\n", (UINTN)entry, TmpStr(entry->signature, 4), entry->revision, TmpStr(entry->oem_id, 6));
|
||||
switch (action) {
|
||||
case HackBGRT_KEEP:
|
||||
if (!bgrt) {
|
||||
Debug(L" -> Returning this one for later use.\n");
|
||||
Log(config.debug, L" -> Returning this one for later use.\n");
|
||||
bgrt = (ACPI_BGRT*) entry;
|
||||
}
|
||||
break;
|
||||
case HackBGRT_REMOVE:
|
||||
Debug(L" -> Deleting.\n");
|
||||
Log(config.debug, L" -> Deleting.\n");
|
||||
for (int k = j+1; k < entry_arr_length; ++k) {
|
||||
entry_arr[k-1] = entry_arr[k];
|
||||
}
|
||||
@@ -176,13 +180,13 @@ static ACPI_BGRT* HandleAcpiTables(enum HackBGRT_action action, ACPI_BGRT* bgrt)
|
||||
--j;
|
||||
break;
|
||||
case HackBGRT_REPLACE:
|
||||
Debug(L" -> Replacing.\n");
|
||||
Log(config.debug, L" -> Replacing.\n");
|
||||
entry_arr[j] = (UINTN) bgrt;
|
||||
}
|
||||
bgrt_count += 1;
|
||||
}
|
||||
if (!bgrt_count && action == HackBGRT_REPLACE && bgrt) {
|
||||
Debug(L" - Adding missing BGRT.\n");
|
||||
Log(config.debug, L" - Adding missing BGRT.\n");
|
||||
xsdt = CreateXsdt(xsdt, entry_arr_length + 1);
|
||||
entry_arr = (UINT64*)&xsdt[1];
|
||||
entry_arr[entry_arr_length++] = (UINTN) bgrt;
|
||||
@@ -208,7 +212,7 @@ static BMP* MakeBMP(int w, int h, UINT8 r, UINT8 g, UINT8 b) {
|
||||
BMP* bmp = 0;
|
||||
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;
|
||||
}
|
||||
@@ -235,24 +239,32 @@ static BMP* MakeBMP(int w, int h, UINT8 r, UINT8 g, UINT8 b) {
|
||||
/**
|
||||
* Load a bitmap or generate a black one.
|
||||
*
|
||||
* @param root_dir The root directory for loading a BMP.
|
||||
* @param path The BMP path within the root directory; NULL for a black BMP.
|
||||
* @param base_dir The directory for loading a BMP.
|
||||
* @param path The BMP path within the directory; NULL for a black BMP.
|
||||
* @return The loaded BMP, or 0 if not available.
|
||||
*/
|
||||
static BMP* LoadBMP(EFI_FILE_HANDLE root_dir, const CHAR16* path) {
|
||||
static BMP* LoadBMP(EFI_FILE_HANDLE base_dir, const CHAR16* path) {
|
||||
if (!path) {
|
||||
return MakeBMP(1, 1, 0, 0, 0); // empty path = black image
|
||||
}
|
||||
Debug(L"HackBGRT: Loading %s.\n", path);
|
||||
Log(config.debug, L"Loading %s.\n", path);
|
||||
UINTN size = 0;
|
||||
BMP* bmp = LoadFile(root_dir, path, &size);
|
||||
BMP* bmp = LoadFile(base_dir, path, &size);
|
||||
if (bmp) {
|
||||
if (size >= bmp->file_size && CompareMem(bmp, "BM", 2) == 0 && bmp->file_size - bmp->pixel_data_offset > 4 && bmp->width && bmp->height && (bmp->bpp == 32 || bmp->bpp == 24) && bmp->compression == 0) {
|
||||
if (size >= bmp->file_size
|
||||
&& CompareMem(bmp, "BM", 2) == 0
|
||||
&& bmp->file_size > bmp->pixel_data_offset
|
||||
&& bmp->width > 0
|
||||
&& bmp->height > 0
|
||||
&& (bmp->bpp == 32 || bmp->bpp == 24)
|
||||
&& bmp->height * (-(-(bmp->width * (bmp->bpp / 8)) & ~3)) <= bmp->file_size - bmp->pixel_data_offset
|
||||
&& bmp->compression == 0) {
|
||||
return bmp;
|
||||
}
|
||||
Print(L"HackBGRT: Invalid BMP (%s)!\n", path);
|
||||
BS->FreePool(bmp);
|
||||
Log(1, L"Invalid BMP (%s)!\n", path);
|
||||
} else {
|
||||
Print(L"HackBGRT: Failed to load BMP (%s)!\n", path);
|
||||
Log(1, L"Failed to load BMP (%s)!\n", path);
|
||||
}
|
||||
BS->Stall(1000000);
|
||||
return MakeBMP(16, 16, 255, 0, 0); // error = red image
|
||||
@@ -270,13 +282,11 @@ static void CropBMP(BMP* bmp, int w, int h) {
|
||||
bmp->image_size = 0;
|
||||
bmp->width = min(bmp->width, w);
|
||||
bmp->height = min(bmp->height, h);
|
||||
const int h_max = (bmp->file_size - bmp->pixel_data_offset) / old_pitch;
|
||||
bmp->height = min(bmp->height, h_max);
|
||||
const int new_pitch = -(-(bmp->width * (bmp->bpp / 8)) & ~3);
|
||||
|
||||
if (new_pitch < old_pitch) {
|
||||
for (int i = 1; i < bmp->height; ++i) {
|
||||
CopyMem(
|
||||
BS->CopyMem(
|
||||
(UINT8*) bmp + bmp->pixel_data_offset + i * new_pitch,
|
||||
(UINT8*) bmp + bmp->pixel_data_offset + i * old_pitch,
|
||||
new_pitch
|
||||
@@ -289,9 +299,9 @@ static void CropBMP(BMP* bmp, int w, int h) {
|
||||
/**
|
||||
* The main logic for BGRT modification.
|
||||
*
|
||||
* @param root_dir The root directory for loading a BMP.
|
||||
* @param base_dir The directory for loading a BMP.
|
||||
*/
|
||||
void HackBgrt(EFI_FILE_HANDLE root_dir) {
|
||||
void HackBgrt(EFI_FILE_HANDLE base_dir) {
|
||||
// REMOVE: simply delete all BGRT entries.
|
||||
if (config.action == HackBGRT_REMOVE) {
|
||||
HandleAcpiTables(config.action, 0);
|
||||
@@ -320,7 +330,7 @@ void HackBgrt(EFI_FILE_HANDLE root_dir) {
|
||||
// Replace missing = allocate new.
|
||||
BS->AllocatePool(EfiACPIReclaimMemory, sizeof(*bgrt), (void**)&bgrt);
|
||||
if (!bgrt) {
|
||||
Print(L"HackBGRT: Failed to allocate memory for BGRT.\n");
|
||||
Log(1, L"Failed to allocate memory for BGRT.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -342,7 +352,7 @@ void HackBgrt(EFI_FILE_HANDLE root_dir) {
|
||||
// Get the image (either old or new).
|
||||
BMP* new_bmp = old_bmp;
|
||||
if (config.action == HackBGRT_REPLACE) {
|
||||
new_bmp = LoadBMP(root_dir, config.image_path);
|
||||
new_bmp = LoadBMP(base_dir, config.image_path);
|
||||
}
|
||||
|
||||
// No image = no need for BGRT.
|
||||
@@ -372,8 +382,8 @@ void HackBgrt(EFI_FILE_HANDLE root_dir) {
|
||||
bgrt->image_offset_x = max(0, min(max_x, new_x + (new_reso_x - new_bmp->width) / 2));
|
||||
bgrt->image_offset_y = max(0, min(max_y, new_y + (new_reso_y - new_bmp->height) / 2));
|
||||
|
||||
Debug(
|
||||
L"HackBGRT: BMP at (%d, %d), center (%d, %d), resolution (%d, %d) with orientation %d applied.\n",
|
||||
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
|
||||
@@ -387,12 +397,12 @@ void HackBgrt(EFI_FILE_HANDLE root_dir) {
|
||||
/**
|
||||
* Load an application.
|
||||
*/
|
||||
static EFI_HANDLE LoadApp(print_t* print_failure, EFI_HANDLE image_handle, EFI_LOADED_IMAGE* image, const CHAR16* path) {
|
||||
static EFI_HANDLE LoadApp(int print_failure, EFI_HANDLE image_handle, EFI_LOADED_IMAGE* image, const CHAR16* path) {
|
||||
EFI_DEVICE_PATH* boot_dp = FileDevicePath(image->DeviceHandle, (CHAR16*) path);
|
||||
EFI_HANDLE result = 0;
|
||||
Debug(L"HackBGRT: Loading application %s.\n", path);
|
||||
Log(config.debug, L"Loading application %s.\n", path);
|
||||
if (EFI_ERROR(BS->LoadImage(0, image_handle, boot_dp, 0, 0, &result))) {
|
||||
print_failure(L"HackBGRT: Failed to load application %s.\n", path);
|
||||
Log(config.debug || print_failure, L"Failed to load application %s.\n", path);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -400,98 +410,116 @@ static EFI_HANDLE LoadApp(print_t* print_failure, EFI_HANDLE image_handle, EFI_L
|
||||
/**
|
||||
* 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);
|
||||
EFI_FILE_IO_INTERFACE* io;
|
||||
if (EFI_ERROR(BS->HandleProtocol(image->DeviceHandle, TmpGuidPtr((EFI_GUID) EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID), (void**) &io))) {
|
||||
Log(config.debug, L"FILE_SYSTEM_PROTOCOL failed.\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
CHAR16 **argv;
|
||||
int argc = GetShellArgcArgv(image_handle, &argv);
|
||||
EFI_FILE_HANDLE root_dir;
|
||||
if (EFI_ERROR(io->OpenVolume(io, &root_dir))) {
|
||||
Log(config.debug, L"Failed to open root directory.\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (argc <= 1) {
|
||||
const CHAR16* config_path = L"\\EFI\\HackBGRT\\config.txt";
|
||||
if (!ReadConfigFile(&config, root_dir, config_path)) {
|
||||
Print(L"HackBGRT: No config, no command line!\n", config_path);
|
||||
EFI_FILE_HANDLE base_dir;
|
||||
if (EFI_ERROR(root_dir->Open(root_dir, &base_dir, L"\\EFI\\HackBGRT", EFI_FILE_MODE_READ, 0))) {
|
||||
Log(config.debug, L"Failed to HackBGRT directory.\n");
|
||||
base_dir = root_dir;
|
||||
}
|
||||
|
||||
EFI_SHELL_PARAMETERS_PROTOCOL *shell_param_proto = NULL;
|
||||
if (EFI_ERROR(BS->OpenProtocol(image_handle, TmpGuidPtr((EFI_GUID) EFI_SHELL_PARAMETERS_PROTOCOL_GUID), (void**) &shell_param_proto, 0, 0, EFI_OPEN_PROTOCOL_GET_PROTOCOL)) || shell_param_proto->Argc <= 1) {
|
||||
const CHAR16* config_path = L"config.txt";
|
||||
if (!ReadConfigFile(&config, base_dir, config_path)) {
|
||||
Log(1, L"No config, no command line!\n", config_path);
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
CHAR16 **argv = shell_param_proto->Argv;
|
||||
int argc = shell_param_proto->Argc;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
ReadConfigLine(&config, base_dir, argv[i]);
|
||||
}
|
||||
}
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
ReadConfigLine(&config, root_dir, argv[i]);
|
||||
|
||||
if (config.debug) {
|
||||
Log(-1, L"HackBGRT version: %s\n", version);
|
||||
}
|
||||
Debug = config.debug ? Print : NullPrint;
|
||||
|
||||
SetResolution(config.resolution_x, config.resolution_y);
|
||||
HackBgrt(root_dir);
|
||||
HackBgrt(base_dir);
|
||||
|
||||
EFI_HANDLE next_image_handle = 0;
|
||||
static CHAR16 backup_boot_path[] = L"\\EFI\\HackBGRT\\bootmgfw-original.efi";
|
||||
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(Print, image_handle, image, config.boot_path);
|
||||
} else {
|
||||
config.boot_path = backup_boot_path;
|
||||
next_image_handle = LoadApp(Debug, image_handle, image, config.boot_path);
|
||||
if (!next_image_handle) {
|
||||
config.boot_path = ms_boot_path;
|
||||
next_image_handle = LoadApp(Debug, image_handle, image, config.boot_path);
|
||||
}
|
||||
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(Print, image_handle, image, config.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(Print, image_handle, image, config.boot_path);
|
||||
next_image_handle = LoadApp(!try_ms_quietly, image_handle, image, config.boot_path);
|
||||
if (!next_image_handle) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
Print(L"HackBGRT: Reverting to %s.\n", config.boot_path);
|
||||
Print(L"Press escape to cancel or any other key (or wait 15 seconds) to boot.\n");
|
||||
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 if (config.debug) {
|
||||
Print(L"HackBGRT: Ready to boot. Disable debug mode to skip this screen.\n");
|
||||
Print(L"Press escape to cancel or any other key (or wait 15 seconds) to boot.\n");
|
||||
} 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: Failed to start %s.\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);
|
||||
Print(L"Please check that %s is not actually HackBGRT!\n", config.boot_path);
|
||||
Log(1, L"Started %s. Why are we still here?!\n", config.boot_path);
|
||||
Log(-1, L"Please check that %s is not actually HackBGRT!\n", config.boot_path);
|
||||
goto fail;
|
||||
|
||||
fail: {
|
||||
Print(L"HackBGRT has failed. Use parameter debug=1 for details.\n");
|
||||
Print(L"Get a Windows install disk or a recovery disk to fix your boot.\n");
|
||||
#ifdef GIT_DESCRIBE
|
||||
Print(L"HackBGRT version: " GIT_DESCRIBE L"\n");
|
||||
#else
|
||||
Print(L"HackBGRT version: unknown; not an official release?\n");
|
||||
#endif
|
||||
Print(L"Press any key (or wait 15 seconds) to exit.\n");
|
||||
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"
|
||||
;
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <efi.h>
|
||||
#include "efi.h"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
|
||||
122
src/util.c
122
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,8 +12,99 @@ const CHAR16* TmpStr(CHAR8 *src, int length) {
|
||||
return dest;
|
||||
}
|
||||
|
||||
UINTN NullPrint(IN CONST 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];
|
||||
}
|
||||
|
||||
#define log_buffer_size (65536)
|
||||
CHAR16 log_buffer[log_buffer_size] = {0};
|
||||
|
||||
CHAR16 LogVarName[] = L"HackBGRTLog";
|
||||
EFI_GUID LogVarGuid = {0x03c64761, 0x075f, 0x4dba, {0xab, 0xfb, 0x2e, 0xd8, 0x9e, 0x18, 0xb2, 0x36}}; // self-made: 03c64761-075f-4dba-abfb-2ed89e18b236
|
||||
|
||||
void Log(int mode, IN CONST CHAR16 *fmt, ...) {
|
||||
va_list args;
|
||||
CHAR16 buf[256] = {0};
|
||||
int buf_i = 0;
|
||||
#define putchar(c) { if (buf_i < 255) { buf[buf_i++] = c; } }
|
||||
va_start(args, fmt);
|
||||
for (int i = 0; fmt[i]; ++i) {
|
||||
if (fmt[i] == '\n') {
|
||||
putchar('\r');
|
||||
putchar('\n');
|
||||
continue;
|
||||
}
|
||||
if (fmt[i] != '%') {
|
||||
putchar(fmt[i]);
|
||||
continue;
|
||||
}
|
||||
++i;
|
||||
switch (fmt[i]) {
|
||||
case '%': putchar('%'); continue;
|
||||
case 'd': goto fmt_decimal;
|
||||
case 'x': goto fmt_hex;
|
||||
case 's': goto fmt_string;
|
||||
case 0: goto fmt_end;
|
||||
}
|
||||
putchar('%');
|
||||
putchar(fmt[i]);
|
||||
continue;
|
||||
|
||||
if (0) fmt_decimal: {
|
||||
INT32 x = va_arg(args, INT32);
|
||||
if (x < 0) {
|
||||
putchar('-');
|
||||
x = -x;
|
||||
}
|
||||
const CHAR16* s = TmpIntToStr(x);
|
||||
while (*s) {
|
||||
putchar(*s++);
|
||||
}
|
||||
}
|
||||
if (0) fmt_hex: {
|
||||
UINT32 x = va_arg(args, UINT32);
|
||||
for (int pos = 8, started = 0; pos--;) {
|
||||
int d = (x >> (4 * pos)) & 0xf;
|
||||
if (d || started || pos == 0) {
|
||||
putchar("0123456789abcdef"[d]);
|
||||
started = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (0) fmt_string: {
|
||||
CHAR16* s = va_arg(args, CHAR16*);
|
||||
while (*s) {
|
||||
putchar(*s++);
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt_end:
|
||||
va_end(args);
|
||||
if (mode) {
|
||||
ST->ConOut->OutputString(ST->ConOut, buf);
|
||||
}
|
||||
if (mode != -1) {
|
||||
StrnCat(log_buffer, buf, log_buffer_size - StrLen(log_buffer) - 1);
|
||||
RT->SetVariable(LogVarName, &LogVarGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, StrLen(log_buffer) * 2, log_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void DumpLog(void) {
|
||||
ST->ConOut->OutputString(ST->ConOut, log_buffer);
|
||||
}
|
||||
|
||||
void ClearLogVariable(void) {
|
||||
RT->SetVariable(LogVarName, &LogVarGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, 0, 0);
|
||||
}
|
||||
|
||||
const CHAR16* TrimLeft(const CHAR16* s) {
|
||||
@@ -71,11 +160,24 @@ void RandomSeedAuto(void) {
|
||||
EFI_STATUS WaitKey(UINT64 timeout_ms) {
|
||||
ST->ConIn->Reset(ST->ConIn, FALSE);
|
||||
const int ms_to_100ns = 10000;
|
||||
return WaitForSingleEvent(ST->ConIn->WaitForKey, timeout_ms * ms_to_100ns);
|
||||
|
||||
EFI_EVENT events[2] = {ST->ConIn->WaitForKey};
|
||||
EFI_STATUS status = BS->CreateEvent(EVT_TIMER, 0, NULL, NULL, &events[1]);
|
||||
if (!EFI_ERROR(status)) {
|
||||
BS->SetTimer(events[1], TimerRelative, timeout_ms * ms_to_100ns);
|
||||
UINTN index;
|
||||
status = BS->WaitForEvent(2, events, &index);
|
||||
BS->CloseEvent(events[1]);
|
||||
if (!EFI_ERROR(status) && index == 1) {
|
||||
status = EFI_TIMEOUT;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
EFI_INPUT_KEY ReadKey(UINT64 timeout_ms) {
|
||||
EFI_INPUT_KEY key = {0};
|
||||
ST->ConOut->EnableCursor(ST->ConOut, 1);
|
||||
WaitKey(timeout_ms);
|
||||
ST->ConIn->ReadKeyStroke(ST->ConIn, &key);
|
||||
return key;
|
||||
@@ -90,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);
|
||||
@@ -106,7 +210,7 @@ void* LoadFileWithPadding(EFI_FILE_HANDLE dir, const CHAR16* path, UINTN* size_p
|
||||
}
|
||||
handle->Close(handle);
|
||||
if (EFI_ERROR(e)) {
|
||||
FreePool(data);
|
||||
BS->FreePool(data);
|
||||
return 0;
|
||||
}
|
||||
if (size_ptr) {
|
||||
|
||||
27
src/util.h
27
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,9 +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 CONST CHAR16 *fmt, ...);
|
||||
extern void Log(int mode, IN CONST CHAR16 *fmt, ...);
|
||||
|
||||
/**
|
||||
* Dump the log buffer to the screen.
|
||||
*/
|
||||
extern void DumpLog(void);
|
||||
|
||||
/**
|
||||
* Clear the log EFI variable, for minor RAM savings.
|
||||
*/
|
||||
extern void ClearLogVariable(void);
|
||||
|
||||
/**
|
||||
* Return the greater of two numbers.
|
||||
@@ -107,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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user