OvmfPkg/QemuFwCfgLib: Add FwCfg cache interface

Since TDVF needs to cache and measure FwCfg, it is required to
add an API to support cache with optional measurement and add some
internal interfaces to support cache in QemuFwCfgLib.
The new API is listed below:
  QemuFwCfgInitCache()

The new Internal interfaces are listed below:
  InternalQemuFwCfgCacheReadBytes()
  InternalQemuFwCfgCacheSelectItem()
  InternalQemuFwCfgCacheGetWorkArea()
  InternalQemuFwCfgCacheResetWorkArea()
  InternalQemuFwCfgItemCached()
  InternalQemuFwCfgCacheReading()
  InternalQemuFwCfgInitCache()
  InternalQemuFwCfgCheckOvmfWorkArea()

Cc: Erdem Aktas <erdemaktas@google.com>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Min Xu <min.m.xu@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Elena Reshetova <elena.reshetova@intel.com>
Signed-off-by: Min Xu <min.m.xu@intel.com>
Signed-off-by: Ceping Sun <cepingx.sun@intel.com>
This commit is contained in:
Ceping Sun 2024-09-27 13:50:26 +08:00 committed by mergify[bot]
parent 63408b2895
commit be529ef3c9
4 changed files with 565 additions and 0 deletions

View File

@ -12,6 +12,7 @@
#define __FW_CFG_LIB__
#include <IndustryStandard/QemuFwCfg.h>
#include <Library/PlatformInitLib.h>
/**
Returns a boolean indicating if the firmware configuration interface
@ -164,4 +165,23 @@ QemuFwCfgFindFile (
OUT UINTN *Size
);
/**
OVMF reads configuration data from QEMU via fw_cfg.
For Td-Guest VMM is out of TCB and the configuration data is untrusted.
From the security perpective the configuration data shall be measured
before it is consumed.
This function reads the fw_cfg items and cached them. In the meanwhile these
fw_cfg items are measured as well. This is to avoid changing the order when
reading the fw_cfg process, which depends on multiple factors(depex, order in
the Firmware volume).
@retval RETURN_SUCCESS - Successfully cache with measurement
@retval Others - As the error code indicates
*/
RETURN_STATUS
EFIAPI
QemuFwCfgInitCache (
IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
);
#endif

View File

