MdeModulePkg/EsrtFmpDxe: Enhance ESRT to support multiple controllers

REF: https://bugzilla.tianocore.org/show_bug.cgi?id=1525

The patch is to merge multiple FMP instances into single ESRT entry
when they have the same GUID.

The policy to LastAttemptStatus/LastAttemptVersion of ESRT entry is:
If all the LastAttemptStatus are LAST_ATTEMPT_STATUS_SUCCESS, then
LastAttemptVersion should be the smallest of LastAttemptVersion. If
any of the LastAttemptStatus is not LAST_ATTEMPT_STATUS_SUCCESS,
then the LastAttemptVersion/LastAttemptStatus should be the values
of the first FMP instance whose LastAttemptStatus is not
LAST_ATTEMPT_STATUS_SUCCESS.

To detect possible duplicated GUID/HardwareInstance, a table of
GUID/HardwareInstance pairs from all the EFI_FIRMWARE_IMAGE_DESCRIPTORs
from all FMP instances is built. If a duplicate is found, then generate
a DEBUG_ERROR message, generate an ASSERT(), and ignore the duplicate
EFI_FIRMWARE_IMAGE_DESCRIPTOR.

Add an internal worker function called FmpGetFirmwareImageDescriptor()
that retrieves the list of EFI_FIRMWARE_IMAGE_DESCRIPTORs from a single
FMP instance and returns the descriptors in an allocated buffer. This
function is used to get the descriptors used to build the table of
unique GUID/HardwareInstance pairs.  It is then used again to generate
the ESRT Table from all the EFI_FIRMWARE_IMAGE_DESCRIPTORs from all the
FMP instances. 2 passes are performed so the total number of
descriptors is known. This allows the correct sized buffers to always
be allocated.

Cc: Sean Brogan <sean.brogan@microsoft.com>
Cc: Bret Barkelew <Bret.Barkelew@microsoft.com>
Cc: Jian J Wang <jian.j.wang@intel.com>
Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com>
Signed-off-by: Eric Jin <eric.jin@intel.com>
Reviewed-by: Hao A Wu <hao.a.wu@intel.com>
This commit is contained in:
Jin, Eric 2019-08-05 16:02:48 +08:00 committed by Hao A Wu
parent c30fbac4e2
commit e314132fea
1 changed files with 261 additions and 141 deletions

View File

