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

nouveau_usif.c (10654B)


      1/*
      2 * Copyright 2014 Red Hat Inc.
      3 *
      4 * Permission is hereby granted, free of charge, to any person obtaining a
      5 * copy of this software and associated documentation files (the "Software"),
      6 * to deal in the Software without restriction, including without limitation
      7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      8 * and/or sell copies of the Software, and to permit persons to whom the
      9 * Software is furnished to do so, subject to the following conditions:
     10 *
     11 * The above copyright notice and this permission notice shall be included in
     12 * all copies or substantial portions of the Software.
     13 *
     14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
     18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     20 * OTHER DEALINGS IN THE SOFTWARE.
     21 *
     22 * Authors: Ben Skeggs <bskeggs@redhat.com>
     23 */
     24
     25#include "nouveau_drv.h"
     26#include "nouveau_usif.h"
     27#include "nouveau_abi16.h"
     28
     29#include <nvif/notify.h>
     30#include <nvif/unpack.h>
     31#include <nvif/client.h>
     32#include <nvif/event.h>
     33#include <nvif/ioctl.h>
     34
     35#include <nvif/class.h>
     36#include <nvif/cl0080.h>
     37
     38struct usif_notify_p {
     39	struct drm_pending_event base;
     40	struct {
     41		struct drm_event base;
     42		u8 data[];
     43	} e;
     44};
     45
     46struct usif_notify {
     47	struct list_head head;
     48	atomic_t enabled;
     49	u32 handle;
     50	u16 reply;
     51	u8  route;
     52	u64 token;
     53	struct usif_notify_p *p;
     54};
     55
     56static inline struct usif_notify *
     57usif_notify_find(struct drm_file *filp, u32 handle)
     58{
     59	struct nouveau_cli *cli = nouveau_cli(filp);
     60	struct usif_notify *ntfy;
     61	list_for_each_entry(ntfy, &cli->notifys, head) {
     62		if (ntfy->handle == handle)
     63			return ntfy;
     64	}
     65	return NULL;
     66}
     67
     68static inline void
     69usif_notify_dtor(struct usif_notify *ntfy)
     70{
     71	list_del(&ntfy->head);
     72	kfree(ntfy);
     73}
     74
     75int
     76usif_notify(const void *header, u32 length, const void *data, u32 size)
     77{
     78	struct usif_notify *ntfy = NULL;
     79	const union {
     80		struct nvif_notify_rep_v0 v0;
     81	} *rep = header;
     82	struct drm_device *dev;
     83	struct drm_file *filp;
     84	unsigned long flags;
     85
     86	if (length == sizeof(rep->v0) && rep->v0.version == 0) {
     87		if (WARN_ON(!(ntfy = (void *)(unsigned long)rep->v0.token)))
     88			return NVIF_NOTIFY_DROP;
     89		BUG_ON(rep->v0.route != NVDRM_NOTIFY_USIF);
     90	} else
     91	if (WARN_ON(1))
     92		return NVIF_NOTIFY_DROP;
     93
     94	if (WARN_ON(!ntfy->p || ntfy->reply != (length + size)))
     95		return NVIF_NOTIFY_DROP;
     96	filp = ntfy->p->base.file_priv;
     97	dev = filp->minor->dev;
     98
     99	memcpy(&ntfy->p->e.data[0], header, length);
    100	memcpy(&ntfy->p->e.data[length], data, size);
    101	switch (rep->v0.version) {
    102	case 0: {
    103		struct nvif_notify_rep_v0 *rep = (void *)ntfy->p->e.data;
    104		rep->route = ntfy->route;
    105		rep->token = ntfy->token;
    106	}
    107		break;
    108	default:
    109		BUG();
    110		break;
    111	}
    112
    113	spin_lock_irqsave(&dev->event_lock, flags);
    114	if (!WARN_ON(filp->event_space < ntfy->p->e.base.length)) {
    115		list_add_tail(&ntfy->p->base.link, &filp->event_list);
    116		filp->event_space -= ntfy->p->e.base.length;
    117	}
    118	wake_up_interruptible(&filp->event_wait);
    119	spin_unlock_irqrestore(&dev->event_lock, flags);
    120	atomic_set(&ntfy->enabled, 0);
    121	return NVIF_NOTIFY_DROP;
    122}
    123
    124static int
    125usif_notify_new(struct drm_file *f, void *data, u32 size, void *argv, u32 argc)
    126{
    127	struct nouveau_cli *cli = nouveau_cli(f);
    128	struct nvif_client *client = &cli->base;
    129	union {
    130		struct nvif_ioctl_ntfy_new_v0 v0;
    131	} *args = data;
    132	union {
    133		struct nvif_notify_req_v0 v0;
    134	} *req;
    135	struct usif_notify *ntfy;
    136	int ret = -ENOSYS;
    137
    138	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
    139		if (usif_notify_find(f, args->v0.index))
    140			return -EEXIST;
    141	} else
    142		return ret;
    143	req = data;
    144	ret = -ENOSYS;
    145
    146	if (!(ntfy = kmalloc(sizeof(*ntfy), GFP_KERNEL)))
    147		return -ENOMEM;
    148	atomic_set(&ntfy->enabled, 0);
    149
    150	if (!(ret = nvif_unpack(ret, &data, &size, req->v0, 0, 0, true))) {
    151		ntfy->reply = sizeof(struct nvif_notify_rep_v0) + req->v0.reply;
    152		ntfy->route = req->v0.route;
    153		ntfy->token = req->v0.token;
    154		req->v0.route = NVDRM_NOTIFY_USIF;
    155		req->v0.token = (unsigned long)(void *)ntfy;
    156		ret = nvif_client_ioctl(client, argv, argc);
    157		req->v0.token = ntfy->token;
    158		req->v0.route = ntfy->route;
    159		ntfy->handle = args->v0.index;
    160	}
    161
    162	if (ret == 0)
    163		list_add(&ntfy->head, &cli->notifys);
    164	if (ret)
    165		kfree(ntfy);
    166	return ret;
    167}
    168
    169static int
    170usif_notify_del(struct drm_file *f, void *data, u32 size, void *argv, u32 argc)
    171{
    172	struct nouveau_cli *cli = nouveau_cli(f);
    173	struct nvif_client *client = &cli->base;
    174	union {
    175		struct nvif_ioctl_ntfy_del_v0 v0;
    176	} *args = data;
    177	struct usif_notify *ntfy;
    178	int ret = -ENOSYS;
    179
    180	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
    181		if (!(ntfy = usif_notify_find(f, args->v0.index)))
    182			return -ENOENT;
    183	} else
    184		return ret;
    185
    186	ret = nvif_client_ioctl(client, argv, argc);
    187	if (ret == 0)
    188		usif_notify_dtor(ntfy);
    189	return ret;
    190}
    191
    192static int
    193usif_notify_get(struct drm_file *f, void *data, u32 size, void *argv, u32 argc)
    194{
    195	struct nouveau_cli *cli = nouveau_cli(f);
    196	struct nvif_client *client = &cli->base;
    197	union {
    198		struct nvif_ioctl_ntfy_del_v0 v0;
    199	} *args = data;
    200	struct usif_notify *ntfy;
    201	int ret = -ENOSYS;
    202
    203	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
    204		if (!(ntfy = usif_notify_find(f, args->v0.index)))
    205			return -ENOENT;
    206	} else
    207		return ret;
    208
    209	if (atomic_xchg(&ntfy->enabled, 1))
    210		return 0;
    211
    212	ntfy->p = kmalloc(sizeof(*ntfy->p) + ntfy->reply, GFP_KERNEL);
    213	if (ret = -ENOMEM, !ntfy->p)
    214		goto done;
    215	ntfy->p->base.event = &ntfy->p->e.base;
    216	ntfy->p->base.file_priv = f;
    217	ntfy->p->e.base.type = DRM_NOUVEAU_EVENT_NVIF;
    218	ntfy->p->e.base.length = sizeof(ntfy->p->e.base) + ntfy->reply;
    219
    220	ret = nvif_client_ioctl(client, argv, argc);
    221done:
    222	if (ret) {
    223		atomic_set(&ntfy->enabled, 0);
    224		kfree(ntfy->p);
    225	}
    226	return ret;
    227}
    228
    229static int
    230usif_notify_put(struct drm_file *f, void *data, u32 size, void *argv, u32 argc)
    231{
    232	struct nouveau_cli *cli = nouveau_cli(f);
    233	struct nvif_client *client = &cli->base;
    234	union {
    235		struct nvif_ioctl_ntfy_put_v0 v0;
    236	} *args = data;
    237	struct usif_notify *ntfy;
    238	int ret = -ENOSYS;
    239
    240	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
    241		if (!(ntfy = usif_notify_find(f, args->v0.index)))
    242			return -ENOENT;
    243	} else
    244		return ret;
    245
    246	ret = nvif_client_ioctl(client, argv, argc);
    247	if (ret == 0 && atomic_xchg(&ntfy->enabled, 0))
    248		kfree(ntfy->p);
    249	return ret;
    250}
    251
    252struct usif_object {
    253	struct list_head head;
    254	struct list_head ntfy;
    255	u8  route;
    256	u64 token;
    257};
    258
    259static void
    260usif_object_dtor(struct usif_object *object)
    261{
    262	list_del(&object->head);
    263	kfree(object);
    264}
    265
    266static int
    267usif_object_new(struct drm_file *f, void *data, u32 size, void *argv, u32 argc, bool parent_abi16)
    268{
    269	struct nouveau_cli *cli = nouveau_cli(f);
    270	struct nvif_client *client = &cli->base;
    271	union {
    272		struct nvif_ioctl_new_v0 v0;
    273	} *args = data;
    274	struct usif_object *object;
    275	int ret = -ENOSYS;
    276
    277	if ((ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true)))
    278		return ret;
    279
    280	switch (args->v0.oclass) {
    281	case NV_DMA_FROM_MEMORY:
    282	case NV_DMA_TO_MEMORY:
    283	case NV_DMA_IN_MEMORY:
    284		return -EINVAL;
    285	case NV_DEVICE: {
    286		union {
    287			struct nv_device_v0 v0;
    288		} *args = data;
    289
    290		if ((ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false)))
    291			return ret;
    292
    293		args->v0.priv = false;
    294		break;
    295	}
    296	default:
    297		if (!parent_abi16)
    298			return -EINVAL;
    299		break;
    300	}
    301
    302	if (!(object = kmalloc(sizeof(*object), GFP_KERNEL)))
    303		return -ENOMEM;
    304	list_add(&object->head, &cli->objects);
    305
    306	object->route = args->v0.route;
    307	object->token = args->v0.token;
    308	args->v0.route = NVDRM_OBJECT_USIF;
    309	args->v0.token = (unsigned long)(void *)object;
    310	ret = nvif_client_ioctl(client, argv, argc);
    311	if (ret) {
    312		usif_object_dtor(object);
    313		return ret;
    314	}
    315
    316	args->v0.token = object->token;
    317	args->v0.route = object->route;
    318	return 0;
    319}
    320
    321int
    322usif_ioctl(struct drm_file *filp, void __user *user, u32 argc)
    323{
    324	struct nouveau_cli *cli = nouveau_cli(filp);
    325	struct nvif_client *client = &cli->base;
    326	void *data = kmalloc(argc, GFP_KERNEL);
    327	u32   size = argc;
    328	union {
    329		struct nvif_ioctl_v0 v0;
    330	} *argv = data;
    331	struct usif_object *object;
    332	bool abi16 = false;
    333	u8 owner;
    334	int ret;
    335
    336	if (ret = -ENOMEM, !argv)
    337		goto done;
    338	if (ret = -EFAULT, copy_from_user(argv, user, size))
    339		goto done;
    340
    341	if (!(ret = nvif_unpack(-ENOSYS, &data, &size, argv->v0, 0, 0, true))) {
    342		/* block access to objects not created via this interface */
    343		owner = argv->v0.owner;
    344		if (argv->v0.object == 0ULL &&
    345		    argv->v0.type != NVIF_IOCTL_V0_DEL)
    346			argv->v0.owner = NVDRM_OBJECT_ANY; /* except client */
    347		else
    348			argv->v0.owner = NVDRM_OBJECT_USIF;
    349	} else
    350		goto done;
    351
    352	/* USIF slightly abuses some return-only ioctl members in order
    353	 * to provide interoperability with the older ABI16 objects
    354	 */
    355	mutex_lock(&cli->mutex);
    356	if (argv->v0.route) {
    357		if (ret = -EINVAL, argv->v0.route == 0xff)
    358			ret = nouveau_abi16_usif(filp, argv, argc);
    359		if (ret) {
    360			mutex_unlock(&cli->mutex);
    361			goto done;
    362		}
    363
    364		abi16 = true;
    365	}
    366
    367	switch (argv->v0.type) {
    368	case NVIF_IOCTL_V0_NEW:
    369		ret = usif_object_new(filp, data, size, argv, argc, abi16);
    370		break;
    371	case NVIF_IOCTL_V0_NTFY_NEW:
    372		ret = usif_notify_new(filp, data, size, argv, argc);
    373		break;
    374	case NVIF_IOCTL_V0_NTFY_DEL:
    375		ret = usif_notify_del(filp, data, size, argv, argc);
    376		break;
    377	case NVIF_IOCTL_V0_NTFY_GET:
    378		ret = usif_notify_get(filp, data, size, argv, argc);
    379		break;
    380	case NVIF_IOCTL_V0_NTFY_PUT:
    381		ret = usif_notify_put(filp, data, size, argv, argc);
    382		break;
    383	default:
    384		ret = nvif_client_ioctl(client, argv, argc);
    385		break;
    386	}
    387	if (argv->v0.route == NVDRM_OBJECT_USIF) {
    388		object = (void *)(unsigned long)argv->v0.token;
    389		argv->v0.route = object->route;
    390		argv->v0.token = object->token;
    391		if (ret == 0 && argv->v0.type == NVIF_IOCTL_V0_DEL) {
    392			list_del(&object->head);
    393			kfree(object);
    394		}
    395	} else {
    396		argv->v0.route = NVIF_IOCTL_V0_ROUTE_HIDDEN;
    397		argv->v0.token = 0;
    398	}
    399	argv->v0.owner = owner;
    400	mutex_unlock(&cli->mutex);
    401
    402	if (copy_to_user(user, argv, argc))
    403		ret = -EFAULT;
    404done:
    405	kfree(argv);
    406	return ret;
    407}
    408
    409void
    410usif_client_fini(struct nouveau_cli *cli)
    411{
    412	struct usif_object *object, *otemp;
    413	struct usif_notify *notify, *ntemp;
    414
    415	list_for_each_entry_safe(notify, ntemp, &cli->notifys, head) {
    416		usif_notify_dtor(notify);
    417	}
    418
    419	list_for_each_entry_safe(object, otemp, &cli->objects, head) {
    420		usif_object_dtor(object);
    421	}
    422}
    423
    424void
    425usif_client_init(struct nouveau_cli *cli)
    426{
    427	INIT_LIST_HEAD(&cli->objects);
    428	INIT_LIST_HEAD(&cli->notifys);
    429}