Usually, to use the AfuEfiX64.efi BIOS upgrader from AMI, you have to run the .efi file through the UEFI shell. This works with secure boot disabled, but the UEFI shell is not accessible when secure boot is enabled, so I have to run this EFI file through a boot entry instead by dynamically creating a boot entry with efibootmgr
, like so:
# this creates the boot entry with the correct parameters to upgrade the BIOS
efibootmgr -C --disk /dev/nvme0n1 --part 1 --label "BiosUpgrader" --loader "\EFI\AfuEfix64Signed.efi" --unicode "fs:0/EFI/BIOS_UPDATE_FILE.BIN /p /b /n /x /k /RLC:F"
# and then make sure it boots next time we restart
efibootmgr -n 000X # (X=number of created boot entry)
This works and creates the boot entry which I can run as if I start the boot loader for an operating system. When I try to boot into this created boot entry from the BIOS firmware interface, even with secure boot disabled, the screen goes dark for a second and then goes back to the firmware interface.
I believe this might happen because maybe the EFI application needs to be run inside of a UEFI shell? To fix this, I created my own EFI application that opens an EFI shell which then calls a .nsh script that runs the AfuEfiX64.efi program with the correct parameters:
#include <Guid/FileInfo.h>
#include <Library/FileHandleLib.h>
#include <Library/PcdLib.h>
#include <Library/ShellLib.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Library/UefiShellLib/UefiShellLib.h>
#include <Protocol/EfiShellEnvironment2.h>
#include <Protocol/EfiShellInterface.h>
#include <Protocol/LoadedImage.h>
#include <Protocol/Shell.h>
#include <Protocol/ShellParameters.h>
#include <Uefi.h>
EFI_STATUS
EFIAPI
UefiMain(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) {
// Get the loaded image protocol for the current image
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
EFI_STATUS Status = gBS->HandleProtocol(
ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&LoadedImage);
if (EFI_ERROR(Status)) {
Print(L"Failed to get loaded image protocol: %r\n", Status);
gBS->Stall(1000000);
return Status;
}
// initialize the shell
Status = ShellInitialize();
if (EFI_ERROR(Status)) {
Print(L"Failed to initialize the shell: %r\n", Status);
gBS->Stall(1000000);
// return Status;
}
// Check whether the UEFI Shell protocol is present
Status = gBS->LocateProtocol(&gEfiShellProtocolGuid, NULL,
(VOID **)&gEfiShellProtocol);
if (EFI_ERROR(Status)) {
Print(L"The UEFI Shell is not installed on this system: %r\n", Status);
gBS->Stall(1000000);
// return Status;
}
// The UEFI Shell is installed on this system, run the script
ShellExecute(ImageHandle, L"bios-update.nsh", FALSE, NULL, &Status);
if (EFI_ERROR(Status)) {
Print(L"Failed to run script: %r\n", Status);
// wait 5 seconds
gBS->Stall(5000000);
return Status;
}
return EFI_SUCCESS;
}
This doesn't seem to work:
L"<command>"
parameterSo here are my questions:
A word about security first, having a signed shell on your system can allow an attacker to bypass secure boot, to avoid this risk you must make sure that you are using a scripting shell without any command that allows memory or system manipulation.
You can build a scripting shell (level 1), create a boot entry with load options and use it as a wrapper for AfuEfiX64.efi.
Clone edk2
Follow the setup steps
Create a new shell build config in ShellPkg\ShellPkg.dsc, there are already 2 build configs, create the new one after them
ShellPkg/Application/Shell/Shell.inf {
<Defines>
FILE_GUID = 9105952F-D171-4F1A-812E-F32B055668C5
<PcdsFixedAtBuild>
gEfiShellPkgTokenSpaceGuid.PcdShellLibAutoInitialize|FALSE
gEfiShellPkgTokenSpaceGuid.PcdShellSupportLevel|1
gEfiShellPkgTokenSpaceGuid.PcdShellProfileMask|0
<LibraryClasses>
NULL|ShellPkg/Library/UefiShellLevel1CommandsLib/UefiShellLevel1CommandsLib.inf
}
Build the shell package
Create a new boot entry for the Level 1 shell with the required boot options
-exit \EFI\AfuEfix64Signed.efi [Parameter]
An example boot entry from a test on my machine, shell_echo.efi is the ShellCTestApp from the ShellPkg:
FS0:\efi\uoe\> bcfg boot dump -v
...
Option: 08. Variable: Boot0002
Desc - Shell (L1)
DevPath - HD(2,GPT,88B7633D-4441-4F23-A535-3AE7FB88B9F6,0x109000,0x32000)/\EFI\UOE\shell_l1.efi
Optional- Y
00000000: 2D 00 65 00 78 00 69 00-74 00 20 00 2D 00 6E 00 *-.e.x.i.t. .-.n.*
00000010: 6F 00 6D 00 61 00 70 00-20 00 5C 00 65 00 66 00 *o.m.a.p. .\.e.f.*
00000020: 69 00 5C 00 75 00 6F 00-65 00 5C 00 73 00 68 00 *i.\.u.o.e.\.s.h.*
00000030: 65 00 6C 00 6C 00 5F 00-65 00 63 00 68 00 6F 00 *e.l.l._.e.c.h.o.*
00000040: 2E 00 65 00 66 00 69 00-20 00 54 00 65 00 73 00 *..e.f.i. .T.e.s.*
00000050: 74 00 20 00 31 00 20 00-32 00 20 00 48 00 65 00 *t. .1. .2. .H.e.*
00000060: 6C 00 6C 00 6F 00 00 00- *l.l.o...*