Translate

Archives

Examine ESRT entries from UEFI Shell

This post details a simple UEFI shell utility for listing the contents of an EFI System Resource Table (ESRT). Essentially ESRT a catalog of firmware which can be updated with the UEFI UpdateCapsule mechanism described in section 7.5 of the UEFI Specification.

The ESRT provides a mechanism for identifying integrated device and system firmware resources for the purposes
of targeting firmware updates to those resources. Each entry in the ESRT describes a device or system firmware resource that can be targeted by a firmware update package. UEFI firmware must allocate and populate an ESRT system resource entry for itself (system firmware). This entry is used to target a system firmware update.

In addition, each firmware resource (typically a UEFI device driver) that can be updated by a firmware update package must be described by exactly one entry in the ESRT to enable firmware updates to be deployed and installed. If an implementation performs system and device firmware updates as a single, monolithic operation, the system firmware entry must be used to target the update. In all other cases, device firmware updates are targeted by an ESRT entry describing device firmware.

Here is a table (from a Microsoft document) detailing the structure of ESRT header and entries:

Possible causes for firmware update failure include, but are not limited to:

  • Insufficient resources
  • Power loss
  • Hardware failure
  • Firmware incompatible with OS drivers
  • Firmware incompatible with OS components

I assume you are familiar with EDK2, UDK2015 and GNU EFI if you are reading this blog post and know how to install the appropriate toolchains to build EFI images. Therefore, I am not going to describe how to install and maintain these toolchains but simply present the source code and corresponding build file for the utility.

Here is the source code and .INF file for building the utility using UDK2015:

//
//  Copyright (c) 2015  Finnbarr P. Murphy.   All rights reserved.
//
//  Display ESRT entries 
//
//  License: BSD License
//

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/ShellLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/UefiBootServicesTableLib.h>

#include <Protocol/EfiShell.h>
#include <Protocol/LoadedImage.h>

#define ESRT_GUID { 0xb122a263, 0x3661, 0x4f68, { 0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80 }}

EFI_GUID EsrtGuid = ESRT_GUID;

typedef struct esrt {
    UINT32 fw_resource_count;
    UINT32 fw_resource_count_max;
    UINT64 fw_resource_version;
} __attribute__((__packed__)) esrt_t;

typedef struct esre1 {
    EFI_GUID fw_class;
    UINT32   fw_type;
    UINT32   fw_version;
    UINT32   lowest_supported_fw_version;
    UINT32   capsule_flags;
    UINT32   last_attempt_version;
    UINT32   last_attempt_status;
} __attribute__((__packed__)) esre1_t;

#define CAPSULE_FLAGS_PERSIST_ACROSS_RESET  0x00010000
#define CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE 0x00020000
#define CAPSULE_FLAGS_INITIATE_RESET        0x00040000


