]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmp.c
b98430edbf09ff1019afb69e03bf974ef455ffcb
[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, Intel Corporation. All rights reserved.<BR>
6
7 All rights reserved.
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions are met:
10 1. Redistributions of source code must retain the above copyright notice,
11 this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright notice,
13 this list of conditions and the following disclaimer in the documentation
14 and/or other materials provided with the distribution.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
20 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
24 OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27 **/
28
29 #include <Uefi.h>
30 #include <Library/BaseLib.h>
31 #include <Library/BaseMemoryLib.h>
32 #include <Library/MemoryAllocationLib.h>
33 #include <Library/UefiBootServicesTableLib.h>
34 #include <Library/DebugLib.h>
35 #include <Library/PcdLib.h>
36 #include <Library/UefiLib.h>
37 #include <Protocol/FirmwareManagement.h>
38 #include <Guid/EventGroup.h>
39 #include <Guid/SystemResourceTable.h>
40
41 //
42 // Print ESRT to debug console
43 //
44 VOID
45 EFIAPI
46 PrintTable (
47 IN EFI_SYSTEM_RESOURCE_TABLE *Table
48 );
49
50 //
51 // Number of ESRT entries to grow by each time we run out of room
52 //
53 #define GROWTH_STEP 10
54
55 //
56 // Module globals.
57 //
58 EFI_EVENT mEsrtReadyToBootEvent;
59 EFI_SYSTEM_RESOURCE_TABLE *mTable = NULL;
60 BOOLEAN mEsrtInstalled = FALSE;
61 EFI_EVENT mFmpInstallEvent;
62 VOID *mFmpInstallEventRegistration = NULL;
63
64 /**
65 Install EFI System Resource Table into the UEFI Configuration Table
66
67 @return Status code.
68
69 **/
70 EFI_STATUS
71 InstallEfiSystemResourceTableInUefiConfigurationTable (
72 VOID
73 )
74 {
75 EFI_STATUS Status;
76
77 Status = EFI_SUCCESS;
78 if (!mEsrtInstalled) {
79 if (mTable == NULL) {
80 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Can't install ESRT table because it is NULL. \n"));
81 Status = EFI_OUT_OF_RESOURCES;
82 } else if (mTable->FwResourceCount == 0) {
83 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Can't install ESRT table because it has zero Entries. \n"));
84 Status = EFI_UNSUPPORTED;
85 } else {
86 //
87 // Install the pointer into config table
88 //
89 Status = gBS->InstallConfigurationTable (&gEfiSystemResourceTableGuid, mTable);
90 if (EFI_ERROR (Status)) {
91 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Can't install ESRT table. Status: %r. \n", Status));
92 } else {
93 DEBUG ((DEBUG_INFO, "EsrtFmpDxe: Installed ESRT table. \n"));
94 mEsrtInstalled = TRUE;
95 }
96 }
97 }
98 return Status;
99 }
100
101 /**
102 Return if this FMP is a system FMP or a device FMP, based upon FmpImageInfo.
103
104 @param[in] FmpImageInfo A pointer to EFI_FIRMWARE_IMAGE_DESCRIPTOR
105
106 @return TRUE It is a system FMP.
107 @return FALSE It is a device FMP.
108 **/
109 BOOLEAN
110 IsSystemFmp (
111 IN EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfo
112 )
113 {
114 GUID *Guid;
115 UINTN Count;
116 UINTN Index;
117
118 Guid = PcdGetPtr (PcdSystemFmpCapsuleImageTypeIdGuid);
119 Count = PcdGetSize (PcdSystemFmpCapsuleImageTypeIdGuid) / sizeof(GUID);
120
121 for (Index = 0; Index < Count; Index++, Guid++) {
122 if (CompareGuid (&FmpImageInfo->ImageTypeId, Guid)) {
123 return TRUE;
124 }
125 }
126
127 return FALSE;
128 }
129
130 /**
131 Function to create a single ESRT Entry and add it to the ESRT
132 given a FMP descriptor. If the guid is already in the ESRT it
133 will be ignored. The ESRT will grow if it does not have enough room.
134
135 @return Status code.
136
137 **/
138 EFI_STATUS
139 EFIAPI
140 CreateEsrtEntry (
141 IN EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf,
142 IN UINT32 FmpVersion
143 )
144 {
145 UINTN Index;
146 EFI_SYSTEM_RESOURCE_ENTRY *Entry;
147 UINTN NewSize;
148 EFI_SYSTEM_RESOURCE_TABLE *NewTable;
149
150 Index = 0;
151 Entry = NULL;
152
153 //
154 // Get our ESRT table. This should never be null at this point
155 //
156 if (mTable == NULL) {
157 return EFI_DEVICE_ERROR;
158 }
159
160 Entry = (EFI_SYSTEM_RESOURCE_ENTRY *)(mTable + 1);
161 //
162 // Make sure Guid isn't already in the list
163 //
164 for (Index = 0; Index < mTable->FwResourceCount; Index++) {
165 if (CompareGuid (&Entry->FwClass, &FmpImageInfoBuf->ImageTypeId)) {
166 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: ESRT Entry already exists for FMP Instance with GUID %g\n", &Entry->FwClass));
167 return EFI_INVALID_PARAMETER;
168 }
169 Entry++;
170 }
171
172 //
173 // Grow table if needed
174 //
175 if (mTable->FwResourceCount >= mTable->FwResourceCountMax) {
176 //
177 // Can't grow table after installed.
178 // Only because didn't add support for this.
179 // Would need to re-install ESRT in system table if wanted to support
180 //
181 if (mEsrtInstalled) {
182 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to install entry because ESRT table needed to grow after table already installed. \n"));
183 return EFI_OUT_OF_RESOURCES;
184 }
185
186 NewSize = ((mTable->FwResourceCountMax + GROWTH_STEP) * sizeof (EFI_SYSTEM_RESOURCE_ENTRY)) + sizeof (EFI_SYSTEM_RESOURCE_TABLE);
187 NewTable = AllocateRuntimeZeroPool (NewSize);
188 if (NewTable == NULL) {
189 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to allocate memory larger table for ESRT. \n"));
190 return EFI_OUT_OF_RESOURCES;
191 }
192 //
193 // Copy the whole old table into new table buffer
194 //
195 CopyMem (
196 NewTable,
197 mTable,
198 ((mTable->FwResourceCountMax) * sizeof (EFI_SYSTEM_RESOURCE_ENTRY)) + sizeof (EFI_SYSTEM_RESOURCE_TABLE)
199 );
200 //
201 // Update max
202 //
203 NewTable->FwResourceCountMax = NewTable->FwResourceCountMax + GROWTH_STEP;
204 //
205 // Free old table
206 //
207 FreePool (mTable);
208 //
209 // Reassign pointer to new table.
210 //
211 mTable = NewTable;
212 }
213
214 //
215 // ESRT table has enough room for the new entry so add new entry
216 //
217 Entry = (EFI_SYSTEM_RESOURCE_ENTRY *)(((UINT8 *)mTable) + sizeof (EFI_SYSTEM_RESOURCE_TABLE));
218 //
219 // Move to the location of new entry
220 //
221 Entry = Entry + mTable->FwResourceCount;
222 //
223 // Increment resource count
224 //
225 mTable->FwResourceCount++;
226
227 CopyGuid (&Entry->FwClass, &FmpImageInfoBuf->ImageTypeId);
228
229 if (IsSystemFmp (FmpImageInfoBuf)) {
230 DEBUG ((DEBUG_INFO, "EsrtFmpDxe: Found an ESRT entry for a System Device.\n"));
231 Entry->FwType = (UINT32)(ESRT_FW_TYPE_SYSTEMFIRMWARE);
232 } else {
233 Entry->FwType = (UINT32)(ESRT_FW_TYPE_DEVICEFIRMWARE);
234 }
235
236 Entry->FwVersion = FmpImageInfoBuf->Version;
237 Entry->LowestSupportedFwVersion = 0;
238 Entry->CapsuleFlags = 0;
239 Entry->LastAttemptVersion = 0;
240 Entry->LastAttemptStatus = 0;
241
242 //
243 // VERSION 2 has Lowest Supported
244 //
245 if (FmpVersion >= 2) {
246 Entry->LowestSupportedFwVersion = FmpImageInfoBuf->LowestSupportedImageVersion;
247 }
248
249 //
250 // VERSION 3 supports last attempt values
251 //
252 if (FmpVersion >= 3) {
253 Entry->LastAttemptVersion = FmpImageInfoBuf->LastAttemptVersion;
254 Entry->LastAttemptStatus = FmpImageInfoBuf->LastAttemptStatus;
255 }
256
257 return EFI_SUCCESS;
258 }
259
260 /**
261 Notify function for every Firmware Management Protocol being installed.
262 Get the descriptors from FMP Instance and create ESRT entries (ESRE)
263
264 @param[in] Event The Event that is being processed.
265 @param[in] Context The Event Context.
266
267 **/
268 VOID
269 EFIAPI
270 FmpInstallProtocolNotify (
271 IN EFI_EVENT Event,
272 IN VOID *Context
273 )
274 {
275 EFI_STATUS Status;
276 EFI_HANDLE Handle;
277 UINTN BufferSize;
278 EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp;
279 UINTN DescriptorSize;
280 EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf;
281 EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBufOrg;
282 UINT8 FmpImageInfoCount;
283 UINT32 FmpImageInfoDescriptorVer;
284 UINTN ImageInfoSize;
285 UINT32 PackageVersion;
286 CHAR16 *PackageVersionName;
287
288 Status = EFI_SUCCESS;
289 Handle = 0;
290 BufferSize = 0;
291 PackageVersionName = NULL;
292 FmpImageInfoBuf = NULL;
293 FmpImageInfoBufOrg = NULL;
294 Fmp = NULL;
295
296 DEBUG ((DEBUG_INFO, "FMP Installed Notify\n"));
297 while (TRUE) {
298 BufferSize = sizeof (EFI_HANDLE);
299 Status = gBS->LocateHandle (ByRegisterNotify, NULL, mFmpInstallEventRegistration, &BufferSize, &Handle);
300 if (EFI_ERROR (Status)) {
301 DEBUG ((DEBUG_WARN, "EsrtFmpDxe: Failed to Locate handle from notify value. Status: %r\n", Status));
302 return;
303 }
304
305 Status = gBS->HandleProtocol (Handle, &gEfiFirmwareManagementProtocolGuid, (VOID **)&Fmp);
306 if (EFI_ERROR (Status)) {
307 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to get FMP for a handle 0x%x\n", Handle));
308 continue;
309 }
310 ImageInfoSize = 0;
311
312 Status = Fmp->GetImageInfo (
313 Fmp, // FMP Pointer
314 &ImageInfoSize, // Buffer Size (in this case 0)
315 NULL, // NULL so we can get size
316 &FmpImageInfoDescriptorVer, // DescriptorVersion
317 &FmpImageInfoCount, // DescriptorCount
318 &DescriptorSize, // DescriptorSize
319 &PackageVersion, // PackageVersion
320 &PackageVersionName // PackageVersionName
321 );
322
323 if (Status != EFI_BUFFER_TOO_SMALL) {
324 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Unexpected Failure in GetImageInfo. Status = %r\n", Status));
325 continue;
326 }
327
328 FmpImageInfoBuf = NULL;
329 FmpImageInfoBuf = AllocateZeroPool (ImageInfoSize);
330 if (FmpImageInfoBuf == NULL) {
331 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to get memory for descriptors.\n"));
332 continue;
333 }
334
335 FmpImageInfoBufOrg = FmpImageInfoBuf;
336 PackageVersionName = NULL;
337 Status = Fmp->GetImageInfo (
338 Fmp,
339 &ImageInfoSize, // ImageInfoSize
340 FmpImageInfoBuf, // ImageInfo
341 &FmpImageInfoDescriptorVer, // DescriptorVersion
342 &FmpImageInfoCount, // DescriptorCount
343 &DescriptorSize, // DescriptorSize
344 &PackageVersion, // PackageVersion
345 &PackageVersionName // PackageVersionName
346 );
347 if (EFI_ERROR (Status)) {
348 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failure in GetImageInfo. Status = %r\n", Status));
349 goto CleanUp;
350 }
351
352 //
353 // Check each descriptor and read from the one specified
354 //
355 while (FmpImageInfoCount > 0) {
356 //
357 // If the descriptor has the IN USE bit set, create ESRT entry otherwise ignore.
358 //
359 if ((FmpImageInfoBuf->AttributesSetting & FmpImageInfoBuf->AttributesSupported & IMAGE_ATTRIBUTE_IN_USE) == IMAGE_ATTRIBUTE_IN_USE) {
360 //
361 // Create ESRT entry
362 //
363 CreateEsrtEntry (FmpImageInfoBuf, FmpImageInfoDescriptorVer);
364 }
365 FmpImageInfoCount--;
366 //
367 // Increment the buffer pointer ahead by the size of the descriptor
368 //
369 FmpImageInfoBuf = (EFI_FIRMWARE_IMAGE_DESCRIPTOR *)(((UINT8 *)FmpImageInfoBuf) + DescriptorSize);
370 }
371
372 if (PackageVersionName != NULL) {
373 FreePool (PackageVersionName);
374 PackageVersionName = NULL;
375 }
376 if (FmpImageInfoBufOrg != NULL) {
377 FreePool (FmpImageInfoBufOrg);
378 FmpImageInfoBufOrg = NULL;
379 }
380 }
381
382 CleanUp:
383 if (FmpImageInfoBufOrg != NULL) {
384 FreePool (FmpImageInfoBufOrg);
385 }
386 return;
387 }
388
389 /**
390 Notify function for event group EFI_EVENT_GROUP_READY_TO_BOOT. This is used to
391 install the Efi System Resource Table.
392
393 @param[in] Event The Event that is being processed.
394 @param[in] Context The Event Context.
395
396 **/
397 VOID
398 EFIAPI
399 EsrtReadyToBootEventNotify (
400 IN EFI_EVENT Event,
401 IN VOID *Context
402 )
403 {
404 InstallEfiSystemResourceTableInUefiConfigurationTable ();
405
406 //
407 // Print table on debug builds
408 //
409 DEBUG_CODE_BEGIN ();
410 PrintTable (mTable);
411 DEBUG_CODE_END ();
412 }
413
414 /**
415 The module Entry Point of the Efi System Resource Table DXE driver.
416
417 @param[in] ImageHandle The firmware allocated handle for the EFI image.
418 @param[in] SystemTable A pointer to the EFI System Table.
419
420 @retval EFI_SUCCESS The entry point is executed successfully.
421 @retval Other Some error occurs when executing this entry point.
422
423 **/
424 EFI_STATUS
425 EFIAPI
426 EsrtFmpEntryPoint (
427 IN EFI_HANDLE ImageHandle,
428 IN EFI_SYSTEM_TABLE *SystemTable
429 )
430 {
431 EFI_STATUS Status;
432
433 //
434 // Allocate Memory for table
435 //
436 mTable = AllocateRuntimeZeroPool (
437 (GROWTH_STEP * sizeof (EFI_SYSTEM_RESOURCE_ENTRY)) + sizeof (EFI_SYSTEM_RESOURCE_TABLE)
438 );
439 ASSERT (mTable != NULL);
440 if (mTable == NULL) {
441 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to allocate memory for ESRT.\n"));
442 return EFI_OUT_OF_RESOURCES;
443 }
444
445 mTable->FwResourceCount = 0;
446 mTable->FwResourceCountMax = GROWTH_STEP;
447 mTable->FwResourceVersion = EFI_SYSTEM_RESOURCE_TABLE_FIRMWARE_RESOURCE_VERSION;
448
449 //
450 // Register notify function for all FMP installed
451 //
452 mFmpInstallEvent = EfiCreateProtocolNotifyEvent (
453 &gEfiFirmwareManagementProtocolGuid,
454 TPL_CALLBACK,
455 FmpInstallProtocolNotify,
456 NULL,
457 &mFmpInstallEventRegistration
458 );
459
460 ASSERT (mFmpInstallEvent != NULL);
461
462 if (mFmpInstallEvent == NULL) {
463 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to Create Protocol Notify Event for FMP.\n"));
464 }
465
466 //
467 // Register notify function to install ESRT on ReadyToBoot Event.
468 //
469 Status = EfiCreateEventReadyToBootEx (
470 TPL_CALLBACK,
471 EsrtReadyToBootEventNotify,
472 NULL,
473 &mEsrtReadyToBootEvent
474 );
475
476 ASSERT_EFI_ERROR (Status);
477 if (EFI_ERROR (Status)) {
478 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to register for ready to boot\n"));
479 }
480
481 return Status;
482 }