MdeModulePkg/PciBus: Disable BME of all devices when entering RT

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>
This commit is contained in:
Ruiyu Ni 2017-10-31 15:53:18 +08:00
parent 3d34e92fab
commit 050763db07
3 changed files with 91 additions and 0 deletions

View File

@ -18,6 +18,8 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#include <PiDxe.h>
#include <Guid/EventGroup.h>
#include <Protocol/LoadedImage.h>
#include <Protocol/PciHostBridgeResourceAllocation.h>
#include <Protocol/PciIo.h>

View File

@ -80,6 +80,9 @@
DebugLib
PeCoffLib
[Guids]
gEfiEventExitBootServicesGuid ## SOMETIMES_CONSUMES ## Event
[Protocols]
gEfiPciHotPlugRequestProtocolGuid ## SOMETIMES_PRODUCES
gEfiPciIoProtocolGuid ## BY_START

View File

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