void
dump_esrt(VOID *data)
{
    esrt_t *esrt = data;
    esre1_t *esre1 = (esre1_t *)((UINT8 *)data + sizeof (*esrt));
    int i, ob;

    Print(L"Dumping ESRT found at 0x%x\n\n", data);
    Print(L"Firmware Resource Count: %d\n", esrt->fw_resource_count);
    Print(L"Firmware Resource Max Count: %d\n", esrt->fw_resource_count_max);
    Print(L"Firmware Resource Version: %ld\n\n", esrt->fw_resource_version);

    if (esrt->fw_resource_version != 1) {
        Print(L"ERROR: Unknown ESRT version: %d\n", esrt->fw_resource_version);
        return;
    }

    for (i = 0; i < esrt->fw_resource_count; i++) {
        ob = 0;
        Print(L"ENTRY NUMBER: %d\n", i);
        Print(L"Firmware Class GUID: %g\n", &esre1->fw_class);
        Print(L"Firmware Type: %d ", esre1->fw_type);
        switch (esre1->fw_type) {
            case 0:  Print(L"(Unknown)\n");
                     break;
            case 1:  Print(L"(System)\n");
                     break;
            case 2:  Print(L"(Device)\n");
                     break;
            case 3:  Print(L"(UEFI Driver)\n");
                     break;
            default: Print(L"\n");
        }
        Print(L"Firmware Version: 0x%08x\n", esre1->fw_version);
        Print(L"Lowest Supported Firmware Version: 0x%08x\n", esre1->lowest_supported_fw_version);
        Print(L"Capsule Flags: 0x%08x", esre1->capsule_flags);
        if ((esre1->capsule_flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET)) == CAPSULE_FLAGS_PERSIST_ACROSS_RESET) {
            if (!ob) {
                ob = 1;
                Print(L"(");
            }
            Print(L"PERSIST");
        }
        if ((esre1->capsule_flags & (CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE)) == CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) {
            if (!ob) {
                ob = 1;
                Print(L"(");
            } else 
                Print(L", ");
            Print(L"POPULATE");
        }
        if ((esre1->capsule_flags & (CAPSULE_FLAGS_INITIATE_RESET)) == CAPSULE_FLAGS_INITIATE_RESET) {
            if (!ob) {
                ob = 1;
                Print(L"(");
            } else 
                Print(L", ");
            Print(L"RESET");
        }
        if (ob) 
            Print(L")");
        Print(L"\n");
        Print(L"Last Attempt Version: 0x%08x\n", esre1->last_attempt_version);
        Print(L"Last Attempt Status: %d ", esre1->last_attempt_status);
        switch(esre1->last_attempt_status) {
            case 0:  Print(L"(Success)\n");
                     break;
            case 1:  Print(L"(Unsuccessful)\n");
                     break;
            case 2:  Print(L"(Insufficient Resources)\n");
                     break;
            case 3:  Print(L"(Incorrect version)\n");
                     break;
            case 4:  Print(L"(Invalid Format)\n");
                     break;
            case 5:  Print(L"(AC Power Issue)\n");
                     break;
            case 6:  Print(L"(Battery Power Issue)\n");
                     break;
            default: Print(L"\n");
        }
        Print(L"\n");
        esre1++;
    }
}


INTN
EFIAPI
ShellAppMain(UINTN Argc, CHAR16 **Argv)
{
    EFI_CONFIGURATION_TABLE *ect = gST->ConfigurationTable;

    for (int i = 0; i < gST->NumberOfTableEntries; i++) {
        if (!CompareMem(&ect->VendorGuid, &EsrtGuid, sizeof(EsrtGuid))) {
            dump_esrt(ect->VendorTable);
            return EFI_SUCCESS;
        }
        ect++;
        continue;
    }

    Print(L"No ESRT found\n");

    return EFI_SUCCESS;
}

[Defines]
  INF_VERSION                    = 0x00010006
  BASE_NAME                      = ShowESRT 
  FILE_GUID                      = 4ea87c51-7491-4dfd-0055-747010f3ce51
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 0.1
  ENTRY_POINT                    = ShellCEntryLib
  VALID_ARCHITECTURES            = X64

[Sources]
  ShowESRT.c

[Packages]
  MdePkg/MdePkg.dec
  ShellPkg/ShellPkg.dec 


[LibraryClasses]
  ShellCEntryLib   
  ShellLib
  BaseLib
  BaseMemoryLib
  UefiLib
  
[Protocols]
  
[BuildOptions]

[Pcd]

Here is the source code and Makefile for building the utility using GNU EFI:

//
//  Copyright (c) 2015  Finnbarr P. Murphy.   All rights reserved.
//
//  Display ESRT entries 
//
//  License: BSD License
//

#include <efi.h>
#include <efilib.h>


#define ESRT_GUID { 0xb122a263, 0x3661, 0x4f68, { 0x99, 0x29, 0x78, 0xf8, 0xb0, 0xd6, 0x21, 0x80 }}

EFI_GUID EsrtGuid = ESRT_GUID;

typedef struct esrt {
    UINT32 fw_resource_count;
    UINT32 fw_resource_count_max;
    UINT64 fw_resource_version;
} __attribute__((__packed__)) esrt_t;

typedef struct esre1 {
    EFI_GUID fw_class;
    UINT32   fw_type;
    UINT32   fw_version;
    UINT32   lowest_supported_fw_version;
    UINT32   capsule_flags;
    UINT32   last_attempt_version;
    UINT32   last_attempt_status;
} __attribute__((__packed__)) esre1_t;

