/**@file | |
Functions related to the Firmware Volume Block service whose | |
implementation is specific to the runtime DXE driver build. | |
Copyright (C) 2015, Red Hat, Inc. | |
Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <Guid/EventGroup.h> | |
#include <Library/DebugLib.h> | |
#include <Library/DevicePathLib.h> | |
#include <Library/DxeServicesTableLib.h> | |
#include <Library/MemEncryptSevLib.h> | |
#include <Library/PcdLib.h> | |
#include <Library/UefiBootServicesTableLib.h> | |
#include <Library/UefiRuntimeLib.h> | |
#include <Protocol/DevicePath.h> | |
#include <Protocol/FirmwareVolumeBlock.h> | |
#include "FwBlockService.h" | |
#include "QemuFlash.h" | |
VOID | |
InstallProtocolInterfaces ( | |
IN EFI_FW_VOL_BLOCK_DEVICE *FvbDevice | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_HANDLE FwbHandle; | |
EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *OldFwbInterface; | |
ASSERT (!FeaturePcdGet (PcdSmmSmramRequire)); | |
// | |
// Find a handle with a matching device path that has supports FW Block | |
// protocol | |
// | |
Status = gBS->LocateDevicePath ( | |
&gEfiFirmwareVolumeBlockProtocolGuid, | |
&FvbDevice->DevicePath, | |
&FwbHandle | |
); | |
if (EFI_ERROR (Status)) { | |
// | |
// LocateDevicePath fails so install a new interface and device path | |
// | |
FwbHandle = NULL; | |
DEBUG ((DEBUG_INFO, "Installing QEMU flash FVB\n")); | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&FwbHandle, | |
&gEfiFirmwareVolumeBlockProtocolGuid, | |
&FvbDevice->FwVolBlockInstance, | |
&gEfiDevicePathProtocolGuid, | |
FvbDevice->DevicePath, | |
NULL | |
); | |
ASSERT_EFI_ERROR (Status); | |
} else if (IsDevicePathEnd (FvbDevice->DevicePath)) { | |
// | |
// Device already exists, so reinstall the FVB protocol | |
// | |
Status = gBS->HandleProtocol ( | |
FwbHandle, | |
&gEfiFirmwareVolumeBlockProtocolGuid, | |
(VOID **)&OldFwbInterface | |
); | |
ASSERT_EFI_ERROR (Status); | |
DEBUG ((DEBUG_INFO, "Reinstalling FVB for QEMU flash region\n")); | |
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); | |
} | |
} | |
STATIC | |
VOID | |
EFIAPI | |
FvbVirtualAddressChangeEvent ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
/*++ | |
Routine Description: | |
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. | |
Arguments: | |
(Standard EFI notify event - EFI_EVENT_NOTIFY) | |
Returns: | |
None | |
--*/ | |
{ | |
EFI_FW_VOL_INSTANCE *FwhInstance; | |
UINTN Index; | |
FwhInstance = mFvbModuleGlobal->FvInstance; | |
EfiConvertPointer (0x0, (VOID **)&mFvbModuleGlobal->FvInstance); | |
// | |
// Convert the base address of all the instances | |
// | |
Index = 0; | |
while (Index < mFvbModuleGlobal->NumFv) { | |
EfiConvertPointer (0x0, (VOID **)&FwhInstance->FvBase); | |
FwhInstance = (EFI_FW_VOL_INSTANCE *) | |
( | |
(UINTN)((UINT8 *)FwhInstance) + | |
FwhInstance->VolumeHeader.HeaderLength + | |
(sizeof (EFI_FW_VOL_INSTANCE) - sizeof (EFI_FIRMWARE_VOLUME_HEADER)) | |
); | |
Index++; | |
} | |
EfiConvertPointer (0x0, (VOID **)&mFvbModuleGlobal); | |
QemuFlashConvertPointers (); | |
} | |
VOID | |
InstallVirtualAddressChangeHandler ( | |
VOID | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_EVENT VirtualAddressChangeEvent; | |
Status = gBS->CreateEventEx ( | |
EVT_NOTIFY_SIGNAL, | |
TPL_NOTIFY, | |
FvbVirtualAddressChangeEvent, | |
NULL, | |
&gEfiEventVirtualAddressChangeGuid, | |
&VirtualAddressChangeEvent | |
); | |
ASSERT_EFI_ERROR (Status); | |
} | |
EFI_STATUS | |
MarkIoMemoryRangeForRuntimeAccess ( | |
IN EFI_PHYSICAL_ADDRESS BaseAddress, | |
IN UINTN Length | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_GCD_MEMORY_SPACE_DESCRIPTOR GcdDescriptor; | |
// | |
// Mark flash region as runtime memory | |
// | |
Status = gDS->RemoveMemorySpace ( | |
BaseAddress, | |
Length | |
); | |
Status = gDS->AddMemorySpace ( | |
EfiGcdMemoryTypeMemoryMappedIo, | |
BaseAddress, | |
Length, | |
EFI_MEMORY_UC | EFI_MEMORY_RUNTIME | |
); | |
ASSERT_EFI_ERROR (Status); | |
Status = gDS->AllocateMemorySpace ( | |
EfiGcdAllocateAddress, | |
EfiGcdMemoryTypeMemoryMappedIo, | |
0, | |
Length, | |
&BaseAddress, | |
gImageHandle, | |
NULL | |
); | |
ASSERT_EFI_ERROR (Status); | |
Status = gDS->GetMemorySpaceDescriptor (BaseAddress, &GcdDescriptor); | |
ASSERT_EFI_ERROR (Status); | |
Status = gDS->SetMemorySpaceAttributes ( | |
BaseAddress, | |
Length, | |
GcdDescriptor.Attributes | EFI_MEMORY_RUNTIME | |
); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// When SEV is active, AmdSevDxe mapped the BaseAddress with C=0 but | |
// SetMemorySpaceAttributes() remaps the range with C=1. Let's restore | |
// the mapping so that both guest and hyervisor can access the flash | |
// memory range. | |
// | |
if (MemEncryptSevIsEnabled ()) { | |
Status = MemEncryptSevClearMmioPageEncMask ( | |
0, | |
BaseAddress, | |
EFI_SIZE_TO_PAGES (Length) | |
); | |
ASSERT_EFI_ERROR (Status); | |
} | |
return Status; | |
} | |
VOID | |
SetPcdFlashNvStorageBaseAddresses ( | |
VOID | |
) | |
{ | |
RETURN_STATUS PcdStatus; | |
// | |
// Set several PCD values to point to flash | |
// | |
PcdStatus = PcdSet64S ( | |
PcdFlashNvStorageVariableBase64, | |
(UINTN)PcdGet32 (PcdOvmfFlashNvStorageVariableBase) | |
); | |
ASSERT_RETURN_ERROR (PcdStatus); | |
PcdStatus = PcdSet32S ( | |
PcdFlashNvStorageFtwWorkingBase, | |
PcdGet32 (PcdOvmfFlashNvStorageFtwWorkingBase) | |
); | |
ASSERT_RETURN_ERROR (PcdStatus); | |
PcdStatus = PcdSet32S ( | |
PcdFlashNvStorageFtwSpareBase, | |
PcdGet32 (PcdOvmfFlashNvStorageFtwSpareBase) | |
); | |
ASSERT_RETURN_ERROR (PcdStatus); | |
} |