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

indirect_pci.c (4033B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Support for indirect PCI bridges.
      4 *
      5 * Copyright (C) 1998 Gabriel Paubert.
      6 */
      7
      8#include <linux/kernel.h>
      9#include <linux/pci.h>
     10#include <linux/delay.h>
     11#include <linux/string.h>
     12#include <linux/init.h>
     13
     14#include <linux/io.h>
     15#include <asm/pci-bridge.h>
     16
     17static int
     18indirect_read_config(struct pci_bus *bus, unsigned int devfn, int offset,
     19		     int len, u32 *val)
     20{
     21	struct pci_controller *hose = pci_bus_to_host(bus);
     22	volatile void __iomem *cfg_data;
     23	u8 cfg_type = 0;
     24	u32 bus_no, reg;
     25
     26	if (hose->indirect_type & INDIRECT_TYPE_NO_PCIE_LINK) {
     27		if (bus->number != hose->first_busno)
     28			return PCIBIOS_DEVICE_NOT_FOUND;
     29		if (devfn != 0)
     30			return PCIBIOS_DEVICE_NOT_FOUND;
     31	}
     32
     33	if (hose->indirect_type & INDIRECT_TYPE_SET_CFG_TYPE)
     34		if (bus->number != hose->first_busno)
     35			cfg_type = 1;
     36
     37	bus_no = (bus->number == hose->first_busno) ?
     38			hose->self_busno : bus->number;
     39
     40	if (hose->indirect_type & INDIRECT_TYPE_EXT_REG)
     41		reg = ((offset & 0xf00) << 16) | (offset & 0xfc);
     42	else
     43		reg = offset & 0xfc; /* Only 3 bits for function */
     44
     45	if (hose->indirect_type & INDIRECT_TYPE_BIG_ENDIAN)
     46		out_be32(hose->cfg_addr, (0x80000000 | (bus_no << 16) |
     47			 (devfn << 8) | reg | cfg_type));
     48	else
     49		out_le32(hose->cfg_addr, (0x80000000 | (bus_no << 16) |
     50			 (devfn << 8) | reg | cfg_type));
     51
     52	/*
     53	 * Note: the caller has already checked that offset is
     54	 * suitably aligned and that len is 1, 2 or 4.
     55	 */
     56	cfg_data = hose->cfg_data + (offset & 3); /* Only 3 bits for function */
     57	switch (len) {
     58	case 1:
     59		*val = in_8(cfg_data);
     60		break;
     61	case 2:
     62		*val = in_le16(cfg_data);
     63		break;
     64	default:
     65		*val = in_le32(cfg_data);
     66		break;
     67	}
     68	return PCIBIOS_SUCCESSFUL;
     69}
     70
     71static int
     72indirect_write_config(struct pci_bus *bus, unsigned int devfn, int offset,
     73		      int len, u32 val)
     74{
     75	struct pci_controller *hose = pci_bus_to_host(bus);
     76	volatile void __iomem *cfg_data;
     77	u8 cfg_type = 0;
     78	u32 bus_no, reg;
     79
     80	if (hose->indirect_type & INDIRECT_TYPE_NO_PCIE_LINK) {
     81		if (bus->number != hose->first_busno)
     82			return PCIBIOS_DEVICE_NOT_FOUND;
     83		if (devfn != 0)
     84			return PCIBIOS_DEVICE_NOT_FOUND;
     85	}
     86
     87	if (hose->indirect_type & INDIRECT_TYPE_SET_CFG_TYPE)
     88		if (bus->number != hose->first_busno)
     89			cfg_type = 1;
     90
     91	bus_no = (bus->number == hose->first_busno) ?
     92			hose->self_busno : bus->number;
     93
     94	if (hose->indirect_type & INDIRECT_TYPE_EXT_REG)
     95		reg = ((offset & 0xf00) << 16) | (offset & 0xfc);
     96	else
     97		reg = offset & 0xfc;
     98
     99	if (hose->indirect_type & INDIRECT_TYPE_BIG_ENDIAN)
    100		out_be32(hose->cfg_addr, (0x80000000 | (bus_no << 16) |
    101			 (devfn << 8) | reg | cfg_type));
    102	else
    103		out_le32(hose->cfg_addr, (0x80000000 | (bus_no << 16) |
    104			 (devfn << 8) | reg | cfg_type));
    105
    106	/* suppress setting of PCI_PRIMARY_BUS */
    107	if (hose->indirect_type & INDIRECT_TYPE_SURPRESS_PRIMARY_BUS)
    108		if ((offset == PCI_PRIMARY_BUS) &&
    109			(bus->number == hose->first_busno))
    110			val &= 0xffffff00;
    111
    112	/* Workaround for PCI_28 Errata in 440EPx/GRx */
    113	if ((hose->indirect_type & INDIRECT_TYPE_BROKEN_MRM) &&
    114			offset == PCI_CACHE_LINE_SIZE) {
    115		val = 0;
    116	}
    117
    118	/*
    119	 * Note: the caller has already checked that offset is
    120	 * suitably aligned and that len is 1, 2 or 4.
    121	 */
    122	cfg_data = hose->cfg_data + (offset & 3);
    123	switch (len) {
    124	case 1:
    125		out_8(cfg_data, val);
    126		break;
    127	case 2:
    128		out_le16(cfg_data, val);
    129		break;
    130	default:
    131		out_le32(cfg_data, val);
    132		break;
    133	}
    134
    135	return PCIBIOS_SUCCESSFUL;
    136}
    137
    138static struct pci_ops indirect_pci_ops = {
    139	.read = indirect_read_config,
    140	.write = indirect_write_config,
    141};
    142
    143void __init
    144setup_indirect_pci(struct pci_controller *hose,
    145		   resource_size_t cfg_addr,
    146		   resource_size_t cfg_data, u32 flags)
    147{
    148	resource_size_t base = cfg_addr & PAGE_MASK;
    149	void __iomem *mbase;
    150
    151	mbase = ioremap(base, PAGE_SIZE);
    152	hose->cfg_addr = mbase + (cfg_addr & ~PAGE_MASK);
    153	if ((cfg_data & PAGE_MASK) != base)
    154		mbase = ioremap(cfg_data & PAGE_MASK, PAGE_SIZE);
    155	hose->cfg_data = mbase + (cfg_data & ~PAGE_MASK);
    156	hose->ops = &indirect_pci_ops;
    157	hose->indirect_type = flags;
    158}