30 Commits

Author SHA1 Message Date
Lauri Kenttä
cbcb630697 Update change log and tag v1.4.0 2017-08-29 02:16:22 +03:00
Lauri Kenttä
8531c728e8 Add some recovery instructions 2017-08-29 01:56:58 +03:00
Lauri Kenttä
f255b13027 Hard-code a fallback boot loader path
If the config is invalid, try to load the default boot loader backup.
2017-08-29 01:56:58 +03:00
Lauri Kenttä
054f8cc751 Convert config.txt to UTF-8 2017-08-29 01:56:58 +03:00
Lauri Kenttä
24c4a8aa0c Support UTF-8 in config.txt 2017-08-29 01:56:58 +03:00
Lauri Kenttä
b3cc80b37b Fix possible buffer overflow 2017-08-29 01:56:58 +03:00
Lauri Kenttä
c01cf121a0 Makefile: Add zip target for easier releases 2017-08-29 01:56:58 +03:00
Lauri Kenttä
c12bd7a859 Makefile: Set version to 'unknown' if git fails 2017-01-07 14:03:43 +02:00
Lauri Kenttä
ce44c3dcb3 Makefile: Build both IA-32 and x86_64 by default 2017-01-07 14:02:51 +02:00
Lauri Kenttä
3b0253f6fc Update change log and tag v1.3.0 2016-12-22 21:46:06 +02:00
Lauri Kenttä
8921bafa90 Check Secure Boot status before installing 2016-12-22 21:43:50 +02:00
Lauri Kenttä
0347a1d921 Add change log 2016-06-05 12:42:38 +03:00
Lauri Kenttä
b78e5cd977 Update README: new installer, IA-32 support 2016-06-05 12:42:37 +03:00
Lauri Kenttä
e1d51be11b Add .gitignore to ignore generated files 2016-06-05 12:42:37 +03:00
Lauri Kenttä
19203eceed Add C# to Doxyfile 2016-06-05 12:42:37 +03:00
Lauri Kenttä
3da9e1818a Add git version to setup.exe 2016-06-05 12:42:37 +03:00
Lauri Kenttä
b7fd08c978 Add git version to the EFI application 2016-06-05 12:42:37 +03:00
Lauri Kenttä
ff838ec0f6 Automatically relaunch the installer as admin 2016-06-05 12:42:37 +03:00
Lauri Kenttä
a627895bfb Reimplement the installer with C#
The new installer includes better error handling and automatic
architecture detection to support both x86-64 and IA-32.
2016-06-05 12:42:37 +03:00
Lauri Kenttä
733acccc42 Build IA-32 version 2016-06-02 22:53:23 +03:00
Lauri Kenttä
efdd91a6d8 Add support for changing screen resolution 2016-06-02 22:52:05 +03:00
Lauri Kenttä
dae1a9abce Fix ACPI table handling and update the main logic
Apparently the BGRT must be stored in every XSDT on the system.
Also some table checksums were not updated correctly.
This patch cleans up the whole process of updating the ACPI tables.
2016-05-14 20:39:39 +03:00
Lauri Kenttä
e9ccec0d76 Add min helper 2016-05-14 20:11:55 +03:00
Lauri Kenttä
51ccb0255e Improve SDT checksum functions 2016-05-14 20:11:54 +03:00
Lauri Kenttä
a633eeb781 Use REPLACE and empty path instead of BLACK action 2016-05-14 20:11:17 +03:00
Lauri Kenttä
b5006f7771 In debug mode, confirm before booting 2016-05-14 11:49:10 +03:00
Lauri Kenttä
c49f0f6cbc Use ReadKey in the confirmation before exiting 2016-05-14 11:46:23 +03:00
Lauri Kenttä
8ed61047dd Add ReadKey to wait and actually read a key 2016-05-14 11:40:49 +03:00
Lauri Kenttä
31323a5111 For ACPI checksum, use header.length, not sizeof
There should be no difference between header.length and sizeof, but if
there is, the correct checksum should be based on header.length.
2016-05-14 10:57:56 +03:00
Lauri Kenttä
9891039b06 Fix a potentian null pointer reference
If GOP is not available and there's no old_bmp, the coordinates can't
be automatically calculated.
2016-05-14 10:51:29 +03:00
16 changed files with 928 additions and 298 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
boot*.efi
setup.exe
src/GIT_DESCRIBE.cs
html

58
CHANGELOG.md Normal file
View File

@@ -0,0 +1,58 @@
# Change Log
All notable changes to this project will be documented in this file.
## 1.4.0 - 2017-08-29
### Added
- Use UTF-8 in the configuration file.
- Use the default boot loader path if the configured one doesn't work.
## 1.3.0 - 2016-12-22
### Added
- Check Secure Boot status before installing.
## 1.2.0 - 2016-06-05
### Added
- Better installer, setup.exe.
- Support for low-end machines with 32-bit IA-32 UEFI.
- Support for changing resolution.
- Version information in the program.
- Change log.
### Removed
- Removed old install scripts, install.bat and uninstall.bat.
## 1.1.0 - 2016-05-14
### Changed
- Wait for input before booting if debug=1 is set.
### Fixed
- Fix handling of multiple BGRT entries.
- Fix ACPI table checksums.
## 1.0.0 - 2016-05-11
### Added
- Easy-to-use installation script.
- Git repository for the project.
## 0.2.0 - 2016-04-26
### Added
- Support for randomly alternating images.
- Support for black background.
- Support for the native Windows logo.
### Changed
- New configuration file format for images.
## 0.1.0 - 2016-01-15
### Added
- Support for loading a bitmap and updating the BGRT.
- Support for loading the next boot loader.
- Support for a configuration file.

View File

@@ -1,8 +1,9 @@
INPUT = src
FILE_PATTERNS = *.c *.h
FILE_PATTERNS = *.c *.h *.cs
JAVADOC_AUTOBRIEF = YES
EXTRACT_ALL = YES
EXTRACT_STATIC = YES
EXTRACT_PRIVATE = YES
STRIP_CODE_COMMENTS = NO
INLINE_SOURCES = NO
GENERATE_HTML = YES

View File

