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