cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

pci_mcfg.c (9298B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2016 Broadcom
      4 *	Author: Jayachandran C <jchandra@broadcom.com>
      5 * Copyright (C) 2016 Semihalf
      6 * 	Author: Tomasz Nowicki <tn@semihalf.com>
      7 */
      8
      9#define pr_fmt(fmt) "ACPI: " fmt
     10
     11#include <linux/kernel.h>
     12#include <linux/pci.h>
     13#include <linux/pci-acpi.h>
     14#include <linux/pci-ecam.h>
     15
     16/* Structure to hold entries from the MCFG table */
     17struct mcfg_entry {
     18	struct list_head	list;
     19	phys_addr_t		addr;
     20	u16			segment;
     21	u8			bus_start;
     22	u8			bus_end;
     23};
     24
     25#ifdef CONFIG_PCI_QUIRKS
     26struct mcfg_fixup {
     27	char oem_id[ACPI_OEM_ID_SIZE + 1];
     28	char oem_table_id[ACPI_OEM_TABLE_ID_SIZE + 1];
     29	u32 oem_revision;
     30	u16 segment;
     31	struct resource bus_range;
     32	const struct pci_ecam_ops *ops;
     33	struct resource cfgres;
     34};
     35
     36#define MCFG_BUS_RANGE(start, end)	DEFINE_RES_NAMED((start),	\
     37						((end) - (start) + 1),	\
     38						NULL, IORESOURCE_BUS)
     39#define MCFG_BUS_ANY			MCFG_BUS_RANGE(0x0, 0xff)
     40
     41static struct mcfg_fixup mcfg_quirks[] = {
     42/*	{ OEM_ID, OEM_TABLE_ID, REV, SEGMENT, BUS_RANGE, ops, cfgres }, */
     43
     44#define AL_ECAM(table_id, rev, seg, ops) \
     45	{ "AMAZON", table_id, rev, seg, MCFG_BUS_ANY, ops }
     46
     47	AL_ECAM("GRAVITON", 0, 0, &al_pcie_ops),
     48	AL_ECAM("GRAVITON", 0, 1, &al_pcie_ops),
     49	AL_ECAM("GRAVITON", 0, 2, &al_pcie_ops),
     50	AL_ECAM("GRAVITON", 0, 3, &al_pcie_ops),
     51	AL_ECAM("GRAVITON", 0, 4, &al_pcie_ops),
     52	AL_ECAM("GRAVITON", 0, 5, &al_pcie_ops),
     53	AL_ECAM("GRAVITON", 0, 6, &al_pcie_ops),
     54	AL_ECAM("GRAVITON", 0, 7, &al_pcie_ops),
     55
     56#define QCOM_ECAM32(seg) \
     57	{ "QCOM  ", "QDF2432 ", 1, seg, MCFG_BUS_ANY, &pci_32b_ops }
     58
     59	QCOM_ECAM32(0),
     60	QCOM_ECAM32(1),
     61	QCOM_ECAM32(2),
     62	QCOM_ECAM32(3),
     63	QCOM_ECAM32(4),
     64	QCOM_ECAM32(5),
     65	QCOM_ECAM32(6),
     66	QCOM_ECAM32(7),
     67
     68#define HISI_QUAD_DOM(table_id, seg, ops) \
     69	{ "HISI  ", table_id, 0, (seg) + 0, MCFG_BUS_ANY, ops }, \
     70	{ "HISI  ", table_id, 0, (seg) + 1, MCFG_BUS_ANY, ops }, \
     71	{ "HISI  ", table_id, 0, (seg) + 2, MCFG_BUS_ANY, ops }, \
     72	{ "HISI  ", table_id, 0, (seg) + 3, MCFG_BUS_ANY, ops }
     73
     74	HISI_QUAD_DOM("HIP05   ",  0, &hisi_pcie_ops),
     75	HISI_QUAD_DOM("HIP06   ",  0, &hisi_pcie_ops),
     76	HISI_QUAD_DOM("HIP07   ",  0, &hisi_pcie_ops),
     77	HISI_QUAD_DOM("HIP07   ",  4, &hisi_pcie_ops),
     78	HISI_QUAD_DOM("HIP07   ",  8, &hisi_pcie_ops),
     79	HISI_QUAD_DOM("HIP07   ", 12, &hisi_pcie_ops),
     80
     81#define THUNDER_PEM_RES(addr, node) \
     82	DEFINE_RES_MEM((addr) + ((u64) (node) << 44), 0x39 * SZ_16M)
     83
     84#define THUNDER_PEM_QUIRK(rev, node) \
     85	{ "CAVIUM", "THUNDERX", rev, 4 + (10 * (node)), MCFG_BUS_ANY,	    \
     86	  &thunder_pem_ecam_ops, THUNDER_PEM_RES(0x88001f000000UL, node) },  \
     87	{ "CAVIUM", "THUNDERX", rev, 5 + (10 * (node)), MCFG_BUS_ANY,	    \
     88	  &thunder_pem_ecam_ops, THUNDER_PEM_RES(0x884057000000UL, node) },  \
     89	{ "CAVIUM", "THUNDERX", rev, 6 + (10 * (node)), MCFG_BUS_ANY,	    \
     90	  &thunder_pem_ecam_ops, THUNDER_PEM_RES(0x88808f000000UL, node) },  \
     91	{ "CAVIUM", "THUNDERX", rev, 7 + (10 * (node)), MCFG_BUS_ANY,	    \
     92	  &thunder_pem_ecam_ops, THUNDER_PEM_RES(0x89001f000000UL, node) },  \
     93	{ "CAVIUM", "THUNDERX", rev, 8 + (10 * (node)), MCFG_BUS_ANY,	    \
     94	  &thunder_pem_ecam_ops, THUNDER_PEM_RES(0x894057000000UL, node) },  \
     95	{ "CAVIUM", "THUNDERX", rev, 9 + (10 * (node)), MCFG_BUS_ANY,	    \
     96	  &thunder_pem_ecam_ops, THUNDER_PEM_RES(0x89808f000000UL, node) }
     97
     98#define THUNDER_ECAM_QUIRK(rev, seg)					\
     99	{ "CAVIUM", "THUNDERX", rev, seg, MCFG_BUS_ANY,			\
    100	&pci_thunder_ecam_ops }
    101
    102	/* SoC pass2.x */
    103	THUNDER_PEM_QUIRK(1, 0),
    104	THUNDER_PEM_QUIRK(1, 1),
    105	THUNDER_ECAM_QUIRK(1, 10),
    106
    107	/* SoC pass1.x */
    108	THUNDER_PEM_QUIRK(2, 0),	/* off-chip devices */
    109	THUNDER_PEM_QUIRK(2, 1),	/* off-chip devices */
    110	THUNDER_ECAM_QUIRK(2,  0),
    111	THUNDER_ECAM_QUIRK(2,  1),
    112	THUNDER_ECAM_QUIRK(2,  2),
    113	THUNDER_ECAM_QUIRK(2,  3),
    114	THUNDER_ECAM_QUIRK(2, 10),
    115	THUNDER_ECAM_QUIRK(2, 11),
    116	THUNDER_ECAM_QUIRK(2, 12),
    117	THUNDER_ECAM_QUIRK(2, 13),
    118
    119	{ "NVIDIA", "TEGRA194", 1, 0, MCFG_BUS_ANY, &tegra194_pcie_ops},
    120	{ "NVIDIA", "TEGRA194", 1, 1, MCFG_BUS_ANY, &tegra194_pcie_ops},
    121	{ "NVIDIA", "TEGRA194", 1, 2, MCFG_BUS_ANY, &tegra194_pcie_ops},
    122	{ "NVIDIA", "TEGRA194", 1, 3, MCFG_BUS_ANY, &tegra194_pcie_ops},
    123	{ "NVIDIA", "TEGRA194", 1, 4, MCFG_BUS_ANY, &tegra194_pcie_ops},
    124	{ "NVIDIA", "TEGRA194", 1, 5, MCFG_BUS_ANY, &tegra194_pcie_ops},
    125
    126#define XGENE_V1_ECAM_MCFG(rev, seg) \
    127	{"APM   ", "XGENE   ", rev, seg, MCFG_BUS_ANY, \
    128		&xgene_v1_pcie_ecam_ops }
    129
    130#define XGENE_V2_ECAM_MCFG(rev, seg) \
    131	{"APM   ", "XGENE   ", rev, seg, MCFG_BUS_ANY, \
    132		&xgene_v2_pcie_ecam_ops }
    133
    134	/* X-Gene SoC with v1 PCIe controller */
    135	XGENE_V1_ECAM_MCFG(1, 0),
    136	XGENE_V1_ECAM_MCFG(1, 1),
    137	XGENE_V1_ECAM_MCFG(1, 2),
    138	XGENE_V1_ECAM_MCFG(1, 3),
    139	XGENE_V1_ECAM_MCFG(1, 4),
    140	XGENE_V1_ECAM_MCFG(2, 0),
    141	XGENE_V1_ECAM_MCFG(2, 1),
    142	XGENE_V1_ECAM_MCFG(2, 2),
    143	XGENE_V1_ECAM_MCFG(2, 3),
    144	XGENE_V1_ECAM_MCFG(2, 4),
    145	/* X-Gene SoC with v2.1 PCIe controller */
    146	XGENE_V2_ECAM_MCFG(3, 0),
    147	XGENE_V2_ECAM_MCFG(3, 1),
    148	/* X-Gene SoC with v2.2 PCIe controller */
    149	XGENE_V2_ECAM_MCFG(4, 0),
    150	XGENE_V2_ECAM_MCFG(4, 1),
    151	XGENE_V2_ECAM_MCFG(4, 2),
    152
    153#define ALTRA_ECAM_QUIRK(rev, seg) \
    154	{ "Ampere", "Altra   ", rev, seg, MCFG_BUS_ANY, &pci_32b_read_ops }
    155
    156	ALTRA_ECAM_QUIRK(1, 0),
    157	ALTRA_ECAM_QUIRK(1, 1),
    158	ALTRA_ECAM_QUIRK(1, 2),
    159	ALTRA_ECAM_QUIRK(1, 3),
    160	ALTRA_ECAM_QUIRK(1, 4),
    161	ALTRA_ECAM_QUIRK(1, 5),
    162	ALTRA_ECAM_QUIRK(1, 6),
    163	ALTRA_ECAM_QUIRK(1, 7),
    164	ALTRA_ECAM_QUIRK(1, 8),
    165	ALTRA_ECAM_QUIRK(1, 9),
    166	ALTRA_ECAM_QUIRK(1, 10),
    167	ALTRA_ECAM_QUIRK(1, 11),
    168	ALTRA_ECAM_QUIRK(1, 12),
    169	ALTRA_ECAM_QUIRK(1, 13),
    170	ALTRA_ECAM_QUIRK(1, 14),
    171	ALTRA_ECAM_QUIRK(1, 15),
    172};
    173
    174static char mcfg_oem_id[ACPI_OEM_ID_SIZE];
    175static char mcfg_oem_table_id[ACPI_OEM_TABLE_ID_SIZE];
    176static u32 mcfg_oem_revision;
    177
    178static int pci_mcfg_quirk_matches(struct mcfg_fixup *f, u16 segment,
    179				  struct resource *bus_range)
    180{
    181	if (!memcmp(f->oem_id, mcfg_oem_id, ACPI_OEM_ID_SIZE) &&
    182	    !memcmp(f->oem_table_id, mcfg_oem_table_id,
    183		    ACPI_OEM_TABLE_ID_SIZE) &&
    184	    f->oem_revision == mcfg_oem_revision &&
    185	    f->segment == segment &&
    186	    resource_contains(&f->bus_range, bus_range))
    187		return 1;
    188
    189	return 0;
    190}
    191#endif
    192
    193static void pci_mcfg_apply_quirks(struct acpi_pci_root *root,
    194				  struct resource *cfgres,
    195				  const struct pci_ecam_ops **ecam_ops)
    196{
    197#ifdef CONFIG_PCI_QUIRKS
    198	u16 segment = root->segment;
    199	struct resource *bus_range = &root->secondary;
    200	struct mcfg_fixup *f;
    201	int i;
    202
    203	for (i = 0, f = mcfg_quirks; i < ARRAY_SIZE(mcfg_quirks); i++, f++) {
    204		if (pci_mcfg_quirk_matches(f, segment, bus_range)) {
    205			if (f->cfgres.start)
    206				*cfgres = f->cfgres;
    207			if (f->ops)
    208				*ecam_ops =  f->ops;
    209			dev_info(&root->device->dev, "MCFG quirk: ECAM at %pR for %pR with %ps\n",
    210				 cfgres, bus_range, *ecam_ops);
    211			return;
    212		}
    213	}
    214#endif
    215}
    216
    217/* List to save MCFG entries */
    218static LIST_HEAD(pci_mcfg_list);
    219
    220int pci_mcfg_lookup(struct acpi_pci_root *root, struct resource *cfgres,
    221		    const struct pci_ecam_ops **ecam_ops)
    222{
    223	const struct pci_ecam_ops *ops = &pci_generic_ecam_ops;
    224	struct resource *bus_res = &root->secondary;
    225	u16 seg = root->segment;
    226	struct mcfg_entry *e;
    227	struct resource res;
    228
    229	/* Use address from _CBA if present, otherwise lookup MCFG */
    230	if (root->mcfg_addr)
    231		goto skip_lookup;
    232
    233	/*
    234	 * We expect the range in bus_res in the coverage of MCFG bus range.
    235	 */
    236	list_for_each_entry(e, &pci_mcfg_list, list) {
    237		if (e->segment == seg && e->bus_start <= bus_res->start &&
    238		    e->bus_end >= bus_res->end) {
    239			root->mcfg_addr = e->addr;
    240		}
    241
    242	}
    243
    244skip_lookup:
    245	memset(&res, 0, sizeof(res));
    246	if (root->mcfg_addr) {
    247		res.start = root->mcfg_addr + (bus_res->start << 20);
    248		res.end = res.start + (resource_size(bus_res) << 20) - 1;
    249		res.flags = IORESOURCE_MEM;
    250	}
    251
    252	/*
    253	 * Allow quirks to override default ECAM ops and CFG resource
    254	 * range.  This may even fabricate a CFG resource range in case
    255	 * MCFG does not have it.  Invalid CFG start address means MCFG
    256	 * firmware bug or we need another quirk in array.
    257	 */
    258	pci_mcfg_apply_quirks(root, &res, &ops);
    259	if (!res.start)
    260		return -ENXIO;
    261
    262	*cfgres = res;
    263	*ecam_ops = ops;
    264	return 0;
    265}
    266
    267static __init int pci_mcfg_parse(struct acpi_table_header *header)
    268{
    269	struct acpi_table_mcfg *mcfg;
    270	struct acpi_mcfg_allocation *mptr;
    271	struct mcfg_entry *e, *arr;
    272	int i, n;
    273
    274	if (header->length < sizeof(struct acpi_table_mcfg))
    275		return -EINVAL;
    276
    277	n = (header->length - sizeof(struct acpi_table_mcfg)) /
    278					sizeof(struct acpi_mcfg_allocation);
    279	mcfg = (struct acpi_table_mcfg *)header;
    280	mptr = (struct acpi_mcfg_allocation *) &mcfg[1];
    281
    282	arr = kcalloc(n, sizeof(*arr), GFP_KERNEL);
    283	if (!arr)
    284		return -ENOMEM;
    285
    286	for (i = 0, e = arr; i < n; i++, mptr++, e++) {
    287		e->segment = mptr->pci_segment;
    288		e->addr =  mptr->address;
    289		e->bus_start = mptr->start_bus_number;
    290		e->bus_end = mptr->end_bus_number;
    291		list_add(&e->list, &pci_mcfg_list);
    292	}
    293
    294#ifdef CONFIG_PCI_QUIRKS
    295	/* Save MCFG IDs and revision for quirks matching */
    296	memcpy(mcfg_oem_id, header->oem_id, ACPI_OEM_ID_SIZE);
    297	memcpy(mcfg_oem_table_id, header->oem_table_id, ACPI_OEM_TABLE_ID_SIZE);
    298	mcfg_oem_revision = header->oem_revision;
    299#endif
    300
    301	pr_info("MCFG table detected, %d entries\n", n);
    302	return 0;
    303}
    304
    305/* Interface called by ACPI - parse and save MCFG table */
    306void __init pci_mmcfg_late_init(void)
    307{
    308	int err = acpi_table_parse(ACPI_SIG_MCFG, pci_mcfg_parse);
    309	if (err)
    310		pr_debug("Failed to parse MCFG (%d)\n", err);
    311}