On resume, the OS queries the power management event that caused it. In order to complete this task, it executes some reads to the piix pm io space. This all happens before the OS has a chance to restore the PCI config space for devices, so it is bios's responsibility to make sure the pm IO space is configured correctly. (During suspend, the piix pm configuration space is lost).
Note: For 'ordinary' pci devices the config space is saved by the OS on sleep and restored on resume.
Signed-off-by: Marcel Apfelbaum marcel.a@redhat.com --- This patch is based on Michael S. Tsirkin's patch: [SeaBIOS] [PATCH] seabios: call pci_init_device on resume If Michael agrees with it, I'll add him to signed-off section.
Notes: - Without this patch the OS gets stuck because it tries repeatedly to read/write to pm io space, but the memory region is not enabled, so -1 is returned. - After resume the OS does not actually use the pm base address configured by the bios to get the IO ports, but uses the value from the ACPI FADT table actually. However, as a side effect of the configuration, the pm-io space is enabled by Qemu and the OS can continue the the boot sequence. - Bioses used for hardware like coreboot have the same init sequence for piix, see enable_pm from src/southbridge/intel/i82371eb/early_pm.c.
src/fw/pciinit.c | 22 ++++++++++++++++++++-- src/hw/pci.c | 31 ++++++++++++++++++++----------- src/hw/pci.h | 1 + src/resume.c | 8 ++++++++ src/util.h | 1 + 5 files changed, 50 insertions(+), 13 deletions(-)
diff --git a/src/fw/pciinit.c b/src/fw/pciinit.c index a24b8ff..252328f 100644 --- a/src/fw/pciinit.c +++ b/src/fw/pciinit.c @@ -230,8 +230,7 @@ static void apple_macio_setup(struct pci_device *pci, void *arg) pci_set_io_region_addr(pci, 0, 0x80800000, 0); }
-/* PIIX4 Power Management device (for ACPI) */ -static void piix4_pm_setup(struct pci_device *pci, void *arg) +static void piix4_pm_config_setup(struct pci_device *pci, void *arg) { u16 bdf = pci->bdf; // acpi sci is hardwired to 9 @@ -241,6 +240,12 @@ static void piix4_pm_setup(struct pci_device *pci, void *arg) pci_config_writeb(bdf, 0x80, 0x01); /* enable PM io space */ pci_config_writel(bdf, 0x90, PORT_SMB_BASE | 1); pci_config_writeb(bdf, 0xd2, 0x09); /* enable SMBus io space */ +} + +/* PIIX4 Power Management device (for ACPI) */ +static void piix4_pm_setup(struct pci_device *pci, void *arg) +{ + piix4_pm_config_setup(pci, arg);
acpi_pm1a_cnt = PORT_ACPI_PM_BASE + 0x04; pmtimer_setup(PORT_ACPI_PM_BASE + 0x08); @@ -295,6 +300,19 @@ static const struct pci_device_id pci_device_tbl[] = { PCI_DEVICE_END, };
+static const struct pci_device_id pci_device_resume_tbl[] = { + /* PIIX4 Power Management device (for ACPI) */ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, + piix4_pm_config_setup), + + PCI_DEVICE_END, +}; + +void pci_bios_resume_device(struct pci_device *pci) +{ + pci_init_device(pci_device_resume_tbl, pci, NULL); +} + static void pci_bios_init_device(struct pci_device *pci) { u16 bdf = pci->bdf; diff --git a/src/hw/pci.c b/src/hw/pci.c index 6c9aa81..c2873c3 100644 --- a/src/hw/pci.c +++ b/src/hw/pci.c @@ -105,6 +105,24 @@ pci_probe_host(void) return 0; }
+void +pci_probe_device(int bdf, struct pci_device *dev) +{ + dev->bdf = bdf; + u32 vendev = pci_config_readl(bdf, PCI_VENDOR_ID); + dev->vendor = vendev & 0xffff; + dev->device = vendev >> 16; + u32 classrev = pci_config_readl(bdf, PCI_CLASS_REVISION); + dev->class = classrev >> 16; + dev->prog_if = classrev >> 8; + dev->revision = classrev & 0xff; + dev->header_type = pci_config_readb(bdf, PCI_HEADER_TYPE); + u8 v = dev->header_type & 0x7f; + if (v == PCI_HEADER_TYPE_BRIDGE || v == PCI_HEADER_TYPE_CARDBUS) { + u8 secbus = pci_config_readb(bdf, PCI_SECONDARY_BUS); + dev->secondary_bus = secbus; + } +} // Find all PCI devices and populate PCIDevices linked list. void pci_probe_devices(void) @@ -145,21 +163,12 @@ pci_probe_devices(void) }
// Populate pci_device info. - dev->bdf = bdf; + pci_probe_device(bdf, dev); dev->parent = parent; dev->rootbus = rootbus; - u32 vendev = pci_config_readl(bdf, PCI_VENDOR_ID); - dev->vendor = vendev & 0xffff; - dev->device = vendev >> 16; - u32 classrev = pci_config_readl(bdf, PCI_CLASS_REVISION); - dev->class = classrev >> 16; - dev->prog_if = classrev >> 8; - dev->revision = classrev & 0xff; - dev->header_type = pci_config_readb(bdf, PCI_HEADER_TYPE); u8 v = dev->header_type & 0x7f; if (v == PCI_HEADER_TYPE_BRIDGE || v == PCI_HEADER_TYPE_CARDBUS) { - u8 secbus = pci_config_readb(bdf, PCI_SECONDARY_BUS); - dev->secondary_bus = secbus; + u8 secbus = dev->secondary_bus; if (secbus > bus && !busdevs[secbus]) busdevs[secbus] = dev; if (secbus > MaxPCIBus) diff --git a/src/hw/pci.h b/src/hw/pci.h index 9c7351d..a64f7c5 100644 --- a/src/hw/pci.h +++ b/src/hw/pci.h @@ -66,6 +66,7 @@ extern u64 pcimem64_start, pcimem64_end; extern struct hlist_head PCIDevices; extern int MaxPCIBus; int pci_probe_host(void); +void pci_probe_device(int bdf, struct pci_device *dev); void pci_probe_devices(void); static inline u32 pci_classprog(struct pci_device *pci) { return (pci->class << 8) | pci->prog_if; diff --git a/src/resume.c b/src/resume.c index d69429c..9626f9a 100644 --- a/src/resume.c +++ b/src/resume.c @@ -101,6 +101,14 @@ s3_resume(void) pic_setup(); smm_setup();
+ int bdf; + foreachbdf(bdf, 0) { + /* Create new pci_device struct and add to list. */ + struct pci_device pci; + pci_probe_device(bdf, &pci); + pci_bios_resume_device(&pci); + } + s3_resume_vga();
make_bios_readonly(); diff --git a/src/util.h b/src/util.h index 1b7d525..a4d4ee3 100644 --- a/src/util.h +++ b/src/util.h @@ -28,6 +28,7 @@ void boot_add_cbfs(void *data, const char *desc, int prio); void interactive_bootmenu(void); void bcv_prepboot(void); struct pci_device; +void pci_bios_resume_device(struct pci_device *pci); int bootprio_find_pci_device(struct pci_device *pci); int bootprio_find_scsi_device(struct pci_device *pci, int target, int lun); int bootprio_find_ata_device(struct pci_device *pci, int chanid, int slave);