X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=OvmfPkg%2FLibrary%2FQemuFwCfgLib%2FQemuFwCfgDxe.c;h=f5787cd24d380a5776539dc37fd60eaa027eb307;hb=ac0a286f4d747a4c6c603a7b225917293cbe1e9f;hp=ac05f4c347f38986ad474373dfa19bef3512b0ca;hpb=fee47a261c65235e263aa128d28e7da8f8f9f100;p=mirror_edk2.git diff --git a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c index ac05f4c347..f5787cd24d 100644 --- a/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c +++ b/OvmfPkg/Library/QemuFwCfgLib/QemuFwCfgDxe.c @@ -4,24 +4,29 @@ Copyright (C) 2013, Red Hat, Inc. Copyright (c) 2011 - 2013, Intel Corporation. All rights reserved.
+ Copyright (c) 2017, Advanced Micro Devices. All rights reserved.
- 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. + SPDX-License-Identifier: BSD-2-Clause-Patent **/ +#include + +#include + +#include +#include +#include #include #include +#include +#include #include "QemuFwCfgLibInternal.h" -STATIC BOOLEAN mQemuFwCfgSupported = FALSE; -STATIC BOOLEAN mQemuFwCfgDmaSupported; +STATIC BOOLEAN mQemuFwCfgSupported = FALSE; +STATIC BOOLEAN mQemuFwCfgDmaSupported; +STATIC EDKII_IOMMU_PROTOCOL *mIoMmuProtocol; /** Returns a boolean indicating if the firmware configuration interface @@ -42,33 +47,33 @@ QemuFwCfgIsAvailable ( return InternalQemuFwCfgIsAvailable (); } - RETURN_STATUS EFIAPI QemuFwCfgInitialize ( VOID ) { - UINT32 Signature; - UINT32 Revision; + UINT32 Signature; + UINT32 Revision; // // Enable the access routines while probing to see if it is supported. // For probing we always use the IO Port (IoReadFifo8()) access method. // - mQemuFwCfgSupported = TRUE; + mQemuFwCfgSupported = TRUE; mQemuFwCfgDmaSupported = FALSE; QemuFwCfgSelectItem (QemuFwCfgItemSignature); Signature = QemuFwCfgRead32 (); - DEBUG ((EFI_D_INFO, "FW CFG Signature: 0x%x\n", Signature)); + DEBUG ((DEBUG_INFO, "FW CFG Signature: 0x%x\n", Signature)); QemuFwCfgSelectItem (QemuFwCfgItemInterfaceVersion); Revision = QemuFwCfgRead32 (); - DEBUG ((EFI_D_INFO, "FW CFG Revision: 0x%x\n", Revision)); + DEBUG ((DEBUG_INFO, "FW CFG Revision: 0x%x\n", Revision)); if ((Signature != SIGNATURE_32 ('Q', 'E', 'M', 'U')) || (Revision < 1) - ) { - DEBUG ((EFI_D_INFO, "QemuFwCfg interface not supported.\n")); + ) + { + DEBUG ((DEBUG_INFO, "QemuFwCfg interface not supported.\n")); mQemuFwCfgSupported = FALSE; return RETURN_SUCCESS; } @@ -79,10 +84,34 @@ QemuFwCfgInitialize ( mQemuFwCfgDmaSupported = TRUE; DEBUG ((DEBUG_INFO, "QemuFwCfg interface (DMA) is supported.\n")); } + + if (mQemuFwCfgDmaSupported && MemEncryptSevIsEnabled ()) { + EFI_STATUS Status; + + // + // IoMmuDxe driver must have installed the IOMMU protocol. If we are not + // able to locate the protocol then something must have gone wrong. + // + Status = gBS->LocateProtocol ( + &gEdkiiIoMmuProtocolGuid, + NULL, + (VOID **)&mIoMmuProtocol + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "QemuFwCfgSevDma %a:%a Failed to locate IOMMU protocol.\n", + gEfiCallerBaseName, + __FUNCTION__ + )); + ASSERT (FALSE); + CpuDeadLoop (); + } + } + return RETURN_SUCCESS; } - /** Returns a boolean indicating if the firmware configuration interface is available for library-internal purposes. @@ -114,3 +143,351 @@ InternalQemuFwCfgDmaIsAvailable ( { return mQemuFwCfgDmaSupported; } + +/** + Function is used for allocating a bi-directional FW_CFG_DMA_ACCESS used + between Host and device to exchange the information. The buffer must be free'd + using FreeFwCfgDmaAccessBuffer (). + +**/ +STATIC +VOID +AllocFwCfgDmaAccessBuffer ( + OUT VOID **Access, + OUT VOID **MapInfo + ) +{ + UINTN Size; + UINTN NumPages; + EFI_STATUS Status; + VOID *HostAddress; + EFI_PHYSICAL_ADDRESS DmaAddress; + VOID *Mapping; + + Size = sizeof (FW_CFG_DMA_ACCESS); + NumPages = EFI_SIZE_TO_PAGES (Size); + + // + // As per UEFI spec, in order to map a host address with + // BusMasterCommonBuffer64, the buffer must be allocated using the IOMMU + // AllocateBuffer() + // + Status = mIoMmuProtocol->AllocateBuffer ( + mIoMmuProtocol, + AllocateAnyPages, + EfiBootServicesData, + NumPages, + &HostAddress, + EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "%a:%a failed to allocate FW_CFG_DMA_ACCESS\n", + gEfiCallerBaseName, + __FUNCTION__ + )); + ASSERT (FALSE); + CpuDeadLoop (); + } + + // + // Avoid exposing stale data even temporarily: zero the area before mapping + // it. + // + ZeroMem (HostAddress, Size); + + // + // Map the host buffer with BusMasterCommonBuffer64 + // + Status = mIoMmuProtocol->Map ( + mIoMmuProtocol, + EdkiiIoMmuOperationBusMasterCommonBuffer64, + HostAddress, + &Size, + &DmaAddress, + &Mapping + ); + if (EFI_ERROR (Status)) { + mIoMmuProtocol->FreeBuffer (mIoMmuProtocol, NumPages, HostAddress); + DEBUG (( + DEBUG_ERROR, + "%a:%a failed to Map() FW_CFG_DMA_ACCESS\n", + gEfiCallerBaseName, + __FUNCTION__ + )); + ASSERT (FALSE); + CpuDeadLoop (); + } + + if (Size < sizeof (FW_CFG_DMA_ACCESS)) { + mIoMmuProtocol->Unmap (mIoMmuProtocol, Mapping); + mIoMmuProtocol->FreeBuffer (mIoMmuProtocol, NumPages, HostAddress); + DEBUG (( + DEBUG_ERROR, + "%a:%a failed to Map() - requested 0x%Lx got 0x%Lx\n", + gEfiCallerBaseName, + __FUNCTION__, + (UINT64)sizeof (FW_CFG_DMA_ACCESS), + (UINT64)Size + )); + ASSERT (FALSE); + CpuDeadLoop (); + } + + *Access = HostAddress; + *MapInfo = Mapping; +} + +/** + Function is to used for freeing the Access buffer allocated using + AllocFwCfgDmaAccessBuffer() + +**/ +STATIC +VOID +FreeFwCfgDmaAccessBuffer ( + IN VOID *Access, + IN VOID *Mapping + ) +{ + UINTN NumPages; + EFI_STATUS Status; + + NumPages = EFI_SIZE_TO_PAGES (sizeof (FW_CFG_DMA_ACCESS)); + + Status = mIoMmuProtocol->Unmap (mIoMmuProtocol, Mapping); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "%a:%a failed to UnMap() Mapping 0x%Lx\n", + gEfiCallerBaseName, + __FUNCTION__, + (UINT64)(UINTN)Mapping + )); + ASSERT (FALSE); + CpuDeadLoop (); + } + + Status = mIoMmuProtocol->FreeBuffer (mIoMmuProtocol, NumPages, Access); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "%a:%a failed to Free() 0x%Lx\n", + gEfiCallerBaseName, + __FUNCTION__, + (UINT64)(UINTN)Access + )); + ASSERT (FALSE); + CpuDeadLoop (); + } +} + +/** + Function is used for mapping host address to device address. The buffer must + be unmapped with UnmapDmaDataBuffer (). + +**/ +STATIC +VOID +MapFwCfgDmaDataBuffer ( + IN BOOLEAN IsWrite, + IN VOID *HostAddress, + IN UINT32 Size, + OUT EFI_PHYSICAL_ADDRESS *DeviceAddress, + OUT VOID **MapInfo + ) +{ + EFI_STATUS Status; + UINTN NumberOfBytes; + VOID *Mapping; + EFI_PHYSICAL_ADDRESS PhysicalAddress; + + NumberOfBytes = Size; + Status = mIoMmuProtocol->Map ( + mIoMmuProtocol, + (IsWrite ? + EdkiiIoMmuOperationBusMasterRead64 : + EdkiiIoMmuOperationBusMasterWrite64), + HostAddress, + &NumberOfBytes, + &PhysicalAddress, + &Mapping + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "%a:%a failed to Map() Address 0x%Lx Size 0x%Lx\n", + gEfiCallerBaseName, + __FUNCTION__, + (UINT64)(UINTN)HostAddress, + (UINT64)Size + )); + ASSERT (FALSE); + CpuDeadLoop (); + } + + if (NumberOfBytes < Size) { + mIoMmuProtocol->Unmap (mIoMmuProtocol, Mapping); + DEBUG (( + DEBUG_ERROR, + "%a:%a failed to Map() - requested 0x%x got 0x%Lx\n", + gEfiCallerBaseName, + __FUNCTION__, + Size, + (UINT64)NumberOfBytes + )); + ASSERT (FALSE); + CpuDeadLoop (); + } + + *DeviceAddress = PhysicalAddress; + *MapInfo = Mapping; +} + +STATIC +VOID +UnmapFwCfgDmaDataBuffer ( + IN VOID *Mapping + ) +{ + EFI_STATUS Status; + + Status = mIoMmuProtocol->Unmap (mIoMmuProtocol, Mapping); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "%a:%a failed to UnMap() Mapping 0x%Lx\n", + gEfiCallerBaseName, + __FUNCTION__, + (UINT64)(UINTN)Mapping + )); + ASSERT (FALSE); + CpuDeadLoop (); + } +} + +/** + Transfer an array of bytes, or skip a number of bytes, using the DMA + interface. + + @param[in] Size Size in bytes to transfer or skip. + + @param[in,out] Buffer Buffer to read data into or write data from. Ignored, + and may be NULL, if Size is zero, or Control is + FW_CFG_DMA_CTL_SKIP. + + @param[in] Control One of the following: + FW_CFG_DMA_CTL_WRITE - write to fw_cfg from Buffer. + FW_CFG_DMA_CTL_READ - read from fw_cfg into Buffer. + FW_CFG_DMA_CTL_SKIP - skip bytes in fw_cfg. +**/ +VOID +InternalQemuFwCfgDmaBytes ( + IN UINT32 Size, + IN OUT VOID *Buffer OPTIONAL, + IN UINT32 Control + ) +{ + volatile FW_CFG_DMA_ACCESS LocalAccess; + volatile FW_CFG_DMA_ACCESS *Access; + UINT32 AccessHigh, AccessLow; + UINT32 Status; + VOID *AccessMapping, *DataMapping; + VOID *DataBuffer; + + ASSERT ( + Control == FW_CFG_DMA_CTL_WRITE || Control == FW_CFG_DMA_CTL_READ || + Control == FW_CFG_DMA_CTL_SKIP + ); + + if (Size == 0) { + return; + } + + Access = &LocalAccess; + AccessMapping = NULL; + DataMapping = NULL; + DataBuffer = Buffer; + + // + // When SEV is enabled, map Buffer to DMA address before issuing the DMA + // request + // + if (MemEncryptSevIsEnabled ()) { + VOID *AccessBuffer; + EFI_PHYSICAL_ADDRESS DataBufferAddress; + + // + // Allocate DMA Access buffer + // + AllocFwCfgDmaAccessBuffer (&AccessBuffer, &AccessMapping); + + Access = AccessBuffer; + + // + // Map actual data buffer + // + if (Control != FW_CFG_DMA_CTL_SKIP) { + MapFwCfgDmaDataBuffer ( + Control == FW_CFG_DMA_CTL_WRITE, + Buffer, + Size, + &DataBufferAddress, + &DataMapping + ); + + DataBuffer = (VOID *)(UINTN)DataBufferAddress; + } + } + + Access->Control = SwapBytes32 (Control); + Access->Length = SwapBytes32 (Size); + Access->Address = SwapBytes64 ((UINTN)DataBuffer); + + // + // Delimit the transfer from (a) modifications to Access, (b) in case of a + // write, from writes to Buffer by the caller. + // + MemoryFence (); + + // + // Start the transfer. + // + AccessHigh = (UINT32)RShiftU64 ((UINTN)Access, 32); + AccessLow = (UINT32)(UINTN)Access; + IoWrite32 (FW_CFG_IO_DMA_ADDRESS, SwapBytes32 (AccessHigh)); + IoWrite32 (FW_CFG_IO_DMA_ADDRESS + 4, SwapBytes32 (AccessLow)); + + // + // Don't look at Access.Control before starting the transfer. + // + MemoryFence (); + + // + // Wait for the transfer to complete. + // + do { + Status = SwapBytes32 (Access->Control); + ASSERT ((Status & FW_CFG_DMA_CTL_ERROR) == 0); + } while (Status != 0); + + // + // After a read, the caller will want to use Buffer. + // + MemoryFence (); + + // + // If Access buffer was dynamically allocated then free it. + // + if (AccessMapping != NULL) { + FreeFwCfgDmaAccessBuffer ((VOID *)Access, AccessMapping); + } + + // + // If DataBuffer was mapped then unmap it. + // + if (DataMapping != NULL) { + UnmapFwCfgDmaDataBuffer (DataMapping); + } +}