@ -0,0 +1,176 @@
/** @file
QemuFwCfg cached feature related functions.
Copyright (c) 2025, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Uefi.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/QemuFwCfgLib.h>
#include "QemuFwCfgLibInternal.h"
/**
Get the pointer to the cached fw_cfg item.
@param[in] Item The fw_cfg item to be retrieved.
@retval FW_CFG_CACHED_ITEM Pointer to the cached fw_cfg item.
@retval NULL The fw_cfg item is not cached.
**/
FW_CFG_CACHED_ITEM *
InternalQemuFwCfgItemCached (
IN FIRMWARE_CONFIG_ITEM Item
)
{
BOOLEAN Cached;
FW_CFG_CACHED_ITEM *CachedItem;
UINT16 SelectItem;
EFI_PEI_HOB_POINTERS Hob;
FW_CFG_CACHED_ITEM *FwCfgCachedItem;
#ifdef TDX_PEI_LESS_BOOT
if (InternalQemuFwCfgCheckOvmfWorkArea () == FALSE) {
return NULL;
}
#endif
SelectItem = (UINT16)(UINTN)Item;
Hob.Raw = GetFirstGuidHob (&gOvmfFwCfgInfoHobGuid);
Cached = FALSE;
CachedItem = NULL;
while (Hob.Raw != NULL) {
FwCfgCachedItem = GET_GUID_HOB_DATA (Hob);
if ((SelectItem == FwCfgCachedItem->FwCfgItem) && (FwCfgCachedItem->DataSize != 0)) {
Cached = TRUE;
CachedItem = FwCfgCachedItem;
break;
}
Hob.Raw = GET_NEXT_HOB (Hob);
Hob.Raw = GetNextGuidHob (&gOvmfFwCfgInfoHobGuid, Hob.Raw);
}
return Cached ? CachedItem : NULL;
}
/**
Clear the QEMU_FW_CFG_CACHE_WORK_AREA.
**/
VOID
InternalQemuFwCfgCacheResetWorkArea (
VOID
)
{
QEMU_FW_CFG_CACHE_WORK_AREA *QemuFwCfgCacheWorkArea;
QemuFwCfgCacheWorkArea = InternalQemuFwCfgCacheGetWorkArea ();
if (QemuFwCfgCacheWorkArea != NULL) {
QemuFwCfgCacheWorkArea->FwCfgItem = 0;
QemuFwCfgCacheWorkArea->Offset = 0;
QemuFwCfgCacheWorkArea->Reading = FALSE;
}
}
/**
Check if reading from FwCfgCache is ongoing.
@retval TRUE Reading from FwCfgCache is ongoing.
@retval FALSE Reading from FwCfgCache is not ongoing.
**/
BOOLEAN
InternalQemuFwCfgCacheReading (
VOID
)
{
BOOLEAN Reading;
QEMU_FW_CFG_CACHE_WORK_AREA *QemuFwCfgCacheWorkArea;
Reading = FALSE;
QemuFwCfgCacheWorkArea = InternalQemuFwCfgCacheGetWorkArea ();
if (QemuFwCfgCacheWorkArea != NULL) {
Reading = QemuFwCfgCacheWorkArea->Reading;
}
return Reading;
}
BOOLEAN
InternalQemuFwCfgCacheSelectItem (
IN FIRMWARE_CONFIG_ITEM Item
)
{
QEMU_FW_CFG_CACHE_WORK_AREA *QemuFwCfgCacheWorkArea;
// Walk thru cached fw_items to see if Item is cached.
if (InternalQemuFwCfgItemCached (Item) == NULL) {
return FALSE;
}
QemuFwCfgCacheWorkArea = InternalQemuFwCfgCacheGetWorkArea ();
if (QemuFwCfgCacheWorkArea == NULL) {
DEBUG ((DEBUG_ERROR, "%a: Invalid FwCfg Cache Work Area\n", __func__));
return FALSE;
}
QemuFwCfgCacheWorkArea->FwCfgItem = (UINT16)Item;
QemuFwCfgCacheWorkArea->Offset = 0;
QemuFwCfgCacheWorkArea->Reading = TRUE;
return TRUE;
}
/**
Read the fw_cfg data from Cache.
@param[in] Size Data size to be read
@param[in] Buffer Pointer to the buffer to which data is written
@retval EFI_SUCCESS - Successfully
@retval Others - As the error code indicates
**/
EFI_STATUS
InternalQemuFwCfgCacheReadBytes (
IN UINTN Size,
IN OUT VOID *Buffer
)
{
QEMU_FW_CFG_CACHE_WORK_AREA *QemuFwCfgCacheWorkArea;
FW_CFG_CACHED_ITEM *CachedItem;
UINTN ReadSize;
if (Buffer == NULL) {
return EFI_INVALID_PARAMETER;
}
QemuFwCfgCacheWorkArea = InternalQemuFwCfgCacheGetWorkArea ();
if (QemuFwCfgCacheWorkArea == NULL) {
return RETURN_NOT_FOUND;
}
if (!QemuFwCfgCacheWorkArea->Reading) {
return RETURN_NOT_READY;
}
CachedItem = InternalQemuFwCfgItemCached (QemuFwCfgCacheWorkArea->FwCfgItem);
if (CachedItem == NULL) {
return RETURN_NOT_FOUND;
}
if (QemuFwCfgCacheWorkArea->Offset >= CachedItem->DataSize) {
DEBUG ((DEBUG_ERROR, "%a: Invalid Item Offset(0x%x) in FwCfg Cache\n", __func__, QemuFwCfgCacheWorkArea->Offset));
ASSERT (FALSE);
return RETURN_ABORTED;
}
if (CachedItem->DataSize - QemuFwCfgCacheWorkArea->Offset > Size) {
ReadSize = Size;
} else {
ReadSize = CachedItem->DataSize - QemuFwCfgCacheWorkArea->Offset;
}
CopyMem (Buffer, (UINT8 *)CachedItem + sizeof (FW_CFG_CACHED_ITEM) + QemuFwCfgCacheWorkArea->Offset, ReadSize);
QemuFwCfgCacheWorkArea->Offset += (UINT32)ReadSize;
DEBUG ((DEBUG_VERBOSE, "%a: found Item 0x%x in FwCfg Cache\n", __func__, QemuFwCfgCacheWorkArea->FwCfgItem));
return RETURN_SUCCESS;
}

