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 if (NonFmpRepositorySize != 0 && NonFmpEsrtRepository != NULL) { 591 CopyMem(EsrtTable + 1, NonFmpEsrtRepository, NonFmpRepositorySize); 592 } 593 594 if (FmpRepositorySize != 0 && FmpEsrtRepository != NULL) { 595 CopyMem((UINT8 *)(EsrtTable + 1) + NonFmpRepositorySize, FmpEsrtRepository, FmpRepositorySize); 596 } 597 598 // 599 // Publish Esrt to system config table 600 // 601 Status = gBS->InstallConfigurationTable (&gEfiSystemResourceTableGuid, EsrtTable); 602 603 // 604 // Only one successful install 605 // 606 gBS->CloseEvent(Event); 607 608 EXIT: 609 610 if (FmpEsrtRepository != NULL) { 611 FreePool(FmpEsrtRepository); 612 } 613 614 if (NonFmpEsrtRepository != NULL) { 615 FreePool(NonFmpEsrtRepository); 616 } 617 } 618 619 /** 620 The module Entry Point of the Esrt DXE driver that manages cached ESRT repository 621 & publishes ESRT table 622 623 @param[in] ImageHandle The firmware allocated handle for the EFI image. 624 @param[in] SystemTable A pointer to the EFI System Table. 625 626 @retval EFI_SUCCESS The entry point is executed successfully. 627 @retval Other Some error occurs when executing this entry point. 628 629 **/ 630 EFI_STATUS 631 EFIAPI 632 EsrtDxeEntryPoint ( 633 IN EFI_HANDLE ImageHandle, 634 IN EFI_SYSTEM_TABLE *SystemTable 635 ) 636 { 637 EFI_STATUS Status; 638 639 EfiInitializeLock (&mPrivate.FmpLock, TPL_CALLBACK); 640 EfiInitializeLock (&mPrivate.NonFmpLock, TPL_CALLBACK); 641 642 // 643 // Install Esrt management Protocol 644 // 645 Status = gBS->InstallMultipleProtocolInterfaces ( 646 &mPrivate.Handle, 647 &gEsrtManagementProtocolGuid, 648 &mEsrtManagementProtocolTemplate, 649 NULL 650 ); 651 ASSERT_EFI_ERROR (Status); 652 653 // 654 // Register notify function to install Esrt Table on ReadyToBoot Event. 655 // 656 Status = gBS->CreateEventEx ( 657 EVT_NOTIFY_SIGNAL, 658 TPL_CALLBACK, 659 EsrtReadyToBootEventNotify, 660 NULL, 661 &gEfiEventReadyToBootGuid, 662 &mPrivate.Event 663 ); 664 ASSERT_EFI_ERROR (Status); 665 666 return EFI_SUCCESS; 667 } 668