Gather log during boot

This commit is contained in:
Lauri Kenttä
2023-11-17 18:29:51 +02:00
parent e93ed54cb2
commit db934099df
9 changed files with 141 additions and 67 deletions

View File

@@ -48,6 +48,7 @@ Instructions for enrolling the certificate (if it's possible at all) depend on y
* `allow-bad-loader` ignore bad boot loader configuration in subsequent commands.
* `disable` run all relevant `disable-*` commands.
* `uninstall` disable and remove completely.
* `show-boot-log` show the debug log collected during boot (if `log=1` is set in `config.txt`).
* For example, run `setup.exe batch install allow-secure-boot enable-overwrite` to copy files and overwrite the MS boot loader regardless of Secure Boot status.
### Multi-boot configurations

View File

@@ -38,6 +38,11 @@ image= y=-200 path=\EFI\HackBGRT\splash.bmp
# Preferred resolution. Use 0x0 for maximum and -1x-1 for original.
resolution=0x0
# Logging (0 for disabled, 1 for enabled).
# When logging is enabled, setup.exe can show debug information about the current boot.
# The log might occupy a few kilobytes of RAM.
log=1
# Debug mode (0 for disabled, 1 for enabled).
# Shows debug information and prompts for keypress before booting.
debug=0

View File

@@ -139,8 +139,19 @@ public class Efi {
}
}
/**
* GUID of the global EFI variables.
*/
public const string EFI_GLOBAL_GUID = "{8be4df61-93ca-11d2-aa0d-00e098032b8c}";
/**
* GUID for HackBGRT EFI variables.
*/
public const string EFI_HACKBGRT_GUID = "{03c64761-075f-4dba-abfb-2ed89e18b236}";
/**
* Directory containing EFI variables in Linux.
*/
public const string LinuxEfiDir = "/sys/firmware/efi/efivars";
/**
@@ -471,6 +482,18 @@ public class Efi {
}
}
/**
* Retrieve HackBGRT log collected during boot.
*/
public static string GetHackBGRTLog() {
try {
var log = GetVariable("HackBGRTLog", EFI_HACKBGRT_GUID);
return new string(BytesToUInt16s(log.Data).Select(i => (char)i).ToArray());
} catch (Exception e) {
return $"Log not found: {e.ToString()}";
}
}
/**
* Log the BGRT table (for debugging).
*/

View File

