19 Commits

Author SHA1 Message Date
Lauri Kenttä
79ee253108 Update change log and tag v2.5.1 2024-08-18 09:38:43 +03:00
Lauri Kenttä
82abb0c120 Update shim to 15.8
Use RockyLinux shim, they have all variants (x86_64, aa64, ia32).
After Windows update KB5041585, the old shim SBAT is not accepted.

Fixes #197.
2024-08-18 09:37:01 +03:00
Lauri Kenttä
830db410ea Properly propagate configuration error message 2024-07-20 15:26:18 +03:00
Lauri Kenttä
1e36d7e388 Support compiling with MSYS2 tools in Windows
Windows and MSYS2 need csc parameters to start with "-", not "/".
Now setup.exe can be compiled with the csc in MSYS2 mono package.
The EFI binaries can already be compiled with MSYS2 clang & lld.
2024-06-22 19:38:29 +03:00
Lauri Kenttä
9038e20cd2 Update change log and tag v2.5.0 2024-06-21 15:36:46 +03:00
Lauri Kenttä
9a0d4737e1 Improve setup.exe metadata 2024-06-21 15:36:46 +03:00
Lauri Kenttä
fa6fae3aa3 Catch errors in writing image 2024-06-13 15:50:19 +03:00
Lauri Kenttä
90fb8e47c1 Don't panic if BootCurrent is not found 2024-06-13 15:50:19 +03:00
Lauri Kenttä
c826149183 Refactor EFI boot entry code into a new class 2024-06-13 15:50:19 +03:00
Lauri Kenttä
af4f99aab6 If missing files, warn that zip is not extracted 2024-05-09 19:50:56 +03:00
Lauri Kenttä
8a97382a2e Clarify 'Log is empty', add a section in README 2024-05-09 19:50:51 +03:00
Lauri Kenttä
8e6466990a Skip the workaround in a44b9290 if skipping shim 2024-04-20 21:58:01 +03:00
Lauri Kenttä
6f94f6bc28 Properly handle skip-shim with enable-overwrite 2024-04-20 21:57:47 +03:00
Lauri Kenttä
bc600a6c2f Handle more command-line options before executing actions 2024-04-20 21:31:19 +03:00
Lauri Kenttä
022ea9b93b Log image dimensions when installing 2024-04-20 21:31:19 +03:00
Lauri Kenttä
7d7d4c2aa4 Clarify installation and upgrading in README 2024-04-20 16:04:54 +03:00
Lauri Kenttä
f1c8b11d6b Add some troubleshooting info to README 2024-04-20 15:54:20 +03:00
Lauri Kenttä
a0553856f0 Fix batch installation instructions 2024-04-20 15:49:33 +03:00
Lauri Kenttä
ffa29f6ffc Update gnu-efi to 3.0.18 2024-04-11 18:07:40 +03:00
14 changed files with 520 additions and 380 deletions

View File

@@ -2,6 +2,18 @@
All notable changes to this project will be documented in this file.
## 2.5.1 - 2024-08-18
### Changed
- Update *shim* to 15.8.
## 2.5.0 - 2024-06-21
### Changed
- Properly handle skip-shim with enable-overwrite.
- Improve instructions (documentation).
- Improve error reporting and logging.
## 2.4.1 - 2024-04-11
### Fixed

View File

