do not enable pci bridge decoding on resume until I/O windows are restored

PCI bus driver restores most but not all of a child PCI-PCI bridge
configuration.  The bridge's I/O windows are restored by pcib driver and
that happens later in time.  This can be problematic because the Command
register is restored before the windows are restored.  If the firmware
programs the windows incorrectly or even does not program them at all,
then the bridge can start claiming I/O cycles that are not intended for
it.  This will continue until the correct windows are restored.

I have observed this problem with a buggy BIOS where after resuming from
S3 an I/O port window of a PCI-PCI bridge was configured with zero base
and limit causing the bridge to claim 0x0 - 0xFFF port range.  That
interfered with ACPI port access including ACPI PM Timer at port 0x808,
thus wreaking havoc in the time keeping.

The solution is to restore the Command register of PCI-PCI bridges after
the windows are restored in pcib driver.  While here, I decided that for
other PCI device types (normal and cardbus) it's better to restore the
Command register after their BARs are restored.

To do: per jhb's suggestion, move the window handling to pci driver.

Reviewed by:	imp, jhb, kib
MFC after:	2 weeks
Differential Revision: https://reviews.freebsd.org/D25028
This commit is contained in:
avg 2020-05-29 07:50:55 +00:00
parent c44d9a48fc
commit 41c3b8a6ad
2 changed files with 9 additions and 1 deletions

View File

@ -5932,7 +5932,6 @@ pci_cfg_restore(device_t dev, struct pci_devinfo *dinfo)
*/
if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0)
pci_set_powerstate(dev, PCI_POWERSTATE_D0);
pci_write_config(dev, PCIR_COMMAND, dinfo->cfg.cmdreg, 2);
pci_write_config(dev, PCIR_INTLINE, dinfo->cfg.intline, 1);
pci_write_config(dev, PCIR_INTPIN, dinfo->cfg.intpin, 1);
pci_write_config(dev, PCIR_CACHELNSZ, dinfo->cfg.cachelnsz, 1);
@ -5971,6 +5970,9 @@ pci_cfg_restore(device_t dev, struct pci_devinfo *dinfo)
}
pci_restore_bars(dev);
if ((dinfo->cfg.hdrtype & PCIM_HDRTYPE) != PCIM_HDRTYPE_BRIDGE)
pci_write_config(dev, PCIR_COMMAND, dinfo->cfg.cmdreg, 2);
/*
* Restore extended capabilities for PCI-Express and PCI-X
*/

View File

@ -1792,6 +1792,12 @@ pcib_resume(device_t dev)
{
pcib_cfg_restore(device_get_softc(dev));
/*
* Restore the Command register only after restoring the windows.
* The bridge should not be claiming random windows.
*/
pci_write_config(dev, PCIR_COMMAND, pci_get_cmdreg(dev), 2);
return (bus_generic_resume(dev));
}