@@ -69,6 +69,7 @@ public class Setup {
"disable",
"uninstall",
"boot-to-fw",
"show-boot-log",
};
/** @var The target directory. */
@@ -751,6 +752,7 @@ public class Setup {
WriteLine(" B = boot to UEFI setup");
WriteLine(" - lets you disable Secure Boot");
WriteLine(" - lets you move HackBGRT before Windows in boot order");
WriteLine(" L = show boot log (what HackBGRT did during boot)");
WriteLine(" C = cancel");
var k = Console.ReadKey().Key;
@@ -773,6 +775,8 @@ public class Setup {
RunPrivilegedActions(new string[] { "uninstall" });
} else if (k == ConsoleKey.B) {
RunPrivilegedActions(new string[] { "boot-to-fw" });
} else if (k == ConsoleKey.L) {
RunPrivilegedActions(new string[] { "show-boot-log" });
} else if (k == ConsoleKey.C) {
throw new ExitSetup(1);
} else {
@@ -806,6 +810,8 @@ public class Setup {
InitEspPath();
InitEspInfo();
var bootLog = $"\n--- BOOT LOG START ---\n{Efi.GetHackBGRTLog()}\n--- BOOT LOG END ---";
Setup.Log(bootLog);
Efi.LogBGRT();
Efi.LogBootEntries();
if (GetBootTime() is DateTime bootTime) {
@@ -869,6 +875,8 @@ public class Setup {
Uninstall();
} else if (arg == "boot-to-fw") {
BootToFW();
} else if (arg == "show-boot-log") {
WriteLine(bootLog);
} else {
throw new SetupException($"Invalid action: '{arg}'!");
}
@@ -934,7 +942,7 @@ public class Setup {
WriteLine("This was a dry run, your system was not actually modified.");
}
if (!Batch) {
WriteLine("If you need to report a bug, please include the setup.log file.");
WriteLine("If you need to report a bug,\n - run this setup again with menu option L (show-boot-log)\n - then include the setup.log file with your report.");
WriteLine("Press any key to quit.");
Console.ReadKey();
}

View File

@@ -8,7 +8,7 @@ BOOLEAN ReadConfigFile(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir,
UINTN data_bytes = 0;
data = LoadFileWithPadding(root_dir, path, &data_bytes, 4);
if (!data) {
Print(L"HackBGRT: Failed to load configuration (%s)!\n", path);
Log(1, L"HackBGRT: Failed to load configuration (%s)!\n", path);
return FALSE;
}
CHAR16* str;
@@ -74,9 +74,7 @@ static void SetBMPWithRandom(struct HackBGRT_config* config, int weight, enum Ha
config->image_weight_sum += weight;
UINT32 random = Random();
UINT32 limit = 0xfffffffful / config->image_weight_sum * weight;
if (config->debug) {
Print(L"HackBGRT: weight %d, action %d, x %d, y %d, o %d, path %s, random = %08x, limit = %08x\n", weight, action, x, y, o, path, random, limit);
}
Log(config->debug, L"HackBGRT: n=%d, action=%d, x=%d, y=%d, o=%d, path=%s, random = %08x, limit = %08x\n", weight, action, x, y, o, path, random, limit);
if (!config->image_weight_sum || random <= limit) {
config->action = action;
config->image_path = path;
@@ -112,7 +110,7 @@ static void ReadConfigImage(struct HackBGRT_config* config, const CHAR16* line)
} else if (StrStr(line, L"keep")) {
action = HackBGRT_KEEP;
} else {
Print(L"HackBGRT: Invalid image line: %s\n", line);
Log(1, L"HackBGRT: Invalid image line: %s\n", line);
return;
}
int weight = n && (!f || n < f) ? Atoi(n) : 1;
@@ -129,7 +127,7 @@ static void ReadConfigResolution(struct HackBGRT_config* config, const CHAR16* l
config->resolution_x = *x == '-' ? -(int)Atoi(x+1) : (int)Atoi(x);
config->resolution_y = *y == '-' ? -(int)Atoi(y+1) : (int)Atoi(y);
} else {
Print(L"HackBGRT: Invalid resolution line: %s\n", line);
Log(1, L"HackBGRT: Invalid resolution line: %s\n", line);
}
}
@@ -143,6 +141,10 @@ void ReadConfigLine(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, co
config->debug = (StrCmp(line, L"debug=1") == 0);
return;
}
if (StrnCmp(line, L"log=", 4) == 0) {
config->log = (StrCmp(line, L"log=1") == 0);
return;
}
if (StrnCmp(line, L"image=", 6) == 0) {
ReadConfigImage(config, line + 6);
return;
@@ -159,5 +161,5 @@ void ReadConfigLine(struct HackBGRT_config* config, EFI_FILE_HANDLE root_dir, co
ReadConfigResolution(config, line + 11);
return;
}
Print(L"Unknown configuration directive: %s\n", line);
Log(1, L"Unknown configuration directive: %s\n", line);
}

View File

@@ -21,7 +21,7 @@ enum HackBGRT_coordinate {
* The configuration.
*/
struct HackBGRT_config {
int debug;
int debug, log;
enum HackBGRT_action action;
const CHAR16* image_path;
int image_x;

View File

@@ -6,20 +6,20 @@
#include "util.h"
/**
* The Print function signature.
* The version.
*/
typedef UINTN print_t(IN CONST CHAR16 *fmt, ...);
/**
* The function for debug printing; either Print or NullPrint.
*/
print_t* Debug = NullPrint;
#ifdef GIT_DESCRIBE_W
const CHAR16 version[] = GIT_DESCRIBE_W;
#else
const CHAR16 version[] = L"unknown; not an official release?";
#endif
/**
* The configuration.
*/
static struct HackBGRT_config config = {
.action = HackBGRT_KEEP
.log = 1,
.action = HackBGRT_KEEP,
};
/**
@@ -45,7 +45,7 @@ static void SetResolution(int w, int h) {
if (!gop) {
config.old_resolution_x = config.resolution_x = 0;
config.old_resolution_y = config.resolution_y = 0;
Debug(L"GOP not found!\n");
Log(config.debug, L"GOP not found!\n");
return;
}
UINTN best_i = gop->Mode->Mode;
@@ -54,7 +54,7 @@ static void SetResolution(int w, int h) {
w = (w <= 0 ? w < 0 ? best_w : 999999 : w);
h = (h <= 0 ? h < 0 ? best_h : 999999 : h);
Debug(L"Looking for resolution %dx%d...\n", w, h);
Log(config.debug, L"Looking for resolution %dx%d...\n", w, h);
for (UINT32 i = gop->Mode->MaxMode; i--;) {
int new_w = 0, new_h = 0;
@@ -87,7 +87,7 @@ static void SetResolution(int w, int h) {
best_h = new_h;
best_i = i;
}
Debug(L"Found resolution %dx%d.\n", best_w, best_h);
Log(config.debug, L"Found resolution %dx%d.\n", best_w, best_h);
config.resolution_x = best_w;
config.resolution_y = best_h;
if (best_i != gop->Mode->Mode) {
@@ -107,7 +107,7 @@ ACPI_SDT_HEADER* CreateXsdt(ACPI_SDT_HEADER* xsdt0, UINTN entries) {
UINT32 xsdt_len = sizeof(ACPI_SDT_HEADER) + entries * sizeof(UINT64);
BS->AllocatePool(EfiACPIReclaimMemory, xsdt_len, (void**)&xsdt);
if (!xsdt) {
Print(L"HackBGRT: Failed to allocate memory for XSDT.\n");
Log(1, L"HackBGRT: Failed to allocate memory for XSDT.\n");
return 0;
}
ZeroMem(xsdt, xsdt_len);
@@ -139,17 +139,17 @@ static ACPI_BGRT* HandleAcpiTables(enum HackBGRT_action action, ACPI_BGRT* bgrt)
if (CompareMem(rsdp->signature, "RSD PTR ", 8) != 0 || rsdp->revision < 2 || !VerifyAcpiRsdp2Checksums(rsdp)) {
continue;
}
Debug(L"RSDP @%x: revision = %d, OEM ID = %s\n", (UINTN)rsdp, rsdp->revision, TmpStr(rsdp->oem_id, 6));
Log(config.debug, L"RSDP @%x: revision = %d, OEM ID = %s\n", (UINTN)rsdp, rsdp->revision, TmpStr(rsdp->oem_id, 6));
ACPI_SDT_HEADER* xsdt = (ACPI_SDT_HEADER *) (UINTN) rsdp->xsdt_address;
if (!xsdt || CompareMem(xsdt->signature, "XSDT", 4) != 0 || !VerifyAcpiSdtChecksum(xsdt)) {
Debug(L"* XSDT: missing or invalid\n");
Log(config.debug, L"* XSDT: missing or invalid\n");
continue;
}
UINT64* entry_arr = (UINT64*)&xsdt[1];
UINT32 entry_arr_length = (xsdt->length - sizeof(*xsdt)) / sizeof(UINT64);
Debug(L"* XSDT @%x: OEM ID = %s, entry count = %d\n", (UINTN)xsdt, TmpStr(xsdt->oem_id, 6), entry_arr_length);
Log(config.debug, L"* XSDT @%x: OEM ID = %s, entry count = %d\n", (UINTN)xsdt, TmpStr(xsdt->oem_id, 6), entry_arr_length);
int bgrt_count = 0;
for (int j = 0; j < entry_arr_length; j++) {
@@ -157,16 +157,16 @@ static ACPI_BGRT* HandleAcpiTables(enum HackBGRT_action action, ACPI_BGRT* bgrt)
if (CompareMem(entry->signature, "BGRT", 4) != 0) {
continue;
}
Debug(L" - ACPI table @%x: %s, revision = %d, OEM ID = %s\n", (UINTN)entry, TmpStr(entry->signature, 4), entry->revision, TmpStr(entry->oem_id, 6));
Log(config.debug, L" - ACPI table @%x: %s, revision = %d, OEM ID = %s\n", (UINTN)entry, TmpStr(entry->signature, 4), entry->revision, TmpStr(entry->oem_id, 6));
switch (action) {
case HackBGRT_KEEP:
if (!bgrt) {
Debug(L" -> Returning this one for later use.\n");
Log(config.debug, L" -> Returning this one for later use.\n");
bgrt = (ACPI_BGRT*) entry;
}
break;
case HackBGRT_REMOVE:
Debug(L" -> Deleting.\n");
Log(config.debug, L" -> Deleting.\n");
for (int k = j+1; k < entry_arr_length; ++k) {
entry_arr[k-1] = entry_arr[k];
}
@@ -176,13 +176,13 @@ static ACPI_BGRT* HandleAcpiTables(enum HackBGRT_action action, ACPI_BGRT* bgrt)
--j;
break;
case HackBGRT_REPLACE:
Debug(L" -> Replacing.\n");
Log(config.debug, L" -> Replacing.\n");
entry_arr[j] = (UINTN) bgrt;
}
bgrt_count += 1;
}
if (!bgrt_count && action == HackBGRT_REPLACE && bgrt) {
Debug(L" - Adding missing BGRT.\n");
Log(config.debug, L" - Adding missing BGRT.\n");
xsdt = CreateXsdt(xsdt, entry_arr_length + 1);
entry_arr = (UINT64*)&xsdt[1];
entry_arr[entry_arr_length++] = (UINTN) bgrt;
@@ -208,7 +208,7 @@ static BMP* MakeBMP(int w, int h, UINT8 r, UINT8 g, UINT8 b) {
BMP* bmp = 0;
BS->AllocatePool(EfiBootServicesData, 54 + w * h * 4, (void**) &bmp);
if (!bmp) {
Print(L"HackBGRT: Failed to allocate a blank BMP!\n");
Log(1, L"HackBGRT: Failed to allocate a blank BMP!\n");
BS->Stall(1000000);
return 0;
}
@@ -243,7 +243,7 @@ static BMP* LoadBMP(EFI_FILE_HANDLE root_dir, const CHAR16* path) {
if (!path) {
return MakeBMP(1, 1, 0, 0, 0); // empty path = black image
}
Debug(L"HackBGRT: Loading %s.\n", path);
Log(config.debug, L"HackBGRT: Loading %s.\n", path);
UINTN size = 0;
BMP* bmp = LoadFile(root_dir, path, &size);
if (bmp) {
@@ -251,9 +251,9 @@ static BMP* LoadBMP(EFI_FILE_HANDLE root_dir, const CHAR16* path) {
return bmp;
}
FreePool(bmp);
Print(L"HackBGRT: Invalid BMP (%s)!\n", path);
Log(1, L"HackBGRT: Invalid BMP (%s)!\n", path);
} else {
Print(L"HackBGRT: Failed to load BMP (%s)!\n", path);
Log(1, L"HackBGRT: Failed to load BMP (%s)!\n", path);
}
BS->Stall(1000000);
return MakeBMP(16, 16, 255, 0, 0); // error = red image
@@ -321,7 +321,7 @@ void HackBgrt(EFI_FILE_HANDLE root_dir) {
// Replace missing = allocate new.
BS->AllocatePool(EfiACPIReclaimMemory, sizeof(*bgrt), (void**)&bgrt);
if (!bgrt) {
Print(L"HackBGRT: Failed to allocate memory for BGRT.\n");
Log(1, L"HackBGRT: Failed to allocate memory for BGRT.\n");
return;
}
}
@@ -373,8 +373,8 @@ void HackBgrt(EFI_FILE_HANDLE root_dir) {
bgrt->image_offset_x = max(0, min(max_x, new_x + (new_reso_x - new_bmp->width) / 2));
bgrt->image_offset_y = max(0, min(max_y, new_y + (new_reso_y - new_bmp->height) / 2));
Debug(
L"HackBGRT: BMP at (%d, %d), center (%d, %d), resolution (%d, %d) with orientation %d applied.\n",
Log(config.debug,
L"HackBGRT: BMP at (%d, %d), center (%d, %d), resolution (%d, %d), orientation %d.\n",
(int) bgrt->image_offset_x, (int) bgrt->image_offset_y,
new_x, new_y, new_reso_x, new_reso_y,
new_orientation * 90
@@ -388,12 +388,12 @@ void HackBgrt(EFI_FILE_HANDLE root_dir) {
/**
* Load an application.
*/
static EFI_HANDLE LoadApp(print_t* print_failure, EFI_HANDLE image_handle, EFI_LOADED_IMAGE* image, const CHAR16* path) {
static EFI_HANDLE LoadApp(int print_failure, EFI_HANDLE image_handle, EFI_LOADED_IMAGE* image, const CHAR16* path) {
EFI_DEVICE_PATH* boot_dp = FileDevicePath(image->DeviceHandle, (CHAR16*) path);
EFI_HANDLE result = 0;
Debug(L"HackBGRT: Loading application %s.\n", path);
Log(config.debug, L"HackBGRT: Loading application %s.\n", path);
if (EFI_ERROR(BS->LoadImage(0, image_handle, boot_dp, 0, 0, &result))) {
print_failure(L"HackBGRT: Failed to load application %s.\n", path);
Log(config.debug || print_failure, L"HackBGRT: Failed to load application %s.\n", path);
}
return result;
}
@@ -403,10 +403,11 @@ static EFI_HANDLE LoadApp(print_t* print_failure, EFI_HANDLE image_handle, EFI_L
*/
EFI_STATUS EFIAPI EfiMain(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *ST_) {
InitializeLib(image_handle, ST_);
Log(0, L"HackBGRT version: %s\n", version);
EFI_LOADED_IMAGE* image;
if (EFI_ERROR(BS->HandleProtocol(image_handle, &LoadedImageProtocol, (void**) &image))) {
Debug(L"HackBGRT: LOADED_IMAGE_PROTOCOL failed.\n");
Log(config.debug, L"HackBGRT: LOADED_IMAGE_PROTOCOL failed.\n");
goto fail;
}
@@ -418,14 +419,16 @@ EFI_STATUS EFIAPI EfiMain(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *ST_) {
if (argc <= 1) {
const CHAR16* config_path = L"\\EFI\\HackBGRT\\config.txt";
if (!ReadConfigFile(&config, root_dir, config_path)) {
Print(L"HackBGRT: No config, no command line!\n", config_path);
Log(1, L"HackBGRT: No config, no command line!\n", config_path);
goto fail;
}
}
for (int i = 1; i < argc; ++i) {
ReadConfigLine(&config, root_dir, argv[i]);
}
Debug = config.debug ? Print : NullPrint;
if (config.debug) {
Print(L"HackBGRT version: %s\n", version);
}
SetResolution(config.resolution_x, config.resolution_y);
HackBgrt(root_dir);
@@ -433,55 +436,54 @@ EFI_STATUS EFIAPI EfiMain(EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *ST_) {
EFI_HANDLE next_image_handle = 0;
static CHAR16 backup_boot_path[] = L"\\EFI\\HackBGRT\\bootmgfw-original.efi";
static CHAR16 ms_boot_path[] = L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi";
int try_ms_quietly = 1;
if (config.boot_path && StriCmp(config.boot_path, L"MS") != 0) {
next_image_handle = LoadApp(Print, image_handle, image, config.boot_path);
} else {
config.boot_path = backup_boot_path;
next_image_handle = LoadApp(Debug, image_handle, image, config.boot_path);
if (!next_image_handle) {
config.boot_path = ms_boot_path;
next_image_handle = LoadApp(Debug, image_handle, image, config.boot_path);
}
next_image_handle = LoadApp(1, image_handle, image, config.boot_path);
try_ms_quietly = 0;
}
if (!next_image_handle) {
config.boot_path = backup_boot_path;
next_image_handle = LoadApp(Print, image_handle, image, config.boot_path);
next_image_handle = LoadApp(!try_ms_quietly, image_handle, image, config.boot_path);
if (!next_image_handle) {
config.boot_path = ms_boot_path;
next_image_handle = LoadApp(Print, image_handle, image, config.boot_path);
next_image_handle = LoadApp(!try_ms_quietly, image_handle, image, config.boot_path);
if (!next_image_handle) {
goto fail;
}
}
Print(L"HackBGRT: Reverting to %s.\n", config.boot_path);
if (try_ms_quietly) {
goto ready_to_boot;
}
Log(1, L"HackBGRT: Reverting to %s.\n", config.boot_path);
Print(L"Press escape to cancel or any other key (or wait 15 seconds) to boot.\n");
if (ReadKey(15000).ScanCode == SCAN_ESC) {
goto fail;
}
} else if (config.debug) {
Print(L"HackBGRT: Ready to boot. Disable debug mode to skip this screen.\n");
} else ready_to_boot: if (config.debug) {
Print(L"HackBGRT: Ready to boot.\n");
Print(L"If all goes well, you can set debug=0 and log=0 in config.txt.\n");
Print(L"Press escape to cancel or any other key (or wait 15 seconds) to boot.\n");
if (ReadKey(15000).ScanCode == SCAN_ESC) {
return 0;
}
}
if (!config.log) {
ClearLogVariable();
}
if (EFI_ERROR(BS->StartImage(next_image_handle, 0, 0))) {
Print(L"HackBGRT: Failed to start %s.\n", config.boot_path);
Log(1, L"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);
Log(1, L"HackBGRT: Started %s. Why are we still here?!\n", config.boot_path);
Print(L"Please check that %s is not actually HackBGRT!\n", config.boot_path);
goto fail;
fail: {
Print(L"HackBGRT has failed. Use parameter debug=1 for details.\n");
Print(L"Get a Windows install disk or a recovery disk to fix your boot.\n");
#ifdef GIT_DESCRIBE_W
Print(L"HackBGRT version: " GIT_DESCRIBE_W L"\n");
#else
Print(L"HackBGRT version: unknown; not an official release?\n");
#endif
Log(1, L"HackBGRT has failed.\n");
Print(L"Dumping log:\n\n");
DumpLog();
Print(L"If you can't boot into Windows, get install/recovery disk to fix your boot.\n");
Print(L"Press any key (or wait 15 seconds) to exit.\n");
ReadKey(15000);
return 1;

View File

@@ -14,8 +14,31 @@ const CHAR16* TmpStr(CHAR8 *src, int length) {
return dest;
}
UINTN NullPrint(IN CONST CHAR16 *fmt, ...) {
return 0;
#define log_buffer_size (65536)
CHAR16 log_buffer[log_buffer_size] = {0};
CHAR16 LogVarName[] = L"HackBGRTLog";
EFI_GUID LogVarGuid = {0x03c64761, 0x075f, 0x4dba, {0xab, 0xfb, 0x2e, 0xd8, 0x9e, 0x18, 0xb2, 0x36}}; // self-made: 03c64761-075f-4dba-abfb-2ed89e18b236
void Log(int print, IN CONST CHAR16 *fmt, ...) {
va_list args;
CHAR16 buf[256];
va_start(args, fmt);
VSPrint(buf, sizeof(buf), fmt, args); // size is in bytes, not CHAR16s
va_end(args);
if (print) {
Print(L"%s", buf);
}
StrnCat(log_buffer, buf, log_buffer_size - StrLen(log_buffer) - 1);
LibSetVariable(LogVarName, &LogVarGuid, StrLen(log_buffer) * 2, log_buffer);
}
void DumpLog(void) {
Print(L"%s", log_buffer);
}
void ClearLogVariable(void) {
LibDeleteVariable(LogVarName, &LogVarGuid);
}
const CHAR16* TrimLeft(const CHAR16* s) {

View File

@@ -14,7 +14,17 @@ extern const CHAR16* TmpStr(CHAR8 *src, int length);
/**
* Empty function that has the same signature as Print.
*/
extern UINTN NullPrint(IN CONST CHAR16 *fmt, ...);
extern void Log(int print, IN CONST CHAR16 *fmt, ...);
/**
* Dump the log buffer to the screen.
*/
extern void DumpLog(void);
/**
* Clear the log EFI variable, for minor RAM savings.
*/
extern void ClearLogVariable(void);
/**
* Return the greater of two numbers.