2 Publishes ESRT table from Firmware Management Protocol instances
4 Copyright (c) 2016, Microsoft Corporation
5 Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
8 SPDX-License-Identifier: BSD-2-Clause-Patent
13 #include <Library/BaseLib.h>
14 #include <Library/BaseMemoryLib.h>
15 #include <Library/MemoryAllocationLib.h>
16 #include <Library/UefiBootServicesTableLib.h>
17 #include <Library/DebugLib.h>
18 #include <Library/PcdLib.h>
19 #include <Library/UefiLib.h>
20 #include <Protocol/FirmwareManagement.h>
21 #include <Guid/EventGroup.h>
22 #include <Guid/SystemResourceTable.h>
25 /// Structure for array of unique GUID/HardwareInstance pairs from the
26 /// current set of EFI_FIRMWARE_IMAGE_DESCRIPTORs from all FMP Protocols.
30 /// A unique GUID identifying the firmware image type.
32 EFI_GUID ImageTypeGuid
;
34 /// An optional number to identify the unique hardware instance within the
35 /// system for devices that may have multiple instances whenever possible.
37 UINT64 HardwareInstance
;
38 } GUID_HARDWAREINSTANCE_PAIR
;
41 Print ESRT to debug console.
43 @param[in] Table Pointer to the ESRT table.
49 IN EFI_SYSTEM_RESOURCE_TABLE
*Table
53 Install EFI System Resource Table into the UEFI Configuration Table
55 @param[in] Table Pointer to the ESRT.
61 InstallEfiSystemResourceTableInUefiConfigurationTable (
62 IN EFI_SYSTEM_RESOURCE_TABLE
*Table
68 if (Table
->FwResourceCount
== 0) {
69 DEBUG ((DEBUG_ERROR
, "EsrtFmpDxe: Can't install ESRT table because it has zero Entries. \n"));
70 Status
= EFI_UNSUPPORTED
;
73 // Install the pointer into config table
75 Status
= gBS
->InstallConfigurationTable (&gEfiSystemResourceTableGuid
, Table
);
76 if (EFI_ERROR (Status
)) {
77 DEBUG ((DEBUG_ERROR
, "EsrtFmpDxe: Can't install ESRT table. Status: %r. \n", Status
));
79 DEBUG ((DEBUG_INFO
, "EsrtFmpDxe: Installed ESRT table. \n"));
87 Return if this FMP is a system FMP or a device FMP, based upon FmpImageInfo.
89 @param[in] FmpImageInfo A pointer to EFI_FIRMWARE_IMAGE_DESCRIPTOR
91 @return TRUE It is a system FMP.
92 @return FALSE It is a device FMP.
96 IN EFI_FIRMWARE_IMAGE_DESCRIPTOR
*FmpImageInfo
103 Guid
= PcdGetPtr (PcdSystemFmpCapsuleImageTypeIdGuid
);
104 Count
= PcdGetSize (PcdSystemFmpCapsuleImageTypeIdGuid
) / sizeof (GUID
);
106 for (Index
= 0; Index
< Count
; Index
++, Guid
++) {
107 if (CompareGuid (&FmpImageInfo
->ImageTypeId
, Guid
)) {
116 Function to create a single ESRT Entry and add it to the ESRT with
117 a given FMP descriptor. If the GUID is already in the ESRT, then the ESRT
120 @param[in,out] Table Pointer to the ESRT Table.
121 @param[in,out] HardwareInstances Pointer to the GUID_HARDWAREINSTANCE_PAIR.
122 @param[in,out] NumberOfDescriptors The number of EFI_FIRMWARE_IMAGE_DESCRIPTORs.
123 @param[in] FmpImageInfoBuf Pointer to the EFI_FIRMWARE_IMAGE_DESCRIPTOR.
124 @param[in] FmpVersion FMP Version.
126 @retval EFI_SUCCESS FmpImageInfoBuf was use to fill in a new ESRT entry
128 @retval EFI_SUCCESS The ImageTypeId GUID in FmpImageInfoBuf matches an
129 existing ESRT entry in Table, and the information
130 from FmpImageInfoBuf was merged into the the existing
132 @retval EFI_UNSPOORTED The GUID/HardareInstance in FmpImageInfoBuf has is a
138 IN OUT EFI_SYSTEM_RESOURCE_TABLE
*Table
,
139 IN OUT GUID_HARDWAREINSTANCE_PAIR
*HardwareInstances
,
140 IN OUT UINT32
*NumberOfDescriptors
,
141 IN EFI_FIRMWARE_IMAGE_DESCRIPTOR
*FmpImageInfoBuf
,
146 EFI_SYSTEM_RESOURCE_ENTRY
*Entry
;
147 UINT64 FmpHardwareInstance
;
149 FmpHardwareInstance
= 0;
150 if (FmpVersion
>= 3) {
151 FmpHardwareInstance
= FmpImageInfoBuf
->HardwareInstance
;
155 // Check to see of FmpImageInfoBuf GUID/HardwareInstance is unique
156 // Skip if HardwareInstance is 0 as this is the case if FmpVersion < 3
157 // or the device can not create a unique ID per UEFI specification
159 if (FmpHardwareInstance
!= 0) {
160 for (Index
= 0; Index
< *NumberOfDescriptors
; Index
++) {
161 if (CompareGuid (&HardwareInstances
[Index
].ImageTypeGuid
, &FmpImageInfoBuf
->ImageTypeId
)) {
162 if (HardwareInstances
[Index
].HardwareInstance
== FmpHardwareInstance
) {
163 DEBUG ((DEBUG_ERROR
, "EsrtFmpDxe: Duplicate firmware image descriptor with GUID %g HardwareInstance:0x%x\n", &FmpImageInfoBuf
->ImageTypeId
, FmpHardwareInstance
));
165 !CompareGuid (&HardwareInstances
[Index
].ImageTypeGuid
, &FmpImageInfoBuf
->ImageTypeId
) ||
166 HardwareInstances
[Index
].HardwareInstance
!= FmpHardwareInstance
168 return EFI_UNSUPPORTED
;
175 // Record new GUID/HardwareInstance pair
177 CopyGuid (&HardwareInstances
[*NumberOfDescriptors
].ImageTypeGuid
, &FmpImageInfoBuf
->ImageTypeId
);
178 HardwareInstances
[*NumberOfDescriptors
].HardwareInstance
= FmpHardwareInstance
;
179 *NumberOfDescriptors
= *NumberOfDescriptors
+ 1;
181 DEBUG ((DEBUG_INFO
, "EsrtFmpDxe: Add new image descriptor with GUID %g HardwareInstance:0x%x\n", &FmpImageInfoBuf
->ImageTypeId
, FmpHardwareInstance
));
184 // Check to see if GUID is already in the ESRT table
186 Entry
= (EFI_SYSTEM_RESOURCE_ENTRY
*)(Table
+ 1);
187 for (Index
= 0; Index
< Table
->FwResourceCount
; Index
++, Entry
++) {
188 if (!CompareGuid (&Entry
->FwClass
, &FmpImageInfoBuf
->ImageTypeId
)) {
192 DEBUG ((DEBUG_INFO
, "EsrtFmpDxe: ESRT Entry already exists for FMP Instance with GUID %g\n", &Entry
->FwClass
));
195 // Set ESRT FwVersion to the smaller of the two values
197 Entry
->FwVersion
= MIN (FmpImageInfoBuf
->Version
, Entry
->FwVersion
);
200 // VERSION 2 has Lowest Supported
202 if (FmpVersion
>= 2) {
204 // Set ESRT LowestSupportedFwVersion to the smaller of the two values
206 Entry
->LowestSupportedFwVersion
=
208 FmpImageInfoBuf
->LowestSupportedImageVersion
,
209 Entry
->LowestSupportedFwVersion
214 // VERSION 3 supports last attempt values
216 if (FmpVersion
>= 3) {
218 // Update the ESRT entry with the last attempt status and last attempt
219 // version from the first FMP instance whose last attempt status is not
220 // SUCCESS. If all FMP instances are SUCCESS, then set version to the
221 // smallest value from all FMP instances.
223 if (Entry
->LastAttemptStatus
== LAST_ATTEMPT_STATUS_SUCCESS
) {
224 if (FmpImageInfoBuf
->LastAttemptStatus
!= LAST_ATTEMPT_STATUS_SUCCESS
) {
225 Entry
->LastAttemptStatus
= FmpImageInfoBuf
->LastAttemptStatus
;
226 Entry
->LastAttemptVersion
= FmpImageInfoBuf
->LastAttemptVersion
;
228 Entry
->LastAttemptVersion
=
230 FmpImageInfoBuf
->LastAttemptVersion
,
231 Entry
->LastAttemptVersion
241 // Add a new ESRT Table Entry
243 Entry
= (EFI_SYSTEM_RESOURCE_ENTRY
*)(Table
+ 1) + Table
->FwResourceCount
;
245 CopyGuid (&Entry
->FwClass
, &FmpImageInfoBuf
->ImageTypeId
);
247 if (IsSystemFmp (FmpImageInfoBuf
)) {
248 DEBUG ((DEBUG_INFO
, "EsrtFmpDxe: Found an ESRT entry for a System Device.\n"));
249 Entry
->FwType
= (UINT32
)(ESRT_FW_TYPE_SYSTEMFIRMWARE
);
251 Entry
->FwType
= (UINT32
)(ESRT_FW_TYPE_DEVICEFIRMWARE
);
254 Entry
->FwVersion
= FmpImageInfoBuf
->Version
;
255 Entry
->LowestSupportedFwVersion
= 0;
256 Entry
->CapsuleFlags
= 0;
257 Entry
->LastAttemptVersion
= 0;
258 Entry
->LastAttemptStatus
= 0;
261 // VERSION 2 has Lowest Supported
263 if (FmpVersion
>= 2) {
264 Entry
->LowestSupportedFwVersion
= FmpImageInfoBuf
->LowestSupportedImageVersion
;
268 // VERSION 3 supports last attempt values
270 if (FmpVersion
>= 3) {
271 Entry
->LastAttemptVersion
= FmpImageInfoBuf
->LastAttemptVersion
;
272 Entry
->LastAttemptStatus
= FmpImageInfoBuf
->LastAttemptStatus
;
276 // Increment the number of active ESRT Table Entries
278 Table
->FwResourceCount
++;
284 Function to retrieve the EFI_FIRMWARE_IMAGE_DESCRIPTOR from an FMP Instance.
285 The returned buffer is allocated using AllocatePool() and must be freed by the
286 caller using FreePool().
288 @param[in] Fmp Pointer to an EFI_FIRMWARE_MANAGEMENT_PROTOCOL.
289 @param[out] FmpImageInfoDescriptorVer Pointer to the version number associated
290 with the returned EFI_FIRMWARE_IMAGE_DESCRIPTOR.
291 @param[out] FmpImageInfoCount Pointer to the number of the returned
292 EFI_FIRMWARE_IMAGE_DESCRIPTORs.
293 @param[out] DescriptorSize Pointer to the size, in bytes, of each
294 returned EFI_FIRMWARE_IMAGE_DESCRIPTOR.
296 @return Pointer to the retrieved EFI_FIRMWARE_IMAGE_DESCRIPTOR. If the
297 descriptor can not be retrieved, then NULL is returned.
300 EFI_FIRMWARE_IMAGE_DESCRIPTOR
*
301 FmpGetFirmwareImageDescriptor (
302 IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL
*Fmp
,
303 OUT UINT32
*FmpImageInfoDescriptorVer
,
304 OUT UINT8
*FmpImageInfoCount
,
305 OUT UINTN
*DescriptorSize
310 UINT32 PackageVersion
;
311 CHAR16
*PackageVersionName
;
312 EFI_FIRMWARE_IMAGE_DESCRIPTOR
*FmpImageInfoBuf
;
315 Status
= Fmp
->GetImageInfo (
317 &ImageInfoSize
, // Buffer Size (in this case 0)
318 NULL
, // NULL so we can get size
319 FmpImageInfoDescriptorVer
, // DescriptorVersion
320 FmpImageInfoCount
, // DescriptorCount
321 DescriptorSize
, // DescriptorSize
322 &PackageVersion
, // PackageVersion
323 &PackageVersionName
// PackageVersionName
325 if (Status
!= EFI_BUFFER_TOO_SMALL
) {
326 DEBUG ((DEBUG_ERROR
, "EsrtFmpDxe: Unexpected Failure in GetImageInfo. Status = %r\n", Status
));
330 FmpImageInfoBuf
= AllocateZeroPool (ImageInfoSize
);
331 if (FmpImageInfoBuf
== NULL
) {
332 DEBUG ((DEBUG_ERROR
, "EsrtFmpDxe: Failed to get memory for FMP descriptor.\n"));
336 PackageVersionName
= NULL
;
337 Status
= Fmp
->GetImageInfo (
339 &ImageInfoSize
, // ImageInfoSize
340 FmpImageInfoBuf
, // ImageInfo
341 FmpImageInfoDescriptorVer
, // DescriptorVersion
342 FmpImageInfoCount
, // DescriptorCount
343 DescriptorSize
, // DescriptorSize
344 &PackageVersion
, // PackageVersion
345 &PackageVersionName
// PackageVersionName
347 if (PackageVersionName
!= NULL
) {
348 FreePool (PackageVersionName
);
351 if (EFI_ERROR (Status
)) {
352 DEBUG ((DEBUG_ERROR
, "EsrtFmpDxe: Failure in GetImageInfo. Status = %r\n", Status
));
353 FreePool (FmpImageInfoBuf
);
357 return FmpImageInfoBuf
;
361 Function to create ESRT based on FMP Instances.
362 Create ESRT table, get the descriptors from FMP Instance and
363 create ESRT entries (ESRE).
365 @return Pointer to the ESRT created.
368 EFI_SYSTEM_RESOURCE_TABLE
*
377 UINT32 FmpImageInfoDescriptorVer
;
378 UINT8 FmpImageInfoCount
;
379 UINTN DescriptorSize
;
380 UINT32 NumberOfDescriptors
;
381 EFI_FIRMWARE_IMAGE_DESCRIPTOR
*FmpImageInfoBuf
;
382 EFI_FIRMWARE_IMAGE_DESCRIPTOR
*OrgFmpImageInfoBuf
;
383 EFI_SYSTEM_RESOURCE_TABLE
*Table
;
384 GUID_HARDWAREINSTANCE_PAIR
*HardwareInstances
;
386 Status
= EFI_SUCCESS
;
389 FmpImageInfoBuf
= NULL
;
390 OrgFmpImageInfoBuf
= NULL
;
392 HardwareInstances
= NULL
;
394 Status
= EfiLocateProtocolBuffer (
395 &gEfiFirmwareManagementProtocolGuid
,
399 if (EFI_ERROR (Status
) || (Buffer
== NULL
)) {
404 // Count the total number of EFI_FIRMWARE_IMAGE_DESCRIPTORs
406 for (Index
= 0, NumberOfDescriptors
= 0; Index
< NoProtocols
; Index
++) {
407 FmpImageInfoBuf
= FmpGetFirmwareImageDescriptor (
408 (EFI_FIRMWARE_MANAGEMENT_PROTOCOL
*)Buffer
[Index
],
409 &FmpImageInfoDescriptorVer
,
413 if (FmpImageInfoBuf
!= NULL
) {
414 NumberOfDescriptors
+= FmpImageInfoCount
;
415 FreePool (FmpImageInfoBuf
);
420 // Allocate ESRT Table and GUID/HardwareInstance table
422 Table
= AllocateZeroPool (
423 (NumberOfDescriptors
* sizeof (EFI_SYSTEM_RESOURCE_ENTRY
)) + sizeof (EFI_SYSTEM_RESOURCE_TABLE
)
426 DEBUG ((DEBUG_ERROR
, "EsrtFmpDxe: Failed to allocate memory for ESRT.\n"));
431 HardwareInstances
= AllocateZeroPool (NumberOfDescriptors
* sizeof (GUID_HARDWAREINSTANCE_PAIR
));
432 if (HardwareInstances
== NULL
) {
433 DEBUG ((DEBUG_ERROR
, "EsrtFmpDxe: Failed to allocate memory for HW Instance Table.\n"));
440 // Initialize ESRT Table
442 Table
->FwResourceCount
= 0;
443 Table
->FwResourceCountMax
= NumberOfDescriptors
;
444 Table
->FwResourceVersion
= EFI_SYSTEM_RESOURCE_TABLE_FIRMWARE_RESOURCE_VERSION
;
446 NumberOfDescriptors
= 0;
447 for (Index
= 0; Index
< NoProtocols
; Index
++) {
448 FmpImageInfoBuf
= FmpGetFirmwareImageDescriptor (
449 (EFI_FIRMWARE_MANAGEMENT_PROTOCOL
*)Buffer
[Index
],
450 &FmpImageInfoDescriptorVer
,
454 if (FmpImageInfoBuf
== NULL
) {
459 // Check each descriptor and read from the one specified
461 OrgFmpImageInfoBuf
= FmpImageInfoBuf
;
462 while (FmpImageInfoCount
> 0) {
464 // If the descriptor has the IN USE bit set, create ESRT entry otherwise ignore.
466 if ((FmpImageInfoBuf
->AttributesSetting
& FmpImageInfoBuf
->AttributesSupported
& IMAGE_ATTRIBUTE_IN_USE
) == IMAGE_ATTRIBUTE_IN_USE
) {
470 CreateEsrtEntry (Table
, HardwareInstances
, &NumberOfDescriptors
, FmpImageInfoBuf
, FmpImageInfoDescriptorVer
);
475 // Increment the buffer pointer ahead by the size of the descriptor
477 FmpImageInfoBuf
= (EFI_FIRMWARE_IMAGE_DESCRIPTOR
*)(((UINT8
*)FmpImageInfoBuf
) + DescriptorSize
);
480 FreePool (OrgFmpImageInfoBuf
);
481 OrgFmpImageInfoBuf
= NULL
;
485 FreePool (HardwareInstances
);
490 Notify function for event group EFI_EVENT_GROUP_READY_TO_BOOT. This is used to
491 install the Efi System Resource Table.
493 @param[in] Event The Event that is being processed.
494 @param[in] Context The Event Context.
499 EsrtReadyToBootEventNotify (
505 EFI_SYSTEM_RESOURCE_TABLE
*Table
;
507 Table
= CreateFmpBasedEsrt ();
510 // Print table on debug builds
516 Status
= InstallEfiSystemResourceTableInUefiConfigurationTable (Table
);
517 if (EFI_ERROR (Status
)) {
521 DEBUG ((DEBUG_ERROR
, "EsrtFmpDxe: Can't install ESRT table because it is NULL. \n"));
525 // Close the event to prevent it be signalled again.
527 gBS
->CloseEvent (Event
);
531 The module Entry Point of the Efi System Resource Table DXE driver.
533 @param[in] ImageHandle The firmware allocated handle for the EFI image.
534 @param[in] SystemTable A pointer to the EFI System Table.
536 @retval EFI_SUCCESS The entry point is executed successfully.
537 @retval Other Some error occurs when executing this entry point.
543 IN EFI_HANDLE ImageHandle
,
544 IN EFI_SYSTEM_TABLE
*SystemTable
548 EFI_EVENT EsrtReadyToBootEvent
;
551 // Register notify function to install ESRT on ReadyToBoot Event.
553 Status
= EfiCreateEventReadyToBootEx (
555 EsrtReadyToBootEventNotify
,
557 &EsrtReadyToBootEvent
560 ASSERT_EFI_ERROR (Status
);
561 if (EFI_ERROR (Status
)) {
562 DEBUG ((DEBUG_ERROR
, "EsrtFmpDxe: Failed to register for ready to boot\n"));