/** @file Install Platform EFI_PEI_RECOVERY_MODULE_PPI and Implementation of EFI_PEI_LOAD_RECOVERY_CAPSULE service. Copyright (c) 2013-2016 Intel Corporation. This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at http://opensource.org/licenses/bsd-license.php THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ #include "CommonHeader.h" #include "PlatformEarlyInit.h" #include // // Capsule Types supported in this platform module // #include #include #include #include #include #include #include #include // // Required Service // EFI_STATUS EFIAPI PlatformRecoveryModule ( IN EFI_PEI_SERVICES **PeiServices, IN EFI_PEI_RECOVERY_MODULE_PPI *This ); VOID AssertNoCapsulesError ( IN EFI_PEI_SERVICES **PeiServices ); VOID AssertMediaDeviceError ( IN EFI_PEI_SERVICES **PeiServices ); VOID ReportLoadCapsuleSuccess ( IN EFI_PEI_SERVICES **PeiServices ); VOID CheckIfMediaPresentOnBlockIoDevice ( IN EFI_PEI_SERVICES **PeiServices, IN OUT BOOLEAN *MediaDeviceError, IN OUT BOOLEAN *MediaPresent ); // // Module globals // EFI_PEI_RECOVERY_MODULE_PPI mRecoveryPpi = { PlatformRecoveryModule }; EFI_PEI_PPI_DESCRIPTOR mRecoveryPpiList = { (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), &gEfiPeiRecoveryModulePpiGuid, &mRecoveryPpi }; EFI_STATUS EFIAPI PeimInitializeRecovery ( IN EFI_PEI_SERVICES **PeiServices ) /*++ Routine Description: Provide the functionality of the Recovery Module. Arguments: PeiServices - General purpose services available to every PEIM. Returns: EFI_SUCCESS - If the interface could be successfully installed. --*/ { EFI_STATUS Status; Status = PeiServicesInstallPpi (&mRecoveryPpiList); return Status; } EFI_STATUS EFIAPI PlatformRecoveryModule ( IN EFI_PEI_SERVICES **PeiServices, IN EFI_PEI_RECOVERY_MODULE_PPI *This ) /*++ Routine Description: Provide the functionality of the Platform Recovery Module. Arguments: PeiServices - General purpose services available to every PEIM. This - Pointer to EFI_PEI_RECOVERY_MODULE_PPI. Returns: EFI_SUCCESS - If the interface could be successfully installed. EFI_UNSUPPORTED - Not supported. --*/ { EFI_STATUS Status; EFI_PEI_DEVICE_RECOVERY_MODULE_PPI *DeviceRecoveryModule; UINTN NumberOfImageProviders; BOOLEAN ProviderAvailable; UINTN NumberRecoveryCapsules; UINTN RecoveryCapsuleSize; EFI_GUID DeviceId; EFI_PHYSICAL_ADDRESS Address; VOID *Buffer; EFI_CAPSULE_HEADER *CapsuleHeader; EFI_PEI_HOB_POINTERS Hob; BOOLEAN HobUpdate; EFI_FIRMWARE_VOLUME_HEADER *FvHeader; UINTN Index; EFI_GUID mEfiCapsuleHeaderGuid = QUARK_CAPSULE_GUID; Index = 0; Status = EFI_SUCCESS; HobUpdate = FALSE; ProviderAvailable = TRUE; NumberOfImageProviders = 0; DeviceRecoveryModule = NULL; DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Recovery Entry\n")); // // Search the platform for some recovery capsule if the DXE IPL // discovered a recovery condition and has requested a load. // while (ProviderAvailable) { Status = PeiServicesLocatePpi ( &gEfiPeiDeviceRecoveryModulePpiGuid, Index, NULL, (VOID **)&DeviceRecoveryModule ); if (!EFI_ERROR (Status)) { DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Device Recovery PPI located\n")); NumberOfImageProviders++; Status = DeviceRecoveryModule->GetNumberRecoveryCapsules ( PeiServices, DeviceRecoveryModule, &NumberRecoveryCapsules ); DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Number Of Recovery Capsules: %d\n", NumberRecoveryCapsules)); if (NumberRecoveryCapsules == 0) { Index++; } else { break; } } else { ProviderAvailable = FALSE; } } // // The number of recovery capsules is 0. // if (!ProviderAvailable) { AssertNoCapsulesError (PeiServices); } // // If there is an image provider, get the capsule ID // if (ProviderAvailable) { RecoveryCapsuleSize = 0; if (FeaturePcdGet (PcdFrameworkCompatibilitySupport)) { Status = DeviceRecoveryModule->GetRecoveryCapsuleInfo ( PeiServices, DeviceRecoveryModule, 0, &RecoveryCapsuleSize, &DeviceId ); } else { Status = DeviceRecoveryModule->GetRecoveryCapsuleInfo ( PeiServices, DeviceRecoveryModule, 1, &RecoveryCapsuleSize, &DeviceId ); } if (EFI_ERROR (Status)) { return Status; } DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Recovery Capsule Size: %d\n", RecoveryCapsuleSize)); // // Only support the 2 capsule types known // Future enhancement is to rank-order the selection // if ((!CompareGuid (&DeviceId, &gPeiCapsuleOnFatIdeDiskGuid)) && (!CompareGuid (&DeviceId, &gPeiCapsuleOnDataCDGuid)) && (!CompareGuid (&DeviceId, &gPeiCapsuleOnFatUsbDiskGuid)) ) { return EFI_UNSUPPORTED; } Buffer = NULL; Address = (UINTN) AllocatePages ((RecoveryCapsuleSize - 1) / 0x1000 + 1); ASSERT (Address); Buffer = (UINT8 *) (UINTN) Address; if (FeaturePcdGet (PcdFrameworkCompatibilitySupport)) { Status = DeviceRecoveryModule->LoadRecoveryCapsule ( PeiServices, DeviceRecoveryModule, 0, Buffer ); } else { Status = DeviceRecoveryModule->LoadRecoveryCapsule ( PeiServices, DeviceRecoveryModule, 1, Buffer ); } DEBUG ((EFI_D_INFO | EFI_D_LOAD, "LoadRecoveryCapsule Returns: %r\n", Status)); if (Status == EFI_DEVICE_ERROR) { AssertMediaDeviceError (PeiServices); } if (EFI_ERROR (Status)) { return Status; } else { ReportLoadCapsuleSuccess (PeiServices); } // // Update FV Hob if found // Buffer = (VOID *)((UINT8 *) Buffer); Status = PeiServicesGetHobList ((VOID **)&Hob.Raw); while (!END_OF_HOB_LIST (Hob)) { if (Hob.Header->HobType == EFI_HOB_TYPE_FV) { DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Hob FV Length: %x\n", Hob.FirmwareVolume->Length)); if (Hob.FirmwareVolume->BaseAddress == (UINTN) PcdGet32 (PcdFlashFvMainBase)) { HobUpdate = TRUE; // // This looks like the Hob we are interested in // DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Hob Updated\n")); Hob.FirmwareVolume->BaseAddress = (UINTN) Buffer; Hob.FirmwareVolume->Length = RecoveryCapsuleSize; } } Hob.Raw = GET_NEXT_HOB (Hob); } // // Check if the top of the file is a firmware volume header // FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *) Buffer; CapsuleHeader = (EFI_CAPSULE_HEADER *) Buffer; if (FvHeader->Signature == EFI_FVH_SIGNATURE) { // // build FV Hob if it is not built before // if (!HobUpdate) { DEBUG ((EFI_D_INFO | EFI_D_LOAD, "FV Hob is not found, Build FV Hob then..\n")); BuildFvHob ( (UINTN) Buffer, FvHeader->FvLength ); DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Install FV Info PPI..\n")); PeiServicesInstallFvInfoPpi ( NULL, Buffer, (UINT32) FvHeader->FvLength, NULL, NULL ); } // // Point to the location immediately after the FV. // CapsuleHeader = (EFI_CAPSULE_HEADER *) ((UINT8 *) Buffer + FvHeader->FvLength); } // // Check if pointer is still within the buffer // if ((UINTN) CapsuleHeader < (UINTN) ((UINT8 *) Buffer + RecoveryCapsuleSize)) { // // Check if it is a capsule // if (CompareGuid ((EFI_GUID *) CapsuleHeader, &mEfiCapsuleHeaderGuid)) { // // Set bootmode to capsule update so the capsule hob gets the right bootmode in the hob header. // Status = PeiServicesSetBootMode (BOOT_ON_FLASH_UPDATE); if (EFI_ERROR (Status)) { return Status; } // // Build capsule hob // BuildCvHob ((EFI_PHYSICAL_ADDRESS)(UINTN)CapsuleHeader, (UINT64)CapsuleHeader->CapsuleImageSize); } } } DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Recovery Module Returning: %r\n", Status)); return Status; } /* AssertNoCapsulesError: There were no recovery capsules found. Case 1: Report the error that no recovery block io device/media is readable and assert. Case 2: Report the error that there is no media present on any recovery block io device and assert. Case 3: There is media present on some recovery block io device, but there is no recovery capsule on it. Report the error and assert. */ VOID AssertNoCapsulesError ( IN EFI_PEI_SERVICES **PeiServices ) { BOOLEAN MediaDeviceError; BOOLEAN MediaPresent; MediaDeviceError = TRUE; MediaPresent = FALSE; CheckIfMediaPresentOnBlockIoDevice (PeiServices, &MediaDeviceError, &MediaPresent); /* if (MediaDeviceError) { ReportStatusCode ( (EFI_ERROR_CODE | EFI_ERROR_UNRECOVERED), (EFI_PERIPHERAL_RECOVERY_MEDIA | EFI_P_EC_MEDIA_DEVICE_ERROR) ); } else if (!MediaPresent) { ReportStatusCode ( (EFI_ERROR_CODE | EFI_ERROR_UNRECOVERED), (EFI_PERIPHERAL_RECOVERY_MEDIA | EFI_P_EC_MEDIA_NOT_PRESENT) ); } else { ReportStatusCode ( (EFI_ERROR_CODE | EFI_ERROR_UNRECOVERED), (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEIM_EC_NO_RECOVERY_CAPSULE) ); }*/ // // Hang. // CpuDeadLoop(); } #define MAX_BLOCK_IO_PPI 32 /* CheckIfMediaPresentOnBlockIoDevice: Checks to see whether there was a media device error or to see if there is media present. */ VOID CheckIfMediaPresentOnBlockIoDevice ( IN EFI_PEI_SERVICES **PeiServices, IN OUT BOOLEAN *MediaDeviceError, IN OUT BOOLEAN *MediaPresent ) { EFI_STATUS Status; UINTN BlockIoPpiInstance; EFI_PEI_RECOVERY_BLOCK_IO_PPI *BlockIoPpi; UINTN NumberBlockDevices; EFI_PEI_BLOCK_IO_MEDIA Media; *MediaDeviceError = TRUE; *MediaPresent = FALSE; for (BlockIoPpiInstance = 0; BlockIoPpiInstance < MAX_BLOCK_IO_PPI; BlockIoPpiInstance++) { Status = PeiServicesLocatePpi ( &gEfiPeiVirtualBlockIoPpiGuid, BlockIoPpiInstance, NULL, (VOID **)&BlockIoPpi ); if (EFI_ERROR (Status)) { // // Done with all Block Io Ppis // break; } Status = BlockIoPpi->GetNumberOfBlockDevices ( PeiServices, BlockIoPpi, &NumberBlockDevices ); if (EFI_ERROR (Status) || (NumberBlockDevices == 0)) { continue; } // // Just retrieve the first block // Status = BlockIoPpi->GetBlockDeviceMediaInfo ( PeiServices, BlockIoPpi, 0, &Media ); if (!EFI_ERROR (Status)) { *MediaDeviceError = FALSE; if (Media.MediaPresent) { *MediaPresent = TRUE; break; } } } } VOID AssertMediaDeviceError ( IN EFI_PEI_SERVICES **PeiServices ) { /* ReportStatusCode ( (EFI_ERROR_CODE | EFI_ERROR_UNRECOVERED), (EFI_PERIPHERAL_RECOVERY_MEDIA | EFI_P_EC_MEDIA_DEVICE_ERROR) ); */ CpuDeadLoop (); } VOID ReportLoadCapsuleSuccess ( IN EFI_PEI_SERVICES **PeiServices ) { // // EFI_SW_PEI_PC_CAPSULE_START: (from the status code spec): // Loaded the recovery capsule. About to hand off control to the capsule. // /* ReportStatusCode ( EFI_PROGRESS_CODE, (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEIM_PC_CAPSULE_LOAD_SUCCESS) );*/ }