40 Commits

Author SHA1 Message Date
Lauri Kenttä
32643fab96 Update change log and tag v1.5.0 2017-09-30 22:41:00 +03:00
Lauri Kenttä
ecbca09419 Update README: clearer installation instructions 2017-09-30 22:41:00 +03:00
Lauri Kenttä
fa0f846f79 Implement rebooting to UEFI setup 2017-09-30 22:41:00 +03:00
Lauri Kenttä
b469b600ba Reorder the setup process a bit 2017-09-30 22:41:00 +03:00
Lauri Kenttä
f7fa54cfee Move Secure Boot handling to another function 2017-09-30 21:23:12 +03:00
Lauri Kenttä
e092c4768c Add throwable for graceful exiting 2017-09-30 16:41:25 +03:00
Lauri Kenttä
7ad4762a3d Improve ESP detection and move to another file 2017-09-30 16:40:17 +03:00
Lauri Kenttä
cea656631a Reorder some BootLoaderInfo code 2017-09-30 15:40:28 +03:00
Lauri Kenttä
1b6b17ec9a Move some helpers to SetupHelper class 2017-09-30 15:39:52 +03:00
Lauri Kenttä
91aad3b971 Makefile: Add variable for C# file list 2017-09-25 21:18:46 +03:00
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
19 changed files with 1268 additions and 306 deletions

4
.gitignore vendored Normal file
View File

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

