/** @file Provide support functions for FVB services in protected mode (including EFI boot time and EFI runtime.) ;****************************************************************************** ;* Copyright (c) 2012 - 2021, Insyde Software Corp. All Rights Reserved. ;* ;* You may not reproduce, distribute, publish, display, perform, modify, adapt, ;* transmit, broadcast, present, recite, release, license or otherwise exploit ;* any part of this publication in any form, by any means, without the prior ;* written permission of Insyde Software Corporation. ;* ;****************************************************************************** */ #include #include "RuntimeFunctions.h" #include "CommonFunctions.h" SMM_FVB_BUFFER *mSmmCommunicationBuffer; SMM_FVB_BUFFER *mSmmPhyCommunicationBuffer; UINTN mSmmCommunicationBufferSize; BOOLEAN mGoneVirtual; BOOLEAN mFvbAccessThroughSmi; EFI_SMM_COMMUNICATION_PROTOCOL *mSmmCommunication; extern EFI_GUID gEfiAlternateFvBlockGuid; FV_MEMMAP_DEVICE_PATH mFvMemmapDevicePathTemplate = { { { HARDWARE_DEVICE_PATH, HW_MEMMAP_DP, { (UINT8)(sizeof (MEMMAP_DEVICE_PATH)), (UINT8)(sizeof (MEMMAP_DEVICE_PATH) >> 8) } }, EfiMemoryMappedIO, (EFI_PHYSICAL_ADDRESS) 0, (EFI_PHYSICAL_ADDRESS) 0, }, { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, { END_DEVICE_PATH_LENGTH, 0 } } }; FV_PIWG_DEVICE_PATH mFvPIWGDevicePathTemplate = { { { MEDIA_DEVICE_PATH, MEDIA_PIWG_FW_VOL_DP, { (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH)), (UINT8)(sizeof (MEDIA_FW_VOL_DEVICE_PATH) >> 8) } }, { 0 } }, { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, { END_DEVICE_PATH_LENGTH, 0 } } }; EFI_FW_VOL_BLOCK_DEVICE mFvbDeviceTemplate = { FVB_DEVICE_SIGNATURE, NULL, 0, { FvbProtocolGetAttributes, FvbProtocolSetAttributes, FvbProtocolGetPhysicalAddress, FvbProtocolGetBlockSize, FvbProtocolRead, FvbProtocolWrite, FvbProtocolEraseBlocks, NULL } }; /** Creates and returns a notification event and registers that event with all the protocol instances specified by ProtocolGuid. This function causes the notification function to be executed for every protocol of type ProtocolGuid instance that exists in the system when this function is invoked. In addition, every time a protocol of type ProtocolGuid instance is installed or reinstalled, the notification function is also executed. This function returns the notification event that was created. If ProtocolGuid is NULL, then ASSERT(). If NotifyTpl is not a legal TPL value, then ASSERT(). If NotifyFunction is NULL, then ASSERT(). If Registration is NULL, then ASSERT(). @param[in] ProtocolGuid Supplies GUID of the protocol upon whose installation the event is fired. @param[in] NotifyTpl Supplies the task priority level of the event notifications. @param[in] NotifyFunction Supplies the function to notify when the event is signaled. @param[in] NotifyContext The context parameter to pass to NotifyFunction. @param[out] Registration A pointer to a memory location to receive the registration value. This value is passed to LocateHandle() to obtain new handles that have been added that support the ProtocolGuid-specified protocol. @return EFI_EVENT The notification event that was created. **/ STATIC EFI_EVENT EFIAPI InternalCreateProtocolNotifyEvent( IN EFI_GUID *ProtocolGuid, IN EFI_TPL NotifyTpl, IN EFI_EVENT_NOTIFY NotifyFunction, IN VOID *NotifyContext, OPTIONAL OUT VOID **Registration ) { EFI_STATUS Status; EFI_EVENT Event; ASSERT (ProtocolGuid != NULL); ASSERT (NotifyFunction != NULL); ASSERT (Registration != NULL); // // Create the event // Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, NotifyTpl, NotifyFunction, NotifyContext, &Event ); ASSERT_EFI_ERROR (Status); // // Register for protocol notifications on this event // Status = gBS->RegisterProtocolNotify ( ProtocolGuid, Event, Registration ); ASSERT_EFI_ERROR (Status); return Event; } /** Internal function to allocate buffer for accessing FVB through SMI. @return EFI_SUCCESS Allocate resources successful. @return EFI_OUT_OF_RESOURCES Allocate pool failed. **/ STATIC EFI_STATUS InitializeResourceForFvbAcessTroughSmi ( VOID ) { EFI_STATUS Status; EFI_HANDLE Handle; // // Allocate mSmmCommunicationBuffer for access FVB through SMI // mSmmCommunicationBuffer = FvbAllocateZeroBuffer ( SMM_COMMUNICATE_HEADER_SIZE + sizeof (SMM_FVB_BUFFER) + (UINTN) FdmGetNAtSize (&gH2OFlashMapRegionFtwBackupGuid , 1), TRUE ); ASSERT (mSmmCommunicationBuffer != NULL); if (mSmmCommunicationBuffer == NULL) { return EFI_OUT_OF_RESOURCES; } mSmmPhyCommunicationBuffer = mSmmCommunicationBuffer; mSmmCommunicationBufferSize = SMM_COMMUNICATE_HEADER_SIZE + sizeof (SMM_FVB_BUFFER) + (UINTN) FdmGetNAtSize (&gH2OFlashMapRegionFtwBackupGuid , 1); Handle = NULL; Status = gBS->InstallMultipleProtocolInterfaces ( &Handle, &gEfiCallerIdGuid, mSmmCommunicationBuffer, NULL ); Status = gBS->LocateProtocol (&gEfiSmmCommunicationProtocolGuid, NULL, (VOID **) &mSmmCommunication); ASSERT_EFI_ERROR (Status); return Status; } /** Callback function to provide FVB access interface for FVB access through SMI. After SMM related services for accessing FVB through, system will SMM code will signal this callback function to initialize related code and provide FVB access interface in protected mode. @param[in] Event The Event that is being processed. @param[in] Context The Event Context. **/ STATIC VOID EFIAPI EnableFvbAccessThroughSmiEvent ( IN EFI_EVENT Event, IN VOID *Context ) { EFI_STATUS Status; mFvbAccessThroughSmi = TRUE; Status = InitializeResourceForFvbAcessTroughSmi (); ASSERT_EFI_ERROR (Status); Status = InitializeFVbServices (); ASSERT_EFI_ERROR (Status); if (Event != NULL) { gBS->CloseEvent (Event); } } /** Fixup internal data so that EFI and SAL can be call in virtual mode. Call the passed in Child Notify event and convert the mFvbModuleGlobal date items to there virtual address. mFvbModuleGlobal->FvInstance[FVB_PHYSICAL] - Physical copy of instance data mFvbModuleGlobal->FvInstance[FVB_VIRTUAL] - Virtual pointer to common instance data. @param[in] Event The Event that is being processed. @param[in] Context The Event Context. **/ STATIC VOID EFIAPI FvbVirtualddressChangeEvent ( IN EFI_EVENT Event, IN VOID *Context ) { EFI_FW_VOL_INSTANCE *FwhInstance; UINTN Index; gRT->ConvertPointer (0x0, (VOID **) &mFvbModuleGlobal->FvInstance[FVB_VIRTUAL]); // // Convert the base address of all the instances // Index = 0; FwhInstance = mFvbModuleGlobal->FvInstance[FVB_PHYSICAL]; while (Index < mFvbModuleGlobal->NumFv) { gRT->ConvertPointer (0x0, (VOID **) &FwhInstance->FvBase[FVB_VIRTUAL]); FwhInstance = (EFI_FW_VOL_INSTANCE *) ( (UINTN) ((UINT8 *) FwhInstance) + FwhInstance->VolumeHeader.HeaderLength + (sizeof (EFI_FW_VOL_INSTANCE) - sizeof (EFI_FIRMWARE_VOLUME_HEADER)) ); Index++; } gRT->ConvertPointer (0x0, (VOID **) &mFvbModuleGlobal->FvbScratchSpace[FVB_VIRTUAL]); gRT->ConvertPointer (0x0, (VOID **) &mFvbModuleGlobal); if (IsFvbAccessThroughSmi ()) { gRT->ConvertPointer (0x0, (VOID **) &mSmmCommunicationBuffer); gRT->ConvertPointer (0x0, (VOID **) &mSmmCommunication); } mGoneVirtual = TRUE; } /** Check the integrity of firmware volume header. @param[in] FwVolHeader A pointer to a firmware volume header. @retval EFI_SUCCESS The firmware volume is consistent. @retval EFI_NOT_FOUND The firmware volume has corrupted. So it is not an FV. **/ STATIC EFI_STATUS ValidateFvHeader ( IN EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader ) { // // Verify the header revision, header signature, length // Length of FvBlock cannot be 2**64-1 // HeaderLength cannot be an odd number // if ((FwVolHeader->Revision != EFI_FVH_REVISION) || (FwVolHeader->Signature != EFI_FVH_SIGNATURE) || (FwVolHeader->FvLength == ((UINTN) -1)) || ((FwVolHeader->HeaderLength & 0x01) != 0)) { return EFI_NOT_FOUND; } // // Verify the header checksum // if (CalculateCheckSum16 ((UINT16 *) FwVolHeader, FwVolHeader->HeaderLength) != 0) { return EFI_NOT_FOUND; } return EFI_SUCCESS; } /** This function uses to get NV Storage base address @return EFI_PHYSICAL_ADDRESS the NV storage start address. **/ STATIC EFI_PHYSICAL_ADDRESS GetNvStoreBaseAddress ( VOID ) { return (EFI_PHYSICAL_ADDRESS) FdmGetNAtAddr (&gH2OFlashMapRegionVarGuid , 1); } /** This functions uses to calculate total size memory size by bytes for FV instances buffer usage. @param[out] FvInstanceBufferSize Pointer to total buffer size. @retval EFI_SUCCESS Calculate total FV instances buffer size successful. @retval EFI_INVALID_PARAMETER FvInstanceBufferSize is NULL. **/ STATIC EFI_STATUS ClaculateFvInstancesBufferSize ( OUT UINTN *FvInstanceBufferSize ) { EFI_STATUS Status; EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; EFI_PHYSICAL_ADDRESS BaseAddress; if (FvInstanceBufferSize == NULL) { return EFI_INVALID_PARAMETER; } BaseAddress = GetNvStoreBaseAddress (); FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) (UINTN) BaseAddress; Status = ValidateFvHeader (FwVolHeader); if (EFI_ERROR (Status)) { Status = GetFvbInfo (BaseAddress, &FwVolHeader); if (EFI_ERROR (Status)) { return EFI_NOT_FOUND; } } *FvInstanceBufferSize = (sizeof (EFI_FW_VOL_INSTANCE) + FwVolHeader->HeaderLength - sizeof (EFI_FIRMWARE_VOLUME_HEADER)); return EFI_SUCCESS; } /** This function restores correct firmware volume header contents to firmware volume header. @param[in] BaseAddress Firmware volume start address. @retval EFI_SUCCESS Restore firmware volume header contents successful. @retval EFI_NOT_FOUND Cannot get correct firmware volume header contents. @return Other Write firmware volume header contents failed. **/ STATIC EFI_STATUS RestoreCorrectFvHeader ( IN EFI_PHYSICAL_ADDRESS BaseAddress ) { EFI_STATUS Status; EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; UINTN DataSize; UINTN EraseSize; RecalculateFvHeaderChecksum (); Status = GetFvbInfo (BaseAddress, &FwVolHeader); if (EFI_ERROR (Status)) { return Status; } // // Write healthy FV header back. // EraseSize = (UINTN) FdmGetNAtSize (&gH2OFlashMapRegionVarGuid, 1); if (EraseSize == 0) { EraseSize = (UINTN) FwVolHeader->BlockMap->Length; } Status = CommonFlashErase ((UINTN) BaseAddress, EraseSize); ASSERT_EFI_ERROR(Status); if (EFI_ERROR (Status)) { return Status; } DataSize = (UINTN) FwVolHeader->HeaderLength; Status = CommonFlashWrite ( (UINTN) BaseAddress, &DataSize, (VOID *) FwVolHeader); ASSERT_EFI_ERROR(Status); return Status; } /** Get total number of blocks from input firmware volume header. @param[in] FwVolHeader Pointer to EFI_FIRMWARE_VOLUME_HEADER instance. @param[out] NumOfBlocks Output total number of blocks @retval EFI_SUCCESS Get total number of blocks successful. @retval EFI_INVALID_PARAMETER FwVolHeader or NumOfBlocks is NULL. **/ STATIC EFI_STATUS GetNumOfBlocksFromFv ( IN EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader, OUT UINTN *NumOfBlocks ) { UINTN WorkingBlocksNum; EFI_FV_BLOCK_MAP_ENTRY *BlockMapEntry; if (FwVolHeader == NULL || NumOfBlocks == NULL) { return EFI_INVALID_PARAMETER; } WorkingBlocksNum = 0; for (BlockMapEntry = FwVolHeader->BlockMap; BlockMapEntry->NumBlocks != 0; BlockMapEntry++) { WorkingBlocksNum = WorkingBlocksNum + BlockMapEntry->NumBlocks; } *NumOfBlocks = WorkingBlocksNum; return EFI_SUCCESS; } /** According to input base address to initialize FWH instance. @param[in] FwhInstance Pointer to EFI_FW_VOL_INSTANCE instance. @param[in] BaseAddress Firmware volume start address. @retval EFI_SUCCESS Initialize FWH instance successful. @retval EFI_INVALID_PARAMETER FwhInstance is NULL or BaseAddress is 0. **/ STATIC EFI_STATUS InitializeFwhInstance ( IN EFI_FW_VOL_INSTANCE *FwhInstance, IN EFI_PHYSICAL_ADDRESS BaseAddress ) { EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; if (FwhInstance == NULL || BaseAddress == 0) { return EFI_INVALID_PARAMETER; } FwhInstance->FvBase[FVB_PHYSICAL] = (UINTN) BaseAddress; FwhInstance->FvBase[FVB_VIRTUAL] = (UINTN) BaseAddress; FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) (UINTN) BaseAddress; CopyMem (&FwhInstance->VolumeHeader, FwVolHeader, FwVolHeader->HeaderLength); return GetNumOfBlocksFromFv (FwVolHeader, &FwhInstance->NumOfBlocks); } /** According to input base address to initialize EFI_FW_VOL_BLOCK_DEVICE instance. @param[in] FvbDevice Pointer to EFI_FW_VOL_BLOCK_DEVICE instance. @param[in] BaseAddress Firmware volume start address. @retval EFI_SUCCESS Initialize EFI_FW_VOL_BLOCK_DEVICE instance successful. @retval EFI_INVALID_PARAMETER FvbDevice is NULL or BaseAddress is 0. **/ STATIC EFI_STATUS InitializeFvDevice ( IN EFI_FW_VOL_BLOCK_DEVICE *FvbDevice, IN EFI_PHYSICAL_ADDRESS BaseAddress ) { EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; if (FvbDevice == NULL || BaseAddress == 0) { return EFI_INVALID_PARAMETER; } CopyMem (FvbDevice, &mFvbDeviceTemplate, sizeof (EFI_FW_VOL_BLOCK_DEVICE)); FvbDevice->Instance = mFvbModuleGlobal->NumFv; mFvbModuleGlobal->NumFv++; FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *) (UINTN) BaseAddress; if (FwVolHeader->ExtHeaderOffset == 0) { // // FV does not contains extension header, then produce MEMMAP_DEVICE_PATH // FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) AllocateCopyPool (sizeof (FV_MEMMAP_DEVICE_PATH), &mFvMemmapDevicePathTemplate); if (FvbDevice->DevicePath == NULL) { return EFI_OUT_OF_RESOURCES; } ((FV_MEMMAP_DEVICE_PATH *) FvbDevice->DevicePath)->MemMapDevPath.StartingAddress = BaseAddress; ((FV_MEMMAP_DEVICE_PATH *) FvbDevice->DevicePath)->MemMapDevPath.EndingAddress = BaseAddress + FwVolHeader->FvLength - 1; } else { FvbDevice->DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) AllocateCopyPool (sizeof (FV_PIWG_DEVICE_PATH), &mFvPIWGDevicePathTemplate); if (FvbDevice->DevicePath == NULL) { return EFI_OUT_OF_RESOURCES; } CopyGuid ( &((FV_PIWG_DEVICE_PATH *) FvbDevice->DevicePath)->FvDevPath.FvName, (GUID *)(UINTN)(BaseAddress + FwVolHeader->ExtHeaderOffset) ); } return EFI_SUCCESS; } /** This function initializes all of related functions which used in protected mode. @retval TRUE The system has finished executing the EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event. @retval FALSE The system has not finished executing the EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event. **/ STATIC EFI_STATUS InitializeRuntimeFunctions ( VOID ) { EFI_EVENT Event; EFI_STATUS Status; // // Register SetVirtualAddressMap () notify function // Status = gBS->CreateEventEx ( EVT_NOTIFY_SIGNAL, TPL_NOTIFY, FvbVirtualddressChangeEvent, NULL, &gEfiEventVirtualAddressChangeGuid, &Event ); ASSERT_EFI_ERROR (Status); return Status; } /** This function allows the caller to determine if UEFI SetVirtualAddressMap() has been called. This function returns TRUE after all the EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE functions have executed as a result of the OS calling SetVirtualAddressMap(). Prior to this time FALSE is returned. This function is used by runtime code to decide it is legal to access services that go away after SetVirtualAddressMap(). @retval TRUE The system has finished executing the EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event. @retval FALSE The system has not finished executing the EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE event. **/ BOOLEAN GoneVirtual ( VOID ) { return mGoneVirtual; } /** According to input base address to get next FWH instance. @param[in] FwhInstance Pointer to EFI_FW_VOL_INSTANCE instance. @retunr EFI_FW_VOL_INSTANCE * Start address of next FWH instance. @retval NULL Cannot find next FWH intance. **/ EFI_FW_VOL_INSTANCE * GetNextFwhInstance ( IN EFI_FW_VOL_INSTANCE *FwhInstance ) { EFI_FW_VOL_INSTANCE *NextFwhInstance; if (FwhInstance == NULL) { return NULL; } NextFwhInstance = (EFI_FW_VOL_INSTANCE *) ((UINTN) ((UINT8 *) FwhInstance) + FwhInstance->VolumeHeader.HeaderLength + (sizeof (EFI_FW_VOL_INSTANCE) - sizeof (EFI_FIRMWARE_VOLUME_HEADER))); return NextFwhInstance; } /** Routine to enable Access FVB through SMI feature. The mainly action of this routine is follows: 1. Install gFvbAccessThroughSmiGuid to meet dependency to load image to SMM RAM. 2. Create event to wait the completion of SMM image initialization. Note: If enabling access FVB through SMI, all of FVB access in this driver MUST through SMI. User should take attention to disable all of FVB access in protected mode. @retval EFI_SUCCESS Enable FVB acccess through SMI feature successful. @retval EFI_UNSUPPORTED System is in SMM mode or at runtime. **/ EFI_STATUS EnableFvbAccessThroughSmi ( VOID ) { EFI_HANDLE Handle; EFI_STATUS Status; VOID *Registration; EFI_EVENT Event; if (mSmst != NULL || GoneVirtual ()) { return EFI_UNSUPPORTED; } Handle = NULL; Status = gBS->InstallMultipleProtocolInterfaces ( &Handle, &gFvbAccessThroughSmiGuid, NULL, NULL ); ASSERT_EFI_ERROR (Status); Event = InternalCreateProtocolNotifyEvent ( &gFvbAccessThroughSmiGuid, TPL_NOTIFY, EnableFvbAccessThroughSmiEvent, NULL, &Registration ); ASSERT (Event != NULL); return Status; } /** Initialize all of FVB services and provide FVB services relative interfaces. Any error occurred in this function will be asserted. @retval EFI_SUCCESS Initialize FVbServices successful. **/ EFI_STATUS InitializeFVbServices ( VOID ) { EFI_STATUS Status; EFI_FW_VOL_INSTANCE *FwhInstance; UINTN BufferSize; EFI_HANDLE FwbHandle; EFI_FW_VOL_BLOCK_DEVICE *FvbDevice; EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *OldFwbInterface; EFI_PHYSICAL_ADDRESS BaseAddress; if (mFvbModuleGlobal != NULL) { return EFI_SUCCESS; } // // Allocate runtime services data for global variable, which contains // the private data of all firmware volume block instances // mFvbModuleGlobal = FvbAllocateZeroBuffer (sizeof (ESAL_FWB_GLOBAL), TRUE); ASSERT (mFvbModuleGlobal != NULL); if (mFvbModuleGlobal == NULL) { return EFI_OUT_OF_RESOURCES; } // // Calculate the total size for all firmware volume block instances // BufferSize = 0; Status = ClaculateFvInstancesBufferSize (&BufferSize); ASSERT_EFI_ERROR(Status); if (EFI_ERROR (Status)) { return Status; } // // Only need to allocate once. There is only one copy of physical memory for // the private data of each FV instance. But in virtual mode or in physical // mode, the address of the the physical memory may be different. // FwhInstance = FvbAllocateZeroBuffer (BufferSize, TRUE); ASSERT (FwhInstance != NULL); if (FwhInstance == NULL) { return EFI_OUT_OF_RESOURCES; } // // Make a virtual copy of the FvInstance pointer. // mFvbModuleGlobal->FvInstance[FVB_PHYSICAL] = FwhInstance; mFvbModuleGlobal->FvInstance[FVB_VIRTUAL] = FwhInstance; mFvbModuleGlobal->NumFv = 0; // // Restore correct firmware volume header if the contents is incorrect. // BaseAddress = GetNvStoreBaseAddress (); Status = ValidateFvHeader ((EFI_FIRMWARE_VOLUME_HEADER *) (UINTN) BaseAddress); if (EFI_ERROR (Status)) { Status = RestoreCorrectFvHeader (BaseAddress); if (EFI_ERROR (Status)) { return Status; } gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL); } Status = InitializeFwhInstance (FwhInstance, BaseAddress); ASSERT_EFI_ERROR(Status); // // Add a FVB Protocol Instance and initialize this instance. // FvbDevice = FvbAllocateZeroBuffer (sizeof (EFI_FW_VOL_BLOCK_DEVICE), TRUE); ASSERT (FvbDevice != NULL); if (FvbDevice == NULL) { return EFI_OUT_OF_RESOURCES; } Status = InitializeFvDevice (FvbDevice, BaseAddress); ASSERT_EFI_ERROR(Status); if (EFI_ERROR (Status)) { return Status; } // // Find a handle with a matching device path that has supports FW Block protocol // FwbHandle = NULL; Status = gBS->LocateDevicePath (&gEfiFirmwareVolumeBlockProtocolGuid, &FvbDevice->DevicePath, &FwbHandle); if (EFI_ERROR (Status)) { // // LocateDevicePath fails so install a new interface and device path // FwbHandle = NULL; Status = gBS->InstallMultipleProtocolInterfaces ( &FwbHandle, &gEfiFirmwareVolumeBlockProtocolGuid, &FvbDevice->FwVolBlockInstance, &gEfiDevicePathProtocolGuid, FvbDevice->DevicePath, NULL ); ASSERT_EFI_ERROR (Status); } else if (IsDevicePathEnd (FvbDevice->DevicePath)) { // // Device allready exists, so reinstall the FVB protocol // Status = gBS->HandleProtocol ( FwbHandle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID**)&OldFwbInterface ); ASSERT_EFI_ERROR (Status); Status = gBS->ReinstallProtocolInterface ( FwbHandle, &gEfiFirmwareVolumeBlockProtocolGuid, OldFwbInterface, &FvbDevice->FwVolBlockInstance ); ASSERT_EFI_ERROR (Status); } else { // // There was a FVB protocol on an End Device Path node // ASSERT (FALSE); } Status = InitializeRuntimeFunctions (); ASSERT_EFI_ERROR (Status); // // Install Alternate FVB on the same handle // Status = gBS->InstallMultipleProtocolInterfaces ( &FwbHandle, &gEfiAlternateFvBlockGuid, NULL, NULL ); ASSERT_EFI_ERROR (Status); return Status; }