/**\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
(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
- // Forget and free the MAP_INFO structure.\r
+ // Forget the MAP_INFO structure, then free it (unless the UEFI memory map is\r
+ // locked).\r
//\r
RemoveEntryList (&MapInfo->Link);\r
- FreePool (MapInfo);\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
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