127111a23SBen Skeggs /*
227111a23SBen Skeggs  * Copyright 2014 Red Hat Inc.
327111a23SBen Skeggs  *
427111a23SBen Skeggs  * Permission is hereby granted, free of charge, to any person obtaining a
527111a23SBen Skeggs  * copy of this software and associated documentation files (the "Software"),
627111a23SBen Skeggs  * to deal in the Software without restriction, including without limitation
727111a23SBen Skeggs  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
827111a23SBen Skeggs  * and/or sell copies of the Software, and to permit persons to whom the
927111a23SBen Skeggs  * Software is furnished to do so, subject to the following conditions:
1027111a23SBen Skeggs  *
1127111a23SBen Skeggs  * The above copyright notice and this permission notice shall be included in
1227111a23SBen Skeggs  * all copies or substantial portions of the Software.
1327111a23SBen Skeggs  *
1427111a23SBen Skeggs  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1527111a23SBen Skeggs  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1627111a23SBen Skeggs  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1727111a23SBen Skeggs  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
1827111a23SBen Skeggs  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
1927111a23SBen Skeggs  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
2027111a23SBen Skeggs  * OTHER DEALINGS IN THE SOFTWARE.
2127111a23SBen Skeggs  *
2227111a23SBen Skeggs  * Authors: Ben Skeggs <bskeggs@redhat.com>
2327111a23SBen Skeggs  */
2427111a23SBen Skeggs 
254dc28134SBen Skeggs #include "nouveau_drv.h"
2627111a23SBen Skeggs #include "nouveau_usif.h"
272621a416SBen Skeggs #include "nouveau_abi16.h"
2827111a23SBen Skeggs 
2927111a23SBen Skeggs #include <nvif/unpack.h>
3027111a23SBen Skeggs #include <nvif/client.h>
3127111a23SBen Skeggs #include <nvif/ioctl.h>
3227111a23SBen Skeggs 
33*148a8653SBen Skeggs #include <nvif/class.h>
34*148a8653SBen Skeggs #include <nvif/cl0080.h>
35*148a8653SBen Skeggs 
3627111a23SBen Skeggs struct usif_object {
3727111a23SBen Skeggs 	struct list_head head;
3827111a23SBen Skeggs 	u8  route;
3927111a23SBen Skeggs 	u64 token;
4027111a23SBen Skeggs };
4127111a23SBen Skeggs 
4227111a23SBen Skeggs static void
usif_object_dtor(struct usif_object * object)4327111a23SBen Skeggs usif_object_dtor(struct usif_object *object)
4427111a23SBen Skeggs {
4527111a23SBen Skeggs 	list_del(&object->head);
4627111a23SBen Skeggs 	kfree(object);
4727111a23SBen Skeggs }
4827111a23SBen Skeggs 
4927111a23SBen Skeggs static int
usif_object_new(struct drm_file * f,void * data,u32 size,void * argv,u32 argc,bool parent_abi16)50*148a8653SBen Skeggs usif_object_new(struct drm_file *f, void *data, u32 size, void *argv, u32 argc, bool parent_abi16)
5127111a23SBen Skeggs {
5227111a23SBen Skeggs 	struct nouveau_cli *cli = nouveau_cli(f);
5327111a23SBen Skeggs 	struct nvif_client *client = &cli->base;
5427111a23SBen Skeggs 	union {
5527111a23SBen Skeggs 		struct nvif_ioctl_new_v0 v0;
5627111a23SBen Skeggs 	} *args = data;
5727111a23SBen Skeggs 	struct usif_object *object;
58f01c4e68SBen Skeggs 	int ret = -ENOSYS;
5927111a23SBen Skeggs 
60*148a8653SBen Skeggs 	if ((ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true)))
61*148a8653SBen Skeggs 		return ret;
62*148a8653SBen Skeggs 
63*148a8653SBen Skeggs 	switch (args->v0.oclass) {
64*148a8653SBen Skeggs 	case NV_DMA_FROM_MEMORY:
65*148a8653SBen Skeggs 	case NV_DMA_TO_MEMORY:
66*148a8653SBen Skeggs 	case NV_DMA_IN_MEMORY:
67*148a8653SBen Skeggs 		return -EINVAL;
68*148a8653SBen Skeggs 	case NV_DEVICE: {
69*148a8653SBen Skeggs 		union {
70*148a8653SBen Skeggs 			struct nv_device_v0 v0;
71*148a8653SBen Skeggs 		} *args = data;
72*148a8653SBen Skeggs 
73*148a8653SBen Skeggs 		if ((ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false)))
74*148a8653SBen Skeggs 			return ret;
75*148a8653SBen Skeggs 
76*148a8653SBen Skeggs 		args->v0.priv = false;
77*148a8653SBen Skeggs 		break;
78*148a8653SBen Skeggs 	}
79*148a8653SBen Skeggs 	default:
80*148a8653SBen Skeggs 		if (!parent_abi16)
81*148a8653SBen Skeggs 			return -EINVAL;
82*148a8653SBen Skeggs 		break;
83*148a8653SBen Skeggs 	}
84*148a8653SBen Skeggs 
8527111a23SBen Skeggs 	if (!(object = kmalloc(sizeof(*object), GFP_KERNEL)))
8627111a23SBen Skeggs 		return -ENOMEM;
8727111a23SBen Skeggs 	list_add(&object->head, &cli->objects);
8827111a23SBen Skeggs 
8927111a23SBen Skeggs 	object->route = args->v0.route;
9027111a23SBen Skeggs 	object->token = args->v0.token;
9127111a23SBen Skeggs 	args->v0.route = NVDRM_OBJECT_USIF;
9227111a23SBen Skeggs 	args->v0.token = (unsigned long)(void *)object;
9327111a23SBen Skeggs 	ret = nvif_client_ioctl(client, argv, argc);
94*148a8653SBen Skeggs 	if (ret) {
9527111a23SBen Skeggs 		usif_object_dtor(object);
9627111a23SBen Skeggs 		return ret;
9727111a23SBen Skeggs 	}
9827111a23SBen Skeggs 
99*148a8653SBen Skeggs 	args->v0.token = object->token;
100*148a8653SBen Skeggs 	args->v0.route = object->route;
101*148a8653SBen Skeggs 	return 0;
102*148a8653SBen Skeggs }
103*148a8653SBen Skeggs 
10427111a23SBen Skeggs int
usif_ioctl(struct drm_file * filp,void __user * user,u32 argc)10527111a23SBen Skeggs usif_ioctl(struct drm_file *filp, void __user *user, u32 argc)
10627111a23SBen Skeggs {
10727111a23SBen Skeggs 	struct nouveau_cli *cli = nouveau_cli(filp);
10827111a23SBen Skeggs 	struct nvif_client *client = &cli->base;
10927111a23SBen Skeggs 	void *data = kmalloc(argc, GFP_KERNEL);
11027111a23SBen Skeggs 	u32   size = argc;
11127111a23SBen Skeggs 	union {
11227111a23SBen Skeggs 		struct nvif_ioctl_v0 v0;
11327111a23SBen Skeggs 	} *argv = data;
11427111a23SBen Skeggs 	struct usif_object *object;
115*148a8653SBen Skeggs 	bool abi16 = false;
11627111a23SBen Skeggs 	u8 owner;
11727111a23SBen Skeggs 	int ret;
11827111a23SBen Skeggs 
11927111a23SBen Skeggs 	if (ret = -ENOMEM, !argv)
12027111a23SBen Skeggs 		goto done;
12127111a23SBen Skeggs 	if (ret = -EFAULT, copy_from_user(argv, user, size))
12227111a23SBen Skeggs 		goto done;
12327111a23SBen Skeggs 
124f01c4e68SBen Skeggs 	if (!(ret = nvif_unpack(-ENOSYS, &data, &size, argv->v0, 0, 0, true))) {
12527111a23SBen Skeggs 		/* block access to objects not created via this interface */
12627111a23SBen Skeggs 		owner = argv->v0.owner;
127c966b627SBen Skeggs 		if (argv->v0.object == 0ULL &&
128c966b627SBen Skeggs 		    argv->v0.type != NVIF_IOCTL_V0_DEL)
129f5e55187SBen Skeggs 			argv->v0.owner = NVDRM_OBJECT_ANY; /* except client */
130f5e55187SBen Skeggs 		else
13127111a23SBen Skeggs 			argv->v0.owner = NVDRM_OBJECT_USIF;
13227111a23SBen Skeggs 	} else
13327111a23SBen Skeggs 		goto done;
13427111a23SBen Skeggs 
1352621a416SBen Skeggs 	/* USIF slightly abuses some return-only ioctl members in order
1362621a416SBen Skeggs 	 * to provide interoperability with the older ABI16 objects
1372621a416SBen Skeggs 	 */
13827111a23SBen Skeggs 	mutex_lock(&cli->mutex);
1392621a416SBen Skeggs 	if (argv->v0.route) {
1402621a416SBen Skeggs 		if (ret = -EINVAL, argv->v0.route == 0xff)
1412621a416SBen Skeggs 			ret = nouveau_abi16_usif(filp, argv, argc);
1422621a416SBen Skeggs 		if (ret) {
1432621a416SBen Skeggs 			mutex_unlock(&cli->mutex);
1442621a416SBen Skeggs 			goto done;
1452621a416SBen Skeggs 		}
146*148a8653SBen Skeggs 
147*148a8653SBen Skeggs 		abi16 = true;
1482621a416SBen Skeggs 	}
1492621a416SBen Skeggs 
15027111a23SBen Skeggs 	switch (argv->v0.type) {
15127111a23SBen Skeggs 	case NVIF_IOCTL_V0_NEW:
152*148a8653SBen Skeggs 		ret = usif_object_new(filp, data, size, argv, argc, abi16);
15327111a23SBen Skeggs 		break;
15427111a23SBen Skeggs 	default:
15527111a23SBen Skeggs 		ret = nvif_client_ioctl(client, argv, argc);
15627111a23SBen Skeggs 		break;
15727111a23SBen Skeggs 	}
15827111a23SBen Skeggs 	if (argv->v0.route == NVDRM_OBJECT_USIF) {
15927111a23SBen Skeggs 		object = (void *)(unsigned long)argv->v0.token;
16027111a23SBen Skeggs 		argv->v0.route = object->route;
16127111a23SBen Skeggs 		argv->v0.token = object->token;
16227111a23SBen Skeggs 		if (ret == 0 && argv->v0.type == NVIF_IOCTL_V0_DEL) {
16327111a23SBen Skeggs 			list_del(&object->head);
16427111a23SBen Skeggs 			kfree(object);
16527111a23SBen Skeggs 		}
16627111a23SBen Skeggs 	} else {
16727111a23SBen Skeggs 		argv->v0.route = NVIF_IOCTL_V0_ROUTE_HIDDEN;
16827111a23SBen Skeggs 		argv->v0.token = 0;
16927111a23SBen Skeggs 	}
17027111a23SBen Skeggs 	argv->v0.owner = owner;
17127111a23SBen Skeggs 	mutex_unlock(&cli->mutex);
17227111a23SBen Skeggs 
17327111a23SBen Skeggs 	if (copy_to_user(user, argv, argc))
17427111a23SBen Skeggs 		ret = -EFAULT;
17527111a23SBen Skeggs done:
17627111a23SBen Skeggs 	kfree(argv);
17727111a23SBen Skeggs 	return ret;
17827111a23SBen Skeggs }
17927111a23SBen Skeggs 
18027111a23SBen Skeggs void
usif_client_fini(struct nouveau_cli * cli)18127111a23SBen Skeggs usif_client_fini(struct nouveau_cli *cli)
18227111a23SBen Skeggs {
18327111a23SBen Skeggs 	struct usif_object *object, *otemp;
18427111a23SBen Skeggs 
18527111a23SBen Skeggs 	list_for_each_entry_safe(object, otemp, &cli->objects, head) {
18627111a23SBen Skeggs 		usif_object_dtor(object);
18727111a23SBen Skeggs 	}
18827111a23SBen Skeggs }
18927111a23SBen Skeggs 
19027111a23SBen Skeggs void
usif_client_init(struct nouveau_cli * cli)19127111a23SBen Skeggs usif_client_init(struct nouveau_cli *cli)
19227111a23SBen Skeggs {
19327111a23SBen Skeggs 	INIT_LIST_HEAD(&cli->objects);
19427111a23SBen Skeggs }
195