]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/EsrtDxe/EsrtDxe.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Universal / EsrtDxe / EsrtDxe.c
1 /** @file
2 Esrt management module.
3
4 Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7 **/
8 #include "EsrtImpl.h"
9
10
11 //
12 // Module globals.
13 //
14
15 ESRT_PRIVATE_DATA mPrivate;
16
17 ESRT_MANAGEMENT_PROTOCOL mEsrtManagementProtocolTemplate = {
18 EsrtDxeGetEsrtEntry,
19 EsrtDxeUpdateEsrtEntry,
20 EsrtDxeRegisterEsrtEntry,
21 EsrtDxeUnRegisterEsrtEntry,
22 EsrtDxeSyncFmp,
23 EsrtDxeLockEsrtRepository
24 };
25
26 /**
27 Get ESRT entry from ESRT Cache by FwClass Guid
28
29 @param[in] FwClass FwClass of Esrt entry to get
30 @param[in, out] Entry Esrt entry returned
31
32 @retval EFI_SUCCESS The variable saving this Esrt Entry exists.
33 @retval EF_NOT_FOUND No correct variable found.
34 @retval EFI_WRITE_PROTECTED ESRT Cache repository is locked
35
36 **/
37 EFI_STATUS
38 EFIAPI
39 EsrtDxeGetEsrtEntry(
40 IN EFI_GUID *FwClass,
41 IN OUT EFI_SYSTEM_RESOURCE_ENTRY *Entry
42 )
43 {
44 EFI_STATUS Status;
45
46 if (FwClass == NULL || Entry == NULL) {
47 return EFI_INVALID_PARAMETER;
48 }
49
50 Status = EfiAcquireLockOrFail (&mPrivate.NonFmpLock);
51 if (EFI_ERROR (Status)) {
52 return Status;
53 }
54
55 //
56 // Find in Non-FMP Cached Esrt Repository
57 //
58 Status = GetEsrtEntry(
59 FwClass,
60 ESRT_FROM_NONFMP,
61 Entry
62 );
63
64 EfiReleaseLock(&mPrivate.NonFmpLock);
65
66 if (EFI_ERROR(Status)) {
67 Status = EfiAcquireLockOrFail (&mPrivate.FmpLock);
68 if (EFI_ERROR (Status)) {
69 return Status;
70 }
71
72 //
73 // Find in FMP Cached Esrt NV Variable
74 //
75 Status = GetEsrtEntry(
76 FwClass,
77 ESRT_FROM_FMP,
78 Entry
79 );
80
81 EfiReleaseLock(&mPrivate.FmpLock);
82 }
83
84 return Status;
85 }
86
87 /**
88 Update one ESRT entry in ESRT Cache.
89
90 @param[in] Entry Esrt entry to be updated
91
92 @retval EFI_SUCCESS Successfully update an ESRT entry in cache.
93 @retval EFI_INVALID_PARAMETER Entry does't exist in ESRT Cache
94 @retval EFI_WRITE_PROTECTED ESRT Cache repositoy is locked
95
96 **/
97 EFI_STATUS
98 EFIAPI
99 EsrtDxeUpdateEsrtEntry(
100 IN EFI_SYSTEM_RESOURCE_ENTRY *Entry
101 )
102 {
103 EFI_STATUS Status;
104
105 if (Entry == NULL) {
106 return EFI_INVALID_PARAMETER;
107 }
108
109 Status = EfiAcquireLockOrFail (&mPrivate.FmpLock);
110 if (EFI_ERROR (Status)) {
111 return Status;
112 }
113
114 Status = UpdateEsrtEntry(Entry, ESRT_FROM_FMP);
115
116 if (!EFI_ERROR(Status)) {
117 EfiReleaseLock(&mPrivate.FmpLock);
118 return Status;
119 }
120 EfiReleaseLock(&mPrivate.FmpLock);
121
122
123 Status = EfiAcquireLockOrFail (&mPrivate.NonFmpLock);
124 if (EFI_ERROR (Status)) {
125 return Status;
126 }
127
128 Status = UpdateEsrtEntry(Entry, ESRT_FROM_NONFMP);
129
130 EfiReleaseLock(&mPrivate.NonFmpLock);
131
132 return Status;
133 }
134
135 /**
136 Non-FMP instance to unregister Esrt Entry from ESRT Cache.
137
138 @param[in] FwClass FwClass of Esrt entry to Unregister
139
140 @retval EFI_SUCCESS Insert all entries Successfully
141 @retval EFI_NOT_FOUND Entry of FwClass does not exsit
142
143 **/
144 EFI_STATUS
145 EFIAPI
146 EsrtDxeUnRegisterEsrtEntry(
147 IN EFI_GUID *FwClass
148 )
149 {
150 EFI_STATUS Status;
151
152 if (FwClass == NULL) {
153 return EFI_INVALID_PARAMETER;
154 }
155
156 Status = EfiAcquireLockOrFail (&mPrivate.NonFmpLock);
157 if (EFI_ERROR (Status)) {
158 return Status;
159 }
160
161 Status = DeleteEsrtEntry(FwClass, ESRT_FROM_NONFMP);
162
163 EfiReleaseLock(&mPrivate.NonFmpLock);
164
165 return Status;
166 }
167
168 /**
169 Non-FMP instance to register one ESRT entry into ESRT Cache.
170
171 @param[in] Entry Esrt entry to be set
172
173 @retval EFI_SUCCESS Successfully set a variable.
174 @retval EFI_INVALID_PARAMETER ESRT Entry is already exist
175 @retval EFI_OUT_OF_RESOURCES Non-FMP ESRT repository is full
176
177 **/
178 EFI_STATUS
179 EFIAPI
180 EsrtDxeRegisterEsrtEntry(
181 IN EFI_SYSTEM_RESOURCE_ENTRY *Entry
182 )
183 {
184 EFI_STATUS Status;
185 EFI_SYSTEM_RESOURCE_ENTRY EsrtEntryTmp;
186
187 if (Entry == NULL) {
188 return EFI_INVALID_PARAMETER;
189 }
190
191 Status = EfiAcquireLockOrFail (&mPrivate.NonFmpLock);
192 if (EFI_ERROR (Status)) {
193 return Status;
194 }
195
196 Status = GetEsrtEntry(
197 &Entry->FwClass,
198 ESRT_FROM_NONFMP,
199 &EsrtEntryTmp
200 );
201
202 if (Status == EFI_NOT_FOUND) {
203 Status = InsertEsrtEntry(Entry, ESRT_FROM_NONFMP);
204 }
205
206 EfiReleaseLock(&mPrivate.NonFmpLock);
207
208 return Status;
209 }
210
211 /**
212 This function syn up Cached ESRT with data from FMP instances
213 Function should be called after Connect All in order to locate all FMP protocols
214 installed.
215
216 @retval EFI_SUCCESS Successfully sync cache repository from FMP instances
217 @retval EFI_NOT_FOUND No FMP Instance are found
218 @retval EFI_OUT_OF_RESOURCES Resource allocaton fail
219
220 **/
221 EFI_STATUS
222 EFIAPI
223 EsrtDxeSyncFmp(
224 VOID
225 )
226 {
227 EFI_STATUS Status;
228 UINTN Index1;
229 UINTN Index2;
230 UINTN Index3;
231 EFI_HANDLE *HandleBuffer;
232 EFI_FIRMWARE_MANAGEMENT_PROTOCOL **FmpBuf;
233 UINTN NumberOfHandles;
234 UINTN *DescriptorSizeBuf;
235 EFI_FIRMWARE_IMAGE_DESCRIPTOR **FmpImageInfoBuf;
236 EFI_FIRMWARE_IMAGE_DESCRIPTOR *TempFmpImageInfo;
237 UINT8 *FmpImageInfoCountBuf;
238 UINT32 *FmpImageInfoDescriptorVerBuf;
239 UINTN ImageInfoSize;
240 UINT32 PackageVersion;
241 CHAR16 *PackageVersionName;
242 EFI_SYSTEM_RESOURCE_ENTRY *EsrtRepositoryNew;
243 UINTN EntryNumNew;
244
245 NumberOfHandles = 0;
246 EntryNumNew = 0;
247 FmpBuf = NULL;
248 HandleBuffer = NULL;
249 FmpImageInfoBuf = NULL;
250 FmpImageInfoCountBuf = NULL;
251 PackageVersionName = NULL;
252 DescriptorSizeBuf = NULL;
253 FmpImageInfoDescriptorVerBuf = NULL;
254 EsrtRepositoryNew = NULL;
255
256 //
257 // Get image information from all FMP protocol
258 //
259 Status = gBS->LocateHandleBuffer (
260 ByProtocol,
261 &gEfiFirmwareManagementProtocolGuid,
262 NULL,
263 &NumberOfHandles,
264 &HandleBuffer
265 );
266
267
268 if (Status == EFI_NOT_FOUND) {
269 EntryNumNew = 0;
270 goto UPDATE_REPOSITORY;
271 } else if (EFI_ERROR(Status)){
272 goto END;
273 }
274
275 //
276 // Allocate buffer to hold new FMP ESRT Cache repository
277 //
278 EsrtRepositoryNew = AllocateZeroPool(PcdGet32(PcdMaxFmpEsrtCacheNum) * sizeof(EFI_SYSTEM_RESOURCE_ENTRY));
279 if (EsrtRepositoryNew == NULL) {
280 Status = EFI_OUT_OF_RESOURCES;
281 goto END;
282 }
283
284 FmpBuf = AllocatePool(sizeof(EFI_FIRMWARE_MANAGEMENT_PROTOCOL *) * NumberOfHandles);
285 if (FmpBuf == NULL) {
286 Status = EFI_OUT_OF_RESOURCES;
287 goto END;
288 }
289
290 FmpImageInfoBuf = AllocateZeroPool(sizeof(EFI_FIRMWARE_IMAGE_DESCRIPTOR *) * NumberOfHandles);
291 if (FmpImageInfoBuf == NULL) {
292 Status = EFI_OUT_OF_RESOURCES;
293 goto END;
294 }
295
296 FmpImageInfoCountBuf = AllocateZeroPool(sizeof(UINT8) * NumberOfHandles);
297 if (FmpImageInfoCountBuf == NULL) {
298 Status = EFI_OUT_OF_RESOURCES;
299 goto END;
300 }
301
302 DescriptorSizeBuf = AllocateZeroPool(sizeof(UINTN) * NumberOfHandles);
303 if (DescriptorSizeBuf == NULL) {
304 Status = EFI_OUT_OF_RESOURCES;
305 goto END;
306 }
307
308 FmpImageInfoDescriptorVerBuf = AllocateZeroPool(sizeof(UINT32) * NumberOfHandles);
309 if (FmpImageInfoDescriptorVerBuf == NULL) {
310 Status = EFI_OUT_OF_RESOURCES;
311 goto END;
312 }
313
314 //
315 // Get all FmpImageInfo Descriptor into FmpImageInfoBuf
316 //
317 for (Index1 = 0; Index1 < NumberOfHandles; Index1++){
318 Status = gBS->HandleProtocol(
319 HandleBuffer[Index1],
320 &gEfiFirmwareManagementProtocolGuid,
321 (VOID **)&FmpBuf[Index1]
322 );
323
324 if (EFI_ERROR(Status)) {
325 continue;
326 }
327
328 ImageInfoSize = 0;
329 Status = FmpBuf[Index1]->GetImageInfo (
330 FmpBuf[Index1],
331 &ImageInfoSize,
332 NULL,
333 NULL,
334 NULL,
335 NULL,
336 NULL,
337 NULL
338 );
339
340 if (Status == EFI_BUFFER_TOO_SMALL) {
341 FmpImageInfoBuf[Index1] = AllocateZeroPool(ImageInfoSize);
342 if (FmpImageInfoBuf[Index1] == NULL) {
343 Status = EFI_OUT_OF_RESOURCES;
344 goto END;
345 }
346 } else {
347 continue;
348 }
349
350 PackageVersionName = NULL;
351 Status = FmpBuf[Index1]->GetImageInfo (
352 FmpBuf[Index1],
353 &ImageInfoSize,
354 FmpImageInfoBuf[Index1],
355 &FmpImageInfoDescriptorVerBuf[Index1],
356 &FmpImageInfoCountBuf[Index1],
357 &DescriptorSizeBuf[Index1],
358 &PackageVersion,
359 &PackageVersionName
360 );
361
362 //
363 // If FMP GetInformation interface failed, skip this resource
364 //
365 if (EFI_ERROR(Status)){
366 FmpImageInfoCountBuf[Index1] = 0;
367 continue;
368 }
369
370 if (PackageVersionName != NULL) {
371 FreePool(PackageVersionName);
372 }
373 }
374
375 //
376 // Create new FMP cache repository based on FmpImageInfoBuf
377 //
378 for (Index2 = 0; Index2 < NumberOfHandles; Index2++){
379 TempFmpImageInfo = FmpImageInfoBuf[Index2];
380 for (Index3 = 0; Index3 < FmpImageInfoCountBuf[Index2]; Index3++){
381 if ((TempFmpImageInfo->AttributesSupported & IMAGE_ATTRIBUTE_IN_USE) != 0
382 && (TempFmpImageInfo->AttributesSetting & IMAGE_ATTRIBUTE_IN_USE) != 0){
383 //
384 // Always put the first smallest version of Image info into ESRT cache
385 //
386 for(Index1 = 0; Index1 < EntryNumNew; Index1++) {
387 if (CompareGuid(&EsrtRepositoryNew[Index1].FwClass, &TempFmpImageInfo->ImageTypeId)) {
388 if(EsrtRepositoryNew[Index1].FwVersion > TempFmpImageInfo->Version) {
389 SetEsrtEntryFromFmpInfo(&EsrtRepositoryNew[Index1], TempFmpImageInfo, FmpImageInfoDescriptorVerBuf[Index2]);
390 }
391 break;
392 }
393 }
394 //
395 // New ImageTypeId can't be found in EsrtRepositoryNew. Create a new one
396 //
397 if (Index1 == EntryNumNew){
398 SetEsrtEntryFromFmpInfo(&EsrtRepositoryNew[EntryNumNew], TempFmpImageInfo, FmpImageInfoDescriptorVerBuf[Index2]);
399 EntryNumNew++;
400 if (EntryNumNew >= PcdGet32(PcdMaxFmpEsrtCacheNum)) {
401 break;
402 }
403 }
404 }
405
406 //
407 // Use DescriptorSize to move ImageInfo Pointer to stay compatible with different ImageInfo version
408 //
409 TempFmpImageInfo = (EFI_FIRMWARE_IMAGE_DESCRIPTOR *)((UINT8 *)TempFmpImageInfo + DescriptorSizeBuf[Index2]);
410 }
411 }
412
413 UPDATE_REPOSITORY:
414
415 Status = EfiAcquireLockOrFail (&mPrivate.FmpLock);
416 if (EFI_ERROR (Status)) {
417 return Status;
418 }
419
420 Status = gRT->SetVariable(
421 EFI_ESRT_FMP_VARIABLE_NAME,
422 &gEfiCallerIdGuid,
423 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
424 EntryNumNew * sizeof(EFI_SYSTEM_RESOURCE_ENTRY),
425 EsrtRepositoryNew
426 );
427
428 EfiReleaseLock(&mPrivate.FmpLock);
429
430 END:
431 if (EsrtRepositoryNew != NULL) {
432 FreePool(EsrtRepositoryNew);
433 }
434
435 if (HandleBuffer != NULL) {
436 FreePool(HandleBuffer);
437 }
438
439 if (FmpBuf != NULL) {
440 FreePool(FmpBuf);
441 }
442
443 if (FmpImageInfoCountBuf != NULL) {
444 FreePool(FmpImageInfoCountBuf);
445 }
446
447 if (DescriptorSizeBuf != NULL) {
448 FreePool(DescriptorSizeBuf);
449 }
450
451 if (FmpImageInfoDescriptorVerBuf != NULL) {
452 FreePool(FmpImageInfoDescriptorVerBuf);
453 }
454
455 if (FmpImageInfoBuf != NULL) {
456 for (Index1 = 0; Index1 < NumberOfHandles; Index1++){
457 if (FmpImageInfoBuf[Index1] != NULL) {
458 FreePool(FmpImageInfoBuf[Index1]);
459 }
460 }
461 FreePool(FmpImageInfoBuf);
462 }
463
464 return Status;
465 }
466
467 /**
468 This function locks up Esrt repository to be readonly. It should be called
469 before gEfiEndOfDxeEventGroupGuid event signaled
470
471 @retval EFI_SUCCESS Locks up FMP Non-FMP repository successfully
472
473 **/
474 EFI_STATUS
475 EFIAPI
476 EsrtDxeLockEsrtRepository(
477 VOID
478 )
479 {
480 EFI_STATUS Status;
481 EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock;
482 //
483 // Mark ACPI_GLOBAL_VARIABLE variable to read-only if the Variable Lock protocol exists
484 //
485 Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock);
486 if (!EFI_ERROR (Status)) {
487 Status = VariableLock->RequestToLock (VariableLock, EFI_ESRT_FMP_VARIABLE_NAME, &gEfiCallerIdGuid);
488 DEBUG((EFI_D_INFO, "EsrtDxe Lock EsrtFmp Variable Status 0x%x", Status));
489
490 Status = VariableLock->RequestToLock (VariableLock, EFI_ESRT_NONFMP_VARIABLE_NAME, &gEfiCallerIdGuid);
491 DEBUG((EFI_D_INFO, "EsrtDxe Lock EsrtNonFmp Variable Status 0x%x", Status));
492 }
493
494 return Status;
495 }
496
497 /**
498 Notify function for event group EFI_EVENT_GROUP_READY_TO_BOOT. This is used to
499 install the Esrt Table into system configuration table
500
501 @param[in] Event The Event that is being processed.
502 @param[in] Context The Event Context.
503
504 **/
505 VOID
506 EFIAPI
507 EsrtReadyToBootEventNotify (
508 IN EFI_EVENT Event,
509 IN VOID *Context
510 )
511 {
512 EFI_STATUS Status;
513 EFI_SYSTEM_RESOURCE_TABLE *EsrtTable;
514 EFI_SYSTEM_RESOURCE_ENTRY *FmpEsrtRepository;
515 EFI_SYSTEM_RESOURCE_ENTRY *NonFmpEsrtRepository;
516 UINTN FmpRepositorySize;
517 UINTN NonFmpRepositorySize;
518
519
520 FmpEsrtRepository = NULL;
521 NonFmpEsrtRepository = NULL;
522 FmpRepositorySize = 0;
523 NonFmpRepositorySize = 0;
524
525 Status = EfiAcquireLockOrFail (&mPrivate.NonFmpLock);
526 if (EFI_ERROR (Status)) {
527 return;
528 }
529
530 Status = GetVariable2 (
531 EFI_ESRT_NONFMP_VARIABLE_NAME,
532 &gEfiCallerIdGuid,
533 (VOID **) &NonFmpEsrtRepository,
534 &NonFmpRepositorySize
535 );
536
537 if (EFI_ERROR(Status)) {
538 NonFmpRepositorySize = 0;
539 }
540
541 if (NonFmpRepositorySize % sizeof(EFI_SYSTEM_RESOURCE_ENTRY) != 0) {
542 DEBUG((EFI_D_ERROR, "NonFmp Repository Corrupt. Need to rebuild NonFmp Repository.\n"));
543 NonFmpRepositorySize = 0;
544 }
545
546 EfiReleaseLock(&mPrivate.NonFmpLock);
547
548 Status = EfiAcquireLockOrFail (&mPrivate.FmpLock);
549 Status = GetVariable2 (
550 EFI_ESRT_FMP_VARIABLE_NAME,
551 &gEfiCallerIdGuid,
552 (VOID **) &FmpEsrtRepository,
553 &FmpRepositorySize
554 );
555
556 if (EFI_ERROR(Status)) {
557 FmpRepositorySize = 0;
558 }
559
560 if (FmpRepositorySize % sizeof(EFI_SYSTEM_RESOURCE_ENTRY) != 0) {
561 DEBUG((EFI_D_ERROR, "Fmp Repository Corrupt. Need to rebuild Fmp Repository.\n"));
562 FmpRepositorySize = 0;
563 }
564
565 EfiReleaseLock(&mPrivate.FmpLock);
566
567 //
568 // Skip ESRT table publish if no ESRT entry exists
569 //
570 if (NonFmpRepositorySize + FmpRepositorySize == 0) {
571 goto EXIT;
572 }
573
574 EsrtTable = AllocatePool(sizeof(EFI_SYSTEM_RESOURCE_TABLE) + NonFmpRepositorySize + FmpRepositorySize);
575 if (EsrtTable == NULL) {
576 DEBUG ((EFI_D_ERROR, "Esrt table memory allocation failure\n"));
577 goto EXIT;
578 }
579
580 EsrtTable->FwResourceVersion = EFI_SYSTEM_RESOURCE_TABLE_FIRMWARE_RESOURCE_VERSION;
581 EsrtTable->FwResourceCount = (UINT32)((NonFmpRepositorySize + FmpRepositorySize) / sizeof(EFI_SYSTEM_RESOURCE_ENTRY));
582 EsrtTable->FwResourceCountMax = PcdGet32(PcdMaxNonFmpEsrtCacheNum) + PcdGet32(PcdMaxFmpEsrtCacheNum);
583
584 if (NonFmpRepositorySize != 0 && NonFmpEsrtRepository != NULL) {
585 CopyMem(EsrtTable + 1, NonFmpEsrtRepository, NonFmpRepositorySize);
586 }
587
588 if (FmpRepositorySize != 0 && FmpEsrtRepository != NULL) {
589 CopyMem((UINT8 *)(EsrtTable + 1) + NonFmpRepositorySize, FmpEsrtRepository, FmpRepositorySize);
590 }
591
592 //
593 // Publish Esrt to system config table
594 //
595 Status = gBS->InstallConfigurationTable (&gEfiSystemResourceTableGuid, EsrtTable);
596
597 //
598 // Only one successful install
599 //
600 gBS->CloseEvent(Event);
601
602 EXIT:
603
604 if (FmpEsrtRepository != NULL) {
605 FreePool(FmpEsrtRepository);
606 }
607
608 if (NonFmpEsrtRepository != NULL) {
609 FreePool(NonFmpEsrtRepository);
610 }
611 }
612
613 /**
614 The module Entry Point of the Esrt DXE driver that manages cached ESRT repository
615 & publishes ESRT table
616
617 @param[in] ImageHandle The firmware allocated handle for the EFI image.
618 @param[in] SystemTable A pointer to the EFI System Table.
619
620 @retval EFI_SUCCESS The entry point is executed successfully.
621 @retval Other Some error occurs when executing this entry point.
622
623 **/
624 EFI_STATUS
625 EFIAPI
626 EsrtDxeEntryPoint (
627 IN EFI_HANDLE ImageHandle,
628 IN EFI_SYSTEM_TABLE *SystemTable
629 )
630 {
631 EFI_STATUS Status;
632
633 EfiInitializeLock (&mPrivate.FmpLock, TPL_CALLBACK);
634 EfiInitializeLock (&mPrivate.NonFmpLock, TPL_CALLBACK);
635
636 //
637 // Install Esrt management Protocol
638 //
639 Status = gBS->InstallMultipleProtocolInterfaces (
640 &mPrivate.Handle,
641 &gEsrtManagementProtocolGuid,
642 &mEsrtManagementProtocolTemplate,
643 NULL
644 );
645 ASSERT_EFI_ERROR (Status);
646
647 //
648 // Register notify function to install Esrt Table on ReadyToBoot Event.
649 //
650 Status = gBS->CreateEventEx (
651 EVT_NOTIFY_SIGNAL,
652 TPL_CALLBACK,
653 EsrtReadyToBootEventNotify,
654 NULL,
655 &gEfiEventReadyToBootGuid,
656 &mPrivate.Event
657 );
658 ASSERT_EFI_ERROR (Status);
659
660 return EFI_SUCCESS;
661 }