When SEV is enabled, the MMIO memory range must be mapped as unencrypted
(i.e C-bit cleared) and DMA must be performed on unencrypted memory.

The patch adds a DXE driver that runs early in boot and clears the memory
encryption attribute from MMIO/NonExistent memory ranges and installs a
IOMMU protocol to provide the DMA support for PCIHostBridge and other drivers.

The driver produces IOMMU protocol introduce by Jiewen
https://lists.01.org/pipermail/edk2-devel/2017-May/010462.html


Cc: Jordan Justen <jordan.l.jus...@intel.com>
Cc: Laszlo Ersek <ler...@redhat.com>
Cc: Leo Duran <leo.du...@amd.com>
Cc: Jiewen Yao <jiewen....@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Brijesh Singh <brijesh.si...@amd.com>
---
 OvmfPkg/OvmfPkgIa32X64.dsc      |   1 +
 OvmfPkg/OvmfPkgX64.dsc          |   1 +
 OvmfPkg/OvmfPkgIa32X64.fdf      |   2 +
 OvmfPkg/OvmfPkgX64.fdf          |   2 +
 OvmfPkg/AmdSevDxe/AmdSevDxe.inf |  49 +++
 OvmfPkg/AmdSevDxe/AmdSevIommu.h |  43 ++
 OvmfPkg/AmdSevDxe/AmdSevMmio.h  |  41 ++
 OvmfPkg/AmdSevDxe/AmdSevDxe.c   |  52 +++
 OvmfPkg/AmdSevDxe/AmdSevIommu.c | 459 ++++++++++++++++++++
 OvmfPkg/AmdSevDxe/AmdSevMmio.c  |  50 +++
 10 files changed, 700 insertions(+)

diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
index 9403f76ce862..ee6f98d68b73 100644
--- a/OvmfPkg/OvmfPkgIa32X64.dsc
+++ b/OvmfPkg/OvmfPkgIa32X64.dsc
@@ -827,6 +827,7 @@ [Components.X64]
 !endif
 
   OvmfPkg/PlatformDxe/Platform.inf
+  OvmfPkg/AmdSevDxe/AmdSevDxe.inf
 
 !if $(SMM_REQUIRE) == TRUE
   OvmfPkg/SmmAccess/SmmAccess2Dxe.inf
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index e137143f7afa..b5f26e06e60b 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -825,6 +825,7 @@ [Components]
 !endif
 
   OvmfPkg/PlatformDxe/Platform.inf
+  OvmfPkg/AmdSevDxe/AmdSevDxe.inf
 
 !if $(SMM_REQUIRE) == TRUE
   OvmfPkg/SmmAccess/SmmAccess2Dxe.inf