View File

@ -0,0 +1,262 @@
/** @file
QemuFwCfg cached feature related functions.
Copyright (c) 2025, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Uefi.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/QemuFwCfgLib.h>
#include "QemuFwCfgLibInternal.h"
#include <Library/TdxHelperLib.h>
#include <Library/PrintLib.h>
#include <Library/TpmMeasurementLib.h>
#include <IndustryStandard/UefiTcgPlatform.h>
#define EV_POSTCODE_INFO_QEMU_FW_CFG_DATA "QEMU FW CFG"
#define QEMU_FW_CFG_SIZE sizeof (EV_POSTCODE_INFO_QEMU_FW_CFG_DATA)
#pragma pack(1)
typedef struct {
CHAR8 FileName[QEMU_FW_CFG_FNAME_SIZE];
BOOLEAN NeedMeasure;
UINT16 FwCfgItem;
UINT32 FwCfgSize;
} CACHE_FW_CFG_STRCUT;
typedef struct {
UINT8 FwCfg[QEMU_FW_CFG_SIZE];
UINT8 FwCfgFileName[QEMU_FW_CFG_FNAME_SIZE];
} FW_CFG_EVENT;
#pragma pack()
#define QEMU_FW_CFG_SIGNATURE \
"QemuFwCfgSignature"
#define QEMU_FW_CFG_SIGNATURE_SIZE sizeof (UINT32)
#define QEMU_FW_CFG_INTERFACE_VERSION \
"QemuFwCfgInterfaceVersion"
#define QEMU_FW_CFG_INTERFACE_VERSION_SIZE sizeof (UINT32)
#define QEMU_FW_CFG_FILE_DIR \
"QemuFwCfgFileDri"
#define E820_FWCFG_FILE \
"etc/e820"
#define SYSTEM_STATES_FWCFG_FILE \
"etc/system-states"
#define EXTRA_PCI_ROOTS_FWCFG_FILE \
"etc/extra-pci-roots"
#define EXTRA_PCI_ROOTS_FWCFG_SIZE sizeof (UINT64)
#define BOOT_MENU_FWCFG_NAME "BootMenu"
#define BOOT_MENU_FWCFG_SIZE sizeof (UINT16)
#define BOOT_MENU_WAIT_TIME_FWCFG_FILE \
"etc/boot-menu-wait"
#define BOOT_MENU_WAIT_TIME_FWCFG_SIZE sizeof (UINT16)
#define RESERVED_MEMORY_END_FWCFG_FILE \
"etc/reserved-memory-end"
#define RESERVED_MEMORY_END_FWCFG_SIZE sizeof (UINT64)
#define PCI_MMIO64_FWCFG_FILE \
"opt/ovmf/X-PciMmio64Mb"
#define BOOTORDER_FWCFG_FILE \
"bootorder"
#define INVALID_FW_CFG_ITEM 0xFFFF
STATIC CONST CACHE_FW_CFG_STRCUT mCacheFwCfgList[] = {
{ QEMU_FW_CFG_SIGNATURE, FALSE, QemuFwCfgItemSignature, QEMU_FW_CFG_SIGNATURE_SIZE },
{ QEMU_FW_CFG_INTERFACE_VERSION, FALSE, QemuFwCfgItemInterfaceVersion, QEMU_FW_CFG_INTERFACE_VERSION_SIZE },
{ QEMU_FW_CFG_FILE_DIR, FALSE, QemuFwCfgItemFileDir, 0 },
{ E820_FWCFG_FILE, FALSE, INVALID_FW_CFG_ITEM, 0 },
{ SYSTEM_STATES_FWCFG_FILE, FALSE, INVALID_FW_CFG_ITEM, 0 },
{ EXTRA_PCI_ROOTS_FWCFG_FILE, TRUE, INVALID_FW_CFG_ITEM, EXTRA_PCI_ROOTS_FWCFG_SIZE },
{ BOOT_MENU_FWCFG_NAME, TRUE, QemuFwCfgItemBootMenu, BOOT_MENU_FWCFG_SIZE },
{ BOOT_MENU_WAIT_TIME_FWCFG_FILE, TRUE, INVALID_FW_CFG_ITEM, BOOT_MENU_WAIT_TIME_FWCFG_SIZE },
{ RESERVED_MEMORY_END_FWCFG_FILE, TRUE, INVALID_FW_CFG_ITEM, RESERVED_MEMORY_END_FWCFG_SIZE },
{ PCI_MMIO64_FWCFG_FILE, TRUE, INVALID_FW_CFG_ITEM, 0 },
{ BOOTORDER_FWCFG_FILE, TRUE, INVALID_FW_CFG_ITEM, 0 },
};
#define CACHE_FW_CFG_COUNT sizeof (mCacheFwCfgList)/sizeof (mCacheFwCfgList[0])
STATIC
UINT32
CalcuateQemuFwCfgFileDirSize (
IN FIRMWARE_CONFIG_ITEM FwCfgItem
)
{
UINT32 FileCount;
UINT32 FileDirSize;
QemuFwCfgSelectItem (FwCfgItem);
FileCount = SwapBytes32 (QemuFwCfgRead32 ());
FileDirSize = FileCount * sizeof (FWCFG_FILE) + sizeof (UINT32);
return FileDirSize;
}
STATIC
EFI_STATUS
ConstructCacheFwCfgList (
IN OUT CACHE_FW_CFG_STRCUT *CacheFwCfgList
)
{
UINT32 Index;
UINT32 Count;
UINTN FwCfgSize;
FIRMWARE_CONFIG_ITEM FwCfgItem;
if (CacheFwCfgList == NULL) {
return EFI_INVALID_PARAMETER;
}
CopyMem (CacheFwCfgList, mCacheFwCfgList, sizeof (mCacheFwCfgList));
Count = CACHE_FW_CFG_COUNT;
for (Index = 0; Index < Count; Index++) {
if (CacheFwCfgList[Index].FwCfgItem == QemuFwCfgItemFileDir) {
CacheFwCfgList[Index].FwCfgSize = CalcuateQemuFwCfgFileDirSize (QemuFwCfgItemFileDir);
continue;
}
if (CacheFwCfgList[Index].FwCfgItem != INVALID_FW_CFG_ITEM) {
continue;
}
if (EFI_ERROR (QemuFwCfgFindFile (CacheFwCfgList[Index].FileName, &FwCfgItem, &FwCfgSize))) {
continue;
}
if (FwCfgSize == 0) {
continue;
}
if ((CacheFwCfgList[Index].FwCfgSize != 0) && (FwCfgSize != CacheFwCfgList[Index].FwCfgSize)) {
continue;
}
CacheFwCfgList[Index].FwCfgItem = (UINT16)FwCfgItem;
CacheFwCfgList[Index].FwCfgSize = (UINT32)FwCfgSize;
}
return EFI_SUCCESS;
}
STATIC
EFI_STATUS
CacheFwCfgInfoWithOptionalMeasurment (
IN CACHE_FW_CFG_STRCUT *CacheFwCfgList
)
{
UINT32 Index;
UINT32 Count;
UINT8 *FwCfginfoHobData;
FW_CFG_CACHED_ITEM *CachedItem;
UINT8 *ItemData;
UINT32 FwCfgItemHobSize;
Count = CACHE_FW_CFG_COUNT;
for (Index = 0; Index < Count; Index++) {
if ((CacheFwCfgList[Index].FwCfgItem == INVALID_FW_CFG_ITEM) || (CacheFwCfgList[Index].FwCfgSize == 0)) {
continue;
}
FwCfginfoHobData = NULL;
FwCfgItemHobSize = sizeof (FW_CFG_CACHED_ITEM) + CacheFwCfgList[Index].FwCfgSize;
FwCfginfoHobData = BuildGuidHob (&gOvmfFwCfgInfoHobGuid, FwCfgItemHobSize);
if (FwCfginfoHobData == NULL) {
DEBUG ((DEBUG_ERROR, "%a: BuildGuidHob Failed with FwCfgItemHobSize(0x%x)\n", __func__, FwCfgItemHobSize));
return EFI_OUT_OF_RESOURCES;
}
ZeroMem (FwCfginfoHobData, FwCfgItemHobSize);
CachedItem = (FW_CFG_CACHED_ITEM *)FwCfginfoHobData;
ItemData = (UINT8 *)CachedItem + sizeof (FW_CFG_CACHED_ITEM);
QemuFwCfgSelectItem (CacheFwCfgList[Index].FwCfgItem);
QemuFwCfgReadBytes (CacheFwCfgList[Index].FwCfgSize, ItemData);
CachedItem->FwCfgItem = CacheFwCfgList[Index].FwCfgItem;
CachedItem->DataSize = CacheFwCfgList[Index].FwCfgSize;
DEBUG ((
DEBUG_INFO,
"Cache FwCfg Name: %a Item:0x%x Size: 0x%x \n",
CacheFwCfgList[Index].FileName,
CachedItem->FwCfgItem,
CachedItem->DataSize
));
if (CacheFwCfgList[Index].NeedMeasure == FALSE) {
continue;
}
if (TdIsEnabled ()) {
FW_CFG_EVENT FwCfgEvent;
EFI_STATUS Status;
ZeroMem (&FwCfgEvent, sizeof (FW_CFG_EVENT));
CopyMem (&FwCfgEvent.FwCfg, EV_POSTCODE_INFO_QEMU_FW_CFG_DATA, sizeof (EV_POSTCODE_INFO_QEMU_FW_CFG_DATA));
CopyMem (&FwCfgEvent.FwCfgFileName, CacheFwCfgList[Index].FileName, QEMU_FW_CFG_FNAME_SIZE);
Status = TpmMeasureAndLogData (
1,
EV_PLATFORM_CONFIG_FLAGS,
(VOID *)&FwCfgEvent,
sizeof (FwCfgEvent),
(VOID *)ItemData,
CacheFwCfgList[Index].FwCfgSize
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "TpmMeasureAndLogData failed with %r\n", Status));
}
}
}
return EFI_SUCCESS;
}
EFI_STATUS
InternalQemuFwCfgInitCache (
IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
)
{
if (PlatformInfoHob == NULL) {
return EFI_INVALID_PARAMETER;
}
PlatformInfoHob->QemuFwCfgCacheWorkArea.FwCfgItem = INVALID_FW_CFG_ITEM;
PlatformInfoHob->QemuFwCfgCacheWorkArea.Offset = 0;
PlatformInfoHob->QemuFwCfgCacheWorkArea.Reading = FALSE;
if (!QemuFwCfgIsAvailable ()) {
return EFI_UNSUPPORTED;
}
CACHE_FW_CFG_STRCUT CacheFwCfgList[CACHE_FW_CFG_COUNT];
if (EFI_ERROR (ConstructCacheFwCfgList (CacheFwCfgList))) {
return EFI_ABORTED;
}
if (EFI_ERROR (CacheFwCfgInfoWithOptionalMeasurment (CacheFwCfgList))) {
return EFI_ABORTED;
}
return EFI_SUCCESS;
}

View File

@ -11,6 +11,31 @@
#ifndef __QEMU_FW_CFG_LIB_INTERNAL_H__
#define __QEMU_FW_CFG_LIB_INTERNAL_H__
#include <Base.h>
#include <Uefi/UefiMultiPhase.h>
#include <Uefi/UefiBaseType.h>
#include <Pi/PiBootMode.h>
#include <Pi/PiHob.h>
#include <Library/HobLib.h>
#include <Library/BaseMemoryLib.h>
#pragma pack(1)
typedef struct {
UINT16 FwCfgItem;
UINT32 DataSize;
// UINT8 *data
} FW_CFG_CACHED_ITEM;
// Refer to https://www.qemu.org/docs/master/specs/fw_cfg.html
// The FwCfg File item struct in QemuFwCfgItemFileDir
typedef struct {
UINT32 Size; /* size of referenced fw_cfg item, big-endian */
UINT16 Select; /* selector key of fw_cfg item, big-endian */
UINT16 Reserved;
UINT8 Name[QEMU_FW_CFG_FNAME_SIZE];
} FWCFG_FILE;
#pragma pack()
/**
Returns a boolean indicating if the firmware configuration interface is
available for library-internal purposes.
@ -70,4 +95,86 @@ QemuFwCfgIsTdxGuest (
VOID
);
/**
Check if the Ovmf work area is built as HobList before invoking Hob services.
@retval TRUE Ovmf work area is not NULL and it is built as HobList.
@retval FALSE Ovmf work area is NULL or it is not built as HobList.
**/
BOOLEAN
InternalQemuFwCfgCheckOvmfWorkArea (
VOID
);
/**
Read the fw_cfg data from Cache.
@retval EFI_SUCCESS - Successfully
@retval Others - As the error code indicates
**/
EFI_STATUS
InternalQemuFwCfgCacheReadBytes (
IN UINTN Size,
IN OUT VOID *Buffer
);
/**
Select the fw_cfg item for reading from cache. If the fw_cfg item
is not cached, then it returns FALSE.
@param[in] Item The fw_cfg item to be selected
@retval TRUE The fw_cfg item is selected.
@retval FALSE The fw_cfg item is not selected.
**/
BOOLEAN
InternalQemuFwCfgCacheSelectItem (
IN FIRMWARE_CONFIG_ITEM Item
);
/**
Get the pointer to the QEMU_FW_CFG_CACHE_WORK_AREA. This data is used as the
workarea to record the ongoing fw_cfg item and offset.
@retval QEMU_FW_CFG_CACHE_WORK_AREA Pointer to the QEMU_FW_CFG_CACHE_WORK_AREA
@retval NULL QEMU_FW_CFG_CACHE_WORK_AREA doesn't exist
**/
QEMU_FW_CFG_CACHE_WORK_AREA *
InternalQemuFwCfgCacheGetWorkArea (
VOID
);
/**
Clear the QEMU_FW_CFG_CACHE_WORK_AREA.
**/
VOID
InternalQemuFwCfgCacheResetWorkArea (
VOID
);
/**
Get the pointer to the cached fw_cfg item.
@param[in] Item The fw_cfg item to be retrieved.
@retval FW_CFG_CACHED_ITEM Pointer to the cached fw_cfg item.
@retval NULL The fw_cfg item is not cached.
**/
FW_CFG_CACHED_ITEM *
InternalQemuFwCfgItemCached (
IN FIRMWARE_CONFIG_ITEM Item
);
/**
Check if reading from FwCfgCache is ongoing.
@retval TRUE Reading from FwCfgCache is ongoing.
@retval FALSE Reading from FwCfgCache is not ongoing.
**/
BOOLEAN
InternalQemuFwCfgCacheReading (
VOID
);
/**
init the fw_cfg info hob with optional measurement
**/
EFI_STATUS
InternalQemuFwCfgInitCache (
IN OUT EFI_HOB_PLATFORM_INFO *PlatformInfoHob
);
#endif