@ -2,7 +2,7 @@
Publishes ESRT table from Firmware Management Protocol instances
Copyright (c) 2016, Microsoft Corporation
Copyright (c) 2018, Intel Corporation. All rights reserved.<BR>
Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
@ -21,6 +21,22 @@
#include <Guid/EventGroup.h>
#include <Guid/SystemResourceTable.h>
///
/// Structure for array of unique GUID/HardwareInstance pairs from the
/// current set of EFI_FIRMWARE_IMAGE_DESCRIPTORs from all FMP Protocols.
///
typedef struct {
///
/// A unique GUID identifying the firmware image type.
///
EFI_GUID ImageTypeGuid;
///
/// An optional number to identify the unique hardware instance within the
/// system for devices that may have multiple instances whenever possible.
///
UINT64 HardwareInstance;
} GUID_HARDWAREINSTANCE_PAIR;
/**
Print ESRT to debug console.
@ -33,11 +49,6 @@ PrintTable (
IN EFI_SYSTEM_RESOURCE_TABLE *Table
);
//
// Number of ESRT entries to grow by each time we run out of room
//
#define GROWTH_STEP 10
/**
Install EFI System Resource Table into the UEFI Configuration Table
@ -101,90 +112,129 @@ IsSystemFmp (
}
/**
Function to create a single ESRT Entry and add it to the ESRT
given a FMP descriptor. If the guid is already in the ESRT it
will be ignored. The ESRT will grow if it does not have enough room.
Function to create a single ESRT Entry and add it to the ESRT with
a given FMP descriptor. If the GUID is already in the ESRT, then the ESRT
entry is updated.
@param[in, out] Table On input, pointer to the pointer to the ESRT.
On output, same as input or pointer to the pointer
to new enlarged ESRT.
@param[in] FmpImageInfoBuf Pointer to the EFI_FIRMWARE_IMAGE_DESCRIPTOR.
@param[in] FmpVersion FMP Version.
@param[in,out] Table Pointer to the ESRT Table.
@param[in,out] HardwareInstances Pointer to the GUID_HARDWAREINSTANCE_PAIR.
@param[in,out] NumberOfDescriptors The number of EFI_FIRMWARE_IMAGE_DESCRIPTORs.
@param[in] FmpImageInfoBuf Pointer to the EFI_FIRMWARE_IMAGE_DESCRIPTOR.
@param[in] FmpVersion FMP Version.
@return Status code.
@retval EFI_SUCCESS FmpImageInfoBuf was use to fill in a new ESRT entry
in Table.
@retval EFI_SUCCESS The ImageTypeId GUID in FmpImageInfoBuf matches an
existing ESRT entry in Table, and the information
from FmpImageInfoBuf was merged into the the existing
ESRT entry.
@retval EFI_UNSPOORTED The GUID/HardareInstance in FmpImageInfoBuf has is a
duplicate.
**/
EFI_STATUS
CreateEsrtEntry (
IN OUT EFI_SYSTEM_RESOURCE_TABLE **Table,
IN EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf,
IN UINT32 FmpVersion
IN OUT EFI_SYSTEM_RESOURCE_TABLE *Table,
IN OUT GUID_HARDWAREINSTANCE_PAIR *HardwareInstances,
IN OUT UINT32 *NumberOfDescriptors,
IN EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf,
IN UINT32 FmpVersion
)
{
UINTN Index;
EFI_SYSTEM_RESOURCE_ENTRY *Entry;
UINTN NewSize;
EFI_SYSTEM_RESOURCE_TABLE *NewTable;
UINT64 FmpHardwareInstance;
Index = 0;
Entry = NULL;
Entry = (EFI_SYSTEM_RESOURCE_ENTRY *)((*Table) + 1);
//
// Make sure Guid isn't already in the list
//
for (Index = 0; Index < (*Table)->FwResourceCount; Index++) {
if (CompareGuid (&Entry->FwClass, &FmpImageInfoBuf->ImageTypeId)) {
DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: ESRT Entry already exists for FMP Instance with GUID %g\n", &Entry->FwClass));
return EFI_INVALID_PARAMETER;
}
Entry++;
FmpHardwareInstance = 0;
if (FmpVersion >= 3) {
FmpHardwareInstance = FmpImageInfoBuf->HardwareInstance;
}
//
// Grow table if needed
// Check to see of FmpImageInfoBuf GUID/HardwareInstance is unique
//
if ((*Table)->FwResourceCount >= (*Table)->FwResourceCountMax) {
NewSize = (((*Table)->FwResourceCountMax + GROWTH_STEP) * sizeof (EFI_SYSTEM_RESOURCE_ENTRY)) + sizeof (EFI_SYSTEM_RESOURCE_TABLE);
NewTable = AllocateZeroPool (NewSize);
if (NewTable == NULL) {
DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to allocate memory larger table for ESRT. \n"));
return EFI_OUT_OF_RESOURCES;
for (Index = 0; Index < *NumberOfDescriptors; Index++) {
if (CompareGuid (&HardwareInstances[Index].ImageTypeGuid, &FmpImageInfoBuf->ImageTypeId)) {
if (HardwareInstances[Index].HardwareInstance == FmpHardwareInstance) {
DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Duplicate firmware image descriptor with GUID %g HardwareInstance:0x%x\n", &FmpImageInfoBuf->ImageTypeId, FmpHardwareInstance));
ASSERT (
!CompareGuid (&HardwareInstances[Index].ImageTypeGuid, &FmpImageInfoBuf->ImageTypeId) ||
HardwareInstances[Index].HardwareInstance != FmpHardwareInstance
);
return EFI_UNSUPPORTED;
}
}
//
// Copy the whole old table into new table buffer
//
CopyMem (
NewTable,
(*Table),
(((*Table)->FwResourceCountMax) * sizeof (EFI_SYSTEM_RESOURCE_ENTRY)) + sizeof (EFI_SYSTEM_RESOURCE_TABLE)
);
//
// Update max
//
NewTable->FwResourceCountMax = NewTable->FwResourceCountMax + GROWTH_STEP;
//
// Free old table
//
FreePool ((*Table));
//
// Reassign pointer to new table.
//
(*Table) = NewTable;
}
//
// ESRT table has enough room for the new entry so add new entry
// Record new GUID/HardwareInstance pair
//
Entry = (EFI_SYSTEM_RESOURCE_ENTRY *)(((UINT8 *)(*Table)) + sizeof (EFI_SYSTEM_RESOURCE_TABLE));
CopyGuid (&HardwareInstances[*NumberOfDescriptors].ImageTypeGuid, &FmpImageInfoBuf->ImageTypeId);
HardwareInstances[*NumberOfDescriptors].HardwareInstance = FmpHardwareInstance;
*NumberOfDescriptors = *NumberOfDescriptors + 1;
DEBUG ((DEBUG_INFO, "EsrtFmpDxe: Add new image descriptor with GUID %g HardwareInstance:0x%x\n", &FmpImageInfoBuf->ImageTypeId, FmpHardwareInstance));
//
// Move to the location of new entry
// Check to see if GUID is already in the ESRT table
//
Entry = Entry + (*Table)->FwResourceCount;
Entry = (EFI_SYSTEM_RESOURCE_ENTRY *)(Table + 1);
for (Index = 0; Index < Table->FwResourceCount; Index++, Entry++) {
if (!CompareGuid (&Entry->FwClass, &FmpImageInfoBuf->ImageTypeId)) {
continue;
}
DEBUG ((DEBUG_INFO, "EsrtFmpDxe: ESRT Entry already exists for FMP Instance with GUID %g\n", &Entry->FwClass));
//
// Set ESRT FwVersion to the smaller of the two values
//
Entry->FwVersion = MIN (FmpImageInfoBuf->Version, Entry->FwVersion);
//
// VERSION 2 has Lowest Supported
//
if (FmpVersion >= 2) {
//
// Set ESRT LowestSupportedFwVersion to the smaller of the two values
//
Entry->LowestSupportedFwVersion =
MIN (
FmpImageInfoBuf->LowestSupportedImageVersion,
Entry->LowestSupportedFwVersion
);
}
//
// VERSION 3 supports last attempt values
//
if (FmpVersion >= 3) {
//
// Update the ESRT entry with the last attempt status and last attempt
// version from the first FMP instance whose last attempt status is not
// SUCCESS. If all FMP instances are SUCCESS, then set version to the
// smallest value from all FMP instances.
//
if (Entry->LastAttemptStatus == LAST_ATTEMPT_STATUS_SUCCESS) {
if (FmpImageInfoBuf->LastAttemptStatus != LAST_ATTEMPT_STATUS_SUCCESS) {
Entry->LastAttemptStatus = FmpImageInfoBuf->LastAttemptStatus;
Entry->LastAttemptVersion = FmpImageInfoBuf->LastAttemptVersion;
} else {
Entry->LastAttemptVersion =
MIN (
FmpImageInfoBuf->LastAttemptVersion,
Entry->LastAttemptVersion
);
}
}
}
return EFI_SUCCESS;
}
//
// Increment resource count
// Add a new ESRT Table Entry
//
(*Table)->FwResourceCount++;
Entry = (EFI_SYSTEM_RESOURCE_ENTRY *)(Table + 1) + Table->FwResourceCount;
CopyGuid (&Entry->FwClass, &FmpImageInfoBuf->ImageTypeId);
@ -195,11 +245,11 @@ CreateEsrtEntry (
Entry->FwType = (UINT32)(ESRT_FW_TYPE_DEVICEFIRMWARE);
}
Entry->FwVersion = FmpImageInfoBuf->Version;
Entry->FwVersion = FmpImageInfoBuf->Version;
Entry->LowestSupportedFwVersion = 0;
Entry->CapsuleFlags = 0;
Entry->LastAttemptVersion = 0;
Entry->LastAttemptStatus = 0;
Entry->CapsuleFlags = 0;
Entry->LastAttemptVersion = 0;
Entry->LastAttemptStatus = 0;
//
// VERSION 2 has Lowest Supported
@ -213,12 +263,93 @@ CreateEsrtEntry (
//
if (FmpVersion >= 3) {
Entry->LastAttemptVersion = FmpImageInfoBuf->LastAttemptVersion;
Entry->LastAttemptStatus = FmpImageInfoBuf->LastAttemptStatus;
Entry->LastAttemptStatus = FmpImageInfoBuf->LastAttemptStatus;
}
//
// Increment the number of active ESRT Table Entries
//
Table->FwResourceCount++;
return EFI_SUCCESS;
}
/**
Function to retrieve the EFI_FIRMWARE_IMAGE_DESCRIPTOR from an FMP Instance.
The returned buffer is allocated using AllocatePool() and must be freed by the
caller using FreePool().
@param[in] Fmp Pointer to an EFI_FIRMWARE_MANAGEMENT_PROTOCOL.
@param[out] FmpImageInfoDescriptorVer Pointer to the version number associated
with the returned EFI_FIRMWARE_IMAGE_DESCRIPTOR.
@param[out] FmpImageInfoCount Pointer to the number of the returned
EFI_FIRMWARE_IMAGE_DESCRIPTORs.
@param[out] DescriptorSize Pointer to the size, in bytes, of each
returned EFI_FIRMWARE_IMAGE_DESCRIPTOR.
@return Pointer to the retrieved EFI_FIRMWARE_IMAGE_DESCRIPTOR. If the
descriptor can not be retrieved, then NULL is returned.
**/
EFI_FIRMWARE_IMAGE_DESCRIPTOR *
FmpGetFirmwareImageDescriptor (
IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp,
OUT UINT32 *FmpImageInfoDescriptorVer,
OUT UINT8 *FmpImageInfoCount,
OUT UINTN *DescriptorSize
)
{
EFI_STATUS Status;
UINTN ImageInfoSize;
UINT32 PackageVersion;
CHAR16 *PackageVersionName;
EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf;
ImageInfoSize = 0;
Status = Fmp->GetImageInfo (
Fmp, // FMP Pointer
&ImageInfoSize, // Buffer Size (in this case 0)
NULL, // NULL so we can get size
FmpImageInfoDescriptorVer, // DescriptorVersion
FmpImageInfoCount, // DescriptorCount
DescriptorSize, // DescriptorSize
&PackageVersion, // PackageVersion
&PackageVersionName // PackageVersionName
);
if (Status != EFI_BUFFER_TOO_SMALL) {
DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Unexpected Failure in GetImageInfo. Status = %r\n", Status));
return NULL;
}
FmpImageInfoBuf = AllocateZeroPool (ImageInfoSize);
if (FmpImageInfoBuf == NULL) {
DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to get memory for FMP descriptor.\n"));
return NULL;
}
PackageVersionName = NULL;
Status = Fmp->GetImageInfo (
Fmp, // FMP Pointer
&ImageInfoSize, // ImageInfoSize
FmpImageInfoBuf, // ImageInfo
FmpImageInfoDescriptorVer, // DescriptorVersion
FmpImageInfoCount, // DescriptorCount
DescriptorSize, // DescriptorSize
&PackageVersion, // PackageVersion
&PackageVersionName // PackageVersionName
);
if (PackageVersionName != NULL) {
FreePool (PackageVersionName);
}
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failure in GetImageInfo. Status = %r\n", Status));
FreePool (FmpImageInfoBuf);
return NULL;
}
return FmpImageInfoBuf;
}
/**
Function to create ESRT based on FMP Instances.
Create ESRT table, get the descriptors from FMP Instance and
@ -232,29 +363,26 @@ CreateFmpBasedEsrt (
VOID
)
{
EFI_STATUS Status;
EFI_SYSTEM_RESOURCE_TABLE *Table;
UINTN NoProtocols;
VOID **Buffer;
UINTN Index;
EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp;
UINTN DescriptorSize;
EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf;
EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBufOrg;
UINT8 FmpImageInfoCount;
UINT32 FmpImageInfoDescriptorVer;
UINTN ImageInfoSize;
UINT32 PackageVersion;
CHAR16 *PackageVersionName;
EFI_STATUS Status;
UINTN NoProtocols;
VOID **Buffer;
UINTN Index;
UINT32 FmpImageInfoDescriptorVer;
UINT8 FmpImageInfoCount;
UINTN DescriptorSize;
UINT32 NumberOfDescriptors;
EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf;
EFI_FIRMWARE_IMAGE_DESCRIPTOR *OrgFmpImageInfoBuf;
EFI_SYSTEM_RESOURCE_TABLE *Table;
GUID_HARDWAREINSTANCE_PAIR *HardwareInstances;
Status = EFI_SUCCESS;
Table = NULL;
NoProtocols = 0;
Buffer = NULL;
PackageVersionName = NULL;
FmpImageInfoBuf = NULL;
FmpImageInfoBufOrg = NULL;
Fmp = NULL;
OrgFmpImageInfoBuf = NULL;
Table = NULL;
HardwareInstances = NULL;
Status = EfiLocateProtocolBuffer (
&gEfiFirmwareManagementProtocolGuid,
@ -266,69 +394,64 @@ CreateFmpBasedEsrt (
}
//
// Allocate Memory for table
// Count the total number of EFI_FIRMWARE_IMAGE_DESCRIPTORs
//
for (Index = 0, NumberOfDescriptors = 0; Index < NoProtocols; Index++) {
FmpImageInfoBuf = FmpGetFirmwareImageDescriptor (
(EFI_FIRMWARE_MANAGEMENT_PROTOCOL *) Buffer[Index],
&FmpImageInfoDescriptorVer,
&FmpImageInfoCount,
&DescriptorSize
);
if (FmpImageInfoBuf != NULL) {
NumberOfDescriptors += FmpImageInfoCount;
FreePool (FmpImageInfoBuf);
}
}
//
// Allocate ESRT Table and GUID/HardwareInstance table
//
Table = AllocateZeroPool (
(GROWTH_STEP * sizeof (EFI_SYSTEM_RESOURCE_ENTRY)) + sizeof (EFI_SYSTEM_RESOURCE_TABLE)
(NumberOfDescriptors * sizeof (EFI_SYSTEM_RESOURCE_ENTRY)) + sizeof (EFI_SYSTEM_RESOURCE_TABLE)
);
if (Table == NULL) {
DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to allocate memory for ESRT.\n"));
gBS->FreePool (Buffer);
FreePool (Buffer);
return NULL;
}
HardwareInstances = AllocateZeroPool (NumberOfDescriptors * sizeof (GUID_HARDWAREINSTANCE_PAIR));
if (HardwareInstances == NULL) {
DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to allocate memory for HW Instance Table.\n"));
FreePool (Table);
FreePool (Buffer);
return NULL;
}
//
// Initialize ESRT Table
//
Table->FwResourceCount = 0;
Table->FwResourceCountMax = GROWTH_STEP;
Table->FwResourceCountMax = NumberOfDescriptors;
Table->FwResourceVersion = EFI_SYSTEM_RESOURCE_TABLE_FIRMWARE_RESOURCE_VERSION;
NumberOfDescriptors = 0;
for (Index = 0; Index < NoProtocols; Index++) {
Fmp = (EFI_FIRMWARE_MANAGEMENT_PROTOCOL *) Buffer[Index];
ImageInfoSize = 0;
Status = Fmp->GetImageInfo (
Fmp, // FMP Pointer
&ImageInfoSize, // Buffer Size (in this case 0)
NULL, // NULL so we can get size
&FmpImageInfoDescriptorVer, // DescriptorVersion
&FmpImageInfoCount, // DescriptorCount
&DescriptorSize, // DescriptorSize
&PackageVersion, // PackageVersion
&PackageVersionName // PackageVersionName
);
if (Status != EFI_BUFFER_TOO_SMALL) {
DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Unexpected Failure in GetImageInfo. Status = %r\n", Status));
continue;
}
FmpImageInfoBuf = AllocateZeroPool (ImageInfoSize);
FmpImageInfoBuf = FmpGetFirmwareImageDescriptor (
(EFI_FIRMWARE_MANAGEMENT_PROTOCOL *) Buffer[Index],
&FmpImageInfoDescriptorVer,
&FmpImageInfoCount,
&DescriptorSize
);
if (FmpImageInfoBuf == NULL) {
DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to get memory for descriptors.\n"));
continue;
}
FmpImageInfoBufOrg = FmpImageInfoBuf;
PackageVersionName = NULL;
Status = Fmp->GetImageInfo (
Fmp,
&ImageInfoSize, // ImageInfoSize
FmpImageInfoBuf, // ImageInfo
&FmpImageInfoDescriptorVer, // DescriptorVersion
&FmpImageInfoCount, // DescriptorCount
&DescriptorSize, // DescriptorSize
&PackageVersion, // PackageVersion
&PackageVersionName // PackageVersionName
);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failure in GetImageInfo. Status = %r\n", Status));
FreePool (FmpImageInfoBufOrg);
FmpImageInfoBufOrg = NULL;
continue;
}
//
// Check each descriptor and read from the one specified
//
OrgFmpImageInfoBuf = FmpImageInfoBuf;
while (FmpImageInfoCount > 0) {
//
// If the descriptor has the IN USE bit set, create ESRT entry otherwise ignore.
@ -337,7 +460,7 @@ CreateFmpBasedEsrt (
//
// Create ESRT entry
//
CreateEsrtEntry (&Table, FmpImageInfoBuf, FmpImageInfoDescriptorVer);
CreateEsrtEntry (Table, HardwareInstances, &NumberOfDescriptors, FmpImageInfoBuf, FmpImageInfoDescriptorVer);
}
FmpImageInfoCount--;
//
@ -346,15 +469,12 @@ CreateFmpBasedEsrt (
FmpImageInfoBuf = (EFI_FIRMWARE_IMAGE_DESCRIPTOR *)(((UINT8 *)FmpImageInfoBuf) + DescriptorSize);
}
if (PackageVersionName != NULL) {
FreePool (PackageVersionName);
PackageVersionName = NULL;
}
FreePool (FmpImageInfoBufOrg);
FmpImageInfoBufOrg = NULL;
FreePool (OrgFmpImageInfoBuf);
OrgFmpImageInfoBuf = NULL;
}
gBS->FreePool (Buffer);
FreePool (Buffer);
FreePool (HardwareInstances);
return Table;
}