From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754935AbdDJRf2 (ORCPT ); Mon, 10 Apr 2017 13:35:28 -0400 Received: from mail.linuxfoundation.org ([140.211.169.12]:35032 "EHLO mail.linuxfoundation.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753691AbdDJQqu (ORCPT ); Mon, 10 Apr 2017 12:46:50 -0400 From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Tomasz Nowicki , Dongdong Liu , Christopher Covington , Bjorn Helgaas , Sasha Levin Subject: [PATCH 4.9 081/152] PCI/ACPI: Check for platform-specific MCFG quirks Date: Mon, 10 Apr 2017 18:42:13 +0200 Message-Id: <20170410164204.344588861@linuxfoundation.org> X-Mailer: git-send-email 2.12.2 In-Reply-To: <20170410164159.934755016@linuxfoundation.org> References: <20170410164159.934755016@linuxfoundation.org> User-Agent: quilt/0.65 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 4.9-stable review patch. If anyone has any objections, please let me know. ------------------ From: Tomasz Nowicki [ Upstream commit 5b69b85ba1ddd36be01f5c57830b37a3c8256009 ] The PCIe spec (r3.0, sec 7.2.2) specifies an "Enhanced Configuration Access Mechanism" (ECAM) for memory-mapped access to configuration space. ECAM is required for PCIe systems unless there's a standard firmware interface for config access. In the absence of a firmware interface, we use pci_generic_ecam_ops, and on ACPI systems, we discover the ECAM space via the MCFG table and/or the _CBA method. Unfortunately some systems provide MCFG but don't implement ECAM according to spec, so we need a mechanism for quirks to make those systems work. Add an MCFG quirk mechanism to override the config accessor functions and/or the memory-mapped address space. A quirk is selected if it matches all of the following: - OEM ID - OEM Table ID - OEM Revision - PCI segment (from _SEG) - PCI bus number range (from _CRS, wildcard allowed) If the quirk specifies config accessor functions or a memory-mapped address range, these override the defaults. [bhelgaas: changelog, reorder quirk matching, fix oem_revision typo per Duc, add under #ifdef CONFIG_PCI_QUIRKS] Signed-off-by: Tomasz Nowicki Signed-off-by: Dongdong Liu Signed-off-by: Christopher Covington Signed-off-by: Bjorn Helgaas Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/pci_mcfg.c | 92 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 86 insertions(+), 6 deletions(-) --- a/drivers/acpi/pci_mcfg.c +++ b/drivers/acpi/pci_mcfg.c @@ -33,6 +33,69 @@ struct mcfg_entry { u8 bus_end; }; +#ifdef CONFIG_PCI_QUIRKS +struct mcfg_fixup { + char oem_id[ACPI_OEM_ID_SIZE + 1]; + char oem_table_id[ACPI_OEM_TABLE_ID_SIZE + 1]; + u32 oem_revision; + u16 segment; + struct resource bus_range; + struct pci_ecam_ops *ops; + struct resource cfgres; +}; + +#define MCFG_BUS_RANGE(start, end) DEFINE_RES_NAMED((start), \ + ((end) - (start) + 1), \ + NULL, IORESOURCE_BUS) +#define MCFG_BUS_ANY MCFG_BUS_RANGE(0x0, 0xff) + +static struct mcfg_fixup mcfg_quirks[] = { +/* { OEM_ID, OEM_TABLE_ID, REV, SEGMENT, BUS_RANGE, ops, cfgres }, */ +}; + +static char mcfg_oem_id[ACPI_OEM_ID_SIZE]; +static char mcfg_oem_table_id[ACPI_OEM_TABLE_ID_SIZE]; +static u32 mcfg_oem_revision; + +static int pci_mcfg_quirk_matches(struct mcfg_fixup *f, u16 segment, + struct resource *bus_range) +{ + if (!memcmp(f->oem_id, mcfg_oem_id, ACPI_OEM_ID_SIZE) && + !memcmp(f->oem_table_id, mcfg_oem_table_id, + ACPI_OEM_TABLE_ID_SIZE) && + f->oem_revision == mcfg_oem_revision && + f->segment == segment && + resource_contains(&f->bus_range, bus_range)) + return 1; + + return 0; +} +#endif + +static void pci_mcfg_apply_quirks(struct acpi_pci_root *root, + struct resource *cfgres, + struct pci_ecam_ops **ecam_ops) +{ +#ifdef CONFIG_PCI_QUIRKS + u16 segment = root->segment; + struct resource *bus_range = &root->secondary; + struct mcfg_fixup *f; + int i; + + for (i = 0, f = mcfg_quirks; i < ARRAY_SIZE(mcfg_quirks); i++, f++) { + if (pci_mcfg_quirk_matches(f, segment, bus_range)) { + if (f->cfgres.start) + *cfgres = f->cfgres; + if (f->ops) + *ecam_ops = f->ops; + dev_info(&root->device->dev, "MCFG quirk: ECAM at %pR for %pR with %ps\n", + cfgres, bus_range, *ecam_ops); + return; + } + } +#endif +} + /* List to save MCFG entries */ static LIST_HEAD(pci_mcfg_list); @@ -61,14 +124,24 @@ int pci_mcfg_lookup(struct acpi_pci_root } - if (!root->mcfg_addr) - return -ENXIO; - skip_lookup: memset(&res, 0, sizeof(res)); - res.start = root->mcfg_addr + (bus_res->start << 20); - res.end = res.start + (resource_size(bus_res) << 20) - 1; - res.flags = IORESOURCE_MEM; + if (root->mcfg_addr) { + res.start = root->mcfg_addr + (bus_res->start << 20); + res.end = res.start + (resource_size(bus_res) << 20) - 1; + res.flags = IORESOURCE_MEM; + } + + /* + * Allow quirks to override default ECAM ops and CFG resource + * range. This may even fabricate a CFG resource range in case + * MCFG does not have it. Invalid CFG start address means MCFG + * firmware bug or we need another quirk in array. + */ + pci_mcfg_apply_quirks(root, &res, &ops); + if (!res.start) + return -ENXIO; + *cfgres = res; *ecam_ops = ops; return 0; @@ -101,6 +174,13 @@ static __init int pci_mcfg_parse(struct list_add(&e->list, &pci_mcfg_list); } +#ifdef CONFIG_PCI_QUIRKS + /* Save MCFG IDs and revision for quirks matching */ + memcpy(mcfg_oem_id, header->oem_id, ACPI_OEM_ID_SIZE); + memcpy(mcfg_oem_table_id, header->oem_table_id, ACPI_OEM_TABLE_ID_SIZE); + mcfg_oem_revision = header->oem_revision; +#endif + pr_info("MCFG table detected, %d entries\n", n); return 0; }