25 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
20 changed files with 478 additions and 200 deletions
+1 -1
View File
@@ -18,4 +18,4 @@ Explain exactly the steps you did:
4. ...
**Log file**
Run the setup again and select menu option `L` (or add parameter `show-boot-log` on command line). Attach the `setup.log` to this report.
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.
+25
View File
@@ -2,6 +2,31 @@
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
+1 -1
View File
@@ -62,7 +62,7 @@ src/GIT_DESCRIBE.cs: $(FILES_CS) $(FILES_C) $(FILES_H)
$(file > $@,$(GIT_DESCRIBE_CS))
setup.exe: $(FILES_CS) src/GIT_DESCRIBE.cs
csc /nologo /define:GIT_DESCRIBE /out:$@ $^
csc -nologo -define:GIT_DESCRIBE -out:$@ $^
certificate.cer pki:
@echo
+38 -16
View File
@@ -12,19 +12,30 @@ When booting on a UEFI-based computer, Windows may show a vendor-defined logo wh
**Important:** If you mess up the installation, your system may become unbootable! Create a rescue disk before use. This software comes with no warranty. Use at your own risk.
* Make sure that your computer is booting with UEFI.
* Make sure that you have read the Secure Boot instructions.
* Make sure that BitLocker is disabled, or find your recovery key.
### Secure Boot instructions
### 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 copy of *shim* is extracted from Debian GNU/Linux many thanks to the maintainers! For copyright information, see [shim-signed/COPYRIGHT](shim-signed/COPYRIGHT).
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.
* 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!
@@ -52,7 +63,10 @@ The *shim* boot loader is maintained by Red Hat, Inc, and the included signed co
* `disable` run all relevant `disable-*` commands.
* `uninstall` disable and remove completely.
* `show-boot-log` show the debug log collected during boot (if `log=1` is set in `config.txt`).
* For example, run `setup.exe batch install allow-secure-boot enable-overwrite` to copy files and overwrite the MS boot loader regardless of Secure Boot status.
* `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
@@ -68,9 +82,11 @@ If you need it for other systems as well:
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 the installer copies into `[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
@@ -82,6 +98,10 @@ If you copy an image file to ESP manually, note that the image must be a 24-bit
## Troubleshooting
### BCDEdit failed
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).
@@ -92,11 +112,11 @@ Sometimes the first boot is very slow (multiple minutes) for an unknown reason.
### Image is not visible, "nothing happens"
Run the setup again and select the option to check the boot log. Continue troubleshooting according to the log contents:
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:
#### Log is empty
#### 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 resets some settings on reboot and skips the newly-installed HackBGRT.
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.)
@@ -112,14 +132,16 @@ Some people report that HackBGRT is not visible in the computer settings. That's
If all else fails and you are sure about your computer skills, you can try the legacy installation method. The method bypasses this particular problem but may cause very serious problems if configured incorrectly.
#### Log is not empty
#### Boot log is not empty
Try to reinstall HackBGRT with the default configuration and image.
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 and that you haven't messed up `config.txt`.
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.
If the default logo does not work, check the boot log again.
You may report an issue and attach the `setup.log` file.
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
+10 -9
View File
@@ -1,5 +1,5 @@
# 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. MS = either backup or original Windows boot loader.
boot=MS
@@ -8,12 +8,13 @@ boot=MS
# 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=(number)", a weight for this image in the randomization process. Default: n=1.
# - "x=(number)" or "x=keep", the x offset from the center. Default: x=0.
# - "y=(number)" or "y=keep", the y offset from the center. Default: y=0.
# - "o=(0|90|180|270|keep)", the screen orientation, degrees anticlockwise. Default: o=keep.
# - "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. Also keeps coordinates 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=file.bmp" to read an image file.
@@ -21,9 +22,9 @@ boot=MS
# Examples:
# - image=remove
# - image=black
# - image= x=0 y=-200 path=topimage.bmp
# - image= x=0 y=.0 path=topimage.bmp
# - image= n=1 o=90 path=sideways.bmp
# - image= n=50 y=999999 o=keep path=probable.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
@@ -31,7 +32,7 @@ boot=MS
# - 1/54 chance for splash.bmp, centered, orientation set to 90 degrees
# - 50/54 chance for probable.bmp, at the bottom edge, explicitly default orientation
# Default: just one image.
image= y=-200 path=splash.bmp
image= path=splash.bmp
# Preferred resolution. Use 0x0 for maximum and -1x-1 for original.
resolution=0x0
+25 -50
View File
@@ -1,55 +1,30 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: shim
Upstream-Contact: Peter Jones <pjones@redhat.com>
Source: https://github.com/rhboot/shim
Copyright 2012 Red Hat, Inc <mjg@redhat.com>
Files: *
Copyright: 2012 Red Hat, Inc
2009-2012 Intel Corporation
License: BSD-2-Clause
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
Files: debian/po/cs.po
Copyright: 2018 Michal Simunek <michal.simunek@gmail.com>
License: BSD-2-Clause
Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
Files: debian/po/de.po
Copyright: 2017, 2018 Markus Hiereth <markus.hiereth@freenet.de>
License: BSD-2-Clause
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.
Files: debian/po/fr.po
Copyright: 2017, 2018 Alban Vidal <alban.vidal@zordhak.fr>
License: BSD-2-Clause
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.
Files: debian/po/nl.po
Copyright: 2017, 2018 Frans Spiesschaert <Frans.Spiesschaert@yucom.be>
License: BSD-2-Clause
Files: debian/po/pt.po
Copyright: 2017, 2018 Rui Branco <ruipb@debianpt.org>
License: BSD-2-Clause
License: BSD-2-Clause
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
.
Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the
distribution.
.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
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.
+19 -15
View File
@@ -4,6 +4,7 @@ using System.IO;
using System.Text;
using System.Linq;
using System.Runtime.InteropServices;
using System.ComponentModel;
using Microsoft.Win32;
/**
@@ -34,6 +35,9 @@ public class Efi {
[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;
@@ -143,28 +147,26 @@ public class Efi {
for (UInt32 i = 4096; i <= 1024*1024; i *= 2) {
byte[] buf = new byte[i];
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) {
var error = Marshal.GetLastWin32Error();
const int ERROR_ENVVAR_NOT_FOUND = 0xCB;
if (error == ERROR_ENVVAR_NOT_FOUND) {
return result;
}
if (len > 0 || error == 0) {
result.Data = new byte[len];
Array.Copy(buf, 0, result.Data, 0, len);
return result;
}
switch (len != 0 ? 0 : Marshal.GetLastWin32Error()) {
case 203:
// Not found.
return result;
case 87:
throw new Exception("GetVariable: Invalid parameter");
case 1314:
throw new Exception("GetVariable: Privilege not held");
default:
throw new Exception("GetVariable: error " + Marshal.GetLastWin32Error());
}
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");
}
/**
@@ -266,11 +268,13 @@ public class Efi {
try {
var log = GetVariable("HackBGRTLog", EFI_HACKBGRT_GUID);
if (log.Data == null) {
return "Log is empty.";
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 $"Log not found: {e.ToString()}";
return $"Boot log not found: {e.ToString()}";
}
}
+33 -14
View File
@@ -90,6 +90,16 @@ public class EfiBootEntries {
}
}
/**
* Status of the own boot entry.
*/
public enum OwnEntryStatus {
NotFound,
Disabled,
EnabledAfterWindows,
Enabled
}
/**
* Path to the Windows boot loader.
*/
@@ -173,6 +183,26 @@ public class EfiBootEntries {
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.
*
@@ -206,9 +236,6 @@ public class EfiBootEntries {
*/
public void MakeOwnEntry(bool alwaysCopyFromMS, bool dryRun = false) {
var (msNum, msVar, msEntry) = WindowsEntry;
if (msEntry == null) {
throw new Exception("MakeOwnEntry: Windows Boot Manager not found.");
}
var (ownNum, ownVar, ownEntry) = OwnEntry;
if (ownVar == null) {
(ownNum, ownVar, ownEntry) = FreeEntry;
@@ -236,6 +263,9 @@ public class EfiBootEntries {
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";
@@ -295,15 +325,4 @@ public class EfiBootEntries {
}
}
}
/**
* Try to log the boot entries.
*/
public static void TryLogEntries() {
try {
new EfiBootEntries().LogEntries();
} catch (Exception e) {
Setup.Log($"LogEntries failed: {e.ToString()}");
}
}
}
+54 -15
View File
@@ -19,6 +19,22 @@ public sealed class Esp {
}
}
/** 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;
}
}
/**
* Constructor: do nothing.
*/
@@ -67,29 +83,48 @@ public sealed class Esp {
*
* @return true if the drive was found.
*/
public static bool Find() {
if (MountInstance != null) {
return true;
}
Setup.Log("Esp.Find()");
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.
MountvolOutput = Setup.Execute("mountvol", "", false);
var re = new Regex(" EFI[^\n]*(?:\n[ \t]*)?([A-Z]:\\\\)");
var m = re.Match(Setup.Execute("mountvol", "", false));
var m = re.Match(MountvolOutput);
if (m.Success && TryPath(m.Groups[1].Captures[0].Value)) {
return true;
}
Setup.Log("Esp.Find: no match");
Setup.Log("Esp: no match");
} catch (Exception e) {
Setup.Log($"Esp.Find: {e.ToString()}");
Setup.Log($"Esp: {e.ToString()}");
}
for (char c = 'A'; c <= 'Z'; ++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.Find: found {c}");
Setup.Log($"Esp: found {c}");
if (c == 'C') {
Setup.Log("Esp: WARNING: It's unlikely that C: is really the ESP.");
}
return true;
}
}
Setup.Log("Esp.Find: not found");
return false;
}
@@ -98,11 +133,15 @@ 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();
+92 -20
View File
@@ -71,6 +71,7 @@ public class Setup {
"disable",
"uninstall",
"boot-to-fw",
"ask-to-boot-to-fw",
"show-boot-log",
};
@@ -106,6 +107,9 @@ public class Setup {
/** @var Dry run? */
protected bool DryRun;
/** @var User-defined ESP path */
protected string UserEspPath;
/** @var Run in batch mode? */
protected bool Batch;
@@ -139,7 +143,7 @@ public class Setup {
var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
var pid = System.Diagnostics.Process.GetCurrentProcess().Id;
var prefix = $"{timestamp} | pid {pid} | ";
File.AppendAllText("setup.log", prefix + s.Replace("\n", "\n" + prefix) + "\n");
File.AppendAllText("setup-log.txt", prefix + s.Replace("\n", "\n" + prefix) + "\n");
}
/**
@@ -289,9 +293,17 @@ public class Setup {
if (DryRun) {
Directory.CreateDirectory(Path.Combine("dry-run", "EFI"));
Esp.TryPath("dry-run", false);
} else if (UserEspPath != null) {
WriteLine($"Using user-defined ESP path: {UserEspPath}");
if (!Esp.TryPath(UserEspPath, false)) {
throw new SetupException("The ESP path doesn't look like an EFI System Partition.");
}
}
if (Esp.Location == null && !Esp.Find() && !Esp.Mount() && !Batch) {
if (!Esp.FindOrMount() && !Batch) {
WriteLine("EFI System Partition was not found.");
if (Esp.MountvolESPNotSupported) {
WriteLine("Your computer doesn't support mountvol /S. You have to mount ESP manually.");
}
WriteLine("Press enter to exit, or give ESP path here: ");
string s = Console.ReadLine();
Log($"User input: {s}");
@@ -306,6 +318,9 @@ public class Setup {
throw new SetupException("EFI System Partition was not found.");
}
WriteLine($"EFI System Partition location is {Esp.Location}");
if (Esp.Location.StartsWith("C:")) {
WriteLine("Warning: EFI System Partition is not normally C: drive.");
}
}
/**
@@ -406,7 +421,11 @@ public class Setup {
if (!SkipShim) {
InstallFile(shimSource, loaderDest);
InstallFile(mmSource, $"mm{EfiArch}.efi");
InstallFile(loaderSource, "\u4957\u444e\u574f\u0053\u0058"); // bytes "WINDOWS\0X\0" as UTF-16
var shimContents = File.ReadAllBytes(shimSource);
var shimContentsStr = System.Text.Encoding.ASCII.GetString(shimContents);
if (shimContentsStr.IndexOf("Version: 15.") >= 0) {
InstallFile(loaderSource, "\u4957\u444e\u574f\u0053\u0058"); // bytes "WINDOWS\0X\0" as UTF-16
}
loaderDest = $"grub{EfiArch}.efi";
}
InstallFile(loaderSource, loaderDest);
@@ -441,12 +460,12 @@ public class Setup {
}
var bcdeditEnum = Execute("bcdedit", "/enum firmware");
if (bcdeditEnum.ExitCode != 0) {
WriteLine("BCDEdit is not working. Fix it or try another method.");
if (bcdeditEnum.Output != null) {
var lastLine = bcdeditEnum.Output.Split("\n".ToCharArray()).Last();
WriteLine($"BCDEdit output: {lastLine}");
WriteLine("Disable antivirus, check your hard disk, or search 'how to fix 0x800703EE'.");
var lines = bcdeditEnum.Output.Split("\n".ToCharArray());
var lastLine = lines.Where(s => s != "").Last();
WriteLine($"BCDEdit failed with: {lastLine}");
}
WriteLine("BCDEdit failed. See README for further information.");
throw new SetupException("Failed to enable HackBGRT with BCDEdit!");
}
try {
@@ -468,13 +487,12 @@ public class Setup {
var fwbootmgr = "{fwbootmgr}";
Execute("bcdedit", $"/set {fwbootmgr} displayorder {guid} /addfirst", true);
WriteLine("Enabled NVRAM entry for HackBGRT with BCDEdit.");
// Verify that the entry was created.
Execute("bcdedit", "/enum firmware", true);
Execute("bcdedit", $"/enum {guid}", true);
var e = new EfiBootEntries();
e.MakeOwnEntry(false, DryRun); // Fix load options for shim.
e.EnableOwnEntry(DryRun);
Execute("bcdedit", $"/enum {guid}", true);
e.LogEntries();
} catch (Exception e) {
Log($"EnableBCDEdit failed: {e.ToString()}");
throw new SetupException("Failed to enable HackBGRT with BCDEdit!");
@@ -522,9 +540,6 @@ public class Setup {
e.MakeOwnEntry(true, DryRun);
e.EnableOwnEntry(DryRun);
WriteLine("Enabled NVRAM entry for HackBGRT.");
// Verify that the entry was created.
e.LogEntries();
Execute("bcdedit", "/enum firmware", true);
}
/**
@@ -535,6 +550,27 @@ public class Setup {
WriteLine("Disabled NVRAM entry for HackBGRT.");
}
/**
* Log boot entries and report the status of HackBGRT boot entry.
*/
public void CheckEntries() {
try {
var efiBootEntries = new EfiBootEntries();
efiBootEntries.LogEntries();
var entryStatusText = efiBootEntries.GetOwnEntryStatus() switch {
EfiBootEntries.OwnEntryStatus.NotFound => "missing",
EfiBootEntries.OwnEntryStatus.Disabled => "disabled (not in BootOrder)",
EfiBootEntries.OwnEntryStatus.EnabledAfterWindows => "disabled (after Windows in BootOrder)",
EfiBootEntries.OwnEntryStatus.Enabled => "enabled correctly",
_ => throw new SetupException("Unknown HackBGRT boot entry status!")
};
WriteLine($"HackBGRT boot entry is {entryStatusText}.");
} catch (Exception e) {
WriteLine($"Failed to check EFI boot entries: {e.Message}");
Setup.Log(e.ToString());
}
}
/**
* Get paths related to MS boot loader.
*/
@@ -782,6 +818,26 @@ public class Setup {
}
}
/**
* Ask if the user wants to boot to the UEFI setup.
*/
protected void AskToBootToFW() {
WriteLine();
WriteLine("Sometimes HackBGRT needs to be enabled manually in the UEFI setup.");
WriteLine("You can do that now, or later if you notice that HackBGRT isn't working.");
WriteLine("Find 'Boot options' or something similar and move HackBGRT before Windows.");
if (!Efi.CanBootToFW()) {
return;
}
WriteLine("Do you want to reboot and enter the UEFI setup now? (Y/N)");
var k = Console.ReadKey().Key;
Log($"User input: {k}");
WriteLine();
if (k == ConsoleKey.Y) {
BootToFW();
}
}
/**
* Detect the EFI architecture.
*/
@@ -904,9 +960,9 @@ public class Setup {
Configure();
}
if (k == ConsoleKey.I) {
RunPrivilegedActions(new string[] { "disable", "install", "enable-bcdedit" });
RunPrivilegedActions(new string[] { "disable", "install", "enable-bcdedit", "ask-to-boot-to-fw" });
} else if (k == ConsoleKey.J) {
RunPrivilegedActions(new string[] { "disable", "install", "enable-entry" });
RunPrivilegedActions(new string[] { "disable", "install", "enable-entry", "ask-to-boot-to-fw" });
} else if (k == ConsoleKey.O) {
RunPrivilegedActions(new string[] { "disable", "install", "enable-overwrite" });
} else if (k == ConsoleKey.F) {
@@ -952,10 +1008,19 @@ public class Setup {
InitEspPath();
InitEspInfo();
var bootLog = $"\n--- BOOT LOG START ---\n{Efi.GetHackBGRTLog()}\n--- BOOT LOG END ---";
Func<string> tryGetBootLog = () => {
try {
return $"\n--- BOOT LOG START ---\n{Efi.GetHackBGRTLog()}\n--- BOOT LOG END ---";
} catch (NotImplementedException e) {
throw new SetupException($"Looks like you're not booting in UEFI mode. ({e.Message})");
}
};
var bootLog = tryGetBootLog();
Setup.Log(bootLog);
Efi.LogBGRT();
EfiBootEntries.TryLogEntries();
CheckEntries();
if (GetBootTime() is DateTime bootTime) {
var configTime = new[] { File.GetCreationTime("config.txt"), File.GetLastWriteTime("config.txt") }.Max();
Log($"Boot time = {bootTime}, config.txt changed = {configTime}");
@@ -976,7 +1041,7 @@ public class Setup {
WriteLine($"Error: {e.Message}");
WriteLine($"Reverting. Use batch mode with allow-bad-loader to override.");
revert();
throw new SetupException("Check your configuration and try again.");
throw new SetupException($"Error in configuration: {e.Message}");
}
}
};
@@ -987,6 +1052,8 @@ public class Setup {
HandleBitLocker();
enable();
verify(revert);
CheckEntries();
Execute("bcdedit", "/enum firmware", true);
};
foreach (var arg in actions) {
Log($"Running action '{arg}'.");
@@ -1008,10 +1075,13 @@ public class Setup {
Disable();
} else if (arg == "uninstall") {
Uninstall();
} else if (arg == "ask-to-boot-to-fw") {
AskToBootToFW();
} else if (arg == "boot-to-fw") {
BootToFW();
} else if (arg == "show-boot-log") {
WriteLine(bootLog);
CheckEntries();
} else {
throw new SetupException($"Invalid action: '{arg}'!");
}
@@ -1028,6 +1098,7 @@ public class Setup {
var self = Assembly.GetExecutingAssembly().Location;
Directory.SetCurrentDirectory(Path.GetDirectoryName(self));
WriteLine($"HackBGRT installer version: {Version}");
Log($"Windows version: {Environment.OSVersion}");
Log($"Args: {String.Join(" ", args)}");
Environment.ExitCode = new Setup().Run(args);
}
@@ -1044,7 +1115,7 @@ public class Setup {
AllowSecureBoot = args.Contains("allow-secure-boot");
AllowBitLocker = args.Contains("allow-bitlocker");
SkipShim = args.Contains("skip-shim");
ForwardArguments = String.Join(" ", args.Where(s => ForwardableArguments.Contains(s) || s.StartsWith("arch=")));
ForwardArguments = String.Join(" ", args.Where(s => ForwardableArguments.Contains(s) || s.StartsWith("arch=") || s.StartsWith("esp=")));
try {
if (!(Directory.Exists("efi") || Directory.Exists("efi-signed")) || !File.Exists("config.txt")) {
WriteLine("This setup program is not in the correct directory!");
@@ -1052,6 +1123,7 @@ public class Setup {
return 1;
}
SetArch(args.Where(s => s.StartsWith("arch=")).Select(s => s.Substring(5)).LastOrDefault());
UserEspPath = args.Where(s => s.StartsWith("esp=")).Select(s => s.Substring(4)).LastOrDefault();
if (args.Contains("is-elevated") && !HasPrivileges() && !DryRun) {
WriteLine("This installer needs to be run as administrator!");
return 1;
@@ -1092,7 +1164,7 @@ public class Setup {
WriteLine("This was a dry run, your system was not actually modified.");
}
if (!Batch) {
WriteLine("If you need to report a bug,\n - run this setup again with menu option L (show-boot-log)\n - then include the setup.log file with your report.");
WriteLine("If you need to report a bug,\n - run this setup again with menu option L (show-boot-log)\n - then include the setup-log.txt file with your report.");
WriteLine("Press any key to quit.");
Console.ReadKey();
}
+64 -29
View File
@@ -68,54 +68,89 @@ BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE base_dir,
return TRUE;
}
static void SetBMPWithRandom(struct HackBGRT_config* config, int weight, enum HackBGRT_action action, int x, int y, int o, const CHAR16* path) {
static void SetBMPWithRandom(struct HackBGRT_config* config, const struct HackBGRT_image_config* image, int weight) {
config->image_weight_sum += weight;
UINT32 random = (((UINT64) Random() & 0xffffffff) * config->image_weight_sum) >> 32;
UINT32 limit = ((UINT64) 0xffffffff * weight) >> 32;
Log(config->debug, L"%s n=%d, action=%d, x=%d, y=%d, o=%d, path=%s, rand=%x/%x\n", random <= limit ? L"Using" : L"Skipping", weight, action, x, y, o, path, random, limit);
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->action = action;
config->image_path = path;
config->orientation = o;
config->image_x = x;
config->image_y = y;
config->image = *image;
}
}
static int ParseCoordinate(const CHAR16* str, enum HackBGRT_action action) {
if (str && ((L'0' <= str[0] && str[0] <= L'9') || str[0] == L'-')) {
return str[0] == L'-' ? -(int)Atoi(str+1) : (int)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"keep", 4) == 0) || action == HackBGRT_KEEP) {
return HackBGRT_coord_keep;
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 0;
}
static void ReadConfigImage(struct HackBGRT_config* config, const CHAR16* line) {
const CHAR16* n = StrStrAfter(line, L"n=");
const CHAR16* x = StrStrAfter(line, L"x=");
const CHAR16* y = StrStrAfter(line, L"y=");
const CHAR16* o = StrStrAfter(line, L"o=");
const CHAR16* f = StrStrAfter(line, L"path=");
enum HackBGRT_action action = HackBGRT_KEEP;
if (f) {
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 {
Log(1, L"Invalid image line: %s\n", line);
return;
}
int weight = n && (!f || n < f) ? Atoi(n) : 1;
int x_val = ParseCoordinate(x, action);
int y_val = ParseCoordinate(y, action);
int o_val = o ? ParseCoordinate(o, action) : HackBGRT_coord_keep;
SetBMPWithRandom(config, weight, action, x_val, y_val, o_val, f);
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) {
+36 -8
View File
@@ -6,15 +6,47 @@
* 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_keep = -1000001
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;
};
/**
@@ -22,12 +54,8 @@ enum HackBGRT_coordinate {
*/
struct HackBGRT_config {
int debug, log;
enum HackBGRT_action action;
const CHAR16* image_path;
int image_x;
int image_y;
struct HackBGRT_image_config image;
int image_weight_sum;
int orientation;
int resolution_x;
int resolution_y;
int old_resolution_x;
+27
View File
@@ -64,6 +64,33 @@ EFI_DEVICE_PATH *FileDevicePath(IN EFI_HANDLE Device OPTIONAL, IN CHAR16 *FileNa
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) {
+53 -22
View File
@@ -21,7 +21,6 @@ EFI_RUNTIME_SERVICES *RT;
*/
static struct HackBGRT_config config = {
.log = 1,
.action = HackBGRT_KEEP,
};
/**
@@ -163,13 +162,13 @@ static ACPI_BGRT* HandleAcpiTables(enum HackBGRT_action action, ACPI_BGRT* bgrt)
}
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) {
Log(config.debug, L" -> Returning this one for later use.\n");
bgrt = (ACPI_BGRT*) entry;
}
break;
case HackBGRT_REMOVE:
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];
@@ -179,13 +178,13 @@ static ACPI_BGRT* HandleAcpiTables(enum HackBGRT_action action, ACPI_BGRT* bgrt)
xsdt->length -= sizeof(entry_arr[0]);
--j;
break;
case HackBGRT_REPLACE:
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) {
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];
@@ -303,13 +302,13 @@ static void CropBMP(BMP* bmp, int w, int h) {
*/
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 (relative to screen center), if possible.
const int old_valid = bgrt && VerifyAcpiSdtChecksum(bgrt);
@@ -324,7 +323,7 @@ void HackBgrt(EFI_FILE_HANDLE base_dir) {
// 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.
@@ -351,13 +350,13 @@ void HackBgrt(EFI_FILE_HANDLE base_dir) {
// Get the image (either old or new).
BMP* new_bmp = old_bmp;
if (config.action == HackBGRT_REPLACE) {
new_bmp = LoadBMP(base_dir, config.image_path);
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;
}
@@ -366,16 +365,23 @@ void HackBgrt(EFI_FILE_HANDLE base_dir) {
// Set the image address and orientation.
bgrt->image_address = (UINTN) new_bmp;
const int new_orientation = config.orientation == HackBGRT_coord_keep ? old_orientation : ((config.orientation / 90) & 3);
const int new_orientation = config.image.orientation == HackBGRT_ORIENTATION_KEEP ? old_orientation : config.image.orientation;
bgrt->status = new_orientation << 1;
// New center coordinates.
const int new_x = config.image_x == HackBGRT_coord_keep ? old_x : config.image_x;
const int new_y = config.image_y == HackBGRT_coord_keep ? old_y : config.image_y;
const int new_swap = new_orientation & 1;
const int new_reso_x = new_swap ? config.resolution_y : config.resolution_x;
const int new_reso_y = new_swap ? config.resolution_x : config.resolution_y;
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;
@@ -383,15 +389,17 @@ void HackBgrt(EFI_FILE_HANDLE base_dir) {
bgrt->image_offset_y = max(0, min(max_y, new_y + (new_reso_y - new_bmp->height) / 2));
Log(config.debug,
L"BMP at (%d, %d), center (%d, %d), resolution (%d, %d), orientation %d.\n",
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_x, new_y, new_reso_x, new_reso_y,
new_orientation * 90
);
// Store this BGRT in the ACPI tables.
SetAcpiSdtChecksum(bgrt);
HandleAcpiTables(HackBGRT_REPLACE, bgrt);
HandleAcpiTables(HackBGRT_ACTION_REPLACE, bgrt);
}
/**
@@ -439,16 +447,39 @@ EFI_STATUS EFIAPI efi_main(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *ST_) {
goto fail;
}
EFI_FILE_HANDLE base_dir;
if (EFI_ERROR(root_dir->Open(root_dir, &base_dir, L"\\EFI\\HackBGRT", EFI_FILE_MODE_READ, 0))) {
Log(config.debug, L"Failed to HackBGRT directory.\n");
base_dir = root_dir;
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';
}
}
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;
}