5
Problems Testing UEFI APIs Using QEMU OVMF and GNU-EFI Problems Testing UEFI APIs Using QEMU OVMF and GNU-EFI Finnbarr P. Murphy ([email protected]) 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 Kerr’s 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; } and its associated Makefile: SRCDIR = . PREFIX := /usr For personnal use only 07-13-2012 Copyright 2004-2012 Finnbarr P. Murphy. All rights reserved. 1/5

Problems Testing UEFI APIs Using QEMU OVMF and GNU-EFI

Embed Size (px)

DESCRIPTION

n this blog I discuss problems testing UEFI APIs using QEMU and the UEFI OVMF. In particular I discuss non volatile variables, the QueryVariableInfo runtime service and the changes I have to make to the existing GNU-EFI header files in order to compile a simple application using this runtime service.Read more: http://blog.fpmurphy.com/2012/07#ixzz20SlxDRkY

Citation preview

Page 1: Problems Testing UEFI APIs Using QEMU OVMF and GNU-EFI

Problems Testing UEFI APIs Using QEMU OVMF and GNU-EFI

Problems Testing UEFI APIs UsingQEMU OVMF and GNU-EFI

Finnbarr P. Murphy([email protected])

Recently there has been interest in using the UEFI EDK2 OVMF package with QEMU todemonstrate UEFI Secure Boot functionality. See James Bottomley, Peter Jones, Jeremy Kerr’ssbsigntool et al. If you are unfamiliar with Secure Boot, I suggest you start by reading thefollowing 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 currentversion of the EDK2 OVMF? Well, it turns out that there are actually two distinct problems. Thefirst is with NV variables and the second – surprisingly – is with the contents of the EFI headerfiles which are provided on modern Linux distributions.

Unfortunately, neither EDK2 DUET or OVMF provide proper NV variable support. In the case ofOVMF, the Firmware Volume Block (FVB) services emulate non-volatile variable storage byp r e t e n d i n g t h a t a m e m o r y b u f f e r i s s t o r a g e f o r t h e N V v a r i a b l e s . S e e…/OvmfPkg/EmuVariableFvbRuntimeDxe/Fvb.c. In the case of DUET, an actual file calledEfivar.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_STATUSefi_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;}

and its associated Makefile:

SRCDIR = .PREFIX := /usr

For p

erson

nal u

se on

ly

07-13-2012 Copyright 2004-2012 Finnbarr P. Murphy. All rights reserved. 1/5

Page 2: Problems Testing UEFI APIs Using QEMU OVMF and GNU-EFI

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-merge-constants --std=gnu99 -D_GNU_SOURCEASFLAGS = $(ARCH3264)LDFLAGS = -nostdlibINSTALL = installCC = gccAS = asLD = ld.bfdAR = arRANLIB = ranlibOBJCOPY = objcopyifeq ($(ARCH), ia32) LIBDIR := $(PREFIX)/lib ifeq ($(HOSTARCH), x86_64) ARCH3264 := -m32 endifendififeq ($(ARCH), x86_64) CFLAGS += -mno-red-zone LIBDIR := $(PREFIX)/lib64 ifeq ($(HOSTARCH), ia32) ARCH3264 := -m64 endifendifFORMAT=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-declarationTARGETS = qv.efiall : $(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 $@

The QueryVariableInfo is an EFI Runtime Service which was added to the UEFI 2.0 Specificationway back in October 2008. The purpose of this protocol is to return information about the EFIvariable store. Specifically, the function allows a caller to obtain the information about themaximum size of the storage space available for the EFI variables, the remaining size of thestorage 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 UEFIplatforms for one. Hardware error records are used to persist information about kernel oops andthe like. Such data is stored in a format called a UEFI Common Platform Error Record (CPER.) For

For p

erson

nal u

se on

ly

07-13-2012 Copyright 2004-2012 Finnbarr P. Murphy. All rights reserved. 2/5

Page 3: Problems Testing UEFI APIs Using QEMU OVMF and GNU-EFI

Problems Testing UEFI APIs Using QEMU OVMF and GNU-EFI

more information about CPER, read the UEFI Specification, Appendix N. To determine the amountof space in bytes guaranteed by the platform to be available for saving such hardware errorr e c o r d s , t h e O S i n v o k e s Q u e r y V a r i a b l e I n f o , s e t t i n g t h eEFI_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 asimilar modern Linux distribution that uses the gnu-efi package, is that QueryVariableInfo is notincluded 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 havenot been updated in many years. In fact, they are for EFI firmware revision 12.33, a very oldversion.

#define EFI_FIRMWARE_MAJOR_REVISION 12#define EFI_FIRMWARE_MINOR_REVISION 33

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

#define EFI_FIRMWARE_MAJOR_REVISION 0x1000#define EFI_FIRMWARE_MINOR_REVISION 1

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

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

$ diff /usr/include/efi/efi.h /usr/include/efi/efi.h.org45d44< #include "eficapsule.h"$

$ diff /usr/include/efi/efiapi.h /usr/include/efi/efiapi.h.org23d22< 217,218d215< #define EFI_VARIABLE_HARDWARE_ERROR_RECORD 0x00000008< #define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x00000010232a230> 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< );

For p

erson

nal u

se on

ly

07-13-2012 Copyright 2004-2012 Finnbarr P. Murphy. All rights reserved. 3/5

Page 4: Problems Testing UEFI APIs Using QEMU OVMF and GNU-EFI

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_

// 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;

#define CAPSULE_BLOCK_DESCRIPTOR_SIGNATURE EFI_SIGNATURE_32 ('C', 'B', 'D', 'S')

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

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

#endif

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

// 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 \ }

For p

erson

nal u

se on

ly

07-13-2012 Copyright 2004-2012 Finnbarr P. Murphy. All rights reserved. 4/5

Page 5: Problems Testing UEFI APIs Using QEMU OVMF and GNU-EFI

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 valuesreturned are all zero. This is because of the OVMF NV storage in memory hack. However, if yourun the application on actual UEFI 2.3.1 firmware, you will get proper valid values.

Keep experimenting!

For p

erson

nal u

se on

ly

07-13-2012 Copyright 2004-2012 Finnbarr P. Murphy. All rights reserved. 5/5