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