From: Isaku Yamahata yamahata@valinux.co.jp
add q35 initialization functions.
[jbaron@redhat.com: restructured to current seabios base, updated pci base to 0xb0000000] Signed-off-by: Isaku Yamahata yamahata@valinux.co.jp Signed-off-by: Jason Baron jbaron@redhat.com --- src/acpi.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++- src/dev-q35.h | 46 ++++++++++++++++++++++++++++++++ src/pciinit.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- src/shadow.c | 13 +++++++++ src/smm.c | 37 +++++++++++++++++++++++++ 5 files changed, 245 insertions(+), 3 deletions(-) create mode 100644 src/dev-q35.h
diff --git a/src/acpi.c b/src/acpi.c index 60a22f5..4f6a0c5 100644 --- a/src/acpi.c +++ b/src/acpi.c @@ -14,6 +14,7 @@ #include "ioport.h" // inl #include "paravirt.h" // qemu_cfg_irq0_override #include "memmap.h" +#include "dev-q35.h"
/****************************************************/ /* ACPI tables init */ @@ -261,11 +262,38 @@ static void piix4_fadt_init(struct pci_device *pci, void *arg) (1 << 15)); }
+/* PCI_VENDOR_ID_INTEL && PCI_DEVICE_ID_INTEL_ICH9_LPC */ +void ich9_lpc_fadt_init(struct pci_device *dev, void *arg) +{ + struct fadt_descriptor_rev1 *fadt = arg; + + fadt->model = 1; + fadt->reserved1 = 0; + fadt->sci_int = cpu_to_le16(9); + fadt->smi_cmd = cpu_to_le32(PORT_SMI_CMD); + fadt->acpi_enable = ICH9_ACPI_ENABLE; + fadt->acpi_disable = ICH9_ACPI_DISABLE; + fadt->pm1a_evt_blk = cpu_to_le32(PORT_ACPI_PM_BASE); + fadt->pm1a_cnt_blk = cpu_to_le32(PORT_ACPI_PM_BASE + 0x04); + fadt->pm_tmr_blk = cpu_to_le32(PORT_ACPI_PM_BASE + 0x08); + fadt->gpe0_blk = cpu_to_le32(PORT_ACPI_PM_BASE + ICH9_PMIO_GPE0_STS); + fadt->pm1_evt_len = 4; + fadt->pm1_cnt_len = 2; + fadt->pm_tmr_len = 4; + fadt->gpe0_blk_len = ICH9_PMIO_GPE0_BLK_LEN; + fadt->plvl2_lat = cpu_to_le16(0xfff); // C2 state not supported + fadt->plvl3_lat = cpu_to_le16(0xfff); // C3 state not supported + /* WBINVD + PROC_C1 + SLP_BUTTON + FIX_RTC + RTC_S4 */ + fadt->flags = cpu_to_le32((1 << 0) | (1 << 2) | (1 << 5) | (1 << 6) | + (1 << 7)); +} + static const struct pci_device_id fadt_init_tbl[] = { /* PIIX4 Power Management device (for ACPI) */ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, piix4_fadt_init), - + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_LPC, + ich9_lpc_fadt_init), PCI_DEVICE_END };
@@ -743,11 +771,49 @@ struct acpi_mcfg { struct e820entry *e820; };
+/* PCI_VENDOR_ID_INTEL && DEVICE_ID_INTEL_Q35_MCH */ +void mch_mcfg_find(struct pci_device *dev, void *arg) +{ + struct acpi_mcfg *mcfg = arg; + + mcfg->nr = 1; +} + +/* PCI_VENDOR_ID_INTEL && DEVICE_ID_INTEL_Q35_MCH */ +void mch_mcfg_init(struct pci_device *dev, void *arg) +{ + u16 bdf = dev->bdf; + + u64 addr = Q35_HOST_BRIDGE_PCIEXBAR_ADDR | Q35_HOST_BRIDGE_PCIEXBAREN; + u32 upper = addr >> 32; + u32 lower = addr & 0xffffffff; + + /* at first disable the region. and then update/enable it. */ + pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR, 0); + pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR + 4, upper); + pci_config_writel(bdf, Q35_HOST_BRIDGE_PCIEXBAR, lower); + + struct acpi_mcfg *mcfg = arg; + struct acpi_mcfg_allocation *alloc = &mcfg->mcfg->allocation[0]; + alloc->address = Q35_HOST_BRIDGE_PCIEXBAR_ADDR; + alloc->pci_segment = Q35_HOST_PCIE_PCI_SEGMENT; + alloc->start_bus_number = Q35_HOST_PCIE_START_BUS_NUMBER; + alloc->end_bus_number = Q35_HOST_PCIE_END_BUS_NUMBER; + + mcfg->e820->start = Q35_HOST_BRIDGE_PCIEXBAR_ADDR; + mcfg->e820->size = Q35_HOST_BRIDGE_PCIEXBAR_SIZE; + mcfg->e820->type = E820_RESERVED; +} + static const struct pci_device_id mcfg_find_tbl[] = { + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_Q35_MCH, + mch_mcfg_find), PCI_DEVICE_END, };
static const struct pci_device_id mcfg_init_tbl[] = { + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_Q35_MCH, + mch_mcfg_init), PCI_DEVICE_END, };
@@ -810,7 +876,7 @@ build_mcfg(void) static const struct pci_device_id acpi_find_tbl[] = { /* PIIX4 Power Management device. */ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, NULL), - + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_LPC, NULL), PCI_DEVICE_END, };
diff --git a/src/dev-q35.h b/src/dev-q35.h new file mode 100644 index 0000000..6ae039f --- /dev/null +++ b/src/dev-q35.h @@ -0,0 +1,46 @@ +#ifndef __DEV_Q35_H +#define __DEV_Q35_H + +#include "types.h" // u16 + +#define PCI_DEVICE_ID_INTEL_Q35_MCH 0x29c0 +#define Q35_HOST_BRIDGE_PAM0 0x90 +#define Q35_HOST_BRIDGE_SMRAM 0x9d +#define Q35_HOST_BRIDGE_PCIEXBAR 0x60 +#define Q35_HOST_BRIDGE_PCIEXBAR_SIZE (256 * 1024 * 1024) +#define Q35_HOST_BRIDGE_PCIEXBAR_ADDR 0xb0000000 +#define Q35_HOST_BRIDGE_PCIEXBAREN ((u64)1) +#define Q35_HOST_PCIE_PCI_SEGMENT 0 +#define Q35_HOST_PCIE_START_BUS_NUMBER 0 +#define Q35_HOST_PCIE_END_BUS_NUMBER 255 + +#define PCI_DEVICE_ID_INTEL_ICH9_LPC 0x2918 +#define ICH9_LPC_PMBASE 0x40 +#define ICH9_LPC_PMBASE_RTE 0x1 + +#define ICH9_LPC_ACPI_CTRL 0x44 +#define ICH9_LPC_ACPI_CTRL_ACPI_EN 0x80 +#define ICH9_LPC_PIRQA_ROUT 0x60 +#define ICH9_LPC_PIRQE_ROUT 0x68 +#define ICH9_LPC_PIRQ_ROUT_IRQEN 0x80 +#define ICH9_LPC_PORT_ELCR1 0x4d0 +#define ICH9_LPC_PORT_ELCR2 0x4d1 +#define PCI_DEVICE_ID_INTEL_ICH9_SMBUS 0x2930 +#define ICH9_SMB_SMB_BASE 0x20 +#define ICH9_SMB_HOSTC 0x40 +#define ICH9_SMB_HOSTC_HST_EN 0x01 + +#define ICH9_ACPI_ENABLE 0x2 +#define ICH9_ACPI_DISABLE 0x3 + +/* ICH9 LPC PM I/O registers are 128 ports and 128-aligned */ +#define ICH9_PMIO_GPE0_STS 0x20 +#define ICH9_PMIO_GPE0_BLK_LEN 0x10 +#define ICH9_PMIO_SMI_EN 0x30 +#define ICH9_PMIO_SMI_EN_APMC_EN (1 << 5) + +/* FADT ACPI_ENABLE/ACPI_DISABLE */ +#define ICH9_APM_ACPI_ENABLE 0x2 +#define ICH9_APM_ACPI_DISABLE 0x3 + +#endif // dev-q35.h diff --git a/src/pciinit.c b/src/pciinit.c index 7e76379..93a53e8 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -12,6 +12,7 @@ #include "ioport.h" // PORT_ATA1_CMD_BASE #include "config.h" // CONFIG_* #include "xen.h" // usingXen +#include "dev-q35.h" // usingXen
#define PCI_DEVICE_MEM_MIN 0x1000 #define PCI_BRIDGE_IO_MIN 0x1000 @@ -121,12 +122,44 @@ static void piix_isa_bridge_init(struct pci_device *pci, void *arg) dprintf(1, "PIIX3/PIIX4 init: elcr=%02x %02x\n", elcr[0], elcr[1]); }
+/* ICH9 LPC PCI to ISA bridge */ +/* PCI_VENDOR_ID_INTEL && PCI_DEVICE_ID_INTEL_ICH9_LPC */ +void mch_isa_bridge_init(struct pci_device *dev, void *arg) +{ + u16 bdf = dev->bdf; + int i, irq; + u8 elcr[2]; + + elcr[0] = 0x00; + elcr[1] = 0x00; + + for (i = 0; i < 4; i++) { + irq = pci_irqs[i]; + /* set to trigger level */ + elcr[irq >> 3] |= (1 << (irq & 7)); + + /* activate irq remapping in LPC */ + + /* PIRQ[A-D] routing */ + pci_config_writeb(bdf, ICH9_LPC_PIRQA_ROUT + i, + irq | ICH9_LPC_PIRQ_ROUT_IRQEN); + /* PIRQ[E-H] routing */ + pci_config_writeb(bdf, ICH9_LPC_PIRQE_ROUT + i, + irq | ICH9_LPC_PIRQ_ROUT_IRQEN); + } + outb(elcr[0], ICH9_LPC_PORT_ELCR1); + outb(elcr[1], ICH9_LPC_PORT_ELCR2); + dprintf(1, "Q35 LPC init: elcr=%02x %02x\n", elcr[0], elcr[1]); +} + static const struct pci_device_id pci_isa_bridge_tbl[] = { /* PIIX3/PIIX4 PCI to ISA bridge */ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, piix_isa_bridge_init), PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, piix_isa_bridge_init), + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_LPC, + mch_isa_bridge_init),
PCI_DEVICE_END }; @@ -200,11 +233,40 @@ static void piix4_pm_init(struct pci_device *pci, void *arg) pmtimer_init(PORT_ACPI_PM_BASE + 0x08, PM_TIMER_FREQUENCY / 1000); }
+/* ICH9 LPC Power Management device (for ACPI) */ +/* PCI_VENDOR_ID_INTEL && PCI_DEVICE_ID_INTEL_ICH9_LPC */ +void ich9_lpc_pm_init(struct pci_device *dev, void *arg) +{ + u16 bdf = dev->bdf; + /* pm io base */ + pci_config_writel(bdf, ICH9_LPC_PMBASE, + PORT_ACPI_PM_BASE | ICH9_LPC_PMBASE_RTE); + + /* acpi enable, SCI: IRQ9 000b = irq9*/ + pci_config_writeb(bdf, ICH9_LPC_ACPI_CTRL, ICH9_LPC_ACPI_CTRL_ACPI_EN); +} + +/* ICH9 SMBUS */ +/* PCI_VENDOR_ID_INTEL && PCI_DEVICE_ID_INTEL_ICH9_SMBUS */ +void ich9_smbus_init(struct pci_device *dev, void *arg) +{ + u16 bdf = dev->bdf; + /* map smbus into io space */ + pci_config_writel(bdf, ICH9_SMB_SMB_BASE, + PORT_SMB_BASE | PCI_BASE_ADDRESS_SPACE_IO); + + /* enable SMBus */ + pci_config_writeb(bdf, ICH9_SMB_HOSTC, ICH9_SMB_HOSTC_HST_EN); +} + static const struct pci_device_id pci_device_tbl[] = { /* PIIX4 Power Management device (for ACPI) */ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, piix4_pm_init), - + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_LPC, + ich9_lpc_pm_init), + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_SMBUS, + ich9_smbus_init), PCI_DEVICE_END, };
@@ -598,10 +660,28 @@ static void pci_region_map_entries(struct pci_bus *busses, struct pci_region *r) } }
+void mch_mem_addr_init(struct pci_device *dev, void *arg) +{ + u64 *start = (u64 *)arg; + /* mmconfig space */ + *start = Q35_HOST_BRIDGE_PCIEXBAR_ADDR + + Q35_HOST_BRIDGE_PCIEXBAR_SIZE; + mtrr_base = *start; +} + +static const struct pci_device_id pci_mem_addr_tbl[] = { + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_Q35_MCH, + mch_mem_addr_init), + PCI_DEVICE_END, +}; + static void pci_bios_map_devices(struct pci_bus *busses) { pcimem_start = RamSize;
+ /* let's add in mmconfig space on q35 */ + pci_find_init_device(pci_mem_addr_tbl, &pcimem_start); + if (pci_bios_init_root_regions(busses)) { struct pci_region r64_mem, r64_pref; r64_mem.list = NULL; diff --git a/src/shadow.c b/src/shadow.c index 11c4d5e..a2195da 100644 --- a/src/shadow.c +++ b/src/shadow.c @@ -11,6 +11,7 @@ #include "pci_ids.h" // PCI_VENDOR_ID_INTEL #include "pci_regs.h" // PCI_VENDOR_ID #include "xen.h" // usingXen +#include "dev-q35.h" // PCI_VENDOR_ID_INTEL
// On the emulators, the bios at 0xf0000 is also at 0xffff0000 #define BIOS_SRC_OFFSET 0xfff00000 @@ -101,9 +102,16 @@ static void i440fx_bios_make_readonly(struct pci_device *pci, void *arg) make_bios_readonly_intel(pci->bdf, I440FX_PAM0); }
+void mch_bios_make_readonly(struct pci_device *pci, void *arg) +{ + make_bios_readonly_intel(pci->bdf, Q35_HOST_BRIDGE_PAM0); +} + static const struct pci_device_id dram_controller_make_readonly_tbl[] = { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441, i440fx_bios_make_readonly), + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_Q35_MCH, + mch_bios_make_readonly), PCI_DEVICE_END };
@@ -127,6 +135,11 @@ make_bios_writable(void) make_bios_writable_intel(bdf, I440FX_PAM0); return; } + if (vendor == PCI_VENDOR_ID_INTEL + && device == PCI_DEVICE_ID_INTEL_Q35_MCH) { + make_bios_writable_intel(bdf, Q35_HOST_BRIDGE_PAM0); + return; + } } dprintf(1, "Unable to unlock ram - bridge not found\n"); } diff --git a/src/smm.c b/src/smm.c index d0d1476..7977ac7 100644 --- a/src/smm.c +++ b/src/smm.c @@ -11,6 +11,7 @@ #include "ioport.h" // outb #include "pci_ids.h" // PCI_VENDOR_ID_INTEL #include "xen.h" // usingXen +#include "dev-q35.h"
ASM32FLAT( ".global smm_relocation_start\n" @@ -137,9 +138,45 @@ static void piix4_apmc_smm_init(struct pci_device *pci, void *arg) pci_config_writeb(i440_pci->bdf, I440FX_SMRAM, 0x02 | 0x08); }
+/* PCI_VENDOR_ID_INTEL && PCI_DEVICE_ID_INTEL_ICH9_LPC */ +void ich9_lpc_apmc_smm_init(struct pci_device *dev, void *arg) +{ + struct pci_device *mch_dev; + int mch_bdf; + + // This code is hardcoded for Q35 Power Management device. + mch_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_Q35_MCH); + mch_bdf = mch_dev->bdf; + + if (mch_bdf < 0) + return; + + /* check if SMM init is already done */ + u32 value = inl(PORT_ACPI_PM_BASE + ICH9_PMIO_SMI_EN); + if (value & ICH9_PMIO_SMI_EN_APMC_EN) + return; + + /* enable the SMM memory window */ + pci_config_writeb(mch_bdf, Q35_HOST_BRIDGE_SMRAM, 0x02 | 0x48); + + smm_save_and_copy(); + + /* enable SMI generation when writing to the APMC register */ + outl(value | ICH9_PMIO_SMI_EN_APMC_EN, + PORT_ACPI_PM_BASE + ICH9_PMIO_SMI_EN); + + smm_relocate_and_restore(); + + /* close the SMM memory window and enable normal SMM */ + pci_config_writeb(mch_bdf, Q35_HOST_BRIDGE_SMRAM, 0x02 | 0x08); +} + static const struct pci_device_id smm_init_tbl[] = { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, piix4_apmc_smm_init), + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_LPC, + ich9_lpc_apmc_smm_init),
PCI_DEVICE_END, };