66
CHANGELOG.md Normal file
View File

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

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,31 @@ 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)
FILES_CS = src/Setup.cs src/SetupHelper.cs src/Esp.cs src/Efi.cs
GIT_DESCRIBE = $(firstword $(shell git describe --tags) unknown)
CFLAGS += '-DGIT_DESCRIBE=L"$(GIT_DESCRIBE)"'
ZIPDIR = HackBGRT-$(GIT_DESCRIBE:v%=%)
ZIP = $(ZIPDIR).zip
.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: $(FILES_CS) $(FILES_C) $(FILES_H)
echo 'public class GIT_DESCRIBE { public static string data = "$(GIT_DESCRIBE)"; }' > $@
setup.exe: $(FILES_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 +42,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,23 +11,37 @@ 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.
* Installation for Windows with another boot loader (e.g. GRUB):
* Copy the mentioned files to `[EFI System Partition]\EFI\HackBGRT\`.
* Set `boot=\EFI\Microsoft\Boot\bootmgfw.efi` in `config.txt`.
* Point your boot loader to `\EFI\HackBGRT\bootx64.efi`.
* Installation for all operating systems:
* Copy the mentioned files to `[EFI System Partition]\EFI\HackBGRT\`.
* Set `boot=` to your preferred boot loader in `config.txt`.
* Set `\EFI\HackBGRT\bootx64.efi` as your default boot loader with `efibootmgr` or some other EFI boot manager tool.
* Make sure that Secure Boot is disabled, unless you know how to sign EFI applications.
### Windows installation
* Get the latest release from the Releases page.
* Start `setup.exe` and follow the instructions.
* You may need to manually disable Secure Boot and then retry.
* The installer will launch Notepad for modifying the configuration.
* If you need only one custom image, the defaults are fine.
* Otherwise, check out the examples in the configuration file.
* The installer will launch Paint for creating the image(s).
* You can create multiple images by using Save As.
* Be sure to always use the 24-bit BMP/DIB format.
* If Windows later restores the original boot loader, just reinstall.
* If you wish to change the image or other configuration, just reinstall.
### Multi-boot configurations
If you only need HackBGRT for Windows:
* Extract the latest release to `[EFI System Partition]\EFI\HackBGRT\`.
* Set `boot=\EFI\Microsoft\Boot\bootmgfw.efi` in `config.txt`.
* Point your boot loader to `\EFI\HackBGRT\bootx64.efi`.
If you need it for other systems as well:
* Extract the latest release to `[EFI System Partition]\EFI\HackBGRT\`.
* Set `boot=\EFI\your-actual-boot-loader.efi` in `config.txt`.
* Set `\EFI\HackBGRT\bootx64.efi` as your default boot loader with `efibootmgr` or some other EFI boot manager tool.
On 32-bit machines, use `bootia32.efi` instead of `bootx64.efi`.
## Configuration
@@ -41,6 +55,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

176
src/Efi.cs Normal file
View File

@@ -0,0 +1,176 @@
using System;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.Win32;
/**
* Methods for handling EFI variables.
*/
public class Efi {
[DllImport("kernel32.dll", SetLastError = true)]
private static extern UInt32 GetFirmwareEnvironmentVariableEx(string lpName, string lpGuid, [MarshalAs(UnmanagedType.LPArray)] byte[] pBuffer, UInt32 nSize, out UInt32 pdwAttributes);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern UInt32 SetFirmwareEnvironmentVariableEx(string lpName, string lpGuid, [MarshalAs(UnmanagedType.LPArray)] byte[] pBuffer, UInt32 nSize, UInt32 pdwAttributes);
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
private static extern bool AdjustTokenPrivileges(IntPtr htoken, bool disable, ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);
[DllImport("kernel32.dll", ExactSpelling = true)]
private static extern IntPtr GetCurrentProcess();
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
private static extern bool OpenProcessToken(IntPtr h, int acc, out IntPtr phtok);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hObject);
[StructLayout(LayoutKind.Sequential, Pack = 1)]
private struct TokPriv1Luid {
public int Count;
public long Luid;
public int Attr;
}
/**
* Information about an EFI variable.
*/
public class Variable {
public string name, guid;
public UInt32 attributes;
public byte[] data;
}
public const string EFI_GLOBAL_GUID = "{8be4df61-93ca-11d2-aa0d-00e098032b8c}";
/**
* Enable the privilege to access EFI variables.
*/
public static void EnablePrivilege() {
const int SE_PRIVILEGE_ENABLED = 0x00000002;
const int TOKEN_QUERY = 0x00000008;
const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
const string SE_SYSTEM_ENVIRONMENT_NAME = "SeSystemEnvironmentPrivilege";
IntPtr htoken = IntPtr.Zero;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, out htoken)) {
throw new Exception("EnablePrivilege: OpenProcessToken failed: " + Marshal.GetLastWin32Error());
}
try {
TokPriv1Luid tp;
tp.Count = 1;
tp.Luid = 0;
tp.Attr = SE_PRIVILEGE_ENABLED;
if (!LookupPrivilegeValue(null, SE_SYSTEM_ENVIRONMENT_NAME, ref tp.Luid)) {
throw new Exception("EnablePrivilege: LookupPrivilegeValue failed: " + Marshal.GetLastWin32Error());
}
if (!AdjustTokenPrivileges(htoken, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero)) {
throw new Exception("EnablePrivilege: AdjustTokenPrivileges failed: " + Marshal.GetLastWin32Error());
}
} finally {
CloseHandle(htoken);
}
}
/**
* Get an EFI variable.
*
* @param name Name of the EFI variable.
* @param guid GUID of the EFI variable.
* @return Information about the EFI variable.
*/
private static Variable GetVariable(string name, string guid = EFI_GLOBAL_GUID) {
Variable result = new Variable();
result.name = name;
result.guid = guid;
result.data = null;
result.attributes = 0;
for (UInt32 i = 4096; i <= 1024*1024; i *= 2) {
byte[] buf = new byte[i];
UInt32 len = GetFirmwareEnvironmentVariableEx(name, guid, buf, (UInt32) buf.Length, out result.attributes);
if (len == buf.Length) {
continue;
}
if (len > 0 || Marshal.GetLastWin32Error() == 0) {
result.data = new byte[len];
Array.Copy(buf, 0, result.data, 0, len);
return result;
}
switch (len != 0 ? 0 : Marshal.GetLastWin32Error()) {
case 203:
// Not found.
return result;
case 87:
throw new Exception("GetVariable: Invalid parameter");
case 1314:
throw new Exception("GetVariable: Privilege not held");
default:
throw new Exception("GetVariable: error " + Marshal.GetLastWin32Error());
}
}
throw new Exception("GetFirmwareEnvironmentVariable: too big data");
}
/**
* Set an EFI variable.
*
* @param v Information of the variable.
*/
private static void SetVariable(Variable v) {
UInt32 r = SetFirmwareEnvironmentVariableEx(v.name, v.guid, v.data, (UInt32) v.data.Length, v.attributes);
if (r == 0) {
switch (Marshal.GetLastWin32Error()) {
case 87:
throw new Exception("GetVariable: Invalid parameter");
case 1314:
throw new Exception("GetVariable: Privilege not held");
default:
throw new Exception("GetVariable: error " + Marshal.GetLastWin32Error());
}
}
}
/**
* Check if Secure Boot is enabled.
*
* @return 0 for disabled, 1 for enabled, other for unknown.
*/
public static int GetSecureBootStatus() {
// GetVariable("SecureBoot") reports always 1 (on Lenovo E335).
// Windows registry seems to work better, though.
try {
return (int) Registry.GetValue(
"HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\SecureBoot\\State",
"UEFISecureBootEnabled",
-1
);
} catch {
return -1;
}
}
/**
* Check if it's possible to reboot to EFI setup.
*
* @return True, if it's possible to reboot to EFI setup.
*/
public static bool CanBootToFW() {
try {
Variable tmp = GetVariable("OsIndicationsSupported");
return tmp.data != null && (tmp.data[0] & 1) != 0;
} catch {
return false;
}
}
/**
* Mark that the next reboot should go to EFI setup.
*/
public static void SetBootToFW() {
Variable tmp = GetVariable("OsIndications");
tmp.data[0] |= 1;
SetVariable(tmp);
}
}

118
src/Esp.cs Normal file
View File

@@ -0,0 +1,118 @@
using System;
using System.IO;
using System.Text.RegularExpressions;
/**
* EFI System Partition mounter.
*/
public sealed class Esp {
/** The singleton instance of this class, if Path is mounted. */
private static Esp MountInstance;
/** EFI System Partition location, internal variable. */
private static string RealPath;
/** EFI System Partition location, read-only. */
public static string Path {
get {
return RealPath;
}
}
/**
* Constructor: do nothing.
*/
private Esp() {
}
/**
* Destructor: unmount.
*/
~Esp() {
if (this == MountInstance) {
Unmount();
}
}
/**
* Try to find ESP at a path.
*
* @param tryPath The new path to try.
* @param requireMsLoader Look for MS boot loader specifically?
* @return true if the path was given and seems valid, false otherwise.
*/
public static bool TryPath(string tryPath, bool requireMsLoader = true) {
if (MountInstance != null && Path != null) {
Unmount();
}
RealPath = tryPath;
if (Path != null && Path != "") {
if (File.Exists(Path + "\\EFI\\Microsoft\\Boot\\bootmgfw.efi")) {
return true;
}
if (!requireMsLoader && Directory.Exists(Path + "\\EFI")) {
return true;
}
}
RealPath = null;
return false;
}
/**
* Find the EFI System Partition, if it's already mounted.
*
* @return true if the drive vas found.
*/
public static bool Find() {
if (MountInstance != null) {
return true;
}
try {
// Match "The EFI System Partition is mounted at E:\" with some language support.
var re = new Regex(" EFI[^\n]*(?:\n[ \t]*)?([A-Z]:)\\\\");
if (TryPath(re.Match(Setup.Execute("mountvol", "", false)).Groups[1].Captures[0].Value)) {
return true;
}
} catch {
}
for (char c = 'A'; c <= 'Z'; ++c) {
if (TryPath(c + ":")) {
return true;
}
}
return false;
}
/**
* Mount the EFI System Partition.
*
* @return true if the drive was mounted, false otherwise.
*/
public static bool Mount() {
if (MountInstance != null) {
return true;
}
for (char c = 'A'; c <= 'Z'; ++c) {
if (Setup.Execute("mountvol", c + ": /S", true) != null) {
MountInstance = new Esp();
if (TryPath(c + ":", false)) {
return true;
} else {
throw new Exception("Mounted ESP at " + c + ": but it seems to be invalid!");
}
}
}
return false;
}
/**
* Unmount the EFI System Partition, if necessary.
*/
private static void Unmount() {
if (MountInstance != null) {
Setup.Execute("mountvol", Path + " /D", true);
RealPath = null;
MountInstance = null;
}
}
}

345
src/Setup.cs Normal file
View File

@@ -0,0 +1,345 @@
using System;
using System.IO;
/**
* HackBGRT Setup.
*/
public class Setup: SetupHelper {
/**
* The custom exception class for expected exceptions.
*/
public class SetupException: Exception {
public SetupException(string msg): base(msg) {
}
}
/**
* A custom exception class for simply exiting the application.
*/
public class ExitSetup: Exception {
public readonly int Code;
public ExitSetup(int code) {
Code = code;
}
}
/**
* Find or mount or manually choose the EFI System Partition.
*/
public static void InitEspPath() {
if (!Esp.Find() && !Esp.Mount()) {
Console.WriteLine("EFI System Partition was not found.");
Console.WriteLine("Press enter to exit, or give ESP path here: ");
string s = Console.ReadLine();
if (s.Length == 1) {
s = s + ":";
}
if (!Esp.TryPath(s, true)) {
Console.WriteLine("That's not a valid ESP path!");
}
}
if (Esp.Path == null) {
throw new SetupException("EFI System Partition was not found.");
}
Console.WriteLine("EFI System Partition location is " + Esp.Path);
}
/**
* Boot loader type (MS/HackBGRT).
*/
public enum BootLoaderType {
Unknown,
MS,
Own
}
/**
* Information about a boot loader: type and architecture.
*/
public class BootLoaderInfo {
public string Path;
public bool Exists;
public BootLoaderType Type;
public string Arch;
/**
* Constructor. Also recognizes the boot loader.
*/
public BootLoaderInfo(string path) {
Path = path;
Detect();
}
/**
* Recognize the boot loader type (MS/HackBGRT) and architecture.
*/
public void Detect() {
Exists = false;
Type = BootLoaderType.Unknown;
Arch = null;
try {
byte[] data = File.ReadAllBytes(Path);
Exists = true;
string tmp = System.Text.Encoding.ASCII.GetString(data);
if (tmp.IndexOf("HackBGRT") >= 0) {
Type = BootLoaderType.Own;
} else if (tmp.IndexOf("Microsoft Corporation") >= 0) {
Type = BootLoaderType.MS;
}
switch (BitConverter.ToUInt16(data, BitConverter.ToInt32(data, 0x3c) + 4)) {
case 0x014c: Arch = "ia32"; break;
case 0x0200: Arch = "ia64"; break;
case 0x8664: Arch = "x64"; break;
}
} catch {
}
}
/**
* Replace this boot loader with another.
*
* @param other The new boot loader.
* @return true if the replacement was successful.
*/
public bool ReplaceWith(BootLoaderInfo other) {
if (!other.Exists || !Copy(other.Path, Path)) {
return false;
}
Exists = other.Exists;
Type = other.Type;
Arch = other.Arch;
return true;
}
}
/**
* Install files and replace the MsLoader with our own.
*/
protected void Install() {
try {
if (!Directory.Exists(Destination)) {
Directory.CreateDirectory(Destination);
}
} catch {
throw new SetupException("Failed to copy files to ESP!");
}
if (MsLoader.Type == BootLoaderType.MS) {
if (!MsLoaderBackup.ReplaceWith(MsLoader)) {
throw new SetupException("Failed to backup MS boot loader!");
}
}
if (MsLoaderBackup.Type != BootLoaderType.MS) {
// Duplicate check, but better to be sure...
throw new SetupException("Failed to detect MS boot loader!");
}
if (!NewLoader.ReplaceWith(NewLoaderSource)) {
throw new SetupException("Couldn't copy new HackBGRT to ESP.");
}
if (!File.Exists(Config)) {
Copy(Source + "\\config.txt", Config);
}
if (!File.Exists(Splash)) {
Copy(Source + "\\splash.bmp", Splash);
}
Configure();
if (!MsLoader.ReplaceWith(NewLoader)) {
MsLoader.ReplaceWith(MsLoaderBackup);
throw new SetupException("Couldn't copy new HackBGRT over the MS loader (bootmgfw.efi).");
}
Console.WriteLine("HackBGRT is now installed.");
}
/**
* Configure HackBGRT.
*/
protected void Configure() {
// Open config.txt in notepad.
Console.WriteLine("Check the configuration in " + Config + ".");
Console.WriteLine(" - Use the supplied config.txt as reference.");
Console.WriteLine(" - Be sure to check for any format changes if updating!");
try {
StartProcess("notepad", Config).WaitForExit();
} catch {
Console.WriteLine("Editing config.txt with notepad failed!");
}
Console.WriteLine();
// Open splash.bmp in mspaint.
Console.WriteLine("Draw or copy your preferred image to " + Splash + ".");
try {
StartProcess("mspaint", Splash).WaitForExit();
} catch {
Console.WriteLine("Editing splash.bmp with mspaint failed!");
}
Console.WriteLine();
}
/**
* Disable HackBGRT, restore the original boot loader.
*/
protected void Disable() {
if (MsLoader.Type != BootLoaderType.MS) {
if (!MsLoader.ReplaceWith(MsLoaderBackup)) {
throw new SetupException("Couldn't restore the old MS loader.");
}
}
Console.WriteLine(MsLoader.Path + " has been restored.");
}
/**
* Uninstall HackBGRT completely.
*/
protected void Uninstall() {
try {
Directory.Delete(Destination, true);
Console.WriteLine("HackBGRT has been removed.");
} catch {
throw new SetupException("The directory " + Destination + " couldn't be removed.");
}
}
/**
* Check Secure Boot status and inform the user.
*/
public static void HandleSecureBoot() {
int secureBoot = Efi.GetSecureBootStatus();
if (secureBoot == 0) {
Console.WriteLine("Secure Boot is disabled, good!");
} else {
if (secureBoot == 1) {
Console.WriteLine("Secure Boot is probably enabled.");
} else {
Console.WriteLine("Secure Boot status could not be determined.");
}
Console.WriteLine("It's very important to disable Secure Boot before installing.");
Console.WriteLine("Otherwise your machine may become unbootable.");
Console.WriteLine("Choose action (press a key):");
bool canBootToFW = Efi.CanBootToFW();
if (canBootToFW) {
Console.WriteLine(" S = Enter EFI Setup to disable Secure Boot manually; requires reboot!");
}
Console.WriteLine(" I = Install anyway; THIS MAY BE DANGEROUS!");
Console.WriteLine(" C = Cancel");
var k = Console.ReadKey().Key;
Console.WriteLine();
if (k == ConsoleKey.I) {
Console.WriteLine("Continuing. THIS MAY BE DANGEROUS!");
} else if (canBootToFW && k == ConsoleKey.S) {
Efi.SetBootToFW();
Console.WriteLine();
Console.WriteLine("Reboot your computer. You will then automatically enter the UEFI setup.");
Console.WriteLine("Find and disable Secure Boot, then save and exit, then run this installer.");
Console.WriteLine("Press R to reboot now, other key to exit.");
var k2 = Console.ReadKey().Key;
Console.WriteLine();
if (k2 == ConsoleKey.R) {
StartProcess("shutdown", "-f -r -t 1");
}
Environment.Exit(0);
throw new ExitSetup(0);
} else {
Console.WriteLine("Aborting because of Secure Boot.");
throw new ExitSetup(1);
}
}
}
protected string Source, Destination, Config, Splash;
protected BootLoaderInfo MsLoader, MsLoaderBackup, NewLoader, NewLoaderSource;
protected string EfiArch;
/**
* Initialize information for the Setup.
*/
protected Setup(string source, string esp) {
Source = source;
Destination = esp + "\\EFI\\HackBGRT";
Config = Destination + "\\config.txt";
Splash = Destination + "\\splash.bmp";
MsLoaderBackup = new BootLoaderInfo(Destination + "\\bootmgfw-original.efi");
MsLoader = new BootLoaderInfo(esp + "\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
if (MsLoader.Type == BootLoaderType.MS) {
EfiArch = MsLoader.Arch;
} else if (MsLoaderBackup.Type == BootLoaderType.MS) {
EfiArch = MsLoaderBackup.Arch;
} else {
throw new SetupException("Failed to detect MS boot loader!");
}
string loaderName = "boot" + EfiArch + ".efi";
NewLoaderSource = new BootLoaderInfo(Source + "\\" + loaderName);
NewLoader = new BootLoaderInfo(Destination + "\\" + loaderName);
if (!NewLoaderSource.Exists) {
throw new SetupException("Couldn't find required files! Missing: " + NewLoaderSource.Path);
}
}
/**
* Ask for user's choice and install/uninstall.
*/
protected void HandleUserAction() {
bool isEnabled = MsLoader.Type == BootLoaderType.Own;
bool isInstalled = NewLoader.Type == BootLoaderType.Own;
if (isEnabled) {
Console.WriteLine("HackBGRT is currently enabled.");
} else {
if (isInstalled) {
Console.WriteLine("HackBGRT is currently disabled.");
} else {
Console.WriteLine("HackBGRT is currently not installed.");
}
}
Console.WriteLine();
Console.WriteLine("Choose action (press a key):");
Console.WriteLine(" I = install, upgrade, repair, modify...");
if (isEnabled) {
Console.WriteLine(" D = disable, restore the original boot loader");
}
if (isInstalled) {
Console.WriteLine(" R = remove completely; delete all HackBGRT files and images");
}
Console.WriteLine(" C = cancel");
var k = Console.ReadKey().Key;
Console.WriteLine();
if (k == ConsoleKey.I) {
Install();
} else if ((isEnabled && k == ConsoleKey.D) || (isInstalled && k == ConsoleKey.R)) {
if (isEnabled) {
Disable();
}
if (k == ConsoleKey.R) {
Uninstall(); // NOTICE: It's imperative to Disable() first!
}
} else if (k == ConsoleKey.C) {
throw new ExitSetup(1);
} else {
throw new SetupException("Invalid choice!");
}
}
/**
* Run the setup.
*
* @param source Path to the installation files.
*/
public static void RunSetup(string source) {
try {
InitEspPath();
HandleSecureBoot();
var s = new Setup(source, Esp.Path);
s.HandleUserAction();
} catch (ExitSetup e) {
Environment.ExitCode = e.Code;
} 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;
}
Console.WriteLine("Press any key to quit.");
Console.ReadKey();
}
}

104
src/SetupHelper.cs Normal file
View File

@@ -0,0 +1,104 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Security.Principal;
using System.Linq;
/**
* Helper functions.
*/
public class SetupHelper {
/**
* Start another process.
*
* @param app Path to the application.
* @param args The argument string.
* @return The started process.
*/
public static Process StartProcess(string app, string args) {
try {
var info = new ProcessStartInfo(app, args);
info.UseShellExecute = false;
return Process.Start(info);
} catch {
return null;
}
}
/**
* Execute another process, return the output.
*
* @param app Path to the application.
* @param args The argument string.
* @param nullOnFail If set, return null if the program exits with a code other than 0, even if there was some output.
* @return The output, or null if the execution failed.
*/
public static string Execute(string app, string args, bool nullOnFail) {
try {
var info = new ProcessStartInfo(app, args);
info.UseShellExecute = false;
info.RedirectStandardOutput = true;
var p = Process.Start(info);
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
return (nullOnFail && p.ExitCode != 0) ? null : output;
} catch {
return null;
}
}
/**
* Copy a file, overwrite by default.
*
* @param src The source path.
* @param dest The destination path.
* @return True, if the file was successfully copied.
*/
public static bool Copy(string src, string dest) {
try {
File.Copy(src, dest, true);
return true;
} catch {
return false;
}
}
/**
* The Main program.
*
* @param args The arguments.
*/
public static void Main(string[] args) {
#if GIT_DESCRIBE
Console.WriteLine("HackBGRT installer version: {0}", GIT_DESCRIBE.data);
#else
Console.WriteLine("HackBGRT installer version: unknown; not an official release?");
#endif
var self = Assembly.GetExecutingAssembly().Location;
try {
// Relaunch as admin, if needed.
var id = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(id);
var admin = WindowsBuiltInRole.Administrator;
if (!principal.IsInRole(admin) && !args.Contains("no-elevate")) {
ProcessStartInfo startInfo = new ProcessStartInfo(self);
startInfo.Arguments = "no-elevate";
startInfo.Verb = "runas";
startInfo.UseShellExecute = true;
Process p = Process.Start(startInfo);
p.WaitForExit();
Environment.ExitCode = p.ExitCode;
return;
}
Efi.EnablePrivilege();
} catch {
Console.WriteLine("This installer needs to be run as administrator!");
Console.WriteLine("Press any key to quit.");
Console.ReadKey();
Environment.ExitCode = 1;
return;
}
Setup.RunSetup(Path.GetDirectoryName(self));
}
}

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