You are on page 1of 5

Problems Testing UEFI APIs Using QEMU OVMF and GNU-EFI

Problems Testing UEFI APIs Using QEMU OVMF and GNU-EFI


Finnbarr P. Murphy
(fpm@fpmurphy.com) Recently there has been interest in using the UEFI EDK2 OVMF package with QEMU to demonstrate UEFI Secure Boot functionality. See James Bottomley, Peter Jones, Jeremy Kerrs sbsigntool et al. If you are unfamiliar with Secure Boot, I suggest you start by reading the following article about Secure Boot which is on linux.com. So how complete is the support for UEFI APIs and non volatile (NV) variables in the current version of the EDK2 OVMF? Well, it turns out that there are actually two distinct problems. The first is with NV variables and the second surprisingly is with the contents of the EFI header files which are provided on modern Linux distributions. Unfortunately, neither EDK2 DUET or OVMF provide proper NV variable support. In the case of OVMF, the Firmware Volume Block (FVB) services emulate non-volatile variable storage by pretending that a memory buffer is storage for the NV variables. See /OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.c. In the case of DUET, an actual file called Efivar.bin is created. See /DuetPkg/FvbRuntimeService/FWBlockService.c and ../DuetPkg/FvbRuntimeService/FileIo.c Consider the following simple UEFI application:

#include <efi.h> #include <efilib.h> EFI_STATUS efi_main (EFI_HANDLE image, EFI_SYSTEM_TABLE *systab) { EFI_STATUS rc = EFI_SUCCESS; UINT32 Attr; UINT64 MaxStoreSize = 0; UINT64 RemainStoreSize = 0; UINT64 MaxSize = 0; InitializeLib(image, systab); Attr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_HARDWARE_ERROR_RECORD; rc = uefi_call_wrapper(RT->QueryVariableInfo, 4, Attr, &amp;MaxStoreSize, &amp;RemainStoreSize, &amp;MaxSize); if (rc != EFI_SUCCESS) { Print(L"ERROR: Failed to get store sizes: %d\n", rc); } else { Print(L"Max Storage Size: %ld\n", MaxStoreSize); Print(L"Remaining Storage Size: %ld\n", RemainStoreSize); Print(L"Max Variable Size: %ld\n", MaxSize); } return rc; }

Fo r

and its associated Makefile:

SRCDIR PREFIX

= . := /usr

07-13-2012

pe rs o

Copyright 2004-2012 Finnbarr P. Murphy. All rights reserved.

nn a

lu

se

on

ly

1/5

Problems Testing UEFI APIs Using QEMU OVMF and GNU-EFI 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) CFLAGS = $(ARCH3264) -g -O0 -fpic -Wall -fshort-wchar -fno-strict-aliasing -fno-mergeconstants --std=gnu99 -D_GNU_SOURCE ASFLAGS = $(ARCH3264) LDFLAGS = -nostdlib INSTALL = install CC = gcc AS = as LD = ld.bfd AR = ar RANLIB = ranlib OBJCOPY = objcopy ifeq ($(ARCH), ia32) LIBDIR := $(PREFIX)/lib ifeq ($(HOSTARCH), x86_64) ARCH3264 := -m32 endif endif ifeq ($(ARCH), x86_64) CFLAGS += -mno-red-zone LIBDIR := $(PREFIX)/lib64 ifeq ($(HOSTARCH), ia32) ARCH3264 := -m64 endif endif FORMAT=efi-app-$(HOSTARCH) LDFLAGS = -nostdlib -T $(LIBDIR)/gnuefi/elf_$(HOSTARCH)_efi.lds -shared -Bsymbolic $(LIBDI R)/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-r ed-zone -Wimplicit-function-declaration TARGETS = qv.efi all : $(TARGETS) clean : @rm -rf *.o *.a *.so $(TARGETS) .PHONY: all clean install .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 $@ %.so: %.o $(LD) $(LDFLAGS) -o $@ $^ $(LIBS) %.o: %.c $(CC) $(INCDIR) $(CFLAGS) $(CPPFLAGS) -D__UEFI__ -c $< -o $@ %.S: %.c $(CC) $(INCDIR) $(CFLAGS) $(CPPFLAGS) -D__UEFI__ -S $< -o $@ %.E: %.c $(CC) $(INCDIR) $(CFLAGS) $(CPPFLAGS) -D__UEFI__ -E $< -o $@

Fo r

The QueryVariableInfo is an EFI Runtime Service which was added to the UEFI 2.0 Specification way back in October 2008. The purpose of this protocol is to return information about the EFI variable store. Specifically, the function allows a caller to obtain the information about the maximum size of the storage space available for the EFI variables, the remaining size of the storage space available for the EFI variables and the maximum size of each individual EFI variable, associated with the attributes specified. So where is QueryVariableInfo used in the real world? Well, hardware error records on UEFI platforms for one. Hardware error records are used to persist information about kernel oops and the like. Such data is stored in a format called a UEFI Common Platform Error Record (CPER.) For

07-13-2012

pe rs o

Copyright 2004-2012 Finnbarr P. Murphy. All rights reserved.

nn a

lu

se

on

ly

2/5

Problems Testing UEFI APIs Using QEMU OVMF and GNU-EFI

more information about CPER, read the UEFI Specification, Appendix N. To determine the amount of space in bytes guaranteed by the platform to be available for saving such hardware error records, the OS invokes QueryVariableInfo, setting the EFI_VARIABLE_HARDWARE_ERROR_RECORD bit in the Attributes bitmask. The first problem you will encounter if you try to compile this application on Fedora 17, or a similar modern Linux distribution that uses the gnu-efi package, is that QueryVariableInfo is not included in which is the header that defines the EFI Runtime Services. Similarly, EFI_VARIABLE_HARDWARE_ERROR_RECORD is undefined. Examining the EFI header files under /usr/include/efi revealed that the header file definitions have not been updated in many years. In fact, they are for EFI firmware revision 12.33, a very old version.

