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

uacce.c (12349B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2#include <linux/compat.h>
      3#include <linux/dma-mapping.h>
      4#include <linux/iommu.h>
      5#include <linux/module.h>
      6#include <linux/poll.h>
      7#include <linux/slab.h>
      8#include <linux/uacce.h>
      9
     10static struct class *uacce_class;
     11static dev_t uacce_devt;
     12static DEFINE_MUTEX(uacce_mutex);
     13static DEFINE_XARRAY_ALLOC(uacce_xa);
     14
     15static int uacce_start_queue(struct uacce_queue *q)
     16{
     17	int ret = 0;
     18
     19	mutex_lock(&uacce_mutex);
     20
     21	if (q->state != UACCE_Q_INIT) {
     22		ret = -EINVAL;
     23		goto out_with_lock;
     24	}
     25
     26	if (q->uacce->ops->start_queue) {
     27		ret = q->uacce->ops->start_queue(q);
     28		if (ret < 0)
     29			goto out_with_lock;
     30	}
     31
     32	q->state = UACCE_Q_STARTED;
     33
     34out_with_lock:
     35	mutex_unlock(&uacce_mutex);
     36
     37	return ret;
     38}
     39
     40static int uacce_put_queue(struct uacce_queue *q)
     41{
     42	struct uacce_device *uacce = q->uacce;
     43
     44	mutex_lock(&uacce_mutex);
     45
     46	if (q->state == UACCE_Q_ZOMBIE)
     47		goto out;
     48
     49	if ((q->state == UACCE_Q_STARTED) && uacce->ops->stop_queue)
     50		uacce->ops->stop_queue(q);
     51
     52	if ((q->state == UACCE_Q_INIT || q->state == UACCE_Q_STARTED) &&
     53	     uacce->ops->put_queue)
     54		uacce->ops->put_queue(q);
     55
     56	q->state = UACCE_Q_ZOMBIE;
     57out:
     58	mutex_unlock(&uacce_mutex);
     59
     60	return 0;
     61}
     62
     63static long uacce_fops_unl_ioctl(struct file *filep,
     64				 unsigned int cmd, unsigned long arg)
     65{
     66	struct uacce_queue *q = filep->private_data;
     67	struct uacce_device *uacce = q->uacce;
     68
     69	switch (cmd) {
     70	case UACCE_CMD_START_Q:
     71		return uacce_start_queue(q);
     72
     73	case UACCE_CMD_PUT_Q:
     74		return uacce_put_queue(q);
     75
     76	default:
     77		if (!uacce->ops->ioctl)
     78			return -EINVAL;
     79
     80		return uacce->ops->ioctl(q, cmd, arg);
     81	}
     82}
     83
     84#ifdef CONFIG_COMPAT
     85static long uacce_fops_compat_ioctl(struct file *filep,
     86				   unsigned int cmd, unsigned long arg)
     87{
     88	arg = (unsigned long)compat_ptr(arg);
     89
     90	return uacce_fops_unl_ioctl(filep, cmd, arg);
     91}
     92#endif
     93
     94static int uacce_bind_queue(struct uacce_device *uacce, struct uacce_queue *q)
     95{
     96	u32 pasid;
     97	struct iommu_sva *handle;
     98
     99	if (!(uacce->flags & UACCE_DEV_SVA))
    100		return 0;
    101
    102	handle = iommu_sva_bind_device(uacce->parent, current->mm, NULL);
    103	if (IS_ERR(handle))
    104		return PTR_ERR(handle);
    105
    106	pasid = iommu_sva_get_pasid(handle);
    107	if (pasid == IOMMU_PASID_INVALID) {
    108		iommu_sva_unbind_device(handle);
    109		return -ENODEV;
    110	}
    111
    112	q->handle = handle;
    113	q->pasid = pasid;
    114	return 0;
    115}
    116
    117static void uacce_unbind_queue(struct uacce_queue *q)
    118{
    119	if (!q->handle)
    120		return;
    121	iommu_sva_unbind_device(q->handle);
    122	q->handle = NULL;
    123}
    124
    125static int uacce_fops_open(struct inode *inode, struct file *filep)
    126{
    127	struct uacce_device *uacce;
    128	struct uacce_queue *q;
    129	int ret;
    130
    131	uacce = xa_load(&uacce_xa, iminor(inode));
    132	if (!uacce)
    133		return -ENODEV;
    134
    135	q = kzalloc(sizeof(struct uacce_queue), GFP_KERNEL);
    136	if (!q)
    137		return -ENOMEM;
    138
    139	ret = uacce_bind_queue(uacce, q);
    140	if (ret)
    141		goto out_with_mem;
    142
    143	q->uacce = uacce;
    144
    145	if (uacce->ops->get_queue) {
    146		ret = uacce->ops->get_queue(uacce, q->pasid, q);
    147		if (ret < 0)
    148			goto out_with_bond;
    149	}
    150
    151	init_waitqueue_head(&q->wait);
    152	filep->private_data = q;
    153	uacce->inode = inode;
    154	q->state = UACCE_Q_INIT;
    155
    156	mutex_lock(&uacce->queues_lock);
    157	list_add(&q->list, &uacce->queues);
    158	mutex_unlock(&uacce->queues_lock);
    159
    160	return 0;
    161
    162out_with_bond:
    163	uacce_unbind_queue(q);
    164out_with_mem:
    165	kfree(q);
    166	return ret;
    167}
    168
    169static int uacce_fops_release(struct inode *inode, struct file *filep)
    170{
    171	struct uacce_queue *q = filep->private_data;
    172
    173	mutex_lock(&q->uacce->queues_lock);
    174	list_del(&q->list);
    175	mutex_unlock(&q->uacce->queues_lock);
    176	uacce_put_queue(q);
    177	uacce_unbind_queue(q);
    178	kfree(q);
    179
    180	return 0;
    181}
    182
    183static void uacce_vma_close(struct vm_area_struct *vma)
    184{
    185	struct uacce_queue *q = vma->vm_private_data;
    186	struct uacce_qfile_region *qfr = NULL;
    187
    188	if (vma->vm_pgoff < UACCE_MAX_REGION)
    189		qfr = q->qfrs[vma->vm_pgoff];
    190
    191	kfree(qfr);
    192}
    193
    194static const struct vm_operations_struct uacce_vm_ops = {
    195	.close = uacce_vma_close,
    196};
    197
    198static int uacce_fops_mmap(struct file *filep, struct vm_area_struct *vma)
    199{
    200	struct uacce_queue *q = filep->private_data;
    201	struct uacce_device *uacce = q->uacce;
    202	struct uacce_qfile_region *qfr;
    203	enum uacce_qfrt type = UACCE_MAX_REGION;
    204	int ret = 0;
    205
    206	if (vma->vm_pgoff < UACCE_MAX_REGION)
    207		type = vma->vm_pgoff;
    208	else
    209		return -EINVAL;
    210
    211	qfr = kzalloc(sizeof(*qfr), GFP_KERNEL);
    212	if (!qfr)
    213		return -ENOMEM;
    214
    215	vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND | VM_WIPEONFORK;
    216	vma->vm_ops = &uacce_vm_ops;
    217	vma->vm_private_data = q;
    218	qfr->type = type;
    219
    220	mutex_lock(&uacce_mutex);
    221
    222	if (q->state != UACCE_Q_INIT && q->state != UACCE_Q_STARTED) {
    223		ret = -EINVAL;
    224		goto out_with_lock;
    225	}
    226
    227	if (q->qfrs[type]) {
    228		ret = -EEXIST;
    229		goto out_with_lock;
    230	}
    231
    232	switch (type) {
    233	case UACCE_QFRT_MMIO:
    234	case UACCE_QFRT_DUS:
    235		if (!uacce->ops->mmap) {
    236			ret = -EINVAL;
    237			goto out_with_lock;
    238		}
    239
    240		ret = uacce->ops->mmap(q, vma, qfr);
    241		if (ret)
    242			goto out_with_lock;
    243		break;
    244
    245	default:
    246		ret = -EINVAL;
    247		goto out_with_lock;
    248	}
    249
    250	q->qfrs[type] = qfr;
    251	mutex_unlock(&uacce_mutex);
    252
    253	return ret;
    254
    255out_with_lock:
    256	mutex_unlock(&uacce_mutex);
    257	kfree(qfr);
    258	return ret;
    259}
    260
    261static __poll_t uacce_fops_poll(struct file *file, poll_table *wait)
    262{
    263	struct uacce_queue *q = file->private_data;
    264	struct uacce_device *uacce = q->uacce;
    265
    266	poll_wait(file, &q->wait, wait);
    267	if (uacce->ops->is_q_updated && uacce->ops->is_q_updated(q))
    268		return EPOLLIN | EPOLLRDNORM;
    269
    270	return 0;
    271}
    272
    273static const struct file_operations uacce_fops = {
    274	.owner		= THIS_MODULE,
    275	.open		= uacce_fops_open,
    276	.release	= uacce_fops_release,
    277	.unlocked_ioctl	= uacce_fops_unl_ioctl,
    278#ifdef CONFIG_COMPAT
    279	.compat_ioctl	= uacce_fops_compat_ioctl,
    280#endif
    281	.mmap		= uacce_fops_mmap,
    282	.poll		= uacce_fops_poll,
    283};
    284
    285#define to_uacce_device(dev) container_of(dev, struct uacce_device, dev)
    286
    287static ssize_t api_show(struct device *dev,
    288			struct device_attribute *attr, char *buf)
    289{
    290	struct uacce_device *uacce = to_uacce_device(dev);
    291
    292	return sysfs_emit(buf, "%s\n", uacce->api_ver);
    293}
    294
    295static ssize_t flags_show(struct device *dev,
    296			  struct device_attribute *attr, char *buf)
    297{
    298	struct uacce_device *uacce = to_uacce_device(dev);
    299
    300	return sysfs_emit(buf, "%u\n", uacce->flags);
    301}
    302
    303static ssize_t available_instances_show(struct device *dev,
    304					struct device_attribute *attr,
    305					char *buf)
    306{
    307	struct uacce_device *uacce = to_uacce_device(dev);
    308
    309	if (!uacce->ops->get_available_instances)
    310		return -ENODEV;
    311
    312	return sysfs_emit(buf, "%d\n",
    313		       uacce->ops->get_available_instances(uacce));
    314}
    315
    316static ssize_t algorithms_show(struct device *dev,
    317			       struct device_attribute *attr, char *buf)
    318{
    319	struct uacce_device *uacce = to_uacce_device(dev);
    320
    321	return sysfs_emit(buf, "%s\n", uacce->algs);
    322}
    323
    324static ssize_t region_mmio_size_show(struct device *dev,
    325				     struct device_attribute *attr, char *buf)
    326{
    327	struct uacce_device *uacce = to_uacce_device(dev);
    328
    329	return sysfs_emit(buf, "%lu\n",
    330		       uacce->qf_pg_num[UACCE_QFRT_MMIO] << PAGE_SHIFT);
    331}
    332
    333static ssize_t region_dus_size_show(struct device *dev,
    334				    struct device_attribute *attr, char *buf)
    335{
    336	struct uacce_device *uacce = to_uacce_device(dev);
    337
    338	return sysfs_emit(buf, "%lu\n",
    339		       uacce->qf_pg_num[UACCE_QFRT_DUS] << PAGE_SHIFT);
    340}
    341
    342static DEVICE_ATTR_RO(api);
    343static DEVICE_ATTR_RO(flags);
    344static DEVICE_ATTR_RO(available_instances);
    345static DEVICE_ATTR_RO(algorithms);
    346static DEVICE_ATTR_RO(region_mmio_size);
    347static DEVICE_ATTR_RO(region_dus_size);
    348
    349static struct attribute *uacce_dev_attrs[] = {
    350	&dev_attr_api.attr,
    351	&dev_attr_flags.attr,
    352	&dev_attr_available_instances.attr,
    353	&dev_attr_algorithms.attr,
    354	&dev_attr_region_mmio_size.attr,
    355	&dev_attr_region_dus_size.attr,
    356	NULL,
    357};
    358
    359static umode_t uacce_dev_is_visible(struct kobject *kobj,
    360				    struct attribute *attr, int n)
    361{
    362	struct device *dev = kobj_to_dev(kobj);
    363	struct uacce_device *uacce = to_uacce_device(dev);
    364
    365	if (((attr == &dev_attr_region_mmio_size.attr) &&
    366	    (!uacce->qf_pg_num[UACCE_QFRT_MMIO])) ||
    367	    ((attr == &dev_attr_region_dus_size.attr) &&
    368	    (!uacce->qf_pg_num[UACCE_QFRT_DUS])))
    369		return 0;
    370
    371	return attr->mode;
    372}
    373
    374static struct attribute_group uacce_dev_group = {
    375	.is_visible	= uacce_dev_is_visible,
    376	.attrs		= uacce_dev_attrs,
    377};
    378
    379__ATTRIBUTE_GROUPS(uacce_dev);
    380
    381static void uacce_release(struct device *dev)
    382{
    383	struct uacce_device *uacce = to_uacce_device(dev);
    384
    385	kfree(uacce);
    386}
    387
    388static unsigned int uacce_enable_sva(struct device *parent, unsigned int flags)
    389{
    390	int ret;
    391
    392	if (!(flags & UACCE_DEV_SVA))
    393		return flags;
    394
    395	flags &= ~UACCE_DEV_SVA;
    396
    397	ret = iommu_dev_enable_feature(parent, IOMMU_DEV_FEAT_IOPF);
    398	if (ret) {
    399		dev_err(parent, "failed to enable IOPF feature! ret = %pe\n", ERR_PTR(ret));
    400		return flags;
    401	}
    402
    403	ret = iommu_dev_enable_feature(parent, IOMMU_DEV_FEAT_SVA);
    404	if (ret) {
    405		dev_err(parent, "failed to enable SVA feature! ret = %pe\n", ERR_PTR(ret));
    406		iommu_dev_disable_feature(parent, IOMMU_DEV_FEAT_IOPF);
    407		return flags;
    408	}
    409
    410	return flags | UACCE_DEV_SVA;
    411}
    412
    413static void uacce_disable_sva(struct uacce_device *uacce)
    414{
    415	if (!(uacce->flags & UACCE_DEV_SVA))
    416		return;
    417
    418	iommu_dev_disable_feature(uacce->parent, IOMMU_DEV_FEAT_SVA);
    419	iommu_dev_disable_feature(uacce->parent, IOMMU_DEV_FEAT_IOPF);
    420}
    421
    422/**
    423 * uacce_alloc() - alloc an accelerator
    424 * @parent: pointer of uacce parent device
    425 * @interface: pointer of uacce_interface for register
    426 *
    427 * Returns uacce pointer if success and ERR_PTR if not
    428 * Need check returned negotiated uacce->flags
    429 */
    430struct uacce_device *uacce_alloc(struct device *parent,
    431				 struct uacce_interface *interface)
    432{
    433	unsigned int flags = interface->flags;
    434	struct uacce_device *uacce;
    435	int ret;
    436
    437	uacce = kzalloc(sizeof(struct uacce_device), GFP_KERNEL);
    438	if (!uacce)
    439		return ERR_PTR(-ENOMEM);
    440
    441	flags = uacce_enable_sva(parent, flags);
    442
    443	uacce->parent = parent;
    444	uacce->flags = flags;
    445	uacce->ops = interface->ops;
    446
    447	ret = xa_alloc(&uacce_xa, &uacce->dev_id, uacce, xa_limit_32b,
    448		       GFP_KERNEL);
    449	if (ret < 0)
    450		goto err_with_uacce;
    451
    452	INIT_LIST_HEAD(&uacce->queues);
    453	mutex_init(&uacce->queues_lock);
    454	device_initialize(&uacce->dev);
    455	uacce->dev.devt = MKDEV(MAJOR(uacce_devt), uacce->dev_id);
    456	uacce->dev.class = uacce_class;
    457	uacce->dev.groups = uacce_dev_groups;
    458	uacce->dev.parent = uacce->parent;
    459	uacce->dev.release = uacce_release;
    460	dev_set_name(&uacce->dev, "%s-%d", interface->name, uacce->dev_id);
    461
    462	return uacce;
    463
    464err_with_uacce:
    465	uacce_disable_sva(uacce);
    466	kfree(uacce);
    467	return ERR_PTR(ret);
    468}
    469EXPORT_SYMBOL_GPL(uacce_alloc);
    470
    471/**
    472 * uacce_register() - add the accelerator to cdev and export to user space
    473 * @uacce: The initialized uacce device
    474 *
    475 * Return 0 if register succeeded, or an error.
    476 */
    477int uacce_register(struct uacce_device *uacce)
    478{
    479	if (!uacce)
    480		return -ENODEV;
    481
    482	uacce->cdev = cdev_alloc();
    483	if (!uacce->cdev)
    484		return -ENOMEM;
    485
    486	uacce->cdev->ops = &uacce_fops;
    487	uacce->cdev->owner = THIS_MODULE;
    488
    489	return cdev_device_add(uacce->cdev, &uacce->dev);
    490}
    491EXPORT_SYMBOL_GPL(uacce_register);
    492
    493/**
    494 * uacce_remove() - remove the accelerator
    495 * @uacce: the accelerator to remove
    496 */
    497void uacce_remove(struct uacce_device *uacce)
    498{
    499	struct uacce_queue *q, *next_q;
    500
    501	if (!uacce)
    502		return;
    503	/*
    504	 * unmap remaining mapping from user space, preventing user still
    505	 * access the mmaped area while parent device is already removed
    506	 */
    507	if (uacce->inode)
    508		unmap_mapping_range(uacce->inode->i_mapping, 0, 0, 1);
    509
    510	/* ensure no open queue remains */
    511	mutex_lock(&uacce->queues_lock);
    512	list_for_each_entry_safe(q, next_q, &uacce->queues, list) {
    513		uacce_put_queue(q);
    514		uacce_unbind_queue(q);
    515	}
    516	mutex_unlock(&uacce->queues_lock);
    517
    518	/* disable sva now since no opened queues */
    519	uacce_disable_sva(uacce);
    520
    521	if (uacce->cdev)
    522		cdev_device_del(uacce->cdev, &uacce->dev);
    523	xa_erase(&uacce_xa, uacce->dev_id);
    524	put_device(&uacce->dev);
    525}
    526EXPORT_SYMBOL_GPL(uacce_remove);
    527
    528static int __init uacce_init(void)
    529{
    530	int ret;
    531
    532	uacce_class = class_create(THIS_MODULE, UACCE_NAME);
    533	if (IS_ERR(uacce_class))
    534		return PTR_ERR(uacce_class);
    535
    536	ret = alloc_chrdev_region(&uacce_devt, 0, MINORMASK, UACCE_NAME);
    537	if (ret)
    538		class_destroy(uacce_class);
    539
    540	return ret;
    541}
    542
    543static __exit void uacce_exit(void)
    544{
    545	unregister_chrdev_region(uacce_devt, MINORMASK);
    546	class_destroy(uacce_class);
    547}
    548
    549subsys_initcall(uacce_init);
    550module_exit(uacce_exit);
    551
    552MODULE_LICENSE("GPL");
    553MODULE_AUTHOR("HiSilicon Tech. Co., Ltd.");
    554MODULE_DESCRIPTION("Accelerator interface for Userland applications");