]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmp.c
MdeModulePkg/String.c: Zero memory before free (CVE-2019-14558)
[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 return Status;
83 }
84
85 /**
86 Return if this FMP is a system FMP or a device FMP, based upon FmpImageInfo.
87
88 @param[in] FmpImageInfo A pointer to EFI_FIRMWARE_IMAGE_DESCRIPTOR
89
90 @return TRUE It is a system FMP.
91 @return FALSE It is a device FMP.
92 **/
93 BOOLEAN
94 IsSystemFmp (
95 IN EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfo
96 )
97 {
98 GUID *Guid;
99 UINTN Count;
100 UINTN Index;
101
102 Guid = PcdGetPtr (PcdSystemFmpCapsuleImageTypeIdGuid);
103 Count = PcdGetSize (PcdSystemFmpCapsuleImageTypeIdGuid) / sizeof(GUID);
104
105 for (Index = 0; Index < Count; Index++, Guid++) {
106 if (CompareGuid (&FmpImageInfo->ImageTypeId, Guid)) {
107 return TRUE;
108 }
109 }
110
111 return FALSE;
112 }
113
114 /**
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
117 entry is updated.
118
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.
124
125 @retval EFI_SUCCESS FmpImageInfoBuf was use to fill in a new ESRT entry
126 in Table.
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
130 ESRT entry.
131 @retval EFI_UNSPOORTED The GUID/HardareInstance in FmpImageInfoBuf has is a
132 duplicate.
133
134 **/
135 EFI_STATUS
136 CreateEsrtEntry (
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,
141 IN UINT32 FmpVersion
142 )
143 {
144 UINTN Index;
145 EFI_SYSTEM_RESOURCE_ENTRY *Entry;
146 UINT64 FmpHardwareInstance;
147
148 FmpHardwareInstance = 0;
149 if (FmpVersion >= 3) {
150 FmpHardwareInstance = FmpImageInfoBuf->HardwareInstance;
151 }
152
153 //
154 // Check to see of FmpImageInfoBuf GUID/HardwareInstance is unique
155 //
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));
160 ASSERT (
161 !CompareGuid (&HardwareInstances[Index].ImageTypeGuid, &FmpImageInfoBuf->ImageTypeId) ||
162 HardwareInstances[Index].HardwareInstance != FmpHardwareInstance
163 );
164 return EFI_UNSUPPORTED;
165 }
166 }
167 }
168
169 //
170 // Record new GUID/HardwareInstance pair
171 //
172 CopyGuid (&HardwareInstances[*NumberOfDescriptors].ImageTypeGuid, &FmpImageInfoBuf->ImageTypeId);
173 HardwareInstances[*NumberOfDescriptors].HardwareInstance = FmpHardwareInstance;
174 *NumberOfDescriptors = *NumberOfDescriptors + 1;
175
176 DEBUG ((DEBUG_INFO, "EsrtFmpDxe: Add new image descriptor with GUID %g HardwareInstance:0x%x\n", &FmpImageInfoBuf->ImageTypeId, FmpHardwareInstance));
177
178 //
179 // Check to see if GUID is already in the ESRT table
180 //
181 Entry = (EFI_SYSTEM_RESOURCE_ENTRY *)(Table + 1);
182 for (Index = 0; Index < Table->FwResourceCount; Index++, Entry++) {
183 if (!CompareGuid (&Entry->FwClass, &FmpImageInfoBuf->ImageTypeId)) {
184 continue;
185 }
186 DEBUG ((DEBUG_INFO, "EsrtFmpDxe: ESRT Entry already exists for FMP Instance with GUID %g\n", &Entry->FwClass));
187
188 //
189 // Set ESRT FwVersion to the smaller of the two values
190 //
191 Entry->FwVersion = MIN (FmpImageInfoBuf->Version, Entry->FwVersion);
192
193 //
194 // VERSION 2 has Lowest Supported
195 //
196 if (FmpVersion >= 2) {
197 //
198 // Set ESRT LowestSupportedFwVersion to the smaller of the two values
199 //
200 Entry->LowestSupportedFwVersion =
201 MIN (
202 FmpImageInfoBuf->LowestSupportedImageVersion,
203 Entry->LowestSupportedFwVersion
204 );
205 }
206
207 //
208 // VERSION 3 supports last attempt values
209 //
210 if (FmpVersion >= 3) {
211 //
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.
216 //
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;
221 } else {
222 Entry->LastAttemptVersion =
223 MIN (
224 FmpImageInfoBuf->LastAttemptVersion,
225 Entry->LastAttemptVersion
226 );
227 }
228 }
229 }
230
231 return EFI_SUCCESS;
232 }
233
234 //
235 // Add a new ESRT Table Entry
236 //
237 Entry = (EFI_SYSTEM_RESOURCE_ENTRY *)(Table + 1) + Table->FwResourceCount;
238
239 CopyGuid (&Entry->FwClass, &FmpImageInfoBuf->ImageTypeId);
240
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);
244 } else {
245 Entry->FwType = (UINT32)(ESRT_FW_TYPE_DEVICEFIRMWARE);
246 }
247
248 Entry->FwVersion = FmpImageInfoBuf->Version;
249 Entry->LowestSupportedFwVersion = 0;
250 Entry->CapsuleFlags = 0;
251 Entry->LastAttemptVersion = 0;
252 Entry->LastAttemptStatus = 0;
253
254 //
255 // VERSION 2 has Lowest Supported
256 //
257 if (FmpVersion >= 2) {
258 Entry->LowestSupportedFwVersion = FmpImageInfoBuf->LowestSupportedImageVersion;
259 }
260
261 //
262 // VERSION 3 supports last attempt values
263 //
264 if (FmpVersion >= 3) {
265 Entry->LastAttemptVersion = FmpImageInfoBuf->LastAttemptVersion;
266 Entry->LastAttemptStatus = FmpImageInfoBuf->LastAttemptStatus;
267 }
268
269 //
270 // Increment the number of active ESRT Table Entries
271 //
272 Table->FwResourceCount++;
273
274 return EFI_SUCCESS;
275 }
276
277 /**
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().
281
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.
289
290 @return Pointer to the retrieved EFI_FIRMWARE_IMAGE_DESCRIPTOR. If the
291 descriptor can not be retrieved, then NULL is returned.
292
293 **/
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
300 )
301 {
302 EFI_STATUS Status;
303 UINTN ImageInfoSize;
304 UINT32 PackageVersion;
305 CHAR16 *PackageVersionName;
306 EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf;
307
308 ImageInfoSize = 0;
309 Status = Fmp->GetImageInfo (
310 Fmp, // FMP Pointer
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
318 );
319 if (Status != EFI_BUFFER_TOO_SMALL) {
320 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Unexpected Failure in GetImageInfo. Status = %r\n", Status));
321 return NULL;
322 }
323
324 FmpImageInfoBuf = AllocateZeroPool (ImageInfoSize);
325 if (FmpImageInfoBuf == NULL) {
326 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to get memory for FMP descriptor.\n"));
327 return NULL;
328 }
329
330 PackageVersionName = NULL;
331 Status = Fmp->GetImageInfo (
332 Fmp, // FMP Pointer
333 &ImageInfoSize, // ImageInfoSize
334 FmpImageInfoBuf, // ImageInfo
335 FmpImageInfoDescriptorVer, // DescriptorVersion
336 FmpImageInfoCount, // DescriptorCount
337 DescriptorSize, // DescriptorSize
338 &PackageVersion, // PackageVersion
339 &PackageVersionName // PackageVersionName
340 );
341 if (PackageVersionName != NULL) {
342 FreePool (PackageVersionName);
343 }
344 if (EFI_ERROR (Status)) {
345 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failure in GetImageInfo. Status = %r\n", Status));
346 FreePool (FmpImageInfoBuf);
347 return NULL;
348 }
349
350 return FmpImageInfoBuf;
351 }
352
353 /**
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).
357
358 @return Pointer to the ESRT created.
359
360 **/
361 EFI_SYSTEM_RESOURCE_TABLE *
362 CreateFmpBasedEsrt (
363 VOID
364 )
365 {
366 EFI_STATUS Status;
367 UINTN NoProtocols;
368 VOID **Buffer;
369 UINTN Index;
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;
378
379 Status = EFI_SUCCESS;
380 NoProtocols = 0;
381 Buffer = NULL;
382 FmpImageInfoBuf = NULL;
383 OrgFmpImageInfoBuf = NULL;
384 Table = NULL;
385 HardwareInstances = NULL;
386
387 Status = EfiLocateProtocolBuffer (
388 &gEfiFirmwareManagementProtocolGuid,
389 &NoProtocols,
390 &Buffer
391 );
392 if (EFI_ERROR(Status) || (Buffer == NULL)) {
393 return NULL;
394 }
395
396 //
397 // Count the total number of EFI_FIRMWARE_IMAGE_DESCRIPTORs
398 //
399 for (Index = 0, NumberOfDescriptors = 0; Index < NoProtocols; Index++) {
400 FmpImageInfoBuf = FmpGetFirmwareImageDescriptor (
401 (EFI_FIRMWARE_MANAGEMENT_PROTOCOL *) Buffer[Index],
402 &FmpImageInfoDescriptorVer,
403 &FmpImageInfoCount,
404 &DescriptorSize
405 );
406 if (FmpImageInfoBuf != NULL) {
407 NumberOfDescriptors += FmpImageInfoCount;
408 FreePool (FmpImageInfoBuf);
409 }
410 }
411
412 //
413 // Allocate ESRT Table and GUID/HardwareInstance table
414 //
415 Table = AllocateZeroPool (
416 (NumberOfDescriptors * sizeof (EFI_SYSTEM_RESOURCE_ENTRY)) + sizeof (EFI_SYSTEM_RESOURCE_TABLE)
417 );
418 if (Table == NULL) {
419 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to allocate memory for ESRT.\n"));
420 FreePool (Buffer);
421 return NULL;
422 }
423
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"));
427 FreePool (Table);
428 FreePool (Buffer);
429 return NULL;
430 }
431
432 //
433 // Initialize ESRT Table
434 //
435 Table->FwResourceCount = 0;
436 Table->FwResourceCountMax = NumberOfDescriptors;
437 Table->FwResourceVersion = EFI_SYSTEM_RESOURCE_TABLE_FIRMWARE_RESOURCE_VERSION;
438
439 NumberOfDescriptors = 0;
440 for (Index = 0; Index < NoProtocols; Index++) {
441 FmpImageInfoBuf = FmpGetFirmwareImageDescriptor (
442 (EFI_FIRMWARE_MANAGEMENT_PROTOCOL *) Buffer[Index],
443 &FmpImageInfoDescriptorVer,
444 &FmpImageInfoCount,
445 &DescriptorSize
446 );
447 if (FmpImageInfoBuf == NULL) {
448 continue;
449 }
450
451 //
452 // Check each descriptor and read from the one specified
453 //
454 OrgFmpImageInfoBuf = FmpImageInfoBuf;
455 while (FmpImageInfoCount > 0) {
456 //
457 // If the descriptor has the IN USE bit set, create ESRT entry otherwise ignore.
458 //
459 if ((FmpImageInfoBuf->AttributesSetting & FmpImageInfoBuf->AttributesSupported & IMAGE_ATTRIBUTE_IN_USE) == IMAGE_ATTRIBUTE_IN_USE) {
460 //
461 // Create ESRT entry
462 //
463 CreateEsrtEntry (Table, HardwareInstances, &NumberOfDescriptors, FmpImageInfoBuf, FmpImageInfoDescriptorVer);
464 }
465 FmpImageInfoCount--;
466 //
467 // Increment the buffer pointer ahead by the size of the descriptor
468 //
469 FmpImageInfoBuf = (EFI_FIRMWARE_IMAGE_DESCRIPTOR *)(((UINT8 *)FmpImageInfoBuf) + DescriptorSize);
470 }
471
472 FreePool (OrgFmpImageInfoBuf);
473 OrgFmpImageInfoBuf = NULL;
474 }
475
476 FreePool (Buffer);
477 FreePool (HardwareInstances);
478 return Table;
479 }
480
481 /**
482 Notify function for event group EFI_EVENT_GROUP_READY_TO_BOOT. This is used to
483 install the Efi System Resource Table.
484
485 @param[in] Event The Event that is being processed.
486 @param[in] Context The Event Context.
487
488 **/
489 VOID
490 EFIAPI
491 EsrtReadyToBootEventNotify (
492 IN EFI_EVENT Event,
493 IN VOID *Context
494 )
495 {
496 EFI_STATUS Status;
497 EFI_SYSTEM_RESOURCE_TABLE *Table;
498
499 Table = CreateFmpBasedEsrt ();
500 if (Table != NULL) {
501 //
502 // Print table on debug builds
503 //
504 DEBUG_CODE_BEGIN ();
505 PrintTable (Table);
506 DEBUG_CODE_END ();
507
508 Status = InstallEfiSystemResourceTableInUefiConfigurationTable (Table);
509 if (EFI_ERROR (Status)) {
510 FreePool (Table);
511 }
512 } else {
513 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Can't install ESRT table because it is NULL. \n"));
514 }
515
516 //
517 // Close the event to prevent it be signalled again.
518 //
519 gBS->CloseEvent (Event);
520 }
521
522 /**
523 The module Entry Point of the Efi System Resource Table DXE driver.
524
525 @param[in] ImageHandle The firmware allocated handle for the EFI image.
526 @param[in] SystemTable A pointer to the EFI System Table.
527
528 @retval EFI_SUCCESS The entry point is executed successfully.
529 @retval Other Some error occurs when executing this entry point.
530
531 **/
532 EFI_STATUS
533 EFIAPI
534 EsrtFmpEntryPoint (
535 IN EFI_HANDLE ImageHandle,
536 IN EFI_SYSTEM_TABLE *SystemTable
537 )
538 {
539 EFI_STATUS Status;
540 EFI_EVENT EsrtReadyToBootEvent;
541
542 //
543 // Register notify function to install ESRT on ReadyToBoot Event.
544 //
545 Status = EfiCreateEventReadyToBootEx (
546 TPL_CALLBACK,
547 EsrtReadyToBootEventNotify,
548 NULL,
549 &EsrtReadyToBootEvent
550 );
551
552 ASSERT_EFI_ERROR (Status);
553 if (EFI_ERROR (Status)) {
554 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to register for ready to boot\n"));
555 }
556
557 return Status;
558 }