The current EFI firmware version is 16.1. This is what the EDK2 expects.

Here are the diffs that you need to apply to the GNU-EFI headers in order for the above application to compile correctly:

$ diff /usr/include/efi/efi.h /usr/include/efi/efi.h.org 45d44 < #include "eficapsule.h" $ $ diff /usr/include/efi/efiapi.h /usr/include/efi/efiapi.h.org 23d22 < 217,218d215 < #define EFI_VARIABLE_HARDWARE_ERROR_RECORD 0x00000008 < #define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x00000010 232a230 > 587,613d584 < typedef < EFI_STATUS < (EFIAPI *EFI_UPDATE_CAPSULE) ( < IN EFI_CAPSULE_HEADER **CapsuleHeaderArray, < IN UINTN CapsuleCount, < IN EFI_PHYSICAL_ADDRESS ScatterGatherList OPTIONAL < ); < < typedef < EFI_STATUS < (EFIAPI *EFI_QUERY_CAPSULE_CAPABILITIES) ( < IN EFI_CAPSULE_HEADER **CapsuleHeaderArray, < IN UINTN CapsuleCount, < OUT UINT64 *MaximumCapsuleSize, < OUT EFI_RESET_TYPE *ResetType < );

Fo r

07-13-2012

pe rs o

Copyright 2004-2012 Finnbarr P. Murphy. All rights reserved.

nn a

These headers come from the gnu-efi package, whose maintainer is Nigel Croxon (still with HP?), and are in need of serious updating.

lu

#define EFI_FIRMWARE_MAJOR_REVISION 0x1000 #define EFI_FIRMWARE_MINOR_REVISION 1

se
3/5

on

#define EFI_FIRMWARE_MAJOR_REVISION 12 #define EFI_FIRMWARE_MINOR_REVISION 33

ly

Problems Testing UEFI APIs Using QEMU OVMF and GNU-EFI < < typedef < EFI_STATUS < (EFIAPI *EFI_QUERY_VARIABLE_INFO) ( < IN UINT32 Attributes, < OUT UINT64 *MaximumVariableStorageSize, < OUT UINT64 *RemainingVariableStorageSize, < OUT UINT64 *MaximumVariableSize < ); < < 748,750c719 < EFI_UPDATE_CAPSULE UpdateCapsule; < EFI_QUERY_CAPSULE_CAPABILITIES QueryCapsuleCapabilities; < EFI_QUERY_VARIABLE_INFO QueryVariableInfo; --> $ $ cat /usr/include/efi/eficapsule.h #ifndef _EFI_CAPSULE_H_ #define _EFI_CAPSULE_H_

typedef struct { UINT32 CapsuleArrayNumber; VOID* CapsulePtr[1]; } EFI_CAPSULE_TABLE; #endif

Fo r

// Bits in the flags field of the capsule header #define EFI_CAPSULE_HEADER_FLAG_SETUP 0x00000001

pe rs o

typedef struct { EFI_GUID CapsuleGuid; UINT32 HeaderSize; UINT32 Flags; UINT32 CapsuleImageSize; } UEFI_CAPSULE_HEADER;

// This is the GUID of the capsule header of the image on disk. #define EFI_CAPSULE_GUID \ { \ 0x3B6686BD, 0x0D76, 0x4030, 0xB7, 0x0E, 0xB5, 0x51, 0x9E, 0x2F, 0xC5, 0xA0 \ } // This is the GUID of the file created by the capsule application that contains the path to the device(s) to update. #define EFI_PATH_FILE_NAME_GUID \ { \ 0x7644C181, 0xFA6E, 0x46DA, 0x80, 0xCB, 0x04, 0xB9, 0x90, 0x40, 0x62, 0xE8 \ } // This is the GUID of the configuration results file created by the capsule application. #define EFI_CONFIG_FILE_NAME_GUID \ { \ 0x98B8D59B, 0xE8BA, 0x48EE, 0x98, 0xDD, 0xC2, 0x95, 0x39, 0x2F, 0x1E, 0xDB \ }

07-13-2012

Copyright 2004-2012 Finnbarr P. Murphy. All rights reserved.

nn a

#define CAPSULE_BLOCK_DESCRIPTOR_SIGNATURE

EFI_SIGNATURE_32 ('C', 'B', 'D', 'S')

lu

// An array of these describe the blocks that make up a capsule for a capsule update. typedef struct { UINT64 Length; // length of the data block EFI_PHYSICAL_ADDRESS Data; // physical address of the data block UINT32 Signature; // CBDS UINT32 CheckSum; // to sum this structure to 0 } EFI_CAPSULE_BLOCK_DESCRIPTOR;

// supports setup changes

se

on
4/5

ly

Problems Testing UEFI APIs Using QEMU OVMF and GNU-EFI #endif // #ifndef _EFI_CAPSULE_H_

Note that if you run this application on the current UEFI QEMU OVMF setup, the three values returned are all zero. This is because of the OVMF NV storage in memory hack. However, if you run the application on actual UEFI 2.3.1 firmware, you will get proper valid values. Keep experimenting!

Fo r

07-13-2012

pe rs o

Copyright 2004-2012 Finnbarr P. Murphy. All rights reserved.

nn a

lu

se
5/5

on

ly

You might also like