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

rpadlpar_core.c (10284B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Interface for Dynamic Logical Partitioning of I/O Slots on
      4 * RPA-compliant PPC64 platform.
      5 *
      6 * John Rose <johnrose@austin.ibm.com>
      7 * Linda Xie <lxie@us.ibm.com>
      8 *
      9 * October 2003
     10 *
     11 * Copyright (C) 2003 IBM.
     12 */
     13
     14#undef DEBUG
     15
     16#include <linux/init.h>
     17#include <linux/module.h>
     18#include <linux/of.h>
     19#include <linux/pci.h>
     20#include <linux/string.h>
     21#include <linux/vmalloc.h>
     22
     23#include <asm/pci-bridge.h>
     24#include <linux/mutex.h>
     25#include <asm/rtas.h>
     26#include <asm/vio.h>
     27#include <linux/firmware.h>
     28
     29#include "../pci.h"
     30#include "rpaphp.h"
     31#include "rpadlpar.h"
     32
     33static DEFINE_MUTEX(rpadlpar_mutex);
     34
     35#define DLPAR_MODULE_NAME "rpadlpar_io"
     36
     37#define NODE_TYPE_VIO  1
     38#define NODE_TYPE_SLOT 2
     39#define NODE_TYPE_PHB  3
     40
     41static struct device_node *find_vio_slot_node(char *drc_name)
     42{
     43	struct device_node *parent = of_find_node_by_name(NULL, "vdevice");
     44	struct device_node *dn;
     45	int rc;
     46
     47	if (!parent)
     48		return NULL;
     49
     50	for_each_child_of_node(parent, dn) {
     51		rc = rpaphp_check_drc_props(dn, drc_name, NULL);
     52		if (rc == 0)
     53			break;
     54	}
     55	of_node_put(parent);
     56
     57	return dn;
     58}
     59
     60/* Find dlpar-capable pci node that contains the specified name and type */
     61static struct device_node *find_php_slot_pci_node(char *drc_name,
     62						  char *drc_type)
     63{
     64	struct device_node *np;
     65	int rc;
     66
     67	for_each_node_by_name(np, "pci") {
     68		rc = rpaphp_check_drc_props(np, drc_name, drc_type);
     69		if (rc == 0)
     70			break;
     71	}
     72
     73	return np;
     74}
     75
     76/* Returns a device_node with its reference count incremented */
     77static struct device_node *find_dlpar_node(char *drc_name, int *node_type)
     78{
     79	struct device_node *dn;
     80
     81	dn = find_php_slot_pci_node(drc_name, "SLOT");
     82	if (dn) {
     83		*node_type = NODE_TYPE_SLOT;
     84		return dn;
     85	}
     86
     87	dn = find_php_slot_pci_node(drc_name, "PHB");
     88	if (dn) {
     89		*node_type = NODE_TYPE_PHB;
     90		return dn;
     91	}
     92
     93	dn = find_vio_slot_node(drc_name);
     94	if (dn) {
     95		*node_type = NODE_TYPE_VIO;
     96		return dn;
     97	}
     98
     99	return NULL;
    100}
    101
    102/**
    103 * find_php_slot - return hotplug slot structure for device node
    104 * @dn: target &device_node
    105 *
    106 * This routine will return the hotplug slot structure
    107 * for a given device node. Note that built-in PCI slots
    108 * may be dlpar-able, but not hot-pluggable, so this routine
    109 * will return NULL for built-in PCI slots.
    110 */
    111static struct slot *find_php_slot(struct device_node *dn)
    112{
    113	struct slot *slot, *next;
    114
    115	list_for_each_entry_safe(slot, next, &rpaphp_slot_head,
    116				 rpaphp_slot_list) {
    117		if (slot->dn == dn)
    118			return slot;
    119	}
    120
    121	return NULL;
    122}
    123
    124static struct pci_dev *dlpar_find_new_dev(struct pci_bus *parent,
    125					struct device_node *dev_dn)
    126{
    127	struct pci_dev *tmp = NULL;
    128	struct device_node *child_dn;
    129
    130	list_for_each_entry(tmp, &parent->devices, bus_list) {
    131		child_dn = pci_device_to_OF_node(tmp);
    132		if (child_dn == dev_dn)
    133			return tmp;
    134	}
    135	return NULL;
    136}
    137
    138static void dlpar_pci_add_bus(struct device_node *dn)
    139{
    140	struct pci_dn *pdn = PCI_DN(dn);
    141	struct pci_controller *phb = pdn->phb;
    142	struct pci_dev *dev = NULL;
    143
    144	pseries_eeh_init_edev_recursive(pdn);
    145
    146	/* Add EADS device to PHB bus, adding new entry to bus->devices */
    147	dev = of_create_pci_dev(dn, phb->bus, pdn->devfn);
    148	if (!dev) {
    149		printk(KERN_ERR "%s: failed to create pci dev for %pOF\n",
    150				__func__, dn);
    151		return;
    152	}
    153
    154	/* Scan below the new bridge */
    155	if (pci_is_bridge(dev))
    156		of_scan_pci_bridge(dev);
    157
    158	/* Map IO space for child bus, which may or may not succeed */
    159	pcibios_map_io_space(dev->subordinate);
    160
    161	/* Finish adding it : resource allocation, adding devices, etc...
    162	 * Note that we need to perform the finish pass on the -parent-
    163	 * bus of the EADS bridge so the bridge device itself gets
    164	 * properly added
    165	 */
    166	pcibios_finish_adding_to_bus(phb->bus);
    167}
    168
    169static int dlpar_add_pci_slot(char *drc_name, struct device_node *dn)
    170{
    171	struct pci_dev *dev;
    172	struct pci_controller *phb;
    173
    174	if (pci_find_bus_by_node(dn))
    175		return -EINVAL;
    176
    177	/* Add pci bus */
    178	dlpar_pci_add_bus(dn);
    179
    180	/* Confirm new bridge dev was created */
    181	phb = PCI_DN(dn)->phb;
    182	dev = dlpar_find_new_dev(phb->bus, dn);
    183
    184	if (!dev) {
    185		printk(KERN_ERR "%s: unable to add bus %s\n", __func__,
    186			drc_name);
    187		return -EIO;
    188	}
    189
    190	if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
    191		printk(KERN_ERR "%s: unexpected header type %d, unable to add bus %s\n",
    192			__func__, dev->hdr_type, drc_name);
    193		return -EIO;
    194	}
    195
    196	/* Add hotplug slot */
    197	if (rpaphp_add_slot(dn)) {
    198		printk(KERN_ERR "%s: unable to add hotplug slot %s\n",
    199			__func__, drc_name);
    200		return -EIO;
    201	}
    202	return 0;
    203}
    204
    205static int dlpar_remove_phb(char *drc_name, struct device_node *dn)
    206{
    207	struct slot *slot;
    208	struct pci_dn *pdn;
    209	int rc = 0;
    210
    211	if (!pci_find_bus_by_node(dn))
    212		return -EINVAL;
    213
    214	/* If pci slot is hotpluggable, use hotplug to remove it */
    215	slot = find_php_slot(dn);
    216	if (slot && rpaphp_deregister_slot(slot)) {
    217		printk(KERN_ERR "%s: unable to remove hotplug slot %s\n",
    218		       __func__, drc_name);
    219		return -EIO;
    220	}
    221
    222	pdn = dn->data;
    223	BUG_ON(!pdn || !pdn->phb);
    224	rc = remove_phb_dynamic(pdn->phb);
    225	if (rc < 0)
    226		return rc;
    227
    228	pdn->phb = NULL;
    229
    230	return 0;
    231}
    232
    233static int dlpar_add_phb(char *drc_name, struct device_node *dn)
    234{
    235	struct pci_controller *phb;
    236
    237	if (PCI_DN(dn) && PCI_DN(dn)->phb) {
    238		/* PHB already exists */
    239		return -EINVAL;
    240	}
    241
    242	phb = init_phb_dynamic(dn);
    243	if (!phb)
    244		return -EIO;
    245
    246	if (rpaphp_add_slot(dn)) {
    247		printk(KERN_ERR "%s: unable to add hotplug slot %s\n",
    248			__func__, drc_name);
    249		return -EIO;
    250	}
    251	return 0;
    252}
    253
    254static int dlpar_add_vio_slot(char *drc_name, struct device_node *dn)
    255{
    256	struct vio_dev *vio_dev;
    257
    258	vio_dev = vio_find_node(dn);
    259	if (vio_dev) {
    260		put_device(&vio_dev->dev);
    261		return -EINVAL;
    262	}
    263
    264	if (!vio_register_device_node(dn)) {
    265		printk(KERN_ERR
    266			"%s: failed to register vio node %s\n",
    267			__func__, drc_name);
    268		return -EIO;
    269	}
    270	return 0;
    271}
    272
    273/**
    274 * dlpar_add_slot - DLPAR add an I/O Slot
    275 * @drc_name: drc-name of newly added slot
    276 *
    277 * Make the hotplug module and the kernel aware of a newly added I/O Slot.
    278 * Return Codes:
    279 * 0			Success
    280 * -ENODEV		Not a valid drc_name
    281 * -EINVAL		Slot already added
    282 * -ERESTARTSYS		Signalled before obtaining lock
    283 * -EIO			Internal PCI Error
    284 */
    285int dlpar_add_slot(char *drc_name)
    286{
    287	struct device_node *dn = NULL;
    288	int node_type;
    289	int rc = -EIO;
    290
    291	if (mutex_lock_interruptible(&rpadlpar_mutex))
    292		return -ERESTARTSYS;
    293
    294	/* Find newly added node */
    295	dn = find_dlpar_node(drc_name, &node_type);
    296	if (!dn) {
    297		rc = -ENODEV;
    298		goto exit;
    299	}
    300
    301	switch (node_type) {
    302		case NODE_TYPE_VIO:
    303			rc = dlpar_add_vio_slot(drc_name, dn);
    304			break;
    305		case NODE_TYPE_SLOT:
    306			rc = dlpar_add_pci_slot(drc_name, dn);
    307			break;
    308		case NODE_TYPE_PHB:
    309			rc = dlpar_add_phb(drc_name, dn);
    310			break;
    311	}
    312	of_node_put(dn);
    313
    314	printk(KERN_INFO "%s: slot %s added\n", DLPAR_MODULE_NAME, drc_name);
    315exit:
    316	mutex_unlock(&rpadlpar_mutex);
    317	return rc;
    318}
    319
    320/**
    321 * dlpar_remove_vio_slot - DLPAR remove a virtual I/O Slot
    322 * @drc_name: drc-name of newly added slot
    323 * @dn: &device_node
    324 *
    325 * Remove the kernel and hotplug representations of an I/O Slot.
    326 * Return Codes:
    327 * 0			Success
    328 * -EINVAL		Vio dev doesn't exist
    329 */
    330static int dlpar_remove_vio_slot(char *drc_name, struct device_node *dn)
    331{
    332	struct vio_dev *vio_dev;
    333
    334	vio_dev = vio_find_node(dn);
    335	if (!vio_dev)
    336		return -EINVAL;
    337
    338	vio_unregister_device(vio_dev);
    339
    340	put_device(&vio_dev->dev);
    341
    342	return 0;
    343}
    344
    345/**
    346 * dlpar_remove_pci_slot - DLPAR remove a PCI I/O Slot
    347 * @drc_name: drc-name of newly added slot
    348 * @dn: &device_node
    349 *
    350 * Remove the kernel and hotplug representations of a PCI I/O Slot.
    351 * Return Codes:
    352 * 0			Success
    353 * -ENODEV		Not a valid drc_name
    354 * -EIO			Internal PCI Error
    355 */
    356static int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn)
    357{
    358	struct pci_bus *bus;
    359	struct slot *slot;
    360	int ret = 0;
    361
    362	pci_lock_rescan_remove();
    363
    364	bus = pci_find_bus_by_node(dn);
    365	if (!bus) {
    366		ret = -EINVAL;
    367		goto out;
    368	}
    369
    370	pr_debug("PCI: Removing PCI slot below EADS bridge %s\n",
    371		 bus->self ? pci_name(bus->self) : "<!PHB!>");
    372
    373	slot = find_php_slot(dn);
    374	if (slot) {
    375		pr_debug("PCI: Removing hotplug slot for %04x:%02x...\n",
    376			 pci_domain_nr(bus), bus->number);
    377
    378		if (rpaphp_deregister_slot(slot)) {
    379			printk(KERN_ERR
    380				"%s: unable to remove hotplug slot %s\n",
    381				__func__, drc_name);
    382			ret = -EIO;
    383			goto out;
    384		}
    385	}
    386
    387	/* Remove all devices below slot */
    388	pci_hp_remove_devices(bus);
    389
    390	/* Unmap PCI IO space */
    391	if (pcibios_unmap_io_space(bus)) {
    392		printk(KERN_ERR "%s: failed to unmap bus range\n",
    393			__func__);
    394		ret = -ERANGE;
    395		goto out;
    396	}
    397
    398	/* Remove the EADS bridge device itself */
    399	BUG_ON(!bus->self);
    400	pr_debug("PCI: Now removing bridge device %s\n", pci_name(bus->self));
    401	pci_stop_and_remove_bus_device(bus->self);
    402
    403 out:
    404	pci_unlock_rescan_remove();
    405	return ret;
    406}
    407
    408/**
    409 * dlpar_remove_slot - DLPAR remove an I/O Slot
    410 * @drc_name: drc-name of newly added slot
    411 *
    412 * Remove the kernel and hotplug representations of an I/O Slot.
    413 * Return Codes:
    414 * 0			Success
    415 * -ENODEV		Not a valid drc_name
    416 * -EINVAL		Slot already removed
    417 * -ERESTARTSYS		Signalled before obtaining lock
    418 * -EIO			Internal Error
    419 */
    420int dlpar_remove_slot(char *drc_name)
    421{
    422	struct device_node *dn;
    423	int node_type;
    424	int rc = 0;
    425
    426	if (mutex_lock_interruptible(&rpadlpar_mutex))
    427		return -ERESTARTSYS;
    428
    429	dn = find_dlpar_node(drc_name, &node_type);
    430	if (!dn) {
    431		rc = -ENODEV;
    432		goto exit;
    433	}
    434
    435	switch (node_type) {
    436		case NODE_TYPE_VIO:
    437			rc = dlpar_remove_vio_slot(drc_name, dn);
    438			break;
    439		case NODE_TYPE_PHB:
    440			rc = dlpar_remove_phb(drc_name, dn);
    441			break;
    442		case NODE_TYPE_SLOT:
    443			rc = dlpar_remove_pci_slot(drc_name, dn);
    444			break;
    445	}
    446	of_node_put(dn);
    447	vm_unmap_aliases();
    448
    449	printk(KERN_INFO "%s: slot %s removed\n", DLPAR_MODULE_NAME, drc_name);
    450exit:
    451	mutex_unlock(&rpadlpar_mutex);
    452	return rc;
    453}
    454
    455static inline int is_dlpar_capable(void)
    456{
    457	int rc = rtas_token("ibm,configure-connector");
    458
    459	return (int) (rc != RTAS_UNKNOWN_SERVICE);
    460}
    461
    462static int __init rpadlpar_io_init(void)
    463{
    464
    465	if (!is_dlpar_capable()) {
    466		printk(KERN_WARNING "%s: partition not DLPAR capable\n",
    467			__func__);
    468		return -EPERM;
    469	}
    470
    471	return dlpar_sysfs_init();
    472}
    473
    474static void __exit rpadlpar_io_exit(void)
    475{
    476	dlpar_sysfs_exit();
    477}
    478
    479module_init(rpadlpar_io_init);
    480module_exit(rpadlpar_io_exit);
    481MODULE_LICENSE("GPL");
    482MODULE_DESCRIPTION("RPA Dynamic Logical Partitioning driver for I/O slots");