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

mmconfig_64.c (3259B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * mmconfig.c - Low-level direct PCI config space access via MMCONFIG
      4 *
      5 * This is an 64bit optimized version that always keeps the full mmconfig
      6 * space mapped. This allows lockless config space operation.
      7 */
      8
      9#include <linux/pci.h>
     10#include <linux/init.h>
     11#include <linux/acpi.h>
     12#include <linux/bitmap.h>
     13#include <linux/rcupdate.h>
     14#include <asm/e820/api.h>
     15#include <asm/pci_x86.h>
     16
     17#define PREFIX "PCI: "
     18
     19static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn)
     20{
     21	struct pci_mmcfg_region *cfg = pci_mmconfig_lookup(seg, bus);
     22
     23	if (cfg && cfg->virt)
     24		return cfg->virt + (PCI_MMCFG_BUS_OFFSET(bus) | (devfn << 12));
     25	return NULL;
     26}
     27
     28static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
     29			  unsigned int devfn, int reg, int len, u32 *value)
     30{
     31	char __iomem *addr;
     32
     33	/* Why do we have this when nobody checks it. How about a BUG()!? -AK */
     34	if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) {
     35err:		*value = -1;
     36		return -EINVAL;
     37	}
     38
     39	rcu_read_lock();
     40	addr = pci_dev_base(seg, bus, devfn);
     41	if (!addr) {
     42		rcu_read_unlock();
     43		goto err;
     44	}
     45
     46	switch (len) {
     47	case 1:
     48		*value = mmio_config_readb(addr + reg);
     49		break;
     50	case 2:
     51		*value = mmio_config_readw(addr + reg);
     52		break;
     53	case 4:
     54		*value = mmio_config_readl(addr + reg);
     55		break;
     56	}
     57	rcu_read_unlock();
     58
     59	return 0;
     60}
     61
     62static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
     63			   unsigned int devfn, int reg, int len, u32 value)
     64{
     65	char __iomem *addr;
     66
     67	/* Why do we have this when nobody checks it. How about a BUG()!? -AK */
     68	if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095)))
     69		return -EINVAL;
     70
     71	rcu_read_lock();
     72	addr = pci_dev_base(seg, bus, devfn);
     73	if (!addr) {
     74		rcu_read_unlock();
     75		return -EINVAL;
     76	}
     77
     78	switch (len) {
     79	case 1:
     80		mmio_config_writeb(addr + reg, value);
     81		break;
     82	case 2:
     83		mmio_config_writew(addr + reg, value);
     84		break;
     85	case 4:
     86		mmio_config_writel(addr + reg, value);
     87		break;
     88	}
     89	rcu_read_unlock();
     90
     91	return 0;
     92}
     93
     94const struct pci_raw_ops pci_mmcfg = {
     95	.read =		pci_mmcfg_read,
     96	.write =	pci_mmcfg_write,
     97};
     98
     99static void __iomem *mcfg_ioremap(struct pci_mmcfg_region *cfg)
    100{
    101	void __iomem *addr;
    102	u64 start, size;
    103	int num_buses;
    104
    105	start = cfg->address + PCI_MMCFG_BUS_OFFSET(cfg->start_bus);
    106	num_buses = cfg->end_bus - cfg->start_bus + 1;
    107	size = PCI_MMCFG_BUS_OFFSET(num_buses);
    108	addr = ioremap(start, size);
    109	if (addr)
    110		addr -= PCI_MMCFG_BUS_OFFSET(cfg->start_bus);
    111	return addr;
    112}
    113
    114int __init pci_mmcfg_arch_init(void)
    115{
    116	struct pci_mmcfg_region *cfg;
    117
    118	list_for_each_entry(cfg, &pci_mmcfg_list, list)
    119		if (pci_mmcfg_arch_map(cfg)) {
    120			pci_mmcfg_arch_free();
    121			return 0;
    122		}
    123
    124	raw_pci_ext_ops = &pci_mmcfg;
    125
    126	return 1;
    127}
    128
    129void __init pci_mmcfg_arch_free(void)
    130{
    131	struct pci_mmcfg_region *cfg;
    132
    133	list_for_each_entry(cfg, &pci_mmcfg_list, list)
    134		pci_mmcfg_arch_unmap(cfg);
    135}
    136
    137int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg)
    138{
    139	cfg->virt = mcfg_ioremap(cfg);
    140	if (!cfg->virt) {
    141		pr_err(PREFIX "can't map MMCONFIG at %pR\n", &cfg->res);
    142		return -ENOMEM;
    143	}
    144
    145	return 0;
    146}
    147
    148void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg)
    149{
    150	if (cfg && cfg->virt) {
    151		iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus));
    152		cfg->virt = NULL;
    153	}
    154}