From a44b929012ed2ad94e4976f389e6c8a32de43e57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauri=20Kentt=C3=A4?= Date: Sun, 17 Dec 2023 14:26:21 +0200 Subject: [PATCH] Fix a shim error caused by bad load options data Shim expects a filename or nothing in the load options. To avoid an error message during boot, do several things: When creating the NVRAM entry, use empty load options. The current string ("HackBGRT\0") was just a decoration, and it's luckily ignored by shim because the length is odd. When creating the entry with BCDEdit, manually fix the load options. The load options in BCDEdit entries start with "WINDOWS\0" followed by UINT32 version, as seen in ReactOS struct BL_WINDOWS_LOAD_OPTIONS. The version is 1, but BCDEdit seems to be happy with a higher number. By setting this version to 'X' (0x58), the string becomes a valid UCS-2 file name. Update the installer so that the HackBGRT loader is installed with this weird file name. The reason why the load options cannot be deleted completely is that BCDEdit will recreate the entry on next boot if it doesn't find the entry it just tried to create. See: https://github.com/rhboot/shim/pull/621 See: https://github.com/reactos/reactos/blob/v0.4.7/boot/environ/include/bl.h#L911 --- src/Efi.cs | 26 ++++++++++++++++++++++---- src/Setup.cs | 4 +++- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/Efi.cs b/src/Efi.cs index d6724be..a78c7e8 100644 --- a/src/Efi.cs +++ b/src/Efi.cs @@ -93,6 +93,7 @@ public class Efi { var pos = 6 + 2 * (Label.Length + 1); var pathNodesEnd = pos + pathNodesLength; DevicePathNodes = new List(); + Arguments = new byte[0]; while (pos + 4 <= pathNodesEnd) { var len = BitConverter.ToUInt16(data, pos + 2); if (len < 4 || pos + len > pathNodesEnd) { @@ -378,9 +379,10 @@ public class Efi { * * @param label Label of the boot entry. * @param fileName File name of the boot entry. + * @param alwaysCopyFromMS If true, do not preserve any existing data. * @param dryRun Don't actually create the entry. */ - public static void MakeAndEnableBootEntry(string label, string fileName, bool dryRun = false) { + public static void MakeAndEnableBootEntry(string label, string fileName, bool alwaysCopyFromMS, bool dryRun = false) { Variable msEntry = null, ownEntry = null; UInt16 msNum = 0, ownNum = 0; @@ -435,12 +437,28 @@ public class Efi { throw new Exception("MakeBootEntry: Windows Boot Manager not found."); } else { Setup.Log($"Read EFI variable: {msEntry}"); + Setup.Log($"Read EFI variable: {ownEntry}"); // Make a new boot entry using the MS entry as a starting point. var entryData = new BootEntryData(msEntry.Data); - entryData.Arguments = Encoding.UTF8.GetBytes(label + "\0"); + if (!alwaysCopyFromMS && ownEntry.Data != null) { + entryData = new BootEntryData(ownEntry.Data); + // Shim expects the arguments to be a filename or nothing. + // But BCDEdit expects some Microsoft-specific data. + // Modify the entry so that BCDEdit still recognises it + // but the data becomes a valid UCS-2 / UTF-16LE file name. + var str = new string(entryData.Arguments.Take(12).Select(c => (char) c).ToArray()); + if (str == "WINDOWS\0\x01\0\0\0") { + entryData.Arguments[8] = (byte) 'X'; + } else if (str != "WINDOWS\0\x58\0\0\0") { + // Not recognized. Clear the arguments. + entryData.Arguments = new byte[0]; + } + } else { + entryData.Arguments = new byte[0]; + entryData.Label = label; + entryData.FileName = fileName; + } entryData.Attributes = 1; // LOAD_OPTION_ACTIVE - entryData.Label = label; - entryData.FileName = fileName; ownEntry.Attributes = 7; // EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS ownEntry.Data = entryData.ToBytes(); SetVariable(ownEntry, dryRun); diff --git a/src/Setup.cs b/src/Setup.cs index 0072fb8..d1da748 100644 --- a/src/Setup.cs +++ b/src/Setup.cs @@ -373,6 +373,7 @@ public class Setup { loaderDest = $"grub{EfiArch}.efi"; } InstallFile(loaderSource, loaderDest); + InstallFile(loaderSource, "\u4957\u444e\u574f\u0053\u0058"); // bytes "WINDOWS\0X\0" as UTF-16 if (LoaderIsSigned) { InstallFile("certificate.cer"); } @@ -415,6 +416,7 @@ public class Setup { WriteLine("Enabled NVRAM entry for HackBGRT with BCDEdit."); // Verify that the entry was created. Execute("bcdedit", "/enum firmware", true); + Efi.MakeAndEnableBootEntry("HackBGRT", "\\EFI\\HackBGRT\\loader.efi", false, DryRun); Execute("bcdedit", $"/enum {guid}", true); Efi.LogBootEntries(); } catch (Exception e) { @@ -460,7 +462,7 @@ public class Setup { * Enable HackBGRT boot entry. */ protected void EnableEntry() { - Efi.MakeAndEnableBootEntry("HackBGRT", "\\EFI\\HackBGRT\\loader.efi", DryRun); + Efi.MakeAndEnableBootEntry("HackBGRT", "\\EFI\\HackBGRT\\loader.efi", true, DryRun); WriteLine("Enabled NVRAM entry for HackBGRT."); // Verify that the entry was created. Efi.LogBootEntries();