@@ -7,11 +7,30 @@ LIBS = -L$(GNUEFI_LIB) -lefi -lgcc
GNUEFI_INC = /usr/$(CC_PREFIX)/include/efi
GNUEFI_LIB = /usr/$(CC_PREFIX)/lib
FILES_C = src/main.c src/util.c src/config.c
FILES_C = src/main.c src/util.c src/types.c src/config.c
FILES_H = $(wildcard src/*.h)
GIT_DESCRIBE = $(firstword $(shell git describe --tags) unknown)
CFLAGS += '-DGIT_DESCRIBE=L"$(GIT_DESCRIBE)"'
ZIPDIR = HackBGRT-$(GIT_DESCRIBE:v%=%)
ZIP = $(ZIPDIR).zip
.PHONY: all
all: bootx64.efi
efi: bootx64.efi bootia32.efi
setup: setup.exe
all: efi setup
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)"
src/GIT_DESCRIBE.cs: src/Setup.cs $(FILES_C) $(FILES_H)
echo 'public class GIT_DESCRIBE { public static string data = "$(GIT_DESCRIBE)"; }' > $@
setup.exe: src/Setup.cs src/GIT_DESCRIBE.cs
mcs -define:GIT_DESCRIBE -out:$@ $^
bootx64.efi: CC_PREFIX = x86_64-w64-mingw32
bootx64.efi: GNUEFI_ARCH = x86_64
@@ -22,6 +41,3 @@ bootia32.efi: CC_PREFIX = i686-w64-mingw32
bootia32.efi: GNUEFI_ARCH = ia32
bootia32.efi: $(FILES_C)
$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ $(LIBS) -s
HackBGRT.tar.xz: bootx64.efi config.txt splash.bmp install.bat uninstall.bat README.md README.efilib LICENSE
tar cJf $@ --transform=s,^,HackBGRT/, $^

View File

@@ -11,21 +11,24 @@ 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 a 64-bit x86-64 processor.
* Make sure that Secure Boot is disabled, or learn to sign EFI applications.
* Simple Windows installation:
* Get at least these files: `bootx64.efi`, `config.txt`, `install.bat`, `splash.bmp`.
* Run Command Prompt as Administrator.
* Run `install.bat` from the Command Prompt.
* The installer will launch Paint for creating the image(s).
* The installer will launch Notepad for modifying the configuration.
* If Windows later reinstalls the original boot loader, run `install.bat` again.
* Get the latest release.
* Start `setup.exe` and select `I` (install).
* 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, simply reinstall.
* If you wish to change the image or other configuration, simply reinstall.
* Installation for Windows with another boot loader (e.g. GRUB):
* Copy the mentioned files to `[EFI System Partition]\EFI\HackBGRT\`.
* 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`.
* Installation for all operating systems:
* Copy the mentioned files to `[EFI System Partition]\EFI\HackBGRT\`.
* Extract the latest release to `[EFI System Partition]\EFI\HackBGRT\`.
* Set `boot=` to your preferred boot loader in `config.txt`.
* Set `\EFI\HackBGRT\bootx64.efi` as your default boot loader with `efibootmgr` or some other EFI boot manager tool.
@@ -41,6 +44,13 @@ The image must be a 24-bit BMP file with a 54-byte header. That's a TrueColor BM
Multiple images may be specified, in which case one is picked at random.
## Recovery
If something breaks and you can't boot to Windows, you have the following options:
* 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.
## Building
* Compiler: GCC targeting w64-mingw32

Binary file not shown.

View File

@@ -1,120 +0,0 @@
@ECHO OFF
CD %~dp0
IF NOT "%1" == "uninstall" (
IF NOT EXIST bootx64.efi (
ECHO Missing bootx64.efi, you're doing something wrong.
GOTO fail_before_esp
)
)
SET ESP_UNMOUNT=1
SET ESP=-
FOR /F "delims=" %%I IN ('CMD /C "MOUNTVOL | FINDSTR /C:EFI | FINDSTR /C::"') DO (
ECHO %%I
SET ESP_STR=%%I
SET ESP=%ESP_STR:~-3,2%
SET ESP_UNMOUNT=0
)
IF %ESP% == - MOUNTVOL S: /S >NUL && SET ESP=S:
IF %ESP% == - MOUNTVOL B: /S >NUL && SET ESP=B:
IF %ESP% == - MOUNTVOL A: /S >NUL && SET ESP=A:
IF %ESP% == - MOUNTVOL X: /S >NUL && SET ESP=X:
IF %ESP% == - (
ECHO The EFI System Partition is not mounted.
GOTO fail_before_esp
)
SET HackBGRT=%ESP%\EFI\HackBGRT
SET MSBOOT=%ESP%\EFI\Microsoft\Boot
IF NOT EXIST %MSBOOT% (
ECHO %MSBOOT% does not exist.
ECHO If the path seems incorrect, report a bug.
GOTO fail
)
IF "%1" == "uninstall" (
IF NOT EXIST %HackBGRT%\bootmgfw-original.efi (
ECHO Missing %HackBGRT%\bootmgfw-original.efi!
GOTO fail
)
COPY %HackBGRT%\bootmgfw-original.efi %MSBOOT%\bootmgfw.efi >NUL || (
ECHO Failed to restore the original bootmgfw.efi.
GOTO fail
)
ECHO The original bootmgfw.efi has been restored.
IF EXIST %HackBGRT% (
DEL /P %HackBGRT%
)
EXIT /B
)
IF NOT EXIST %HackBGRT% (
MKDIR %HackBGRT%
)
IF NOT EXIST %HackBGRT%\bootmgfw-original.efi (
COPY %MSBOOT%\bootmgfw.efi %HackBGRT%\bootmgfw-original.efi >NUL || (
ECHO Couldn't copy the original bootmgfw.efi.
GOTO fail
)
)
ECHO Copying files...
COPY /Y LICENSE %HackBGRT%\ >NUL
COPY /Y README.md %HackBGRT%\ >NUL
COPY /Y README.efilib %HackBGRT%\ >NUL
COPY /Y install.bat %HackBGRT%\ >NUL
COPY /Y uninstall.bat %HackBGRT%\ >NUL
COPY /Y bootx64.efi %HackBGRT%\ >NUL || GOTO fail
IF NOT EXIST %HackBGRT%\splash.bmp (
COPY splash.bmp %HackBGRT%\ >NUL || GOTO fail
)
IF EXIST %HackBGRT%\config.txt (
ECHO Copying configuration as config-new.txt.
ECHO Be sure to check for any format changes!
COPY /Y config.txt %HackBGRT%\config-new.txt >NUL || GOTO fail
) ELSE (
COPY /Y config.txt %HackBGRT%\config.txt >NUL || GOTO fail
)
ECHO Draw or copy your preferred image to splash.bmp.
START /WAIT mspaint %HackBGRT%\splash.bmp
ECHO Check the configuration in config.txt.
IF EXIST %HackBGRT%\config-new.txt (
ECHO See config-new.txt for reference.
START notepad %HackBGRT%\config-new.txt
)
START /WAIT notepad %HackBGRT%\config.txt
ECHO Replacing bootmgfw.efi.
COPY /Y bootx64.efi %MSBOOT%\bootmgfw.efi >NUL || (
ECHO Failed to copy the boot loader!
ECHO Restoring the original bootmgfw.efi...
COPY %HackBGRT%\bootmgfw-original.efi %MSBOOT%\bootmgfw.efi >NUL || (
ECHO Restoration failed You will need to fix this!
)
GOTO fail
)
IF %ESP_UNMOUNT% == 1 (
MOUNTVOL %ESP% /D
)
ECHO Installation is ready.
ECHO If your CPU is not x86-64, you should definitely uninstall now.
ECHO Remember to disable Secure Boot, or HackBGRT will not boot.
PAUSE
EXIT /B
:fail
IF %ESP_UNMOUNT% == 1 (
MOUNTVOL %ESP% /D
)
:fail_before_esp
ECHO Exiting due to errors.
PAUSE
EXIT /B 1

431
src/Setup.cs Normal file
View File

@@ -0,0 +1,431 @@
using System;
using System.Reflection;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
using System.Linq;
using System.Security.Principal;
using Microsoft.Win32;
/**
* HackBGRT Setup.
*/
public class Setup {
/**
* The custom exception class for expected exceptions.
*/
public class SetupException: Exception {
public SetupException(string msg): base(msg) {
}
}
/**
* EFI System Partition mounter.
*/
public class ESP {
/** EFI System Partition mount point. */
public string Path = null;
/** Was the ESP mounted by this instance? */
private bool Mounted = false;
/**
* Mount the EFI System Partition, or determine an existing mount point.
*/
public void Mount() {
try {
// Match "The EFI System Partition is mounted at E:\" with some language support.
var re = new Regex(" EFI[^\n]*(?:\n[ \t]*)?([A-Z]:)\\\\");
Path = re.Match(Execute("mountvol", "", false)).Groups[1].Captures[0].Value;
return;
} catch {
}
// Try to mount somewhere.
for (char c = 'A'; c <= 'Z'; ++c) {
if (Execute("mountvol", c + ": /S", true) != null) {
Console.WriteLine("The EFI System Partition is mounted in " + c + ":\\");
Path = c + ":";
Mounted = true;
return;
}
}
throw new SetupException("The EFI System Partition is not mounted.");
}
/**
* Unmount the EFI System Partition, if it was previously mounted by this instance.
*/
public void Unmount() {
if (Mounted) {
Execute("mountvol", Path + " /D", true);
Mounted = false;
Path = null;
}
}
}
/**
* Boot loader type (MS/HackBGRT).
*/
public enum BootLoaderType {
Unknown,
MS,
HackBGRT
}
/**
* Information about a boot loader: type and architecture.
*/
public struct BootLoaderInfo {
public string Path;
public bool Exists;
public BootLoaderType Type;
public string Arch;
}
/**
* Recognize the boot loader type (MS/HackBGRT) and architecture.
*
* @param path Path to the boot loader.
* @return Information about the boot loader.
*/
public static BootLoaderInfo RecognizeLoader(string path) {
BootLoaderInfo info;
info.Path = path;
info.Exists = false;
info.Type = BootLoaderType.Unknown;
info.Arch = null;
try {
byte[] data = File.ReadAllBytes(path);
info.Exists = true;
string tmp = System.Text.Encoding.ASCII.GetString(data);
if (tmp.IndexOf("HackBGRT") >= 0) {
info.Type = BootLoaderType.HackBGRT;
} else if (tmp.IndexOf("Microsoft Corporation") >= 0) {
info.Type = BootLoaderType.MS;
}
switch (BitConverter.ToUInt16(data, BitConverter.ToInt32(data, 0x3c) + 4)) {
case 0x014c: info.Arch = "ia32"; break;
case 0x0200: info.Arch = "ia64"; break;
case 0x8664: info.Arch = "x64"; break;
}
} catch {
}
return info;
}
/**
* Backup the original boot loader.
*
* @param hackbgrt Path to the HackBGRT directory on the ESP.
* @param msloader Path of the MS boot loader to backup.
* @return Information about the boot loader backup.
*/
public static BootLoaderInfo Backup(string hackbgrt, string msloader) {
try {
if (!Directory.Exists(hackbgrt)) {
Directory.CreateDirectory(hackbgrt);
}
BootLoaderInfo info = RecognizeLoader(msloader);
if (info.Type == BootLoaderType.HackBGRT) {
Console.WriteLine(msloader + " already contains a version of HackBGRT, skipping backup.");
} else if (info.Type == BootLoaderType.MS) {
// Overwrite any previous backup, the file might have changed.
Copy(msloader, hackbgrt + "\\bootmgfw-original.efi");
} else {
Console.WriteLine(msloader + " doesn't look right, skipping backup.");
}
} catch {
}
BootLoaderInfo msbackup = RecognizeLoader(hackbgrt + "\\bootmgfw-original.efi");
if (!msbackup.Exists) {
throw new SetupException("Couldn't backup the original bootmgfw.efi.");
}
if (msbackup.Arch == null || msbackup.Type != BootLoaderType.MS) {
throw new SetupException("The boot loader backup (" + msbackup.Path + ") doesn't look like a supported MS boot loader");
}
return msbackup;
}
/**
* Install HackBGRT, copy files and replace the msloader with our own.
*
* @param src Path to the installation files.
* @param hackbgrt Path to the HackBGRT directory on the ESP.
* @param msloader Path of the MS boot loader to replace.
* @param msbackup Information of the boot loader backup.
*/
public static void Install(string src, string hackbgrt, string msloader, BootLoaderInfo msbackup) {
string loaderName = "boot" + msbackup.Arch + ".efi";
string loaderSrc = src + "\\" + loaderName;
string loaderInst = hackbgrt + "\\" + loaderName;
if (!File.Exists(loaderSrc)) {
throw new SetupException(loaderName + " doesn't exist.");
}
Copy(loaderSrc, loaderInst);
if (!File.Exists(hackbgrt + "\\config.txt")) {
Copy(src + "\\config.txt", hackbgrt + "\\config.txt");
}
if (!File.Exists(hackbgrt + "\\splash.bmp")) {
Copy(src + "\\splash.bmp", hackbgrt + "\\splash.bmp");
}
Enable(hackbgrt, msloader, msbackup);
Configure(hackbgrt);
}
/**
* Enable HackBGRT, replace the msloader with our own.
*
* @param hackbgrt Path to the HackBGRT directory on the ESP.
* @param msloader Path of the MS boot loader to replace.
* @param msbackup Information of the boot loader backup.
*/
public static void Enable(string hackbgrt, string msloader, BootLoaderInfo msbackup) {
string loaderName = "boot" + msbackup.Arch + ".efi";
string loaderInst = hackbgrt + "\\" + loaderName;
if (!File.Exists(loaderInst)) {
throw new SetupException(loaderInst + " doesn't exist.");
}
if (!Copy(loaderInst, msloader)) {
Copy(msbackup.Path, msloader);
throw new SetupException("Couldn't install the new bootmgfw.efi.");
}
Console.WriteLine("HackBGRT is now enabled.");
}
/**
* Configure HackBGRT.
*
* @param hackbgrt Path to the HackBGRT directory on the ESP.
*/
public static void Configure(string hackbgrt) {
// Open config.txt in notepad.
Console.WriteLine("Check the configuration in " + hackbgrt + "\\config.txt.");
Console.WriteLine("Use the supplied config.txt as reference.");
Console.WriteLine("Be sure to check for any format changes if updating!");
try {
StartProcess("notepad", hackbgrt + "\\config.txt").WaitForExit();
} catch {
Console.WriteLine("Editing config.txt with notepad failed! Edit it manually.");
}
// Open splash.bmp in mspaint.
Console.WriteLine("Draw or copy your preferred image to splash.bmp.");
try {
StartProcess("mspaint", hackbgrt + "\\splash.bmp").WaitForExit();
} catch {
Console.WriteLine("Editing splash.bmp with mspaint failed! Edit it manually.");
}
}
/**
* Disable HackBGRT, restore the original boot loader.
*
* @param hackbgrt Where HackBGRT is installed.
* @param msloader Where to restore the MS boot loader.
*/
public static void Disable(string hackbgrt, string msloader) {
BootLoaderInfo info = RecognizeLoader(msloader);
if (info.Type == BootLoaderType.MS) {
Console.WriteLine(msloader + " is already ok.");
} else {
if (!File.Exists(hackbgrt + "\\bootmgfw-original.efi")) {
throw new SetupException("Missing the original bootmgfw.efi.");
}
if (!Copy(hackbgrt + "\\bootmgfw-original.efi", msloader)) {
throw new SetupException("Failed to restore the original bootmgfw.efi.");
}
Console.WriteLine(msloader + " has been restored.");
}
}
/**
* Uninstall HackBGRT, restore the original boot loader.
*
* @param hackbgrt Where HackBGRT is installed.
* @param msloader Where to restore the MS boot loader.
*/
public static void Uninstall(string hackbgrt, string msloader) {
Disable(hackbgrt, msloader);
try {
Directory.Delete(hackbgrt, true);
Console.WriteLine("HackBGRT has been removed.");
} catch {
Console.WriteLine("The directory " + hackbgrt + " couldn't be removed.");
}
}
/**
* Run the setup.
*
* @param src Path to the installation files.
*/
protected static void RunSetup(string src) {
ESP esp = new ESP();
try {
esp.Mount();
int secureBoot = -1;
try {
secureBoot = (int) Registry.GetValue(
"HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\SecureBoot\\State",
"UEFISecureBootEnabled",
-1
);
} catch (Exception) {
}
if (secureBoot != 0) {
if (secureBoot == 1) {
Console.WriteLine("Secure Boot is enabled.");
Console.WriteLine("HackBGRT doesn't work with Secure Boot.");
Console.WriteLine("If you install HackBGRT, your machine may become unbootable,");
Console.WriteLine("unless you manually disable Secure Boot.");
} else {
Console.WriteLine("Could not determine Secure Boot status.");
Console.WriteLine("Your system may be incompatible with HackBGRT.");
Console.WriteLine("If you install HackBGRT, your machine may become unbootable.");
}
Console.WriteLine("Do you still wish to continue? [Y/N]");
var k = Console.ReadKey().Key;
Console.WriteLine();
if (k == ConsoleKey.Y) {
Console.WriteLine("Continuing. THIS IS DANGEROUS!");
} else if (k == ConsoleKey.N) {
Console.WriteLine("Aborting.");
return;
} else {
throw new SetupException("Invalid choice!");
}
}
string hackbgrt = esp.Path + "\\EFI\\HackBGRT";
string msloader = esp.Path + "\\EFI\\Microsoft\\Boot\\bootmgfw.efi";
if (!Directory.Exists(hackbgrt)) {
Install(src, hackbgrt, msloader, Backup(hackbgrt, msloader));
} else {
Console.WriteLine("Choose action (press a key):");
Console.WriteLine(" I = install, upgrade, fix installation");
Console.WriteLine(" C = configure (edit config.txt and splash.bmp)");
Console.WriteLine(" E = enable (after disabling); if in doubt, choose 'I' instead");
Console.WriteLine(" D = disable, restore the original boot loader");
Console.WriteLine(" R = remove completely; fully delete " + hackbgrt);
var k = Console.ReadKey().Key;
Console.WriteLine();
if (k == ConsoleKey.I) {
Install(src, hackbgrt, msloader, Backup(hackbgrt, msloader));
} else if (k == ConsoleKey.C) {
Configure(hackbgrt);
} else if (k == ConsoleKey.E) {
Enable(hackbgrt, msloader, Backup(hackbgrt, msloader));
} else if (k == ConsoleKey.D) {
Disable(hackbgrt, msloader);
} else if (k == ConsoleKey.R) {
Uninstall(hackbgrt, msloader);
} else {
throw new SetupException("Invalid choice!");
}
}
} catch (SetupException e) {
Console.WriteLine("Error: {0}", e.Message);
Environment.ExitCode = 1;
} catch (Exception e) {
Console.WriteLine("Unexpected error!\n{0}", e.ToString());
Console.WriteLine("If this is the most current release, please report this bug.");
Environment.ExitCode = 1;
} finally {
esp.Unmount();
}
}
/**
* Start another process.
*
* @param app Path to the application.
* @param args The argument string.
* @return The started process.
*/
protected 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.
*/
protected 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;
}
} catch {
Console.WriteLine("This installer needs to be run as administrator!");
Console.WriteLine("Press any key to quit.");
Console.ReadKey();
Environment.ExitCode = 1;
return;
}
RunSetup(Path.GetDirectoryName(self));
Console.WriteLine("Press any key to quit.");
Console.ReadKey();
}
}

