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

rcec.c (4466B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Root Complex Event Collector Support
      4 *
      5 * Authors:
      6 *  Sean V Kelley <sean.v.kelley@intel.com>
      7 *  Qiuxu Zhuo <qiuxu.zhuo@intel.com>
      8 *
      9 * Copyright (C) 2020 Intel Corp.
     10 */
     11
     12#include <linux/kernel.h>
     13#include <linux/pci.h>
     14#include <linux/pci_regs.h>
     15
     16#include "../pci.h"
     17
     18struct walk_rcec_data {
     19	struct pci_dev *rcec;
     20	int (*user_callback)(struct pci_dev *dev, void *data);
     21	void *user_data;
     22};
     23
     24static bool rcec_assoc_rciep(struct pci_dev *rcec, struct pci_dev *rciep)
     25{
     26	unsigned long bitmap = rcec->rcec_ea->bitmap;
     27	unsigned int devn;
     28
     29	/* An RCiEP found on a different bus in range */
     30	if (rcec->bus->number != rciep->bus->number)
     31		return true;
     32
     33	/* Same bus, so check bitmap */
     34	for_each_set_bit(devn, &bitmap, 32)
     35		if (devn == PCI_SLOT(rciep->devfn))
     36			return true;
     37
     38	return false;
     39}
     40
     41static int link_rcec_helper(struct pci_dev *dev, void *data)
     42{
     43	struct walk_rcec_data *rcec_data = data;
     44	struct pci_dev *rcec = rcec_data->rcec;
     45
     46	if ((pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) &&
     47	    rcec_assoc_rciep(rcec, dev)) {
     48		dev->rcec = rcec;
     49		pci_dbg(dev, "PME & error events signaled via %s\n",
     50			pci_name(rcec));
     51	}
     52
     53	return 0;
     54}
     55
     56static int walk_rcec_helper(struct pci_dev *dev, void *data)
     57{
     58	struct walk_rcec_data *rcec_data = data;
     59	struct pci_dev *rcec = rcec_data->rcec;
     60
     61	if ((pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END) &&
     62	    rcec_assoc_rciep(rcec, dev))
     63		rcec_data->user_callback(dev, rcec_data->user_data);
     64
     65	return 0;
     66}
     67
     68static void walk_rcec(int (*cb)(struct pci_dev *dev, void *data),
     69		      void *userdata)
     70{
     71	struct walk_rcec_data *rcec_data = userdata;
     72	struct pci_dev *rcec = rcec_data->rcec;
     73	u8 nextbusn, lastbusn;
     74	struct pci_bus *bus;
     75	unsigned int bnr;
     76
     77	if (!rcec->rcec_ea)
     78		return;
     79
     80	/* Walk own bus for bitmap based association */
     81	pci_walk_bus(rcec->bus, cb, rcec_data);
     82
     83	nextbusn = rcec->rcec_ea->nextbusn;
     84	lastbusn = rcec->rcec_ea->lastbusn;
     85
     86	/* All RCiEP devices are on the same bus as the RCEC */
     87	if (nextbusn == 0xff && lastbusn == 0x00)
     88		return;
     89
     90	for (bnr = nextbusn; bnr <= lastbusn; bnr++) {
     91		/* No association indicated (PCIe 5.0-1, 7.9.10.3) */
     92		if (bnr == rcec->bus->number)
     93			continue;
     94
     95		bus = pci_find_bus(pci_domain_nr(rcec->bus), bnr);
     96		if (!bus)
     97			continue;
     98
     99		/* Find RCiEP devices on the given bus ranges */
    100		pci_walk_bus(bus, cb, rcec_data);
    101	}
    102}
    103
    104/**
    105 * pcie_link_rcec - Link RCiEP devices associated with RCEC.
    106 * @rcec: RCEC whose RCiEP devices should be linked.
    107 *
    108 * Link the given RCEC to each RCiEP device found.
    109 */
    110void pcie_link_rcec(struct pci_dev *rcec)
    111{
    112	struct walk_rcec_data rcec_data;
    113
    114	if (!rcec->rcec_ea)
    115		return;
    116
    117	rcec_data.rcec = rcec;
    118	rcec_data.user_callback = NULL;
    119	rcec_data.user_data = NULL;
    120
    121	walk_rcec(link_rcec_helper, &rcec_data);
    122}
    123
    124/**
    125 * pcie_walk_rcec - Walk RCiEP devices associating with RCEC and call callback.
    126 * @rcec:	RCEC whose RCiEP devices should be walked
    127 * @cb:		Callback to be called for each RCiEP device found
    128 * @userdata:	Arbitrary pointer to be passed to callback
    129 *
    130 * Walk the given RCEC. Call the callback on each RCiEP found.
    131 *
    132 * If @cb returns anything other than 0, break out.
    133 */
    134void pcie_walk_rcec(struct pci_dev *rcec, int (*cb)(struct pci_dev *, void *),
    135		    void *userdata)
    136{
    137	struct walk_rcec_data rcec_data;
    138
    139	if (!rcec->rcec_ea)
    140		return;
    141
    142	rcec_data.rcec = rcec;
    143	rcec_data.user_callback = cb;
    144	rcec_data.user_data = userdata;
    145
    146	walk_rcec(walk_rcec_helper, &rcec_data);
    147}
    148
    149void pci_rcec_init(struct pci_dev *dev)
    150{
    151	struct rcec_ea *rcec_ea;
    152	u32 rcec, hdr, busn;
    153	u8 ver;
    154
    155	/* Only for Root Complex Event Collectors */
    156	if (pci_pcie_type(dev) != PCI_EXP_TYPE_RC_EC)
    157		return;
    158
    159	rcec = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_RCEC);
    160	if (!rcec)
    161		return;
    162
    163	rcec_ea = kzalloc(sizeof(*rcec_ea), GFP_KERNEL);
    164	if (!rcec_ea)
    165		return;
    166
    167	pci_read_config_dword(dev, rcec + PCI_RCEC_RCIEP_BITMAP,
    168			      &rcec_ea->bitmap);
    169
    170	/* Check whether RCEC BUSN register is present */
    171	pci_read_config_dword(dev, rcec, &hdr);
    172	ver = PCI_EXT_CAP_VER(hdr);
    173	if (ver >= PCI_RCEC_BUSN_REG_VER) {
    174		pci_read_config_dword(dev, rcec + PCI_RCEC_BUSN, &busn);
    175		rcec_ea->nextbusn = PCI_RCEC_BUSN_NEXT(busn);
    176		rcec_ea->lastbusn = PCI_RCEC_BUSN_LAST(busn);
    177	} else {
    178		/* Avoid later ver check by setting nextbusn */
    179		rcec_ea->nextbusn = 0xff;
    180		rcec_ea->lastbusn = 0x00;
    181	}
    182
    183	dev->rcec_ea = rcec_ea;
    184}
    185
    186void pci_rcec_exit(struct pci_dev *dev)
    187{
    188	kfree(dev->rcec_ea);
    189	dev->rcec_ea = NULL;
    190}