} MAP_INFO;\r
\r
//\r
-// List of MAP_INFO structures recycled by Unmap().\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
-// Recycled MAP_INFO structures are equally good for future recycling and\r
-// freeing.\r
-//\r
-STATIC LIST_ENTRY mRecycledMapInfos = INITIALIZE_LIST_HEAD_VARIABLE (\r
- mRecycledMapInfos\r
- );\r
+STATIC LIST_ENTRY mMapInfos = INITIALIZE_LIST_HEAD_VARIABLE (mMapInfos);\r
\r
#define COMMON_BUFFER_SIG SIGNATURE_64 ('C', 'M', 'N', 'B', 'U', 'F', 'F', 'R')\r
\r
)\r
{\r
EFI_STATUS Status;\r
- LIST_ENTRY *RecycledMapInfo;\r
MAP_INFO *MapInfo;\r
EFI_ALLOCATE_TYPE AllocateType;\r
COMMON_BUFFER_HEADER *CommonBufferHeader;\r
// Allocate a MAP_INFO structure to remember the mapping when Unmap() is\r
// called later.\r
//\r
- RecycledMapInfo = GetFirstNode (&mRecycledMapInfos);\r
- if (RecycledMapInfo == &mRecycledMapInfos) {\r
- //\r
- // No recycled MAP_INFO structure, allocate a new one.\r
- //\r
- MapInfo = AllocatePool (sizeof (MAP_INFO));\r
- if (MapInfo == NULL) {\r
- Status = EFI_OUT_OF_RESOURCES;\r
- goto Failed;\r
- }\r
- } else {\r
- MapInfo = CR (RecycledMapInfo, MAP_INFO, Link, MAP_INFO_SIG);\r
- RemoveEntryList (RecycledMapInfo);\r
+ MapInfo = AllocatePool (sizeof (MAP_INFO));\r
+ if (MapInfo == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto Failed;\r
}\r
\r
//\r
);\r
}\r
\r
+ //\r
+ // Track all MAP_INFO structures.\r
+ //\r
+ InsertHeadList (&mMapInfos, &MapInfo->Link);\r
//\r
// Populate output parameters.\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
@retval EFI_DEVICE_ERROR The data was not committed to the target system\r
memory.\r
**/\r
+STATIC\r
EFI_STATUS\r
EFIAPI\r
-IoMmuUnmap (\r
+IoMmuUnmapWorker (\r
IN EDKII_IOMMU_PROTOCOL *This,\r
- IN VOID *Mapping\r
+ IN VOID *Mapping,\r
+ IN BOOLEAN MemoryMapLocked\r
)\r
{\r
MAP_INFO *MapInfo;\r
COMMON_BUFFER_HEADER *CommonBufferHeader;\r
VOID *EncryptionTarget;\r
\r
- DEBUG ((DEBUG_VERBOSE, "%a: Mapping=0x%p\n", __FUNCTION__, Mapping));\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
// 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.\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
CommonBufferHeader->StashBuffer,\r
MapInfo->NumberOfBytes\r
);\r
-\r
- //\r
- // Recycle the MAP_INFO structure.\r
- //\r
- InsertTailList (&mRecycledMapInfos, &MapInfo->Link);\r
} else {\r
ZeroMem (\r
(VOID *)(UINTN)MapInfo->PlainTextAddress,\r
EFI_PAGES_TO_SIZE (MapInfo->NumberOfPages)\r
);\r
- gBS->FreePages (MapInfo->PlainTextAddress, MapInfo->NumberOfPages);\r
+ if (!MemoryMapLocked) {\r
+ gBS->FreePages (MapInfo->PlainTextAddress, MapInfo->NumberOfPages);\r
+ }\r
+ }\r
\r
- //\r
- // Free the MAP_INFO structure.\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
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
FreePages (CommonBufferHeader->StashBuffer, Pages);\r
\r
- DEBUG ((\r
- DEBUG_VERBOSE,\r
- "%a Address 0x%Lx Pages 0x%Lx\n",\r
- __FUNCTION__,\r
- (UINT64)(UINTN)HostAddress,\r
- (UINT64)Pages\r
- ));\r
-\r
//\r
// Release the common buffer itself. Unmap() has re-encrypted it in-place, so\r
// no need to zero it.\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
+AmdSevExitBoot (\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
+ // AmdSevExitBoot() is entered.\r
+ //\r
+ // (2) AmdSevExitBoot() is executing minimally at TPL_CALLBACK.\r
+ //\r
+ // (3) AmdSevExitBoot() 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 AmdSevExitBoot() returns.\r
+ //\r
+ // - If Event's NotifyTpl is TPL_NOTIFY, then some other NotifyFunctions\r
+ // queued at TPL_NOTIFY may be invoked after AmdSevExitBoot() returns; plus\r
+ // *all* NotifyFunctions queued at TPL_CALLBACK will be invoked strictly\r
+ // after all NotifyFunctions queued at TPL_NOTIFY, including\r
+ // AmdSevExitBoot(), 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
+AmdSevUnmapAllMappings (\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
+ &mAmdSev, // This\r
+ MapInfo, // Mapping\r
+ TRUE // MemoryMapLocked\r
+ );\r
+ }\r
+}\r
+\r
/**\r
Initialize Iommu Protocol.\r
\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
+ AmdSevUnmapAllMappings, // 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
+ AmdSevExitBoot, // 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, &mAmdSev,\r
NULL\r
);\r
+ if (EFI_ERROR (Status)) {\r
+ goto CloseExitBootEvent;\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