132 Commits

Author SHA1 Message Date
Lauri Kenttä 62cc4a8cb7 Update change log and tag v2.6.0 2026-04-02 22:37:51 +03:00
Lauri Kenttä 630adfc50b Update shim to 16.1 (x64, aa64)
Use OL10 shim.
For ia32 we're stuck with shim 15.8, distros don't support it anymore.
2026-04-02 22:36:48 +03:00
Lauri Kenttä a78b6b22a3 Only install the weirdly named loader if shim 15 is used 2026-04-02 22:32:34 +03:00
Lauri Kenttä 7db9c2d3ec Suggest booting to FW to enable HackBGRT 2026-04-02 22:32:34 +03:00
Lauri Kenttä 28129c23fb Rename setup.log to setup-log.txt for easier use 2026-04-02 22:32:34 +03:00
Lauri Kenttä bc2a192506 Improve README for TPM, Secure Boot, only one HDD 2026-04-02 22:32:34 +03:00
Lauri Kenttä 3d5efa2117 Fix GetVariable with >4kB data 2026-04-02 22:32:34 +03:00
Lauri Kenttä 683ee49110 Allow position as a fraction of resolution
UEFI recommends y=.382 (1 - 1 / (golden ratio)), make this the default.
Fractional position starts with a decimal point: x=.5 y=.382
Pixel offset relative to the center is set as before: x=123 y=456
If the coordinates were omitted, old default was: x=0 y=0
2026-03-30 19:14:31 +03:00
Lauri Kenttä d40ce9f6c5 Make batch example the same as option I 2025-07-29 11:24:47 +03:00
Lauri Kenttä 447ff30f61 Fix getting last line from BCDEdit output 2025-07-29 11:14:37 +03:00
Lauri Kenttä 5e9ef85451 Update change log and tag v2.5.2 2025-04-13 00:06:42 +03:00
Lauri Kenttä 96599b1838 Try to load config.txt from the current dir
To allow multiple configurations for HackBGRT, or to allow a custom
installation path, try to load config.txt from the same directory as
the executable.
2025-04-12 23:59:35 +03:00
Lauri Kenttä 611ab30db3 Improve output and README for BootOrder problems
Report own entry status (missing, disabled, after windows, enabled).
Also improve README instructions on the topic.
2025-04-12 23:59:35 +03:00
Lauri Kenttä ffa3e335ea Improve output and README for BCDEdit errors 2025-04-12 23:59:35 +03:00
Lauri Kenttä a1f6297759 Report if UEFI is missing (can't read variables) 2025-04-12 23:59:35 +03:00
Lauri Kenttä afd0780b61 Try to avoid mistaking C: for ESP 2025-04-12 23:59:35 +03:00
Lauri Kenttä 320e154457 Check for missing mountvol /S 2025-04-12 23:59:35 +03:00
Lauri Kenttä 785307c0e2 Allow overriding ESP path 2025-04-12 23:59:35 +03:00
Lauri Kenttä be62caa400 Add missing README entries for dry-run and arch 2025-04-12 23:59:35 +03:00
Lauri Kenttä 734ea21308 Don't require Windows Boot Manager entry if it's not needed 2025-04-12 23:59:35 +03:00
Lauri Kenttä 1946765680 Log Windows version 2025-04-12 23:59:35 +03:00
Lauri Kenttä 79ee253108 Update change log and tag v2.5.1 2024-08-18 09:38:43 +03:00
Lauri Kenttä 82abb0c120 Update shim to 15.8
Use RockyLinux shim, they have all variants (x86_64, aa64, ia32).
After Windows update KB5041585, the old shim SBAT is not accepted.

Fixes #197.
2024-08-18 09:37:01 +03:00
Lauri Kenttä 830db410ea Properly propagate configuration error message 2024-07-20 15:26:18 +03:00
Lauri Kenttä 1e36d7e388 Support compiling with MSYS2 tools in Windows
Windows and MSYS2 need csc parameters to start with "-", not "/".
Now setup.exe can be compiled with the csc in MSYS2 mono package.
The EFI binaries can already be compiled with MSYS2 clang & lld.
2024-06-22 19:38:29 +03:00
Lauri Kenttä 9038e20cd2 Update change log and tag v2.5.0 2024-06-21 15:36:46 +03:00
Lauri Kenttä 9a0d4737e1 Improve setup.exe metadata 2024-06-21 15:36:46 +03:00
Lauri Kenttä fa6fae3aa3 Catch errors in writing image 2024-06-13 15:50:19 +03:00
Lauri Kenttä 90fb8e47c1 Don't panic if BootCurrent is not found 2024-06-13 15:50:19 +03:00
Lauri Kenttä c826149183 Refactor EFI boot entry code into a new class 2024-06-13 15:50:19 +03:00
Lauri Kenttä af4f99aab6 If missing files, warn that zip is not extracted 2024-05-09 19:50:56 +03:00
Lauri Kenttä 8a97382a2e Clarify 'Log is empty', add a section in README 2024-05-09 19:50:51 +03:00
Lauri Kenttä 8e6466990a Skip the workaround in a44b9290 if skipping shim 2024-04-20 21:58:01 +03:00
Lauri Kenttä 6f94f6bc28 Properly handle skip-shim with enable-overwrite 2024-04-20 21:57:47 +03:00
Lauri Kenttä bc600a6c2f Handle more command-line options before executing actions 2024-04-20 21:31:19 +03:00
Lauri Kenttä 022ea9b93b Log image dimensions when installing 2024-04-20 21:31:19 +03:00
Lauri Kenttä 7d7d4c2aa4 Clarify installation and upgrading in README 2024-04-20 16:04:54 +03:00
Lauri Kenttä f1c8b11d6b Add some troubleshooting info to README 2024-04-20 15:54:20 +03:00
Lauri Kenttä a0553856f0 Fix batch installation instructions 2024-04-20 15:49:33 +03:00
Lauri Kenttä ffa29f6ffc Update gnu-efi to 3.0.18 2024-04-11 18:07:40 +03:00
Lauri Kenttä b9e23c91a3 Update change log and tag 2.4.1 2024-04-11 17:48:55 +03:00
Lauri Kenttä 697c57355b Disable old version before copying files
Apparently some people still have the obsolete v1.5.1 installed
and manage to break things by copying v2.x config in place.
2024-04-11 17:46:13 +03:00
Lauri Kenttä da16365508 Report if BCDEdit is not working
One possible cause for BCDEdit failure is Windows error 0x800703EE.
"The volume for a file has been externally altered so that the opened file is no longer valid."
Suggested solutions include disabling antivirus or backup software.
2024-04-11 17:46:13 +03:00
Lauri Kenttä 665a4732ca Detect device paths which contain extra data
Apparently some firmware may add extra data to the device path,
so exit the loop on the first end-of-path record.
Fixes GH issue #181.
2024-03-24 17:27:02 +02:00
Lauri Kenttä 39596aadfc Don't try to remove non-existent dir. 2024-01-20 12:43:00 +02:00
Lauri Kenttä 1a5b1df064 Update change log and tag 2.4.0 2023-12-31 18:35:35 +02:00
Lauri Kenttä ea70f3ce79 Disable before enabling, and default to BCDEdit 2023-12-31 18:29:48 +02:00
Lauri Kenttä a44b929012 Fix a shim error caused by bad load options data
Shim expects a filename or nothing in the load options.

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

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

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

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

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

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

Set architecture in PE file manually:
EFI uses IMAGE_FILE_MACHINE_ARMTHUMB_MIXED = 0x01C2,
while the default is IMAGE_FILE_MACHINE_ARMNT = 0x01C4.
2023-11-25 19:16:43 +02:00
Lauri Kenttä 7ccdcc4a77 Add ARM (32-bit) target (without shim, though)
Remove some integer divisions because they emit function calls.
2023-11-20 19:45:28 +02:00
Lauri Kenttä 50e84e8c90 Add AArch64 target 2023-11-20 19:45:28 +02:00
Lauri Kenttä 24c7e2b316 Switch to Clang
Clang can cross-compile to PE and is easier to setup than MinGW.
2023-11-20 19:45:28 +02:00
Lauri Kenttä 4379f9cbeb Add gnu-efi submodule for headers, don't link, just re-implement some functions 2023-11-20 19:42:23 +02:00
Lauri Kenttä 2a0f2a7757 Get shell arguments without gnu-efi 2023-11-20 19:31:25 +02:00
Lauri Kenttä ebbacb72bb Open ESP root dir without gnu-efi 2023-11-20 19:31:25 +02:00
Lauri Kenttä a908036ee8 Implement WaitKey without gnu-efi 2023-11-20 19:31:25 +02:00
Lauri Kenttä 57ce3ae33e Avoid using gnu-efi static global GUIDs 2023-11-20 19:31:25 +02:00
Lauri Kenttä 7dd048346d Use EFI BS FreePool, SetMem, CopyMem 2023-11-20 19:31:25 +02:00
Lauri Kenttä 0dfc49c800 Re-implement string formatting (%s, %d, %x) 2023-11-20 19:31:25 +02:00
Lauri Kenttä 4e50b33636 Check BMP pixel data size when loading 2023-11-20 19:31:25 +02:00
Lauri Kenttä 5856d25b5b Update change log and tag v2.2.0 2023-11-17 23:24:58 +02:00
Lauri Kenttä 466ab69c48 Use shim 15.7 to support Secure Boot
Use shim-signed and shim-helpers-{arch}-signed from Debian:
https://packages.debian.org/bookworm/shim-signed
2023-11-17 23:24:58 +02:00
Lauri Kenttä 14aa79929a Easily override GIT_DESCRIBE 2023-11-17 23:13:12 +02:00
Lauri Kenttä 5e32a3f880 Add clean target to Makefile 2023-11-17 23:13:12 +02:00
Lauri Kenttä db934099df Gather log during boot 2023-11-17 23:13:12 +02:00
Lauri Kenttä e93ed54cb2 Make setup menu shorter 2023-11-17 22:47:29 +02:00
Lauri Kenttä 9b3b045a21 Add SBAT section to the EFI binaries 2023-11-17 22:26:43 +02:00
Lauri Kenttä be8a5d35d2 Sign the EFI files 2023-11-17 22:26:43 +02:00
Lauri Kenttä 2366fc8b98 Warn about Fast Startup (Hiberboot) 2023-11-16 22:56:10 +02:00
Lauri Kenttä e04ba3e451 Log boot entries during setup 2023-11-16 22:07:08 +02:00
Lauri Kenttä f40f2bc9dc Clarify LogBGRT output for corner cases 2023-11-09 21:46:39 +02:00
Lauri Kenttä 2f572b24d4 Free BMP if it's invalid 2023-11-09 21:46:35 +02:00
Lauri Kenttä da9909bbdd Update change log and tag v2.1.0 2023-10-04 15:25:03 +03:00
Lauri Kenttä 0ce904f133 Use 999999 as the 'big' value instead of 2**31-1 2023-10-04 15:25:03 +03:00
Lauri Kenttä 62c892009a Overwrite BGRT in a more readable manner 2023-10-04 15:25:03 +03:00
Lauri Kenttä 00bbfd6d82 Add ACPI table addresses to debug output 2023-10-04 15:25:03 +03:00
Lauri Kenttä 1e566a05a0 Clarify README for image conversion 2023-10-04 15:25:03 +03:00
Lauri Kenttä 1058f3e2b4 Log BGRT contents during setup 2023-10-04 15:25:03 +03:00
Lauri Kenttä 661758ba58 Crop BMP to screen size 2023-09-14 18:07:48 +03:00
Lauri Kenttä 93eec1a250 Improve BMP handling 2023-09-14 18:05:38 +03:00
Lauri Kenttä 33e4450d19 Check that BitLocker is disabled 2023-09-10 23:03:50 +03:00
Lauri Kenttä 99bdf5a310 Update change log and tag v2.0.0 2023-09-10 15:43:04 +03:00
Lauri Kenttä d2bac39680 Add assembly information to setup.exe 2023-09-10 15:43:04 +03:00
Lauri Kenttä a4c917294c Add option to boot to firmware 2023-09-10 15:43:04 +03:00
Lauri Kenttä d6da4b7cee Support creating boot entry with bcdedit 2023-09-10 15:42:18 +03:00
Lauri Kenttä f2185f624d Detect issues with boot= option 2023-09-10 15:02:58 +03:00
Lauri Kenttä 75e64a7ef3 Handle screen orientation 2023-09-10 15:02:58 +03:00
Lauri Kenttä e5616c6cd2 Make coordinates relative to center
Relative coordinates make it easier to center the image.
The value "auto" is now replaced with simply 0.
The value "native" is replaced with "keep" for consistency.
The value is clamped, so x=-9999 will align to the left border etc.
2023-09-10 15:02:58 +03:00
Lauri Kenttä ad0b71c49b Set EFI ReadKey timeout to 15 seconds 2023-09-10 15:02:58 +03:00
Lauri Kenttä e44ce9f5ee Add logging 2023-09-10 15:02:58 +03:00
Lauri Kenttä 5f3c6afc23 Remove BootLoaderInfo class 2023-09-10 15:02:58 +03:00
Lauri Kenttä 1fa53f3f06 Remove SetupHelper class 2023-09-10 15:02:58 +03:00
Lauri Kenttä 8301a16ca8 Support arch=... parameter, improve auto-detection 2023-09-10 15:02:58 +03:00
Lauri Kenttä de9d0d984a Add partial Linux support to installer 2023-09-10 15:02:58 +03:00
Lauri Kenttä 7dbdf33ea8 Create an own entry, don't replace MS boot loader 2023-09-10 15:02:58 +03:00
Lauri Kenttä dfb5b916ed Elevate privileges only when needed 2023-09-10 15:02:58 +03:00
Lauri Kenttä 267af0bd9c Support dry run in the installer 2023-09-10 15:02:58 +03:00
Lauri Kenttä be5894b387 Check for HackBGRT string when disabling old version 2023-09-10 15:02:58 +03:00
Lauri Kenttä 199650a567 Don't access ESP before showing the menu 2023-09-10 15:02:58 +03:00
Lauri Kenttä 31172f71ca Add support for quiet setup 2023-09-10 15:02:58 +03:00
Lauri Kenttä 990f245ac9 Convert images when installing 2023-09-09 17:55:44 +03:00
Lauri Kenttä dfadf67a21 Configure before installing 2023-09-09 17:55:44 +03:00
Lauri Kenttä 691fbd164b Improve path handling for platform independency 2023-09-09 17:55:39 +03:00
Lauri Kenttä 7d5f4eac17 Makefile default target: efi, setup and zip 2023-08-27 13:05:19 +03:00
Lauri Kenttä 0b5899e801 Make GIT_DESCRIBE.data const 2023-08-27 13:05:08 +03:00
Lauri Kenttä 4cf12f26b2 Update Print function signature for gnu-efi 3.0.11 2020-03-01 06:03:17 +02:00
Lauri Kenttä 3396a4799d Update change log and tag v1.5.1 2018-08-11 14:01:48 +03:00
Lauri Kenttä d35a9abb0a Add debug=0 to the default config.txt 2018-08-11 13:45:04 +03:00
Lauri Kenttä 85811d62a6 Add note about EFI System Partition to config.txt 2018-08-11 13:42:41 +03:00
Lauri Kenttä 449dc6acc6 Create OsIndications if it's missing 2018-07-19 19:10:01 +03:00
Lauri Kenttä 1980e5c05c Clarify default config.txt 2018-07-19 19:10:01 +03:00
Lauri Kenttä 9a59f69a28 Compile C# with csc (not mcs) 2018-07-11 16:59:27 +03:00
Lauri Kenttä 1fffbcff2c Fix member naming 2018-07-11 16:55:26 +03:00
Soheibooo bd7a5f3eea Correct typo in README 2018-03-02 00:45:10 -05:00
29 changed files with 3109 additions and 707 deletions
+21
View File
@@ -0,0 +1,21 @@
---
name: Bug report
about: Create a report if you encounter a bug
title: ''
labels: ''
assignees: ''
---
**What happens?**
Describe the bug.
**How this happens?**
Explain exactly the steps you did:
1. Download HackBGRT-(version).zip and extract all files.
2. Start setup.
3. Select ...
4. ...
**Log file**
Run the setup again and select menu option `L` (or add parameter `show-boot-log` on command line). Attach the `setup-log.txt` to this report.
+3
View File
@@ -0,0 +1,3 @@
[submodule "gnu-efi"]
path = gnu-efi
url = https://github.com/rhboot/gnu-efi.git
+97
View File
@@ -2,6 +2,103 @@
All notable changes to this project will be documented in this file.
## 2.6.0 - 2026-04-02
### Added
- Allow setting image position as a fraction of resolution.
### Changed
- Update *shim* to 16.1 for x86_64 and Aarch64.
- Rename `setup.log` to `setup-log.txt` for easier use.
## 2.5.2 - 2025-04-13
### Fixed
- Try to avoid mistaking C: for ESP.
- Don't require Windows boot loader entry if it's not needed.
### Changed
- Allow overriding ESP path.
- Try to load `config.txt` from the current dir, to support multiple configurations.
- Improve output and README for various situations.
## 2.5.1 - 2024-08-18
### Changed
- Update *shim* to 15.8.
## 2.5.0 - 2024-06-21
### Changed
- Properly handle skip-shim with enable-overwrite.
- Improve instructions (documentation).
- Improve error reporting and logging.
## 2.4.1 - 2024-04-11
### Fixed
- Report better if BCDEdit is unable to operate.
- Improve support for non-BCDEdit boot entries.
- Remove old version before copying any new files.
## 2.4.0 - 2023-12-31
### Fixed
- Fix BCDEdit boot entries to avoid *shim* error messages.
- Combine BCDEdit and own code to create boot entries more reliably.
### Changed
- Clear the screen to wipe the vendor logo as soon as possible.
- Image paths in `config.txt` may be relative (just file names).
## 2.3.1 - 2023-11-27
### Fixed
- BitLocker detection is more reliable.
## 2.3.0 - 2023-11-27
### Added
- AArch64 and ARM builds, and *shim* for AArch64.
### Fixed
- Boot entry is more reliable, avoids conflicts with firmware entries.
## 2.2.0 - 2023-11-17
### Added
- Support Secure Boot with *shim* boot loader.
- Gather debug log during boot and read it with setup.exe.
## 2.1.0 - 2023-10-04
### Added
- Check image size, crop if it's bigger than the screen.
- Check BitLocker status to avoid unbootable machine.
## 2.0.0 - 2023-09-10
### Added
- Log to `setup.log`.
- Image conversion (GIF, EXIF, JPG, PNG, TIFF) to BMP during setup.
- Quiet (batch) setup.
- Dry run in setup.
- EFI boot entry support in setup.
- Orientation parameter (o=0|90|180|270) for images.
### Changed
- Configure (edit config and images) before installing.
- Escalate privileges only when needed (after the menu).
- Try to detect and avoid some configuration errors.
- Wait at most 15 seconds for key presses during boot.
- Image coordinates are now relative to the center.
## 1.5.1 - 2018-08-11
### Fixed
- Clarify the default config.txt.
- Fix an exception in some cases when trying to boot to UEFI setup.
## 1.5.0 - 2017-09-30
### Added
+123 -31
View File
@@ -1,44 +1,136 @@
CC = $(CC_PREFIX)-gcc
CFLAGS = -std=c11 -O2 -ffreestanding -mno-red-zone -fno-stack-protector -Wshadow -Wall -Wunused -Werror-implicit-function-declaration -Werror
CC = clang
CFLAGS = -target $(CLANG_TARGET) -ffreestanding -fshort-wchar
CFLAGS += -std=c17 -Wshadow -Wall -Wunused -Werror-implicit-function-declaration
CFLAGS += -I$(GNUEFI_INC) -I$(GNUEFI_INC)/$(GNUEFI_ARCH) -I$(GNUEFI_INC)/protocol
LDFLAGS = -nostdlib -shared -Wl,-dll -Wl,--subsystem,10 -e _EfiMain
LIBS = -L$(GNUEFI_LIB) -lefi -lgcc
CFLAGS += $(ARCH_CFLAGS)
LDFLAGS = -target $(CLANG_TARGET) -nostdlib -Wl,-entry:efi_main -Wl,-subsystem:efi_application -fuse-ld=lld
ARCH_CFLAGS = -O2 -mno-red-zone
GNUEFI_INC = /usr/$(CC_PREFIX)/include/efi
GNUEFI_LIB = /usr/$(CC_PREFIX)/lib
GNUEFI_INC = gnu-efi/inc
FILES_C = src/main.c src/util.c src/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/SetupHelper.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
FILES_CS = src/Setup.cs src/Esp.cs src/Efi.cs src/EfiBootEntries.cs
# Generate version number from git describe.
# In the numeric form, add the number of commits as the last part.
# (Add .1 for uncommitted changes.)
GIT_DESCRIBE := $(firstword $(GIT_DESCRIBE) $(shell git describe --tags --dirty=-1-dirty) unknown)
GIT_DESCRIBE_PARTS := $(subst -, ,$(patsubst v%,%,$(GIT_DESCRIBE))) 0
GIT_DESCRIBE_NUMERIC := $(firstword $(GIT_DESCRIBE_PARTS)).$(word 2,$(GIT_DESCRIBE_PARTS))
define GIT_DESCRIBE_CS
public class GIT_DESCRIBE {
public const string data = "$(GIT_DESCRIBE)";
public const string numeric = "$(GIT_DESCRIBE_NUMERIC)";
}
endef
CFLAGS += '-DGIT_DESCRIBE_W=L"$(GIT_DESCRIBE)"' '-DGIT_DESCRIBE="$(GIT_DESCRIBE)"'
RELEASE_NAME = HackBGRT-$(GIT_DESCRIBE:v%=%)
EFI_ARCH_LIST = x64 ia32 aa64 arm
EFI_SIGNED_FILES = $(patsubst %,efi-signed/boot%.efi,$(EFI_ARCH_LIST))
.PHONY: all efi efi-signed setup release clean
efi: bootx64.efi bootia32.efi
setup: setup.exe
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."
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)"
efi: $(patsubst %,efi/boot%.efi,$(EFI_ARCH_LIST))
@echo "EFI executables are in the efi/ directory."
efi-signed: $(patsubst %,efi-signed/boot%.efi,$(EFI_ARCH_LIST))
@echo "Signed EFI executables are in the efi-signed/ directory."
setup: setup.exe
release: release/$(RELEASE_NAME).zip
@echo "Current version is packaged: $<"
release/$(RELEASE_NAME): $(EFI_SIGNED_FILES) certificate.cer config.txt splash.bmp setup.exe README.md CHANGELOG.md README.efilib LICENSE shim-signed/* shim.md
rm -rf $@
tar c --transform=s,^,$@/, $^ | tar x
release/$(RELEASE_NAME).zip: release/$(RELEASE_NAME)
rm -rf $@
(cd release; 7z a -mx=9 "$(RELEASE_NAME).zip" "$(RELEASE_NAME)" -bd -bb1)
src/GIT_DESCRIBE.cs: $(FILES_CS) $(FILES_C) $(FILES_H)
echo 'public class GIT_DESCRIBE { public static string data = "$(GIT_DESCRIBE)"; }' > $@
$(file > $@,$(GIT_DESCRIBE_CS))
setup.exe: $(FILES_CS) src/GIT_DESCRIBE.cs
mcs -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)
+115 -28
View File
@@ -4,66 +4,153 @@ HackBGRT is intended as a boot logo changer for UEFI-based Windows systems.
## Summary
When booting on a UEFI-based computer, Windows may show a vendor-defined logo which is stored on the UEFI firmware in a section called Boot Graphics Resource Table (BGRT). It's usually very difficult to change the image permamently, but a custom UEFI application may be used to overwrite it during the boot. HackBGRT does exactly that.
When booting on a UEFI-based computer, Windows may show a vendor-defined logo which is stored on the UEFI firmware in a section called Boot Graphics Resource Table (BGRT). It's usually very difficult to change the image permanently, but a custom UEFI application may be used to overwrite it during the boot. HackBGRT does exactly that.
**Note:** The original logo is often visible for a moment before HackBGRT is started. This is expected, please do not report this "bug". This can't be changed without modifying computer firmware, which this project will not do.
## Usage
**Important:** If you mess up the installation, your system may become unbootable! Create a rescue disk before use. This software comes with no warranty. Use at your own risk.
* Make sure that your computer is booting with UEFI.
* Make sure that Secure Boot is disabled, unless you know how to sign EFI applications.
### Secure Boot
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 copies of *shim* are extracted from various Linux distributions many thanks to the maintainers! For copyright information, see [shim-signed/COPYRIGHT](shim-signed/COPYRIGHT).
### TPM
TPM, or Trusted Platform Module, watches how your computer boots. When there is a change in the boot process such as using HackBGRT some things may stop working. This includes:
* BitLocker or similar disk encryption.
* Anti-cheat software.
* Windows PIN unlock method.
* Other security-related things which mention TPM.
You should disable these features before using HackBGRT. Some of them can be re-enabled afterwards, some can't. For any TPM problems, it's recommended to either uninstall HackBGRT or stop using the problematic feature.
(If you find an easy way to reconfigure the TPM, please document, test and share it. Technically one solution would be to install HackBGRT before Windows and make sure that Windows is always booted through HackBGRT. But that's easier said than done.)
### Windows installation
* Make sure you **read the above sections**.
* Get the latest release from the Releases page.
* Make sure you have only one bootable hard drive. Otherwise the automatic setup may fail.
* Start `setup.exe` and follow the instructions.
* You may need to manually disable Secure Boot and then retry.
* The installer will launch Notepad for modifying the configuration.
* If you need only one custom image, the defaults are fine.
* Otherwise, check out the examples in the configuration file.
* The installer will launch Paint for creating the image(s).
* You can create multiple images by using Save As.
* Be sure to always use the 24-bit BMP/DIB format.
* If Windows later restores the original boot loader, just reinstall.
* If you wish to change the image or other configuration, just reinstall.
* The installer will launch Paint for editing the image, or you can edit it otherwise.
* For advanced settings, edit `config.txt` before installing. No extra support provided!
* Read the instructions in [shim.md](shim.md).
* Check the common [troubleshooting](#troubleshooting) to be prepared.
* Reboot your computer.
* If Windows later restores the original boot loader, just reinstall.
* If you wish to change the image or configuration later, choose the option to only install files.
### Quiet (batch) installation
* Edit the `config.txt` and `splash.bmp` (or any other images) to your needs.
* Run `setup.exe batch COMMANDS` as administrator, with some of the following commands:
* `install` copy the files but don't enable.
* `enable-bcdedit` use `bcdedit` to create a new EFI boot entry.
* `disable-bcdedit` use `bcdedit` to disable the EFI boot entry.
* `enable-entry` write NVRAM to create a new EFI boot entry.
* `disable-entry` write NVRAM to disable the EFI boot entry.
* `enable-overwrite` overwrite the MS boot loader.
* `disable-overwrite` restore the MS boot loader.
* `skip-shim` skip *shim* when installing.
* `allow-secure-boot` ignore Secure Boot in subsequent commands.
* `allow-bitlocker` ignore BitLocker in subsequent commands.
* `allow-bad-loader` ignore bad boot loader configuration in subsequent commands.
* `disable` run all relevant `disable-*` commands.
* `uninstall` disable and remove completely.
* `show-boot-log` show the debug log collected during boot (if `log=1` is set in `config.txt`).
* `arch=...` force architecture.
* `esp=...` force EFI System Partition path.
* `dry-run` skip actual changes.
* For example, `setup.exe batch disable install enable-bcdedit` would disable any previous installation, then install the files and create the EFI boot entry with `bcdedit`.
### Multi-boot configurations
If you only need HackBGRT for Windows:
* Extract the latest release to `[EFI System Partition]\EFI\HackBGRT\`.
* Set `boot=\EFI\Microsoft\Boot\bootmgfw.efi` in `config.txt`.
* Point your boot loader to `\EFI\HackBGRT\bootx64.efi`.
* Run `setup.exe`, install files without enabling.
* Configure your boot loader to start `\EFI\HackBGRT\loader.efi`.
If you need it for other systems as well:
* Extract the latest release to `[EFI System Partition]\EFI\HackBGRT\`.
* Set `boot=\EFI\your-actual-boot-loader.efi` in `config.txt`.
* Set `\EFI\HackBGRT\bootx64.efi` as your default boot loader with `efibootmgr` or some other EFI boot manager tool.
* Configure HackBGRT to start your boot loader (such as systemd-boot): `boot=\EFI\systemd\systemd-bootx64.efi`.
* Run `setup.exe`, install as a new EFI boot entry.
On 32-bit machines, use `bootia32.efi` instead of `bootx64.efi`.
To install purely on Linux, you can install with `setup.exe dry-run` and then manually copy files from `dry-run/EFI` to your `[EFI System Partition]/EFI`. For further instructions, consult the documentation of your own Linux system.
HackBGRT tries to read its configuration from the same directory where it's installed, so you can even make (manually) multiple installations in different directories.
## Configuration
The configuration options are described in `config.txt`, which should be stored in `[EFI System Partition]\EFI\HackBGRT\config.txt`.
The configuration options are described in `config.txt`, which the installer copies into `[EFI System Partition]\EFI\HackBGRT\config.txt`. For debugging purposes, the same options may be given also as command line parameters in the EFI Shell.
## Images
The image path can be changed in the configuration file. The default path is `[EFI System Partition]\EFI\HackBGRT\splash.bmp`.
If you only need one image, just edit `splash.bmp` to your needs.
The image must be a 24-bit BMP file with a 54-byte header. That's a TrueColor BMP3 in Imagemagick, or 24-bit BMP/DIB in Microsoft Paint.
Advanced users may edit the `config.txt` to define multiple images, in which case one is picked at random. The installer copies and converts the images. For example, to use a file named `my.jpg`, copy it in the installer folder (same folder as `setup.exe`) and set the image path in `config.txt` to `path=my.jpg` before running the installer.
Multiple images may be specified, in which case one is picked at random.
If you copy an image file to ESP manually, note that the image must be a 24-bit BMP file with a 54-byte header. That's a TrueColor BMP3 in Imagemagick, or 24-bit BMP/DIB in Microsoft Paint.
## Recovery
## Troubleshooting
If something breaks and you can't boot to Windows, you have the following options:
### BCDEdit failed
* Windows installation (or recovery) media can fix boot issues.
* You can copy `[EFI System Partition]\EFI\HackBGRT\bootmgfw-original.efi` into `[EFI System Partition]\EFI\Microsoft\Boot\bootmgfw.efi` by some other means such as Linux or Windows command prompt.
You can first try the other installation option in the menu. If it doesn't work either, your computer might have a problem. Open Command Prompt and figure out why `bcdedit /enum firmware` fails. In some cases, disabling antivirus, checking the hard disk or searching for 'how to fix 0x800703EE' may help.
### Verification failed, Security violation
This is part of the setup on first boot. Make sure you have read and understood [shim.md](shim.md).
### Boot is slow, boot is stuck, just spinning
Sometimes the first boot is very slow (multiple minutes) for an unknown reason. Wait patiently until you get into Windows. Try to reboot at least a few times to see if it gets any better. It it does not, there's not much else to do than give up.
### Image is not visible, "nothing happens"
Run the setup again and select the option to check the boot log, marked with `BOOT LOG START` in the log file. Continue troubleshooting according to the log contents:
#### Boot log is empty
If the log is empty, then HackBGRT is not in use. Many computers now have a security feature which causes this problem: the computer prevents enabling HackBGRT automatically, instead it resets a certain setting (BootOrder) on reboot and skips the newly-installed HackBGRT.
You have to fix this manually. (After all, the security feature is specifically designed to prevent automatic changes.)
1. Run the setup again.
2. Select the option "boot to UEFI setup".
3. After a reboot, you should get into your computer's own setup utility (UEFI or Firmware settings, or so-called "BIOS").
4. Find boot options and the list of boot entries.
5. Select HackBGRT as the default boot entry (before Windows Boot Loader).
The setup utility is different for each computer and manufacturer, so search online for "[computer model] UEFI setup" or "firmware setup" for images and instructions.
Some people report that HackBGRT is not visible in the computer settings. That's unfortunately a problem with your computer, and you should ask your computer manufacturer how to edit boot entries inside your computer settings. HackBGRT needs to boot `\EFI\HackBGRT\loader.efi`.
If all else fails and you are sure about your computer skills, you can try the legacy installation method. The method bypasses this particular problem but may cause very serious problems if configured incorrectly.
#### Boot log is not empty
If the log shows that HackBGRT has been run during boot, the problem is usually in your configuration file or image. Try to reinstall HackBGRT with the default configuration and image.
If the default logo works, try again with your custom image. Make sure that the image has a reasonable size and position so that it fits the resolution which HackBGRT reports during boot. The resolution may be lower than your desktop resolution.
When you get your image working with the default configuration, you can do any other necessary changes to `config.txt`.
If the default logo does not work, check the boot log again to see if there is some obvious error.
You may report an issue and attach the `setup-log.txt` file.
### Impossible to boot at all
If you used the default installation method, then your Windows boot loader is still in place and you should be able to access UEFI Setup ("BIOS setup") or boot loader list by some key combination right after powering on your computer. There you can choose the `Windows Boot Loader` and continue as usual to uninstall HackBGRT.
If you selected the legacy installation method which overwrites Windows boot loader, then you need to use the Windows installation disk (or recovery disk) to fix boot issues.
## Building
* Compiler: GCC targeting w64-mingw32
* Compiler: Clang
* Compiler flags: see Makefile
* Libraries: gnu-efi
Executable → Regular
+27 -15
View File
@@ -1,35 +1,47 @@
# vim: set fileencoding=utf-8
# The same options may be given also as command line parameters in the EFI Shell, which is useful for debugging.
# Lines starting with '#' are comments.
# Boot loader path. Point to the real Windows boot loader.
boot=\EFI\HackBGRT\bootmgfw-original.efi
# Boot loader path. MS = either backup or original Windows boot loader.
boot=MS
# The image is specified with an image line.
# Multiple image lines may be present, in which case one will be picked by random.
# The image line may contain the following parts:
# Any of the following:
# - "n=[0-9]+", a weight for this image in the randomization process. Default: n=1.
# - "x={auto|native|[0-9]+}", the x coordinate. Default: x=auto.
# - "y={auto|native|[0-9]+}", the y coordinate. Default: y=auto.
# - "x=(number) y=(number)" positions the image relative to the screen center.
# - "x=.(decimal) y=.(decimal)" positions the image as a fraction of the resolution.
# - "o=(0|90|180|270|keep)", the screen orientation, degrees anticlockwise.
# - "n=(number)", a weight for this image in the randomization process.
# - Default values: o=keep x=.5 y=.382 n=1
# One of the following:
# - "keep" to keep the firmware logo. Sets also x=native,y=native by default.
# - "keep" to keep the firmware logo (and coordinates, unless specified).
# - "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. The file must be a 24-bit BMP file with a 54-byte header.
# - "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=auto,y=0,path=\EFI\HackBGRT\topimage.bmp
# - image=n=1,path=\EFI\HackBGRT\splash.bmp
# - image=n=50,path=\EFI\HackBGRT\probable.bmp
# - image= x=0 y=.0 path=topimage.bmp
# - image= n=1 o=90 path=sideways.bmp
# - image= n=50 y=999999 path=probable.bmp
# The above examples together would produce
# - 1/54 chance for the default OS logo
# - 1/54 chance for black screen
# - 1/54 chance for topimage.bmp, centered at the top of the screen
# - 1/54 chance for splash.bmp, automatically positioned
# - 50/54 chance for probable.bmp, automatically positioned
# - 1/54 chance for topimage.bmp, 200 px above the center of the screen
# - 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=path=\EFI\HackBGRT\splash.bmp
image= 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
Submodule
+1
Submodule gnu-efi added at 74bd9b60ba
+30
View File
@@ -0,0 +1,30 @@
Copyright 2012 Red Hat, Inc <mjg@redhat.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the
distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
Significant portions of this code are derived from Tianocore
(http://tianocore.sf.net) and are Copyright 2009-2012 Intel
Corporation.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+286
View 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.
+156 -30
View File
@@ -1,6 +1,10 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Linq;
using System.Runtime.InteropServices;
using System.ComponentModel;
using Microsoft.Win32;
/**
@@ -28,6 +32,12 @@ public class Efi {
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern UInt32 GetSystemFirmwareTable(UInt32 provider, UInt32 table, [MarshalAs(UnmanagedType.LPArray)] byte[] buffer, UInt32 len);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern void SetLastError(UInt32 errorCode);
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct TokPriv1Luid {
public int Count;
@@ -39,17 +49,54 @@ public class Efi {
* Information about an EFI variable.
*/
public class Variable {
public string name, guid;
public UInt32 attributes;
public byte[] data;
public string Name, Guid;
public UInt32 Attributes;
public byte[] Data;
/**
* Convert to string.
*
* @return String representation of this object.
*/
public override string ToString() {
if (Data == null) {
return $"{Name} Guid={Guid} Attributes={Attributes} Data=null";
}
var hex = BitConverter.ToString(Data).Replace("-", " ");
var text = new string(Data.Select(c => 0x20 <= c && c <= 0x7f ? (char) c : ' ').ToArray());
return $"{Name} Guid={Guid} Attributes={Attributes} Text='{text}' Bytes='{hex}'";
}
}
/**
* GUID of the global EFI variables.
*/
public const string EFI_GLOBAL_GUID = "{8be4df61-93ca-11d2-aa0d-00e098032b8c}";
/**
* GUID for HackBGRT EFI variables.
*/
public const string EFI_HACKBGRT_GUID = "{03c64761-075f-4dba-abfb-2ed89e18b236}";
/**
* Directory containing EFI variables in Linux.
*/
public const string LinuxEfiDir = "/sys/firmware/efi/efivars";
/**
* Enable the privilege to access EFI variables.
*/
public static void EnablePrivilege() {
if (Directory.Exists(LinuxEfiDir)) {
var linuxEfiFile = $"{LinuxEfiDir}/BootOrder-8be4df61-93ca-11d2-aa0d-00e098032b8c";
if (File.Exists(linuxEfiFile)) {
using (FileStream fs = File.OpenWrite(linuxEfiFile)) {
// OpenWrite throws an exception on error.
}
}
return;
}
const int SE_PRIVILEGE_ENABLED = 0x00000002;
const int TOKEN_QUERY = 0x00000008;
const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
@@ -81,53 +128,78 @@ public class Efi {
* @param guid GUID of the EFI variable.
* @return Information about the EFI variable.
*/
private static Variable GetVariable(string name, string guid = EFI_GLOBAL_GUID) {
public static Variable GetVariable(string name, string guid = EFI_GLOBAL_GUID) {
Variable result = new Variable();
result.name = name;
result.guid = guid;
result.data = null;
result.attributes = 0;
result.Name = name;
result.Guid = guid;
result.Data = null;
result.Attributes = 0;
if (Directory.Exists(LinuxEfiDir)) {
var linuxEfiFile = $"{LinuxEfiDir}/{name}-{guid.Substring(1, guid.Length - 2)}";
if (File.Exists(linuxEfiFile)) {
var d = File.ReadAllBytes(linuxEfiFile);
result.Attributes = (UInt32)(d[0] + 0x100 * d[1] + 0x10000 * d[2] + 0x1000000 * d[3]);
result.Data = d.Skip(4).ToArray();
}
return result;
}
for (UInt32 i = 4096; i <= 1024*1024; i *= 2) {
byte[] buf = new byte[i];
UInt32 len = GetFirmwareEnvironmentVariableEx(name, guid, buf, (UInt32) buf.Length, out result.attributes);
SetLastError(0);
UInt32 len = GetFirmwareEnvironmentVariableEx(name, guid, buf, (UInt32) buf.Length, out result.Attributes);
if (len == buf.Length) {
continue;
}
if (len > 0 || Marshal.GetLastWin32Error() == 0) {
result.data = new byte[len];
Array.Copy(buf, 0, result.data, 0, len);
var error = Marshal.GetLastWin32Error();
const int ERROR_ENVVAR_NOT_FOUND = 0xCB;
if (error == ERROR_ENVVAR_NOT_FOUND) {
return result;
}
switch (len != 0 ? 0 : Marshal.GetLastWin32Error()) {
case 203:
// Not found.
return result;
case 87:
throw new Exception("GetVariable: Invalid parameter");
case 1314:
throw new Exception("GetVariable: Privilege not held");
default:
throw new Exception("GetVariable: error " + Marshal.GetLastWin32Error());
if (len > 0 || error == 0) {
result.Data = new byte[len];
Array.Copy(buf, 0, result.Data, 0, len);
return result;
}
const int ERROR_INVALID_FUNCTION = 0x1;
var msg = $"GetVariable: error 0x{error:X08}: {new Win32Exception(error).Message}";
throw error == ERROR_INVALID_FUNCTION ? new NotImplementedException(msg) : new Exception(msg);
}
throw new Exception("GetFirmwareEnvironmentVariable: too big data");
throw new Exception("GetVariable: result exceeds 1MB");
}
/**
* Set an EFI variable.
*
* @param v Information of the variable.
* @param dryRun Don't actually set the variable.
*/
private static void SetVariable(Variable v) {
UInt32 r = SetFirmwareEnvironmentVariableEx(v.name, v.guid, v.data, (UInt32) v.data.Length, v.attributes);
public static void SetVariable(Variable v, bool dryRun = false) {
Setup.WriteLine($"Writing EFI variable {v.Name} (see log for details)");
Setup.Log($"Writing EFI variable: {v}");
if (dryRun) {
return;
}
if (Directory.Exists(LinuxEfiDir)) {
var linuxEfiFile = $"{LinuxEfiDir}/{v.Name}-{v.Guid.Substring(1, v.Guid.Length - 2)}";
var a = v.Attributes;
var b = new byte[] { (byte) a, (byte) (a >> 8), (byte) (a >> 16), (byte) (a >> 24) };
// FIXME: Just writing won't work: File.WriteAllBytes(linuxEfiFile, b.Concat(v.Data).ToArray());
Setup.WriteLine("FIXME: Can't yet write EFI variables in Linux.");
return;
}
UInt32 r = SetFirmwareEnvironmentVariableEx(v.Name, v.Guid, v.Data, (UInt32) v.Data.Length, v.Attributes);
if (r == 0) {
switch (Marshal.GetLastWin32Error()) {
case 87:
throw new Exception("GetVariable: Invalid parameter");
throw new Exception("SetVariable: Invalid parameter");
case 1314:
throw new Exception("GetVariable: Privilege not held");
throw new Exception("SetVariable: Privilege not held");
default:
throw new Exception("GetVariable: error " + Marshal.GetLastWin32Error());
throw new Exception("SetVariable: error " + Marshal.GetLastWin32Error());
}
}
}
@@ -159,7 +231,7 @@ public class Efi {
public static bool CanBootToFW() {
try {
Variable tmp = GetVariable("OsIndicationsSupported");
return tmp.data != null && (tmp.data[0] & 1) != 0;
return tmp.Data != null && (tmp.Data[0] & 1) != 0;
} catch {
return false;
}
@@ -170,7 +242,61 @@ public class Efi {
*/
public static void SetBootToFW() {
Variable tmp = GetVariable("OsIndications");
tmp.data[0] |= 1;
if (tmp.Data == null) {
tmp.Data = new byte[8];
tmp.Attributes = 7;
}
tmp.Data[0] |= 1;
SetVariable(tmp);
}
/**
* Convert bytes into UInt16 values.
*
* @param bytes The byte array.
* @return An enumeration of UInt16 values.
*/
public static IEnumerable<UInt16> BytesToUInt16s(byte[] bytes) {
// TODO: return bytes.Chunk(2).Select(b => (UInt16) (b[0] + 0x100 * b[1])).ToArray();
return Enumerable.Range(0, bytes.Length / 2).Select(i => (UInt16) (bytes[2 * i] + 0x100 * bytes[2 * i + 1]));
}
/**
* Retrieve HackBGRT log collected during boot.
*/
public static string GetHackBGRTLog() {
try {
var log = GetVariable("HackBGRTLog", EFI_HACKBGRT_GUID);
if (log.Data == null) {
return "Boot log is empty.";
}
return new string(BytesToUInt16s(log.Data).Select(i => (char)i).ToArray());
} catch (NotImplementedException e) {
throw e;
} catch (Exception e) {
return $"Boot log not found: {e.ToString()}";
}
}
/**
* Log the BGRT table (for debugging).
*/
public static void LogBGRT() {
try {
const UInt32 acpiBE = 0x41435049, bgrtLE = 0x54524742;
UInt32 size = GetSystemFirmwareTable(acpiBE, bgrtLE, null, 0);
byte[] buf = new byte[size];
var ret = GetSystemFirmwareTable(acpiBE, bgrtLE, buf, size);
if (ret == size) {
var hex = BitConverter.ToString(buf).Replace("-", " ");
Setup.Log($"LogBGRT: {size} bytes: {hex}");
} else if (ret == 0) {
Setup.Log($"LogBGRT: Win32Error {Marshal.GetLastWin32Error()}");
} else {
Setup.Log($"LogBGRT: Size problems: spec {0x38}, buf {size}, ret {ret}");
}
} catch (Exception e) {
Setup.Log($"LogBGRT failed: {e}");
}
}
}
+328
View File
@@ -0,0 +1,328 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
/**
* A class for handling EFI boot entries. Lazy access with cache.
* Notice: Data is not updated after the first read.
*/
public class EfiBootEntries {
/**
* Information about an EFI boot entry.
*/
public class BootEntryData {
public UInt32 Attributes;
public string Label;
public class DevicePathNode {
public byte Type, SubType;
public byte[] Data;
public DevicePathNode(byte[] data) {
Type = data[0];
SubType = data[1];
Data = data.Skip(4).ToArray();
}
public byte[] ToBytes() {
var len = Data.Length + 4;
return new byte[] { Type, SubType, (byte)(len & 0xff), (byte)(len >> 8) }.Concat(Data).ToArray();
}
}
public List<DevicePathNode> DevicePathNodes;
public byte[] Arguments;
public BootEntryData(byte[] data) {
Attributes = BitConverter.ToUInt32(data, 0);
var pathNodesLength = BitConverter.ToUInt16(data, 4);
Label = new string(Efi.BytesToUInt16s(data).Skip(3).TakeWhile(i => i != 0).Select(i => (char)i).ToArray());
var pos = 6 + 2 * (Label.Length + 1);
var pathNodesEnd = pos + pathNodesLength;
DevicePathNodes = new List<DevicePathNode>();
Arguments = new byte[0];
while (pos + 4 <= pathNodesEnd) {
var len = BitConverter.ToUInt16(data, pos + 2);
if (len < 4 || pos + len > pathNodesEnd) {
return; // throw new Exception("Bad entry.");
}
var node = new DevicePathNode(data.Skip(pos).Take(len).ToArray());
DevicePathNodes.Add(node);
if (node.Type == 0x7f && node.SubType == 0xff) {
// End of entire device path.
// Apparently some firmwares produce paths with unused nodes at the end.
break;
}
pos += len;
}
Arguments = data.Skip(pathNodesEnd).ToArray();
}
public byte[] ToBytes() {
return new byte[0]
.Concat(BitConverter.GetBytes((UInt32) Attributes))
.Concat(BitConverter.GetBytes((UInt16) DevicePathNodes.Sum(n => n.Data.Length + 4)))
.Concat(Encoding.Unicode.GetBytes(Label + "\0"))
.Concat(DevicePathNodes.SelectMany(n => n.ToBytes()))
.Concat(Arguments)
.ToArray();
}
public DevicePathNode FileNameNode {
get {
var d = DevicePathNodes;
return d.Count > 1 && d[d.Count - 1].Type == 0x7F && d[d.Count - 2].Type == 0x04 ? d[d.Count - 2] : null;
}
}
public bool HasFileName {
get {
return FileNameNode != null;
}
}
public string FileName {
get {
if (!HasFileName) {
return "";
}
return new string(Encoding.Unicode.GetChars(FileNameNode.Data).TakeWhile(c => c != '\0').ToArray());
}
set {
if (!HasFileName) {
throw new Exception("Logic error: Setting FileName on a bad boot entry.");
}
FileNameNode.Data = Encoding.Unicode.GetBytes(value + "\0");
}
}
}
/**
* Status of the own boot entry.
*/
public enum OwnEntryStatus {
NotFound,
Disabled,
EnabledAfterWindows,
Enabled
}
/**
* Path to the Windows boot loader.
*/
public const string WindowsLoaderPath = "\\EFI\\Microsoft\\Boot\\bootmgfw.efi";
/**
* Path to the HackBGRT loader.
*/
public const string OwnLoaderPath = "\\EFI\\HackBGRT\\loader.efi";
private readonly Dictionary<UInt16, (Efi.Variable, BootEntryData)> cache;
private readonly Efi.Variable BootOrder;
private readonly Efi.Variable BootCurrent;
private readonly List<UInt16> BootOrderInts;
private readonly List<UInt16> BootCurrentInts;
/**
* Constructor. Reads BootOrder and BootCurrent.
*/
public EfiBootEntries() {
cache = new Dictionary<UInt16, (Efi.Variable, BootEntryData)>();
BootOrder = Efi.GetVariable("BootOrder");
BootCurrent = Efi.GetVariable("BootCurrent");
if (BootOrder.Data == null) {
throw new Exception("Could not read BootOrder.");
}
BootCurrentInts = new List<UInt16>(Efi.BytesToUInt16s(BootCurrent.Data ?? new byte[0]));
BootOrderInts = new List<UInt16>(Efi.BytesToUInt16s(BootOrder.Data));
}
/**
* Get the boot entry with the given number.
*
* @param num Number of the boot entry.
* @return The boot entry.
*/
public (Efi.Variable, BootEntryData) GetEntry(UInt16 num) {
if (!cache.ContainsKey(num)) {
var v = Efi.GetVariable(String.Format("Boot{0:X04}", num));
cache[num] = (v, v.Data == null ? null : new BootEntryData(v.Data));
}
return cache[num];
}
/**
* Find entry by file name.
*
* @param fileName File name of the boot entry.
* @return The boot entry.
*/
public (UInt16, Efi.Variable, BootEntryData) FindEntry(string fileName) {
var rest = Enumerable.Range(0, 0xff).Select(i => (UInt16) i);
var entryAccessOrder = BootCurrentInts.Concat(BootOrderInts).Concat(rest);
foreach (var num in entryAccessOrder) {
var (v, e) = GetEntry(num);
if (fileName == null ? e == null : (e != null && e.FileName.Equals(fileName, StringComparison.OrdinalIgnoreCase))) {
return (num, v, e);
}
}
return (0xffff, null, null);
}
/**
* Get the Windows boot entry.
*/
public (UInt16, Efi.Variable, BootEntryData) WindowsEntry {
get { return FindEntry(WindowsLoaderPath); }
}
/**
* Get the HackBGRT boot entry.
*/
public (UInt16, Efi.Variable, BootEntryData) OwnEntry {
get { return FindEntry(OwnLoaderPath); }
}
/**
* Get a free boot entry.
*/
public (UInt16, Efi.Variable, BootEntryData) FreeEntry {
get { return FindEntry(null); }
}
/**
* Check if the own entry is enabled.
*/
public OwnEntryStatus GetOwnEntryStatus() {
var (ownNum, ownVar, _) = OwnEntry;
if (ownVar == null) {
return OwnEntryStatus.NotFound;
}
var (msNum, _, _) = WindowsEntry;
var msPos = BootOrderInts.IndexOf(msNum);
var ownPos = BootOrderInts.IndexOf(ownNum);
if (ownPos < 0) {
return OwnEntryStatus.Disabled;
}
if (ownPos < msPos || msPos < 0) {
return OwnEntryStatus.Enabled;
}
return OwnEntryStatus.EnabledAfterWindows;
}
/**
* Disable the said boot entry from BootOrder.
*
* @param dryRun Don't actually write to NVRAM.
* @return True, if the entry was found in BootOrder.
*/
public bool DisableOwnEntry(bool dryRun = false) {
var (ownNum, ownVar, _) = OwnEntry;
if (ownVar == null) {
Setup.Log("Own entry not found.");
return false;
}
Setup.Log($"Old boot order: {BootOrder}");
if (!BootOrderInts.Contains(ownNum)) {
Setup.Log("Own entry not in BootOrder.");
} else {
Setup.Log($"Disabling own entry: {ownNum:X04}");
BootOrderInts.Remove(ownNum);
BootOrder.Data = BootOrderInts.SelectMany(num => new byte[] { (byte)(num & 0xff), (byte)(num >> 8) }).ToArray();
Efi.SetVariable(BootOrder, dryRun);
return true;
}
return false;
}
/**
* Create the boot entry.
*
* @param alwaysCopyFromMS If true, do not preserve any existing data.
* @param dryRun Don't actually write to NVRAM.
*/
public void MakeOwnEntry(bool alwaysCopyFromMS, bool dryRun = false) {
var (msNum, msVar, msEntry) = WindowsEntry;
var (ownNum, ownVar, ownEntry) = OwnEntry;
if (ownVar == null) {
(ownNum, ownVar, ownEntry) = FreeEntry;
if (ownVar == null) {
throw new Exception("MakeOwnEntry: No free entry.");
}
Setup.Log($"Creating own entry {ownNum:X4}.");
} else {
Setup.Log($"Updating own entry {ownNum:X4}.");
}
Setup.Log($"Read EFI variable: {msVar}");
Setup.Log($"Read EFI variable: {ownVar}");
// Make a new boot entry using the MS entry as a starting point.
if (!alwaysCopyFromMS && ownEntry != null) {
// Shim expects the arguments to be a filename or nothing.
// But BCDEdit expects some Microsoft-specific data.
// Modify the entry so that BCDEdit still recognises it
// but the data becomes a valid UCS-2 / UTF-16LE file name.
var str = new string(ownEntry.Arguments.Take(12).Select(c => (char) c).ToArray());
if (str == "WINDOWS\0\x01\0\0\0") {
ownEntry.Arguments[8] = (byte) 'X';
} else if (str != "WINDOWS\0\x58\0\0\0") {
// Not recognized. Clear the arguments.
ownEntry.Arguments = new byte[0];
}
} else {
if (msEntry == null) {
throw new Exception("MakeOwnEntry: Windows Boot Manager not found.");
}
ownEntry = msEntry;
ownEntry.Arguments = new byte[0];
ownEntry.Label = "HackBGRT";
ownEntry.FileName = OwnLoaderPath;
}
ownEntry.Attributes = 1; // LOAD_OPTION_ACTIVE
ownVar.Attributes = 7; // EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS
ownVar.Data = ownEntry.ToBytes();
Efi.SetVariable(ownVar, dryRun);
}
/**
* Enable the own boot entry.
*
* @param dryRun Don't actually write to NVRAM.
*/
public void EnableOwnEntry(bool dryRun = false) {
var (ownNum, ownVar, _) = OwnEntry;
if (ownVar == null) {
Setup.Log("Own entry not found.");
return;
}
var (msNum, _, _) = WindowsEntry;
var msPos = BootOrderInts.IndexOf(msNum);
var ownPos = BootOrderInts.IndexOf(ownNum);
var mustAdd = ownPos == -1;
var mustMove = 0 <= msPos && msPos <= ownPos;
Setup.Log($"Old boot order: {BootOrder}");
if (mustAdd || mustMove) {
Setup.Log($"Enabling own entry: {ownNum:X04}");
if (mustMove) {
BootOrderInts.RemoveAt(ownPos);
}
BootOrderInts.Insert(msPos < 0 ? 0 : msPos, ownNum);
BootOrder.Data = BootOrderInts.SelectMany(num => new byte[] { (byte)(num & 0xff), (byte)(num >> 8) }).ToArray();
Efi.SetVariable(BootOrder, dryRun);
}
}
/**
* Log the boot entries.
*/
public void LogEntries() {
Setup.Log($"LogEntries: {BootOrder}");
Setup.Log($"LogEntries: {BootCurrent}");
// Windows can't enumerate EFI variables, and trying them all is too slow.
// BootOrder + BootCurrent + the first 0xff entries should be enough.
var seen = new HashSet<UInt16>();
foreach (var num in BootOrderInts.Concat(BootCurrentInts).Concat(Enumerable.Range(0, 0xff).Select(i => (UInt16) i))) {
if (seen.Contains(num)) {
continue;
}
seen.Add(num);
var (v, e) = GetEntry(num);
if (e != null) {
Setup.Log($"LogEntries: {v}");
}
}
}
}
+81 -31
View File
@@ -6,16 +6,32 @@ using System.Text.RegularExpressions;
* EFI System Partition mounter.
*/
public sealed class Esp {
/** The singleton instance of this class, if Path is mounted. */
/** The singleton instance of this class, if ESP is mounted. */
private static Esp MountInstance;
/** EFI System Partition location, internal variable. */
private static string RealPath;
/** EFI System Partition location. */
public static string Location { get; private set; }
/** EFI System Partition location, read-only. */
public static string Path {
/** MS boot loader path on ESP. */
public static string MsLoaderPath {
get {
return RealPath;
return Path.Combine(new string[] { Location, "EFI", "Microsoft", "Boot", "bootmgfw.efi"});
}
}
/** Output of mountvol. */
private static string MountvolOutput;
/** Possible drive letter for the ESP. */
public static string DriveLetters = "ABZYXWVUTSRQPONMLKJIHGFEDC";
/** Does MountvolOutput contain /S? */
public static bool MountvolESPNotSupported {
get {
if (MountvolOutput == null) {
MountvolOutput = Setup.Execute("mountvol", "", false);
}
return MountvolOutput.Contains(" /S") == false;
}
}
@@ -42,41 +58,70 @@ public sealed class Esp {
* @return true if the path was given and seems valid, false otherwise.
*/
public static bool TryPath(string tryPath, bool requireMsLoader = true) {
if (MountInstance != null && Path != null) {
if (MountInstance != null && Location != null) {
Unmount();
}
RealPath = tryPath;
if (Path != null && Path != "") {
if (File.Exists(Path + "\\EFI\\Microsoft\\Boot\\bootmgfw.efi")) {
Location = tryPath;
if (Location != null && Location != "") {
if (File.Exists(MsLoaderPath)) {
Setup.Log($"Esp.TryPath: {Location} has MS boot loader");
return true;
}
if (!requireMsLoader && Directory.Exists(Path + "\\EFI")) {
return true;
if (Directory.Exists(Path.Combine(Location, "EFI"))) {
Setup.Log($"Esp.TryPath: {Location} has EFI directory but no loader");
if (!requireMsLoader) {
return true;
}
}
}
RealPath = null;
Location = null;
return false;
}
/**
* Find the EFI System Partition, if it's already mounted.
*
* @return true if the drive vas found.
* @return true if the drive was found.
*/
public static bool Find() {
if (MountInstance != null) {
return true;
}
public static bool FindOrMount() {
return Location != null || FindWithMountvol() || Mount() || TryAllDrives();
}
/**
* Find the EFI System Partition, if it's already mounted.
*
* @return true if the drive was found.
*/
private static bool FindWithMountvol() {
Setup.Log("Esp: Detect from mountvol output.");
try {
// Match "The EFI System Partition is mounted at E:\" with some language support.
var re = new Regex(" EFI[^\n]*(?:\n[ \t]*)?([A-Z]:)\\\\");
if (TryPath(re.Match(Setup.Execute("mountvol", "", false)).Groups[1].Captures[0].Value)) {
MountvolOutput = Setup.Execute("mountvol", "", false);
var re = new Regex(" EFI[^\n]*(?:\n[ \t]*)?([A-Z]:\\\\)");
var m = re.Match(MountvolOutput);
if (m.Success && TryPath(m.Groups[1].Captures[0].Value)) {
return true;
}
} catch {
Setup.Log("Esp: no match");
} catch (Exception e) {
Setup.Log($"Esp: {e.ToString()}");
}
for (char c = 'A'; c <= 'Z'; ++c) {
if (TryPath(c + ":")) {
return false;
}
/**
* Try all drive letters to find the EFI System Partition.
*
* @return true if the drive was found.
*/
private static bool TryAllDrives() {
Setup.Log("Esp: Detect by trying all drive letters.");
foreach (char c in DriveLetters) {
if (TryPath(c + ":\\")) {
Setup.Log($"Esp: found {c}");
if (c == 'C') {
Setup.Log("Esp: WARNING: It's unlikely that C: is really the ESP.");
}
return true;
}
}
@@ -88,17 +133,22 @@ public sealed class Esp {
*
* @return true if the drive was mounted, false otherwise.
*/
public static bool Mount() {
if (MountInstance != null) {
return true;
private static bool Mount() {
if (MountvolESPNotSupported) {
return false;
}
for (char c = 'A'; c <= 'Z'; ++c) {
Setup.Log("Esp: Try to mount with mountvol.");
foreach (char c in DriveLetters) {
if (Directory.Exists(c + ":\\")) {
continue;
}
Setup.Log($"Esp.Mount: {c}");
if (Setup.Execute("mountvol", c + ": /S", true) != null) {
MountInstance = new Esp();
if (TryPath(c + ":", false)) {
if (TryPath(c + ":\\", false)) {
return true;
} else {
throw new Exception("Mounted ESP at " + c + ": but it seems to be invalid!");
throw new Exception("Mounted ESP at " + c + ":\\ but it seems to be invalid!");
}
}
}
@@ -110,8 +160,8 @@ public sealed class Esp {
*/
private static void Unmount() {
if (MountInstance != null) {
Setup.Execute("mountvol", Path + " /D", true);
RealPath = null;
Setup.Execute("mountvol", Location + " /D", true);
Location = null;
MountInstance = null;
}
}
+1044 -216
View File
File diff suppressed because it is too large Load Diff
-104
View File
@@ -1,104 +0,0 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Security.Principal;
using System.Linq;
/**
* Helper functions.
*/
public class SetupHelper {
/**
* Start another process.
*
* @param app Path to the application.
* @param args The argument string.
* @return The started process.
*/
public static Process StartProcess(string app, string args) {
try {
var info = new ProcessStartInfo(app, args);
info.UseShellExecute = false;
return Process.Start(info);
} catch {
return null;
}
}
/**
* 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) {
try {
var info = new ProcessStartInfo(app, args);
info.UseShellExecute = false;
info.RedirectStandardOutput = true;
var p = Process.Start(info);
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
return (nullOnFail && p.ExitCode != 0) ? null : output;
} catch {
return null;
}
}
/**
* Copy a file, overwrite by default.
*
* @param src The source path.
* @param dest The destination path.
* @return True, if the file was successfully copied.
*/
public static bool Copy(string src, string dest) {
try {
File.Copy(src, dest, true);
return true;
} catch {
return false;
}
}
/**
* The Main program.
*
* @param args The arguments.
*/
public static void Main(string[] args) {
#if GIT_DESCRIBE
Console.WriteLine("HackBGRT installer version: {0}", GIT_DESCRIBE.data);
#else
Console.WriteLine("HackBGRT installer version: unknown; not an official release?");
#endif
var self = Assembly.GetExecutingAssembly().Location;
try {
// Relaunch as admin, if needed.
var id = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(id);
var admin = WindowsBuiltInRole.Administrator;
if (!principal.IsInRole(admin) && !args.Contains("no-elevate")) {
ProcessStartInfo startInfo = new ProcessStartInfo(self);
startInfo.Arguments = "no-elevate";
startInfo.Verb = "runas";
startInfo.UseShellExecute = true;
Process p = Process.Start(startInfo);
p.WaitForExit();
Environment.ExitCode = p.ExitCode;
return;
}
Efi.EnablePrivilege();
} catch {
Console.WriteLine("This installer needs to be run as administrator!");
Console.WriteLine("Press any key to quit.");
Console.ReadKey();
Environment.ExitCode = 1;
return;
}
Setup.RunSetup(Path.GetDirectoryName(self));
}
}
+82 -42
View File
@@ -1,14 +1,12 @@
#include "config.h"
#include "util.h"
#include <efilib.h>
BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, const CHAR16* path) {
BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE base_dir, const CHAR16* path) {
void* data = 0;
UINTN data_bytes = 0;
data = LoadFileWithPadding(root_dir, path, &data_bytes, 4);
data = LoadFileWithPadding(base_dir, path, &data_bytes, 4);
if (!data) {
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,58 +61,96 @@ BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir,
str[j] = 0;
++j;
}
ReadConfigLine(config, root_dir, &str[i]);
ReadConfigLine(config, base_dir, &str[i]);
i = j;
}
// NOTICE: string is not freed, because paths are not copied.
return TRUE;
}
static void SetBMPWithRandom(struct HackBGRT_config* config, int weight, enum HackBGRT_action action, int x, int y, const CHAR16* path) {
static void SetBMPWithRandom(struct HackBGRT_config* config, const struct HackBGRT_image_config* image, int weight) {
config->image_weight_sum += weight;
UINT32 random = Random();
UINT32 limit = 0xfffffffful / config->image_weight_sum * weight;
if (config->debug) {
Print(L"HackBGRT: weight %d, action %d, x %d, y %d, path %s, random = %08x, limit = %08x\n", weight, action, x, y, path, random, limit);
}
if (!config->image_weight_sum || random <= limit) {
config->action = action;
config->image_path = path;
config->image_x = x;
config->image_y = y;
UINT32 random = (((UINT64) Random() & 0xffffffff) * config->image_weight_sum) >> 32;
UINT32 limit = ((UINT64) 0xffffffff * weight) >> 32;
Log(
config->debug,
L"image n=%d, action=%d, x=%d (mode %d), y=%d (mode %d), o=%d, path=%s, rand=%x/%x, chosen=%d\n",
weight, image->action, image->x, image->x_mode, image->y, image->y_mode, image->orientation, image->path, random, limit, (random <= limit)
);
if (random <= limit) {
config->image = *image;
}
}
static int ParseCoordinate(const CHAR16* str, enum HackBGRT_action action) {
if (str && L'0' <= str[0] && str[0] <= L'9') {
return Atoi(str);
static void ParseCoordinate(const CHAR16* str, int* out_int, enum HackBGRT_coordinate_mode* out_mode) {
if (!str) {
return;
}
if ((str && StrnCmp(str, L"native", 6) == 0) || action == HackBGRT_KEEP) {
return HackBGRT_coord_native;
if (StrnCmp(str, L"keep", 4) == 0) {
*out_int = 0;
*out_mode = HackBGRT_COORDINATE_MODE_KEEP;
return;
}
if (str[0] == L'.' && L'0' <= str[1] && str[1] <= L'9') {
int result = 0, i = 1, length = 0;
for (length = 0; length < HackBGRT_FRACTION_DIGITS; ++length) {
result = 10 * result;
if (L'0' <= str[i] && str[i] <= L'9') {
result += str[i] - L'0';
i += 1;
}
}
*out_mode = HackBGRT_COORDINATE_MODE_FRACTION;
*out_int = result;
return;
}
int neg = str[0] == L'-' ? 1 : 0;
if (L'0' <= str[neg] && str[neg] <= L'9') {
*out_int = Atoi(str + neg) * (neg ? -1 : 1);
*out_mode = HackBGRT_COORDINATE_MODE_CENTERED;
return;
}
return HackBGRT_coord_auto;
}
static void ReadConfigImage(struct HackBGRT_config* config, const CHAR16* line) {
const CHAR16* n = StrStrAfter(line, L"n=");
const CHAR16* x = StrStrAfter(line, L"x=");
const CHAR16* y = StrStrAfter(line, L"y=");
const CHAR16* f = StrStrAfter(line, L"path=");
enum HackBGRT_action action = HackBGRT_KEEP;
if (f) {
action = HackBGRT_REPLACE;
struct HackBGRT_image_config image = {
.action = HackBGRT_ACTION_KEEP,
.orientation = HackBGRT_ORIENTATION_KEEP,
};
const CHAR16* tmp;
image.path = StrStrAfter(line, L"path=");
if (image.path) {
image.action = HackBGRT_ACTION_REPLACE;
// Default: x centered, y 38.2 % (= 1 - 1 / golden_ratio)
image.x_mode = image.y_mode = HackBGRT_COORDINATE_MODE_FRACTION;
image.x = HackBGRT_FRACTION_HALF;
image.y = HackBGRT_FRACTION_381966011;
} else if (StrStr(line, L"remove")) {
action = HackBGRT_REMOVE;
image.action = HackBGRT_ACTION_REMOVE;
} else if (StrStr(line, L"black")) {
action = HackBGRT_REPLACE;
image.action = HackBGRT_ACTION_REPLACE;
image.path = 0;
} else if (StrStr(line, L"keep")) {
action = HackBGRT_KEEP;
image.action = HackBGRT_ACTION_KEEP;
image.x_mode = image.y_mode = HackBGRT_COORDINATE_MODE_KEEP;
} else {
Print(L"HackBGRT: Invalid image line: %s\n", line);
Log(1, L"Invalid image line: %s\n", line);
return;
}
int weight = n && (!f || n < f) ? Atoi(n) : 1;
SetBMPWithRandom(config, weight, action, ParseCoordinate(x, action), ParseCoordinate(y, action), f);
ParseCoordinate(StrStrAfter(line, L"x="), &image.x, &image.x_mode);
ParseCoordinate(StrStrAfter(line, L"y="), &image.y, &image.y_mode);
if (StrStrAfter(line, L"o=keep")) {
image.orientation = HackBGRT_ORIENTATION_KEEP;
} else if ((tmp = StrStrAfter(line, L"o="))) {
// convert orientation in degrees to number 0-3 (* 90 degrees)
int i = tmp[0] == L'-' ? -(int)Atoi(tmp+1) : (int)Atoi(tmp);
image.orientation = (i / 90) & 3;
}
int weight = 1;
if ((tmp = StrStrAfter(line, L"n="))) {
weight = Atoi(tmp);
}
SetBMPWithRandom(config, &image, weight);
}
static void ReadConfigResolution(struct HackBGRT_config* config, const CHAR16* line) {
@@ -124,11 +160,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;
@@ -138,6 +174,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;
@@ -147,12 +187,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);
}
+44 -14
View File
@@ -1,35 +1,65 @@
#pragma once
#include <efi.h>
#include "efi.h"
/**
* Possible actions to perform on the BGRT.
*/
enum HackBGRT_action {
HackBGRT_KEEP = 0, HackBGRT_REPLACE, HackBGRT_REMOVE
HackBGRT_ACTION_KEEP = 0,
HackBGRT_ACTION_REPLACE,
HackBGRT_ACTION_REMOVE
};
/**
* Special values for the image coordinates.
* @see struct HackBGRT_config
*/
enum HackBGRT_coordinate {
HackBGRT_coord_auto = 0x10000001,
HackBGRT_coord_native = 0x10000002
enum HackBGRT_coordinate_mode {
HackBGRT_COORDINATE_MODE_KEEP = 0,
HackBGRT_COORDINATE_MODE_CENTERED,
HackBGRT_COORDINATE_MODE_FRACTION,
};
/**
* Constants for the fractional coordinates.
*/
enum HackBGRT_fraction {
HackBGRT_FRACTION_DIGITS = 4,
HackBGRT_FRACTION_ONE = 10000,
HackBGRT_FRACTION_HALF = 5000,
HackBGRT_FRACTION_381966011 = 3820,
};
/**
* Possible values for the orientation.
*/
enum HackBGRT_orientation {
HackBGRT_ORIENTATION_KEEP = -1,
};
/**
* The configuration for one image.
*/
struct HackBGRT_image_config {
enum HackBGRT_action action;
const CHAR16* path;
enum HackBGRT_coordinate_mode x_mode, y_mode;
int x, y;
int orientation;
};
/**
* The configuration.
*/
struct HackBGRT_config {
int debug;
enum HackBGRT_action action;
const CHAR16* image_path;
int image_x;
int image_y;
int debug, log;
struct HackBGRT_image_config image;
int image_weight_sum;
int resolution_x;
int resolution_y;
int old_resolution_x;
int old_resolution_y;
const CHAR16* boot_path;
};
@@ -37,17 +67,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);
+186
View File
@@ -0,0 +1,186 @@
#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;
}
CHAR16 *DevicePathToStr(EFI_DEVICE_PATH *DevPath) {
UINTN path_length = 0;
for (EFI_DEVICE_PATH *p0 = DevPath;; p0 = NextDevicePathNode(p0)) {
if (DevicePathType(p0) != MEDIA_DEVICE_PATH || DevicePathSubType(p0) != MEDIA_FILEPATH_DP) {
break;
}
path_length += DevicePathNodeLength(p0) + 1;
}
CHAR16* str;
UINTN size_str = (path_length + 1) * sizeof(*str);
if (!path_length || EFI_ERROR(BS->AllocatePool(EfiBootServicesData, size_str, (void**)&str))) {
return 0;
}
UINTN pos = 0;
for (EFI_DEVICE_PATH *p0 = DevPath; pos < path_length; p0 = NextDevicePathNode(p0)) {
FILEPATH_DEVICE_PATH *f = (FILEPATH_DEVICE_PATH *) p0;
BS->CopyMem(str + pos, f->PathName, StrLen(f->PathName) * sizeof(*str));
pos += DevicePathNodeLength(p0) + 1;
str[pos - 1] = L'\\';
}
str[pos - 1] = 0;
return str;
}
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
View File
@@ -0,0 +1,5 @@
#pragma once
#include <stdint.h>
#include <efi.h>
#include <efilib.h>
+317 -178
View File
@@ -1,25 +1,26 @@
#include <efi.h>
#include <efilib.h>
#include "efi.h"
#include "types.h"
#include "config.h"
#include "util.h"
/**
* The Print function signature.
* The version.
*/
typedef UINTN print_t(IN CHAR16 *fmt, ...);
#ifdef GIT_DESCRIBE_W
const CHAR16 version[] = GIT_DESCRIBE_W;
#else
const CHAR16 version[] = L"unknown; not an official release?";
#endif
/**
* The function for debug printing; either Print or NullPrint.
*/
print_t* Debug = NullPrint;
EFI_SYSTEM_TABLE *ST;
EFI_BOOT_SERVICES *BS;
EFI_RUNTIME_SERVICES *RT;
/**
* The configuration.
*/
static struct HackBGRT_config config = {
.action = HackBGRT_KEEP
.log = 1,
};
/**
@@ -28,8 +29,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,16 +43,22 @@ static EFI_GRAPHICS_OUTPUT_PROTOCOL* GOP(void) {
static void SetResolution(int w, int h) {
EFI_GRAPHICS_OUTPUT_PROTOCOL* gop = GOP();
if (!gop) {
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;
int best_w = gop->Mode->Info->HorizontalResolution;
int best_h = gop->Mode->Info->VerticalResolution;
w = (w <= 0 ? w < 0 ? best_w : 0x7fffffff : w);
h = (h <= 0 ? h < 0 ? best_h : 0x7fffffff : h);
int best_w = config.old_resolution_x = gop->Mode->Info->HorizontalResolution;
int best_h = config.old_resolution_y = gop->Mode->Info->VerticalResolution;
w = (w <= 0 ? w < 0 ? best_w : 999999 : w);
h = (h <= 0 ? h < 0 ? best_h : 999999 : h);
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;
@@ -62,12 +68,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);
@@ -85,30 +91,14 @@ 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) {
gop->SetMode(gop, best_i);
}
}
/**
* Select the correct coordinate (manual, automatic, native)
*
* @param value The configured coordinate value; has special values for automatic and native.
* @param automatic The automatically calculated alternative.
* @param native The original coordinate.
* @see enum HackBGRT_coordinate
*/
static int SelectCoordinate(int value, int automatic, int native) {
if (value == HackBGRT_coord_auto) {
return automatic;
}
if (value == HackBGRT_coord_native) {
return native;
}
return value;
}
/**
* Create a new XSDT with the given number of entries.
*
@@ -121,11 +111,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;
@@ -144,26 +134,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: revision = %d, OEM ID = %s\n", rsdp->revision, TmpStr(rsdp->oem_id, 6));
Log(config.debug, L"RSDP @%x: revision = %d, OEM ID = %s\n", (UINTN)rsdp, rsdp->revision, TmpStr(rsdp->oem_id, 6));
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: OEM ID = %s, entry count = %d\n", TmpStr(xsdt->oem_id, 6), entry_arr_length);
Log(config.debug, L"* XSDT @%x: OEM ID = %s, entry count = %d\n", (UINTN)xsdt, TmpStr(xsdt->oem_id, 6), entry_arr_length);
int bgrt_count = 0;
for (int j = 0; j < entry_arr_length; j++) {
@@ -171,16 +160,16 @@ static ACPI_BGRT* HandleAcpiTables(enum HackBGRT_action action, ACPI_BGRT* bgrt)
if (CompareMem(entry->signature, "BGRT", 4) != 0) {
continue;
}
Debug(L" - ACPI table: %s, revision = %d, OEM ID = %s\n", 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:
case HackBGRT_ACTION_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");
case HackBGRT_ACTION_REMOVE:
Log(config.debug, L" -> Deleting.\n");
for (int k = j+1; k < entry_arr_length; ++k) {
entry_arr[k-1] = entry_arr[k];
}
@@ -189,14 +178,14 @@ static ACPI_BGRT* HandleAcpiTables(enum HackBGRT_action action, ACPI_BGRT* bgrt)
xsdt->length -= sizeof(entry_arr[0]);
--j;
break;
case HackBGRT_REPLACE:
Debug(L" -> Replacing.\n");
case HackBGRT_ACTION_REPLACE:
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");
if (!bgrt_count && action == HackBGRT_ACTION_REPLACE && bgrt) {
Log(config.debug, L" - Adding missing BGRT.\n");
xsdt = CreateXsdt(xsdt, entry_arr_length + 1);
entry_arr = (UINT64*)&xsdt[1];
entry_arr[entry_arr_length++] = (UINTN) bgrt;
@@ -209,209 +198,359 @@ static ACPI_BGRT* HandleAcpiTables(enum HackBGRT_action action, ACPI_BGRT* bgrt)
}
/**
* Load a bitmap or generate a black one.
* Generate a BMP with the given size and color.
*
* @param root_dir The root directory for loading a BMP.
* @param path The BMP path within the root directory; NULL for a black BMP.
* @return The loaded BMP, or 0 if not available.
* @param w The width.
* @param h The height.
* @param r The red component.
* @param g The green component.
* @param b The blue component.
* @return The generated BMP, or 0 on failure.
*/
static BMP* LoadBMP(EFI_FILE_HANDLE root_dir, const CHAR16* path) {
static BMP* MakeBMP(int w, int h, UINT8 r, UINT8 g, UINT8 b) {
BMP* bmp = 0;
if (!path) {
BS->AllocatePool(EfiBootServicesData, 58, (void**) &bmp);
if (!bmp) {
Print(L"HackBGRT: Failed to allocate a blank BMP!\n");
BS->Stall(1000000);
return 0;
}
CopyMem(
bmp,
"\x42\x4d\x3a\x00\x00\x00\x00\x00\x00\x00\x36\x00\x00\x00\x28\x00"
"\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x01\x00\x18\x00\x00\x00"
"\x00\x00\x04\x00\x00\x00\x13\x0b\x00\x00\x13\x0b\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
58
);
return bmp;
}
Debug(L"HackBGRT: Loading %s.\n", path);
bmp = LoadFile(root_dir, path, 0);
BS->AllocatePool(EfiBootServicesData, 54 + w * h * 4, (void**) &bmp);
if (!bmp) {
Print(L"HackBGRT: Failed to load BMP (%s)!\n", path);
Log(1, L"Failed to allocate a blank BMP!\n");
BS->Stall(1000000);
return 0;
}
*bmp = (BMP) {
.magic_BM = { 'B', 'M' },
.file_size = 54 + w * h * 4,
.pixel_data_offset = 54,
.dib_header_size = 40,
.width = w,
.height = h,
.planes = 1,
.bpp = 32,
};
UINT8* data = (UINT8*) bmp + bmp->pixel_data_offset;
for (int y = 0; y < h; ++y) for (int x = 0; x < w; ++x) {
*data++ = b;
*data++ = g;
*data++ = r;
*data++ = 0;
}
return bmp;
}
/**
* Load a bitmap or generate a black one.
*
* @param base_dir The directory for loading a BMP.
* @param path The BMP path within the directory; NULL for a black BMP.
* @return The loaded BMP, or 0 if not available.
*/
static BMP* LoadBMP(EFI_FILE_HANDLE base_dir, const CHAR16* path) {
if (!path) {
return MakeBMP(1, 1, 0, 0, 0); // empty path = black image
}
Log(config.debug, L"Loading %s.\n", path);
UINTN size = 0;
BMP* bmp = LoadFile(base_dir, path, &size);
if (bmp) {
if (size >= bmp->file_size
&& CompareMem(bmp, "BM", 2) == 0
&& bmp->file_size > bmp->pixel_data_offset
&& bmp->width > 0
&& bmp->height > 0
&& (bmp->bpp == 32 || bmp->bpp == 24)
&& bmp->height * (-(-(bmp->width * (bmp->bpp / 8)) & ~3)) <= bmp->file_size - bmp->pixel_data_offset
&& bmp->compression == 0) {
return bmp;
}
BS->FreePool(bmp);
Log(1, L"Invalid BMP (%s)!\n", path);
} else {
Log(1, L"Failed to load BMP (%s)!\n", path);
}
BS->Stall(1000000);
return MakeBMP(16, 16, 255, 0, 0); // error = red image
}
/**
* Crop a BMP to the given size.
*
* @param bmp The BMP to crop.
* @param w The maximum width.
* @param h The maximum height.
*/
static void CropBMP(BMP* bmp, int w, int h) {
const int old_pitch = -(-(bmp->width * (bmp->bpp / 8)) & ~3);
bmp->image_size = 0;
bmp->width = min(bmp->width, w);
bmp->height = min(bmp->height, h);
const int new_pitch = -(-(bmp->width * (bmp->bpp / 8)) & ~3);
if (new_pitch < old_pitch) {
for (int i = 1; i < bmp->height; ++i) {
BS->CopyMem(
(UINT8*) bmp + bmp->pixel_data_offset + i * new_pitch,
(UINT8*) bmp + bmp->pixel_data_offset + i * old_pitch,
new_pitch
);
}
}
bmp->file_size = bmp->pixel_data_offset + bmp->height * new_pitch;
}
/**
* The main logic for BGRT modification.
*
* @param 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);
if (config.image.action == HackBGRT_ACTION_REMOVE) {
HandleAcpiTables(config.image.action, 0);
return;
}
// KEEP/REPLACE: first get the old BGRT entry.
ACPI_BGRT* bgrt = HandleAcpiTables(HackBGRT_KEEP, 0);
ACPI_BGRT* bgrt = HandleAcpiTables(HackBGRT_ACTION_KEEP, 0);
// Get the old BMP and position, if possible.
BMP* old_bmp = 0;
int old_x = 0, old_y = 0;
if (bgrt && VerifyAcpiSdtChecksum(bgrt)) {
old_bmp = (BMP*) (UINTN) bgrt->image_address;
old_x = bgrt->image_offset_x;
old_y = bgrt->image_offset_y;
}
// Get the old BMP and position (relative to screen center), if possible.
const int old_valid = bgrt && VerifyAcpiSdtChecksum(bgrt);
BMP* old_bmp = old_valid ? (BMP*) (UINTN) bgrt->image_address : 0;
const int old_orientation = old_valid ? ((bgrt->status >> 1) & 3) : 0;
const int old_swap = old_orientation & 1;
const int old_reso_x = old_swap ? config.old_resolution_y : config.old_resolution_x;
const int old_reso_y = old_swap ? config.old_resolution_x : config.old_resolution_y;
const int old_x = old_bmp ? bgrt->image_offset_x + (old_bmp->width - old_reso_x) / 2 : 0;
const int old_y = old_bmp ? bgrt->image_offset_y + (old_bmp->height - old_reso_y) / 2 : 0;
// Missing BGRT?
if (!bgrt) {
// Keep missing = do nothing.
if (config.action == HackBGRT_KEEP) {
if (config.image.action == HackBGRT_ACTION_KEEP) {
return;
}
// 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;
}
}
// Clear the BGRT.
const char data[0x38] =
"BGRT" "\x38\x00\x00\x00" "\x00" "\xd6" "Mtblx*" "HackBGRT"
"\x20\x17\x00\x00" "PTL " "\x02\x00\x00\x00"
"\x01\x00" "\x00" "\x00";
CopyMem(bgrt, data, sizeof(data));
*bgrt = (ACPI_BGRT) {
.header = {
.signature = "BGRT",
.length = sizeof(*bgrt),
.revision = 1,
.oem_id = "Mtblx*",
.oem_table_id = "HackBGRT",
.oem_revision = 1,
.asl_compiler_id = *(const UINT32*) "None",
.asl_compiler_revision = 1,
},
.version = 1,
};
// Get the image (either old or new).
BMP* new_bmp = old_bmp;
if (config.action == HackBGRT_REPLACE) {
new_bmp = LoadBMP(root_dir, config.image_path);
if (config.image.action == HackBGRT_ACTION_REPLACE) {
new_bmp = LoadBMP(base_dir, config.image.path);
}
// No image = no need for BGRT.
if (!new_bmp) {
HandleAcpiTables(HackBGRT_REMOVE, 0);
HandleAcpiTables(HackBGRT_ACTION_REMOVE, 0);
return;
}
// Crop the image to screen.
CropBMP(new_bmp, config.resolution_x, config.resolution_y);
// Set the image address and orientation.
bgrt->image_address = (UINTN) new_bmp;
const int new_orientation = config.image.orientation == HackBGRT_ORIENTATION_KEEP ? old_orientation : config.image.orientation;
bgrt->status = new_orientation << 1;
// Calculate the automatically centered position for the image.
int auto_x = 0, auto_y = 0;
if (GOP()) {
auto_x = max(0, ((int)GOP()->Mode->Info->HorizontalResolution - (int)new_bmp->width) / 2);
auto_y = max(0, ((int)GOP()->Mode->Info->VerticalResolution * 2/3 - (int)new_bmp->height) / 2);
} else if (old_bmp) {
auto_x = max(0, old_x + ((int)old_bmp->width - (int)new_bmp->width) / 2);
auto_y = max(0, old_y + ((int)old_bmp->height - (int)new_bmp->height) / 2);
}
// New center coordinates.
const int new_swap = new_orientation & 1;
const int new_reso_x = new_swap ? config.resolution_y : config.resolution_x;
const int new_reso_y = new_swap ? config.resolution_x : config.resolution_y;
// Set the position (manual, automatic, original).
bgrt->image_offset_x = SelectCoordinate(config.image_x, auto_x, old_x);
bgrt->image_offset_y = SelectCoordinate(config.image_y, auto_y, old_y);
Debug(L"HackBGRT: BMP at (%d, %d).\n", (int) bgrt->image_offset_x, (int) bgrt->image_offset_y);
const int new_x =
config.image.x_mode == HackBGRT_COORDINATE_MODE_KEEP ? old_x :
config.image.x_mode == HackBGRT_COORDINATE_MODE_CENTERED ? config.image.x :
(config.image.x - HackBGRT_FRACTION_HALF) * new_reso_x / HackBGRT_FRACTION_ONE;
const int new_y =
config.image.y_mode == HackBGRT_COORDINATE_MODE_KEEP ? old_y :
config.image.y_mode == HackBGRT_COORDINATE_MODE_CENTERED ? config.image.y :
(config.image.y - HackBGRT_FRACTION_HALF) * new_reso_y / HackBGRT_FRACTION_ONE;
// Calculate absolute position.
const int max_x = new_reso_x - new_bmp->width;
const int max_y = new_reso_y - new_bmp->height;
bgrt->image_offset_x = max(0, min(max_x, new_x + (new_reso_x - new_bmp->width) / 2));
bgrt->image_offset_y = max(0, min(max_y, new_y + (new_reso_y - new_bmp->height) / 2));
Log(config.debug,
L"Screen %dx%d, BMP %dx%d, center (%d, %d) = corner (%d, %d), orientation %d.\n",
new_reso_x, new_reso_y,
new_bmp->width, new_bmp->height,
new_x, new_y,
(int) bgrt->image_offset_x, (int) bgrt->image_offset_y,
new_orientation * 90
);
// Store this BGRT in the ACPI tables.
SetAcpiSdtChecksum(bgrt);
HandleAcpiTables(HackBGRT_REPLACE, bgrt);
HandleAcpiTables(HackBGRT_ACTION_REPLACE, bgrt);
}
/**
* Load an application.
*/
static EFI_HANDLE LoadApp(int print_failure, EFI_HANDLE image_handle, EFI_LOADED_IMAGE* image, const CHAR16* path) {
EFI_DEVICE_PATH* boot_dp = FileDevicePath(image->DeviceHandle, (CHAR16*) path);
EFI_HANDLE result = 0;
Log(config.debug, L"Loading application %s.\n", path);
if (EFI_ERROR(BS->LoadImage(0, image_handle, boot_dp, 0, 0, &result))) {
Log(config.debug || print_failure, L"Failed to load application %s.\n", path);
}
return result;
}
/**
* The main program.
*/
EFI_STATUS EFIAPI EfiMain(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *ST_) {
InitializeLib(image_handle, ST_);
EFI_STATUS EFIAPI efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *ST_) {
ST = ST_;
BS = ST_->BootServices;
RT = ST_->RuntimeServices;
// Clear the screen to wipe the vendor logo.
ST->ConOut->EnableCursor(ST->ConOut, 0);
ST->ConOut->ClearScreen(ST->ConOut);
Log(0, L"HackBGRT version: %s\n", version);
EFI_LOADED_IMAGE* image;
if (EFI_ERROR(BS->HandleProtocol(image_handle, &LoadedImageProtocol, (void**) &image))) {
Debug(L"HackBGRT: LOADED_IMAGE_PROTOCOL failed.\n");
if (EFI_ERROR(BS->HandleProtocol(image_handle, TmpGuidPtr((EFI_GUID) EFI_LOADED_IMAGE_PROTOCOL_GUID), (void**) &image))) {
Log(config.debug, L"LOADED_IMAGE_PROTOCOL failed.\n");
goto fail;
}
EFI_FILE_HANDLE root_dir = LibOpenRoot(image->DeviceHandle);
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);
goto fail;
CHAR16* default_dir_path = L"\\EFI\\HackBGRT";
Log(config.debug, L"Default directory: %s\n", default_dir_path);
EFI_FILE_HANDLE default_dir;
if (EFI_ERROR(root_dir->Open(root_dir, &default_dir, default_dir_path, EFI_FILE_MODE_READ, 0))) {
Log(config.debug, L"Failed to open HackBGRT default directory.\n");
default_dir = root_dir;
}
CHAR16* working_dir_path = DevicePathToStr(image->FilePath);
for (int i = StrLen(working_dir_path), skipped_last_component = 0; i--;) {
if (working_dir_path[i] == L'/' || working_dir_path[i] == L'\\') {
working_dir_path[i] = skipped_last_component++ ? L'\\' : L'\0';
}
}
for (int i = 1; i < argc; ++i) {
ReadConfigLine(&config, root_dir, argv[i]);
Log(config.debug, L"Working directory: %s\n", working_dir_path);
EFI_FILE_HANDLE working_dir;
if (EFI_ERROR(root_dir->Open(root_dir, &working_dir, working_dir_path, EFI_FILE_MODE_READ, 0))) {
Log(config.debug, L"Failed to open HackBGRT working directory.\n");
working_dir = default_dir;
}
EFI_FILE_HANDLE base_dir = working_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";
retry_read_config:
if (!ReadConfigFile(&config, base_dir, config_path)) {
if (base_dir != default_dir && StrCmp(default_dir_path, working_dir_path) != 0) {
base_dir = default_dir;
Log(config.debug, L"Trying the default directory.\n");
goto retry_read_config;
}
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]);
}
}
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;
if (!config.boot_path) {
Print(L"HackBGRT: Boot path not specified.\n");
} else {
Debug(L"HackBGRT: Loading application %s.\n", config.boot_path);
EFI_DEVICE_PATH* boot_dp = FileDevicePath(image->DeviceHandle, (CHAR16*) config.boot_path);
if (EFI_ERROR(BS->LoadImage(0, image_handle, boot_dp, 0, 0, &next_image_handle))) {
Print(L"HackBGRT: Failed to load application %s.\n", config.boot_path);
}
static CHAR16 backup_boot_path[] = L"\\EFI\\HackBGRT\\bootmgfw-original.efi";
static CHAR16 ms_boot_path[] = L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi";
int try_ms_quietly = 1;
if (config.boot_path && StriCmp(config.boot_path, L"MS") != 0) {
next_image_handle = LoadApp(1, image_handle, image, config.boot_path);
try_ms_quietly = 0;
}
if (!next_image_handle) {
static CHAR16 default_boot_path[] = L"\\EFI\\HackBGRT\\bootmgfw-original.efi";
Debug(L"HackBGRT: Loading application %s.\n", default_boot_path);
EFI_DEVICE_PATH* boot_dp = FileDevicePath(image->DeviceHandle, default_boot_path);
if (EFI_ERROR(BS->LoadImage(0, image_handle, boot_dp, 0, 0, &next_image_handle))) {
Print(L"HackBGRT: Also failed to load application %s.\n", default_boot_path);
config.boot_path = backup_boot_path;
next_image_handle = LoadApp(!try_ms_quietly, image_handle, image, config.boot_path);
if (!next_image_handle) {
config.boot_path = ms_boot_path;
next_image_handle = LoadApp(!try_ms_quietly, image_handle, image, config.boot_path);
if (!next_image_handle) {
goto fail;
}
}
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;
}
Print(L"HackBGRT: Reverting to %s.\n", default_boot_path);
Print(L"Press escape to cancel, any other key to boot.\n");
if (ReadKey().ScanCode == SCAN_ESC) {
goto fail;
}
config.boot_path = default_boot_path;
}
if (config.debug) {
Print(L"HackBGRT: Ready to boot.\nPress escape to cancel, any other key to boot.\n");
if (ReadKey().ScanCode == SCAN_ESC) {
} 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);
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 to exit.\n");
ReadKey();
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
View 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"
;
+7 -1
View File
@@ -1,6 +1,6 @@
#pragma once
#include <efi.h>
#include "efi.h"
#pragma pack(push, 1)
@@ -52,6 +52,12 @@ typedef struct {
UINT32 height;
UINT16 planes;
UINT16 bpp;
UINT32 compression;
UINT32 image_size;
UINT32 x_pixels_per_meter;
UINT32 y_pixels_per_meter;
UINT32 colors_used;
UINT32 important_colors;
} BMP;
/**
+117 -12
View File
@@ -1,7 +1,5 @@
#include "util.h"
#include <efilib.h>
const CHAR16* TmpStr(CHAR8 *src, int length) {
static CHAR16 arr[4][16];
static int j;
@@ -14,8 +12,99 @@ const CHAR16* TmpStr(CHAR8 *src, int length) {
return dest;
}
UINTN NullPrint(IN CHAR16 *fmt, ...) {
return 0;
const CHAR16* TmpIntToStr(UINT32 x) {
static CHAR16 buf[20];
int i = 20 - 1;
buf[i] = 0;
if (!x) {
buf[--i] = '0';
}
while (x && i) {
buf[--i] = '0' + (x % 10);
x /= 10;
}
return &buf[i];
}
#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) {
@@ -68,14 +157,28 @@ void RandomSeedAuto(void) {
RandomSeed(a, b), Random(), Random();
}
void WaitKey(void) {
EFI_STATUS WaitKey(UINT64 timeout_ms) {
ST->ConIn->Reset(ST->ConIn, FALSE);
WaitForSingleEvent(ST->ConIn->WaitForKey, 0);
const int ms_to_100ns = 10000;
EFI_EVENT events[2] = {ST->ConIn->WaitForKey};
EFI_STATUS status = BS->CreateEvent(EVT_TIMER, 0, NULL, NULL, &events[1]);
if (!EFI_ERROR(status)) {
BS->SetTimer(events[1], TimerRelative, timeout_ms * ms_to_100ns);
UINTN index;
status = BS->WaitForEvent(2, events, &index);
BS->CloseEvent(events[1]);
if (!EFI_ERROR(status) && index == 1) {
status = EFI_TIMEOUT;
}
}
return status;
}
EFI_INPUT_KEY ReadKey(void) {
WaitKey();
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;
}
@@ -89,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);
@@ -105,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) {
+29 -5
View File
@@ -1,6 +1,6 @@
#pragma once
#include <efi.h>
#include "efi.h"
/**
* Convert a short ASCII string to UCS2, store in a static array.
@@ -12,9 +12,22 @@
extern const CHAR16* TmpStr(CHAR8 *src, int length);
/**
* Empty function that has the same signature as Print.
* Print or log a string.
*
* @param mode -1 = print without logging, 0 = no, 1 = yes.
* @param fmt The format string. Supports %d, %x, %s.
*/
extern UINTN NullPrint(IN CHAR16 *fmt, ...);
extern void Log(int mode, IN CONST CHAR16 *fmt, ...);
/**
* 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.
@@ -82,15 +95,18 @@ extern void RandomSeedAuto(void);
/**
* Wait for a key press. It will still remain in the buffer.
*
* @param timeout_ms The timeout in milliseconds, or 0 for no timeout.
*/
extern void WaitKey(void);
extern EFI_STATUS WaitKey(UINT64 timeout_ms);
/**
* Wait for a key press and read it.
*
* @param timeout_ms The timeout in milliseconds, or 0 for no timeout.
* @return The pressed key.
*/
extern EFI_INPUT_KEY ReadKey(void);
extern EFI_INPUT_KEY ReadKey(UINT64 timeout_ms);
/**
* Load a file, allocate some extra bytes as well.
@@ -104,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;
}