]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmp.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / MdeModulePkg / Universal / EsrtFmpDxe / EsrtFmp.c
1 /** @file
2 Publishes ESRT table from Firmware Management Protocol instances
3
4 Copyright (c) 2016, Microsoft Corporation
5 Copyright (c) 2018 - 2019, Intel Corporation. All rights reserved.<BR>
6
7 All rights reserved.
8 SPDX-License-Identifier: BSD-2-Clause-Patent
9
10 **/
11
12 #include <Uefi.h>
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>
23
24 ///
25 /// Structure for array of unique GUID/HardwareInstance pairs from the
26 /// current set of EFI_FIRMWARE_IMAGE_DESCRIPTORs from all FMP Protocols.
27 ///
28 typedef struct {
29 ///
30 /// A unique GUID identifying the firmware image type.
31 ///
32 EFI_GUID ImageTypeGuid;
33 ///
34 /// An optional number to identify the unique hardware instance within the
35 /// system for devices that may have multiple instances whenever possible.
36 ///
37 UINT64 HardwareInstance;
38 } GUID_HARDWAREINSTANCE_PAIR;
39
40 /**
41 Print ESRT to debug console.
42
43 @param[in] Table Pointer to the ESRT table.
44
45 **/
46 VOID
47 EFIAPI
48 PrintTable (
49 IN EFI_SYSTEM_RESOURCE_TABLE *Table
50 );
51
52 /**
53 Install EFI System Resource Table into the UEFI Configuration Table
54
55 @param[in] Table Pointer to the ESRT.
56
57 @return Status code.
58
59 **/
60 EFI_STATUS
61 InstallEfiSystemResourceTableInUefiConfigurationTable (
62 IN EFI_SYSTEM_RESOURCE_TABLE *Table
63 )
64 {
65 EFI_STATUS Status;
66
67 Status = EFI_SUCCESS;
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;
71 } else {
72 //
73 // Install the pointer into config table
74 //
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));
78 } else {
79 DEBUG ((DEBUG_INFO, "EsrtFmpDxe: Installed ESRT table. \n"));
80 }
81 }
82
83 return Status;
84 }
85
86 /**
87 Return if this FMP is a system FMP or a device FMP, based upon FmpImageInfo.
88
89 @param[in] FmpImageInfo A pointer to EFI_FIRMWARE_IMAGE_DESCRIPTOR
90
91 @return TRUE It is a system FMP.
92 @return FALSE It is a device FMP.
93 **/
94 BOOLEAN
95 IsSystemFmp (
96 IN EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfo
97 )
98 {
99 GUID *Guid;
100 UINTN Count;
101 UINTN Index;
102
103 Guid = PcdGetPtr (PcdSystemFmpCapsuleImageTypeIdGuid);
104 Count = PcdGetSize (PcdSystemFmpCapsuleImageTypeIdGuid) / sizeof (GUID);
105
106 for (Index = 0; Index < Count; Index++, Guid++) {
107 if (CompareGuid (&FmpImageInfo->ImageTypeId, Guid)) {
108 return TRUE;
109 }
110 }
111
112 return FALSE;
113 }
114
115 /**
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
118 entry is updated.
119
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.
125
126 @retval EFI_SUCCESS FmpImageInfoBuf was use to fill in a new ESRT entry
127 in Table.
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
131 ESRT entry.
132 @retval EFI_UNSPOORTED The GUID/HardareInstance in FmpImageInfoBuf has is a
133 duplicate.
134
135 **/
136 EFI_STATUS
137 CreateEsrtEntry (
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,
142 IN UINT32 FmpVersion
143 )
144 {
145 UINTN Index;
146 EFI_SYSTEM_RESOURCE_ENTRY *Entry;
147 UINT64 FmpHardwareInstance;
148
149 FmpHardwareInstance = 0;
150 if (FmpVersion >= 3) {
151 FmpHardwareInstance = FmpImageInfoBuf->HardwareInstance;
152 }
153
154 //
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
158 //
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));
164 ASSERT (
165 !CompareGuid (&HardwareInstances[Index].ImageTypeGuid, &FmpImageInfoBuf->ImageTypeId) ||
166 HardwareInstances[Index].HardwareInstance != FmpHardwareInstance
167 );
168 return EFI_UNSUPPORTED;
169 }
170 }
171 }
172 }
173
174 //
175 // Record new GUID/HardwareInstance pair
176 //
177 CopyGuid (&HardwareInstances[*NumberOfDescriptors].ImageTypeGuid, &FmpImageInfoBuf->ImageTypeId);
178 HardwareInstances[*NumberOfDescriptors].HardwareInstance = FmpHardwareInstance;
179 *NumberOfDescriptors = *NumberOfDescriptors + 1;
180
181 DEBUG ((DEBUG_INFO, "EsrtFmpDxe: Add new image descriptor with GUID %g HardwareInstance:0x%x\n", &FmpImageInfoBuf->ImageTypeId, FmpHardwareInstance));
182
183 //
184 // Check to see if GUID is already in the ESRT table
185 //
186 Entry = (EFI_SYSTEM_RESOURCE_ENTRY *)(Table + 1);
187 for (Index = 0; Index < Table->FwResourceCount; Index++, Entry++) {
188 if (!CompareGuid (&Entry->FwClass, &FmpImageInfoBuf->ImageTypeId)) {
189 continue;
190 }
191
192 DEBUG ((DEBUG_INFO, "EsrtFmpDxe: ESRT Entry already exists for FMP Instance with GUID %g\n", &Entry->FwClass));
193
194 //
195 // Set ESRT FwVersion to the smaller of the two values
196 //
197 Entry->FwVersion = MIN (FmpImageInfoBuf->Version, Entry->FwVersion);
198
199 //
200 // VERSION 2 has Lowest Supported
201 //
202 if (FmpVersion >= 2) {
203 //
204 // Set ESRT LowestSupportedFwVersion to the smaller of the two values
205 //
206 Entry->LowestSupportedFwVersion =
207 MIN (
208 FmpImageInfoBuf->LowestSupportedImageVersion,
209 Entry->LowestSupportedFwVersion
210 );
211 }
212
213 //
214 // VERSION 3 supports last attempt values
215 //
216 if (FmpVersion >= 3) {
217 //
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.
222 //
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;
227 } else {
228 Entry->LastAttemptVersion =
229 MIN (
230 FmpImageInfoBuf->LastAttemptVersion,
231 Entry->LastAttemptVersion
232 );
233 }
234 }
235 }
236
237 return EFI_SUCCESS;
238 }
239
240 //
241 // Add a new ESRT Table Entry
242 //
243 Entry = (EFI_SYSTEM_RESOURCE_ENTRY *)(Table + 1) + Table->FwResourceCount;
244
245 CopyGuid (&Entry->FwClass, &FmpImageInfoBuf->ImageTypeId);
246
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);
250 } else {
251 Entry->FwType = (UINT32)(ESRT_FW_TYPE_DEVICEFIRMWARE);
252 }
253
254 Entry->FwVersion = FmpImageInfoBuf->Version;
255 Entry->LowestSupportedFwVersion = 0;
256 Entry->CapsuleFlags = 0;
257 Entry->LastAttemptVersion = 0;
258 Entry->LastAttemptStatus = 0;
259
260 //
261 // VERSION 2 has Lowest Supported
262 //
263 if (FmpVersion >= 2) {
264 Entry->LowestSupportedFwVersion = FmpImageInfoBuf->LowestSupportedImageVersion;
265 }
266
267 //
268 // VERSION 3 supports last attempt values
269 //
270 if (FmpVersion >= 3) {
271 Entry->LastAttemptVersion = FmpImageInfoBuf->LastAttemptVersion;
272 Entry->LastAttemptStatus = FmpImageInfoBuf->LastAttemptStatus;
273 }
274
275 //
276 // Increment the number of active ESRT Table Entries
277 //
278 Table->FwResourceCount++;
279
280 return EFI_SUCCESS;
281 }
282
283 /**
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().
287
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.
295
296 @return Pointer to the retrieved EFI_FIRMWARE_IMAGE_DESCRIPTOR. If the
297 descriptor can not be retrieved, then NULL is returned.
298
299 **/
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
306 )
307 {
308 EFI_STATUS Status;
309 UINTN ImageInfoSize;
310 UINT32 PackageVersion;
311 CHAR16 *PackageVersionName;
312 EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf;
313
314 ImageInfoSize = 0;
315 Status = Fmp->GetImageInfo (
316 Fmp, // FMP Pointer
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
324 );
325 if (Status != EFI_BUFFER_TOO_SMALL) {
326 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Unexpected Failure in GetImageInfo. Status = %r\n", Status));
327 return NULL;
328 }
329
330 FmpImageInfoBuf = AllocateZeroPool (ImageInfoSize);
331 if (FmpImageInfoBuf == NULL) {
332 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to get memory for FMP descriptor.\n"));
333 return NULL;
334 }
335
336 PackageVersionName = NULL;
337 Status = Fmp->GetImageInfo (
338 Fmp, // FMP Pointer
339 &ImageInfoSize, // ImageInfoSize
340 FmpImageInfoBuf, // ImageInfo
341 FmpImageInfoDescriptorVer, // DescriptorVersion
342 FmpImageInfoCount, // DescriptorCount
343 DescriptorSize, // DescriptorSize
344 &PackageVersion, // PackageVersion
345 &PackageVersionName // PackageVersionName
346 );
347 if (PackageVersionName != NULL) {
348 FreePool (PackageVersionName);
349 }
350
351 if (EFI_ERROR (Status)) {
352 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failure in GetImageInfo. Status = %r\n", Status));
353 FreePool (FmpImageInfoBuf);
354 return NULL;
355 }
356
357 return FmpImageInfoBuf;
358 }
359
360 /**
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).
364
365 @return Pointer to the ESRT created.
366
367 **/
368 EFI_SYSTEM_RESOURCE_TABLE *
369 CreateFmpBasedEsrt (
370 VOID
371 )
372 {
373 EFI_STATUS Status;
374 UINTN NoProtocols;
375 VOID **Buffer;
376 UINTN Index;
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;
385
386 Status = EFI_SUCCESS;
387 NoProtocols = 0;
388 Buffer = NULL;
389 FmpImageInfoBuf = NULL;
390 OrgFmpImageInfoBuf = NULL;
391 Table = NULL;
392 HardwareInstances = NULL;
393
394 Status = EfiLocateProtocolBuffer (
395 &gEfiFirmwareManagementProtocolGuid,
396 &NoProtocols,
397 &Buffer
398 );
399 if (EFI_ERROR (Status) || (Buffer == NULL)) {
400 return NULL;
401 }
402
403 //
404 // Count the total number of EFI_FIRMWARE_IMAGE_DESCRIPTORs
405 //
406 for (Index = 0, NumberOfDescriptors = 0; Index < NoProtocols; Index++) {
407 FmpImageInfoBuf = FmpGetFirmwareImageDescriptor (
408 (EFI_FIRMWARE_MANAGEMENT_PROTOCOL *)Buffer[Index],
409 &FmpImageInfoDescriptorVer,
410 &FmpImageInfoCount,
411 &DescriptorSize
412 );
413 if (FmpImageInfoBuf != NULL) {
414 NumberOfDescriptors += FmpImageInfoCount;
415 FreePool (FmpImageInfoBuf);
416 }
417 }
418
419 //
420 // Allocate ESRT Table and GUID/HardwareInstance table
421 //
422 Table = AllocateZeroPool (
423 (NumberOfDescriptors * sizeof (EFI_SYSTEM_RESOURCE_ENTRY)) + sizeof (EFI_SYSTEM_RESOURCE_TABLE)
424 );
425 if (Table == NULL) {
426 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to allocate memory for ESRT.\n"));
427 FreePool (Buffer);
428 return NULL;
429 }
430
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"));
434 FreePool (Table);
435 FreePool (Buffer);
436 return NULL;
437 }
438
439 //
440 // Initialize ESRT Table
441 //
442 Table->FwResourceCount = 0;
443 Table->FwResourceCountMax = NumberOfDescriptors;
444 Table->FwResourceVersion = EFI_SYSTEM_RESOURCE_TABLE_FIRMWARE_RESOURCE_VERSION;
445
446 NumberOfDescriptors = 0;
447 for (Index = 0; Index < NoProtocols; Index++) {
448 FmpImageInfoBuf = FmpGetFirmwareImageDescriptor (
449 (EFI_FIRMWARE_MANAGEMENT_PROTOCOL *)Buffer[Index],
450 &FmpImageInfoDescriptorVer,
451 &FmpImageInfoCount,
452 &DescriptorSize
453 );
454 if (FmpImageInfoBuf == NULL) {
455 continue;
456 }
457
458 //
459 // Check each descriptor and read from the one specified
460 //
461 OrgFmpImageInfoBuf = FmpImageInfoBuf;
462 while (FmpImageInfoCount > 0) {
463 //
464 // If the descriptor has the IN USE bit set, create ESRT entry otherwise ignore.
465 //
466 if ((FmpImageInfoBuf->AttributesSetting & FmpImageInfoBuf->AttributesSupported & IMAGE_ATTRIBUTE_IN_USE) == IMAGE_ATTRIBUTE_IN_USE) {
467 //
468 // Create ESRT entry
469 //
470 CreateEsrtEntry (Table, HardwareInstances, &NumberOfDescriptors, FmpImageInfoBuf, FmpImageInfoDescriptorVer);
471 }
472
473 FmpImageInfoCount--;
474 //
475 // Increment the buffer pointer ahead by the size of the descriptor
476 //
477 FmpImageInfoBuf = (EFI_FIRMWARE_IMAGE_DESCRIPTOR *)(((UINT8 *)FmpImageInfoBuf) + DescriptorSize);
478 }
479
480 FreePool (OrgFmpImageInfoBuf);
481 OrgFmpImageInfoBuf = NULL;
482 }
483
484 FreePool (Buffer);
485 FreePool (HardwareInstances);
486 return Table;
487 }
488
489 /**
490 Notify function for event group EFI_EVENT_GROUP_READY_TO_BOOT. This is used to
491 install the Efi System Resource Table.
492
493 @param[in] Event The Event that is being processed.
494 @param[in] Context The Event Context.
495
496 **/
497 VOID
498 EFIAPI
499 EsrtReadyToBootEventNotify (
500 IN EFI_EVENT Event,
501 IN VOID *Context
502 )
503 {
504 EFI_STATUS Status;
505 EFI_SYSTEM_RESOURCE_TABLE *Table;
506
507 Table = CreateFmpBasedEsrt ();
508 if (Table != NULL) {
509 //
510 // Print table on debug builds
511 //
512 DEBUG_CODE_BEGIN ();
513 PrintTable (Table);
514 DEBUG_CODE_END ();
515
516 Status = InstallEfiSystemResourceTableInUefiConfigurationTable (Table);
517 if (EFI_ERROR (Status)) {
518 FreePool (Table);
519 }
520 } else {
521 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Can't install ESRT table because it is NULL. \n"));
522 }
523
524 //
525 // Close the event to prevent it be signalled again.
526 //
527 gBS->CloseEvent (Event);
528 }
529
530 /**
531 The module Entry Point of the Efi System Resource Table DXE driver.
532
533 @param[in] ImageHandle The firmware allocated handle for the EFI image.
534 @param[in] SystemTable A pointer to the EFI System Table.
535
536 @retval EFI_SUCCESS The entry point is executed successfully.
537 @retval Other Some error occurs when executing this entry point.
538
539 **/
540 EFI_STATUS
541 EFIAPI
542 EsrtFmpEntryPoint (
543 IN EFI_HANDLE ImageHandle,
544 IN EFI_SYSTEM_TABLE *SystemTable
545 )
546 {
547 EFI_STATUS Status;
548 EFI_EVENT EsrtReadyToBootEvent;
549
550 //
551 // Register notify function to install ESRT on ReadyToBoot Event.
552 //
553 Status = EfiCreateEventReadyToBootEx (
554 TPL_CALLBACK,
555 EsrtReadyToBootEventNotify,
556 NULL,
557 &EsrtReadyToBootEvent
558 );
559
560 ASSERT_EFI_ERROR (Status);
561 if (EFI_ERROR (Status)) {
562 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to register for ready to boot\n"));
563 }
564
565 return Status;
566 }