]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[xhci] Avoid DMA during shutdown if firmware has disabled bus mastering
authorMichael Brown <mcb30@ipxe.org>
Fri, 12 Nov 2021 15:57:51 +0000 (15:57 +0000)
committerMichael Brown <mcb30@ipxe.org>
Fri, 12 Nov 2021 22:27:25 +0000 (22:27 +0000)
On some systems (observed with the Thunderbolt ports on a ThinkPad X1
Extreme Gen3 and a ThinkPad P53), the system firmware will disable bus
mastering on the xHCI controller and all PCI bridges at the point that
ExitBootServices() is called if the IOMMU is enabled.  This leaves the
xHCI controller unable to shut down cleanly since all commands will
fail with a timeout.

Commit 85eb961 ("[xhci] Allow for permanent failure of the command
mechanism") allows us to detect that this has happened and respond
cleanly.  However, some unidentified hardware component (either the
xHCI controller or one of the PCI bridges) seems to manage to enqueue
the attempted DMA operation and eventually complete it after the
operating system kernel has reenabled bus mastering.  This results in
a DMA operation to an area of memory that the hardware is no longer
permitted to access.  On Windows with the Driver Verifier enabled,
this will result in a STOP 0xE6 (DRIVER_VERIFIER_DMA_VIOLATION).

Work around this problem by detecting when bus mastering has been
disabled, and immediately failing the device to avoid initiating any
further DMA attempts.

Reported-by: Andreas Hammarskjöld <junior@2PintSoftware.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/drivers/usb/xhci.c

index 3d98b1e10f93876f6ea980ef30c18d205f55313d..3247ee69c173b1741bd466e4a031460a366c7970 100644 (file)
@@ -3459,14 +3459,36 @@ static int xhci_probe ( struct pci_device *pci ) {
 static void xhci_remove ( struct pci_device *pci ) {
        struct xhci_device *xhci = pci_get_drvdata ( pci );
        struct usb_bus *bus = xhci->bus;
+       uint16_t command;
 
+       /* Some systems are observed to disable bus mastering on
+        * Thunderbolt controllers before we get a chance to shut
+        * down.  Detect this and avoid attempting any DMA operations,
+        * which are guaranteed to fail and may end up spuriously
+        * completing after the operating system kernel starts up.
+        */
+       pci_read_config_word ( pci, PCI_COMMAND, &command );
+       if ( ! ( command & PCI_COMMAND_MASTER ) ) {
+               DBGC ( xhci, "XHCI %s DMA was disabled\n", xhci->name );
+               xhci_fail ( xhci );
+       }
+
+       /* Unregister and free USB bus */
        unregister_usb_bus ( bus );
        free_usb_bus ( bus );
+
+       /* Reset device and undo any PCH-specific fixes */
        xhci_reset ( xhci );
        if ( xhci->quirks & XHCI_PCH )
                xhci_pch_undo ( xhci, pci );
+
+       /* Release ownership back to BIOS */
        xhci_legacy_release ( xhci );
+
+       /* Unmap registers */
        iounmap ( xhci->regs );
+
+       /* Free device */
        free ( xhci );
 }