]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmp.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[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 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 Print ESRT to debug console.
26
27 @param[in] Table Pointer to the ESRT table.
28
29 **/
30 VOID
31 EFIAPI
32 PrintTable (
33 IN EFI_SYSTEM_RESOURCE_TABLE *Table
34 );
35
36 //
37 // Number of ESRT entries to grow by each time we run out of room
38 //
39 #define GROWTH_STEP 10
40
41 /**
42 Install EFI System Resource Table into the UEFI Configuration Table
43
44 @param[in] Table Pointer to the ESRT.
45
46 @return Status code.
47
48 **/
49 EFI_STATUS
50 InstallEfiSystemResourceTableInUefiConfigurationTable (
51 IN EFI_SYSTEM_RESOURCE_TABLE *Table
52 )
53 {
54 EFI_STATUS Status;
55
56 Status = EFI_SUCCESS;
57 if (Table->FwResourceCount == 0) {
58 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Can't install ESRT table because it has zero Entries. \n"));
59 Status = EFI_UNSUPPORTED;
60 } else {
61 //
62 // Install the pointer into config table
63 //
64 Status = gBS->InstallConfigurationTable (&gEfiSystemResourceTableGuid, Table);
65 if (EFI_ERROR (Status)) {
66 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Can't install ESRT table. Status: %r. \n", Status));
67 } else {
68 DEBUG ((DEBUG_INFO, "EsrtFmpDxe: Installed ESRT table. \n"));
69 }
70 }
71 return Status;
72 }
73
74 /**
75 Return if this FMP is a system FMP or a device FMP, based upon FmpImageInfo.
76
77 @param[in] FmpImageInfo A pointer to EFI_FIRMWARE_IMAGE_DESCRIPTOR
78
79 @return TRUE It is a system FMP.
80 @return FALSE It is a device FMP.
81 **/
82 BOOLEAN
83 IsSystemFmp (
84 IN EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfo
85 )
86 {
87 GUID *Guid;
88 UINTN Count;
89 UINTN Index;
90
91 Guid = PcdGetPtr (PcdSystemFmpCapsuleImageTypeIdGuid);
92 Count = PcdGetSize (PcdSystemFmpCapsuleImageTypeIdGuid) / sizeof(GUID);
93
94 for (Index = 0; Index < Count; Index++, Guid++) {
95 if (CompareGuid (&FmpImageInfo->ImageTypeId, Guid)) {
96 return TRUE;
97 }
98 }
99
100 return FALSE;
101 }
102
103 /**
104 Function to create a single ESRT Entry and add it to the ESRT
105 given a FMP descriptor. If the guid is already in the ESRT it
106 will be ignored. The ESRT will grow if it does not have enough room.
107
108 @param[in, out] Table On input, pointer to the pointer to the ESRT.
109 On output, same as input or pointer to the pointer
110 to new enlarged ESRT.
111 @param[in] FmpImageInfoBuf Pointer to the EFI_FIRMWARE_IMAGE_DESCRIPTOR.
112 @param[in] FmpVersion FMP Version.
113
114 @return Status code.
115
116 **/
117 EFI_STATUS
118 CreateEsrtEntry (
119 IN OUT EFI_SYSTEM_RESOURCE_TABLE **Table,
120 IN EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf,
121 IN UINT32 FmpVersion
122 )
123 {
124 UINTN Index;
125 EFI_SYSTEM_RESOURCE_ENTRY *Entry;
126 UINTN NewSize;
127 EFI_SYSTEM_RESOURCE_TABLE *NewTable;
128
129 Index = 0;
130 Entry = NULL;
131
132 Entry = (EFI_SYSTEM_RESOURCE_ENTRY *)((*Table) + 1);
133 //
134 // Make sure Guid isn't already in the list
135 //
136 for (Index = 0; Index < (*Table)->FwResourceCount; Index++) {
137 if (CompareGuid (&Entry->FwClass, &FmpImageInfoBuf->ImageTypeId)) {
138 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: ESRT Entry already exists for FMP Instance with GUID %g\n", &Entry->FwClass));
139 return EFI_INVALID_PARAMETER;
140 }
141 Entry++;
142 }
143
144 //
145 // Grow table if needed
146 //
147 if ((*Table)->FwResourceCount >= (*Table)->FwResourceCountMax) {
148 NewSize = (((*Table)->FwResourceCountMax + GROWTH_STEP) * sizeof (EFI_SYSTEM_RESOURCE_ENTRY)) + sizeof (EFI_SYSTEM_RESOURCE_TABLE);
149 NewTable = AllocateZeroPool (NewSize);
150 if (NewTable == NULL) {
151 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to allocate memory larger table for ESRT. \n"));
152 return EFI_OUT_OF_RESOURCES;
153 }
154 //
155 // Copy the whole old table into new table buffer
156 //
157 CopyMem (
158 NewTable,
159 (*Table),
160 (((*Table)->FwResourceCountMax) * sizeof (EFI_SYSTEM_RESOURCE_ENTRY)) + sizeof (EFI_SYSTEM_RESOURCE_TABLE)
161 );
162 //
163 // Update max
164 //
165 NewTable->FwResourceCountMax = NewTable->FwResourceCountMax + GROWTH_STEP;
166 //
167 // Free old table
168 //
169 FreePool ((*Table));
170 //
171 // Reassign pointer to new table.
172 //
173 (*Table) = NewTable;
174 }
175
176 //
177 // ESRT table has enough room for the new entry so add new entry
178 //
179 Entry = (EFI_SYSTEM_RESOURCE_ENTRY *)(((UINT8 *)(*Table)) + sizeof (EFI_SYSTEM_RESOURCE_TABLE));
180 //
181 // Move to the location of new entry
182 //
183 Entry = Entry + (*Table)->FwResourceCount;
184 //
185 // Increment resource count
186 //
187 (*Table)->FwResourceCount++;
188
189 CopyGuid (&Entry->FwClass, &FmpImageInfoBuf->ImageTypeId);
190
191 if (IsSystemFmp (FmpImageInfoBuf)) {
192 DEBUG ((DEBUG_INFO, "EsrtFmpDxe: Found an ESRT entry for a System Device.\n"));
193 Entry->FwType = (UINT32)(ESRT_FW_TYPE_SYSTEMFIRMWARE);
194 } else {
195 Entry->FwType = (UINT32)(ESRT_FW_TYPE_DEVICEFIRMWARE);
196 }
197
198 Entry->FwVersion = FmpImageInfoBuf->Version;
199 Entry->LowestSupportedFwVersion = 0;
200 Entry->CapsuleFlags = 0;
201 Entry->LastAttemptVersion = 0;
202 Entry->LastAttemptStatus = 0;
203
204 //
205 // VERSION 2 has Lowest Supported
206 //
207 if (FmpVersion >= 2) {
208 Entry->LowestSupportedFwVersion = FmpImageInfoBuf->LowestSupportedImageVersion;
209 }
210
211 //
212 // VERSION 3 supports last attempt values
213 //
214 if (FmpVersion >= 3) {
215 Entry->LastAttemptVersion = FmpImageInfoBuf->LastAttemptVersion;
216 Entry->LastAttemptStatus = FmpImageInfoBuf->LastAttemptStatus;
217 }
218
219 return EFI_SUCCESS;
220 }
221
222 /**
223 Function to create ESRT based on FMP Instances.
224 Create ESRT table, get the descriptors from FMP Instance and
225 create ESRT entries (ESRE).
226
227 @return Pointer to the ESRT created.
228
229 **/
230 EFI_SYSTEM_RESOURCE_TABLE *
231 CreateFmpBasedEsrt (
232 VOID
233 )
234 {
235 EFI_STATUS Status;
236 EFI_SYSTEM_RESOURCE_TABLE *Table;
237 UINTN NoProtocols;
238 VOID **Buffer;
239 UINTN Index;
240 EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp;
241 UINTN DescriptorSize;
242 EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf;
243 EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBufOrg;
244 UINT8 FmpImageInfoCount;
245 UINT32 FmpImageInfoDescriptorVer;
246 UINTN ImageInfoSize;
247 UINT32 PackageVersion;
248 CHAR16 *PackageVersionName;
249
250 Status = EFI_SUCCESS;
251 Table = NULL;
252 NoProtocols = 0;
253 Buffer = NULL;
254 PackageVersionName = NULL;
255 FmpImageInfoBuf = NULL;
256 FmpImageInfoBufOrg = NULL;
257 Fmp = NULL;
258
259 Status = EfiLocateProtocolBuffer (
260 &gEfiFirmwareManagementProtocolGuid,
261 &NoProtocols,
262 &Buffer
263 );
264 if (EFI_ERROR(Status) || (Buffer == NULL)) {
265 return NULL;
266 }
267
268 //
269 // Allocate Memory for table
270 //
271 Table = AllocateZeroPool (
272 (GROWTH_STEP * sizeof (EFI_SYSTEM_RESOURCE_ENTRY)) + sizeof (EFI_SYSTEM_RESOURCE_TABLE)
273 );
274 if (Table == NULL) {
275 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to allocate memory for ESRT.\n"));
276 gBS->FreePool (Buffer);
277 return NULL;
278 }
279
280 Table->FwResourceCount = 0;
281 Table->FwResourceCountMax = GROWTH_STEP;
282 Table->FwResourceVersion = EFI_SYSTEM_RESOURCE_TABLE_FIRMWARE_RESOURCE_VERSION;
283
284 for (Index = 0; Index < NoProtocols; Index++) {
285 Fmp = (EFI_FIRMWARE_MANAGEMENT_PROTOCOL *) Buffer[Index];
286
287 ImageInfoSize = 0;
288 Status = Fmp->GetImageInfo (
289 Fmp, // FMP Pointer
290 &ImageInfoSize, // Buffer Size (in this case 0)
291 NULL, // NULL so we can get size
292 &FmpImageInfoDescriptorVer, // DescriptorVersion
293 &FmpImageInfoCount, // DescriptorCount
294 &DescriptorSize, // DescriptorSize
295 &PackageVersion, // PackageVersion
296 &PackageVersionName // PackageVersionName
297 );
298
299 if (Status != EFI_BUFFER_TOO_SMALL) {
300 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Unexpected Failure in GetImageInfo. Status = %r\n", Status));
301 continue;
302 }
303
304 FmpImageInfoBuf = AllocateZeroPool (ImageInfoSize);
305 if (FmpImageInfoBuf == NULL) {
306 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to get memory for descriptors.\n"));
307 continue;
308 }
309
310 FmpImageInfoBufOrg = FmpImageInfoBuf;
311 PackageVersionName = NULL;
312 Status = Fmp->GetImageInfo (
313 Fmp,
314 &ImageInfoSize, // ImageInfoSize
315 FmpImageInfoBuf, // ImageInfo
316 &FmpImageInfoDescriptorVer, // DescriptorVersion
317 &FmpImageInfoCount, // DescriptorCount
318 &DescriptorSize, // DescriptorSize
319 &PackageVersion, // PackageVersion
320 &PackageVersionName // PackageVersionName
321 );
322 if (EFI_ERROR (Status)) {
323 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failure in GetImageInfo. Status = %r\n", Status));
324 FreePool (FmpImageInfoBufOrg);
325 FmpImageInfoBufOrg = NULL;
326 continue;
327 }
328
329 //
330 // Check each descriptor and read from the one specified
331 //
332 while (FmpImageInfoCount > 0) {
333 //
334 // If the descriptor has the IN USE bit set, create ESRT entry otherwise ignore.
335 //
336 if ((FmpImageInfoBuf->AttributesSetting & FmpImageInfoBuf->AttributesSupported & IMAGE_ATTRIBUTE_IN_USE) == IMAGE_ATTRIBUTE_IN_USE) {
337 //
338 // Create ESRT entry
339 //
340 CreateEsrtEntry (&Table, FmpImageInfoBuf, FmpImageInfoDescriptorVer);
341 }
342 FmpImageInfoCount--;
343 //
344 // Increment the buffer pointer ahead by the size of the descriptor
345 //
346 FmpImageInfoBuf = (EFI_FIRMWARE_IMAGE_DESCRIPTOR *)(((UINT8 *)FmpImageInfoBuf) + DescriptorSize);
347 }
348
349 if (PackageVersionName != NULL) {
350 FreePool (PackageVersionName);
351 PackageVersionName = NULL;
352 }
353 FreePool (FmpImageInfoBufOrg);
354 FmpImageInfoBufOrg = NULL;
355 }
356
357 gBS->FreePool (Buffer);
358 return Table;
359 }
360
361 /**
362 Notify function for event group EFI_EVENT_GROUP_READY_TO_BOOT. This is used to
363 install the Efi System Resource Table.
364
365 @param[in] Event The Event that is being processed.
366 @param[in] Context The Event Context.
367
368 **/
369 VOID
370 EFIAPI
371 EsrtReadyToBootEventNotify (
372 IN EFI_EVENT Event,
373 IN VOID *Context
374 )
375 {
376 EFI_STATUS Status;
377 EFI_SYSTEM_RESOURCE_TABLE *Table;
378
379 Table = CreateFmpBasedEsrt ();
380 if (Table != NULL) {
381 //
382 // Print table on debug builds
383 //
384 DEBUG_CODE_BEGIN ();
385 PrintTable (Table);
386 DEBUG_CODE_END ();
387
388 Status = InstallEfiSystemResourceTableInUefiConfigurationTable (Table);
389 if (EFI_ERROR (Status)) {
390 FreePool (Table);
391 }
392 } else {
393 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Can't install ESRT table because it is NULL. \n"));
394 }
395
396 //
397 // Close the event to prevent it be signalled again.
398 //
399 gBS->CloseEvent (Event);
400 }
401
402 /**
403 The module Entry Point of the Efi System Resource Table DXE driver.
404
405 @param[in] ImageHandle The firmware allocated handle for the EFI image.
406 @param[in] SystemTable A pointer to the EFI System Table.
407
408 @retval EFI_SUCCESS The entry point is executed successfully.
409 @retval Other Some error occurs when executing this entry point.
410
411 **/
412 EFI_STATUS
413 EFIAPI
414 EsrtFmpEntryPoint (
415 IN EFI_HANDLE ImageHandle,
416 IN EFI_SYSTEM_TABLE *SystemTable
417 )
418 {
419 EFI_STATUS Status;
420 EFI_EVENT EsrtReadyToBootEvent;
421
422 //
423 // Register notify function to install ESRT on ReadyToBoot Event.
424 //
425 Status = EfiCreateEventReadyToBootEx (
426 TPL_CALLBACK,
427 EsrtReadyToBootEventNotify,
428 NULL,
429 &EsrtReadyToBootEvent
430 );
431
432 ASSERT_EFI_ERROR (Status);
433 if (EFI_ERROR (Status)) {
434 DEBUG ((DEBUG_ERROR, "EsrtFmpDxe: Failed to register for ready to boot\n"));
435 }
436
437 return Status;
438 }