View File

@@ -4,14 +4,55 @@
#include <efilib.h>
BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, const CHAR16* path) {
CHAR16* str = 0;
UINTN str_bytes = 0;
str = LoadFileWithPadding(root_dir, path, &str_bytes, sizeof(*str));
if (!str) {
void* data = 0;
UINTN data_bytes = 0;
data = LoadFileWithPadding(root_dir, path, &data_bytes, 4);
if (!data) {
Print(L"HackBGRT: Failed to load configuration (%s)!\n", path);
return FALSE;
}
UINTN str_len = str_bytes / sizeof(*str);
CHAR16* str;
UINTN str_len;
if (*(CHAR16*)data == 0xfeff) {
// UCS-2
str = data;
str_len = data_bytes / sizeof(*str);
} else {
// UTF-8 -> UCS-2
EFI_STATUS e = BS->AllocatePool(EfiBootServicesData, data_bytes * 2 + 2, (void**)&str);
if (EFI_ERROR(e)) {
FreePool(data);
return FALSE;
}
UINT8* str0 = data;
for (UINTN i = str_len = 0; i < data_bytes;) {
UINTN unicode = 0xfffd;
if (str0[i] < 0x80) {
unicode = str0[i];
i += 1;
} else if (str0[i] < 0xc0) {
i += 1;
} else if (str0[i] < 0xe0) {
unicode = ((str0[i] & 0x1f) << 6) | ((str0[i+1] & 0x3f) << 0);
i += 2;
} else if (str0[i] < 0xf0) {
unicode = ((str0[i] & 0x0f) << 12) | ((str0[i+1] & 0x3f) << 6) | ((str0[i+2] & 0x3f) << 0);
i += 3;
} else if (str0[i] < 0xf8) {
unicode = ((str0[i] & 0x07) << 18) | ((str0[i+1] & 0x3f) << 12) | ((str0[i+2] & 0x3f) << 6) | ((str0[i+3] & 0x3f) << 0);
i += 4;
} else {
i += 1;
}
if (unicode <= 0xffff) {
str[str_len++] = unicode;
} else {
str[str_len++] = 0xfffd;
}
}
str[str_len] = 0;
FreePool(data);
}
for (int i = 0; i < str_len;) {
int j = i;
@@ -65,7 +106,7 @@ static void ReadConfigImage(struct HackBGRT_config* config, const CHAR16* line)
} else if (StrStr(line, L"remove")) {
action = HackBGRT_REMOVE;
} else if (StrStr(line, L"black")) {
action = HackBGRT_BLACK;
action = HackBGRT_REPLACE;
} else if (StrStr(line, L"keep")) {
action = HackBGRT_KEEP;
} else {
@@ -76,6 +117,17 @@ static void ReadConfigImage(struct HackBGRT_config* config, const CHAR16* line)
SetBMPWithRandom(config, weight, action, ParseCoordinate(x, action), ParseCoordinate(y, action), f);
}
static void ReadConfigResolution(struct HackBGRT_config* config, const CHAR16* line) {
const CHAR16* x = line;
const CHAR16* y = StrStrAfter(line, L"x");
if (x && *x && y && *y) {
config->resolution_x = *x == '-' ? -(int)Atoi(x+1) : (int)Atoi(x);
config->resolution_y = *y == '-' ? -(int)Atoi(y+1) : (int)Atoi(y);
} else {
Print(L"HackBGRT: Invalid resolution line: %s\n", line);
}
}
void ReadConfigLine(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, const CHAR16* line) {
line = TrimLeft(line);
if (line[0] == L'#' || line[0] == 0) {
@@ -98,5 +150,9 @@ void ReadConfigLine(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, co
ReadConfigFile(config, root_dir, line + 7);
return;
}
if (StrnCmp(line, L"resolution=", 11) == 0) {
ReadConfigResolution(config, line + 11);
return;
}
Print(L"Unknown configuration directive: %s\n", line);
}

View File

@@ -6,7 +6,7 @@
* Possible actions to perform on the BGRT.
*/
enum HackBGRT_action {
HackBGRT_KEEP = 0, HackBGRT_REPLACE, HackBGRT_REMOVE, HackBGRT_BLACK
HackBGRT_KEEP = 0, HackBGRT_REPLACE, HackBGRT_REMOVE
};
/**
@@ -28,6 +28,8 @@ struct HackBGRT_config {
int image_x;
int image_y;
int image_weight_sum;
int resolution_x;
int resolution_y;
const CHAR16* boot_path;
};

View File

@@ -34,6 +34,63 @@ static EFI_GRAPHICS_OUTPUT_PROTOCOL* GOP(void) {
return gop;
}
/**
* Set screen resolution. If there is no exact match, try to find a bigger one.
*
* @param w Horizontal resolution. 0 for max, -1 for current.
* @param h Vertical resolution. 0 for max, -1 for current.
*/
static void SetResolution(int w, int h) {
EFI_GRAPHICS_OUTPUT_PROTOCOL* gop = GOP();
if (!gop) {
Debug(L"GOP not found!\n");
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);
Debug(L"Looking for resolution %dx%d...\n", w, h);
for (UINT32 i = gop->Mode->MaxMode; i--;) {
int new_w = 0, new_h = 0;
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION* info = 0;
UINTN info_size;
if (EFI_ERROR(gop->QueryMode(gop, i, &info_size, &info))) {
continue;
}
if (info_size < sizeof(*info)) {
FreePool(info);
continue;
}
new_w = info->HorizontalResolution;
new_h = info->VerticalResolution;
FreePool(info);
// Sum of missing w/h should be minimal.
int new_missing = max(w - new_w, 0) + max(h - new_h, 0);
int best_missing = max(w - best_w, 0) + max(h - best_h, 0);
if (new_missing > best_missing) {
continue;
}
// Sum of extra w/h should be minimal.
int new_over = max(-w + new_w, 0) + max(-h + new_h, 0);
int best_over = max(-w + best_w, 0) + max(-h + best_h, 0);
if (new_missing == best_missing && new_over >= best_over) {
continue;
}
best_w = new_w;
best_h = new_h;
best_i = i;
}
Debug(L"Found resolution %dx%d.\n", best_w, best_h);
if (best_i != gop->Mode->Mode) {
gop->SetMode(gop, best_i);
}
}
/**
* Select the correct coordinate (manual, automatic, native)
*
@@ -53,106 +110,77 @@ static int SelectCoordinate(int value, int automatic, int native) {
}
/**
* Initialize (clear) a BGRT.
* Create a new XSDT with the given number of entries.
*
* @param bgrt The BGRT to initialize.
* @param xsdt0 The old XSDT.
* @param entries The number of SDT entries.
* @return Pointer to a new XSDT.
*/
static void InitBGRT(ACPI_BGRT* bgrt) {
const char data[0x38] = "BGRT" "\x38\x00\x00\x00" "\x00" "\xd6" "Mtblx*" "HackBGRT" "\x20\x17\x00\x00" "PTL " "\x02\x00\x00\x00" "\x01\x00" "\x00" "\x00";
CopyMem(bgrt, data, sizeof(data));
ACPI_SDT_HEADER* CreateXsdt(ACPI_SDT_HEADER* xsdt0, UINTN entries) {
ACPI_SDT_HEADER* xsdt = 0;
UINT32 xsdt_len = sizeof(ACPI_SDT_HEADER) + entries * sizeof(UINT64);
BS->AllocatePool(EfiACPIReclaimMemory, xsdt_len, (void**)&xsdt);
if (!xsdt) {
Print(L"HackBGRT: Failed to allocate memory for XSDT.\n");
return 0;
}
ZeroMem(xsdt, xsdt_len);
CopyMem(xsdt, xsdt0, min(xsdt0->length, xsdt_len));
xsdt->length = xsdt_len;
SetAcpiSdtChecksum(xsdt);
return xsdt;
}
/**
* Fill a BGRT as specified by the parameters.
* Update the ACPI tables as needed for the desired BGRT change.
*
* @param bgrt The BGRT to fill.
* @param new_bmp The BMP to use.
* @param new_x The x coordinate to use.
* @param new_y The y coordinate to use.
*/
static void FillBGRT(ACPI_BGRT* bgrt, BMP* new_bmp, int new_x, int new_y) {
BMP* old_bmp = (BMP*) (UINTN) bgrt->image_address;
ACPI_BGRT bgrt0 = *bgrt;
InitBGRT(bgrt);
if (new_bmp) {
bgrt->image_address = (UINTN) new_bmp;
}
BMP* bmp = (BMP*) (UINTN) bgrt->image_address;
// Calculate the automatically centered position for the image.
int x_auto, y_auto;
if (GOP()) {
x_auto = max(0, ((int)GOP()->Mode->Info->HorizontalResolution - (int)bmp->width) / 2);
y_auto = max(0, ((int)GOP()->Mode->Info->VerticalResolution * 2/3 - (int)bmp->height) / 2);
} else {
x_auto = max(0, (int)bgrt0.image_offset_x + ((int)old_bmp->width - (int)bmp->width) / 2);
y_auto = max(0, (int)bgrt0.image_offset_y + ((int)old_bmp->height - (int)bmp->height) / 2);
}
// Set the position (manual, automatic, original).
bgrt->image_offset_x = SelectCoordinate(new_x, x_auto, bgrt0.image_offset_x);
bgrt->image_offset_y = SelectCoordinate(new_y, y_auto, bgrt0.image_offset_y);
Debug(L"HackBGRT: BMP at (%d, %d).\n", (int) bgrt->image_offset_x, (int) bgrt->image_offset_y);
bgrt->header.checksum = 0;
bgrt->header.checksum = CalculateAcpiChecksum(bgrt, sizeof(*bgrt));
}
/**
* Find the BGRT and optionally destroy it or create if missing.
* If action is REMOVE, all BGRT entries will be removed.
* If action is KEEP, the first BGRT entry will be returned.
* If action is REPLACE, the given BGRT entry will be stored in each XSDT.
*
* @param action The intended action.
* @param bgrt The BGRT, if action is REPLACE.
* @return Pointer to the BGRT, or 0 if not found (or destroyed).
*/
static ACPI_BGRT* FindBGRT(enum HackBGRT_action action) {
ACPI_20_RSDP* good_rsdp = 0;
ACPI_BGRT* bgrt = 0;
static ACPI_BGRT* HandleAcpiTables(enum HackBGRT_action action, ACPI_BGRT* bgrt) {
for (int i = 0; i < ST->NumberOfTableEntries; i++) {
EFI_GUID Acpi20TableGuid = ACPI_20_TABLE_GUID;
EFI_GUID* vendor_guid = &ST->ConfigurationTable[i].VendorGuid;
if (!CompareGuid(vendor_guid, &AcpiTableGuid) && !CompareGuid(vendor_guid, &Acpi20TableGuid)) {
continue;
}
EFI_CONFIGURATION_TABLE *ect = &ST->ConfigurationTable[i];
if (CompareMem(ect->VendorTable, "RSD PTR ", 8) != 0) {
ACPI_20_RSDP* rsdp = (ACPI_20_RSDP *) ST->ConfigurationTable[i].VendorTable;
if (CompareMem(rsdp->signature, "RSD PTR ", 8) != 0 || rsdp->revision < 2 || !VerifyAcpiRsdp2Checksums(rsdp)) {
continue;
}
ACPI_20_RSDP* rsdp = (ACPI_20_RSDP *)ect->VendorTable;
Debug(L"RSDP: revision = %d, OEM ID = %s\n", rsdp->revision, TmpStr(rsdp->oem_id, 6));
if (rsdp->revision < 2) {
Debug(L"* XSDT: N/A (revision < 2)\n");
continue;
}
ACPI_SDT_HEADER* xsdt = (ACPI_SDT_HEADER *) (UINTN) rsdp->xsdt_address;
if (!xsdt) {
Debug(L"* XSDT: N/A (null)\n");
if (!xsdt || CompareMem(xsdt->signature, "XSDT", 4) != 0 || !VerifyAcpiSdtChecksum(xsdt)) {
Debug(L"* XSDT: missing or invalid\n");
continue;
}
if (CompareMem(xsdt->signature, "XSDT", 4) != 0) {
Debug(L"* XSDT: N/A (invalid signature)\n");
continue;
}
good_rsdp = rsdp;
UINT64* entry_arr = (UINT64*)&xsdt[1];
UINT32 entry_arr_length = (xsdt->length - sizeof(*xsdt)) / sizeof(UINT64);
Debug(L"* XSDT: OEM ID = %s, entry count = %d\n", TmpStr(xsdt->oem_id, 6), entry_arr_length);
int bgrt_count = 0;
for (int j = 0; j < entry_arr_length; j++) {
ACPI_SDT_HEADER *entry = (ACPI_SDT_HEADER *)((UINTN)entry_arr[j]);
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));
if (CompareMem(entry->signature, "BGRT", 4) == 0) {
if (!bgrt && action != HackBGRT_REMOVE) {
bgrt = (void*) entry;
} else {
if (bgrt) {
Debug(L" -> Deleting; BGRT was already found!\n");
} else {
Debug(L" -> Deleting.\n");
switch (action) {
case HackBGRT_KEEP:
if (!bgrt) {
Debug(L" -> Returning this one for later use.\n");
bgrt = (ACPI_BGRT*) entry;
}
break;
case HackBGRT_REMOVE:
Debug(L" -> Deleting.\n");
for (int k = j+1; k < entry_arr_length; ++k) {
entry_arr[k-1] = entry_arr[k];
}
@@ -160,56 +188,36 @@ static ACPI_BGRT* FindBGRT(enum HackBGRT_action action) {
entry_arr[entry_arr_length] = 0;
xsdt->length -= sizeof(entry_arr[0]);
--j;
}
break;
case HackBGRT_REPLACE:
Debug(L" -> Replacing.\n");
entry_arr[j] = (UINTN) bgrt;
}
bgrt_count += 1;
}
}
if (action == HackBGRT_REMOVE) {
return 0;
}
if (!good_rsdp) {
Print(L"HackBGRT: RSDP or XSDT not found.\n");
return 0;
}
if (!bgrt) {
if (action == HackBGRT_KEEP) {
Print(L"HackBGRT: BGRT not found.\n");
return 0;
if (!bgrt_count && action == HackBGRT_REPLACE && bgrt) {
Debug(L" - Adding missing BGRT.\n");
xsdt = CreateXsdt(xsdt, entry_arr_length + 1);
entry_arr = (UINT64*)&xsdt[1];
entry_arr[entry_arr_length++] = (UINTN) bgrt;
rsdp->xsdt_address = (UINTN) xsdt;
SetAcpiRsdp2Checksums(rsdp);
}
Debug(L"HackBGRT: BGRT not found, creating.\n");
ACPI_20_RSDP* rsdp = good_rsdp;
ACPI_SDT_HEADER* xsdt0 = (ACPI_SDT_HEADER *) (UINTN) rsdp->xsdt_address;
ACPI_SDT_HEADER* xsdt = 0;
UINT32 xsdt_len = xsdt0->length + sizeof(UINT64);
BS->AllocatePool(EfiACPIReclaimMemory, xsdt_len, (void**)&xsdt);
BS->AllocatePool(EfiACPIReclaimMemory, sizeof(*bgrt), (void**)&bgrt);
if (!xsdt || !bgrt) {
Print(L"HackBGRT: Failed to allocate memory for XSDT and BGRT.\n");
return 0;
}
rsdp->xsdt_address = (UINTN) xsdt;
CopyMem(xsdt, xsdt0, xsdt0->length);
*(UINT64*)((char*)xsdt + xsdt->length) = (UINTN) bgrt;
xsdt->length = xsdt_len;
InitBGRT(bgrt);
SetAcpiSdtChecksum(xsdt);
}
return bgrt;
}
/**
* Load a bitmap or generate one, or return 0 if not applicable.
* Load a bitmap or generate a black one.
*
* @param action Tells what to do.
* @param root_dir The root directory for loading a BMP.
* @param path The BMP path within the root directory.
* @return The loaded BMP, or 0 if not needed or not available.
* @param path The BMP path within the root directory; NULL for a black BMP.
* @return The loaded BMP, or 0 if not available.
*/
static BMP* LoadBMP(enum HackBGRT_action action, EFI_FILE_HANDLE root_dir, const CHAR16* path) {
static BMP* LoadBMP(EFI_FILE_HANDLE root_dir, const CHAR16* path) {
BMP* bmp = 0;
if (action == HackBGRT_KEEP || action == HackBGRT_REMOVE) {
return 0;
}
if (action == HackBGRT_BLACK) {
if (!path) {
BS->AllocatePool(EfiBootServicesData, 58, (void**) &bmp);
if (!bmp) {
Print(L"HackBGRT: Failed to allocate a blank BMP!\n");
@@ -226,10 +234,6 @@ static BMP* LoadBMP(enum HackBGRT_action action, EFI_FILE_HANDLE root_dir, const
);
return bmp;
}
if (!path) {
Print(L"HackBGRT: Missing BMP path. REPORT THIS BUG!");
return 0;
}
Debug(L"HackBGRT: Loading %s.\n", path);
bmp = LoadFile(root_dir, path, 0);
if (!bmp) {
@@ -240,6 +244,85 @@ static BMP* LoadBMP(enum HackBGRT_action action, EFI_FILE_HANDLE root_dir, const
return bmp;
}
/**
* The main logic for BGRT modification.
*
* @param root_dir The root directory for loading a BMP.
*/
void HackBgrt(EFI_FILE_HANDLE root_dir) {
// REMOVE: simply delete all BGRT entries.
if (config.action == HackBGRT_REMOVE) {
HandleAcpiTables(config.action, 0);
return;
}
// KEEP/REPLACE: first get the old BGRT entry.
ACPI_BGRT* bgrt = HandleAcpiTables(HackBGRT_KEEP, 0);
// Get the old BMP and position, 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;
}
// Missing BGRT?
if (!bgrt) {
// Keep missing = do nothing.
if (config.action == HackBGRT_KEEP) {
return;
}
// Replace missing = allocate new.
BS->AllocatePool(EfiACPIReclaimMemory, sizeof(*bgrt), (void**)&bgrt);
if (!bgrt) {
Print(L"HackBGRT: 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));
// 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);
}
// No image = no need for BGRT.
if (!new_bmp) {
HandleAcpiTables(HackBGRT_REMOVE, 0);
return;
}
bgrt->image_address = (UINTN) new_bmp;
// 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);
}
// 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);
// Store this BGRT in the ACPI tables.
SetAcpiSdtChecksum(bgrt);
HandleAcpiTables(HackBGRT_REPLACE, bgrt);
}
/**
* The main program.
*/
@@ -269,34 +352,57 @@ EFI_STATUS EFIAPI EfiMain(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *ST_) {
}
Debug = config.debug ? Print : NullPrint;
BMP* new_bmp = LoadBMP(config.action, root_dir, config.image_path);
ACPI_BGRT* bgrt = FindBGRT(config.action);
if (bgrt) {
FillBGRT(bgrt, new_bmp, config.image_x, config.image_y);
}
SetResolution(config.resolution_x, config.resolution_y);
HackBgrt(root_dir);
EFI_HANDLE next_image_handle = 0;
if (!config.boot_path) {
Print(L"HackBGRT: Boot path not specified.\n");
goto fail;
} 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);
}
}
Debug(L"HackBGRT: Loading and booting %s.\n", config.boot_path);
EFI_DEVICE_PATH* boot_dp = FileDevicePath(image->DeviceHandle, (CHAR16*) config.boot_path);
EFI_HANDLE next_image_handle;
if (EFI_ERROR(BS->LoadImage(0, image_handle, boot_dp, 0, 0, &next_image_handle))) {
Print(L"HackBGRT: LoadImage for new image (%s) failed.\n", config.boot_path);
goto fail;
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);
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) {
return 0;
}
}
if (EFI_ERROR(BS->StartImage(next_image_handle, 0, 0))) {
Print(L"HackBGRT: StartImage for %s failed.\n", config.boot_path);
Print(L"HackBGRT: Failed to start %s.\n", config.boot_path);
goto fail;
}
Print(L"HackBGRT: Started %s. Why are we still here?!\n", config.boot_path);
goto fail;
fail: {
Print(L"HackBGRT has failed. Use parameter debug=1 for details.\nPress any key to exit.\n");
WaitKey();
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();
return 1;
}
}

37
src/types.c Normal file
View File

@@ -0,0 +1,37 @@
#include "types.h"
UINT8 SumBytes(const UINT8* arr, UINTN size) {
UINT8 sum = 0;
for (UINTN i = 0; i < size; ++i) {
sum += arr[i];
}
return sum;
}
int VerifyAcpiRsdp2Checksums(const void* data) {
const UINT8* arr = data;
UINTN size = *(const UINT32*)&arr[20];
return SumBytes(arr, 20) == 0 && SumBytes(arr, size) == 0;
}
void SetAcpiRsdp2Checksums(void* data) {
UINT8* arr = data;
UINTN size = *(const UINT32*)&arr[20];
arr[9] = 0;
arr[32] = 0;
arr[9] = -SumBytes(arr, 20);
arr[32] = -SumBytes(arr, size);
}
int VerifyAcpiSdtChecksum(const void* data) {
const UINT8* arr = data;
UINTN size = *(const UINT32*)&arr[4];
return SumBytes(arr, size) == 0;
}
void SetAcpiSdtChecksum(void* data) {
UINT8* arr = data;
UINTN size = *(const UINT32*)&arr[4];
arr[9] = 0;
arr[9] = -SumBytes(arr, size);
}

View File

@@ -1,5 +1,7 @@
#pragma once
#include <efi.h>
#pragma pack(push, 1)
/** RSDP (Root System Description Pointer) */
@@ -51,4 +53,35 @@ typedef struct {
UINT16 planes;
UINT16 bpp;
} BMP;
/**
* Verify the checksums of an ACPI RSDP version 2.
*
* @param data Pointer to the table.
* @return 1 if the checksum is correct, 0 otherwise.
*/
extern int VerifyAcpiRsdp2Checksums(const void* data);
/**
* Set the correct checksums of an ACPI RSDP version 2.
*
* @param data Pointer to the table.
*/
extern void SetAcpiRsdp2Checksums(void* data);
/**
* Verify the checksum of an ACPI SDT.
*
* @param data Pointer to the table.
* @return 1 if the checksum is correct, 0 otherwise.
*/
extern int VerifyAcpiSdtChecksum(const void* data);
/**
* Set the correct checksum for an ACPI SDT.
*
* @param data Pointer to the table.
*/
extern void SetAcpiSdtChecksum(void* data);
#pragma pack(pop)

View File

@@ -18,15 +18,6 @@ UINTN NullPrint(IN CHAR16 *fmt, ...) {
return 0;
}
UINT8 CalculateAcpiChecksum(void* data, UINTN size) {
UINT8 sum = 0;
UINT8* arr = data;
for (UINTN i = 0; i < size; ++i) {
sum += arr[i];
}
return 256 - sum;
}
const CHAR16* TrimLeft(const CHAR16* s) {
// Skip white-space and BOM.
while (s[0] == L'\xfeff' || s[0] == ' ' || s[0] == '\t') {
@@ -82,6 +73,13 @@ void WaitKey(void) {
WaitForSingleEvent(ST->ConIn->WaitForKey, 0);
}
EFI_INPUT_KEY ReadKey(void) {
WaitKey();
EFI_INPUT_KEY key = {0};
ST->ConIn->ReadKeyStroke(ST->ConIn, &key);
return key;
}
void* LoadFileWithPadding(EFI_FILE_HANDLE dir, const CHAR16* path, UINTN* size_ptr, UINTN padding) {
EFI_STATUS e;
EFI_FILE_HANDLE handle;
@@ -102,7 +100,9 @@ void* LoadFileWithPadding(EFI_FILE_HANDLE dir, const CHAR16* path, UINTN* size_p
return 0;
}
e = handle->Read(handle, &size, data);
*(UINT32*)((char*)data + size) = 0;
for (int i = 0; i < padding; ++i) {
*((char*)data + size + i) = 0;
}
handle->Close(handle);
if (EFI_ERROR(e)) {
FreePool(data);

View File

@@ -16,15 +16,6 @@ extern const CHAR16* TmpStr(CHAR8 *src, int length);
*/
extern UINTN NullPrint(IN CHAR16 *fmt, ...);
/**
* Calculate the checksum for an ACPI table.
*
* @param data Pointer to the table.
* @param size Table length in bytes.
* @return Checksum.
*/
extern UINT8 CalculateAcpiChecksum(void* data, UINTN size);
/**
* Return the greater of two numbers.
*/
@@ -32,6 +23,13 @@ static inline int max(int a, int b) {
return a > b ? a : b;
}
/**
* Return the smaller of two numbers.
*/
static inline int min(int a, int b) {
return a < b ? a : b;
}
/**
* Trim BOM, spaces and tabs from the beginning of a string.
*
@@ -83,10 +81,17 @@ extern void RandomSeed(UINT64 a, UINT64 b);
extern void RandomSeedAuto(void);
/**
* Wait for a key press.
* Wait for a key press. It will still remain in the buffer.
*/
extern void WaitKey(void);
/**
* Wait for a key press and read it.
*
* @return The pressed key.
*/
extern EFI_INPUT_KEY ReadKey(void);
/**
* Load a file, allocate some extra bytes as well.
*/

View File

@@ -1,9 +0,0 @@
@ECHO OFF
CD %~dp0
IF NOT EXIST install.bat (
ECHO The uninstaller needs install.bat!
EXIT /B 1
)
CALL install.bat uninstall