]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/IoMmuDxe/AmdSevIoMmu.c
OvmfPkg/PlatformDxe: list "Platform.h" in the INF file
[mirror_edk2.git] / OvmfPkg / IoMmuDxe / AmdSevIoMmu.c
index 0ab7043498bda817c54c32970805f92a8f82313d..6b7df8d8aa3ddd5a2b866a34c765f4dec3a3ebef 100644 (file)
@@ -33,14 +33,11 @@ typedef struct {
 } 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
@@ -123,7 +120,6 @@ IoMmuMap (
   )\r
 {\r
   EFI_STATUS                                        Status;\r
-  LIST_ENTRY                                        *RecycledMapInfo;\r
   MAP_INFO                                          *MapInfo;\r
   EFI_ALLOCATE_TYPE                                 AllocateType;\r
   COMMON_BUFFER_HEADER                              *CommonBufferHeader;\r
@@ -150,19 +146,10 @@ IoMmuMap (
   // 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
@@ -298,6 +285,10 @@ IoMmuMap (
       );\r
   }\r
 \r
+  //\r
+  // Track all MAP_INFO structures.\r
+  //\r
+  InsertHeadList (&mMapInfos, &MapInfo->Link);\r
   //\r
   // Populate output parameters.\r
   //\r
@@ -327,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
@@ -336,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
@@ -348,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
@@ -421,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
@@ -430,27 +436,54 @@ IoMmuUnmap (
       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
@@ -611,6 +644,14 @@ IoMmuFreeBuffer (
   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
@@ -630,14 +671,6 @@ IoMmuFreeBuffer (
   //\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
@@ -715,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
@@ -726,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