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:
- I'm getting the error "The UEFI Shell is not installed on this system: Not found"
- When proceeding, the ShellExecute function returns "Failed to run script: Not found", no matter what I put into the
L"<command>"
parameter - If I try to execute the above EFI application through the UEFI shell, nothing happens and it doesn't output anything.
So here are my questions:
- Is there a better way to directly run the AfuEfiX64.efi program, without creating my own EFI file to wrap it inside of a UEFI shell?
- Is there a way to get more verbose information for why the EFI program fails to run as a boot entry?
- Is it even possible to use ShellExecute when booting from a boot entry (instead of through the UEFI shell)? Or is there something wrong with my script?