MdeModulePkg/PciBus: Disable BME of all devices when entering RT
authorRuiyu Ni <ruiyu.ni@intel.com>
Tue, 31 Oct 2017 07:53:18 +0000 (15:53 +0800)
committerRuiyu Ni <ruiyu.ni@intel.com>
Tue, 7 Nov 2017 03:23:41 +0000 (11:23 +0800)
The patch ensures all DMA transactions are blocked after
ExitBootService.
If a platform enables IOMMU before and needs disable IOMMU after
ExitBootService, the IOMMU should be disabled after PCI bus driver
disables BME.

Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Michael Turner <michael.turner@microsoft.com>
Signed-off-by: Ruiyu Ni <ruiyu.ni@intel.com>
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Reviewed-by: Jiewen Yao <jiewen.yao@intel.com>
Cc: Jeff Fan <vanjeff_919@hotmail.com>
MdeModulePkg/Bus/Pci/PciBusDxe/PciBus.h
MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf
MdeModulePkg/Bus/Pci/PciBusDxe/PciDeviceSupport.c

index 55eb3a5a80700e3956c6974df6503ee78e9e7243..79b5b71082794f0953797051dd37606c007e16f7 100644 (file)
@@ -18,6 +18,8 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 \r
 #include <PiDxe.h>\r
 \r
+#include <Guid/EventGroup.h>\r
+\r
 #include <Protocol/LoadedImage.h>\r
 #include <Protocol/PciHostBridgeResourceAllocation.h>\r
 #include <Protocol/PciIo.h>\r
index 97608bfcf2458d4c792c52c4ec4934f68897bce7..d5b8fab3ca92579e1ca73a970845f08a8c67d7a3 100644 (file)
@@ -80,6 +80,9 @@
   DebugLib\r
   PeCoffLib\r
 \r
+[Guids]\r
+  gEfiEventExitBootServicesGuid                   ## SOMETIMES_CONSUMES ## Event\r
+\r
 [Protocols]\r
   gEfiPciHotPlugRequestProtocolGuid               ## SOMETIMES_PRODUCES\r
   gEfiPciIoProtocolGuid                           ## BY_START\r
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