wolfBoot/hal/aarch64_efi.c

672 lines
25 KiB
C

/* aarch64_efi.c
*
* Copyright (C) 2026 wolfSSL Inc.
*
* This file is part of wolfBoot.
*
* wolfBoot is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wolfBoot is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
/* Generic AArch64 UEFI-application HAL (validated on NVIDIA Jetson Orin Nano,
* Tegra234 / Cortex-A78AE).
*
* wolfBoot runs here as an AArch64 UEFI application (wolfboot.efi), launched
* by the platform UEFI firmware. It reads the kernel image from the EFI
* Simple File System, verifies it with wolfCrypt, and hands off to it via
* the UEFI LoadImage/StartImage services (the AArch64 Linux Image is itself
* a PE/COFF EFI-stub application). UEFI owns DRAM/MMU/GIC state, so this HAL
* needs no bare-metal init; the flash ops are stubs and storage/console come
* from UEFI Boot Services. This is the AArch64 sibling of hal/x86_64_efi.c. */
#include <stdint.h>
#include <target.h>
#include "image.h"
#include "loader.h"
#include "printf.h"
#ifdef TARGET_aarch64_efi
/* -DDEBUG (object macro from DEBUG=1) collides with gnu-efi efidebug.h's
* DEBUG(a); drop it before the EFI headers (unused here). */
#undef DEBUG
#include <efi/efi.h>
#include <efi/efilib.h>
/* Shared EFI helper: read the authenticated kernel command line (HDR_CMDLINE
* TLV) from the verified manifest. Must follow the gnu-efi headers above. */
#include "wolfboot_efi.h"
#ifdef __WOLFBOOT
void hal_init(void)
{
}
void hal_prepare_boot(void)
{
}
#endif
#define PAGE_SIZE 0x1000
#define EFI_DEVICE_PATH_PROTOCOL_HW_TYPE 0x01
#define EFI_DEVICE_PATH_PROTOCOL_MEM_SUBTYPE 0x03
static EFI_SYSTEM_TABLE *gSystemTable;
static EFI_HANDLE gImageHandle;
EFI_PHYSICAL_ADDRESS kernel_addr;
EFI_PHYSICAL_ADDRESS update_addr;
/* Linux kernel command line, read from the signed HDR_CMDLINE TLV of the
* verified image and handed to the kernel EFI stub via LoadOptions (see
* aarch64_efi_do_boot). NULL if the image carries no command line. */
static CHAR16 *kernel_cmdline = NULL;
static UINTN kernel_cmdline_bytes = 0;
int RAMFUNCTION hal_flash_write(uintptr_t address, const uint8_t *data, int len)
{
(void)address; (void)data; (void)len;
return 0;
}
void RAMFUNCTION hal_flash_unlock(void)
{
}
void RAMFUNCTION hal_flash_lock(void)
{
}
int RAMFUNCTION hal_flash_erase(uintptr_t address, int len)
{
(void)address; (void)len;
return 0;
}
void* hal_get_primary_address(void)
{
return (void*)kernel_addr;
}
void* hal_get_update_address(void)
{
return (void*)update_addr;
}
void *hal_get_dts_address(void)
{
return NULL;
}
void *hal_get_dts_update_address(void)
{
return NULL;
}
static void panic()
{
while(1) {}
}
#ifdef WOLFBOOT_MEASURED_BOOT_EFI_TCG2
/* EFI TCG2 protocol (TCG EFI Protocol Specification) -- not provided by gnu-efi.
* Used to measure the wolfBoot-verified kernel into a firmware-TPM PCR via
* HashLogExtendEvent before StartImage, extending the platform's measured-boot
* chain to the OS. This is the same consumer pattern U-Boot uses: the platform
* firmware / fTPM performs the hashing, PCR extend and event-log append behind
* EFI_TCG2_PROTOCOL, so wolfBoot needs no TPM transport driver of its own. */
#define WOLFBOOT_TCG2_PROTOCOL_GUID \
{ 0x607f766c, 0x7455, 0x42be, {0x93,0x0b,0xe4,0xd7,0x6d,0xb2,0x72,0x0f} }
#define WOLFBOOT_TCG2_EV_IPL 0x0000000DUL /* boot-loader / OS measurement */
typedef struct {
UINT8 Major;
UINT8 Minor;
} WOLFBOOT_TCG2_VERSION;
typedef struct {
UINT8 Size;
WOLFBOOT_TCG2_VERSION StructureVersion;
WOLFBOOT_TCG2_VERSION ProtocolVersion;
UINT32 HashAlgorithmBitmap;
UINT32 SupportedEventLogs;
BOOLEAN TPMPresentFlag;
UINT16 MaxCommandSize;
UINT16 MaxResponseSize;
UINT32 ManufacturerID;
UINT32 NumberOfPcrBanks;
UINT32 ActivePcrBanks;
} WOLFBOOT_TCG2_CAPABILITY;
typedef struct {
UINT32 HeaderSize;
UINT16 HeaderVersion;
UINT32 PCRIndex;
UINT32 EventType;
} __attribute__((packed)) WOLFBOOT_TCG2_EVENT_HEADER;
/* Event has a flexible array member: sizeof() is the header only; the payload
* size is added explicitly (Event->Size = offsetof(Event) + payload). */
typedef struct {
UINT32 Size;
WOLFBOOT_TCG2_EVENT_HEADER Header;
UINT8 Event[];
} __attribute__((packed)) WOLFBOOT_TCG2_EVENT;
#define WOLFBOOT_TCG2_DESC_MAX 32
/* SHA-256 bit in the TCG2 HashAlgorithmBitmap / ActivePcrBanks (EFI_TCG2 spec). */
#define WOLFBOOT_TCG2_BANK_SHA256 0x00000002u
/* Sane bounds for a device tree before hashing it (fdt v17 header is 40 bytes). */
#define WOLFBOOT_TCG2_FDT_MIN 40u
#define WOLFBOOT_TCG2_FDT_MAX (8u * 1024u * 1024u)
struct wolfboot_tcg2_protocol;
typedef struct wolfboot_tcg2_protocol WOLFBOOT_TCG2_PROTOCOL;
struct wolfboot_tcg2_protocol {
EFI_STATUS (EFIAPI *GetCapability)(WOLFBOOT_TCG2_PROTOCOL *This,
WOLFBOOT_TCG2_CAPABILITY *Cap);
void *GetEventLog;
EFI_STATUS (EFIAPI *HashLogExtendEvent)(WOLFBOOT_TCG2_PROTOCOL *This,
UINT64 Flags,
EFI_PHYSICAL_ADDRESS DataToHash,
UINT64 DataToHashLen,
WOLFBOOT_TCG2_EVENT *Event);
EFI_STATUS (EFIAPI *SubmitCommand)(WOLFBOOT_TCG2_PROTOCOL *This,
UINT32 InputBlockSize, UINT8 *InputBlock,
UINT32 OutputBlockSize, UINT8 *OutputBlock);
void *GetActivePcrBanks;
void *SetActivePcrBanks;
void *GetResultOfSetActivePcrBanks;
};
/* Locate EFI_TCG2_PROTOCOL and confirm a TPM is present. Returns NULL (and
* logs why) if measured boot is unavailable, so callers skip it best-effort. */
static WOLFBOOT_TCG2_PROTOCOL *tcg2_open(UINT32 *activeBanks)
{
EFI_GUID guid = WOLFBOOT_TCG2_PROTOCOL_GUID;
WOLFBOOT_TCG2_PROTOCOL *tcg2 = NULL;
WOLFBOOT_TCG2_CAPABILITY cap;
EFI_STATUS status;
*activeBanks = 0;
status = uefi_call_wrapper(BS->LocateProtocol, 3, &guid, NULL,
(void **)&tcg2);
if (status != EFI_SUCCESS || tcg2 == NULL) {
wolfBoot_printf("TCG2: protocol not found (0x%lx); measure skipped\n",
(unsigned long)status);
return NULL;
}
ZeroMem(&cap, sizeof(cap));
cap.Size = (UINT8)sizeof(cap);
status = uefi_call_wrapper(tcg2->GetCapability, 2, tcg2, &cap);
if (status != EFI_SUCCESS) {
wolfBoot_printf("TCG2: GetCapability failed (0x%lx)\n",
(unsigned long)status);
return NULL;
}
wolfBoot_printf("TCG2: TPM present=%d activeBanks=0x%x banks=%d\n",
(int)cap.TPMPresentFlag, (unsigned)cap.ActivePcrBanks,
(int)cap.NumberOfPcrBanks);
if (!cap.TPMPresentFlag) {
wolfBoot_printf("TCG2: no firmware TPM present; measure skipped\n");
return NULL;
}
*activeBanks = cap.ActivePcrBanks;
return tcg2;
}
/* Extend one measurement (addr,size) into PCR WOLFBOOT_MEASURED_PCR_A, tagging
* the event-log entry with a short ASCII description. */
static void tcg2_extend(WOLFBOOT_TCG2_PROTOCOL *tcg2, const void *addr,
uint32_t size, const char *desc)
{
uint8_t buf[__builtin_offsetof(WOLFBOOT_TCG2_EVENT, Event)
+ WOLFBOOT_TCG2_DESC_MAX];
WOLFBOOT_TCG2_EVENT *evt = (WOLFBOOT_TCG2_EVENT *)buf;
EFI_STATUS status;
UINTN desclen = 0;
/* desclen is the description length capped at WOLFBOOT_TCG2_DESC_MAX - 1, so
* there is always room for a terminating NUL even when desc is longer. */
while (desc[desclen] != '\0' && desclen < WOLFBOOT_TCG2_DESC_MAX - 1)
desclen++;
ZeroMem(buf, sizeof(buf));
/* Event data is the description plus a NUL. The buffer was just zeroed, so
* copying exactly desclen bytes leaves evt->Event[desclen] as that NUL. */
evt->Size = (UINT32)(__builtin_offsetof(WOLFBOOT_TCG2_EVENT, Event)
+ desclen + 1);
evt->Header.HeaderSize = (UINT32)sizeof(WOLFBOOT_TCG2_EVENT_HEADER);
evt->Header.HeaderVersion = 1;
evt->Header.PCRIndex = (UINT32)WOLFBOOT_MEASURED_PCR_A;
evt->Header.EventType = (UINT32)WOLFBOOT_TCG2_EV_IPL;
CopyMem(evt->Event, (void *)desc, desclen);
status = uefi_call_wrapper(tcg2->HashLogExtendEvent, 5, tcg2, (UINT64)0,
(EFI_PHYSICAL_ADDRESS)(uintptr_t)addr,
(UINT64)size, evt);
/* %a: desc is an ASCII string; wolfBoot_printf maps to gnu-efi Print where
* %s is CHAR16 and %a is CHAR8. */
if (status != EFI_SUCCESS)
wolfBoot_printf("TCG2: extend '%a' failed 0x%lx\n", desc,
(unsigned long)status);
else
wolfBoot_printf("TCG2: measured %a (%u bytes) into PCR %d\n",
desc, size, (int)WOLFBOOT_MEASURED_PCR_A);
}
/* Read back the resulting PCR (SHA-256 bank) via a raw TPM2_PCR_Read submitted
* through TCG2 SubmitCommand, and print it -- the attestable value a verifier
* can compare against the event log. */
static void tcg2_read_pcr(WOLFBOOT_TCG2_PROTOCOL *tcg2, uint32_t pcr,
UINT32 activeBanks)
{
static const char hc[] = "0123456789abcdef";
uint8_t cmd[20];
uint8_t resp[128];
char hex[33];
EFI_STATUS status;
UINTN i, row;
const uint8_t *d;
/* TPM2 defines PCRs 0-23; the 3-byte selection below can only address those,
* and cmd[17 + pcr/8] would otherwise overflow the command buffer. */
if (pcr > 23) {
wolfBoot_printf("TCG2: PCR %d out of range (0-23); read skipped\n",
(int)pcr);
return;
}
/* This read-back requests the SHA-256 bank; skip it (rather than report a
* misleading failure) if the TPM has no SHA-256 bank active. The
* measurements above still extended whatever banks are active. */
if (!(activeBanks & WOLFBOOT_TCG2_BANK_SHA256)) {
wolfBoot_printf("TCG2: SHA-256 PCR bank not active; PCR read skipped\n");
return;
}
ZeroMem(cmd, sizeof(cmd));
cmd[0] = 0x80; cmd[1] = 0x01; /* TPM_ST_NO_SESSIONS */
cmd[5] = 20; /* commandSize */
cmd[8] = 0x01; cmd[9] = 0x7E; /* TPM_CC_PCR_Read */
cmd[13] = 1; /* pcrSelectionIn count = 1 */
cmd[15] = 0x0B; /* TPM_ALG_SHA256 */
cmd[16] = 3; /* sizeofSelect */
cmd[17 + (pcr / 8)] = (uint8_t)(1u << (pcr % 8)); /* select the PCR bit */
ZeroMem(resp, sizeof(resp));
status = uefi_call_wrapper(tcg2->SubmitCommand, 5, tcg2,
(UINT32)sizeof(cmd), cmd,
(UINT32)sizeof(resp), resp);
if (status != EFI_SUCCESS) {
wolfBoot_printf("TCG2: PCR read submit failed 0x%lx\n",
(unsigned long)status);
return;
}
/* responseCode (offset 6, 4 bytes) == 0 and digest size (offset 28) == 32 */
if (resp[6] || resp[7] || resp[8] || resp[9] ||
resp[28] != 0x00 || resp[29] != 0x20) {
wolfBoot_printf("TCG2: PCR read unexpected response\n");
return;
}
d = resp + 30; /* TPM2B_DIGEST data (32-byte SHA-256) */
/* Print in two 16-byte rows: the UEFI serial console truncates at 80
* columns, so the full 64-char digest on one line would be cut off. */
wolfBoot_printf("TCG2: PCR %d (SHA256):\n", (int)pcr);
for (row = 0; row < 2; row++) {
for (i = 0; i < 16; i++) {
hex[i * 2] = hc[(d[row * 16 + i] >> 4) & 0xf];
hex[i * 2 + 1] = hc[d[row * 16 + i] & 0xf];
}
hex[32] = '\0';
wolfBoot_printf(" %a\n", hex); /* %a: ASCII */
}
}
/* The platform publishes its device tree under this standard UEFI
* configuration-table GUID. It is stable across firmwares and bootloaders
* (u-boot, edk2, systemd-boot all agree on it), so we look the DTB up by GUID
* rather than scanning every table for the FDT magic. */
#ifndef EFI_DTB_GUID
#define EFI_DTB_GUID \
{ 0xb1b621d5, 0xf19c, 0x41a5, \
{ 0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0 } }
#endif
/* Measure the platform device tree the firmware published in the UEFI
* configuration table (the kernel boots against it), extending the OS
* measurement beyond the kernel image alone. */
static void tcg2_measure_dtb(WOLFBOOT_TCG2_PROTOCOL *tcg2)
{
EFI_GUID dtbGuid = EFI_DTB_GUID;
UINTN i;
uint32_t totalsize;
const uint8_t *p;
/* Find the DTB config table by its stable EFI_DTB_GUID, then validate the
* FDT magic and size before hashing. Note gnu-efi's CompareGuid returns 1
* when the GUIDs are equal (0 otherwise), so skip the non-matching ones. */
for (i = 0; i < gSystemTable->NumberOfTableEntries; i++) {
if (!CompareGuid(&gSystemTable->ConfigurationTable[i].VendorGuid,
&dtbGuid))
continue;
p = (const uint8_t *)gSystemTable->ConfigurationTable[i].VendorTable;
if (p == NULL)
continue;
/* GUID matched; the contents must still be a valid FDT. */
if (!(p[0] == 0xd0 && p[1] == 0x0d && p[2] == 0xfe && p[3] == 0xed)) {
wolfBoot_printf("TCG2: DTB table not a valid FDT; measure skipped\n");
return;
}
totalsize = ((uint32_t)p[4] << 24) | ((uint32_t)p[5] << 16) |
((uint32_t)p[6] << 8) | (uint32_t)p[7];
/* Bound the size before hashing: a corrupt header must not send
* HashLogExtendEvent over a huge/invalid range. */
if (totalsize < WOLFBOOT_TCG2_FDT_MIN ||
totalsize > WOLFBOOT_TCG2_FDT_MAX) {
wolfBoot_printf("TCG2: FDT size %u out of range; skipped\n",
(unsigned)totalsize);
return;
}
tcg2_extend(tcg2, p, totalsize, "wolfBoot dtb");
return;
}
wolfBoot_printf("TCG2: no DTB config table (EFI_DTB_GUID); measure skipped\n");
}
/* Measure the full OS input set -- kernel image, kernel command line and the
* platform device tree -- into the firmware TPM, then read the PCR back. All
* best-effort; a missing TPM or item is logged and skipped, never fatal. */
static void tcg2_measure_all(const void *kernel, uint32_t kernel_size)
{
UINT32 activeBanks = 0;
WOLFBOOT_TCG2_PROTOCOL *tcg2 = tcg2_open(&activeBanks);
if (tcg2 == NULL)
return;
tcg2_extend(tcg2, kernel, kernel_size, "wolfBoot kernel.img");
if (kernel_cmdline != NULL && kernel_cmdline_bytes > 0)
tcg2_extend(tcg2, kernel_cmdline, (uint32_t)kernel_cmdline_bytes,
"wolfBoot cmdline");
tcg2_measure_dtb(tcg2);
tcg2_read_pcr(tcg2, (uint32_t)WOLFBOOT_MEASURED_PCR_A, activeBanks);
}
#endif /* WOLFBOOT_MEASURED_BOOT_EFI_TCG2 */
void RAMFUNCTION aarch64_efi_do_boot(const uint32_t *boot_addr)
{
const uint32_t *size;
const uint8_t* manifest = ((const uint8_t*)boot_addr) - IMAGE_HEADER_SIZE;
MEMMAP_DEVICE_PATH mem_path_device[2];
EFI_HANDLE kernelImageHandle;
EFI_STATUS status;
EFI_LOADED_IMAGE *kernel_li = NULL;
EFI_GUID lipGuid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
size = (const uint32_t *)(manifest + 4);
/* Guard against a zero-size image: EndingAddress below would underflow and
* an empty range would be handed to LoadImage (and the TCG2 measurement). */
if (*size == 0) {
wolfBoot_printf("invalid zero-size image\n");
panic();
}
mem_path_device->Header.Type = EFI_DEVICE_PATH_PROTOCOL_HW_TYPE;
mem_path_device->Header.SubType = EFI_DEVICE_PATH_PROTOCOL_MEM_SUBTYPE;
mem_path_device->MemoryType = EfiLoaderData;
mem_path_device->StartingAddress = (EFI_PHYSICAL_ADDRESS)(uintptr_t)boot_addr;
/* MEMMAP_DEVICE_PATH EndingAddress is inclusive (last valid byte). */
mem_path_device->EndingAddress =
(EFI_PHYSICAL_ADDRESS)((uintptr_t)boot_addr + *size - 1);
SetDevicePathNodeLength(&mem_path_device->Header,
sizeof(MEMMAP_DEVICE_PATH));
SetDevicePathEndNode(&mem_path_device[1].Header);
/* Fetch the authenticated kernel command line from the (already verified)
* manifest BEFORE measuring, so it is covered by the TCG2 measurement and
* ready for LoadOptions after LoadImage. It comes from the HDR_CMDLINE TLV,
* so it is covered by the image signature. NULL if the image carries no
* command line (the kernel then uses its built-in CONFIG_CMDLINE). */
kernel_cmdline = wolfBoot_efi_get_cmdline(manifest, &kernel_cmdline_bytes);
#ifdef WOLFBOOT_MEASURED_BOOT_EFI_TCG2
/* Measure kernel + cmdline + DTB into the firmware TPM before handoff. */
tcg2_measure_all(boot_addr, *size);
#endif
/* gnu-efi Print truncates %p to 32 bits; use %lx for the full 64-bit addr. */
wolfBoot_printf("Staging kernel at address 0x%lx, size: %u\n",
(unsigned long)(uintptr_t)boot_addr, *size);
status = uefi_call_wrapper(gSystemTable->BootServices->LoadImage,
6,
0, /* bool */
gImageHandle,
(EFI_DEVICE_PATH*)mem_path_device,
(void*)(uintptr_t)boot_addr,
*size,
&kernelImageHandle);
if (status != EFI_SUCCESS) {
wolfBoot_printf("LoadImage failed: 0x%lx\n", (unsigned long)status);
panic();
}
/* Pass the authenticated kernel command line (fetched above) to the loaded
* image via LoadOptions for the Linux EFI stub. A direct root= boot needs
* no initrd; an initramfs flow would add it via LINUX_EFI_INITRD_MEDIA_GUID
* (LoadFile2). */
if (kernel_cmdline != NULL) {
status = uefi_call_wrapper(gSystemTable->BootServices->HandleProtocol, 3,
kernelImageHandle, &lipGuid,
(void**)&kernel_li);
if (status == EFI_SUCCESS && kernel_li != NULL) {
kernel_li->LoadOptions = kernel_cmdline;
kernel_li->LoadOptionsSize = (UINT32)kernel_cmdline_bytes;
}
}
status = uefi_call_wrapper(gSystemTable->BootServices->StartImage,
3,
kernelImageHandle, 0, NULL);
if (status != EFI_SUCCESS) {
wolfBoot_printf("StartImage failed: 0x%lx\n", (unsigned long)status);
panic();
}
}
static UINT64 FileSize(EFI_FILE_HANDLE FileHandle)
{
EFI_FILE_INFO *FileInfo;
UINT64 ret;
FileInfo = LibFileInfo(FileHandle);
if (FileInfo == NULL) {
panic();
return 0; /* Never reached, for static analyzer */
}
ret = FileInfo->FileSize;
FreePool(FileInfo);
return ret;
}
static EFI_FILE_HANDLE GetVolume(EFI_HANDLE image)
{
EFI_GUID fsGuid = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
EFI_GUID lipGuid = EFI_LOADED_IMAGE_PROTOCOL_GUID;
EFI_LOADED_IMAGE *loaded_image = NULL;
EFI_FILE_IO_INTERFACE *IOVolume;
EFI_FILE_HANDLE Volume;
EFI_STATUS status;
status = uefi_call_wrapper(BS->HandleProtocol, 3,
image, &lipGuid, (void **) &loaded_image);
if (status != EFI_SUCCESS)
panic();
status = uefi_call_wrapper(BS->HandleProtocol, 3,
loaded_image->DeviceHandle,
&fsGuid, (VOID*)&IOVolume);
if (status != EFI_SUCCESS)
panic();
status = uefi_call_wrapper(IOVolume->OpenVolume, 2, IOVolume, &Volume);
if (status != EFI_SUCCESS)
panic();
return Volume;
}
static EFI_FILE_HANDLE openFile(CHAR16 *file, EFI_FILE_HANDLE volume)
{
EFI_FILE_HANDLE file_handle;
EFI_STATUS status;
/* Open() ignores Attributes for read-only access (they only apply with
* EFI_FILE_MODE_CREATE); pass 0 so stricter firmware does not reject it. */
status = uefi_call_wrapper(volume->Open, 5,
volume,
&file_handle,
file,
EFI_FILE_MODE_READ,
0);
if (status != EFI_SUCCESS)
file_handle = NULL;
return file_handle;
}
static int open_kernel_image(EFI_FILE_HANDLE vol, CHAR16 *filename,
EFI_PHYSICAL_ADDRESS *_addr, uint32_t *sz)
{
EFI_FILE_HANDLE file;
EFI_STATUS status;
UINTN readsz;
UINTN pages;
UINT64 filesz;
/* Always leave *_addr well-defined so the caller's "no image" check is
* reliable: 0 on any failure, the loaded address only on full success. */
*_addr = 0;
*sz = 0;
file = openFile(filename, vol);
if (file == NULL)
return -1;
/* FileSize() is 64-bit; the rest of the loader (header parsing, LoadImage
* size) works in uint32_t. Reject a size that would not fit rather than
* silently truncating it into *sz. */
filesz = FileSize(file);
if (filesz == 0 || filesz > 0xFFFFFFFFULL) {
wolfBoot_printf("Invalid file size: 0x%lx\n", (unsigned long)filesz);
uefi_call_wrapper(file->Close, 1, file);
return -1;
}
*sz = (uint32_t)filesz;
/* Round up to whole pages (no extra page when *sz is a page multiple). */
pages = ((UINTN)*sz + PAGE_SIZE - 1) / PAGE_SIZE;
wolfBoot_printf("Opening file: %s, size: %u\n", filename, *sz);
status = uefi_call_wrapper(BS->AllocatePages,
4,
AllocateAnyPages,
EfiLoaderData,
pages, _addr);
if (status != EFI_SUCCESS) {
wolfBoot_printf("AllocatePages failed: 0x%lx\n", (unsigned long)status);
*_addr = 0;
uefi_call_wrapper(file->Close, 1, file);
/* EFI_STATUS is an unsigned UINTN with the error high-bit set; returning
* it through this int would truncate to a positive (non-error) value.
* Return -1 so the caller's return-code check is reliable. */
return -1;
}
/* EFI_FILE Read() takes UINTN *BufferSize and VOID *Buffer. On AArch64
* uefi_call_wrapper is a native passthrough (no arg casting), so pass the
* correct types explicitly rather than a uint32_t pointer and an integer
* address. */
readsz = (UINTN)*sz;
status = uefi_call_wrapper(file->Read, 3, file, &readsz,
(void*)(uintptr_t)*_addr);
*sz = (uint32_t)readsz;
uefi_call_wrapper(file->Close, 1, file); /* done with the file */
if (status != EFI_SUCCESS) {
wolfBoot_printf("file Read failed: 0x%lx\n", (unsigned long)status);
uefi_call_wrapper(BS->FreePages, 2, *_addr, pages);
*_addr = 0;
/* Return -1, not the truncated EFI_STATUS (see note above). */
return -1;
}
if (*sz < IMAGE_HEADER_SIZE) {
wolfBoot_printf("Image smaller than the header\n");
uefi_call_wrapper(BS->FreePages, 2, *_addr, pages);
*_addr = 0;
return -1;
}
return 0;
}
EFI_STATUS
EFIAPI
efi_main (EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
{
CHAR16 *kernel_filename = L"kernel.img";
CHAR16 *update_filename = L"update.img";
EFI_LOADED_IMAGE *loaded_image = NULL;
EFI_FILE_HANDLE vol;
EFI_STATUS status;
uint32_t kernel_size, update_size;
InitializeLib(ImageHandle, SystemTable);
gSystemTable = SystemTable;
gImageHandle = ImageHandle;
status = uefi_call_wrapper(SystemTable->BootServices->HandleProtocol,
3,
ImageHandle,
&LoadedImageProtocol,
(void **)&loaded_image);
if (status == EFI_SUCCESS)
wolfBoot_printf("Image base: 0x%lx\n", loaded_image->ImageBase);
vol = GetVolume(ImageHandle);
/* open_kernel_image() leaves *_addr == 0 on failure (and frees any pages),
* so the "no image" check below is reliable; the sizes are logged inside
* and not needed here. */
(void)open_kernel_image(vol, kernel_filename, &kernel_addr, &kernel_size);
(void)open_kernel_image(vol, update_filename, &update_addr, &update_size);
(void)kernel_size;
(void)update_size;
if (kernel_addr == 0 && update_addr == 0) {
wolfBoot_printf("No image to load\n");
panic();
}
wolfBoot_start();
return EFI_SUCCESS;
}
#endif /* TARGET_aarch64_efi */