]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Pci/PciBusDxe/PciDeviceSupport.c
MdeModulePkg/PciBus: Disable BME of all devices when entering RT
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / PciBusDxe / PciDeviceSupport.c
index 97bb971a5966a2d869d8da42b47cb4e5be386f27..004f2a3b5bb14986409977fb7b2d92366387355b 100644 (file)
@@ -20,6 +20,72 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 //\r
 LIST_ENTRY  mPciDevicePool;\r
 \r
+/**\r
+ Disable Bus Master Enable bit in all devices in the list.\r
+\r
+ @param Devices  A device list.\r
+**/\r
+VOID\r
+DisableBmeOnTree (\r
+  IN LIST_ENTRY      *Devices\r
+  )\r
+{\r
+  LIST_ENTRY      *Link;\r
+  PCI_IO_DEVICE   *PciIoDevice;\r
+  UINT16           Command;\r
+\r
+  for ( Link = GetFirstNode (Devices)\r
+      ; !IsNull (Devices, Link)\r
+      ; Link = GetNextNode (Devices, Link)\r
+      ) {\r
+    PciIoDevice = PCI_IO_DEVICE_FROM_LINK (Link);\r
+    //\r
+    // Turn off all children's Bus Master, if any\r
+    //\r
+    DisableBmeOnTree (&PciIoDevice->ChildList);\r
+\r
+    //\r
+    // If this is a device that supports BME, disable BME on this device.\r
+    //\r
+    if ((PciIoDevice->Supports & EFI_PCI_IO_ATTRIBUTE_BUS_MASTER) != 0) {\r
+      PCI_READ_COMMAND_REGISTER(PciIoDevice, &Command);\r
+      if ((Command & EFI_PCI_COMMAND_BUS_MASTER) != 0) {\r
+        Command &= ~EFI_PCI_COMMAND_BUS_MASTER;\r
+        PCI_SET_COMMAND_REGISTER (PciIoDevice, Command);\r
+        DEBUG ((\r
+          DEBUG_INFO,"  %02x   %02x      %02x         %04x\n",\r
+          PciIoDevice->BusNumber, PciIoDevice->DeviceNumber, PciIoDevice->FunctionNumber,\r
+          Command\r
+          ));\r
+      }\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+  Exit Boot Services Event notification handler.\r
+\r
+  Disable Bus Master on any that were enabled during BDS.\r
+\r
+  @param[in]  Event     Event whose notification function is being invoked.\r
+  @param[in]  Context   Pointer to the notification function's context.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+OnExitBootServices (\r
+  IN      EFI_EVENT                 Event,\r
+  IN      VOID                      *Context\r
+  )\r
+{\r
+  DEBUG ((\r
+    DEBUG_INFO,\r
+    "PciBus: Disable Bus Master of all devices...\n"\r
+    "  Bus# Device# Function#  NewCommand\n"\r
+    ));\r
+  DisableBmeOnTree(&mPciDevicePool);\r
+}\r
+\r
 /**\r
   Initialize the PCI devices pool.\r
 \r
@@ -29,7 +95,27 @@ InitializePciDevicePool (
   VOID\r
   )\r
 {\r
+  EFI_EVENT   ExitBootServicesEvent;\r
+  EFI_STATUS  Status;\r
+\r
   InitializeListHead (&mPciDevicePool);\r
+\r
+  //\r
+  // DisableBME on ExitBootServices should be synchonized with any IOMMU ExitBootServices routine.\r
+  // DisableBME should be run before the IOMMU protections are disabled.\r
+  // One way to do this is to ensure that the IOMMU ExitBootServices callback runs at TPL_CALLBACK.\r
+  //\r
+  Status = gBS->CreateEventEx (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_NOTIFY,\r
+                  OnExitBootServices,\r
+                  NULL,\r
+                  &gEfiEventExitBootServicesGuid,\r
+                  &ExitBootServicesEvent\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "PciBus: Unable to hook ExitBootServices event - %r\n", Status));\r
+  }\r
 }\r
 \r
 /**\r