1 /** @file 2 Install Platform EFI_PEI_RECOVERY_MODULE_PPI and Implementation of 3 EFI_PEI_LOAD_RECOVERY_CAPSULE service. 4 5 Copyright (c) 2013 Intel Corporation. 6 7 This program and the accompanying materials 8 are licensed and made available under the terms and conditions of the BSD License 9 which accompanies this distribution. The full text of the license may be found at 10 http://opensource.org/licenses/bsd-license.php 11 12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 14 15 **/ 16 17 #include "CommonHeader.h" 18 #include "PlatformEarlyInit.h" 19 20 #include <Ppi/BlockIo.h> 21 22 // 23 // Capsule Types supported in this platform module 24 // 25 #include <Guid/CapsuleOnFatFloppyDisk.h> 26 #include <Guid/CapsuleOnFatIdeDisk.h> 27 #include <Guid/CapsuleOnFatUsbDisk.h> 28 #include <Guid/CapsuleOnDataCD.h> 29 #include <Guid/QuarkCapsuleGuid.h> 30 31 #include <Ppi/RecoveryModule.h> 32 #include <Ppi/DeviceRecoveryModule.h> 33 34 #include <Library/PeiServicesLib.h> 35 36 // 37 // Required Service 38 // 39 EFI_STATUS 40 EFIAPI 41 PlatformRecoveryModule ( 42 IN EFI_PEI_SERVICES **PeiServices, 43 IN EFI_PEI_RECOVERY_MODULE_PPI *This 44 ); 45 46 VOID 47 AssertNoCapsulesError ( 48 IN EFI_PEI_SERVICES **PeiServices 49 ); 50 51 VOID 52 AssertMediaDeviceError ( 53 IN EFI_PEI_SERVICES **PeiServices 54 ); 55 56 VOID 57 ReportLoadCapsuleSuccess ( 58 IN EFI_PEI_SERVICES **PeiServices 59 ); 60 61 VOID 62 CheckIfMediaPresentOnBlockIoDevice ( 63 IN EFI_PEI_SERVICES **PeiServices, 64 IN OUT BOOLEAN *MediaDeviceError, 65 IN OUT BOOLEAN *MediaPresent 66 ); 67 68 // 69 // Module globals 70 // 71 EFI_PEI_RECOVERY_MODULE_PPI mRecoveryPpi = { PlatformRecoveryModule }; 72 73 EFI_PEI_PPI_DESCRIPTOR mRecoveryPpiList = { 74 (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), 75 &gEfiPeiRecoveryModulePpiGuid, 76 &mRecoveryPpi 77 }; 78 79 EFI_STATUS 80 EFIAPI 81 PeimInitializeRecovery ( 82 IN EFI_PEI_SERVICES **PeiServices 83 ) 84 /*++ 85 86 Routine Description: 87 88 Provide the functionality of the Recovery Module. 89 90 Arguments: 91 92 PeiServices - General purpose services available to every PEIM. 93 94 Returns: 95 96 EFI_SUCCESS - If the interface could be successfully 97 installed. 98 99 --*/ 100 { 101 EFI_STATUS Status; 102 103 Status = PeiServicesInstallPpi (&mRecoveryPpiList); 104 105 return Status; 106 } 107 108 EFI_STATUS 109 EFIAPI 110 PlatformRecoveryModule ( 111 IN EFI_PEI_SERVICES **PeiServices, 112 IN EFI_PEI_RECOVERY_MODULE_PPI *This 113 ) 114 /*++ 115 116 Routine Description: 117 118 Provide the functionality of the Platform Recovery Module. 119 120 Arguments: 121 122 PeiServices - General purpose services available to every PEIM. 123 This - Pointer to EFI_PEI_RECOVERY_MODULE_PPI. 124 125 Returns: 126 127 EFI_SUCCESS - If the interface could be successfully 128 installed. 129 EFI_UNSUPPORTED - Not supported. 130 131 --*/ 132 { 133 EFI_STATUS Status; 134 EFI_PEI_DEVICE_RECOVERY_MODULE_PPI *DeviceRecoveryModule; 135 UINTN NumberOfImageProviders; 136 BOOLEAN ProviderAvailable; 137 UINTN NumberRecoveryCapsules; 138 UINTN RecoveryCapsuleSize; 139 EFI_GUID DeviceId; 140 BOOLEAN ImageFound; 141 EFI_PHYSICAL_ADDRESS Address; 142 VOID *Buffer; 143 EFI_CAPSULE_HEADER *CapsuleHeader; 144 EFI_PEI_HOB_POINTERS Hob; 145 EFI_PEI_HOB_POINTERS HobOld; 146 BOOLEAN HobUpdate; 147 EFI_FIRMWARE_VOLUME_HEADER *FvHeader; 148 UINTN Index; 149 EFI_STATUS AuthStatus; 150 EFI_GUID mEfiCapsuleHeaderGuid = QUARK_CAPSULE_GUID; 151 152 Index = 0; 153 154 Status = EFI_SUCCESS; 155 AuthStatus = EFI_SUCCESS; 156 HobUpdate = FALSE; 157 158 ProviderAvailable = TRUE; 159 ImageFound = FALSE; 160 NumberOfImageProviders = 0; 161 162 DeviceRecoveryModule = NULL; 163 164 DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Recovery Entry\n")); 165 166 // 167 // Search the platform for some recovery capsule if the DXE IPL 168 // discovered a recovery condition and has requested a load. 169 // 170 while (ProviderAvailable) { 171 172 Status = PeiServicesLocatePpi ( 173 &gEfiPeiDeviceRecoveryModulePpiGuid, 174 Index, 175 NULL, 176 (VOID **)&DeviceRecoveryModule 177 ); 178 179 if (!EFI_ERROR (Status)) { 180 DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Device Recovery PPI located\n")); 181 NumberOfImageProviders++; 182 183 Status = DeviceRecoveryModule->GetNumberRecoveryCapsules ( 184 PeiServices, 185 DeviceRecoveryModule, 186 &NumberRecoveryCapsules 187 ); 188 189 DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Number Of Recovery Capsules: %d\n", NumberRecoveryCapsules)); 190 191 if (NumberRecoveryCapsules == 0) { 192 Index++; 193 } else { 194 break; 195 } 196 } else { 197 ProviderAvailable = FALSE; 198 } 199 } 200 // 201 // The number of recovery capsules is 0. 202 // 203 if (!ProviderAvailable) { 204 AssertNoCapsulesError (PeiServices); 205 } 206 // 207 // If there is an image provider, get the capsule ID 208 // 209 if (ProviderAvailable) { 210 RecoveryCapsuleSize = 0; 211 if (FeaturePcdGet (PcdFrameworkCompatibilitySupport)) { 212 Status = DeviceRecoveryModule->GetRecoveryCapsuleInfo ( 213 PeiServices, 214 DeviceRecoveryModule, 215 0, 216 &RecoveryCapsuleSize, 217 &DeviceId 218 ); 219 } else { 220 Status = DeviceRecoveryModule->GetRecoveryCapsuleInfo ( 221 PeiServices, 222 DeviceRecoveryModule, 223 1, 224 &RecoveryCapsuleSize, 225 &DeviceId 226 ); 227 228 229 } 230 231 if (EFI_ERROR (Status)) { 232 return Status; 233 } 234 235 DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Recovery Capsule Size: %d\n", RecoveryCapsuleSize)); 236 237 // 238 // Only support the 2 capsule types known 239 // Future enhancement is to rank-order the selection 240 // 241 if ((!CompareGuid (&DeviceId, &gPeiCapsuleOnFatIdeDiskGuid)) && 242 (!CompareGuid (&DeviceId, &gPeiCapsuleOnDataCDGuid)) && 243 (!CompareGuid (&DeviceId, &gPeiCapsuleOnFatUsbDiskGuid)) 244 ) { 245 return EFI_UNSUPPORTED; 246 } 247 248 Buffer = NULL; 249 Address = (UINTN) AllocatePages ((RecoveryCapsuleSize - 1) / 0x1000 + 1); 250 ASSERT (Address); 251 252 Buffer = (UINT8 *) (UINTN) Address; 253 if (FeaturePcdGet (PcdFrameworkCompatibilitySupport)) { 254 Status = DeviceRecoveryModule->LoadRecoveryCapsule ( 255 PeiServices, 256 DeviceRecoveryModule, 257 0, 258 Buffer 259 ); 260 } else { 261 Status = DeviceRecoveryModule->LoadRecoveryCapsule ( 262 PeiServices, 263 DeviceRecoveryModule, 264 1, 265 Buffer 266 ); 267 268 } 269 270 DEBUG ((EFI_D_INFO | EFI_D_LOAD, "LoadRecoveryCapsule Returns: %r\n", Status)); 271 272 if (Status == EFI_DEVICE_ERROR) { 273 AssertMediaDeviceError (PeiServices); 274 } 275 276 if (EFI_ERROR (Status)) { 277 return Status; 278 } else { 279 ReportLoadCapsuleSuccess (PeiServices); 280 } 281 282 // 283 // Update FV Hob if found 284 // 285 Buffer = (VOID *)((UINT8 *) Buffer); 286 Status = PeiServicesGetHobList ((VOID **)&Hob.Raw); 287 HobOld.Raw = Hob.Raw; 288 while (!END_OF_HOB_LIST (Hob)) { 289 if (Hob.Header->HobType == EFI_HOB_TYPE_FV) { 290 DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Hob FV Length: %x\n", Hob.FirmwareVolume->Length)); 291 292 if (Hob.FirmwareVolume->BaseAddress == (UINTN) PcdGet32 (PcdFlashFvMainBase)) { 293 HobUpdate = TRUE; 294 // 295 // This looks like the Hob we are interested in 296 // 297 DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Hob Updated\n")); 298 Hob.FirmwareVolume->BaseAddress = (UINTN) Buffer; 299 Hob.FirmwareVolume->Length = RecoveryCapsuleSize; 300 } 301 } 302 303 Hob.Raw = GET_NEXT_HOB (Hob); 304 } 305 // 306 // Check if the top of the file is a firmware volume header 307 // 308 FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *) Buffer; 309 CapsuleHeader = (EFI_CAPSULE_HEADER *) Buffer; 310 if (FvHeader->Signature == EFI_FVH_SIGNATURE) { 311 // 312 // build FV Hob if it is not built before 313 // 314 if (!HobUpdate) { 315 DEBUG ((EFI_D_INFO | EFI_D_LOAD, "FV Hob is not found, Build FV Hob then..\n")); 316 BuildFvHob ( 317 (UINTN) Buffer, 318 FvHeader->FvLength 319 ); 320 321 DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Install FV Info PPI..\n")); 322 323 PeiServicesInstallFvInfoPpi ( 324 NULL, 325 Buffer, 326 (UINT32) FvHeader->FvLength, 327 NULL, 328 NULL 329 ); 330 } 331 // 332 // Point to the location immediately after the FV. 333 // 334 CapsuleHeader = (EFI_CAPSULE_HEADER *) ((UINT8 *) Buffer + FvHeader->FvLength); 335 } 336 337 // 338 // Check if pointer is still within the buffer 339 // 340 if ((UINTN) CapsuleHeader < (UINTN) ((UINT8 *) Buffer + RecoveryCapsuleSize)) { 341 342 // 343 // Check if it is a capsule 344 // 345 if (CompareGuid ((EFI_GUID *) CapsuleHeader, &mEfiCapsuleHeaderGuid)) { 346 347 // 348 // Set bootmode to capsule update so the capsule hob gets the right bootmode in the hob header. 349 // 350 Status = PeiServicesSetBootMode (BOOT_ON_FLASH_UPDATE); 351 if (EFI_ERROR (Status)) { 352 return Status; 353 } 354 355 // 356 // Build capsule hob 357 // 358 BuildCvHob ((EFI_PHYSICAL_ADDRESS)(UINTN)CapsuleHeader, (UINT64)CapsuleHeader->CapsuleImageSize); 359 } 360 } 361 } 362 363 DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Recovery Module Returning: %r\n", Status)); 364 return Status; 365 } 366 367 /* 368 AssertNoCapsulesError: 369 There were no recovery capsules found. 370 Case 1: Report the error that no recovery block io device/media is readable and assert. 371 Case 2: Report the error that there is no media present on any recovery block io device and assert. 372 Case 3: There is media present on some recovery block io device, 373 but there is no recovery capsule on it. Report the error and assert. 374 */ 375 VOID 376 AssertNoCapsulesError ( 377 IN EFI_PEI_SERVICES **PeiServices 378 ) 379 { 380 BOOLEAN MediaDeviceError; 381 BOOLEAN MediaPresent; 382 383 MediaDeviceError = TRUE; 384 MediaPresent = FALSE; 385 386 CheckIfMediaPresentOnBlockIoDevice (PeiServices, &MediaDeviceError, &MediaPresent); 387 /* if (MediaDeviceError) { 388 ReportStatusCode ( 389 (EFI_ERROR_CODE | EFI_ERROR_UNRECOVERED), 390 (EFI_PERIPHERAL_RECOVERY_MEDIA | EFI_P_EC_MEDIA_DEVICE_ERROR) 391 ); 392 393 } else if (!MediaPresent) { 394 ReportStatusCode ( 395 (EFI_ERROR_CODE | EFI_ERROR_UNRECOVERED), 396 (EFI_PERIPHERAL_RECOVERY_MEDIA | EFI_P_EC_MEDIA_NOT_PRESENT) 397 ); 398 399 } else { 400 ReportStatusCode ( 401 (EFI_ERROR_CODE | EFI_ERROR_UNRECOVERED), 402 (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEIM_EC_NO_RECOVERY_CAPSULE) 403 ); 404 }*/ 405 // 406 // Hang. 407 // 408 CpuDeadLoop(); 409 } 410 411 #define MAX_BLOCK_IO_PPI 32 412 413 /* 414 CheckIfMediaPresentOnBlockIoDevice: 415 Checks to see whether there was a media device error or to see if there is media present. 416 */ 417 VOID 418 CheckIfMediaPresentOnBlockIoDevice ( 419 IN EFI_PEI_SERVICES **PeiServices, 420 IN OUT BOOLEAN *MediaDeviceError, 421 IN OUT BOOLEAN *MediaPresent 422 ) 423 { 424 EFI_STATUS Status; 425 UINTN BlockIoPpiInstance; 426 EFI_PEI_RECOVERY_BLOCK_IO_PPI *BlockIoPpi; 427 UINTN NumberBlockDevices; 428 EFI_PEI_BLOCK_IO_MEDIA Media; 429 430 *MediaDeviceError = TRUE; 431 *MediaPresent = FALSE; 432 433 for (BlockIoPpiInstance = 0; BlockIoPpiInstance < MAX_BLOCK_IO_PPI; BlockIoPpiInstance++) { 434 Status = PeiServicesLocatePpi ( 435 &gEfiPeiVirtualBlockIoPpiGuid, 436 BlockIoPpiInstance, 437 NULL, 438 (VOID **)&BlockIoPpi 439 ); 440 if (EFI_ERROR (Status)) { 441 // 442 // Done with all Block Io Ppis 443 // 444 break; 445 } 446 447 Status = BlockIoPpi->GetNumberOfBlockDevices ( 448 PeiServices, 449 BlockIoPpi, 450 &NumberBlockDevices 451 ); 452 if (EFI_ERROR (Status) || (NumberBlockDevices == 0)) { 453 continue; 454 } 455 // 456 // Just retrieve the first block 457 // 458 Status = BlockIoPpi->GetBlockDeviceMediaInfo ( 459 PeiServices, 460 BlockIoPpi, 461 0, 462 &Media 463 ); 464 if (!EFI_ERROR (Status)) { 465 *MediaDeviceError = FALSE; 466 if (Media.MediaPresent) { 467 *MediaPresent = TRUE; 468 break; 469 } 470 } 471 } 472 } 473 474 VOID 475 AssertMediaDeviceError ( 476 IN EFI_PEI_SERVICES **PeiServices 477 ) 478 { 479 /* ReportStatusCode ( 480 (EFI_ERROR_CODE | EFI_ERROR_UNRECOVERED), 481 (EFI_PERIPHERAL_RECOVERY_MEDIA | EFI_P_EC_MEDIA_DEVICE_ERROR) 482 ); 483 */ 484 CpuDeadLoop (); 485 } 486 487 VOID 488 ReportLoadCapsuleSuccess ( 489 IN EFI_PEI_SERVICES **PeiServices 490 ) 491 { 492 // 493 // EFI_SW_PEI_PC_CAPSULE_START: (from the status code spec): 494 // Loaded the recovery capsule. About to hand off control to the capsule. 495 // 496 /* ReportStatusCode ( 497 EFI_PROGRESS_CODE, 498 (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEIM_PC_CAPSULE_LOAD_SUCCESS) 499 );*/ 500 } 501 502