#define CAPSULE_FLAGS_PERSIST_ACROSS_RESET  0x00010000
#define CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE 0x00020000
#define CAPSULE_FLAGS_INITIATE_RESET        0x00040000


void
dump_esrt(VOID *data)
{
    esrt_t *esrt = data;
    esre1_t *esre1 = (esre1_t *)((UINT8 *)data + sizeof (*esrt));
    int i, ob;

    Print(L"Dumping ESRT found at 0x%x\n\n", data);
    Print(L"Firmware Resource Count: %d\n", esrt->fw_resource_count);
    Print(L"Firmware Resource Max Count: %d\n", esrt->fw_resource_count_max);
    Print(L"Firmware Resource Version: %ld\n\n", esrt->fw_resource_version);

    if (esrt->fw_resource_version != 1) {
        Print(L"ERROR: Unknown ESRT version: %d\n", esrt->fw_resource_version);
        return;
    }

    for (i = 0; i < esrt->fw_resource_count; i++) {
        ob = 0;
        Print(L"ENTRY NUMBER: %d\n", i);
        Print(L"Firmware Class GUID: %g\n", &esre1->fw_class);
        Print(L"Firmware Type: %d ", esre1->fw_type);
        switch (esre1->fw_type) {
            case 0:  Print(L"(Unknown)\n");
                     break;
            case 1:  Print(L"(System)\n");
                     break;
            case 2:  Print(L"(Device)\n");
                     break;
            case 3:  Print(L"(UEFI Driver)\n");
                     break;
            default: Print(L"\n");
        }
        Print(L"Firmware Version: 0x%08x\n", esre1->fw_version);
        Print(L"Lowest Supported Firmware Version: 0x%08x\n", esre1->lowest_supported_fw_version);
        Print(L"Capsule Flags: 0x%08x", esre1->capsule_flags);
        if ((esre1->capsule_flags & (CAPSULE_FLAGS_PERSIST_ACROSS_RESET)) == CAPSULE_FLAGS_PERSIST_ACROSS_RESET) {
            if (!ob) {
                ob = 1;
                Print(L"(");
            }
            Print(L"PERSIST");
        }
        if ((esre1->capsule_flags & (CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE)) == CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) {
            if (!ob) {
                ob = 1;
                Print(L"(");
            } else 
                Print(L", ");
            Print(L"POPULATE");
        }
        if ((esre1->capsule_flags & (CAPSULE_FLAGS_INITIATE_RESET)) == CAPSULE_FLAGS_INITIATE_RESET) {
            if (!ob) {
                ob = 1;
                Print(L"(");
            } else 
                Print(L", ");
            Print(L"RESET");
        }
        if (ob) 
            Print(L")");
        Print(L"\n");
        Print(L"Last Attempt Version: 0x%08x\n", esre1->last_attempt_version);
        Print(L"Last Attempt Status: %d ", esre1->last_attempt_status);
        switch(esre1->last_attempt_status) {
            case 0:  Print(L"(Success)\n");
                     break;
            case 1:  Print(L"(Unsuccessful)\n");
                     break;
            case 2:  Print(L"(Insufficient Resources)\n");
                     break;
            case 3:  Print(L"(Incorrect version)\n");
                     break;
            case 4:  Print(L"(Invalid Format)\n");
                     break;
            case 5:  Print(L"(AC Power Issue)\n");
                     break;
            case 6:  Print(L"(Battery Power Issue)\n");
                     break;
            default: Print(L"\n");
        }
        Print(L"\n");
        esre1++;
    }
}


EFI_STATUS
efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab)
{
    EFI_CONFIGURATION_TABLE *ect = systab->ConfigurationTable;

    InitializeLib(image_handle, systab);

    for (int i = 0; i < systab->NumberOfTableEntries; i++) {
        if (!CompareMem(&ect->VendorGuid, &EsrtGuid, sizeof(EsrtGuid))) {
            dump_esrt(ect->VendorTable);
            return EFI_SUCCESS;
        }
        ect++;
        continue;
    }

    Print(L"No ESRT found\n");

    return EFI_SUCCESS;
}

