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

mdev_core.c (8325B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Mediated device Core Driver
      4 *
      5 * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
      6 *     Author: Neo Jia <cjia@nvidia.com>
      7 *             Kirti Wankhede <kwankhede@nvidia.com>
      8 */
      9
     10#include <linux/module.h>
     11#include <linux/device.h>
     12#include <linux/slab.h>
     13#include <linux/uuid.h>
     14#include <linux/sysfs.h>
     15#include <linux/mdev.h>
     16
     17#include "mdev_private.h"
     18
     19#define DRIVER_VERSION		"0.1"
     20#define DRIVER_AUTHOR		"NVIDIA Corporation"
     21#define DRIVER_DESC		"Mediated device Core Driver"
     22
     23static LIST_HEAD(parent_list);
     24static DEFINE_MUTEX(parent_list_lock);
     25static struct class_compat *mdev_bus_compat_class;
     26
     27static LIST_HEAD(mdev_list);
     28static DEFINE_MUTEX(mdev_list_lock);
     29
     30struct device *mdev_parent_dev(struct mdev_device *mdev)
     31{
     32	return mdev->type->parent->dev;
     33}
     34EXPORT_SYMBOL(mdev_parent_dev);
     35
     36/*
     37 * Return the index in supported_type_groups that this mdev_device was created
     38 * from.
     39 */
     40unsigned int mdev_get_type_group_id(struct mdev_device *mdev)
     41{
     42	return mdev->type->type_group_id;
     43}
     44EXPORT_SYMBOL(mdev_get_type_group_id);
     45
     46/*
     47 * Used in mdev_type_attribute sysfs functions to return the index in the
     48 * supported_type_groups that the sysfs is called from.
     49 */
     50unsigned int mtype_get_type_group_id(struct mdev_type *mtype)
     51{
     52	return mtype->type_group_id;
     53}
     54EXPORT_SYMBOL(mtype_get_type_group_id);
     55
     56/*
     57 * Used in mdev_type_attribute sysfs functions to return the parent struct
     58 * device
     59 */
     60struct device *mtype_get_parent_dev(struct mdev_type *mtype)
     61{
     62	return mtype->parent->dev;
     63}
     64EXPORT_SYMBOL(mtype_get_parent_dev);
     65
     66/* Should be called holding parent_list_lock */
     67static struct mdev_parent *__find_parent_device(struct device *dev)
     68{
     69	struct mdev_parent *parent;
     70
     71	list_for_each_entry(parent, &parent_list, next) {
     72		if (parent->dev == dev)
     73			return parent;
     74	}
     75	return NULL;
     76}
     77
     78void mdev_release_parent(struct kref *kref)
     79{
     80	struct mdev_parent *parent = container_of(kref, struct mdev_parent,
     81						  ref);
     82	struct device *dev = parent->dev;
     83
     84	kfree(parent);
     85	put_device(dev);
     86}
     87
     88/* Caller must hold parent unreg_sem read or write lock */
     89static void mdev_device_remove_common(struct mdev_device *mdev)
     90{
     91	struct mdev_parent *parent = mdev->type->parent;
     92
     93	mdev_remove_sysfs_files(mdev);
     94	device_del(&mdev->dev);
     95	lockdep_assert_held(&parent->unreg_sem);
     96	/* Balances with device_initialize() */
     97	put_device(&mdev->dev);
     98}
     99
    100static int mdev_device_remove_cb(struct device *dev, void *data)
    101{
    102	struct mdev_device *mdev = mdev_from_dev(dev);
    103
    104	if (mdev)
    105		mdev_device_remove_common(mdev);
    106	return 0;
    107}
    108
    109/*
    110 * mdev_register_device : Register a device
    111 * @dev: device structure representing parent device.
    112 * @mdev_driver: Device driver to bind to the newly created mdev
    113 *
    114 * Add device to list of registered parent devices.
    115 * Returns a negative value on error, otherwise 0.
    116 */
    117int mdev_register_device(struct device *dev, struct mdev_driver *mdev_driver)
    118{
    119	int ret;
    120	struct mdev_parent *parent;
    121	char *env_string = "MDEV_STATE=registered";
    122	char *envp[] = { env_string, NULL };
    123
    124	/* check for mandatory ops */
    125	if (!mdev_driver->supported_type_groups)
    126		return -EINVAL;
    127
    128	dev = get_device(dev);
    129	if (!dev)
    130		return -EINVAL;
    131
    132	mutex_lock(&parent_list_lock);
    133
    134	/* Check for duplicate */
    135	parent = __find_parent_device(dev);
    136	if (parent) {
    137		parent = NULL;
    138		ret = -EEXIST;
    139		goto add_dev_err;
    140	}
    141
    142	parent = kzalloc(sizeof(*parent), GFP_KERNEL);
    143	if (!parent) {
    144		ret = -ENOMEM;
    145		goto add_dev_err;
    146	}
    147
    148	kref_init(&parent->ref);
    149	init_rwsem(&parent->unreg_sem);
    150
    151	parent->dev = dev;
    152	parent->mdev_driver = mdev_driver;
    153
    154	if (!mdev_bus_compat_class) {
    155		mdev_bus_compat_class = class_compat_register("mdev_bus");
    156		if (!mdev_bus_compat_class) {
    157			ret = -ENOMEM;
    158			goto add_dev_err;
    159		}
    160	}
    161
    162	ret = parent_create_sysfs_files(parent);
    163	if (ret)
    164		goto add_dev_err;
    165
    166	ret = class_compat_create_link(mdev_bus_compat_class, dev, NULL);
    167	if (ret)
    168		dev_warn(dev, "Failed to create compatibility class link\n");
    169
    170	list_add(&parent->next, &parent_list);
    171	mutex_unlock(&parent_list_lock);
    172
    173	dev_info(dev, "MDEV: Registered\n");
    174	kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
    175
    176	return 0;
    177
    178add_dev_err:
    179	mutex_unlock(&parent_list_lock);
    180	if (parent)
    181		mdev_put_parent(parent);
    182	else
    183		put_device(dev);
    184	return ret;
    185}
    186EXPORT_SYMBOL(mdev_register_device);
    187
    188/*
    189 * mdev_unregister_device : Unregister a parent device
    190 * @dev: device structure representing parent device.
    191 *
    192 * Remove device from list of registered parent devices. Give a chance to free
    193 * existing mediated devices for given device.
    194 */
    195
    196void mdev_unregister_device(struct device *dev)
    197{
    198	struct mdev_parent *parent;
    199	char *env_string = "MDEV_STATE=unregistered";
    200	char *envp[] = { env_string, NULL };
    201
    202	mutex_lock(&parent_list_lock);
    203	parent = __find_parent_device(dev);
    204
    205	if (!parent) {
    206		mutex_unlock(&parent_list_lock);
    207		return;
    208	}
    209	dev_info(dev, "MDEV: Unregistering\n");
    210
    211	list_del(&parent->next);
    212	mutex_unlock(&parent_list_lock);
    213
    214	down_write(&parent->unreg_sem);
    215
    216	class_compat_remove_link(mdev_bus_compat_class, dev, NULL);
    217
    218	device_for_each_child(dev, NULL, mdev_device_remove_cb);
    219
    220	parent_remove_sysfs_files(parent);
    221	up_write(&parent->unreg_sem);
    222
    223	mdev_put_parent(parent);
    224
    225	/* We still have the caller's reference to use for the uevent */
    226	kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp);
    227}
    228EXPORT_SYMBOL(mdev_unregister_device);
    229
    230static void mdev_device_release(struct device *dev)
    231{
    232	struct mdev_device *mdev = to_mdev_device(dev);
    233
    234	/* Pairs with the get in mdev_device_create() */
    235	kobject_put(&mdev->type->kobj);
    236
    237	mutex_lock(&mdev_list_lock);
    238	list_del(&mdev->next);
    239	mutex_unlock(&mdev_list_lock);
    240
    241	dev_dbg(&mdev->dev, "MDEV: destroying\n");
    242	kfree(mdev);
    243}
    244
    245int mdev_device_create(struct mdev_type *type, const guid_t *uuid)
    246{
    247	int ret;
    248	struct mdev_device *mdev, *tmp;
    249	struct mdev_parent *parent = type->parent;
    250	struct mdev_driver *drv = parent->mdev_driver;
    251
    252	mutex_lock(&mdev_list_lock);
    253
    254	/* Check for duplicate */
    255	list_for_each_entry(tmp, &mdev_list, next) {
    256		if (guid_equal(&tmp->uuid, uuid)) {
    257			mutex_unlock(&mdev_list_lock);
    258			return -EEXIST;
    259		}
    260	}
    261
    262	mdev = kzalloc(sizeof(*mdev), GFP_KERNEL);
    263	if (!mdev) {
    264		mutex_unlock(&mdev_list_lock);
    265		return -ENOMEM;
    266	}
    267
    268	device_initialize(&mdev->dev);
    269	mdev->dev.parent  = parent->dev;
    270	mdev->dev.bus = &mdev_bus_type;
    271	mdev->dev.release = mdev_device_release;
    272	mdev->dev.groups = mdev_device_groups;
    273	mdev->type = type;
    274	/* Pairs with the put in mdev_device_release() */
    275	kobject_get(&type->kobj);
    276
    277	guid_copy(&mdev->uuid, uuid);
    278	list_add(&mdev->next, &mdev_list);
    279	mutex_unlock(&mdev_list_lock);
    280
    281	ret = dev_set_name(&mdev->dev, "%pUl", uuid);
    282	if (ret)
    283		goto out_put_device;
    284
    285	/* Check if parent unregistration has started */
    286	if (!down_read_trylock(&parent->unreg_sem)) {
    287		ret = -ENODEV;
    288		goto out_put_device;
    289	}
    290
    291	ret = device_add(&mdev->dev);
    292	if (ret)
    293		goto out_unlock;
    294
    295	ret = device_driver_attach(&drv->driver, &mdev->dev);
    296	if (ret)
    297		goto out_del;
    298
    299	ret = mdev_create_sysfs_files(mdev);
    300	if (ret)
    301		goto out_del;
    302
    303	mdev->active = true;
    304	dev_dbg(&mdev->dev, "MDEV: created\n");
    305	up_read(&parent->unreg_sem);
    306
    307	return 0;
    308
    309out_del:
    310	device_del(&mdev->dev);
    311out_unlock:
    312	up_read(&parent->unreg_sem);
    313out_put_device:
    314	put_device(&mdev->dev);
    315	return ret;
    316}
    317
    318int mdev_device_remove(struct mdev_device *mdev)
    319{
    320	struct mdev_device *tmp;
    321	struct mdev_parent *parent = mdev->type->parent;
    322
    323	mutex_lock(&mdev_list_lock);
    324	list_for_each_entry(tmp, &mdev_list, next) {
    325		if (tmp == mdev)
    326			break;
    327	}
    328
    329	if (tmp != mdev) {
    330		mutex_unlock(&mdev_list_lock);
    331		return -ENODEV;
    332	}
    333
    334	if (!mdev->active) {
    335		mutex_unlock(&mdev_list_lock);
    336		return -EAGAIN;
    337	}
    338
    339	mdev->active = false;
    340	mutex_unlock(&mdev_list_lock);
    341
    342	/* Check if parent unregistration has started */
    343	if (!down_read_trylock(&parent->unreg_sem))
    344		return -ENODEV;
    345
    346	mdev_device_remove_common(mdev);
    347	up_read(&parent->unreg_sem);
    348	return 0;
    349}
    350
    351static int __init mdev_init(void)
    352{
    353	return bus_register(&mdev_bus_type);
    354}
    355
    356static void __exit mdev_exit(void)
    357{
    358	if (mdev_bus_compat_class)
    359		class_compat_unregister(mdev_bus_compat_class);
    360	bus_unregister(&mdev_bus_type);
    361}
    362
    363subsys_initcall(mdev_init)
    364module_exit(mdev_exit)
    365
    366MODULE_VERSION(DRIVER_VERSION);
    367MODULE_LICENSE("GPL v2");
    368MODULE_AUTHOR(DRIVER_AUTHOR);
    369MODULE_DESCRIPTION(DRIVER_DESC);