diff --git a/OvmfPkg/OvmfPkgIa32X64.fdf b/OvmfPkg/OvmfPkgIa32X64.fdf
index 5233314139bc..12871860d001 100644
--- a/OvmfPkg/OvmfPkgIa32X64.fdf
+++ b/OvmfPkg/OvmfPkgIa32X64.fdf
@@ -190,6 +190,7 @@ [FV.DXEFV]
 APRIORI DXE {
   INF  MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf
   INF  MdeModulePkg/Universal/PCD/Dxe/Pcd.inf
+  INF  OvmfPkg/AmdSevDxe/AmdSevDxe.inf
 !if $(SMM_REQUIRE) == FALSE
   INF  OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
 !endif
@@ -351,6 +352,7 @@ [FV.DXEFV]
 INF  OvmfPkg/QemuVideoDxe/QemuVideoDxe.inf
 INF  OvmfPkg/VirtioGpuDxe/VirtioGpu.inf
 INF  OvmfPkg/PlatformDxe/Platform.inf
+INF  OvmfPkg/AmdSevDxe/AmdSevDxe.inf
 
 !if $(SMM_REQUIRE) == TRUE
 INF  OvmfPkg/SmmAccess/SmmAccess2Dxe.inf
diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
index 36150101e784..ae6e66a1c08d 100644
--- a/OvmfPkg/OvmfPkgX64.fdf
+++ b/OvmfPkg/OvmfPkgX64.fdf
@@ -190,6 +190,7 @@ [FV.DXEFV]
 APRIORI DXE {
   INF  MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf
   INF  MdeModulePkg/Universal/PCD/Dxe/Pcd.inf
+  INF  OvmfPkg/AmdSevDxe/AmdSevDxe.inf
 !if $(SMM_REQUIRE) == FALSE
   INF  OvmfPkg/QemuFlashFvbServicesRuntimeDxe/FvbServicesRuntimeDxe.inf
 !endif
@@ -351,6 +352,7 @@ [FV.DXEFV]
 INF  OvmfPkg/QemuVideoDxe/QemuVideoDxe.inf
 INF  OvmfPkg/VirtioGpuDxe/VirtioGpu.inf
 INF  OvmfPkg/PlatformDxe/Platform.inf
+INF  OvmfPkg/AmdSevDxe/AmdSevDxe.inf
 
 !if $(SMM_REQUIRE) == TRUE
 INF  OvmfPkg/SmmAccess/SmmAccess2Dxe.inf
diff --git a/OvmfPkg/AmdSevDxe/AmdSevDxe.inf b/OvmfPkg/AmdSevDxe/AmdSevDxe.inf
new file mode 100644
index 000000000000..775dda9be386
--- /dev/null
+++ b/OvmfPkg/AmdSevDxe/AmdSevDxe.inf
@@ -0,0 +1,49 @@
+#/** @file
+#
+#  Driver clears the encryption attribute from MMIO regions and installs IOMMU
+#  protcol to provides DMA support for PciHostBridge and others
+#
+#  Copyright (c) 2017, AMD Inc. All rights reserved.<BR>
+#
+#  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.
+#
+#**/
+
+[Defines]
+  INF_VERSION                    = 1.25
+  BASE_NAME                      = AmdSevDxe
+  FILE_GUID                      = 2ec9da37-ee35-4de9-86c5-6d9a81dc38a7
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = AmdSevDxeEntryPoint
+
+[Sources]
+  AmdSevDxe.c
+  AmdSevIommu.c
+  AmdSevMmio.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  UefiLib
+  UefiDriverEntryPoint
+  UefiBootServicesTableLib
+  DxeServicesTableLib
+  DebugLib
+  MemEncryptSevLib
+
+[Protocols]
+  gEdkiiIoMmuProtocolGuid                     ## PRODUCES
+
+[Depex]
+  TRUE
diff --git a/OvmfPkg/AmdSevDxe/AmdSevIommu.h b/OvmfPkg/AmdSevDxe/AmdSevIommu.h
new file mode 100644
index 000000000000..5712cb57052d
--- /dev/null
+++ b/OvmfPkg/AmdSevDxe/AmdSevIommu.h
@@ -0,0 +1,43 @@
+/** @file
+
+  The protocol provides support to allocate, free, map and umap a DMA buffer 
for
+  bus master (e.g PciHostBridge). When SEV is enabled, the DMA operations must
+  be performed on unencrypted buffer hence protocol clear the encryption bit
+  from the DMA buffer.
+
+  Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
+  Copyright (c) 2017, AMD Inc. All rights reserved.<BR>
+  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.
+
+**/
+
+#ifndef __AMDSEVIOMMU_H_
+#define __AMDSEVIOMMU_H
+
+#include <Protocol/IoMmu.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemEncryptSevLib.h>
+
+/**
+  Install IOMMU protocol to provide the DMA support for PciHostBridge and
+  MemEncryptSevLib.
+
+**/
+VOID
+EFIAPI
+AmdSevInstallIommuProtocol (
+  VOID
+  );
+
+#endif
diff --git a/OvmfPkg/AmdSevDxe/AmdSevMmio.h b/OvmfPkg/AmdSevDxe/AmdSevMmio.h
new file mode 100644
index 000000000000..c6191025d921
--- /dev/null
+++ b/OvmfPkg/AmdSevDxe/AmdSevMmio.h
@@ -0,0 +1,41 @@
+/** @file
+
+  Implements routines to clear C-bit from MMIO Memory Range
+
+  Copyright (c) 2017, AMD Inc. All rights reserved.<BR>
+
+  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.
+
+**/
+
+#ifndef __AMDSEVMMIO_H_
+#define __AMDSEVMMIO_H
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/MemEncryptSevLib.h>
+
+/**
+
+  Iterate through the GCD map and clear the C-bit from MMIO and NonExistent
+  memory space. The NonExistent memory space will be used for mapping the MMIO
+  space added later (eg PciRootBridge). By clearing both known NonExistent
+  memory space can gurantee that any MMIO mapped later will have C-bit cleared.
+*/
+VOID
+EFIAPI
+AmdSevClearEncMaskMmioRange (
+  VOID
+  );
+
+#endif
diff --git a/OvmfPkg/AmdSevDxe/AmdSevDxe.c b/OvmfPkg/AmdSevDxe/AmdSevDxe.c
new file mode 100644
index 000000000000..e22e7ef7314f
--- /dev/null
+++ b/OvmfPkg/AmdSevDxe/AmdSevDxe.c
@@ -0,0 +1,52 @@
+/** @file
+
+  AMD Sev Dxe driver. The driver runs early in DXE phase and clears C-bit from
+  MMIO space and installs EDKII_IOMMU_PROTOCOL to provide the support for DMA
+  operations when SEV is enabled.
+
+  Copyright (c) 2017, AMD Inc. All rights reserved.<BR>
+
+  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 <PiDxe.h>
+
+#include <Library/MemEncryptSevLib.h>
+
+#include "AmdSevMmio.h"
+#include "AmdSevIommu.h"
+
+EFI_STATUS
+EFIAPI
+AmdSevDxeEntryPoint (
+  IN EFI_HANDLE         ImageHandle,
+  IN EFI_SYSTEM_TABLE   *SystemTable
+  )
+{
+  //
+  // Do nothing when SEV is not enabled
+  //
+  if (!MemEncryptSevIsEnabled ()) {
+    return EFI_SUCCESS;
+  }
+
+  //
+  // Clear C-bit from MMIO Memory Range
+  //
+  AmdSevClearEncMaskMmioRange ();
+
+  //
+  // Install IOMMU protocol to provide DMA support for PCIHostBridgeIo and
+  // AmdSevMemEncryptLib.
+  //
+  AmdSevInstallIommuProtocol ();
+
+  return EFI_SUCCESS;
+}
diff --git a/OvmfPkg/AmdSevDxe/AmdSevIommu.c b/OvmfPkg/AmdSevDxe/AmdSevIommu.c
new file mode 100644
index 000000000000..9b35469ca34f
--- /dev/null
+++ b/OvmfPkg/AmdSevDxe/AmdSevIommu.c
@@ -0,0 +1,459 @@
+/** @file
+  AmdSevIommu related function
+
+  The protocol provides support to allocate, free, map and umap a DMA buffer 
for
+  bus master (e.g PciHostBridge). When SEV is enabled, the DMA operations must
+  be performed on unencrypted buffer hence we use a bounce buffer to map the 
host
+  buffer into an unencrypted buffer.
+
+  Copyright (c) 2017, AMD Inc. All rights reserved.<BR>
+
+  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 "AmdSevIommu.h"
+
+typedef struct {
+  EDKII_IOMMU_OPERATION                     Operation;
+  UINTN                                     NumberOfBytes;
+  UINTN                                     NumberOfPages;
+  EFI_PHYSICAL_ADDRESS                      HostAddress;
+  EFI_PHYSICAL_ADDRESS                      DeviceAddress;
+} MAP_INFO;
+
+#define NO_MAPPING             (VOID *) (UINTN) -1
+
+/**
+  Provides the controller-specific addresses required to access system memory 
from a
+  DMA bus master. On SEV guest, the DMA operations must be performed on shared
+  buffer hence we allocate a bounce buffer to map the HostAddress to a 
DeviceAddress.
+  The Encryption attribute is removed from the DeviceAddress buffer.
+
+  @param  This                  The protocol instance pointer.
+  @param  Operation             Indicates if the bus master is going to read or
+                                write to system memory.
+  @param  HostAddress           The system memory address to map to the PCI 
controller.
+  @param  NumberOfBytes         On input the number of bytes to map. On output
+                                the number of bytes
+                                that were mapped.
+  @param  DeviceAddress         The resulting map address for the bus master 
PCI
+                                controller to use to
+                                access the hosts HostAddress.
+  @param  Mapping               A resulting value to pass to Unmap().
+
+  @retval EFI_SUCCESS           The range was mapped for the returned 
NumberOfBytes.
+  @retval EFI_UNSUPPORTED       The HostAddress cannot be mapped as a common 
buffer.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a 
lack
+                                of resources.
+  @retval EFI_DEVICE_ERROR      The system hardware could not map the 
requested address.
+
+**/
+EFI_STATUS
+EFIAPI
+IoMmuMap (
+  IN     EDKII_IOMMU_PROTOCOL                       *This,
+  IN     EDKII_IOMMU_OPERATION                      Operation,
+  IN     VOID                                       *HostAddress,
+  IN OUT UINTN                                      *NumberOfBytes,
+  OUT    EFI_PHYSICAL_ADDRESS                       *DeviceAddress,
+  OUT    VOID                                       **Mapping
+  )
+{
+  EFI_STATUS                                        Status;
+  EFI_PHYSICAL_ADDRESS                              PhysicalAddress;
+  MAP_INFO                                          *MapInfo;
+  EFI_PHYSICAL_ADDRESS                              DmaMemoryTop;
+  EFI_ALLOCATE_TYPE                                 AllocateType;
+
+  if (HostAddress == NULL || NumberOfBytes == NULL || DeviceAddress == NULL ||
+      Mapping == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Make sure that Operation is valid
+  //
+  if ((UINT32) Operation >= EdkiiIoMmuOperationMaximum) {
+    return EFI_INVALID_PARAMETER;
+  }
+  PhysicalAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress;
+
+  DmaMemoryTop = (UINTN)-1;
+  AllocateType = AllocateAnyPages;
+
+  if (((Operation != EdkiiIoMmuOperationBusMasterRead64 &&
+        Operation != EdkiiIoMmuOperationBusMasterWrite64 &&
+        Operation != EdkiiIoMmuOperationBusMasterCommonBuffer64)) &&
+      ((PhysicalAddress + *NumberOfBytes) > SIZE_4GB)) {
+    //
+    // If the root bridge or the device cannot handle performing DMA above
+    // 4GB but any part of the DMA transfer being mapped is above 4GB, then
+    // map the DMA transfer to a buffer below 4GB.
+    //
+    DmaMemoryTop = SIZE_4GB - 1;
+    AllocateType = AllocateMaxAddress;
+
+    if (Operation == EdkiiIoMmuOperationBusMasterCommonBuffer ||
+        Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) {
+        //
+        // Common Buffer operations can not be remapped.  If the common buffer
+        // if above 4GB, then it is not possible to generate a mapping, so 
return
+        // an error.
+        //
+        return EFI_UNSUPPORTED;
+    }
+  }
+
+  //
+  // CommandBuffer was allocated by us (AllocateBuffer) and is already in
+  // unencryted buffer so no need to create bounce buffer
+  //
+  if (Operation == EdkiiIoMmuOperationBusMasterCommonBuffer ||
+      Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) {
+    *Mapping = NO_MAPPING;
+    *DeviceAddress = PhysicalAddress;
+
+    return EFI_SUCCESS;
+  }
+
+  //
+  // Allocate a MAP_INFO structure to remember the mapping when Unmap() is
+  // called later.
+  //
+  MapInfo = AllocatePool (sizeof (MAP_INFO));
+  if (MapInfo == NULL) {
+    *NumberOfBytes = 0;
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  //
+  // Initialize the MAP_INFO structure
+  //
+  MapInfo->Operation         = Operation;
+  MapInfo->NumberOfBytes     = *NumberOfBytes;
+  MapInfo->NumberOfPages     = EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes);
+  MapInfo->HostAddress       = PhysicalAddress;
+  MapInfo->DeviceAddress     = DmaMemoryTop;
+
+  //
+  // Allocate a buffer to map the transfer to.
+  //
+  Status = gBS->AllocatePages (
+                  AllocateType,
+                  EfiBootServicesData,
+                  MapInfo->NumberOfPages,
+                  &MapInfo->DeviceAddress
+                  );
+  if (EFI_ERROR (Status)) {
+    FreePool (MapInfo);
+    *NumberOfBytes = 0;
+    return Status;
+  }
+
+  //
+  // Clear the memory encryption mask from the device buffer
+  //
+  Status = MemEncryptSevClearPageEncMask (0, MapInfo->DeviceAddress, 
MapInfo->NumberOfPages, TRUE);
+  ASSERT_EFI_ERROR(Status);
+
+  //
+  // If this is a read operation from the Bus Master's point of view,
+  // then copy the contents of the real buffer into the mapped buffer
+  // so the Bus Master can read the contents of the real buffer.
+  //
+  if (Operation == EdkiiIoMmuOperationBusMasterRead ||
+      Operation == EdkiiIoMmuOperationBusMasterRead64) {
+    CopyMem (
+      (VOID *) (UINTN) MapInfo->DeviceAddress,
+      (VOID *) (UINTN) MapInfo->HostAddress,
+      MapInfo->NumberOfBytes
+      );
+  }
+
+  //
+  // The DeviceAddress is the address of the maped buffer below 4GB
+  //
+  *DeviceAddress = MapInfo->DeviceAddress;
+
+  //
+  // Return a pointer to the MAP_INFO structure in Mapping
+  //
+  *Mapping       = MapInfo;
+
+  DEBUG ((DEBUG_VERBOSE, "%a Host 0x%Lx Device 0x%Lx Pages 0x%Lx Bytes 
0x%Lx\n",
+        __FUNCTION__, MapInfo->DeviceAddress, MapInfo->HostAddress,
+        MapInfo->NumberOfPages, MapInfo->NumberOfBytes));
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Completes the Map() operation and releases any corresponding resources.
+
+  @param  This                  The protocol instance pointer.
+  @param  Mapping               The mapping value returned from Map().
+
+  @retval EFI_SUCCESS           The range was unmapped.
+  @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by 
Map().
+  @retval EFI_DEVICE_ERROR      The data was not committed to the target 
system memory.
+**/
+EFI_STATUS
+EFIAPI
+IoMmuUnmap (
+  IN  EDKII_IOMMU_PROTOCOL                     *This,
+  IN  VOID                                     *Mapping
+  )
+{
+  MAP_INFO                 *MapInfo;
+  EFI_STATUS               Status;
+
+  if (Mapping == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // See if the Map() operation associated with this Unmap() required a mapping
+  // buffer. If a mapping buffer was not required, then this function simply
+  // buffer. If a mapping buffer was not required, then this function simply
+  //
+  if (Mapping == NO_MAPPING) {
+    return EFI_SUCCESS;
+  }
+
+  MapInfo = (MAP_INFO *)Mapping;
+
+  //
+  // If this is a write operation from the Bus Master's point of view,
+  // then copy the contents of the mapped buffer into the real buffer
+  // so the processor can read the contents of the real buffer.
+  //
+  if (MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite ||
+      MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite64) {
+    CopyMem (
+      (VOID *) (UINTN) MapInfo->HostAddress,
+      (VOID *) (UINTN) MapInfo->DeviceAddress,
+      MapInfo->NumberOfBytes
+      );
+  }
+
+  DEBUG ((DEBUG_VERBOSE, "%a Host 0x%Lx Device 0x%Lx Pages 0x%Lx Bytes 
0x%Lx\n",
+        __FUNCTION__, MapInfo->DeviceAddress, MapInfo->HostAddress,
+        MapInfo->NumberOfPages, MapInfo->NumberOfBytes));
+  //
+  // Restore the memory encryption mask
+  //
+  Status = MemEncryptSevSetPageEncMask (0, MapInfo->DeviceAddress, 
MapInfo->NumberOfPages, TRUE);
+  ASSERT_EFI_ERROR(Status);
+
+  //
+  // Free the mapped buffer and the MAP_INFO structure.
+  //
+  gBS->FreePages (MapInfo->DeviceAddress, MapInfo->NumberOfPages);
+  FreePool (Mapping);
+  return EFI_SUCCESS;
+}
+
+/**
+  Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
+  OperationBusMasterCommonBuffer64 mapping.
+
+  @param  This                  The protocol instance pointer.
+  @param  Type                  This parameter is not used and must be ignored.
+  @param  MemoryType            The type of memory to allocate, 
EfiBootServicesData
+                                or EfiRuntimeServicesData.
+  @param  Pages                 The number of pages to allocate.
+  @param  HostAddress           A pointer to store the base system memory 
address
+                                of the allocated range.
+  @param  Attributes            The requested bit mask of attributes for the 
allocated range.
+
+  @retval EFI_SUCCESS           The requested memory pages were allocated.
+  @retval EFI_UNSUPPORTED       Attributes is unsupported. The only legal 
attribute
+                                bits are MEMORY_WRITE_COMBINE and 
MEMORY_CACHED.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+  @retval EFI_OUT_OF_RESOURCES  The memory pages could not be allocated.
+
+**/
+EFI_STATUS
+EFIAPI
+IoMmuAllocateBuffer (
+  IN     EDKII_IOMMU_PROTOCOL                     *This,
+  IN     EFI_ALLOCATE_TYPE                        Type,
+  IN     EFI_MEMORY_TYPE                          MemoryType,
+  IN     UINTN                                    Pages,
+  IN OUT VOID                                     **HostAddress,
+  IN     UINT64                                   Attributes
+  )
+{
+  EFI_STATUS                Status;
+  EFI_PHYSICAL_ADDRESS      PhysicalAddress;
+
+  //
+  // Validate Attributes
+  //
+  if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) {
+    return EFI_UNSUPPORTED;
+  }
+
+  //
+  // Check for invalid inputs
+  //
+  if (HostAddress == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // The only valid memory types are EfiBootServicesData and
+  // EfiRuntimeServicesData
+  //
+  if (MemoryType != EfiBootServicesData &&
+      MemoryType != EfiRuntimeServicesData) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  PhysicalAddress = (UINTN)-1;
+  if ((Attributes & EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0) {
+    //
+    // Limit allocations to memory below 4GB
+    //
+    PhysicalAddress = SIZE_4GB - 1;
+  }
+  Status = gBS->AllocatePages (
+                  AllocateMaxAddress,
+                  MemoryType,
+                  Pages,
+                  &PhysicalAddress
+                  );
+  if (!EFI_ERROR (Status)) {
+    *HostAddress = (VOID *) (UINTN) PhysicalAddress;
+
+    //
+    // Clear memory encryption mask
+    //
+    Status = MemEncryptSevClearPageEncMask (0, PhysicalAddress, Pages, TRUE);
+    ASSERT_EFI_ERROR(Status);
+  }
+
+  DEBUG ((DEBUG_VERBOSE, "%a Address 0x%Lx Pages 0x%Lx\n", __FUNCTION__, 
PhysicalAddress, Pages));
+  return Status;
+}
+
+/**
+  Frees memory that was allocated with AllocateBuffer().
+
+  @param  This                  The protocol instance pointer.
+  @param  Pages                 The number of pages to free.
+  @param  HostAddress           The base system memory address of the 
allocated range.
+
+  @retval EFI_SUCCESS           The requested memory pages were freed.
+  @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and 
Pages
+                                was not allocated with AllocateBuffer().
+
+**/
+EFI_STATUS
+EFIAPI
+IoMmuFreeBuffer (
+  IN  EDKII_IOMMU_PROTOCOL                     *This,
+  IN  UINTN                                    Pages,
+  IN  VOID                                     *HostAddress
+  )
+{
+  EFI_STATUS  Status;
+
+  //
+  // Set memory encryption mask
+  //
+  Status = MemEncryptSevSetPageEncMask (0, 
(EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress, Pages, TRUE);
+  ASSERT_EFI_ERROR(Status);
+
+  DEBUG ((DEBUG_VERBOSE, "%a Address 0x%Lx Pages 0x%Lx\n", __FUNCTION__, 
(UINTN)HostAddress, Pages));
+  return gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress, Pages);
+}
+
+
+/**
+  Set IOMMU attribute for a system memory.
+
+  If the IOMMU protocol exists, the system memory cannot be used
+  for DMA by default.
+
+  When a device requests a DMA access for a system memory,
+  the device driver need use SetAttribute() to update the IOMMU
+  attribute to request DMA access (read and/or write).
+
+  The DeviceHandle is used to identify which device submits the request.
+  The IOMMU implementation need translate the device path to an IOMMU device 
ID,
+  and set IOMMU hardware register accordingly.
+  1) DeviceHandle can be a standard PCI device.
+     The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ.
+     The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE.
+     The memory for BusMasterCommonBuffer need set 
EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE.
+     After the memory is used, the memory need set 0 to keep it being 
protected.
+  2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc).
+     The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or 
EDKII_IOMMU_ACCESS_WRITE.
+
+  @param[in]  This              The protocol instance pointer.
+  @param[in]  DeviceHandle      The device who initiates the DMA access 
request.
+  @param[in]  Mapping           The mapping value returned from Map().
+  @param[in]  IoMmuAccess       The IOMMU access.
+
+  @retval EFI_SUCCESS            The IoMmuAccess is set for the memory range 
specified by DeviceAddress and Length.
+  @retval EFI_INVALID_PARAMETER  DeviceHandle is an invalid handle.
+  @retval EFI_INVALID_PARAMETER  Mapping is not a value that was returned by 
Map().
+  @retval EFI_INVALID_PARAMETER  IoMmuAccess specified an illegal combination 
of access.
+  @retval EFI_UNSUPPORTED        DeviceHandle is unknown by the IOMMU.
+  @retval EFI_UNSUPPORTED        The bit mask of IoMmuAccess is not supported 
by the IOMMU.
+  @retval EFI_UNSUPPORTED        The IOMMU does not support the memory range 
specified by Mapping.
+  @retval EFI_OUT_OF_RESOURCES   There are not enough resources available to 
modify the IOMMU access.
+  @retval EFI_DEVICE_ERROR       The IOMMU device reported an error while 
attempting the operation.
+
+**/
+EFI_STATUS
+EFIAPI
+IoMmuSetAttribute (
+  IN EDKII_IOMMU_PROTOCOL  *This,
+  IN EFI_HANDLE            DeviceHandle,
+  IN VOID                  *Mapping,
+  IN UINT64                IoMmuAccess
+  )
+{
+  return EFI_UNSUPPORTED;
+}
+
+EDKII_IOMMU_PROTOCOL  mAmdSev = {
+  EDKII_IOMMU_PROTOCOL_REVISION,
+  IoMmuSetAttribute,
+  IoMmuMap,
+  IoMmuUnmap,
+  IoMmuAllocateBuffer,
+  IoMmuFreeBuffer,
+};
+
+/**
+  Initialize Iommu Protocol.
+
+**/
+VOID
+EFIAPI
+AmdSevInstallIommuProtocol (
+  VOID
+  )
+{
+  EFI_STATUS  Status;
+  EFI_HANDLE  Handle;
+
+  Handle = NULL;
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &Handle,
+                  &gEdkiiIoMmuProtocolGuid, &mAmdSev,
+                  NULL
+                  );
+  ASSERT_EFI_ERROR (Status);
+}
diff --git a/OvmfPkg/AmdSevDxe/AmdSevMmio.c b/OvmfPkg/AmdSevDxe/AmdSevMmio.c
new file mode 100644
index 000000000000..b623f82b7baa
--- /dev/null
+++ b/OvmfPkg/AmdSevDxe/AmdSevMmio.c
@@ -0,0 +1,50 @@
+/** @file
+
+  Implements routines to clear C-bit from MMIO Memory Range
+
+  Copyright (c) 2017, AMD Inc. All rights reserved.<BR>
+
+  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 "AmdSevMmio.h"
+
+/**
+
+  Iterate through the GCD map and clear the C-bit from MMIO and NonExistent
+  memory space. The NonExistent memory space will be used for mapping the MMIO
+  space added later (eg PciRootBridge). By clearing both known NonExistent
+  memory space can gurantee that any MMIO mapped later will have C-bit cleared.
+*/
+VOID
+EFIAPI
+AmdSevClearEncMaskMmioRange (
+  VOID
+  )
+{
+  EFI_STATUS                       Status;
+  EFI_GCD_MEMORY_SPACE_DESCRIPTOR  *AllDescMap;
+  UINTN                            NumEntries;
+  UINTN                            Index;
+
+  Status = gDS->GetMemorySpaceMap (&NumEntries, &AllDescMap);
+  if (Status == EFI_SUCCESS) {
+    for (Index = 0; Index < NumEntries; Index++) {
+      CONST EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Desc;
+
+      Desc = &AllDescMap[Index];
+      if (Desc->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo ||
+          Desc->GcdMemoryType == EfiGcdMemoryTypeNonExistent) {
+        Status = MemEncryptSevClearPageEncMask (0, Desc->BaseAddress, 
EFI_SIZE_TO_PAGES(Desc->Length), FALSE);
+        ASSERT_EFI_ERROR(Status);
+      }
+    }
+  }
+}
-- 
2.7.4

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel

Reply via email to