TARGET     = showesrt.efi
SRCS       = showesrt.c

SRCDIR     = .
PREFIX     := /usr
HOSTARCH   = $(shell uname -m | sed s,i[3456789]86,ia32,)
ARCH       := $(shell uname -m | sed s,i[3456789]86,ia32,)
INCDIR     = -I.
CPPFLAGS   = -DCONFIG_$(ARCH)
ASFLAGS    = $(ARCH3264)
LDFLAGS    = -nostdlib
INSTALL    = install

CC         = gcc
AS         = as
LD         = ld.bfd
AR         = ar
RANLIB     = ranlib
OBJCOPY    = objcopy

ifeq ($(ARCH), x86_64)
  CFLAGS += -mno-red-zone
  LIBDIR := $(PREFIX)/lib64
  ifeq ($(HOSTARCH), ia32)
    ARCH3264 := -m64
  endif
endif

OBJS = $(SRCS:.c=.o)

FORMAT = efi-app-$(HOSTARCH)
LDFLAGS = -nostdlib -T $(LIBDIR)/gnuefi/elf_$(HOSTARCH)_efi.lds -shared -Bsymbolic $(LIBDIR)/gnuefi/crt0-efi-$(HOSTARCH).o -L$(LIBDIR)
LIBS = -lefi -lgnuefi $(shell $(CC) -print-libgcc-file-name)
CCLDFLAGS =
CFLAGS = -I/usr/include/efi/ -I/usr/include/efi/$(HOSTARCH)/ -I/usr/include/efi/protocol -fpic -fshort-wchar -fno-reorder-functions -fno-strict-aliasing -fno-merge-constants -mno-red-zone -Wimplicit-function-declaration


all : $(TARGET)

.PHONY: all clean install

%.efi: %.so
	$(OBJCOPY) -j .text -j .sdata -j .data -j .dynamic -j .dynsym -j .rel \
		-j .rela -j .reloc --target=$(FORMAT) $*.so $@

$(TARGET:.efi=.so): $(OBJS) 
	$(LD) $(LDFLAGS) -o $@ $^ $(LIBS)

%.o: %.c
	$(CC) $(INCDIR) $(CFLAGS) $(CPPFLAGS) -D__UEFI__ -c $< -o $@

clean : 
	@rm -rf *.o *.a *.so $(TARGET)


I tested both builds using 64-bit Fedora 23. Neither build has been tested on 32-bit architectures nor has been fully tested for flawed login or bugs.

Here is the output I got on my Lenovo T450 laptop:

Dumping ESRT found at 0xBAAEA000

Firmware Resource Count: 2
Firmware Resource Max Count: 2
Firmware Resource Version: 1

ENTRY NUMBER: 0
Firmware Class GUID: DE431F21-4606-4787-B426-25A77C5B9B46
Firmware Type: 1 (System)
Firmware Version: 0x00010012
Lowest Supported Firmware Version: 0x00010012
Capsule Flags: 0x00000000
Last Attempt Version: 0x01497000
Last Attempt Status: 0 (Success)

ENTRY NUMBER: 1
Firmware Class GUID: FFEC4692-FF4F-4D19-A311-453F50256192
Firmware Type: 2 (Device)
Firmware Version: 0xA01E0430
Lowest Supported Firmware Version: 0xA01E0430
Capsule Flags: 0x00008010
Last Attempt Version: 0x00000000
Last Attempt Status: 0 (Success)

If you want further information, Microsoft has some good documentation on the ESRT and firmware updating in Windows 8 and later. Do an Internet search for “Windows UEFI Firmware Update Platform.”

Happy 2016!

2 comments to Examine ESRT entries from UEFI Shell

  • Timothy

    Hi sir,

    For EADK2 environment to create a showesrt.efi, The below code should be removed/changed from showesrt.c before compiler the code.

    1.removed
    __attribute__((__packed__))

    2.Changed “int i” to “UINTN i”

    Best Regards,
    Timothy

    • Note, in general, ACPI structures are packed structures and you need to inform your compiler of that fact using an appropriate compiler directive.