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

vio.c (13299B)


      1// SPDX-License-Identifier: GPL-2.0
      2/* vio.c: Virtual I/O channel devices probing infrastructure.
      3 *
      4 *    Copyright (c) 2003-2005 IBM Corp.
      5 *     Dave Engebretsen engebret@us.ibm.com
      6 *     Santiago Leon santil@us.ibm.com
      7 *     Hollis Blanchard <hollisb@us.ibm.com>
      8 *     Stephen Rothwell
      9 *
     10 * Adapted to sparc64 by David S. Miller davem@davemloft.net
     11 */
     12
     13#include <linux/kernel.h>
     14#include <linux/slab.h>
     15#include <linux/irq.h>
     16#include <linux/export.h>
     17#include <linux/init.h>
     18
     19#include <asm/mdesc.h>
     20#include <asm/vio.h>
     21
     22static const struct vio_device_id *vio_match_device(
     23	const struct vio_device_id *matches,
     24	const struct vio_dev *dev)
     25{
     26	const char *type, *compat;
     27	int len;
     28
     29	type = dev->type;
     30	compat = dev->compat;
     31	len = dev->compat_len;
     32
     33	while (matches->type[0] || matches->compat[0]) {
     34		int match = 1;
     35		if (matches->type[0])
     36			match &= !strcmp(matches->type, type);
     37
     38		if (matches->compat[0]) {
     39			match &= len &&
     40				of_find_in_proplist(compat, matches->compat, len);
     41		}
     42		if (match)
     43			return matches;
     44		matches++;
     45	}
     46	return NULL;
     47}
     48
     49static int vio_hotplug(struct device *dev, struct kobj_uevent_env *env)
     50{
     51	const struct vio_dev *vio_dev = to_vio_dev(dev);
     52
     53	add_uevent_var(env, "MODALIAS=vio:T%sS%s", vio_dev->type, vio_dev->compat);
     54	return 0;
     55}
     56
     57static int vio_bus_match(struct device *dev, struct device_driver *drv)
     58{
     59	struct vio_dev *vio_dev = to_vio_dev(dev);
     60	struct vio_driver *vio_drv = to_vio_driver(drv);
     61	const struct vio_device_id *matches = vio_drv->id_table;
     62
     63	if (!matches)
     64		return 0;
     65
     66	return vio_match_device(matches, vio_dev) != NULL;
     67}
     68
     69static int vio_device_probe(struct device *dev)
     70{
     71	struct vio_dev *vdev = to_vio_dev(dev);
     72	struct vio_driver *drv = to_vio_driver(dev->driver);
     73	const struct vio_device_id *id;
     74
     75	if (!drv->probe)
     76		return -ENODEV;
     77
     78	id = vio_match_device(drv->id_table, vdev);
     79	if (!id)
     80		return -ENODEV;
     81
     82	/* alloc irqs (unless the driver specified not to) */
     83	if (!drv->no_irq) {
     84		if (vdev->tx_irq == 0 && vdev->tx_ino != ~0UL)
     85			vdev->tx_irq = sun4v_build_virq(vdev->cdev_handle,
     86							vdev->tx_ino);
     87
     88		if (vdev->rx_irq == 0 && vdev->rx_ino != ~0UL)
     89			vdev->rx_irq = sun4v_build_virq(vdev->cdev_handle,
     90							vdev->rx_ino);
     91	}
     92
     93	return drv->probe(vdev, id);
     94}
     95
     96static void vio_device_remove(struct device *dev)
     97{
     98	struct vio_dev *vdev = to_vio_dev(dev);
     99	struct vio_driver *drv = to_vio_driver(dev->driver);
    100
    101	if (drv->remove) {
    102		/*
    103		 * Ideally, we would remove/deallocate tx/rx virqs
    104		 * here - however, there are currently no support
    105		 * routines to do so at the moment. TBD
    106		 */
    107
    108		drv->remove(vdev);
    109	}
    110}
    111
    112static ssize_t devspec_show(struct device *dev,
    113		struct device_attribute *attr, char *buf)
    114{
    115	struct vio_dev *vdev = to_vio_dev(dev);
    116	const char *str = "none";
    117
    118	if (!strcmp(vdev->type, "vnet-port"))
    119		str = "vnet";
    120	else if (!strcmp(vdev->type, "vdc-port"))
    121		str = "vdisk";
    122
    123	return sprintf(buf, "%s\n", str);
    124}
    125static DEVICE_ATTR_RO(devspec);
    126
    127static ssize_t type_show(struct device *dev,
    128		struct device_attribute *attr, char *buf)
    129{
    130	struct vio_dev *vdev = to_vio_dev(dev);
    131	return sprintf(buf, "%s\n", vdev->type);
    132}
    133static DEVICE_ATTR_RO(type);
    134
    135static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
    136			     char *buf)
    137{
    138	const struct vio_dev *vdev = to_vio_dev(dev);
    139
    140	return sprintf(buf, "vio:T%sS%s\n", vdev->type, vdev->compat);
    141}
    142static DEVICE_ATTR_RO(modalias);
    143
    144static struct attribute *vio_dev_attrs[] = {
    145	&dev_attr_devspec.attr,
    146	&dev_attr_type.attr,
    147	&dev_attr_modalias.attr,
    148	NULL,
    149 };
    150ATTRIBUTE_GROUPS(vio_dev);
    151
    152static struct bus_type vio_bus_type = {
    153	.name		= "vio",
    154	.dev_groups	= vio_dev_groups,
    155	.uevent         = vio_hotplug,
    156	.match		= vio_bus_match,
    157	.probe		= vio_device_probe,
    158	.remove		= vio_device_remove,
    159};
    160
    161int __vio_register_driver(struct vio_driver *viodrv, struct module *owner,
    162			const char *mod_name)
    163{
    164	viodrv->driver.bus = &vio_bus_type;
    165	viodrv->driver.name = viodrv->name;
    166	viodrv->driver.owner = owner;
    167	viodrv->driver.mod_name = mod_name;
    168
    169	return driver_register(&viodrv->driver);
    170}
    171EXPORT_SYMBOL(__vio_register_driver);
    172
    173void vio_unregister_driver(struct vio_driver *viodrv)
    174{
    175	driver_unregister(&viodrv->driver);
    176}
    177EXPORT_SYMBOL(vio_unregister_driver);
    178
    179static void vio_dev_release(struct device *dev)
    180{
    181	kfree(to_vio_dev(dev));
    182}
    183
    184static ssize_t
    185show_pciobppath_attr(struct device *dev, struct device_attribute *attr,
    186		     char *buf)
    187{
    188	struct vio_dev *vdev;
    189	struct device_node *dp;
    190
    191	vdev = to_vio_dev(dev);
    192	dp = vdev->dp;
    193
    194	return scnprintf(buf, PAGE_SIZE, "%pOF\n", dp);
    195}
    196
    197static DEVICE_ATTR(obppath, S_IRUSR | S_IRGRP | S_IROTH,
    198		   show_pciobppath_attr, NULL);
    199
    200static struct device_node *cdev_node;
    201
    202static struct vio_dev *root_vdev;
    203static u64 cdev_cfg_handle;
    204
    205static const u64 *vio_cfg_handle(struct mdesc_handle *hp, u64 node)
    206{
    207	const u64 *cfg_handle = NULL;
    208	u64 a;
    209
    210	mdesc_for_each_arc(a, hp, node, MDESC_ARC_TYPE_BACK) {
    211		u64 target;
    212
    213		target = mdesc_arc_target(hp, a);
    214		cfg_handle = mdesc_get_property(hp, target,
    215						"cfg-handle", NULL);
    216		if (cfg_handle)
    217			break;
    218	}
    219
    220	return cfg_handle;
    221}
    222
    223/**
    224 * vio_vdev_node() - Find VDEV node in MD
    225 * @hp:  Handle to the MD
    226 * @vdev:  Pointer to VDEV
    227 *
    228 * Find the node in the current MD which matches the given vio_dev. This
    229 * must be done dynamically since the node value can change if the MD
    230 * is updated.
    231 *
    232 * NOTE: the MD must be locked, using mdesc_grab(), when calling this routine
    233 *
    234 * Return: The VDEV node in MDESC
    235 */
    236u64 vio_vdev_node(struct mdesc_handle *hp, struct vio_dev *vdev)
    237{
    238	u64 node;
    239
    240	if (vdev == NULL)
    241		return MDESC_NODE_NULL;
    242
    243	node = mdesc_get_node(hp, (const char *)vdev->node_name,
    244			      &vdev->md_node_info);
    245
    246	return node;
    247}
    248EXPORT_SYMBOL(vio_vdev_node);
    249
    250static void vio_fill_channel_info(struct mdesc_handle *hp, u64 mp,
    251				  struct vio_dev *vdev)
    252{
    253	u64 a;
    254
    255	vdev->tx_ino = ~0UL;
    256	vdev->rx_ino = ~0UL;
    257	vdev->channel_id = ~0UL;
    258	mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) {
    259		const u64 *chan_id;
    260		const u64 *irq;
    261		u64 target;
    262
    263		target = mdesc_arc_target(hp, a);
    264
    265		irq = mdesc_get_property(hp, target, "tx-ino", NULL);
    266		if (irq)
    267			vdev->tx_ino = *irq;
    268
    269		irq = mdesc_get_property(hp, target, "rx-ino", NULL);
    270		if (irq)
    271			vdev->rx_ino = *irq;
    272
    273		chan_id = mdesc_get_property(hp, target, "id", NULL);
    274		if (chan_id)
    275			vdev->channel_id = *chan_id;
    276	}
    277
    278	vdev->cdev_handle = cdev_cfg_handle;
    279}
    280
    281int vio_set_intr(unsigned long dev_ino, int state)
    282{
    283	int err;
    284
    285	err = sun4v_vintr_set_valid(cdev_cfg_handle, dev_ino, state);
    286	return err;
    287}
    288EXPORT_SYMBOL(vio_set_intr);
    289
    290static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp,
    291				      const char *node_name,
    292				      struct device *parent)
    293{
    294	const char *type, *compat;
    295	struct device_node *dp;
    296	struct vio_dev *vdev;
    297	int err, tlen, clen;
    298	const u64 *id, *cfg_handle;
    299
    300	type = mdesc_get_property(hp, mp, "device-type", &tlen);
    301	if (!type) {
    302		type = mdesc_get_property(hp, mp, "name", &tlen);
    303		if (!type) {
    304			type = mdesc_node_name(hp, mp);
    305			tlen = strlen(type) + 1;
    306		}
    307	}
    308	if (tlen > VIO_MAX_TYPE_LEN || strlen(type) >= VIO_MAX_TYPE_LEN) {
    309		printk(KERN_ERR "VIO: Type string [%s] is too long.\n",
    310		       type);
    311		return NULL;
    312	}
    313
    314	id = mdesc_get_property(hp, mp, "id", NULL);
    315
    316	cfg_handle = vio_cfg_handle(hp, mp);
    317
    318	compat = mdesc_get_property(hp, mp, "device-type", &clen);
    319	if (!compat) {
    320		clen = 0;
    321	} else if (clen > VIO_MAX_COMPAT_LEN) {
    322		printk(KERN_ERR "VIO: Compat len %d for [%s] is too long.\n",
    323		       clen, type);
    324		return NULL;
    325	}
    326
    327	vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
    328	if (!vdev) {
    329		printk(KERN_ERR "VIO: Could not allocate vio_dev\n");
    330		return NULL;
    331	}
    332
    333	vdev->mp = mp;
    334	memcpy(vdev->type, type, tlen);
    335	if (compat)
    336		memcpy(vdev->compat, compat, clen);
    337	else
    338		memset(vdev->compat, 0, sizeof(vdev->compat));
    339	vdev->compat_len = clen;
    340
    341	vdev->port_id = ~0UL;
    342	vdev->tx_irq = 0;
    343	vdev->rx_irq = 0;
    344
    345	vio_fill_channel_info(hp, mp, vdev);
    346
    347	if (!id) {
    348		dev_set_name(&vdev->dev, "%s", type);
    349		vdev->dev_no = ~(u64)0;
    350	} else if (!cfg_handle) {
    351		dev_set_name(&vdev->dev, "%s-%llu", type, *id);
    352		vdev->dev_no = *id;
    353	} else {
    354		dev_set_name(&vdev->dev, "%s-%llu-%llu", type,
    355			     *cfg_handle, *id);
    356		vdev->dev_no = *cfg_handle;
    357		vdev->port_id = *id;
    358	}
    359
    360	vdev->dev.parent = parent;
    361	vdev->dev.bus = &vio_bus_type;
    362	vdev->dev.release = vio_dev_release;
    363
    364	if (parent == NULL) {
    365		dp = cdev_node;
    366	} else if (to_vio_dev(parent) == root_vdev) {
    367		for_each_child_of_node(cdev_node, dp) {
    368			if (of_node_is_type(dp, type))
    369				break;
    370		}
    371	} else {
    372		dp = to_vio_dev(parent)->dp;
    373	}
    374	vdev->dp = dp;
    375
    376	/*
    377	 * node_name is NULL for the parent/channel-devices node and
    378	 * the parent doesn't require the MD node info.
    379	 */
    380	if (node_name != NULL) {
    381		(void) snprintf(vdev->node_name, VIO_MAX_NAME_LEN, "%s",
    382				node_name);
    383
    384		err = mdesc_get_node_info(hp, mp, node_name,
    385					  &vdev->md_node_info);
    386		if (err) {
    387			pr_err("VIO: Could not get MD node info %s, err=%d\n",
    388			       dev_name(&vdev->dev), err);
    389			kfree(vdev);
    390			return NULL;
    391		}
    392	}
    393
    394	pr_info("VIO: Adding device %s (tx_ino = %llx, rx_ino = %llx)\n",
    395		dev_name(&vdev->dev), vdev->tx_ino, vdev->rx_ino);
    396
    397	err = device_register(&vdev->dev);
    398	if (err) {
    399		printk(KERN_ERR "VIO: Could not register device %s, err=%d\n",
    400		       dev_name(&vdev->dev), err);
    401		put_device(&vdev->dev);
    402		return NULL;
    403	}
    404	if (vdev->dp)
    405		err = sysfs_create_file(&vdev->dev.kobj,
    406					&dev_attr_obppath.attr);
    407
    408	return vdev;
    409}
    410
    411static void vio_add(struct mdesc_handle *hp, u64 node,
    412		    const char *node_name)
    413{
    414	(void) vio_create_one(hp, node, node_name, &root_vdev->dev);
    415}
    416
    417struct vio_remove_node_data {
    418	struct mdesc_handle *hp;
    419	u64 node;
    420};
    421
    422static int vio_md_node_match(struct device *dev, void *arg)
    423{
    424	struct vio_dev *vdev = to_vio_dev(dev);
    425	struct vio_remove_node_data *node_data;
    426	u64 node;
    427
    428	node_data = (struct vio_remove_node_data *)arg;
    429
    430	node = vio_vdev_node(node_data->hp, vdev);
    431
    432	if (node == node_data->node)
    433		return 1;
    434	else
    435		return 0;
    436}
    437
    438static void vio_remove(struct mdesc_handle *hp, u64 node, const char *node_name)
    439{
    440	struct vio_remove_node_data node_data;
    441	struct device *dev;
    442
    443	node_data.hp = hp;
    444	node_data.node = node;
    445
    446	dev = device_find_child(&root_vdev->dev, (void *)&node_data,
    447				vio_md_node_match);
    448	if (dev) {
    449		printk(KERN_INFO "VIO: Removing device %s\n", dev_name(dev));
    450
    451		device_unregister(dev);
    452		put_device(dev);
    453	} else {
    454		pr_err("VIO: %s node not found in MDESC\n", node_name);
    455	}
    456}
    457
    458static struct mdesc_notifier_client vio_device_notifier = {
    459	.add		= vio_add,
    460	.remove		= vio_remove,
    461	.node_name	= "virtual-device-port",
    462};
    463
    464/* We are only interested in domain service ports under the
    465 * "domain-services" node.  On control nodes there is another port
    466 * under "openboot" that we should not mess with as aparently that is
    467 * reserved exclusively for OBP use.
    468 */
    469static void vio_add_ds(struct mdesc_handle *hp, u64 node,
    470		       const char *node_name)
    471{
    472	int found;
    473	u64 a;
    474
    475	found = 0;
    476	mdesc_for_each_arc(a, hp, node, MDESC_ARC_TYPE_BACK) {
    477		u64 target = mdesc_arc_target(hp, a);
    478		const char *name = mdesc_node_name(hp, target);
    479
    480		if (!strcmp(name, "domain-services")) {
    481			found = 1;
    482			break;
    483		}
    484	}
    485
    486	if (found)
    487		(void) vio_create_one(hp, node, node_name, &root_vdev->dev);
    488}
    489
    490static struct mdesc_notifier_client vio_ds_notifier = {
    491	.add		= vio_add_ds,
    492	.remove		= vio_remove,
    493	.node_name	= "domain-services-port",
    494};
    495
    496static const char *channel_devices_node = "channel-devices";
    497static const char *channel_devices_compat = "SUNW,sun4v-channel-devices";
    498static const char *cfg_handle_prop = "cfg-handle";
    499
    500static int __init vio_init(void)
    501{
    502	struct mdesc_handle *hp;
    503	const char *compat;
    504	const u64 *cfg_handle;
    505	int err, len;
    506	u64 root;
    507
    508	err = bus_register(&vio_bus_type);
    509	if (err) {
    510		printk(KERN_ERR "VIO: Could not register bus type err=%d\n",
    511		       err);
    512		return err;
    513	}
    514
    515	hp = mdesc_grab();
    516	if (!hp)
    517		return 0;
    518
    519	root = mdesc_node_by_name(hp, MDESC_NODE_NULL, channel_devices_node);
    520	if (root == MDESC_NODE_NULL) {
    521		printk(KERN_INFO "VIO: No channel-devices MDESC node.\n");
    522		mdesc_release(hp);
    523		return 0;
    524	}
    525
    526	cdev_node = of_find_node_by_name(NULL, "channel-devices");
    527	err = -ENODEV;
    528	if (!cdev_node) {
    529		printk(KERN_INFO "VIO: No channel-devices OBP node.\n");
    530		goto out_release;
    531	}
    532
    533	compat = mdesc_get_property(hp, root, "compatible", &len);
    534	if (!compat) {
    535		printk(KERN_ERR "VIO: Channel devices lacks compatible "
    536		       "property\n");
    537		goto out_release;
    538	}
    539	if (!of_find_in_proplist(compat, channel_devices_compat, len)) {
    540		printk(KERN_ERR "VIO: Channel devices node lacks (%s) "
    541		       "compat entry.\n", channel_devices_compat);
    542		goto out_release;
    543	}
    544
    545	cfg_handle = mdesc_get_property(hp, root, cfg_handle_prop, NULL);
    546	if (!cfg_handle) {
    547		printk(KERN_ERR "VIO: Channel devices lacks %s property\n",
    548		       cfg_handle_prop);
    549		goto out_release;
    550	}
    551
    552	cdev_cfg_handle = *cfg_handle;
    553
    554	root_vdev = vio_create_one(hp, root, NULL, NULL);
    555	err = -ENODEV;
    556	if (!root_vdev) {
    557		printk(KERN_ERR "VIO: Could not create root device.\n");
    558		goto out_release;
    559	}
    560
    561	mdesc_register_notifier(&vio_device_notifier);
    562	mdesc_register_notifier(&vio_ds_notifier);
    563
    564	mdesc_release(hp);
    565
    566	return err;
    567
    568out_release:
    569	mdesc_release(hp);
    570	return err;
    571}
    572
    573postcore_initcall(vio_init);