diff --git a/xen/arch/x86/cpu/amd.c b/xen/arch/x86/cpu/amd.c index f1d1a999ad..591a566f57 100644 --- a/xen/arch/x86/cpu/amd.c +++ b/xen/arch/x86/cpu/amd.c @@ -607,6 +607,14 @@ static void __devinit init_amd(struct cpuinfo_x86 *c) #ifdef __x86_64__ /* AMD CPUs do not support SYSENTER outside of legacy mode. */ clear_bit(X86_FEATURE_SEP, c->x86_capability); + + if (c->x86 == 0x10) { + /* do this for boot cpu */ + if (c == &boot_cpu_data) + check_enable_amd_mmconf_dmi(); + + fam10h_check_enable_mmcfg(); + } #endif /* Prevent TSC drift in non single-processor, single-core platforms. */ diff --git a/xen/arch/x86/x86_64/Makefile b/xen/arch/x86/x86_64/Makefile index 5d1335c460..1309ff33ba 100644 --- a/xen/arch/x86/x86_64/Makefile +++ b/xen/arch/x86/x86_64/Makefile @@ -7,6 +7,7 @@ obj-y += traps.o obj-y += machine_kexec.o obj-y += pci.o obj-y += acpi_mmcfg.o +obj-y += mmconf-fam10h.o obj-y += mmconfig_64.o obj-y += mmconfig-shared.o obj-y += compat.o diff --git a/xen/arch/x86/x86_64/mmconf-fam10h.c b/xen/arch/x86/x86_64/mmconf-fam10h.c new file mode 100644 index 0000000000..9d5de6cc06 --- /dev/null +++ b/xen/arch/x86/x86_64/mmconf-fam10h.c @@ -0,0 +1,210 @@ +/* + * AMD Family 10h mmconfig enablement (taken from Linux 2.6.36) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mmconfig.h" + +struct pci_hostbridge_probe { + u32 bus; + u32 slot; + u32 vendor; + u32 device; +}; + +static u64 __cpuinitdata fam10h_pci_mmconf_base; + +static struct pci_hostbridge_probe pci_probes[] __cpuinitdata = { + { 0, 0x18, PCI_VENDOR_ID_AMD, 0x1200 }, + { 0xff, 0, PCI_VENDOR_ID_AMD, 0x1200 }, +}; + +#define UNIT (1ULL << (5 + 3 + 12)) +#define MASK (~(UNIT - 1)) +#define SIZE (UNIT << 8) +/* need to avoid (0xfd<<32) and (0xfe<<32), ht used space */ +#define FAM10H_PCI_MMCONF_BASE (0xfcULL<<32) +#define BASE_VALID(b) ((b != (0xfdULL << 32)) && (b != (0xfeULL << 32))) +static void __init get_fam10h_pci_mmconf_base(void) +{ + unsigned int i, j, bus, slot, hi_mmio_num; + u32 address; + u64 val, tom2, start, end; + struct range { + u64 start, end; + } range[8]; + + for (i = 0; i < ARRAY_SIZE(pci_probes); i++) { + u32 id; + u16 device; + u16 vendor; + + bus = pci_probes[i].bus; + slot = pci_probes[i].slot; + id = pci_conf_read32(bus, slot, 0, PCI_VENDOR_ID); + + vendor = id & 0xffff; + device = (id>>16) & 0xffff; + if (pci_probes[i].vendor == vendor && + pci_probes[i].device == device) + break; + } + + if (i >= ARRAY_SIZE(pci_probes)) + return; + + /* SYS_CFG */ + address = MSR_K8_SYSCFG; + rdmsrl(address, val); + + /* TOP_MEM2 is not enabled? */ + if (!(val & (1<<21))) { + tom2 = 0; + } else { + /* TOP_MEM2 */ + address = MSR_K8_TOP_MEM2; + rdmsrl(address, val); + tom2 = val & 0xffffff800000ULL; + } + + /* + * need to check if the range is in the high mmio range that is + * above 4G + */ + for (hi_mmio_num = i = 0; i < 8; i++) { + val = pci_conf_read32(bus, slot, 1, 0x80 + (i << 3)); + if (!(val & 3)) + continue; + + start = (val & 0xffffff00) << 8; /* 39:16 on 31:8*/ + val = pci_conf_read32(bus, slot, 1, 0x84 + (i << 3)); + end = ((val & 0xffffff00) << 8) | 0xffff; /* 39:16 on 31:8*/ + + if (!end) + continue; + + for (j = hi_mmio_num; j; --j) { + if (range[j - 1].start < start) + break; + range[j] = range[j - 1]; + } + range[j].start = start; + range[j].end = end; + hi_mmio_num++; + } + + start = FAM10H_PCI_MMCONF_BASE; + if (start <= tom2) + start = (tom2 + 2 * UNIT - 1) & MASK; + + if (!hi_mmio_num) + goto out; + + if (range[hi_mmio_num - 1].end < start) + goto out; + if (range[0].start > start + SIZE) + goto out; + + /* need to find one window */ + start = (range[0].start & MASK) - UNIT; + if (start > tom2 && BASE_VALID(start)) + goto out; + start = (range[hi_mmio_num - 1].end + UNIT) & MASK; + if (start > tom2 && BASE_VALID(start)) + goto out; + /* need to find window between ranges */ + for (i = 1; i < hi_mmio_num; i++) { + start = (range[i - 1].end + UNIT) & MASK; + end = range[i].start & MASK; + if (end >= start + SIZE && start > tom2 && BASE_VALID(start)) + goto out; + } + return; + +out: + fam10h_pci_mmconf_base = start; +} + +void __cpuinit fam10h_check_enable_mmcfg(void) +{ + u64 val; + bool_t print = opt_cpu_info; + + if (!(pci_probe & PCI_CHECK_ENABLE_AMD_MMCONF)) + return; + + rdmsrl(MSR_FAM10H_MMIO_CONF_BASE, val); + + /* try to make sure that AP's setting is identical to BSP setting */ + if (val & FAM10H_MMIO_CONF_ENABLE) { + unsigned busnbits; + busnbits = (val >> FAM10H_MMIO_CONF_BUSRANGE_SHIFT) & + FAM10H_MMIO_CONF_BUSRANGE_MASK; + + /* only trust the one handle 256 buses, if acpi=off */ + if (!acpi_pci_disabled || busnbits >= 8) { + u64 base = val & MASK; + + if (!fam10h_pci_mmconf_base) { + fam10h_pci_mmconf_base = base; + return; + } + if (fam10h_pci_mmconf_base == base) + return; + } + } + + /* + * if it is not enabled, try to enable it and assume only one segment + * with 256 buses + */ + /* only try to get setting from BSP */ + if (!fam10h_pci_mmconf_base) { + get_fam10h_pci_mmconf_base(); + print = 1; + } + if (!fam10h_pci_mmconf_base) { + pci_probe &= ~PCI_CHECK_ENABLE_AMD_MMCONF; + return; + } + + if (print) + printk(KERN_INFO "Enable MMCONFIG on AMD Fam10h at %"PRIx64"\n", + fam10h_pci_mmconf_base); + val &= ~((FAM10H_MMIO_CONF_BASE_MASK<