X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=OvmfPkg%2FIoMmuDxe%2FAmdSevIoMmu.c;h=49ffa24488110f9b41987058b8fe59b437d7f0d5;hb=b26f0cf9ee09a180c91a4beeeb1b149e7f92afed;hp=0a85ee6559e799a1b8cb1a4c235f86239da7a281;hpb=e130229c0aea069f44fc942e585733b435680a35;p=mirror_edk2.git
diff --git a/OvmfPkg/IoMmuDxe/AmdSevIoMmu.c b/OvmfPkg/IoMmuDxe/AmdSevIoMmu.c
index 0a85ee6559..49ffa24488 100644
--- a/OvmfPkg/IoMmuDxe/AmdSevIoMmu.c
+++ b/OvmfPkg/IoMmuDxe/AmdSevIoMmu.c
@@ -8,19 +8,17 @@
Copyright (c) 2017, AMD Inc. All rights reserved.
Copyright (c) 2017, Intel Corporation. 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 "AmdSevIoMmu.h"
+#define MAP_INFO_SIG SIGNATURE_64 ('M', 'A', 'P', '_', 'I', 'N', 'F', 'O')
+
typedef struct {
+ UINT64 Signature;
+ LIST_ENTRY Link;
EDKII_IOMMU_OPERATION Operation;
UINTN NumberOfBytes;
UINTN NumberOfPages;
@@ -28,7 +26,51 @@ typedef struct {
EFI_PHYSICAL_ADDRESS PlainTextAddress;
} MAP_INFO;
-#define NO_MAPPING (VOID *) (UINTN) -1
+//
+// List of the MAP_INFO structures that have been set up by IoMmuMap() and not
+// yet torn down by IoMmuUnmap(). The list represents the full set of mappings
+// currently in effect.
+//
+STATIC LIST_ENTRY mMapInfos = INITIALIZE_LIST_HEAD_VARIABLE (mMapInfos);
+
+#define COMMON_BUFFER_SIG SIGNATURE_64 ('C', 'M', 'N', 'B', 'U', 'F', 'F', 'R')
+
+//
+// ASCII names for EDKII_IOMMU_OPERATION constants, for debug logging.
+//
+STATIC CONST CHAR8 * CONST
+mBusMasterOperationName[EdkiiIoMmuOperationMaximum] = {
+ "Read",
+ "Write",
+ "CommonBuffer",
+ "Read64",
+ "Write64",
+ "CommonBuffer64"
+};
+
+//
+// The following structure enables Map() and Unmap() to perform in-place
+// decryption and encryption, respectively, for BusMasterCommonBuffer[64]
+// operations, without dynamic memory allocation or release.
+//
+// Both COMMON_BUFFER_HEADER and COMMON_BUFFER_HEADER.StashBuffer are allocated
+// by AllocateBuffer() and released by FreeBuffer().
+//
+#pragma pack (1)
+typedef struct {
+ UINT64 Signature;
+
+ //
+ // Always allocated from EfiBootServicesData type memory, and always
+ // encrypted.
+ //
+ VOID *StashBuffer;
+
+ //
+ // Followed by the actual common buffer, starting at the next page.
+ //
+} COMMON_BUFFER_HEADER;
+#pragma pack ()
/**
Provides the controller-specific addresses required to access system memory
@@ -74,6 +116,20 @@ IoMmuMap (
EFI_STATUS Status;
MAP_INFO *MapInfo;
EFI_ALLOCATE_TYPE AllocateType;
+ COMMON_BUFFER_HEADER *CommonBufferHeader;
+ VOID *DecryptionSource;
+
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a: Operation=%a Host=0x%p Bytes=0x%Lx\n",
+ __FUNCTION__,
+ ((Operation >= 0 &&
+ Operation < ARRAY_SIZE (mBusMasterOperationName)) ?
+ mBusMasterOperationName[Operation] :
+ "Invalid"),
+ HostAddress,
+ (UINT64)((NumberOfBytes == NULL) ? 0 : *NumberOfBytes)
+ ));
if (HostAddress == NULL || NumberOfBytes == NULL || DeviceAddress == NULL ||
Mapping == NULL) {
@@ -93,6 +149,8 @@ IoMmuMap (
//
// Initialize the MAP_INFO structure, except the PlainTextAddress field
//
+ ZeroMem (&MapInfo->Link, sizeof MapInfo->Link);
+ MapInfo->Signature = MAP_INFO_SIG;
MapInfo->Operation = Operation;
MapInfo->NumberOfBytes = *NumberOfBytes;
MapInfo->NumberOfPages = EFI_SIZE_TO_PAGES (MapInfo->NumberOfBytes);
@@ -100,10 +158,11 @@ IoMmuMap (
//
// In the switch statement below, we point "MapInfo->PlainTextAddress" to the
- // plaintext buffer, according to Operation.
+ // plaintext buffer, according to Operation. We also set "DecryptionSource".
//
MapInfo->PlainTextAddress = MAX_ADDRESS;
AllocateType = AllocateAnyPages;
+ DecryptionSource = (VOID *)(UINTN)MapInfo->CryptedAddress;
switch (Operation) {
//
// For BusMasterRead[64] and BusMasterWrite[64] operations, a bounce buffer
@@ -135,9 +194,10 @@ IoMmuMap (
break;
//
- // For BusMasterCommonBuffer[64] operations, a plaintext buffer has been
- // allocated already, with AllocateBuffer(). We only check whether the
- // address is low enough for the requested operation.
+ // For BusMasterCommonBuffer[64] operations, a to-be-plaintext buffer and a
+ // stash buffer (for in-place decryption) have been allocated already, with
+ // AllocateBuffer(). We only check whether the address of the to-be-plaintext
+ // buffer is low enough for the requested operation.
//
case EdkiiIoMmuOperationBusMasterCommonBuffer:
if ((MapInfo->CryptedAddress > BASE_4GB) ||
@@ -156,18 +216,27 @@ IoMmuMap (
//
case EdkiiIoMmuOperationBusMasterCommonBuffer64:
//
- // The buffer at MapInfo->CryptedAddress comes from AllocateBuffer(),
- // and it is already decrypted.
+ // The buffer at MapInfo->CryptedAddress comes from AllocateBuffer().
//
MapInfo->PlainTextAddress = MapInfo->CryptedAddress;
-
//
- // Therefore no mapping is necessary.
+ // Stash the crypted data.
//
- *DeviceAddress = MapInfo->PlainTextAddress;
- *Mapping = NO_MAPPING;
- FreePool (MapInfo);
- return EFI_SUCCESS;
+ CommonBufferHeader = (COMMON_BUFFER_HEADER *)(
+ (UINTN)MapInfo->CryptedAddress - EFI_PAGE_SIZE
+ );
+ ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG);
+ CopyMem (
+ CommonBufferHeader->StashBuffer,
+ (VOID *)(UINTN)MapInfo->CryptedAddress,
+ MapInfo->NumberOfBytes
+ );
+ //
+ // Point "DecryptionSource" to the stash buffer so that we decrypt
+ // it to the original location, after the switch statement.
+ //
+ DecryptionSource = CommonBufferHeader->StashBuffer;
+ break;
default:
//
@@ -186,22 +255,34 @@ IoMmuMap (
MapInfo->NumberOfPages,
TRUE
);
- ASSERT_EFI_ERROR(Status);
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ CpuDeadLoop ();
+ }
//
// 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.
//
+ // For BusMasterCommonBuffer[64] operations, the CopyMem() below will decrypt
+ // the original data (from the stash buffer) back to the original location.
+ //
if (Operation == EdkiiIoMmuOperationBusMasterRead ||
- Operation == EdkiiIoMmuOperationBusMasterRead64) {
+ Operation == EdkiiIoMmuOperationBusMasterRead64 ||
+ Operation == EdkiiIoMmuOperationBusMasterCommonBuffer ||
+ Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) {
CopyMem (
(VOID *) (UINTN) MapInfo->PlainTextAddress,
- (VOID *) (UINTN) MapInfo->CryptedAddress,
+ DecryptionSource,
MapInfo->NumberOfBytes
);
}
+ //
+ // Track all MAP_INFO structures.
+ //
+ InsertHeadList (&mMapInfos, &MapInfo->Link);
//
// Populate output parameters.
//
@@ -210,12 +291,12 @@ IoMmuMap (
DEBUG ((
DEBUG_VERBOSE,
- "%a PlainText 0x%Lx Crypted 0x%Lx Pages 0x%Lx Bytes 0x%Lx\n",
+ "%a: Mapping=0x%p Device(PlainText)=0x%Lx Crypted=0x%Lx Pages=0x%Lx\n",
__FUNCTION__,
+ MapInfo,
MapInfo->PlainTextAddress,
MapInfo->CryptedAddress,
- (UINT64)MapInfo->NumberOfPages,
- (UINT64)MapInfo->NumberOfBytes
+ (UINT64)MapInfo->NumberOfPages
));
return EFI_SUCCESS;
@@ -231,8 +312,14 @@ Failed:
/**
Completes the Map() operation and releases any corresponding resources.
+ This is an internal worker function that only extends the Map() API with
+ the MemoryMapLocked parameter.
+
@param This The protocol instance pointer.
@param Mapping The mapping value returned from Map().
+ @param MemoryMapLocked The function is executing on the stack of
+ gBS->ExitBootServices(); changes to the UEFI
+ memory map are forbidden.
@retval EFI_SUCCESS The range was unmapped.
@retval EFI_INVALID_PARAMETER Mapping is not a value that was returned by
@@ -240,56 +327,82 @@ Failed:
@retval EFI_DEVICE_ERROR The data was not committed to the target system
memory.
**/
+STATIC
EFI_STATUS
EFIAPI
-IoMmuUnmap (
+IoMmuUnmapWorker (
IN EDKII_IOMMU_PROTOCOL *This,
- IN VOID *Mapping
+ IN VOID *Mapping,
+ IN BOOLEAN MemoryMapLocked
)
{
MAP_INFO *MapInfo;
EFI_STATUS Status;
+ COMMON_BUFFER_HEADER *CommonBufferHeader;
+ VOID *EncryptionTarget;
+
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a: Mapping=0x%p MemoryMapLocked=%d\n",
+ __FUNCTION__,
+ Mapping,
+ MemoryMapLocked
+ ));
if (Mapping == NULL) {
return EFI_INVALID_PARAMETER;
}
+ MapInfo = (MAP_INFO *)Mapping;
+
//
- // 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
+ // set CommonBufferHeader to suppress incorrect compiler/analyzer warnings
//
- if (Mapping == NO_MAPPING) {
- return EFI_SUCCESS;
- }
-
- MapInfo = (MAP_INFO *)Mapping;
+ CommonBufferHeader = NULL;
//
- // 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.
+ // For BusMasterWrite[64] operations and BusMasterCommonBuffer[64] operations
+ // we have to encrypt the results, ultimately to the original place (i.e.,
+ // "MapInfo->CryptedAddress").
+ //
+ // For BusMasterCommonBuffer[64] operations however, this encryption has to
+ // land in-place, so divert the encryption to the stash buffer first.
//
- if (MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite ||
- MapInfo->Operation == EdkiiIoMmuOperationBusMasterWrite64) {
+ EncryptionTarget = (VOID *)(UINTN)MapInfo->CryptedAddress;
+
+ switch (MapInfo->Operation) {
+ case EdkiiIoMmuOperationBusMasterCommonBuffer:
+ case EdkiiIoMmuOperationBusMasterCommonBuffer64:
+ ASSERT (MapInfo->PlainTextAddress == MapInfo->CryptedAddress);
+
+ CommonBufferHeader = (COMMON_BUFFER_HEADER *)(
+ (UINTN)MapInfo->PlainTextAddress - EFI_PAGE_SIZE
+ );
+ ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG);
+ EncryptionTarget = CommonBufferHeader->StashBuffer;
+ //
+ // fall through
+ //
+
+ case EdkiiIoMmuOperationBusMasterWrite:
+ case EdkiiIoMmuOperationBusMasterWrite64:
CopyMem (
- (VOID *) (UINTN) MapInfo->CryptedAddress,
+ EncryptionTarget,
(VOID *) (UINTN) MapInfo->PlainTextAddress,
MapInfo->NumberOfBytes
);
+ break;
+
+ default:
+ //
+ // nothing to encrypt after BusMasterRead[64] operations
+ //
+ break;
}
- DEBUG ((
- DEBUG_VERBOSE,
- "%a PlainText 0x%Lx Crypted 0x%Lx Pages 0x%Lx Bytes 0x%Lx\n",
- __FUNCTION__,
- MapInfo->PlainTextAddress,
- MapInfo->CryptedAddress,
- (UINT64)MapInfo->NumberOfPages,
- (UINT64)MapInfo->NumberOfBytes
- ));
//
- // Restore the memory encryption mask
+ // Restore the memory encryption mask on the area we used to hold the
+ // plaintext.
//
Status = MemEncryptSevSetPageEncMask (
0,
@@ -297,20 +410,74 @@ IoMmuUnmap (
MapInfo->NumberOfPages,
TRUE
);
- ASSERT_EFI_ERROR(Status);
- ZeroMem (
- (VOID*)(UINTN)MapInfo->PlainTextAddress,
- EFI_PAGES_TO_SIZE (MapInfo->NumberOfPages)
- );
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ CpuDeadLoop ();
+ }
+
+ //
+ // For BusMasterCommonBuffer[64] operations, copy the stashed data to the
+ // original (now encrypted) location.
+ //
+ // For all other operations, fill the late bounce buffer (which existed as
+ // plaintext at some point) with zeros, and then release it (unless the UEFI
+ // memory map is locked).
+ //
+ if (MapInfo->Operation == EdkiiIoMmuOperationBusMasterCommonBuffer ||
+ MapInfo->Operation == EdkiiIoMmuOperationBusMasterCommonBuffer64) {
+ CopyMem (
+ (VOID *)(UINTN)MapInfo->CryptedAddress,
+ CommonBufferHeader->StashBuffer,
+ MapInfo->NumberOfBytes
+ );
+ } else {
+ ZeroMem (
+ (VOID *)(UINTN)MapInfo->PlainTextAddress,
+ EFI_PAGES_TO_SIZE (MapInfo->NumberOfPages)
+ );
+ if (!MemoryMapLocked) {
+ gBS->FreePages (MapInfo->PlainTextAddress, MapInfo->NumberOfPages);
+ }
+ }
//
- // Free the mapped buffer and the MAP_INFO structure.
+ // Forget the MAP_INFO structure, then free it (unless the UEFI memory map is
+ // locked).
//
- gBS->FreePages (MapInfo->PlainTextAddress, MapInfo->NumberOfPages);
- FreePool (Mapping);
+ RemoveEntryList (&MapInfo->Link);
+ if (!MemoryMapLocked) {
+ FreePool (MapInfo);
+ }
+
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
+ )
+{
+ return IoMmuUnmapWorker (
+ This,
+ Mapping,
+ FALSE // MemoryMapLocked
+ );
+}
+
/**
Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
OperationBusMasterCommonBuffer64 mapping.
@@ -346,6 +513,18 @@ IoMmuAllocateBuffer (
{
EFI_STATUS Status;
EFI_PHYSICAL_ADDRESS PhysicalAddress;
+ VOID *StashBuffer;
+ UINTN CommonBufferPages;
+ COMMON_BUFFER_HEADER *CommonBufferHeader;
+
+ DEBUG ((
+ DEBUG_VERBOSE,
+ "%a: MemoryType=%u Pages=0x%Lx Attributes=0x%Lx\n",
+ __FUNCTION__,
+ (UINT32)MemoryType,
+ (UINT64)Pages,
+ Attributes
+ ));
//
// Validate Attributes
@@ -370,6 +549,32 @@ IoMmuAllocateBuffer (
return EFI_INVALID_PARAMETER;
}
+ //
+ // We'll need a header page for the COMMON_BUFFER_HEADER structure.
+ //
+ if (Pages > MAX_UINTN - 1) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ CommonBufferPages = Pages + 1;
+
+ //
+ // Allocate the stash in EfiBootServicesData type memory.
+ //
+ // Map() will temporarily save encrypted data in the stash for
+ // BusMasterCommonBuffer[64] operations, so the data can be decrypted to the
+ // original location.
+ //
+ // Unmap() will temporarily save plaintext data in the stash for
+ // BusMasterCommonBuffer[64] operations, so the data can be encrypted to the
+ // original location.
+ //
+ // StashBuffer always resides in encrypted memory.
+ //
+ StashBuffer = AllocatePages (Pages);
+ if (StashBuffer == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
PhysicalAddress = (UINTN)-1;
if ((Attributes & EDKII_IOMMU_ATTRIBUTE_DUAL_ADDRESS_CYCLE) == 0) {
//
@@ -380,26 +585,32 @@ IoMmuAllocateBuffer (
Status = gBS->AllocatePages (
AllocateMaxAddress,
MemoryType,
- Pages,
+ CommonBufferPages,
&PhysicalAddress
);
- if (!EFI_ERROR (Status)) {
- *HostAddress = (VOID *) (UINTN) PhysicalAddress;
-
- //
- // Clear memory encryption mask
- //
- Status = MemEncryptSevClearPageEncMask (0, PhysicalAddress, Pages, TRUE);
- ASSERT_EFI_ERROR(Status);
+ if (EFI_ERROR (Status)) {
+ goto FreeStashBuffer;
}
+ CommonBufferHeader = (VOID *)(UINTN)PhysicalAddress;
+ PhysicalAddress += EFI_PAGE_SIZE;
+
+ CommonBufferHeader->Signature = COMMON_BUFFER_SIG;
+ CommonBufferHeader->StashBuffer = StashBuffer;
+
+ *HostAddress = (VOID *)(UINTN)PhysicalAddress;
+
DEBUG ((
DEBUG_VERBOSE,
- "%a Address 0x%Lx Pages 0x%Lx\n",
+ "%a: Host=0x%Lx Stash=0x%p\n",
__FUNCTION__,
PhysicalAddress,
- (UINT64)Pages
+ StashBuffer
));
+ return EFI_SUCCESS;
+
+FreeStashBuffer:
+ FreePages (StashBuffer, Pages);
return Status;
}
@@ -424,28 +635,41 @@ IoMmuFreeBuffer (
IN VOID *HostAddress
)
{
- EFI_STATUS Status;
-
- //
- // Set memory encryption mask
- //
- Status = MemEncryptSevSetPageEncMask (
- 0,
- (EFI_PHYSICAL_ADDRESS)(UINTN)HostAddress,
- Pages,
- TRUE
- );
- ASSERT_EFI_ERROR(Status);
- ZeroMem (HostAddress, EFI_PAGES_TO_SIZE (Pages));
+ UINTN CommonBufferPages;
+ COMMON_BUFFER_HEADER *CommonBufferHeader;
DEBUG ((
DEBUG_VERBOSE,
- "%a Address 0x%Lx Pages 0x%Lx\n",
+ "%a: Host=0x%p Pages=0x%Lx\n",
__FUNCTION__,
- (UINT64)(UINTN)HostAddress,
+ HostAddress,
(UINT64)Pages
));
- return gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress, Pages);
+
+ CommonBufferPages = Pages + 1;
+ CommonBufferHeader = (COMMON_BUFFER_HEADER *)(
+ (UINTN)HostAddress - EFI_PAGE_SIZE
+ );
+
+ //
+ // Check the signature.
+ //
+ ASSERT (CommonBufferHeader->Signature == COMMON_BUFFER_SIG);
+ if (CommonBufferHeader->Signature != COMMON_BUFFER_SIG) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Free the stash buffer. This buffer was always encrypted, so no need to
+ // zero it.
+ //
+ FreePages (CommonBufferHeader->StashBuffer, Pages);
+
+ //
+ // Release the common buffer itself. Unmap() has re-encrypted it in-place, so
+ // no need to zero it.
+ //
+ return gBS->FreePages ((UINTN)CommonBufferHeader, CommonBufferPages);
}
@@ -518,6 +742,107 @@ EDKII_IOMMU_PROTOCOL mAmdSev = {
IoMmuFreeBuffer,
};
+/**
+ Notification function that is queued when gBS->ExitBootServices() signals the
+ EFI_EVENT_GROUP_EXIT_BOOT_SERVICES event group. This function signals another
+ event, received as Context, and returns.
+
+ Signaling an event in this context is safe. The UEFI spec allows
+ gBS->SignalEvent() to return EFI_SUCCESS only; EFI_OUT_OF_RESOURCES is not
+ listed, hence memory is not allocated. The edk2 implementation also does not
+ release memory (and we only have to care about the edk2 implementation
+ because EDKII_IOMMU_PROTOCOL is edk2-specific anyway).
+
+ @param[in] Event Event whose notification function is being invoked.
+ Event is permitted to request the queueing of this
+ function at TPL_CALLBACK or TPL_NOTIFY task
+ priority level.
+
+ @param[in] EventToSignal Identifies the EFI_EVENT to signal. EventToSignal
+ is permitted to request the queueing of its
+ notification function only at TPL_CALLBACK level.
+**/
+STATIC
+VOID
+EFIAPI
+AmdSevExitBoot (
+ IN EFI_EVENT Event,
+ IN VOID *EventToSignal
+ )
+{
+ //
+ // (1) The NotifyFunctions of all the events in
+ // EFI_EVENT_GROUP_EXIT_BOOT_SERVICES will have been queued before
+ // AmdSevExitBoot() is entered.
+ //
+ // (2) AmdSevExitBoot() is executing minimally at TPL_CALLBACK.
+ //
+ // (3) AmdSevExitBoot() has been queued in unspecified order relative to the
+ // NotifyFunctions of all the other events in
+ // EFI_EVENT_GROUP_EXIT_BOOT_SERVICES whose NotifyTpl is the same as
+ // Event's.
+ //
+ // Consequences:
+ //
+ // - If Event's NotifyTpl is TPL_CALLBACK, then some other NotifyFunctions
+ // queued at TPL_CALLBACK may be invoked after AmdSevExitBoot() returns.
+ //
+ // - If Event's NotifyTpl is TPL_NOTIFY, then some other NotifyFunctions
+ // queued at TPL_NOTIFY may be invoked after AmdSevExitBoot() returns; plus
+ // *all* NotifyFunctions queued at TPL_CALLBACK will be invoked strictly
+ // after all NotifyFunctions queued at TPL_NOTIFY, including
+ // AmdSevExitBoot(), have been invoked.
+ //
+ // - By signaling EventToSignal here, whose NotifyTpl is TPL_CALLBACK, we
+ // queue EventToSignal's NotifyFunction after the NotifyFunctions of *all*
+ // events in EFI_EVENT_GROUP_EXIT_BOOT_SERVICES.
+ //
+ DEBUG ((DEBUG_VERBOSE, "%a\n", __FUNCTION__));
+ gBS->SignalEvent (EventToSignal);
+}
+
+/**
+ Notification function that is queued after the notification functions of all
+ events in the EFI_EVENT_GROUP_EXIT_BOOT_SERVICES event group. The same memory
+ map restrictions apply.
+
+ This function unmaps all currently existing IOMMU mappings.
+
+ @param[in] Event Event whose notification function is being invoked. Event
+ is permitted to request the queueing of this function
+ only at TPL_CALLBACK task priority level.
+
+ @param[in] Context Ignored.
+**/
+STATIC
+VOID
+EFIAPI
+AmdSevUnmapAllMappings (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ LIST_ENTRY *Node;
+ LIST_ENTRY *NextNode;
+ MAP_INFO *MapInfo;
+
+ DEBUG ((DEBUG_VERBOSE, "%a\n", __FUNCTION__));
+
+ //
+ // All drivers that had set up IOMMU mappings have halted their respective
+ // controllers by now; tear down the mappings.
+ //
+ for (Node = GetFirstNode (&mMapInfos); Node != &mMapInfos; Node = NextNode) {
+ NextNode = GetNextNode (&mMapInfos, Node);
+ MapInfo = CR (Node, MAP_INFO, Link, MAP_INFO_SIG);
+ IoMmuUnmapWorker (
+ &mAmdSev, // This
+ MapInfo, // Mapping
+ TRUE // MemoryMapLocked
+ );
+ }
+}
+
/**
Initialize Iommu Protocol.
@@ -529,13 +854,57 @@ AmdSevInstallIoMmuProtocol (
)
{
EFI_STATUS Status;
+ EFI_EVENT UnmapAllMappingsEvent;
+ EFI_EVENT ExitBootEvent;
EFI_HANDLE Handle;
+ //
+ // Create the "late" event whose notification function will tear down all
+ // left-over IOMMU mappings.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL, // Type
+ TPL_CALLBACK, // NotifyTpl
+ AmdSevUnmapAllMappings, // NotifyFunction
+ NULL, // NotifyContext
+ &UnmapAllMappingsEvent // Event
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Create the event whose notification function will be queued by
+ // gBS->ExitBootServices() and will signal the event created above.
+ //
+ Status = gBS->CreateEvent (
+ EVT_SIGNAL_EXIT_BOOT_SERVICES, // Type
+ TPL_CALLBACK, // NotifyTpl
+ AmdSevExitBoot, // NotifyFunction
+ UnmapAllMappingsEvent, // NotifyContext
+ &ExitBootEvent // Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto CloseUnmapAllMappingsEvent;
+ }
+
Handle = NULL;
Status = gBS->InstallMultipleProtocolInterfaces (
&Handle,
&gEdkiiIoMmuProtocolGuid, &mAmdSev,
NULL
);
+ if (EFI_ERROR (Status)) {
+ goto CloseExitBootEvent;
+ }
+
+ return EFI_SUCCESS;
+
+CloseExitBootEvent:
+ gBS->CloseEvent (ExitBootEvent);
+
+CloseUnmapAllMappingsEvent:
+ gBS->CloseEvent (UnmapAllMappingsEvent);
+
return Status;
}