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"));
86 Return if this FMP is a system FMP or a device FMP, based upon FmpImageInfo.
88 @param[in] FmpImageInfo A pointer to EFI_FIRMWARE_IMAGE_DESCRIPTOR
90 @return TRUE It is a system FMP.
91 @return FALSE It is a device FMP.
95 IN EFI_FIRMWARE_IMAGE_DESCRIPTOR
*FmpImageInfo
102 Guid
= PcdGetPtr (PcdSystemFmpCapsuleImageTypeIdGuid
);
103 Count
= PcdGetSize (PcdSystemFmpCapsuleImageTypeIdGuid
) / sizeof(GUID
);
105 for (Index
= 0; Index
< Count
; Index
++, Guid
++) {
106 if (CompareGuid (&FmpImageInfo
->ImageTypeId
, Guid
)) {
115 Function to create a single ESRT Entry and add it to the ESRT with
116 a given FMP descriptor. If the GUID is already in the ESRT, then the ESRT
119 @param[in,out] Table Pointer to the ESRT Table.
120 @param[in,out] HardwareInstances Pointer to the GUID_HARDWAREINSTANCE_PAIR.
121 @param[in,out] NumberOfDescriptors The number of EFI_FIRMWARE_IMAGE_DESCRIPTORs.
122 @param[in] FmpImageInfoBuf Pointer to the EFI_FIRMWARE_IMAGE_DESCRIPTOR.
123 @param[in] FmpVersion FMP Version.
125 @retval EFI_SUCCESS FmpImageInfoBuf was use to fill in a new ESRT entry
127 @retval EFI_SUCCESS The ImageTypeId GUID in FmpImageInfoBuf matches an
128 existing ESRT entry in Table, and the information
129 from FmpImageInfoBuf was merged into the the existing
131 @retval EFI_UNSPOORTED The GUID/HardareInstance in FmpImageInfoBuf has is a
137 IN OUT EFI_SYSTEM_RESOURCE_TABLE
*Table
,
138 IN OUT GUID_HARDWAREINSTANCE_PAIR
*HardwareInstances
,
139 IN OUT UINT32
*NumberOfDescriptors
,
140 IN EFI_FIRMWARE_IMAGE_DESCRIPTOR
*FmpImageInfoBuf
,
145 EFI_SYSTEM_RESOURCE_ENTRY
*Entry
;
146 UINT64 FmpHardwareInstance
;
148 FmpHardwareInstance
= 0;
149 if (FmpVersion
>= 3) {
150 FmpHardwareInstance
= FmpImageInfoBuf
->HardwareInstance
;
154 // Check to see of FmpImageInfoBuf GUID/HardwareInstance is unique
156 for (Index
= 0; Index
< *NumberOfDescriptors
; Index
++) {
157 if (CompareGuid (&HardwareInstances
[Index
].ImageTypeGuid
, &FmpImageInfoBuf
->ImageTypeId
)) {
158 if (HardwareInstances
[Index
].HardwareInstance
== FmpHardwareInstance
) {
159 DEBUG ((DEBUG_ERROR
, "EsrtFmpDxe: Duplicate firmware image descriptor with GUID %g HardwareInstance:0x%x\n", &FmpImageInfoBuf
->ImageTypeId
, FmpHardwareInstance
));
161 !CompareGuid (&HardwareInstances
[Index
].ImageTypeGuid
, &FmpImageInfoBuf
->ImageTypeId
) ||
162 HardwareInstances
[Index
].HardwareInstance
!= FmpHardwareInstance
164 return EFI_UNSUPPORTED
;
170 // Record new GUID/HardwareInstance pair
172 CopyGuid (&HardwareInstances
[*NumberOfDescriptors
].ImageTypeGuid
, &FmpImageInfoBuf
->ImageTypeId
);
173 HardwareInstances
[*NumberOfDescriptors
].HardwareInstance
= FmpHardwareInstance
;
174 *NumberOfDescriptors
= *NumberOfDescriptors
+ 1;
176 DEBUG ((DEBUG_INFO
, "EsrtFmpDxe: Add new image descriptor with GUID %g HardwareInstance:0x%x\n", &FmpImageInfoBuf
->ImageTypeId
, FmpHardwareInstance
));
179 // Check to see if GUID is already in the ESRT table
181 Entry
= (EFI_SYSTEM_RESOURCE_ENTRY
*)(Table
+ 1);
182 for (Index
= 0; Index
< Table
->FwResourceCount
; Index
++, Entry
++) {
183 if (!CompareGuid (&Entry
->FwClass
, &FmpImageInfoBuf
->ImageTypeId
)) {
186 DEBUG ((DEBUG_INFO
, "EsrtFmpDxe: ESRT Entry already exists for FMP Instance with GUID %g\n", &Entry
->FwClass
));
189 // Set ESRT FwVersion to the smaller of the two values
191 Entry
->FwVersion
= MIN (FmpImageInfoBuf
->Version
, Entry
->FwVersion
);
194 // VERSION 2 has Lowest Supported
196 if (FmpVersion
>= 2) {
198 // Set ESRT LowestSupportedFwVersion to the smaller of the two values
200 Entry
->LowestSupportedFwVersion
=
202 FmpImageInfoBuf
->LowestSupportedImageVersion
,
203 Entry
->LowestSupportedFwVersion
208 // VERSION 3 supports last attempt values
210 if (FmpVersion
>= 3) {
212 // Update the ESRT entry with the last attempt status and last attempt
213 // version from the first FMP instance whose last attempt status is not
214 // SUCCESS. If all FMP instances are SUCCESS, then set version to the
215 // smallest value from all FMP instances.
217 if (Entry
->LastAttemptStatus
== LAST_ATTEMPT_STATUS_SUCCESS
) {
218 if (FmpImageInfoBuf
->LastAttemptStatus
!= LAST_ATTEMPT_STATUS_SUCCESS
) {
219 Entry
->LastAttemptStatus
= FmpImageInfoBuf
->LastAttemptStatus
;
220 Entry
->LastAttemptVersion
= FmpImageInfoBuf
->LastAttemptVersion
;
222 Entry
->LastAttemptVersion
=
224 FmpImageInfoBuf
->LastAttemptVersion
,
225 Entry
->LastAttemptVersion
235 // Add a new ESRT Table Entry
237 Entry
= (EFI_SYSTEM_RESOURCE_ENTRY
*)(Table
+ 1) + Table
->FwResourceCount
;
239 CopyGuid (&Entry
->FwClass
, &FmpImageInfoBuf
->ImageTypeId
);
241 if (IsSystemFmp (FmpImageInfoBuf
)) {
242 DEBUG ((DEBUG_INFO
, "EsrtFmpDxe: Found an ESRT entry for a System Device.\n"));
243 Entry
->FwType
= (UINT32
)(ESRT_FW_TYPE_SYSTEMFIRMWARE
);
245 Entry
->FwType
= (UINT32
)(ESRT_FW_TYPE_DEVICEFIRMWARE
);
248 Entry
->FwVersion
= FmpImageInfoBuf
->Version
;
249 Entry
->LowestSupportedFwVersion
= 0;
250 Entry
->CapsuleFlags
= 0;
251 Entry
->LastAttemptVersion
= 0;
252 Entry
->LastAttemptStatus
= 0;
255 // VERSION 2 has Lowest Supported
257 if (FmpVersion
>= 2) {
258 Entry
->LowestSupportedFwVersion
= FmpImageInfoBuf
->LowestSupportedImageVersion
;
262 // VERSION 3 supports last attempt values
264 if (FmpVersion
>= 3) {
265 Entry
->LastAttemptVersion
= FmpImageInfoBuf
->LastAttemptVersion
;
266 Entry
->LastAttemptStatus
= FmpImageInfoBuf
->LastAttemptStatus
;
270 // Increment the number of active ESRT Table Entries
272 Table
->FwResourceCount
++;
278 Function to retrieve the EFI_FIRMWARE_IMAGE_DESCRIPTOR from an FMP Instance.
279 The returned buffer is allocated using AllocatePool() and must be freed by the
280 caller using FreePool().
282 @param[in] Fmp Pointer to an EFI_FIRMWARE_MANAGEMENT_PROTOCOL.
283 @param[out] FmpImageInfoDescriptorVer Pointer to the version number associated
284 with the returned EFI_FIRMWARE_IMAGE_DESCRIPTOR.
285 @param[out] FmpImageInfoCount Pointer to the number of the returned
286 EFI_FIRMWARE_IMAGE_DESCRIPTORs.
287 @param[out] DescriptorSize Pointer to the size, in bytes, of each
288 returned EFI_FIRMWARE_IMAGE_DESCRIPTOR.
290 @return Pointer to the retrieved EFI_FIRMWARE_IMAGE_DESCRIPTOR. If the
291 descriptor can not be retrieved, then NULL is returned.
294 EFI_FIRMWARE_IMAGE_DESCRIPTOR
*
295 FmpGetFirmwareImageDescriptor (
296 IN EFI_FIRMWARE_MANAGEMENT_PROTOCOL
*Fmp
,
297 OUT UINT32
*FmpImageInfoDescriptorVer
,
298 OUT UINT8
*FmpImageInfoCount
,
299 OUT UINTN
*DescriptorSize
304 UINT32 PackageVersion
;
305 CHAR16
*PackageVersionName
;
306 EFI_FIRMWARE_IMAGE_DESCRIPTOR
*FmpImageInfoBuf
;
309 Status
= Fmp
->GetImageInfo (
311 &ImageInfoSize
, // Buffer Size (in this case 0)
312 NULL
, // NULL so we can get size
313 FmpImageInfoDescriptorVer
, // DescriptorVersion
314 FmpImageInfoCount
, // DescriptorCount
315 DescriptorSize
, // DescriptorSize
316 &PackageVersion
, // PackageVersion
317 &PackageVersionName
// PackageVersionName
319 if (Status
!= EFI_BUFFER_TOO_SMALL
) {
320 DEBUG ((DEBUG_ERROR
, "EsrtFmpDxe: Unexpected Failure in GetImageInfo. Status = %r\n", Status
));
324 FmpImageInfoBuf
= AllocateZeroPool (ImageInfoSize
);
325 if (FmpImageInfoBuf
== NULL
) {
326 DEBUG ((DEBUG_ERROR
, "EsrtFmpDxe: Failed to get memory for FMP descriptor.\n"));
330 PackageVersionName
= NULL
;
331 Status
= Fmp
->GetImageInfo (
333 &ImageInfoSize
, // ImageInfoSize
334 FmpImageInfoBuf
, // ImageInfo
335 FmpImageInfoDescriptorVer
, // DescriptorVersion
336 FmpImageInfoCount
, // DescriptorCount
337 DescriptorSize
, // DescriptorSize
338 &PackageVersion
, // PackageVersion
339 &PackageVersionName
// PackageVersionName
341 if (PackageVersionName
!= NULL
) {
342 FreePool (PackageVersionName
);
344 if (EFI_ERROR (Status
)) {
345 DEBUG ((DEBUG_ERROR
, "EsrtFmpDxe: Failure in GetImageInfo. Status = %r\n", Status
));
346 FreePool (FmpImageInfoBuf
);
350 return FmpImageInfoBuf
;
354 Function to create ESRT based on FMP Instances.
355 Create ESRT table, get the descriptors from FMP Instance and
356 create ESRT entries (ESRE).
358 @return Pointer to the ESRT created.
361 EFI_SYSTEM_RESOURCE_TABLE
*
370 UINT32 FmpImageInfoDescriptorVer
;
371 UINT8 FmpImageInfoCount
;
372 UINTN DescriptorSize
;
373 UINT32 NumberOfDescriptors
;
374 EFI_FIRMWARE_IMAGE_DESCRIPTOR
*FmpImageInfoBuf
;
375 EFI_FIRMWARE_IMAGE_DESCRIPTOR
*OrgFmpImageInfoBuf
;
376 EFI_SYSTEM_RESOURCE_TABLE
*Table
;
377 GUID_HARDWAREINSTANCE_PAIR
*HardwareInstances
;
379 Status
= EFI_SUCCESS
;
382 FmpImageInfoBuf
= NULL
;
383 OrgFmpImageInfoBuf
= NULL
;
385 HardwareInstances
= NULL
;
387 Status
= EfiLocateProtocolBuffer (
388 &gEfiFirmwareManagementProtocolGuid
,
392 if (EFI_ERROR(Status
) || (Buffer
== NULL
)) {
397 // Count the total number of EFI_FIRMWARE_IMAGE_DESCRIPTORs
399 for (Index
= 0, NumberOfDescriptors
= 0; Index
< NoProtocols
; Index
++) {
400 FmpImageInfoBuf
= FmpGetFirmwareImageDescriptor (
401 (EFI_FIRMWARE_MANAGEMENT_PROTOCOL
*) Buffer
[Index
],
402 &FmpImageInfoDescriptorVer
,
406 if (FmpImageInfoBuf
!= NULL
) {
407 NumberOfDescriptors
+= FmpImageInfoCount
;
408 FreePool (FmpImageInfoBuf
);
413 // Allocate ESRT Table and GUID/HardwareInstance table
415 Table
= AllocateZeroPool (
416 (NumberOfDescriptors
* sizeof (EFI_SYSTEM_RESOURCE_ENTRY
)) + sizeof (EFI_SYSTEM_RESOURCE_TABLE
)
419 DEBUG ((DEBUG_ERROR
, "EsrtFmpDxe: Failed to allocate memory for ESRT.\n"));
424 HardwareInstances
= AllocateZeroPool (NumberOfDescriptors
* sizeof (GUID_HARDWAREINSTANCE_PAIR
));
425 if (HardwareInstances
== NULL
) {
426 DEBUG ((DEBUG_ERROR
, "EsrtFmpDxe: Failed to allocate memory for HW Instance Table.\n"));
433 // Initialize ESRT Table
435 Table
->FwResourceCount
= 0;
436 Table
->FwResourceCountMax
= NumberOfDescriptors
;
437 Table
->FwResourceVersion
= EFI_SYSTEM_RESOURCE_TABLE_FIRMWARE_RESOURCE_VERSION
;
439 NumberOfDescriptors
= 0;
440 for (Index
= 0; Index
< NoProtocols
; Index
++) {
441 FmpImageInfoBuf
= FmpGetFirmwareImageDescriptor (
442 (EFI_FIRMWARE_MANAGEMENT_PROTOCOL
*) Buffer
[Index
],
443 &FmpImageInfoDescriptorVer
,
447 if (FmpImageInfoBuf
== NULL
) {
452 // Check each descriptor and read from the one specified
454 OrgFmpImageInfoBuf
= FmpImageInfoBuf
;
455 while (FmpImageInfoCount
> 0) {
457 // If the descriptor has the IN USE bit set, create ESRT entry otherwise ignore.
459 if ((FmpImageInfoBuf
->AttributesSetting
& FmpImageInfoBuf
->AttributesSupported
& IMAGE_ATTRIBUTE_IN_USE
) == IMAGE_ATTRIBUTE_IN_USE
) {
463 CreateEsrtEntry (Table
, HardwareInstances
, &NumberOfDescriptors
, FmpImageInfoBuf
, FmpImageInfoDescriptorVer
);
467 // Increment the buffer pointer ahead by the size of the descriptor
469 FmpImageInfoBuf
= (EFI_FIRMWARE_IMAGE_DESCRIPTOR
*)(((UINT8
*)FmpImageInfoBuf
) + DescriptorSize
);
472 FreePool (OrgFmpImageInfoBuf
);
473 OrgFmpImageInfoBuf
= NULL
;
477 FreePool (HardwareInstances
);
482 Notify function for event group EFI_EVENT_GROUP_READY_TO_BOOT. This is used to
483 install the Efi System Resource Table.
485 @param[in] Event The Event that is being processed.
486 @param[in] Context The Event Context.
491 EsrtReadyToBootEventNotify (
497 EFI_SYSTEM_RESOURCE_TABLE
*Table
;
499 Table
= CreateFmpBasedEsrt ();
502 // Print table on debug builds
508 Status
= InstallEfiSystemResourceTableInUefiConfigurationTable (Table
);
509 if (EFI_ERROR (Status
)) {
513 DEBUG ((DEBUG_ERROR
, "EsrtFmpDxe: Can't install ESRT table because it is NULL. \n"));
517 // Close the event to prevent it be signalled again.
519 gBS
->CloseEvent (Event
);
523 The module Entry Point of the Efi System Resource Table DXE driver.
525 @param[in] ImageHandle The firmware allocated handle for the EFI image.
526 @param[in] SystemTable A pointer to the EFI System Table.
528 @retval EFI_SUCCESS The entry point is executed successfully.
529 @retval Other Some error occurs when executing this entry point.
535 IN EFI_HANDLE ImageHandle
,
536 IN EFI_SYSTEM_TABLE
*SystemTable
540 EFI_EVENT EsrtReadyToBootEvent
;
543 // Register notify function to install ESRT on ReadyToBoot Event.
545 Status
= EfiCreateEventReadyToBootEx (
547 EsrtReadyToBootEventNotify
,
549 &EsrtReadyToBootEvent
552 ASSERT_EFI_ERROR (Status
);
553 if (EFI_ERROR (Status
)) {
554 DEBUG ((DEBUG_ERROR
, "EsrtFmpDxe: Failed to register for ready to boot\n"));