From: Isaku Yamahata yamahata@valinux.co.jp
add q35 initialization functions.
[jbaron@redhat.com: restructured to current seabios base, updated pci base to 0xb0000000] [kraxel@redhat.com: join the two lpc init funcs into one]
Signed-off-by: Isaku Yamahata yamahata@valinux.co.jp Signed-off-by: Jason Baron jbaron@redhat.com Signed-off-by: Gerd Hoffmann kraxel@redhat.com --- src/acpi.c | 32 ++++++++++++++++++++- src/dev-q35.h | 46 +++++++++++++++++++++++++++++++ src/pciinit.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- src/shadow.c | 13 +++++++++ src/smm.c | 37 +++++++++++++++++++++++++ 5 files changed, 208 insertions(+), 5 deletions(-) create mode 100644 src/dev-q35.h
diff --git a/src/acpi.c b/src/acpi.c index 1fc170f..72c8fe8 100644 --- a/src/acpi.c +++ b/src/acpi.c @@ -13,6 +13,7 @@ #include "pci_regs.h" // PCI_INTERRUPT_LINE #include "ioport.h" // inl #include "paravirt.h" // qemu_cfg_irq0_override +#include "dev-q35.h" // qemu_cfg_irq0_override
/****************************************************/ /* ACPI tables init */ @@ -260,11 +261,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 };
@@ -739,7 +767,7 @@ build_srat(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 565a0e4..5bec062 100644 --- a/src/pciinit.c +++ b/src/pciinit.c @@ -12,6 +12,11 @@ #include "ioport.h" // PORT_ATA1_CMD_BASE #include "config.h" // CONFIG_* #include "xen.h" // usingXen +#include "memmap.h" // add_e820 +#include "dev-q35.h" + +/* PM Timer ticks per second (HZ) */ +#define PM_TIMER_FREQUENCY 3579545
#define PCI_DEVICE_MEM_MIN 0x1000 #define PCI_BRIDGE_IO_MIN 0x1000 @@ -121,6 +126,45 @@ 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]); + + /* 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); + + pmtimer_init(PORT_ACPI_PM_BASE + 0x08, PM_TIMER_FREQUENCY / 1000); +} + static void storage_ide_init(struct pci_device *pci, void *arg) { /* IDE: we map it as in ISA mode */ @@ -150,9 +194,6 @@ static void apple_macio_init(struct pci_device *pci, void *arg) pci_set_io_region_addr(pci, 0, 0x80800000, 0); }
-/* PM Timer ticks per second (HZ) */ -#define PM_TIMER_FREQUENCY 3579545 - /* PIIX4 Power Management device (for ACPI) */ static void piix4_pm_init(struct pci_device *pci, void *arg) { @@ -168,12 +209,27 @@ static void piix4_pm_init(struct pci_device *pci, void *arg) pmtimer_init(PORT_ACPI_PM_BASE + 0x08, PM_TIMER_FREQUENCY / 1000); }
+/* 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[] = { /* 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),
/* STORAGE IDE */ PCI_DEVICE_CLASS(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, @@ -192,6 +248,8 @@ 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_SMBUS, + ich9_smbus_init),
/* 0xff00 */ PCI_DEVICE_CLASS(PCI_VENDOR_ID_APPLE, 0x0017, 0xff00, apple_macio_init), @@ -240,9 +298,30 @@ void i440fx_mem_addr_init(struct pci_device *dev, void *arg) pcimem_start = 0xc0000000; }
+void mch_mem_addr_init(struct pci_device *dev, void *arg) +{ + u64 addr = Q35_HOST_BRIDGE_PCIEXBAR_ADDR; + u32 size = Q35_HOST_BRIDGE_PCIEXBAR_SIZE; + + /* setup mmconfig */ + u16 bdf = dev->bdf; + u32 upper = addr >> 32; + u32 lower = (addr & 0xffffffff) | Q35_HOST_BRIDGE_PCIEXBAREN; + 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); + add_e820(addr, size, E820_RESERVED); + + /* setup pci i/o window (above mmconfig) */ + pcimem_start = addr + size; + mtrr_base = addr + size; +} + static const struct pci_device_id pci_platform_tbl[] = { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441, i440fx_mem_addr_init), + PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_Q35_MCH, + mch_mem_addr_init), PCI_DEVICE_END };
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, };