+++ /dev/null
-/** @file\r
-\r
- The protocol provides support to allocate, free, map and umap a DMA buffer\r
- for bus master (e.g PciHostBridge). When SEV or TDX is enabled, the DMA\r
- operations must be performed on unencrypted buffer hence we use a bounce\r
- buffer to map the guest buffer into an unencrypted DMA buffer.\r
-\r
- Copyright (c) 2017, AMD Inc. All rights reserved.<BR>\r
- Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
-\r
- SPDX-License-Identifier: BSD-2-Clause-Patent\r
-\r
-**/\r
-\r
-#include <Library/PcdLib.h>\r
-#include <ConfidentialComputingGuestAttr.h>\r
-#include "AmdSevIoMmu.h"\r
-#include "IoMmuInternal.h"\r
-\r
-//\r
-// List of the MAP_INFO structures that have been set up by IoMmuMap() and not\r
-// yet torn down by IoMmuUnmap(). The list represents the full set of mappings\r
-// currently in effect.\r
-//\r
-STATIC LIST_ENTRY mMapInfos = INITIALIZE_LIST_HEAD_VARIABLE (mMapInfos);\r
-\r
-//\r
-// Indicate if the feature of reserved memory is supported in DMA operation.\r
-//\r
-BOOLEAN mReservedSharedMemSupported = FALSE;\r
-\r
-//\r
-// ASCII names for EDKII_IOMMU_OPERATION constants, for debug logging.\r
-//\r
-STATIC CONST CHAR8 *CONST\r
-mBusMasterOperationName[EdkiiIoMmuOperationMaximum] = {\r
- "Read",\r
- "Write",\r
- "CommonBuffer",\r
- "Read64",\r
- "Write64",\r
- "CommonBuffer64"\r
-};\r
-\r
-/**\r
- Provides the controller-specific addresses required to access system memory\r
- from a DMA bus master. On SEV/TDX guest, the DMA operations must be performed on\r
- shared buffer hence we allocate a bounce buffer to map the HostAddress to a\r
- DeviceAddress. The Encryption attribute is removed from the DeviceAddress\r
- buffer.\r
-\r
- @param This The protocol instance pointer.\r
- @param Operation Indicates if the bus master is going to read or\r
- write to system memory.\r
- @param HostAddress The system memory address to map to the PCI\r
- controller.\r
- @param NumberOfBytes On input the number of bytes to map. On output\r
- the number of bytes that were mapped.\r
- @param DeviceAddress The resulting map address for the bus master\r
- PCI controller to use to access the hosts\r
- HostAddress.\r
- @param Mapping A resulting value to pass to Unmap().\r
-\r
- @retval EFI_SUCCESS The range was mapped for the returned\r
- NumberOfBytes.\r
- @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common\r
- buffer.\r
- @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
- @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a\r
- lack of resources.\r
- @retval EFI_DEVICE_ERROR The system hardware could not map the requested\r
- address.\r
-\r
-**/\r
-EFI_STATUS\r
-EFIAPI\r
-IoMmuMap (\r
- IN EDKII_IOMMU_PROTOCOL *This,\r
- IN EDKII_IOMMU_OPERATION Operation,\r
- IN VOID *HostAddress,\r
- IN OUT UINTN *NumberOfBytes,\r
- OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,\r
- OUT VOID **Mapping\r
- )\r
-{\r
- EFI_STATUS Status;\r
- MAP_INFO *MapInfo;\r
- EFI_ALLOCATE_TYPE AllocateType;\r
- COMMON_BUFFER_HEADER *CommonBufferHeader;\r
- VOID *DecryptionSource;\r
-\r
- DEBUG ((\r
- DEBUG_VERBOSE,\r
- "%a: Operation=%a Host=0x%p Bytes=0x%Lx\n",\r
- __FUNCTION__,\r
- ((Operation >= 0 &&\r
- Operation < ARRAY_SIZE (mBusMasterOperationName)) ?\r
- mBusMasterOperationName[Operation] :\r
- "Invalid"),\r
- HostAddress,\r
- (UINT64)((NumberOfBytes == NULL) ? 0 : *NumberOfBytes)\r
- ));\r
-\r
- if ((HostAddress == NULL) || (NumberOfBytes == NULL) || (DeviceAddress == NULL) ||\r
- (Mapping == NULL))\r
- {\r
- return EFI_INVALID_PARAMETER;\r
- }\r
-\r
- Status = EFI_SUCCESS;\r
-\r
- //\r
- // Allocate a MAP_INFO structure to remember the mapping when Unmap() is\r
- // called later.\r
- //\r
- MapInfo = AllocatePool (sizeof (MAP_INFO));\r
- if (MapInfo == NULL) {\r
- Status = EFI_OUT_OF_RESOURCES;\r
- goto Failed;\r
- }\r
-\r
- //\r
- // Initialize the MAP_INFO structure, except the PlainTextAddress field\r
- //\r
- ZeroMem (&MapInfo->Link, sizeof MapInfo->Link);\r
- MapInfo->Signature = MAP_INFO_SIG;\r
- MapInfo->Operation = Operation;\r
- MapInfo->NumberOfBytes = *NumberOfBytes;\r
- MapInfo->NumberOfPages = EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes);\r
- MapInfo->CryptedAddress = (UINTN)HostAddress;\r
- MapInfo->ReservedMemBitmap = 0;\r
-\r
- //\r
- // In the switch statement below, we point "MapInfo->PlainTextAddress" to the\r
- // plaintext buffer, according to Operation. We also set "DecryptionSource".\r
- //\r
- MapInfo->PlainTextAddress = MAX_ADDRESS;\r
- AllocateType = AllocateAnyPages;\r
- DecryptionSource = (VOID *)(UINTN)MapInfo->CryptedAddress;\r
- switch (Operation) {\r
- //\r
- // For BusMasterRead[64] and BusMasterWrite[64] operations, a bounce buffer\r
- // is necessary regardless of whether the original (crypted) buffer crosses\r
- // the 4GB limit or not -- we have to allocate a separate plaintext buffer.\r
- // The only variable is whether the plaintext buffer should be under 4GB.\r
- //\r
- case EdkiiIoMmuOperationBusMasterRead:\r
- case EdkiiIoMmuOperationBusMasterWrite:\r
- MapInfo->PlainTextAddress = BASE_4GB - 1;\r
- AllocateType = AllocateMaxAddress;\r
- //\r
- // fall through\r
- //\r
- case EdkiiIoMmuOperationBusMasterRead64:\r
- case EdkiiIoMmuOperationBusMasterWrite64:\r
- //\r
- // Allocate the implicit plaintext bounce buffer.\r
- //\r
- Status = IoMmuAllocateBounceBuffer (\r
- AllocateType,\r
- EfiBootServicesData,\r
- MapInfo\r
- );\r
- if (EFI_ERROR (Status)) {\r
- goto FreeMapInfo;\r
- }\r
-\r
- break;\r
-\r
- //\r
- // For BusMasterCommonBuffer[64] operations, a to-be-plaintext buffer and a\r
- // stash buffer (for in-place decryption) have been allocated already, with\r
- // AllocateBuffer(). We only check whether the address of the to-be-plaintext\r
- // buffer is low enough for the requested operation.\r
- //\r
- case EdkiiIoMmuOperationBusMasterCommonBuffer:\r
- if ((MapInfo->CryptedAddress > BASE_4GB) ||\r
- (EFI_PAGES_TO_SIZE (MapInfo->NumberOfPages) >\r
- BASE_4GB - MapInfo->CryptedAddress))\r
- {\r
- //\r
- // CommonBuffer operations cannot be remapped. If the common buffer is\r
- // above 4GB, then it is not possible to generate a mapping, so return an\r
- // error.\r
- //\r
- Status = EFI_UNSUPPORTED;\r
- goto FreeMapInfo;\r
- }\r
-\r
- //\r
- // fall through\r
- //\r
- case EdkiiIoMmuOperationBusMasterCommonBuffer64:\r
- //\r
- // The buffer at MapInfo->CryptedAddress comes from AllocateBuffer().\r
- //\r
- MapInfo->PlainTextAddress = MapInfo->CryptedAddress;\r
- //\r
- // Stash the crypted data.\r
- //\r
- CommonBufferHeader = (COMMON_BUFFER_HEADER *)(\r
- (UINTN)MapInfo->CryptedAddress - EFI_PAGE_SIZE\r
- );\r
- ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG);\r
- CopyMem (\r
- CommonBufferHeader->StashBuffer,\r
- (VOID *)(UINTN)MapInfo->CryptedAddress,\r
- MapInfo->NumberOfBytes\r
- );\r
- //\r
- // Point "DecryptionSource" to the stash buffer so that we decrypt\r
- // it to the original location, after the switch statement.\r
- //\r
- DecryptionSource = CommonBufferHeader->StashBuffer;\r
- MapInfo->ReservedMemBitmap = CommonBufferHeader->ReservedMemBitmap;\r
- break;\r
-\r
- default:\r
- //\r
- // Operation is invalid\r
- //\r
- Status = EFI_INVALID_PARAMETER;\r
- goto FreeMapInfo;\r
- }\r
-\r
- if (CC_GUEST_IS_SEV (PcdGet64 (PcdConfidentialComputingGuestAttr))) {\r
- //\r
- // Clear the memory encryption mask on the plaintext buffer.\r
- //\r
- Status = MemEncryptSevClearPageEncMask (\r
- 0,\r
- MapInfo->PlainTextAddress,\r
- MapInfo->NumberOfPages\r
- );\r
- } else if (CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr))) {\r
- //\r
- // Set the memory shared bit.\r
- // If MapInfo->ReservedMemBitmap is 0, it means the bounce buffer is not allocated\r
- // from the pre-allocated shared memory, so it must be converted to shared memory here.\r
- //\r
- if (MapInfo->ReservedMemBitmap == 0) {\r
- Status = MemEncryptTdxSetPageSharedBit (\r
- 0,\r
- MapInfo->PlainTextAddress,\r
- MapInfo->NumberOfPages\r
- );\r
- }\r
- } else {\r
- ASSERT (FALSE);\r
- }\r
-\r
- ASSERT_EFI_ERROR (Status);\r
- if (EFI_ERROR (Status)) {\r
- CpuDeadLoop ();\r
- }\r
-\r
- //\r
- // If this is a read operation from the Bus Master's point of view,\r
- // then copy the contents of the real buffer into the mapped buffer\r
- // so the Bus Master can read the contents of the real buffer.\r
- //\r
- // For BusMasterCommonBuffer[64] operations, the CopyMem() below will decrypt\r
- // the original data (from the stash buffer) back to the original location.\r
- //\r
- if ((Operation == EdkiiIoMmuOperationBusMasterRead) ||\r
- (Operation == EdkiiIoMmuOperationBusMasterRead64) ||\r
- (Operation == EdkiiIoMmuOperationBusMasterCommonBuffer) ||\r
- (Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64))\r
- {\r
- CopyMem (\r
- (VOID *)(UINTN)MapInfo->PlainTextAddress,\r
- DecryptionSource,\r
- MapInfo->NumberOfBytes\r
- );\r
- }\r
-\r
- //\r
- // Track all MAP_INFO structures.\r
- //\r
- InsertHeadList (&mMapInfos, &MapInfo->Link);\r
- //\r
- // Populate output parameters.\r
- //\r
- *DeviceAddress = MapInfo->PlainTextAddress;\r
- *Mapping = MapInfo;\r
-\r
- DEBUG ((\r
- DEBUG_VERBOSE,\r
- "%a: Mapping=0x%p Device(PlainText)=0x%Lx Crypted=0x%Lx Pages=0x%Lx, ReservedMemBitmap=0x%Lx\n",\r
- __FUNCTION__,\r
- MapInfo,\r
- MapInfo->PlainTextAddress,\r
- MapInfo->CryptedAddress,\r
- (UINT64)MapInfo->NumberOfPages,\r
- MapInfo->ReservedMemBitmap\r
- ));\r
-\r
- return EFI_SUCCESS;\r
-\r
-FreeMapInfo:\r
- FreePool (MapInfo);\r
-\r
-Failed:\r
- *NumberOfBytes = 0;\r
- return Status;\r
-}\r
-\r
-/**\r
- Completes the Map() operation and releases any corresponding resources.\r
-\r
- This is an internal worker function that only extends the Map() API with\r
- the MemoryMapLocked parameter.\r
-\r
- @param This The protocol instance pointer.\r
- @param Mapping The mapping value returned from Map().\r
- @param MemoryMapLocked The function is executing on the stack of\r
- gBS->ExitBootServices(); changes to the UEFI\r
- memory map are forbidden.\r
-\r
- @retval EFI_SUCCESS The range was unmapped.\r
- @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by\r
- Map().\r
- @retval EFI_DEVICE_ERROR The data was not committed to the target system\r
- memory.\r
-**/\r
-STATIC\r
-EFI_STATUS\r
-EFIAPI\r
-IoMmuUnmapWorker (\r
- IN EDKII_IOMMU_PROTOCOL *This,\r
- IN VOID *Mapping,\r
- IN BOOLEAN MemoryMapLocked\r
- )\r
-{\r
- MAP_INFO *MapInfo;\r
- EFI_STATUS Status;\r
- COMMON_BUFFER_HEADER *CommonBufferHeader;\r
- VOID *EncryptionTarget;\r
-\r
- DEBUG ((\r
- DEBUG_VERBOSE,\r
- "%a: Mapping=0x%p MemoryMapLocked=%d\n",\r
- __FUNCTION__,\r
- Mapping,\r
- MemoryMapLocked\r
- ));\r
-\r
- if (Mapping == NULL) {\r
- return EFI_INVALID_PARAMETER;\r
- }\r
-\r
- MapInfo = (MAP_INFO *)Mapping;\r
- Status = EFI_SUCCESS;\r
- //\r
- // set CommonBufferHeader to suppress incorrect compiler/analyzer warnings\r
- //\r
- CommonBufferHeader = NULL;\r
-\r
- //\r
- // For BusMasterWrite[64] operations and BusMasterCommonBuffer[64] operations\r
- // we have to encrypt the results, ultimately to the original place (i.e.,\r
- // "MapInfo->CryptedAddress").\r
- //\r
- // For BusMasterCommonBuffer[64] operations however, this encryption has to\r
- // land in-place, so divert the encryption to the stash buffer first.\r
- //\r
- EncryptionTarget = (VOID *)(UINTN)MapInfo->CryptedAddress;\r
-\r
- switch (MapInfo->Operation) {\r
- case EdkiiIoMmuOperationBusMasterCommonBuffer:\r
- case EdkiiIoMmuOperationBusMasterCommonBuffer64:\r
- ASSERT (MapInfo->PlainTextAddress == MapInfo->CryptedAddress);\r
-\r
- CommonBufferHeader = (COMMON_BUFFER_HEADER *)(\r
- (UINTN)MapInfo->PlainTextAddress - EFI_PAGE_SIZE\r
- );\r
- ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG);\r
- EncryptionTarget = CommonBufferHeader->StashBuffer;\r
- //\r
- // fall through\r
- //\r
-\r
- case EdkiiIoMmuOperationBusMasterWrite:\r
- case EdkiiIoMmuOperationBusMasterWrite64:\r
- CopyMem (\r
- EncryptionTarget,\r
- (VOID *)(UINTN)MapInfo->PlainTextAddress,\r
- MapInfo->NumberOfBytes\r
- );\r
- break;\r
-\r
- default:\r
- //\r
- // nothing to encrypt after BusMasterRead[64] operations\r
- //\r
- break;\r
- }\r
-\r
- if (CC_GUEST_IS_SEV (PcdGet64 (PcdConfidentialComputingGuestAttr))) {\r
- //\r
- // Restore the memory encryption mask on the area we used to hold the\r
- // plaintext.\r
- //\r
- Status = MemEncryptSevSetPageEncMask (\r
- 0,\r
- MapInfo->PlainTextAddress,\r
- MapInfo->NumberOfPages\r
- );\r
- } else if (CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr))) {\r
- //\r
- // Restore the memory shared bit mask on the area we used to hold the\r
- // plaintext.\r
- //\r
- if (MapInfo->ReservedMemBitmap == 0) {\r
- Status = MemEncryptTdxClearPageSharedBit (\r
- 0,\r
- MapInfo->PlainTextAddress,\r
- MapInfo->NumberOfPages\r
- );\r
- }\r
- } else {\r
- ASSERT (FALSE);\r
- }\r
-\r
- ASSERT_EFI_ERROR (Status);\r
- if (EFI_ERROR (Status)) {\r
- CpuDeadLoop ();\r
- }\r
-\r
- //\r
- // For BusMasterCommonBuffer[64] operations, copy the stashed data to the\r
- // original (now encrypted) location.\r
- //\r
- // For all other operations, fill the late bounce buffer (which existed as\r
- // plaintext at some point) with zeros, and then release it (unless the UEFI\r
- // memory map is locked).\r
- //\r
- if ((MapInfo->Operation == EdkiiIoMmuOperationBusMasterCommonBuffer) ||\r
- (MapInfo->Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64))\r
- {\r
- CopyMem (\r
- (VOID *)(UINTN)MapInfo->CryptedAddress,\r
- CommonBufferHeader->StashBuffer,\r
- MapInfo->NumberOfBytes\r
- );\r
- } else {\r
- ZeroMem (\r
- (VOID *)(UINTN)MapInfo->PlainTextAddress,\r
- EFI_PAGES_TO_SIZE (MapInfo->NumberOfPages)\r
- );\r
-\r
- if (!MemoryMapLocked) {\r
- IoMmuFreeBounceBuffer (MapInfo);\r
- }\r
- }\r
-\r
- //\r
- // Forget the MAP_INFO structure, then free it (unless the UEFI memory map is\r
- // locked).\r
- //\r
- RemoveEntryList (&MapInfo->Link);\r
- if (!MemoryMapLocked) {\r
- FreePool (MapInfo);\r
- }\r
-\r
- return EFI_SUCCESS;\r
-}\r
-\r
-/**\r
- Completes the Map() operation and releases any corresponding resources.\r
-\r
- @param This The protocol instance pointer.\r
- @param Mapping The mapping value returned from Map().\r
-\r
- @retval EFI_SUCCESS The range was unmapped.\r
- @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by\r
- Map().\r
- @retval EFI_DEVICE_ERROR The data was not committed to the target system\r
- memory.\r
-**/\r
-EFI_STATUS\r
-EFIAPI\r
-IoMmuUnmap (\r
- IN EDKII_IOMMU_PROTOCOL *This,\r
- IN VOID *Mapping\r
- )\r
-{\r
- return IoMmuUnmapWorker (\r
- This,\r
- Mapping,\r
- FALSE // MemoryMapLocked\r
- );\r
-}\r
-\r
-/**\r
- Allocates pages that are suitable for an OperationBusMasterCommonBuffer or\r
- OperationBusMasterCommonBuffer64 mapping.\r
-\r
- @param This The protocol instance pointer.\r
- @param Type This parameter is not used and must be ignored.\r
- @param MemoryType The type of memory to allocate,\r
- EfiBootServicesData or EfiRuntimeServicesData.\r
- @param Pages The number of pages to allocate.\r
- @param HostAddress A pointer to store the base system memory\r
- address of the allocated range.\r
- @param Attributes The requested bit mask of attributes for the\r
- allocated range.\r
-\r
- @retval EFI_SUCCESS The requested memory pages were allocated.\r
- @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal\r
- attribute bits are MEMORY_WRITE_COMBINE and\r
- MEMORY_CACHED.\r
- @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
- @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.\r
-\r
-**/\r
-EFI_STATUS\r
-EFIAPI\r
-IoMmuAllocateBuffer (\r
- IN EDKII_IOMMU_PROTOCOL *This,\r
- IN EFI_ALLOCATE_TYPE Type,\r
- IN EFI_MEMORY_TYPE MemoryType,\r
- IN UINTN Pages,\r
- IN OUT VOID **HostAddress,\r
- IN UINT64 Attributes\r
- )\r
-{\r
- EFI_STATUS Status;\r
- EFI_PHYSICAL_ADDRESS PhysicalAddress;\r
- VOID *StashBuffer;\r
- UINTN CommonBufferPages;\r
- COMMON_BUFFER_HEADER *CommonBufferHeader;\r
- UINT32 ReservedMemBitmap;\r
-\r
- DEBUG ((\r
- DEBUG_VERBOSE,\r
- "%a: MemoryType=%u Pages=0x%Lx Attributes=0x%Lx\n",\r
- __FUNCTION__,\r
- (UINT32)MemoryType,\r
- (UINT64)Pages,\r
- Attributes\r
- ));\r
-\r
- //\r
- // Validate Attributes\r
- //\r
- if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) {\r
- return EFI_UNSUPPORTED;\r
- }\r
-\r
- //\r
- // Check for invalid inputs\r
- //\r
- if (HostAddress == NULL) {\r
- return EFI_INVALID_PARAMETER;\r
- }\r
-\r
- //\r
- // The only valid memory types are EfiBootServicesData and\r
- // EfiRuntimeServicesData\r
- //\r
- if ((MemoryType != EfiBootServicesData) &&\r
- (MemoryType != EfiRuntimeServicesData))\r
- {\r
- return EFI_INVALID_PARAMETER;\r
- }\r
-\r
- //\r
- // We'll need a header page for the COMMON_BUFFER_HEADER structure.\r
- //\r
- if (Pages > MAX_UINTN - 1) {\r
- return EFI_OUT_OF_RESOURCES;\r
- }\r
-\r
- CommonBufferPages = Pages + 1;\r
-\r
- //\r
- // Allocate the stash in EfiBootServicesData type memory.\r
- //\r
- // Map() will temporarily save encrypted data in the stash for\r
- // BusMasterCommonBuffer[64] operations, so the data can be decrypted to the\r
- // original location.\r
- //\r
- // Unmap() will temporarily save plaintext data in the stash for\r
- // BusMasterCommonBuffer[64] operations, so the data can be encrypted to the\r
- // original location.\r
- //\r
- // StashBuffer always resides in encrypted memory.\r
- //\r
- StashBuffer = AllocatePages (Pages);\r
- if (StashBuffer == NULL) {\r
- return EFI_OUT_OF_RESOURCES;\r
- }\r
-\r
- PhysicalAddress = (UINTN)-1;\r
- if ((Attributes & EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0) {\r
- //\r
- // Limit allocations to memory below 4GB\r
- //\r
- PhysicalAddress = SIZE_4GB - 1;\r
- }\r
-\r
- Status = IoMmuAllocateCommonBuffer (\r
- MemoryType,\r
- CommonBufferPages,\r
- &PhysicalAddress,\r
- &ReservedMemBitmap\r
- );\r
-\r
- if (EFI_ERROR (Status)) {\r
- goto FreeStashBuffer;\r
- }\r
-\r
- CommonBufferHeader = (VOID *)(UINTN)PhysicalAddress;\r
- PhysicalAddress += EFI_PAGE_SIZE;\r
-\r
- CommonBufferHeader->Signature = COMMON_BUFFER_SIG;\r
- CommonBufferHeader->StashBuffer = StashBuffer;\r
- CommonBufferHeader->ReservedMemBitmap = ReservedMemBitmap;\r
-\r
- *HostAddress = (VOID *)(UINTN)PhysicalAddress;\r
-\r
- DEBUG ((\r
- DEBUG_VERBOSE,\r
- "%a: Host=0x%Lx Stash=0x%p\n",\r
- __FUNCTION__,\r
- PhysicalAddress,\r
- StashBuffer\r
- ));\r
- return EFI_SUCCESS;\r
-\r
-FreeStashBuffer:\r
- FreePages (StashBuffer, Pages);\r
- return Status;\r
-}\r
-\r
-/**\r
- Frees memory that was allocated with AllocateBuffer().\r
-\r
- @param This The protocol instance pointer.\r
- @param Pages The number of pages to free.\r
- @param HostAddress The base system memory address of the allocated\r
- range.\r
-\r
- @retval EFI_SUCCESS The requested memory pages were freed.\r
- @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and\r
- Pages was not allocated with AllocateBuffer().\r
-\r
-**/\r
-EFI_STATUS\r
-EFIAPI\r
-IoMmuFreeBuffer (\r
- IN EDKII_IOMMU_PROTOCOL *This,\r
- IN UINTN Pages,\r
- IN VOID *HostAddress\r
- )\r
-{\r
- UINTN CommonBufferPages;\r
- COMMON_BUFFER_HEADER *CommonBufferHeader;\r
-\r
- DEBUG ((\r
- DEBUG_VERBOSE,\r
- "%a: Host=0x%p Pages=0x%Lx\n",\r
- __FUNCTION__,\r
- HostAddress,\r
- (UINT64)Pages\r
- ));\r
-\r
- CommonBufferPages = Pages + 1;\r
- CommonBufferHeader = (COMMON_BUFFER_HEADER *)(\r
- (UINTN)HostAddress - EFI_PAGE_SIZE\r
- );\r
-\r
- //\r
- // Check the signature.\r
- //\r
- ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG);\r
- if (CommonBufferHeader->Signature != COMMON_BUFFER_SIG) {\r
- return EFI_INVALID_PARAMETER;\r
- }\r
-\r
- //\r
- // Free the stash buffer. This buffer was always encrypted, so no need to\r
- // zero it.\r
- //\r
- FreePages (CommonBufferHeader->StashBuffer, Pages);\r
-\r
- //\r
- // Release the common buffer itself. Unmap() has re-encrypted it in-place, so\r
- // no need to zero it.\r
- //\r
- return IoMmuFreeCommonBuffer (CommonBufferHeader, CommonBufferPages);\r
-}\r
-\r
-/**\r
- Set IOMMU attribute for a system memory.\r
-\r
- If the IOMMU protocol exists, the system memory cannot be used\r
- for DMA by default.\r
-\r
- When a device requests a DMA access for a system memory,\r
- the device driver need use SetAttribute() to update the IOMMU\r
- attribute to request DMA access (read and/or write).\r
-\r
- The DeviceHandle is used to identify which device submits the request.\r
- The IOMMU implementation need translate the device path to an IOMMU device\r
- ID, and set IOMMU hardware register accordingly.\r
- 1) DeviceHandle can be a standard PCI device.\r
- The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ.\r
- The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE.\r
- The memory for BusMasterCommonBuffer need set\r
- EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE.\r
- After the memory is used, the memory need set 0 to keep it being\r
- protected.\r
- 2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc).\r
- The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or\r
- EDKII_IOMMU_ACCESS_WRITE.\r
-\r
- @param[in] This The protocol instance pointer.\r
- @param[in] DeviceHandle The device who initiates the DMA access\r
- request.\r
- @param[in] Mapping The mapping value returned from Map().\r
- @param[in] IoMmuAccess The IOMMU access.\r
-\r
- @retval EFI_SUCCESS The IoMmuAccess is set for the memory range\r
- specified by DeviceAddress and Length.\r
- @retval EFI_INVALID_PARAMETER DeviceHandle is an invalid handle.\r
- @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by\r
- Map().\r
- @retval EFI_INVALID_PARAMETER IoMmuAccess specified an illegal combination\r
- of access.\r
- @retval EFI_UNSUPPORTED DeviceHandle is unknown by the IOMMU.\r
- @retval EFI_UNSUPPORTED The bit mask of IoMmuAccess is not supported\r
- by the IOMMU.\r
- @retval EFI_UNSUPPORTED The IOMMU does not support the memory range\r
- specified by Mapping.\r
- @retval EFI_OUT_OF_RESOURCES There are not enough resources available to\r
- modify the IOMMU access.\r
- @retval EFI_DEVICE_ERROR The IOMMU device reported an error while\r
- attempting the operation.\r
-\r
-**/\r
-EFI_STATUS\r
-EFIAPI\r
-IoMmuSetAttribute (\r
- IN EDKII_IOMMU_PROTOCOL *This,\r
- IN EFI_HANDLE DeviceHandle,\r
- IN VOID *Mapping,\r
- IN UINT64 IoMmuAccess\r
- )\r
-{\r
- return EFI_UNSUPPORTED;\r
-}\r
-\r
-EDKII_IOMMU_PROTOCOL mIoMmu = {\r
- EDKII_IOMMU_PROTOCOL_REVISION,\r
- IoMmuSetAttribute,\r
- IoMmuMap,\r
- IoMmuUnmap,\r
- IoMmuAllocateBuffer,\r
- IoMmuFreeBuffer,\r
-};\r
-\r
-/**\r
- Notification function that is queued when gBS->ExitBootServices() signals the\r
- EFI_EVENT_GROUP_EXIT_BOOT_SERVICES event group. This function signals another\r
- event, received as Context, and returns.\r
-\r
- Signaling an event in this context is safe. The UEFI spec allows\r
- gBS->SignalEvent() to return EFI_SUCCESS only; EFI_OUT_OF_RESOURCES is not\r
- listed, hence memory is not allocated. The edk2 implementation also does not\r
- release memory (and we only have to care about the edk2 implementation\r
- because EDKII_IOMMU_PROTOCOL is edk2-specific anyway).\r
-\r
- @param[in] Event Event whose notification function is being invoked.\r
- Event is permitted to request the queueing of this\r
- function at TPL_CALLBACK or TPL_NOTIFY task\r
- priority level.\r
-\r
- @param[in] EventToSignal Identifies the EFI_EVENT to signal. EventToSignal\r
- is permitted to request the queueing of its\r
- notification function only at TPL_CALLBACK level.\r
-**/\r
-STATIC\r
-VOID\r
-EFIAPI\r
-IoMmuExitBoot (\r
- IN EFI_EVENT Event,\r
- IN VOID *EventToSignal\r
- )\r
-{\r
- //\r
- // (1) The NotifyFunctions of all the events in\r
- // EFI_EVENT_GROUP_EXIT_BOOT_SERVICES will have been queued before\r
- // IoMmuExitBoot() is entered.\r
- //\r
- // (2) IoMmuExitBoot() is executing minimally at TPL_CALLBACK.\r
- //\r
- // (3) IoMmuExitBoot() has been queued in unspecified order relative to the\r
- // NotifyFunctions of all the other events in\r
- // EFI_EVENT_GROUP_EXIT_BOOT_SERVICES whose NotifyTpl is the same as\r
- // Event's.\r
- //\r
- // Consequences:\r
- //\r
- // - If Event's NotifyTpl is TPL_CALLBACK, then some other NotifyFunctions\r
- // queued at TPL_CALLBACK may be invoked after IoMmuExitBoot() returns.\r
- //\r
- // - If Event's NotifyTpl is TPL_NOTIFY, then some other NotifyFunctions\r
- // queued at TPL_NOTIFY may be invoked after IoMmuExitBoot() returns; plus\r
- // *all* NotifyFunctions queued at TPL_CALLBACK will be invoked strictly\r
- // after all NotifyFunctions queued at TPL_NOTIFY, including\r
- // IoMmuExitBoot(), have been invoked.\r
- //\r
- // - By signaling EventToSignal here, whose NotifyTpl is TPL_CALLBACK, we\r
- // queue EventToSignal's NotifyFunction after the NotifyFunctions of *all*\r
- // events in EFI_EVENT_GROUP_EXIT_BOOT_SERVICES.\r
- //\r
- DEBUG ((DEBUG_VERBOSE, "%a\n", __FUNCTION__));\r
- gBS->SignalEvent (EventToSignal);\r
-}\r
-\r
-/**\r
- Notification function that is queued after the notification functions of all\r
- events in the EFI_EVENT_GROUP_EXIT_BOOT_SERVICES event group. The same memory\r
- map restrictions apply.\r
-\r
- This function unmaps all currently existing IOMMU mappings.\r
-\r
- @param[in] Event Event whose notification function is being invoked. Event\r
- is permitted to request the queueing of this function\r
- only at TPL_CALLBACK task priority level.\r
-\r
- @param[in] Context Ignored.\r
-**/\r
-STATIC\r
-VOID\r
-EFIAPI\r
-IoMmuUnmapAllMappings (\r
- IN EFI_EVENT Event,\r
- IN VOID *Context\r
- )\r
-{\r
- LIST_ENTRY *Node;\r
- LIST_ENTRY *NextNode;\r
- MAP_INFO *MapInfo;\r
-\r
- DEBUG ((DEBUG_VERBOSE, "%a\n", __FUNCTION__));\r
-\r
- //\r
- // All drivers that had set up IOMMU mappings have halted their respective\r
- // controllers by now; tear down the mappings.\r
- //\r
- for (Node = GetFirstNode (&mMapInfos); Node != &mMapInfos; Node = NextNode) {\r
- NextNode = GetNextNode (&mMapInfos, Node);\r
- MapInfo = CR (Node, MAP_INFO, Link, MAP_INFO_SIG);\r
- IoMmuUnmapWorker (\r
- &mIoMmu, // This\r
- MapInfo, // Mapping\r
- TRUE // MemoryMapLocked\r
- );\r
- }\r
-\r
- //\r
- // Release the reserved shared memory as well.\r
- //\r
- IoMmuReleaseReservedSharedMem (TRUE);\r
-}\r
-\r
-/**\r
- Initialize Iommu Protocol.\r
-\r
-**/\r
-EFI_STATUS\r
-EFIAPI\r
-InstallIoMmuProtocol (\r
- VOID\r
- )\r
-{\r
- EFI_STATUS Status;\r
- EFI_EVENT UnmapAllMappingsEvent;\r
- EFI_EVENT ExitBootEvent;\r
- EFI_HANDLE Handle;\r
-\r
- //\r
- // Create the "late" event whose notification function will tear down all\r
- // left-over IOMMU mappings.\r
- //\r
- Status = gBS->CreateEvent (\r
- EVT_NOTIFY_SIGNAL, // Type\r
- TPL_CALLBACK, // NotifyTpl\r
- IoMmuUnmapAllMappings, // NotifyFunction\r
- NULL, // NotifyContext\r
- &UnmapAllMappingsEvent // Event\r
- );\r
- if (EFI_ERROR (Status)) {\r
- return Status;\r
- }\r
-\r
- //\r
- // Create the event whose notification function will be queued by\r
- // gBS->ExitBootServices() and will signal the event created above.\r
- //\r
- Status = gBS->CreateEvent (\r
- EVT_SIGNAL_EXIT_BOOT_SERVICES, // Type\r
- TPL_CALLBACK, // NotifyTpl\r
- IoMmuExitBoot, // NotifyFunction\r
- UnmapAllMappingsEvent, // NotifyContext\r
- &ExitBootEvent // Event\r
- );\r
- if (EFI_ERROR (Status)) {\r
- goto CloseUnmapAllMappingsEvent;\r
- }\r
-\r
- Handle = NULL;\r
- Status = gBS->InstallMultipleProtocolInterfaces (\r
- &Handle,\r
- &gEdkiiIoMmuProtocolGuid,\r
- &mIoMmu,\r
- NULL\r
- );\r
- if (EFI_ERROR (Status)) {\r
- goto CloseExitBootEvent;\r
- }\r
-\r
- //\r
- // Currently only Tdx guest support Reserved shared memory for DMA operation.\r
- //\r
- if (CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr))) {\r
- mReservedSharedMemSupported = TRUE;\r
- Status = IoMmuInitReservedSharedMem ();\r
- if (EFI_ERROR (Status)) {\r
- mReservedSharedMemSupported = FALSE;\r
- } else {\r
- DEBUG ((DEBUG_INFO, "%a: Feature of reserved memory for DMA is supported.\n", __FUNCTION__));\r
- }\r
- }\r
-\r
- return EFI_SUCCESS;\r
-\r
-CloseExitBootEvent:\r
- gBS->CloseEvent (ExitBootEvent);\r
-\r
-CloseUnmapAllMappingsEvent:\r
- gBS->CloseEvent (UnmapAllMappingsEvent);\r
-\r
- return Status;\r
-}\r
+++ /dev/null
-/** @file\r
-\r
- The protocol provides support to allocate, free, map and umap a DMA buffer\r
- for bus master (e.g PciHostBridge). When SEV is enabled, the DMA operations\r
- must be performed on unencrypted buffer hence protocol clear the encryption\r
- bit from the DMA buffer.\r
-\r
- Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
- Copyright (c) 2017, AMD Inc. All rights reserved.<BR>\r
- (C) Copyright 2017 Hewlett Packard Enterprise Development LP<BR>\r
- SPDX-License-Identifier: BSD-2-Clause-Patent\r
-\r
-**/\r
-\r
-#ifndef _AMD_SEV_IOMMU_H_\r
-#define _AMD_SEV_IOMMU_H_\r
-\r
-#include <Protocol/IoMmu.h>\r
-\r
-#include <Library/BaseLib.h>\r
-#include <Library/BaseMemoryLib.h>\r
-#include <Library/DebugLib.h>\r
-#include <Library/MemEncryptSevLib.h>\r
-#include <Library/MemEncryptTdxLib.h>\r
-#include <Library/MemoryAllocationLib.h>\r
-#include <Library/UefiBootServicesTableLib.h>\r
-\r
-/**\r
- Install IOMMU protocol to provide the DMA support for PciHostBridge.\r
-\r
-**/\r
-EFI_STATUS\r
-EFIAPI\r
-InstallIoMmuProtocol (\r
- VOID\r
- );\r
-\r
-#endif\r
--- /dev/null
+/** @file\r
+\r
+ The protocol provides support to allocate, free, map and umap a DMA buffer\r
+ for bus master (e.g PciHostBridge). When SEV or TDX is enabled, the DMA\r
+ operations must be performed on unencrypted buffer hence we use a bounce\r
+ buffer to map the guest buffer into an unencrypted DMA buffer.\r
+\r
+ Copyright (c) 2017, AMD Inc. All rights reserved.<BR>\r
+ Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
+\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#include <Library/PcdLib.h>\r
+#include <ConfidentialComputingGuestAttr.h>\r
+#include "CcIoMmu.h"\r
+#include "IoMmuInternal.h"\r
+\r
+//\r
+// List of the MAP_INFO structures that have been set up by IoMmuMap() and not\r
+// yet torn down by IoMmuUnmap(). The list represents the full set of mappings\r
+// currently in effect.\r
+//\r
+STATIC LIST_ENTRY mMapInfos = INITIALIZE_LIST_HEAD_VARIABLE (mMapInfos);\r
+\r
+//\r
+// Indicate if the feature of reserved memory is supported in DMA operation.\r
+//\r
+BOOLEAN mReservedSharedMemSupported = FALSE;\r
+\r
+//\r
+// ASCII names for EDKII_IOMMU_OPERATION constants, for debug logging.\r
+//\r
+STATIC CONST CHAR8 *CONST\r
+mBusMasterOperationName[EdkiiIoMmuOperationMaximum] = {\r
+ "Read",\r
+ "Write",\r
+ "CommonBuffer",\r
+ "Read64",\r
+ "Write64",\r
+ "CommonBuffer64"\r
+};\r
+\r
+/**\r
+ Provides the controller-specific addresses required to access system memory\r
+ from a DMA bus master. On SEV/TDX guest, the DMA operations must be performed on\r
+ shared buffer hence we allocate a bounce buffer to map the HostAddress to a\r
+ DeviceAddress. The Encryption attribute is removed from the DeviceAddress\r
+ buffer.\r
+\r
+ @param This The protocol instance pointer.\r
+ @param Operation Indicates if the bus master is going to read or\r
+ write to system memory.\r
+ @param HostAddress The system memory address to map to the PCI\r
+ controller.\r
+ @param NumberOfBytes On input the number of bytes to map. On output\r
+ the number of bytes that were mapped.\r
+ @param DeviceAddress The resulting map address for the bus master\r
+ PCI controller to use to access the hosts\r
+ HostAddress.\r
+ @param Mapping A resulting value to pass to Unmap().\r
+\r
+ @retval EFI_SUCCESS The range was mapped for the returned\r
+ NumberOfBytes.\r
+ @retval EFI_UNSUPPORTED The HostAddress cannot be mapped as a common\r
+ buffer.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a\r
+ lack of resources.\r
+ @retval EFI_DEVICE_ERROR The system hardware could not map the requested\r
+ address.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IoMmuMap (\r
+ IN EDKII_IOMMU_PROTOCOL *This,\r
+ IN EDKII_IOMMU_OPERATION Operation,\r
+ IN VOID *HostAddress,\r
+ IN OUT UINTN *NumberOfBytes,\r
+ OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,\r
+ OUT VOID **Mapping\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ MAP_INFO *MapInfo;\r
+ EFI_ALLOCATE_TYPE AllocateType;\r
+ COMMON_BUFFER_HEADER *CommonBufferHeader;\r
+ VOID *DecryptionSource;\r
+\r
+ DEBUG ((\r
+ DEBUG_VERBOSE,\r
+ "%a: Operation=%a Host=0x%p Bytes=0x%Lx\n",\r
+ __FUNCTION__,\r
+ ((Operation >= 0 &&\r
+ Operation < ARRAY_SIZE (mBusMasterOperationName)) ?\r
+ mBusMasterOperationName[Operation] :\r
+ "Invalid"),\r
+ HostAddress,\r
+ (UINT64)((NumberOfBytes == NULL) ? 0 : *NumberOfBytes)\r
+ ));\r
+\r
+ if ((HostAddress == NULL) || (NumberOfBytes == NULL) || (DeviceAddress == NULL) ||\r
+ (Mapping == NULL))\r
+ {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ //\r
+ // Allocate a MAP_INFO structure to remember the mapping when Unmap() is\r
+ // called later.\r
+ //\r
+ MapInfo = AllocatePool (sizeof (MAP_INFO));\r
+ if (MapInfo == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Failed;\r
+ }\r
+\r
+ //\r
+ // Initialize the MAP_INFO structure, except the PlainTextAddress field\r
+ //\r
+ ZeroMem (&MapInfo->Link, sizeof MapInfo->Link);\r
+ MapInfo->Signature = MAP_INFO_SIG;\r
+ MapInfo->Operation = Operation;\r
+ MapInfo->NumberOfBytes = *NumberOfBytes;\r
+ MapInfo->NumberOfPages = EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes);\r
+ MapInfo->CryptedAddress = (UINTN)HostAddress;\r
+ MapInfo->ReservedMemBitmap = 0;\r
+\r
+ //\r
+ // In the switch statement below, we point "MapInfo->PlainTextAddress" to the\r
+ // plaintext buffer, according to Operation. We also set "DecryptionSource".\r
+ //\r
+ MapInfo->PlainTextAddress = MAX_ADDRESS;\r
+ AllocateType = AllocateAnyPages;\r
+ DecryptionSource = (VOID *)(UINTN)MapInfo->CryptedAddress;\r
+ switch (Operation) {\r
+ //\r
+ // For BusMasterRead[64] and BusMasterWrite[64] operations, a bounce buffer\r
+ // is necessary regardless of whether the original (crypted) buffer crosses\r
+ // the 4GB limit or not -- we have to allocate a separate plaintext buffer.\r
+ // The only variable is whether the plaintext buffer should be under 4GB.\r
+ //\r
+ case EdkiiIoMmuOperationBusMasterRead:\r
+ case EdkiiIoMmuOperationBusMasterWrite:\r
+ MapInfo->PlainTextAddress = BASE_4GB - 1;\r
+ AllocateType = AllocateMaxAddress;\r
+ //\r
+ // fall through\r
+ //\r
+ case EdkiiIoMmuOperationBusMasterRead64:\r
+ case EdkiiIoMmuOperationBusMasterWrite64:\r
+ //\r
+ // Allocate the implicit plaintext bounce buffer.\r
+ //\r
+ Status = IoMmuAllocateBounceBuffer (\r
+ AllocateType,\r
+ EfiBootServicesData,\r
+ MapInfo\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto FreeMapInfo;\r
+ }\r
+\r
+ break;\r
+\r
+ //\r
+ // For BusMasterCommonBuffer[64] operations, a to-be-plaintext buffer and a\r
+ // stash buffer (for in-place decryption) have been allocated already, with\r
+ // AllocateBuffer(). We only check whether the address of the to-be-plaintext\r
+ // buffer is low enough for the requested operation.\r
+ //\r
+ case EdkiiIoMmuOperationBusMasterCommonBuffer:\r
+ if ((MapInfo->CryptedAddress > BASE_4GB) ||\r
+ (EFI_PAGES_TO_SIZE (MapInfo->NumberOfPages) >\r
+ BASE_4GB - MapInfo->CryptedAddress))\r
+ {\r
+ //\r
+ // CommonBuffer operations cannot be remapped. If the common buffer is\r
+ // above 4GB, then it is not possible to generate a mapping, so return an\r
+ // error.\r
+ //\r
+ Status = EFI_UNSUPPORTED;\r
+ goto FreeMapInfo;\r
+ }\r
+\r
+ //\r
+ // fall through\r
+ //\r
+ case EdkiiIoMmuOperationBusMasterCommonBuffer64:\r
+ //\r
+ // The buffer at MapInfo->CryptedAddress comes from AllocateBuffer().\r
+ //\r
+ MapInfo->PlainTextAddress = MapInfo->CryptedAddress;\r
+ //\r
+ // Stash the crypted data.\r
+ //\r
+ CommonBufferHeader = (COMMON_BUFFER_HEADER *)(\r
+ (UINTN)MapInfo->CryptedAddress - EFI_PAGE_SIZE\r
+ );\r
+ ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG);\r
+ CopyMem (\r
+ CommonBufferHeader->StashBuffer,\r
+ (VOID *)(UINTN)MapInfo->CryptedAddress,\r
+ MapInfo->NumberOfBytes\r
+ );\r
+ //\r
+ // Point "DecryptionSource" to the stash buffer so that we decrypt\r
+ // it to the original location, after the switch statement.\r
+ //\r
+ DecryptionSource = CommonBufferHeader->StashBuffer;\r
+ MapInfo->ReservedMemBitmap = CommonBufferHeader->ReservedMemBitmap;\r
+ break;\r
+\r
+ default:\r
+ //\r
+ // Operation is invalid\r
+ //\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto FreeMapInfo;\r
+ }\r
+\r
+ if (CC_GUEST_IS_SEV (PcdGet64 (PcdConfidentialComputingGuestAttr))) {\r
+ //\r
+ // Clear the memory encryption mask on the plaintext buffer.\r
+ //\r
+ Status = MemEncryptSevClearPageEncMask (\r
+ 0,\r
+ MapInfo->PlainTextAddress,\r
+ MapInfo->NumberOfPages\r
+ );\r
+ } else if (CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr))) {\r
+ //\r
+ // Set the memory shared bit.\r
+ // If MapInfo->ReservedMemBitmap is 0, it means the bounce buffer is not allocated\r
+ // from the pre-allocated shared memory, so it must be converted to shared memory here.\r
+ //\r
+ if (MapInfo->ReservedMemBitmap == 0) {\r
+ Status = MemEncryptTdxSetPageSharedBit (\r
+ 0,\r
+ MapInfo->PlainTextAddress,\r
+ MapInfo->NumberOfPages\r
+ );\r
+ }\r
+ } else {\r
+ ASSERT (FALSE);\r
+ }\r
+\r
+ ASSERT_EFI_ERROR (Status);\r
+ if (EFI_ERROR (Status)) {\r
+ CpuDeadLoop ();\r
+ }\r
+\r
+ //\r
+ // If this is a read operation from the Bus Master's point of view,\r
+ // then copy the contents of the real buffer into the mapped buffer\r
+ // so the Bus Master can read the contents of the real buffer.\r
+ //\r
+ // For BusMasterCommonBuffer[64] operations, the CopyMem() below will decrypt\r
+ // the original data (from the stash buffer) back to the original location.\r
+ //\r
+ if ((Operation == EdkiiIoMmuOperationBusMasterRead) ||\r
+ (Operation == EdkiiIoMmuOperationBusMasterRead64) ||\r
+ (Operation == EdkiiIoMmuOperationBusMasterCommonBuffer) ||\r
+ (Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64))\r
+ {\r
+ CopyMem (\r
+ (VOID *)(UINTN)MapInfo->PlainTextAddress,\r
+ DecryptionSource,\r
+ MapInfo->NumberOfBytes\r
+ );\r
+ }\r
+\r
+ //\r
+ // Track all MAP_INFO structures.\r
+ //\r
+ InsertHeadList (&mMapInfos, &MapInfo->Link);\r
+ //\r
+ // Populate output parameters.\r
+ //\r
+ *DeviceAddress = MapInfo->PlainTextAddress;\r
+ *Mapping = MapInfo;\r
+\r
+ DEBUG ((\r
+ DEBUG_VERBOSE,\r
+ "%a: Mapping=0x%p Device(PlainText)=0x%Lx Crypted=0x%Lx Pages=0x%Lx, ReservedMemBitmap=0x%Lx\n",\r
+ __FUNCTION__,\r
+ MapInfo,\r
+ MapInfo->PlainTextAddress,\r
+ MapInfo->CryptedAddress,\r
+ (UINT64)MapInfo->NumberOfPages,\r
+ MapInfo->ReservedMemBitmap\r
+ ));\r
+\r
+ return EFI_SUCCESS;\r
+\r
+FreeMapInfo:\r
+ FreePool (MapInfo);\r
+\r
+Failed:\r
+ *NumberOfBytes = 0;\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Completes the Map() operation and releases any corresponding resources.\r
+\r
+ This is an internal worker function that only extends the Map() API with\r
+ the MemoryMapLocked parameter.\r
+\r
+ @param This The protocol instance pointer.\r
+ @param Mapping The mapping value returned from Map().\r
+ @param MemoryMapLocked The function is executing on the stack of\r
+ gBS->ExitBootServices(); changes to the UEFI\r
+ memory map are forbidden.\r
+\r
+ @retval EFI_SUCCESS The range was unmapped.\r
+ @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by\r
+ Map().\r
+ @retval EFI_DEVICE_ERROR The data was not committed to the target system\r
+ memory.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+IoMmuUnmapWorker (\r
+ IN EDKII_IOMMU_PROTOCOL *This,\r
+ IN VOID *Mapping,\r
+ IN BOOLEAN MemoryMapLocked\r
+ )\r
+{\r
+ MAP_INFO *MapInfo;\r
+ EFI_STATUS Status;\r
+ COMMON_BUFFER_HEADER *CommonBufferHeader;\r
+ VOID *EncryptionTarget;\r
+\r
+ DEBUG ((\r
+ DEBUG_VERBOSE,\r
+ "%a: Mapping=0x%p MemoryMapLocked=%d\n",\r
+ __FUNCTION__,\r
+ Mapping,\r
+ MemoryMapLocked\r
+ ));\r
+\r
+ if (Mapping == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ MapInfo = (MAP_INFO *)Mapping;\r
+ Status = EFI_SUCCESS;\r
+ //\r
+ // set CommonBufferHeader to suppress incorrect compiler/analyzer warnings\r
+ //\r
+ CommonBufferHeader = NULL;\r
+\r
+ //\r
+ // For BusMasterWrite[64] operations and BusMasterCommonBuffer[64] operations\r
+ // we have to encrypt the results, ultimately to the original place (i.e.,\r
+ // "MapInfo->CryptedAddress").\r
+ //\r
+ // For BusMasterCommonBuffer[64] operations however, this encryption has to\r
+ // land in-place, so divert the encryption to the stash buffer first.\r
+ //\r
+ EncryptionTarget = (VOID *)(UINTN)MapInfo->CryptedAddress;\r
+\r
+ switch (MapInfo->Operation) {\r
+ case EdkiiIoMmuOperationBusMasterCommonBuffer:\r
+ case EdkiiIoMmuOperationBusMasterCommonBuffer64:\r
+ ASSERT (MapInfo->PlainTextAddress == MapInfo->CryptedAddress);\r
+\r
+ CommonBufferHeader = (COMMON_BUFFER_HEADER *)(\r
+ (UINTN)MapInfo->PlainTextAddress - EFI_PAGE_SIZE\r
+ );\r
+ ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG);\r
+ EncryptionTarget = CommonBufferHeader->StashBuffer;\r
+ //\r
+ // fall through\r
+ //\r
+\r
+ case EdkiiIoMmuOperationBusMasterWrite:\r
+ case EdkiiIoMmuOperationBusMasterWrite64:\r
+ CopyMem (\r
+ EncryptionTarget,\r
+ (VOID *)(UINTN)MapInfo->PlainTextAddress,\r
+ MapInfo->NumberOfBytes\r
+ );\r
+ break;\r
+\r
+ default:\r
+ //\r
+ // nothing to encrypt after BusMasterRead[64] operations\r
+ //\r
+ break;\r
+ }\r
+\r
+ if (CC_GUEST_IS_SEV (PcdGet64 (PcdConfidentialComputingGuestAttr))) {\r
+ //\r
+ // Restore the memory encryption mask on the area we used to hold the\r
+ // plaintext.\r
+ //\r
+ Status = MemEncryptSevSetPageEncMask (\r
+ 0,\r
+ MapInfo->PlainTextAddress,\r
+ MapInfo->NumberOfPages\r
+ );\r
+ } else if (CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr))) {\r
+ //\r
+ // Restore the memory shared bit mask on the area we used to hold the\r
+ // plaintext.\r
+ //\r
+ if (MapInfo->ReservedMemBitmap == 0) {\r
+ Status = MemEncryptTdxClearPageSharedBit (\r
+ 0,\r
+ MapInfo->PlainTextAddress,\r
+ MapInfo->NumberOfPages\r
+ );\r
+ }\r
+ } else {\r
+ ASSERT (FALSE);\r
+ }\r
+\r
+ ASSERT_EFI_ERROR (Status);\r
+ if (EFI_ERROR (Status)) {\r
+ CpuDeadLoop ();\r
+ }\r
+\r
+ //\r
+ // For BusMasterCommonBuffer[64] operations, copy the stashed data to the\r
+ // original (now encrypted) location.\r
+ //\r
+ // For all other operations, fill the late bounce buffer (which existed as\r
+ // plaintext at some point) with zeros, and then release it (unless the UEFI\r
+ // memory map is locked).\r
+ //\r
+ if ((MapInfo->Operation == EdkiiIoMmuOperationBusMasterCommonBuffer) ||\r
+ (MapInfo->Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64))\r
+ {\r
+ CopyMem (\r
+ (VOID *)(UINTN)MapInfo->CryptedAddress,\r
+ CommonBufferHeader->StashBuffer,\r
+ MapInfo->NumberOfBytes\r
+ );\r
+ } else {\r
+ ZeroMem (\r
+ (VOID *)(UINTN)MapInfo->PlainTextAddress,\r
+ EFI_PAGES_TO_SIZE (MapInfo->NumberOfPages)\r
+ );\r
+\r
+ if (!MemoryMapLocked) {\r
+ IoMmuFreeBounceBuffer (MapInfo);\r
+ }\r
+ }\r
+\r
+ //\r
+ // Forget the MAP_INFO structure, then free it (unless the UEFI memory map is\r
+ // locked).\r
+ //\r
+ RemoveEntryList (&MapInfo->Link);\r
+ if (!MemoryMapLocked) {\r
+ FreePool (MapInfo);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Completes the Map() operation and releases any corresponding resources.\r
+\r
+ @param This The protocol instance pointer.\r
+ @param Mapping The mapping value returned from Map().\r
+\r
+ @retval EFI_SUCCESS The range was unmapped.\r
+ @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by\r
+ Map().\r
+ @retval EFI_DEVICE_ERROR The data was not committed to the target system\r
+ memory.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IoMmuUnmap (\r
+ IN EDKII_IOMMU_PROTOCOL *This,\r
+ IN VOID *Mapping\r
+ )\r
+{\r
+ return IoMmuUnmapWorker (\r
+ This,\r
+ Mapping,\r
+ FALSE // MemoryMapLocked\r
+ );\r
+}\r
+\r
+/**\r
+ Allocates pages that are suitable for an OperationBusMasterCommonBuffer or\r
+ OperationBusMasterCommonBuffer64 mapping.\r
+\r
+ @param This The protocol instance pointer.\r
+ @param Type This parameter is not used and must be ignored.\r
+ @param MemoryType The type of memory to allocate,\r
+ EfiBootServicesData or EfiRuntimeServicesData.\r
+ @param Pages The number of pages to allocate.\r
+ @param HostAddress A pointer to store the base system memory\r
+ address of the allocated range.\r
+ @param Attributes The requested bit mask of attributes for the\r
+ allocated range.\r
+\r
+ @retval EFI_SUCCESS The requested memory pages were allocated.\r
+ @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal\r
+ attribute bits are MEMORY_WRITE_COMBINE and\r
+ MEMORY_CACHED.\r
+ @retval EFI_INVALID_PARAMETER One or more parameters are invalid.\r
+ @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IoMmuAllocateBuffer (\r
+ IN EDKII_IOMMU_PROTOCOL *This,\r
+ IN EFI_ALLOCATE_TYPE Type,\r
+ IN EFI_MEMORY_TYPE MemoryType,\r
+ IN UINTN Pages,\r
+ IN OUT VOID **HostAddress,\r
+ IN UINT64 Attributes\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_PHYSICAL_ADDRESS PhysicalAddress;\r
+ VOID *StashBuffer;\r
+ UINTN CommonBufferPages;\r
+ COMMON_BUFFER_HEADER *CommonBufferHeader;\r
+ UINT32 ReservedMemBitmap;\r
+\r
+ DEBUG ((\r
+ DEBUG_VERBOSE,\r
+ "%a: MemoryType=%u Pages=0x%Lx Attributes=0x%Lx\n",\r
+ __FUNCTION__,\r
+ (UINT32)MemoryType,\r
+ (UINT64)Pages,\r
+ Attributes\r
+ ));\r
+\r
+ //\r
+ // Validate Attributes\r
+ //\r
+ if ((Attributes & EDKII_IOMMU_ATTRIBUTE_INVALID_FOR_ALLOCATE_BUFFER) != 0) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // Check for invalid inputs\r
+ //\r
+ if (HostAddress == NULL) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // The only valid memory types are EfiBootServicesData and\r
+ // EfiRuntimeServicesData\r
+ //\r
+ if ((MemoryType != EfiBootServicesData) &&\r
+ (MemoryType != EfiRuntimeServicesData))\r
+ {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // We'll need a header page for the COMMON_BUFFER_HEADER structure.\r
+ //\r
+ if (Pages > MAX_UINTN - 1) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ CommonBufferPages = Pages + 1;\r
+\r
+ //\r
+ // Allocate the stash in EfiBootServicesData type memory.\r
+ //\r
+ // Map() will temporarily save encrypted data in the stash for\r
+ // BusMasterCommonBuffer[64] operations, so the data can be decrypted to the\r
+ // original location.\r
+ //\r
+ // Unmap() will temporarily save plaintext data in the stash for\r
+ // BusMasterCommonBuffer[64] operations, so the data can be encrypted to the\r
+ // original location.\r
+ //\r
+ // StashBuffer always resides in encrypted memory.\r
+ //\r
+ StashBuffer = AllocatePages (Pages);\r
+ if (StashBuffer == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ PhysicalAddress = (UINTN)-1;\r
+ if ((Attributes & EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0) {\r
+ //\r
+ // Limit allocations to memory below 4GB\r
+ //\r
+ PhysicalAddress = SIZE_4GB - 1;\r
+ }\r
+\r
+ Status = IoMmuAllocateCommonBuffer (\r
+ MemoryType,\r
+ CommonBufferPages,\r
+ &PhysicalAddress,\r
+ &ReservedMemBitmap\r
+ );\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ goto FreeStashBuffer;\r
+ }\r
+\r
+ CommonBufferHeader = (VOID *)(UINTN)PhysicalAddress;\r
+ PhysicalAddress += EFI_PAGE_SIZE;\r
+\r
+ CommonBufferHeader->Signature = COMMON_BUFFER_SIG;\r
+ CommonBufferHeader->StashBuffer = StashBuffer;\r
+ CommonBufferHeader->ReservedMemBitmap = ReservedMemBitmap;\r
+\r
+ *HostAddress = (VOID *)(UINTN)PhysicalAddress;\r
+\r
+ DEBUG ((\r
+ DEBUG_VERBOSE,\r
+ "%a: Host=0x%Lx Stash=0x%p\n",\r
+ __FUNCTION__,\r
+ PhysicalAddress,\r
+ StashBuffer\r
+ ));\r
+ return EFI_SUCCESS;\r
+\r
+FreeStashBuffer:\r
+ FreePages (StashBuffer, Pages);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Frees memory that was allocated with AllocateBuffer().\r
+\r
+ @param This The protocol instance pointer.\r
+ @param Pages The number of pages to free.\r
+ @param HostAddress The base system memory address of the allocated\r
+ range.\r
+\r
+ @retval EFI_SUCCESS The requested memory pages were freed.\r
+ @retval EFI_INVALID_PARAMETER The memory range specified by HostAddress and\r
+ Pages was not allocated with AllocateBuffer().\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IoMmuFreeBuffer (\r
+ IN EDKII_IOMMU_PROTOCOL *This,\r
+ IN UINTN Pages,\r
+ IN VOID *HostAddress\r
+ )\r
+{\r
+ UINTN CommonBufferPages;\r
+ COMMON_BUFFER_HEADER *CommonBufferHeader;\r
+\r
+ DEBUG ((\r
+ DEBUG_VERBOSE,\r
+ "%a: Host=0x%p Pages=0x%Lx\n",\r
+ __FUNCTION__,\r
+ HostAddress,\r
+ (UINT64)Pages\r
+ ));\r
+\r
+ CommonBufferPages = Pages + 1;\r
+ CommonBufferHeader = (COMMON_BUFFER_HEADER *)(\r
+ (UINTN)HostAddress - EFI_PAGE_SIZE\r
+ );\r
+\r
+ //\r
+ // Check the signature.\r
+ //\r
+ ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG);\r
+ if (CommonBufferHeader->Signature != COMMON_BUFFER_SIG) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Free the stash buffer. This buffer was always encrypted, so no need to\r
+ // zero it.\r
+ //\r
+ FreePages (CommonBufferHeader->StashBuffer, Pages);\r
+\r
+ //\r
+ // Release the common buffer itself. Unmap() has re-encrypted it in-place, so\r
+ // no need to zero it.\r
+ //\r
+ return IoMmuFreeCommonBuffer (CommonBufferHeader, CommonBufferPages);\r
+}\r
+\r
+/**\r
+ Set IOMMU attribute for a system memory.\r
+\r
+ If the IOMMU protocol exists, the system memory cannot be used\r
+ for DMA by default.\r
+\r
+ When a device requests a DMA access for a system memory,\r
+ the device driver need use SetAttribute() to update the IOMMU\r
+ attribute to request DMA access (read and/or write).\r
+\r
+ The DeviceHandle is used to identify which device submits the request.\r
+ The IOMMU implementation need translate the device path to an IOMMU device\r
+ ID, and set IOMMU hardware register accordingly.\r
+ 1) DeviceHandle can be a standard PCI device.\r
+ The memory for BusMasterRead need set EDKII_IOMMU_ACCESS_READ.\r
+ The memory for BusMasterWrite need set EDKII_IOMMU_ACCESS_WRITE.\r
+ The memory for BusMasterCommonBuffer need set\r
+ EDKII_IOMMU_ACCESS_READ|EDKII_IOMMU_ACCESS_WRITE.\r
+ After the memory is used, the memory need set 0 to keep it being\r
+ protected.\r
+ 2) DeviceHandle can be an ACPI device (ISA, I2C, SPI, etc).\r
+ The memory for DMA access need set EDKII_IOMMU_ACCESS_READ and/or\r
+ EDKII_IOMMU_ACCESS_WRITE.\r
+\r
+ @param[in] This The protocol instance pointer.\r
+ @param[in] DeviceHandle The device who initiates the DMA access\r
+ request.\r
+ @param[in] Mapping The mapping value returned from Map().\r
+ @param[in] IoMmuAccess The IOMMU access.\r
+\r
+ @retval EFI_SUCCESS The IoMmuAccess is set for the memory range\r
+ specified by DeviceAddress and Length.\r
+ @retval EFI_INVALID_PARAMETER DeviceHandle is an invalid handle.\r
+ @retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by\r
+ Map().\r
+ @retval EFI_INVALID_PARAMETER IoMmuAccess specified an illegal combination\r
+ of access.\r
+ @retval EFI_UNSUPPORTED DeviceHandle is unknown by the IOMMU.\r
+ @retval EFI_UNSUPPORTED The bit mask of IoMmuAccess is not supported\r
+ by the IOMMU.\r
+ @retval EFI_UNSUPPORTED The IOMMU does not support the memory range\r
+ specified by Mapping.\r
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources available to\r
+ modify the IOMMU access.\r
+ @retval EFI_DEVICE_ERROR The IOMMU device reported an error while\r
+ attempting the operation.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IoMmuSetAttribute (\r
+ IN EDKII_IOMMU_PROTOCOL *This,\r
+ IN EFI_HANDLE DeviceHandle,\r
+ IN VOID *Mapping,\r
+ IN UINT64 IoMmuAccess\r
+ )\r
+{\r
+ return EFI_UNSUPPORTED;\r
+}\r
+\r
+EDKII_IOMMU_PROTOCOL mIoMmu = {\r
+ EDKII_IOMMU_PROTOCOL_REVISION,\r
+ IoMmuSetAttribute,\r
+ IoMmuMap,\r
+ IoMmuUnmap,\r
+ IoMmuAllocateBuffer,\r
+ IoMmuFreeBuffer,\r
+};\r
+\r
+/**\r
+ Notification function that is queued when gBS->ExitBootServices() signals the\r
+ EFI_EVENT_GROUP_EXIT_BOOT_SERVICES event group. This function signals another\r
+ event, received as Context, and returns.\r
+\r
+ Signaling an event in this context is safe. The UEFI spec allows\r
+ gBS->SignalEvent() to return EFI_SUCCESS only; EFI_OUT_OF_RESOURCES is not\r
+ listed, hence memory is not allocated. The edk2 implementation also does not\r
+ release memory (and we only have to care about the edk2 implementation\r
+ because EDKII_IOMMU_PROTOCOL is edk2-specific anyway).\r
+\r
+ @param[in] Event Event whose notification function is being invoked.\r
+ Event is permitted to request the queueing of this\r
+ function at TPL_CALLBACK or TPL_NOTIFY task\r
+ priority level.\r
+\r
+ @param[in] EventToSignal Identifies the EFI_EVENT to signal. EventToSignal\r
+ is permitted to request the queueing of its\r
+ notification function only at TPL_CALLBACK level.\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+IoMmuExitBoot (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *EventToSignal\r
+ )\r
+{\r
+ //\r
+ // (1) The NotifyFunctions of all the events in\r
+ // EFI_EVENT_GROUP_EXIT_BOOT_SERVICES will have been queued before\r
+ // IoMmuExitBoot() is entered.\r
+ //\r
+ // (2) IoMmuExitBoot() is executing minimally at TPL_CALLBACK.\r
+ //\r
+ // (3) IoMmuExitBoot() has been queued in unspecified order relative to the\r
+ // NotifyFunctions of all the other events in\r
+ // EFI_EVENT_GROUP_EXIT_BOOT_SERVICES whose NotifyTpl is the same as\r
+ // Event's.\r
+ //\r
+ // Consequences:\r
+ //\r
+ // - If Event's NotifyTpl is TPL_CALLBACK, then some other NotifyFunctions\r
+ // queued at TPL_CALLBACK may be invoked after IoMmuExitBoot() returns.\r
+ //\r
+ // - If Event's NotifyTpl is TPL_NOTIFY, then some other NotifyFunctions\r
+ // queued at TPL_NOTIFY may be invoked after IoMmuExitBoot() returns; plus\r
+ // *all* NotifyFunctions queued at TPL_CALLBACK will be invoked strictly\r
+ // after all NotifyFunctions queued at TPL_NOTIFY, including\r
+ // IoMmuExitBoot(), have been invoked.\r
+ //\r
+ // - By signaling EventToSignal here, whose NotifyTpl is TPL_CALLBACK, we\r
+ // queue EventToSignal's NotifyFunction after the NotifyFunctions of *all*\r
+ // events in EFI_EVENT_GROUP_EXIT_BOOT_SERVICES.\r
+ //\r
+ DEBUG ((DEBUG_VERBOSE, "%a\n", __FUNCTION__));\r
+ gBS->SignalEvent (EventToSignal);\r
+}\r
+\r
+/**\r
+ Notification function that is queued after the notification functions of all\r
+ events in the EFI_EVENT_GROUP_EXIT_BOOT_SERVICES event group. The same memory\r
+ map restrictions apply.\r
+\r
+ This function unmaps all currently existing IOMMU mappings.\r
+\r
+ @param[in] Event Event whose notification function is being invoked. Event\r
+ is permitted to request the queueing of this function\r
+ only at TPL_CALLBACK task priority level.\r
+\r
+ @param[in] Context Ignored.\r
+**/\r
+STATIC\r
+VOID\r
+EFIAPI\r
+IoMmuUnmapAllMappings (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ LIST_ENTRY *Node;\r
+ LIST_ENTRY *NextNode;\r
+ MAP_INFO *MapInfo;\r
+\r
+ DEBUG ((DEBUG_VERBOSE, "%a\n", __FUNCTION__));\r
+\r
+ //\r
+ // All drivers that had set up IOMMU mappings have halted their respective\r
+ // controllers by now; tear down the mappings.\r
+ //\r
+ for (Node = GetFirstNode (&mMapInfos); Node != &mMapInfos; Node = NextNode) {\r
+ NextNode = GetNextNode (&mMapInfos, Node);\r
+ MapInfo = CR (Node, MAP_INFO, Link, MAP_INFO_SIG);\r
+ IoMmuUnmapWorker (\r
+ &mIoMmu, // This\r
+ MapInfo, // Mapping\r
+ TRUE // MemoryMapLocked\r
+ );\r
+ }\r
+\r
+ //\r
+ // Release the reserved shared memory as well.\r
+ //\r
+ IoMmuReleaseReservedSharedMem (TRUE);\r
+}\r
+\r
+/**\r
+ Initialize Iommu Protocol.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+InstallIoMmuProtocol (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_EVENT UnmapAllMappingsEvent;\r
+ EFI_EVENT ExitBootEvent;\r
+ EFI_HANDLE Handle;\r
+\r
+ //\r
+ // Create the "late" event whose notification function will tear down all\r
+ // left-over IOMMU mappings.\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_NOTIFY_SIGNAL, // Type\r
+ TPL_CALLBACK, // NotifyTpl\r
+ IoMmuUnmapAllMappings, // NotifyFunction\r
+ NULL, // NotifyContext\r
+ &UnmapAllMappingsEvent // Event\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Create the event whose notification function will be queued by\r
+ // gBS->ExitBootServices() and will signal the event created above.\r
+ //\r
+ Status = gBS->CreateEvent (\r
+ EVT_SIGNAL_EXIT_BOOT_SERVICES, // Type\r
+ TPL_CALLBACK, // NotifyTpl\r
+ IoMmuExitBoot, // NotifyFunction\r
+ UnmapAllMappingsEvent, // NotifyContext\r
+ &ExitBootEvent // Event\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto CloseUnmapAllMappingsEvent;\r
+ }\r
+\r
+ Handle = NULL;\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ &Handle,\r
+ &gEdkiiIoMmuProtocolGuid,\r
+ &mIoMmu,\r
+ NULL\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ goto CloseExitBootEvent;\r
+ }\r
+\r
+ //\r
+ // Currently only Tdx guest support Reserved shared memory for DMA operation.\r
+ //\r
+ if (CC_GUEST_IS_TDX (PcdGet64 (PcdConfidentialComputingGuestAttr))) {\r
+ mReservedSharedMemSupported = TRUE;\r
+ Status = IoMmuInitReservedSharedMem ();\r
+ if (EFI_ERROR (Status)) {\r
+ mReservedSharedMemSupported = FALSE;\r
+ } else {\r
+ DEBUG ((DEBUG_INFO, "%a: Feature of reserved memory for DMA is supported.\n", __FUNCTION__));\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+CloseExitBootEvent:\r
+ gBS->CloseEvent (ExitBootEvent);\r
+\r
+CloseUnmapAllMappingsEvent:\r
+ gBS->CloseEvent (UnmapAllMappingsEvent);\r
+\r
+ return Status;\r
+}\r
--- /dev/null
+/** @file\r
+\r
+ The protocol provides support to allocate, free, map and umap a DMA buffer\r
+ for bus master (e.g PciHostBridge). When SEV is enabled, the DMA operations\r
+ must be performed on unencrypted buffer hence protocol clear the encryption\r
+ bit from the DMA buffer.\r
+\r
+ Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>\r
+ Copyright (c) 2017, AMD Inc. All rights reserved.<BR>\r
+ (C) Copyright 2017 Hewlett Packard Enterprise Development LP<BR>\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#ifndef _AMD_SEV_IOMMU_H_\r
+#define _AMD_SEV_IOMMU_H_\r
+\r
+#include <Protocol/IoMmu.h>\r
+\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/MemEncryptSevLib.h>\r
+#include <Library/MemEncryptTdxLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+\r
+/**\r
+ Install IOMMU protocol to provide the DMA support for PciHostBridge.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+InstallIoMmuProtocol (\r
+ VOID\r
+ );\r
+\r
+#endif\r
\r
**/\r
\r
-#include "AmdSevIoMmu.h"\r
+#include "CcIoMmu.h"\r
\r
EFI_STATUS\r
EFIAPI\r
ENTRY_POINT = IoMmuDxeEntryPoint\r
\r
[Sources]\r
- AmdSevIoMmu.c\r
- AmdSevIoMmu.h\r
+ CcIoMmu.c\r
+ CcIoMmu.h\r
IoMmuDxe.c\r
IoMmuBuffer.c\r
\r
MdePkg/MdePkg.dec\r
MdeModulePkg/MdeModulePkg.dec\r
OvmfPkg/OvmfPkg.dec\r
-# UefiCpuPkg/UefiCpuPkg.dec\r
\r
[LibraryClasses]\r
BaseLib\r