@@ -10,8 +10,22 @@ GNUEFI_INC = gnu-efi/inc
FILES_C = src/main.c src/util.c src/types.c src/config.c src/sbat.c src/efi.c
FILES_H = $(wildcard src/*.h)
FILES_CS = src/Setup.cs src/Esp.cs src/Efi.cs
GIT_DESCRIBE := $(firstword $(GIT_DESCRIBE) $(shell git describe --tags) unknown)
FILES_CS = src/Setup.cs src/Esp.cs src/Efi.cs src/EfiBootEntries.cs
# Generate version number from git describe.
# In the numeric form, add the number of commits as the last part.
# (Add .1 for uncommitted changes.)
GIT_DESCRIBE := $(firstword $(GIT_DESCRIBE) $(shell git describe --tags --dirty=-1-dirty) unknown)
GIT_DESCRIBE_PARTS := $(subst -, ,$(patsubst v%,%,$(GIT_DESCRIBE))) 0
GIT_DESCRIBE_NUMERIC := $(firstword $(GIT_DESCRIBE_PARTS)).$(word 2,$(GIT_DESCRIBE_PARTS))
define GIT_DESCRIBE_CS
public class GIT_DESCRIBE {
public const string data = "$(GIT_DESCRIBE)";
public const string numeric = "$(GIT_DESCRIBE_NUMERIC)";
}
endef
CFLAGS += '-DGIT_DESCRIBE_W=L"$(GIT_DESCRIBE)"' '-DGIT_DESCRIBE="$(GIT_DESCRIBE)"'
RELEASE_NAME = HackBGRT-$(GIT_DESCRIBE:v%=%)
@@ -45,10 +59,10 @@ release/$(RELEASE_NAME).zip: release/$(RELEASE_NAME)
(cd release; 7z a -mx=9 "$(RELEASE_NAME).zip" "$(RELEASE_NAME)" -bd -bb1)
src/GIT_DESCRIBE.cs: $(FILES_CS) $(FILES_C) $(FILES_H)
echo 'public class GIT_DESCRIBE { public const string data = "$(GIT_DESCRIBE)"; }' > $@
$(file > $@,$(GIT_DESCRIBE_CS))
setup.exe: $(FILES_CS) src/GIT_DESCRIBE.cs
csc /nologo /define:GIT_DESCRIBE /out:$@ $^
csc -nologo -define:GIT_DESCRIBE -out:$@ $^
certificate.cer pki:
@echo

View File

@@ -26,21 +26,23 @@ The *shim* boot loader is maintained by Red Hat, Inc, and the included signed co
* Get the latest release from the Releases page.
* Start `setup.exe` and follow the instructions.
* The installer will launch Paint for editing the image.
* If Windows later restores the original boot loader, just reinstall.
* If you wish to change the image or other configuration, just reinstall.
* The installer will launch Paint for editing the image, or you can edit it otherwise.
* For advanced settings, edit `config.txt` before installing. No extra support provided!
* After installing, read the instructions in [shim.md](shim.md) and reboot your computer.
* Read the instructions in [shim.md](shim.md).
* Check the common [troubleshooting](#troubleshooting) to be prepared.
* Reboot your computer.
* If Windows later restores the original boot loader, just reinstall.
* If you wish to change the image or configuration later, choose the option to only install files.
### Quiet (batch) installation
* Edit the `config.txt` and `splash.bmp` (or any other images) to your needs.
* Run `setup.exe batch COMMANDS` as administrator, with some of the following commands:
* `install` copy the files but don't enable.
* `enable-entry` create a new EFI boot entry.
* `disable-entry` disable the EFI boot entry.
* `enable-bcdedit` use `bcdedit` to create a new EFI boot entry.
* `disable-bootmgr` use `bcdedit` to disable the EFI boot entry.
* `disable-bcdedit` use `bcdedit` to disable the EFI boot entry.
* `enable-entry` write NVRAM to create a new EFI boot entry.
* `disable-entry` write NVRAM to disable the EFI boot entry.
* `enable-overwrite` overwrite the MS boot loader.
* `disable-overwrite` restore the MS boot loader.
* `skip-shim` skip *shim* when installing.
@@ -78,9 +80,52 @@ Advanced users may edit the `config.txt` to define multiple images, in which cas
If you copy an image file to ESP manually, note that the image must be a 24-bit BMP file with a 54-byte header. That's a TrueColor BMP3 in Imagemagick, or 24-bit BMP/DIB in Microsoft Paint.
## Recovery
## Troubleshooting
If something breaks and you can't boot to Windows, you need to use the Windows installation disk (or recovery disk) to fix boot issues.
### Verification failed, Security violation
This is part of the setup on first boot. Make sure you have read and understood [shim.md](shim.md).
### Boot is slow, boot is stuck, just spinning
Sometimes the first boot is very slow (multiple minutes) for an unknown reason. Wait patiently until you get into Windows. Try to reboot at least a few times to see if it gets any better. It it does not, there's not much else to do than give up.
### Image is not visible, "nothing happens"
Run the setup again and select the option to check the boot log. Continue troubleshooting according to the log contents:
#### Log is empty
If the log is empty, then HackBGRT is not in use. Many computers now have a security feature which causes this problem: the computer resets some settings on reboot and skips the newly-installed HackBGRT.
You have to fix this manually. (After all, the security feature is specifically designed to prevent automatic changes.)
1. Run the setup again.
2. Select the option "boot to UEFI setup".
3. After a reboot, you should get into your computer's own setup utility (UEFI or Firmware settings, or so-called "BIOS").
4. Find boot options and the list of boot entries.
5. Select HackBGRT as the default boot entry (before Windows Boot Loader).
The setup utility is different for each computer and manufacturer, so search online for "[computer model] UEFI setup" or "firmware setup" for images and instructions.
Some people report that HackBGRT is not visible in the computer settings. That's unfortunately a problem with your computer, and you should ask your computer manufacturer how to edit boot entries inside your computer settings. HackBGRT needs to boot `\EFI\HackBGRT\loader.efi`.
If all else fails and you are sure about your computer skills, you can try the legacy installation method. The method bypasses this particular problem but may cause very serious problems if configured incorrectly.
#### Log is not empty
Try to reinstall HackBGRT with the default configuration and image.
If the default logo works, try again with your custom image. Make sure that the image has a reasonable size and position and that you haven't messed up `config.txt`.
If the default logo does not work, check the boot log again.
You may report an issue and attach the `setup.log` file.
### Impossible to boot at all
If you used the default installation method, then your Windows boot loader is still in place and you should be able to access UEFI Setup ("BIOS setup") or boot loader list by some key combination right after powering on your computer. There you can choose the `Windows Boot Loader` and continue as usual to uninstall HackBGRT.
If you selected the legacy installation method which overwrites Windows boot loader, then you need to use the Windows installation disk (or recovery disk) to fix boot issues.
## Building

Submodule gnu-efi updated: 965f557ab7...74bd9b60ba

View File

@@ -1,55 +1,30 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: shim
Upstream-Contact: Peter Jones <pjones@redhat.com>
Source: https://github.com/rhboot/shim
Copyright 2012 Red Hat, Inc <mjg@redhat.com>
Files: *
Copyright: 2012 Red Hat, Inc
2009-2012 Intel Corporation
License: BSD-2-Clause
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
Files: debian/po/cs.po
Copyright: 2018 Michal Simunek <michal.simunek@gmail.com>
License: BSD-2-Clause
Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
Files: debian/po/de.po
Copyright: 2017, 2018 Markus Hiereth <markus.hiereth@freenet.de>
License: BSD-2-Clause
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the
distribution.
Files: debian/po/fr.po
Copyright: 2017, 2018 Alban Vidal <alban.vidal@zordhak.fr>
License: BSD-2-Clause
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
Files: debian/po/nl.po
Copyright: 2017, 2018 Frans Spiesschaert <Frans.Spiesschaert@yucom.be>
License: BSD-2-Clause
Files: debian/po/pt.po
Copyright: 2017, 2018 Rui Branco <ruipb@debianpt.org>
License: BSD-2-Clause
License: BSD-2-Clause
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
.
Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the
distribution.
.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
Significant portions of this code are derived from Tianocore
(http://tianocore.sf.net) and are Copyright 2009-2012 Intel
Corporation.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -64,88 +64,6 @@ public class Efi {
}
}
/**
* Information about an EFI boot entry.
*/
public class BootEntryData {
public UInt32 Attributes;
public string Label;
public class DevicePathNode {
public byte Type, SubType;
public byte[] Data;
public DevicePathNode(byte[] data) {
Type = data[0];
SubType = data[1];
Data = data.Skip(4).ToArray();
}
public byte[] ToBytes() {
var len = Data.Length + 4;
return new byte[] { Type, SubType, (byte)(len & 0xff), (byte)(len >> 8) }.Concat(Data).ToArray();
}
}
public List<DevicePathNode> DevicePathNodes;
public byte[] Arguments;
public BootEntryData(byte[] data) {
Attributes = BitConverter.ToUInt32(data, 0);
var pathNodesLength = BitConverter.ToUInt16(data, 4);
Label = new string(BytesToUInt16s(data).Skip(3).TakeWhile(i => i != 0).Select(i => (char)i).ToArray());
var pos = 6 + 2 * (Label.Length + 1);
var pathNodesEnd = pos + pathNodesLength;
DevicePathNodes = new List<DevicePathNode>();
Arguments = new byte[0];
while (pos + 4 <= pathNodesEnd) {
var len = BitConverter.ToUInt16(data, pos + 2);
if (len < 4 || pos + len > pathNodesEnd) {
return; // throw new Exception("Bad entry.");
}
var node = new DevicePathNode(data.Skip(pos).Take(len).ToArray());
DevicePathNodes.Add(node);
if (node.Type == 0x7f && node.SubType == 0xff) {
// End of entire device path.
// Apparently some firmwares produce paths with unused nodes at the end.
break;
}
pos += len;
}
Arguments = data.Skip(pathNodesEnd).ToArray();
}
public byte[] ToBytes() {
return new byte[0]
.Concat(BitConverter.GetBytes((UInt32) Attributes))
.Concat(BitConverter.GetBytes((UInt16) DevicePathNodes.Sum(n => n.Data.Length + 4)))
.Concat(Encoding.Unicode.GetBytes(Label + "\0"))
.Concat(DevicePathNodes.SelectMany(n => n.ToBytes()))
.Concat(Arguments)
.ToArray();
}
public DevicePathNode FileNameNode {
get {
var d = DevicePathNodes;
return d.Count > 1 && d[d.Count - 1].Type == 0x7F && d[d.Count - 2].Type == 0x04 ? d[d.Count - 2] : null;
}
}
public bool HasFileName {
get {
return FileNameNode != null;
}
}
public string FileName {
get {
if (!HasFileName) {
return "";
}
return new string(Encoding.Unicode.GetChars(FileNameNode.Data).TakeWhile(c => c != '\0').ToArray());
}
set {
if (!HasFileName) {
throw new Exception("Logic error: Setting FileName on a bad boot entry.");
}
FileNameNode.Data = Encoding.Unicode.GetBytes(value + "\0");
}
}
}
/**
* GUID of the global EFI variables.
*/
@@ -206,7 +124,7 @@ public class Efi {
* @param guid GUID of the EFI variable.
* @return Information about the EFI variable.
*/
private static Variable GetVariable(string name, string guid = EFI_GLOBAL_GUID) {
public static Variable GetVariable(string name, string guid = EFI_GLOBAL_GUID) {
Variable result = new Variable();
result.Name = name;
result.Guid = guid;
@@ -255,7 +173,7 @@ public class Efi {
* @param v Information of the variable.
* @param dryRun Don't actually set the variable.
*/
private static void SetVariable(Variable v, bool dryRun = false) {
public static void SetVariable(Variable v, bool dryRun = false) {
Setup.WriteLine($"Writing EFI variable {v.Name} (see log for details)");
Setup.Log($"Writing EFI variable: {v}");
if (dryRun) {
@@ -341,177 +259,6 @@ public class Efi {
return Enumerable.Range(0, bytes.Length / 2).Select(i => (UInt16) (bytes[2 * i] + 0x100 * bytes[2 * i + 1]));
}
/**
* Disable the said boot entry from BootOrder.
*
* @param label Label of the boot entry.
* @param fileName File name of the boot entry.
* @param dryRun Don't actually disable the entry.
* @return True, if the entry was found in BootOrder.
*/
public static bool DisableBootEntry(string label, string fileName, bool dryRun = false) {
Variable bootOrder;
try {
bootOrder = GetVariable("BootOrder");
} catch {
return false;
}
if (bootOrder.Data == null) {
return false;
}
Setup.Log($"Read EFI variable: {bootOrder}");
var found = false;
var bootOrderInts = new List<UInt16>();
foreach (var num in BytesToUInt16s(bootOrder.Data)) {
var entry = GetVariable(String.Format("Boot{0:X04}", num));
if (entry.Data != null) {
var entryData = new BootEntryData(entry.Data);
if (entryData.Label == label && entryData.FileName == fileName) {
found = true;
continue;
}
}
bootOrderInts.Add(num);
}
if (found) {
bootOrder.Data = bootOrderInts.SelectMany(num => new byte[] { (byte)(num & 0xff), (byte)(num >> 8) }).ToArray();
SetVariable(bootOrder, dryRun);
}
return found;
}
/**
* Create and enable the said boot entry from BootOrder.
*
* @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 alwaysCopyFromMS, bool dryRun = false) {
Variable msEntry = null, ownEntry = null;
UInt16 msNum = 0, ownNum = 0;
// Find a free entry and the MS bootloader entry.
Variable bootOrder = null;
try {
bootOrder = GetVariable("BootOrder");
} catch {
if (dryRun) {
return;
}
}
if (bootOrder == null || bootOrder.Data == null) {
throw new Exception("MakeBootEntry: Could not read BootOrder. Maybe your computer is defective.");
}
var bootCurrent = GetVariable("BootCurrent");
if (bootCurrent.Data == null) {
throw new Exception("MakeBootEntry: Could not read BootCurrent. Maybe your computer is defective.");
}
Setup.Log($"Read EFI variable: {bootOrder}");
Setup.Log($"Read EFI variable: {bootCurrent}");
var bootOrderInts = new List<UInt16>(BytesToUInt16s(bootOrder.Data));
foreach (var num in BytesToUInt16s(bootCurrent.Data).Concat(bootOrderInts).Concat(Enumerable.Range(0, 0xff).Select(i => (UInt16) i))) {
var entry = GetVariable(String.Format("Boot{0:X04}", num));
if (entry.Data == null) {
// Use only Boot0000 .. Boot00FF because some firmwares expect that.
if (ownEntry == null && num < 0x100) {
ownNum = num;
ownEntry = entry;
}
} else {
var entryData = new BootEntryData(entry.Data);
if (!entryData.HasFileName) {
continue;
}
if (entryData.Label == label && entryData.FileName == fileName) {
ownNum = num;
ownEntry = entry;
}
if (msEntry == null && entryData.FileName.StartsWith("\\EFI\\Microsoft\\Boot\\bootmgfw.efi", StringComparison.OrdinalIgnoreCase)) {
msNum = num;
msEntry = entry;
}
}
if (ownEntry != null && msEntry != null) {
break;
}
}
if (ownEntry == null) {
throw new Exception("MakeBootEntry: Boot entry list is full.");
} else if (msEntry == null) {
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);
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
ownEntry.Attributes = 7; // EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS
ownEntry.Data = entryData.ToBytes();
SetVariable(ownEntry, dryRun);
}
var msPos = bootOrderInts.IndexOf(msNum);
var ownPos = bootOrderInts.IndexOf(ownNum);
var mustAdd = ownPos == -1;
var mustMove = 0 <= msPos && msPos <= ownPos;
if (mustAdd || mustMove) {
if (mustMove) {
bootOrderInts.RemoveAt(ownPos);
}
bootOrderInts.Insert(msPos < 0 ? 0 : msPos, ownNum);
bootOrder.Data = bootOrderInts.SelectMany(num => new byte[] { (byte)(num & 0xff), (byte)(num >> 8) }).ToArray();
SetVariable(bootOrder, dryRun);
}
}
/**
* Log the boot entries.
*/
public static void LogBootEntries() {
try {
var bootOrder = GetVariable("BootOrder");
var bootCurrent = GetVariable("BootCurrent");
Setup.Log($"LogBootOrder: {bootOrder}");
Setup.Log($"LogBootOrder: {bootCurrent}");
var bootOrderInts = new List<UInt16>(BytesToUInt16s(bootOrder.Data));
// Windows can't enumerate EFI variables, and trying them all is too slow.
// BootOrder + BootCurrent + the first 0xff entries should be enough.
var seen = new HashSet<UInt16>();
foreach (var num in bootOrderInts.Concat(BytesToUInt16s(bootCurrent.Data)).Concat(Enumerable.Range(0, 0xff).Select(i => (UInt16) i))) {
if (seen.Contains(num)) {
continue;
}
seen.Add(num);
var entry = GetVariable(String.Format("Boot{0:X04}", num));
if (entry.Data != null) {
Setup.Log($"LogBootOrder: {entry}");
}
}
} catch (Exception e) {
Setup.Log($"LogBootOrder failed: {e.ToString()}");
}
}
/**
* Retrieve HackBGRT log collected during boot.
*/
@@ -519,7 +266,7 @@ public class Efi {
try {
var log = GetVariable("HackBGRTLog", EFI_HACKBGRT_GUID);
if (log.Data == null) {
return "(null)";
return "Log is empty.";
}
return new string(BytesToUInt16s(log.Data).Select(i => (char)i).ToArray());
} catch (Exception e) {

309
src/EfiBootEntries.cs Normal file
View File

@@ -0,0 +1,309 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
/**
* A class for handling EFI boot entries. Lazy access with cache.
* Notice: Data is not updated after the first read.
*/
public class EfiBootEntries {
/**
* Information about an EFI boot entry.
*/
public class BootEntryData {
public UInt32 Attributes;
public string Label;
public class DevicePathNode {
public byte Type, SubType;
public byte[] Data;
public DevicePathNode(byte[] data) {
Type = data[0];
SubType = data[1];
Data = data.Skip(4).ToArray();
}
public byte[] ToBytes() {
var len = Data.Length + 4;
return new byte[] { Type, SubType, (byte)(len & 0xff), (byte)(len >> 8) }.Concat(Data).ToArray();
}
}
public List<DevicePathNode> DevicePathNodes;
public byte[] Arguments;
public BootEntryData(byte[] data) {
Attributes = BitConverter.ToUInt32(data, 0);
var pathNodesLength = BitConverter.ToUInt16(data, 4);
Label = new string(Efi.BytesToUInt16s(data).Skip(3).TakeWhile(i => i != 0).Select(i => (char)i).ToArray());
var pos = 6 + 2 * (Label.Length + 1);
var pathNodesEnd = pos + pathNodesLength;
DevicePathNodes = new List<DevicePathNode>();
Arguments = new byte[0];
while (pos + 4 <= pathNodesEnd) {
var len = BitConverter.ToUInt16(data, pos + 2);
if (len < 4 || pos + len > pathNodesEnd) {
return; // throw new Exception("Bad entry.");
}
var node = new DevicePathNode(data.Skip(pos).Take(len).ToArray());
DevicePathNodes.Add(node);
if (node.Type == 0x7f && node.SubType == 0xff) {
// End of entire device path.
// Apparently some firmwares produce paths with unused nodes at the end.
break;
}
pos += len;
}
Arguments = data.Skip(pathNodesEnd).ToArray();
}
public byte[] ToBytes() {
return new byte[0]
.Concat(BitConverter.GetBytes((UInt32) Attributes))
.Concat(BitConverter.GetBytes((UInt16) DevicePathNodes.Sum(n => n.Data.Length + 4)))
.Concat(Encoding.Unicode.GetBytes(Label + "\0"))
.Concat(DevicePathNodes.SelectMany(n => n.ToBytes()))
.Concat(Arguments)
.ToArray();
}
public DevicePathNode FileNameNode {
get {
var d = DevicePathNodes;
return d.Count > 1 && d[d.Count - 1].Type == 0x7F && d[d.Count - 2].Type == 0x04 ? d[d.Count - 2] : null;
}
}
public bool HasFileName {
get {
return FileNameNode != null;
}
}
public string FileName {
get {
if (!HasFileName) {
return "";
}
return new string(Encoding.Unicode.GetChars(FileNameNode.Data).TakeWhile(c => c != '\0').ToArray());
}
set {
if (!HasFileName) {
throw new Exception("Logic error: Setting FileName on a bad boot entry.");
}
FileNameNode.Data = Encoding.Unicode.GetBytes(value + "\0");
}
}
}
/**
* Path to the Windows boot loader.
*/
public const string WindowsLoaderPath = "\\EFI\\Microsoft\\Boot\\bootmgfw.efi";
/**
* Path to the HackBGRT loader.
*/
public const string OwnLoaderPath = "\\EFI\\HackBGRT\\loader.efi";
private readonly Dictionary<UInt16, (Efi.Variable, BootEntryData)> cache;
private readonly Efi.Variable BootOrder;
private readonly Efi.Variable BootCurrent;
private readonly List<UInt16> BootOrderInts;
private readonly List<UInt16> BootCurrentInts;
/**
* Constructor. Reads BootOrder and BootCurrent.
*/
public EfiBootEntries() {
cache = new Dictionary<UInt16, (Efi.Variable, BootEntryData)>();
BootOrder = Efi.GetVariable("BootOrder");
BootCurrent = Efi.GetVariable("BootCurrent");
if (BootOrder.Data == null) {
throw new Exception("Could not read BootOrder.");
}
BootCurrentInts = new List<UInt16>(Efi.BytesToUInt16s(BootCurrent.Data ?? new byte[0]));
BootOrderInts = new List<UInt16>(Efi.BytesToUInt16s(BootOrder.Data));
}
/**
* Get the boot entry with the given number.
*
* @param num Number of the boot entry.
* @return The boot entry.
*/
public (Efi.Variable, BootEntryData) GetEntry(UInt16 num) {
if (!cache.ContainsKey(num)) {
var v = Efi.GetVariable(String.Format("Boot{0:X04}", num));
cache[num] = (v, v.Data == null ? null : new BootEntryData(v.Data));
}
return cache[num];
}
/**
* Find entry by file name.
*
* @param fileName File name of the boot entry.
* @return The boot entry.
*/
public (UInt16, Efi.Variable, BootEntryData) FindEntry(string fileName) {
var rest = Enumerable.Range(0, 0xff).Select(i => (UInt16) i);
var entryAccessOrder = BootCurrentInts.Concat(BootOrderInts).Concat(rest);
foreach (var num in entryAccessOrder) {
var (v, e) = GetEntry(num);
if (fileName == null ? e == null : (e != null && e.FileName.Equals(fileName, StringComparison.OrdinalIgnoreCase))) {
return (num, v, e);
}
}
return (0xffff, null, null);
}
/**
* Get the Windows boot entry.
*/
public (UInt16, Efi.Variable, BootEntryData) WindowsEntry {
get { return FindEntry(WindowsLoaderPath); }
}
/**
* Get the HackBGRT boot entry.
*/
public (UInt16, Efi.Variable, BootEntryData) OwnEntry {
get { return FindEntry(OwnLoaderPath); }
}
/**
* Get a free boot entry.
*/
public (UInt16, Efi.Variable, BootEntryData) FreeEntry {
get { return FindEntry(null); }
}
/**
* Disable the said boot entry from BootOrder.
*
* @param dryRun Don't actually write to NVRAM.
* @return True, if the entry was found in BootOrder.
*/
public bool DisableOwnEntry(bool dryRun = false) {
var (ownNum, ownVar, _) = OwnEntry;
if (ownVar == null) {
Setup.Log("Own entry not found.");
return false;
}
Setup.Log($"Old boot order: {BootOrder}");
if (!BootOrderInts.Contains(ownNum)) {
Setup.Log("Own entry not in BootOrder.");
} else {
Setup.Log($"Disabling own entry: {ownNum:X04}");
BootOrderInts.Remove(ownNum);
BootOrder.Data = BootOrderInts.SelectMany(num => new byte[] { (byte)(num & 0xff), (byte)(num >> 8) }).ToArray();
Efi.SetVariable(BootOrder, dryRun);
return true;
}
return false;
}
/**
* Create the boot entry.
*
* @param alwaysCopyFromMS If true, do not preserve any existing data.
* @param dryRun Don't actually write to NVRAM.
*/
public void MakeOwnEntry(bool alwaysCopyFromMS, bool dryRun = false) {
var (msNum, msVar, msEntry) = WindowsEntry;
if (msEntry == null) {
throw new Exception("MakeOwnEntry: Windows Boot Manager not found.");
}
var (ownNum, ownVar, ownEntry) = OwnEntry;
if (ownVar == null) {
(ownNum, ownVar, ownEntry) = FreeEntry;
if (ownVar == null) {
throw new Exception("MakeOwnEntry: No free entry.");
}
Setup.Log($"Creating own entry {ownNum:X4}.");
} else {
Setup.Log($"Updating own entry {ownNum:X4}.");
}
Setup.Log($"Read EFI variable: {msVar}");
Setup.Log($"Read EFI variable: {ownVar}");
// Make a new boot entry using the MS entry as a starting point.
if (!alwaysCopyFromMS && ownEntry != null) {
// 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(ownEntry.Arguments.Take(12).Select(c => (char) c).ToArray());
if (str == "WINDOWS\0\x01\0\0\0") {
ownEntry.Arguments[8] = (byte) 'X';
} else if (str != "WINDOWS\0\x58\0\0\0") {
// Not recognized. Clear the arguments.
ownEntry.Arguments = new byte[0];
}
} else {
ownEntry = msEntry;
ownEntry.Arguments = new byte[0];
ownEntry.Label = "HackBGRT";
ownEntry.FileName = OwnLoaderPath;
}
ownEntry.Attributes = 1; // LOAD_OPTION_ACTIVE
ownVar.Attributes = 7; // EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS
ownVar.Data = ownEntry.ToBytes();
Efi.SetVariable(ownVar, dryRun);
}
/**
* Enable the own boot entry.
*
* @param dryRun Don't actually write to NVRAM.
*/
public void EnableOwnEntry(bool dryRun = false) {
var (ownNum, ownVar, _) = OwnEntry;
if (ownVar == null) {
Setup.Log("Own entry not found.");
return;
}
var (msNum, _, _) = WindowsEntry;
var msPos = BootOrderInts.IndexOf(msNum);
var ownPos = BootOrderInts.IndexOf(ownNum);
var mustAdd = ownPos == -1;
var mustMove = 0 <= msPos && msPos <= ownPos;
Setup.Log($"Old boot order: {BootOrder}");
if (mustAdd || mustMove) {
Setup.Log($"Enabling own entry: {ownNum:X04}");
if (mustMove) {
BootOrderInts.RemoveAt(ownPos);
}
BootOrderInts.Insert(msPos < 0 ? 0 : msPos, ownNum);
BootOrder.Data = BootOrderInts.SelectMany(num => new byte[] { (byte)(num & 0xff), (byte)(num >> 8) }).ToArray();
Efi.SetVariable(BootOrder, dryRun);
}
}
/**
* Log the boot entries.
*/
public void LogEntries() {
Setup.Log($"LogEntries: {BootOrder}");
Setup.Log($"LogEntries: {BootCurrent}");
// Windows can't enumerate EFI variables, and trying them all is too slow.
// BootOrder + BootCurrent + the first 0xff entries should be enough.
var seen = new HashSet<UInt16>();
foreach (var num in BootOrderInts.Concat(BootCurrentInts).Concat(Enumerable.Range(0, 0xff).Select(i => (UInt16) i))) {
if (seen.Contains(num)) {
continue;
}
seen.Add(num);
var (v, e) = GetEntry(num);
if (e != null) {
Setup.Log($"LogEntries: {v}");
}
}
}
/**
* Try to log the boot entries.
*/
public static void TryLogEntries() {
try {
new EfiBootEntries().LogEntries();
} catch (Exception e) {
Setup.Log($"LogEntries failed: {e.ToString()}");
}
}
}

View File

@@ -13,8 +13,12 @@ using System.Runtime.CompilerServices;
using System.Management;
using Microsoft.Win32;
[assembly: AssemblyInformationalVersionAttribute(GIT_DESCRIBE.data)]
[assembly: AssemblyProductAttribute("HackBGRT")]
#if GIT_DESCRIBE
[assembly: AssemblyVersion(GIT_DESCRIBE.numeric)]
#endif
[assembly: AssemblyProduct("HackBGRT")]
[assembly: AssemblyTitle("HackBGRT Installer")]
[assembly: AssemblyDescription("HackBGRT boot logo changer for UEFI")]
/**
* HackBGRT Setup.
@@ -59,12 +63,8 @@ public class Setup {
}
/** @var The privileged actions. */
protected static readonly string[] privilegedActions = new string[] {
protected static readonly string[] PrivilegedActions = new string[] {
"install",
"allow-secure-boot",
"allow-bitlocker",
"allow-bad-loader",
"skip-shim",
"enable-entry", "disable-entry",
"enable-bcdedit", "disable-bcdedit",
"enable-overwrite", "disable-overwrite",
@@ -74,6 +74,16 @@ public class Setup {
"show-boot-log",
};
/** @var Forwardable arguments. */
protected static readonly string[] ForwardableArguments = new string[] {
"dry-run",
"batch",
"skip-shim",
"allow-secure-boot",
"allow-bitlocker",
"allow-bad-loader",
};
/** @var The target directory. */
protected string InstallPath;
@@ -99,6 +109,18 @@ public class Setup {
/** @var Run in batch mode? */
protected bool Batch;
/** @var Skip the shim? */
protected bool SkipShim;
/** @var Allow Secure Boot to be enabled? */
protected bool AllowSecureBoot;
/** @var Allow BitLocker to be enabled? */
protected bool AllowBitLocker;
/** @var Allow bad loader in config file? */
protected bool AllowBadLoader;
/** @var Is the loader signed? */
protected bool LoaderIsSigned = false;
@@ -321,20 +343,22 @@ public class Setup {
g.DrawImageUnscaledAndClipped(img, new Rectangle(Point.Empty, img.Size));
}
try {
bmp.Save(newName, ImageFormat.Bmp);
} catch {
var ms = new MemoryStream();
bmp.Save(ms, ImageFormat.Bmp);
var bytes = ms.ToArray();
File.WriteAllBytes(newName, bytes);
} catch (Exception e) {
Log($"InstallImageFile failed: {e.ToString()}");
throw new SetupException($"Failed to install image {name} to {newName}.");
}
}
WriteLine($"Installed image {name} to {newName}.");
WriteLine($"Installed image {name} to {newName}, size {img.Width}x{img.Height}.");
}
/**
* Install files to ESP.
*
* @param skipShim Whether to skip installing shim.
*/
protected void InstallFiles(bool skipShim) {
protected void InstallFiles() {
var loaderSource = Path.Combine("efi-signed", $"boot{EfiArch}.efi");
LoaderIsSigned = true;
if (!File.Exists(loaderSource)) {
@@ -346,7 +370,7 @@ public class Setup {
}
var shimSource = Path.Combine("shim-signed", $"shim{EfiArch}.efi");
var mmSource = Path.Combine("shim-signed", $"mm{EfiArch}.efi");
if (!skipShim) {
if (!SkipShim) {
if (!File.Exists(shimSource)) {
throw new SetupException($"Missing shim ({shimSource}), can't install shim for {EfiArch}!");
}
@@ -379,13 +403,13 @@ public class Setup {
}
}
var loaderDest = "loader.efi";
if (!skipShim) {
if (!SkipShim) {
InstallFile(shimSource, loaderDest);
InstallFile(mmSource, $"mm{EfiArch}.efi");
InstallFile(loaderSource, "\u4957\u444e\u574f\u0053\u0058"); // bytes "WINDOWS\0X\0" as UTF-16
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");
}
@@ -393,7 +417,7 @@ public class Setup {
var enrollHashPath = $"EFI\\HackBGRT\\{loaderDest}";
var enrollKeyPath = "EFI\\HackBGRT\\certificate.cer";
if (skipShim) {
if (SkipShim) {
if (LoaderIsSigned) {
WriteLine($"Remember to enroll {enrollKeyPath} in your firmware!");
} else {
@@ -437,7 +461,7 @@ public class Setup {
}
var guid = match.Value;
Execute("bcdedit", $"/set {guid} device partition={Esp.Location}", true);
Execute("bcdedit", $"/set {guid} path \\EFI\\HackBGRT\\loader.efi", true);
Execute("bcdedit", $"/set {guid} path {EfiBootEntries.OwnLoaderPath}", true);
foreach (var arg in new string[] { "locale", "inherit", "default", "resumeobject", "displayorder", "toolsdisplayorder", "timeout" }) {
Execute("bcdedit", $"/deletevalue {guid} {arg}", true);
}
@@ -446,9 +470,11 @@ 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);
var e = new EfiBootEntries();
e.MakeOwnEntry(false, DryRun); // Fix load options for shim.
e.EnableOwnEntry(DryRun);
Execute("bcdedit", $"/enum {guid}", true);
Efi.LogBootEntries();
e.LogEntries();
} catch (Exception e) {
Log($"EnableBCDEdit failed: {e.ToString()}");
throw new SetupException("Failed to enable HackBGRT with BCDEdit!");
@@ -492,10 +518,12 @@ public class Setup {
* Enable HackBGRT boot entry.
*/
protected void EnableEntry() {
Efi.MakeAndEnableBootEntry("HackBGRT", "\\EFI\\HackBGRT\\loader.efi", true, DryRun);
var e = new EfiBootEntries();
e.MakeOwnEntry(true, DryRun);
e.EnableOwnEntry(DryRun);
WriteLine("Enabled NVRAM entry for HackBGRT.");
// Verify that the entry was created.
Efi.LogBootEntries();
e.LogEntries();
Execute("bcdedit", "/enum firmware", true);
}
@@ -503,15 +531,26 @@ public class Setup {
* Disable HackBGRT boot entry.
*/
protected void DisableEntry() {
Efi.DisableBootEntry("HackBGRT", "\\EFI\\HackBGRT\\loader.efi", DryRun);
new EfiBootEntries().DisableOwnEntry(DryRun);
WriteLine("Disabled NVRAM entry for HackBGRT.");
}
/**
* Get paths related to MS boot loader.
*/
protected (string Ms, string MsDir, string MsGrub, string MsMokManager) GetMsLoaderPaths() {
var ms = Esp.MsLoaderPath;
var msDir = Path.GetDirectoryName(ms);
var msGrub = Path.Combine(msDir, $"grub{EfiArch}.efi");
var msMm = Path.Combine(msDir, $"mm{EfiArch}.efi");
return (ms, msDir, msGrub, msMm);
}
/**
* Enable HackBGRT by overwriting the MS boot loader.
*/
protected void OverwriteMsLoader() {
var ms = Esp.MsLoaderPath;
var (ms, msDir, msGrub, msMm) = GetMsLoaderPaths();
var backup = BackupLoaderPath;
if (DetectLoader(ms) == BootLoaderType.Microsoft) {
@@ -521,13 +560,16 @@ public class Setup {
// Duplicate check, but better to be sure...
throw new SetupException("Missing MS boot loader backup!");
}
var msDir = Path.GetDirectoryName(ms);
var msGrub = Path.Combine(msDir, $"grub{EfiArch}.efi");
var msMm = Path.Combine(msDir, $"mm{EfiArch}.efi");
var loader = Path.Combine(InstallPath, "loader.efi");
if (SkipShim == (DetectLoader(loader) == BootLoaderType.Shim)) {
throw new SetupException("Bad skip-shim usage. Install and enable with consistent options.");
}
try {
InstallFile(Path.Combine(InstallPath, "loader.efi"), ms, false);
InstallFile(Path.Combine(InstallPath, $"grub{EfiArch}.efi"), msGrub, false);
InstallFile(Path.Combine(InstallPath, $"mm{EfiArch}.efi"), msMm, false);
InstallFile(loader, ms, false);
if (!SkipShim) {
InstallFile(Path.Combine(InstallPath, $"grub{EfiArch}.efi"), msGrub, false);
InstallFile(Path.Combine(InstallPath, $"mm{EfiArch}.efi"), msMm, false);
}
} catch (SetupException e) {
WriteLine(e.Message);
if (DetectLoader(ms) != BootLoaderType.Microsoft) {
@@ -538,6 +580,7 @@ public class Setup {
throw new SetupException("Rollback failed, your system may be unbootable! Create a rescue disk IMMEADIATELY!");
}
}
throw e;
}
}
@@ -545,11 +588,13 @@ public class Setup {
* Restore the MS boot loader if it was previously replaced.
*/
protected void RestoreMsLoader() {
var ms = Esp.MsLoaderPath;
var (ms, msDir, msGrub, msMm) = GetMsLoaderPaths();
if (DetectLoader(ms) == BootLoaderType.Own || DetectLoader(ms) == BootLoaderType.Shim) {
WriteLine("Disabling an old version of HackBGRT.");
InstallFile(BackupLoaderPath, ms, false);
WriteLine($"{ms} has been restored.");
File.Delete(msGrub);
File.Delete(msMm);
}
if (DetectLoader(BackupLoaderPath) == BootLoaderType.Own) {
File.Delete(BackupLoaderPath);
@@ -638,10 +683,8 @@ public class Setup {
/**
* Check Secure Boot status and inform the user.
*
* @param allowSecureBoot Allow Secure Boot to be enabled?
*/
protected void HandleSecureBoot(bool allowSecureBoot) {
protected void HandleSecureBoot() {
int secureBoot = Efi.GetSecureBootStatus();
if (secureBoot == 0) {
WriteLine("Secure Boot is disabled, good!");
@@ -657,7 +700,7 @@ public class Setup {
}
WriteLine("Otherwise your machine may become unbootable.");
if (Batch) {
if (allowSecureBoot) {
if (AllowSecureBoot) {
return;
}
throw new SetupException("Aborting because of Secure Boot.");
@@ -681,10 +724,8 @@ public class Setup {
/**
* Check BitLocker status and inform the user.
*
* @param allowBitLocker Allow BitLocker to be enabled?
*/
protected void HandleBitLocker(bool allowBitLocker) {
protected void HandleBitLocker() {
var output = Execute("manage-bde", "-status", true);
if (output == null) {
WriteLine("BitLocker status could not be determined.");
@@ -704,7 +745,7 @@ public class Setup {
WriteLine("BitLocker status is unclear. Run manage-bde -status to check.");
}
if (Batch) {
if (allowBitLocker) {
if (AllowBitLocker) {
return;
}
throw new SetupException("Aborting because of BitLocker.");
@@ -914,7 +955,7 @@ public class Setup {
var bootLog = $"\n--- BOOT LOG START ---\n{Efi.GetHackBGRTLog()}\n--- BOOT LOG END ---";
Setup.Log(bootLog);
Efi.LogBGRT();
Efi.LogBootEntries();
EfiBootEntries.TryLogEntries();
if (GetBootTime() is DateTime bootTime) {
var configTime = new[] { File.GetCreationTime("config.txt"), File.GetLastWriteTime("config.txt") }.Max();
Log($"Boot time = {bootTime}, config.txt changed = {configTime}");
@@ -925,44 +966,32 @@ public class Setup {
if (IsHiberbootEnabled()) {
WriteLine("You may have to disable Fast Startup (Hiberboot) to reboot properly.");
}
bool allowSecureBoot = false;
bool allowBitLocker = false;
bool allowBadLoader = false;
bool skipShim = false;
Action<Action> verify = (Action revert) => {
try {
VerifyLoaderConfig();
} catch (SetupException e) {
if (allowBadLoader) {
if (AllowBadLoader) {
WriteLine($"Warning: {e.Message}");
} else {
WriteLine($"Error: {e.Message}");
WriteLine($"Reverting. Use batch mode with allow-bad-loader to override.");
revert();
throw new SetupException("Check your configuration and try again.");
throw new SetupException($"Error in configuration: {e.Message}");
}
}
};
Action<Action, Action> enable = (Action enable, Action revert) => {
if (skipShim) {
HandleSecureBoot(allowSecureBoot);
if (SkipShim) {
HandleSecureBoot();
}
HandleBitLocker(allowBitLocker);
HandleBitLocker();
enable();
verify(revert);
};
foreach (var arg in actions) {
Log($"Running action '{arg}'.");
if (arg == "install") {
InstallFiles(skipShim);
} else if (arg == "allow-secure-boot") {
allowSecureBoot = true;
} else if (arg == "allow-bitlocker") {
allowBitLocker = true;
} else if (arg == "allow-bad-loader") {
allowBadLoader = true;
} else if (arg == "skip-shim") {
skipShim = true;
InstallFiles();
} else if (arg == "enable-entry") {
enable(() => EnableEntry(), () => DisableEntry());
} else if (arg == "disable-entry") {
@@ -1011,14 +1040,23 @@ public class Setup {
protected int Run(string[] args) {
DryRun = args.Contains("dry-run");
Batch = args.Contains("batch");
ForwardArguments = String.Join(" ", args.Where(s => s == "dry-run" || s == "batch" || s.StartsWith("arch=")));
AllowBadLoader = args.Contains("allow-bad-loader");
AllowSecureBoot = args.Contains("allow-secure-boot");
AllowBitLocker = args.Contains("allow-bitlocker");
SkipShim = args.Contains("skip-shim");
ForwardArguments = String.Join(" ", args.Where(s => ForwardableArguments.Contains(s) || s.StartsWith("arch=")));
try {
if (!(Directory.Exists("efi") || Directory.Exists("efi-signed")) || !File.Exists("config.txt")) {
WriteLine("This setup program is not in the correct directory!");
WriteLine("Please extract the zip file and run the setup program from there.");
return 1;
}
SetArch(args.Where(s => s.StartsWith("arch=")).Select(s => s.Substring(5)).LastOrDefault());
if (args.Contains("is-elevated") && !HasPrivileges() && !DryRun) {
WriteLine("This installer needs to be run as administrator!");
return 1;
}
var actions = args.Where(s => privilegedActions.Contains(s));
var actions = args.Where(s => PrivilegedActions.Contains(s));
if (actions.Count() > 0) {
RunPrivilegedActions(actions);
return 0;