Following what Linux did quite a while ago, don't generally disallow MMCFG base addresses to live above the 4Gb boundary: New systems are assumed to be fine, and SGI ones are, too. Signed-off-by: Jan Beulich --- a/xen/arch/x86/dmi_scan.c +++ b/xen/arch/x86/dmi_scan.c @@ -520,6 +520,76 @@ fail: d++; return count; } +/** + * dmi_get_date - parse a DMI date + * @field: data index (see enum dmi_field) + * @yearp: optional out parameter for the year + * @monthp: optional out parameter for the month + * @dayp: optional out parameter for the day + * + * The date field is assumed to be in the form resembling + * [mm[/dd]]/yy[yy] and the result is stored in the out + * parameters any or all of which can be omitted. + * + * If the field doesn't exist, all out parameters are set to zero + * and false is returned. Otherwise, true is returned with any + * invalid part of date set to zero. + * + * On return, year, month and day are guaranteed to be in the + * range of [0,9999], [0,12] and [0,31] respectively. + */ +bool_t __init dmi_get_date(int field, int *yearp, int *monthp, int *dayp) +{ + int year = 0, month = 0, day = 0; + bool_t exists; + const char *s, *e, *y; + + s = field < DMI_STRING_MAX ? dmi_ident[field] : NULL; + exists = !!s; + if (!exists) + goto out; + + /* + * Determine year first. We assume the date string resembles + * mm/dd/yy[yy] but the original code extracted only the year + * from the end. Keep the behavior in the spirit of no + * surprises. + */ + y = strrchr(s, '/'); + if (!y) + goto out; + + y++; + year = simple_strtoul(y, &e, 10); + if (y != e && year < 100) { /* 2-digit year */ + year += 1900; + if (year < 1996) /* no dates < spec 1.0 */ + year += 100; + } + if (year > 9999) /* year should fit in %04d */ + year = 0; + + /* parse the mm and dd */ + month = simple_strtoul(s, &e, 10); + if (s == e || *e != '/' || !month || month > 12) { + month = 0; + goto out; + } + + s = e + 1; + day = simple_strtoul(s, &e, 10); + if (s == y || s == e || *e != '/' || day > 31) + day = 0; +out: + if (yearp) + *yearp = year; + if (monthp) + *monthp = month; + if (dayp) + *dayp = day; + return exists; +} + void __init dmi_end_boot(void) { unsigned int i; --- a/xen/arch/x86/x86_64/acpi_mmcfg.c +++ b/xen/arch/x86/x86_64/acpi_mmcfg.c @@ -48,6 +48,30 @@ struct acpi_mcfg_allocation *pci_mmcfg_config; int pci_mmcfg_config_num; +static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, + struct acpi_mcfg_allocation *cfg) +{ + int year; + + if (cfg->address < 0xFFFFFFFF) + return 0; + + if (!strcmp(mcfg->header.oem_id, "SGI") || + !strcmp(mcfg->header.oem_id, "SGI2")) + return 0; + + if (mcfg->header.revision >= 1 && + dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && + year >= 2010) + return 0; + + printk(KERN_ERR "MCFG region for %04x:%02x-%02x at %#"PRIx64 + " (above 4GB) ignored\n", + cfg->pci_segment, cfg->start_bus_number, cfg->end_bus_number, + cfg->address); + return -EINVAL; +} + int __init acpi_parse_mcfg(struct acpi_table_header *header) { struct acpi_table_mcfg *mcfg; @@ -82,9 +106,7 @@ int __init acpi_parse_mcfg(struct acpi_t pci_mmcfg_config_num * sizeof(*pci_mmcfg_config)); for (i = 0; i < pci_mmcfg_config_num; ++i) { - if (pci_mmcfg_config[i].address > 0xFFFFFFFF) { - printk(KERN_ERR PREFIX - "MMCONFIG not in low 4GB of memory\n"); + if (acpi_mcfg_check_entry(mcfg, &pci_mmcfg_config[i])) { xfree(pci_mmcfg_config); pci_mmcfg_config_num = 0; return -ENODEV; --- a/xen/include/xen/dmi.h +++ b/xen/include/xen/dmi.h @@ -36,6 +36,7 @@ extern int dmi_check_system(struct dmi_s extern void dmi_scan_machine(void); extern int dmi_get_table(u32 *base, u32 *len); extern void dmi_efi_get_table(void *); +bool_t dmi_get_date(int field, int *yearp, int *monthp, int *dayp); extern void dmi_end_boot(void); #endif /* __DMI_H__ */