]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/IoMmuDxe/AmdSevIoMmu.c
OvmfPkg/VirtioMmioDeviceLib: list "VirtioMmioDevice.h" in the INF file
[mirror_edk2.git] / OvmfPkg / IoMmuDxe / AmdSevIoMmu.c
index c86e734985552d2f9e5657109cfcd8e4750528da..6b7df8d8aa3ddd5a2b866a34c765f4dec3a3ebef 100644 (file)
@@ -318,8 +318,14 @@ Failed:
 /**\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
@@ -327,11 +333,13 @@ Failed:
   @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
@@ -339,7 +347,13 @@ IoMmuUnmap (
   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
@@ -412,7 +426,8 @@ IoMmuUnmap (
   // 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
@@ -426,18 +441,49 @@ IoMmuUnmap (
       (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
@@ -702,6 +748,107 @@ EDKII_IOMMU_PROTOCOL  mAmdSev = {
   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
@@ -713,13 +860,57 @@ AmdSevInstallIoMmuProtocol (
   )\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