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#include <core/ioctl.h>
25#include <core/client.h>
26#include <core/engine.h>
27#include <core/handle.h>
28#include <core/namedb.h>
29
30#include <nvif/unpack.h>
31#include <nvif/ioctl.h>
32
33static int
34nvkm_ioctl_nop(struct nvkm_handle *handle, void *data, u32 size)
35{
36	struct nvkm_object *object = handle->object;
37	union {
38		struct nvif_ioctl_nop none;
39	} *args = data;
40	int ret;
41
42	nv_ioctl(object, "nop size %d\n", size);
43	if (nvif_unvers(args->none)) {
44		nv_ioctl(object, "nop\n");
45	}
46
47	return ret;
48}
49
50static int
51nvkm_ioctl_sclass(struct nvkm_handle *handle, void *data, u32 size)
52{
53	struct nvkm_object *object = handle->object;
54	union {
55		struct nvif_ioctl_sclass_v0 v0;
56	} *args = data;
57	int ret;
58
59	if (!nv_iclass(object, NV_PARENT_CLASS)) {
60		nv_debug(object, "cannot have children (sclass)\n");
61		return -ENODEV;
62	}
63
64	nv_ioctl(object, "sclass size %d\n", size);
65	if (nvif_unpack(args->v0, 0, 0, true)) {
66		nv_ioctl(object, "sclass vers %d count %d\n",
67			 args->v0.version, args->v0.count);
68		if (size == args->v0.count * sizeof(args->v0.oclass[0])) {
69			ret = nvkm_parent_lclass(object, args->v0.oclass,
70							 args->v0.count);
71			if (ret >= 0) {
72				args->v0.count = ret;
73				ret = 0;
74			}
75		} else {
76			ret = -EINVAL;
77		}
78	}
79
80	return ret;
81}
82
83static int
84nvkm_ioctl_new(struct nvkm_handle *handle, void *data, u32 size)
85{
86	union {
87		struct nvif_ioctl_new_v0 v0;
88	} *args = data;
89	struct nvkm_client *client = nvkm_client(handle->object);
90	struct nvkm_object *engctx = NULL;
91	struct nvkm_object *object = NULL;
92	struct nvkm_parent *parent;
93	struct nvkm_object *engine;
94	struct nvkm_oclass *oclass;
95	u32 _handle, _oclass;
96	int ret;
97
98	nv_ioctl(client, "new size %d\n", size);
99	if (nvif_unpack(args->v0, 0, 0, true)) {
100		_handle = args->v0.handle;
101		_oclass = args->v0.oclass;
102	} else
103		return ret;
104
105	nv_ioctl(client, "new vers %d handle %08x class %08x "
106			 "route %02x token %llx\n",
107		 args->v0.version, _handle, _oclass,
108		 args->v0.route, args->v0.token);
109
110	if (!nv_iclass(handle->object, NV_PARENT_CLASS)) {
111		nv_debug(handle->object, "cannot have children (ctor)\n");
112		ret = -ENODEV;
113		goto fail_class;
114	}
115
116	parent = nv_parent(handle->object);
117
118	/* check that parent supports the requested subclass */
119	ret = nvkm_parent_sclass(&parent->object, _oclass, &engine, &oclass);
120	if (ret) {
121		nv_debug(parent, "illegal class 0x%04x\n", _oclass);
122		goto fail_class;
123	}
124
125	/* make sure engine init has been completed *before* any objects
126	 * it controls are created - the constructors may depend on
127	 * state calculated at init (ie. default context construction)
128	 */
129	if (engine) {
130		ret = nvkm_object_inc(engine);
131		if (ret)
132			goto fail_class;
133	}
134
135	/* if engine requires it, create a context object to insert
136	 * between the parent and its children (eg. PGRAPH context)
137	 */
138	if (engine && nv_engine(engine)->cclass) {
139		ret = nvkm_object_ctor(&parent->object, engine,
140				       nv_engine(engine)->cclass,
141				       data, size, &engctx);
142		if (ret)
143			goto fail_engctx;
144	} else {
145		nvkm_object_ref(&parent->object, &engctx);
146	}
147
148	/* finally, create new object and bind it to its handle */
149	ret = nvkm_object_ctor(engctx, engine, oclass, data, size, &object);
150	client->data = object;
151	if (ret)
152		goto fail_ctor;
153
154	ret = nvkm_object_inc(object);
155	if (ret)
156		goto fail_init;
157
158	ret = nvkm_handle_create(&parent->object, handle->name,
159				 _handle, object, &handle);
160	if (ret)
161		goto fail_handle;
162
163	ret = nvkm_handle_init(handle);
164	handle->route = args->v0.route;
165	handle->token = args->v0.token;
166	if (ret)
167		nvkm_handle_destroy(handle);
168
169fail_handle:
170	nvkm_object_dec(object, false);
171fail_init:
172	nvkm_object_ref(NULL, &object);
173fail_ctor:
174	nvkm_object_ref(NULL, &engctx);
175fail_engctx:
176	if (engine)
177		nvkm_object_dec(engine, false);
178fail_class:
179	return ret;
180}
181
182static int
183nvkm_ioctl_del(struct nvkm_handle *handle, void *data, u32 size)
184{
185	struct nvkm_object *object = handle->object;
186	union {
187		struct nvif_ioctl_del none;
188	} *args = data;
189	int ret;
190
191	nv_ioctl(object, "delete size %d\n", size);
192	if (nvif_unvers(args->none)) {
193		nv_ioctl(object, "delete\n");
194		nvkm_handle_fini(handle, false);
195		nvkm_handle_destroy(handle);
196	}
197
198	return ret;
199}
200
201static int
202nvkm_ioctl_mthd(struct nvkm_handle *handle, void *data, u32 size)
203{
204	struct nvkm_object *object = handle->object;
205	struct nvkm_ofuncs *ofuncs = object->oclass->ofuncs;
206	union {
207		struct nvif_ioctl_mthd_v0 v0;
208	} *args = data;
209	int ret;
210
211	nv_ioctl(object, "mthd size %d\n", size);
212	if (nvif_unpack(args->v0, 0, 0, true)) {
213		nv_ioctl(object, "mthd vers %d mthd %02x\n",
214			 args->v0.version, args->v0.method);
215		if (ret = -ENODEV, ofuncs->mthd)
216			ret = ofuncs->mthd(object, args->v0.method, data, size);
217	}
218
219	return ret;
220}
221
222
223static int
224nvkm_ioctl_rd(struct nvkm_handle *handle, void *data, u32 size)
225{
226	struct nvkm_object *object = handle->object;
227	struct nvkm_ofuncs *ofuncs = object->oclass->ofuncs;
228	union {
229		struct nvif_ioctl_rd_v0 v0;
230	} *args = data;
231	int ret;
232
233	nv_ioctl(object, "rd size %d\n", size);
234	if (nvif_unpack(args->v0, 0, 0, false)) {
235		nv_ioctl(object, "rd vers %d size %d addr %016llx\n",
236			 args->v0.version, args->v0.size, args->v0.addr);
237		switch (args->v0.size) {
238		case 1:
239			if (ret = -ENODEV, ofuncs->rd08) {
240				args->v0.data = nv_ro08(object, args->v0.addr);
241				ret = 0;
242			}
243			break;
244		case 2:
245			if (ret = -ENODEV, ofuncs->rd16) {
246				args->v0.data = nv_ro16(object, args->v0.addr);
247				ret = 0;
248			}
249			break;
250		case 4:
251			if (ret = -ENODEV, ofuncs->rd32) {
252				args->v0.data = nv_ro32(object, args->v0.addr);
253				ret = 0;
254			}
255			break;
256		default:
257			ret = -EINVAL;
258			break;
259		}
260	}
261
262	return ret;
263}
264
265static int
266nvkm_ioctl_wr(struct nvkm_handle *handle, void *data, u32 size)
267{
268	struct nvkm_object *object = handle->object;
269	struct nvkm_ofuncs *ofuncs = object->oclass->ofuncs;
270	union {
271		struct nvif_ioctl_wr_v0 v0;
272	} *args = data;
273	int ret;
274
275	nv_ioctl(object, "wr size %d\n", size);
276	if (nvif_unpack(args->v0, 0, 0, false)) {
277		nv_ioctl(object, "wr vers %d size %d addr %016llx data %08x\n",
278			 args->v0.version, args->v0.size, args->v0.addr,
279			 args->v0.data);
280		switch (args->v0.size) {
281		case 1:
282			if (ret = -ENODEV, ofuncs->wr08) {
283				nv_wo08(object, args->v0.addr, args->v0.data);
284				ret = 0;
285			}
286			break;
287		case 2:
288			if (ret = -ENODEV, ofuncs->wr16) {
289				nv_wo16(object, args->v0.addr, args->v0.data);
290				ret = 0;
291			}
292			break;
293		case 4:
294			if (ret = -ENODEV, ofuncs->wr32) {
295				nv_wo32(object, args->v0.addr, args->v0.data);
296				ret = 0;
297			}
298			break;
299		default:
300			ret = -EINVAL;
301			break;
302		}
303	}
304
305	return ret;
306}
307
308static int
309nvkm_ioctl_map(struct nvkm_handle *handle, void *data, u32 size)
310{
311	struct nvkm_object *object = handle->object;
312	struct nvkm_ofuncs *ofuncs = object->oclass->ofuncs;
313	union {
314		struct nvif_ioctl_map_v0 v0;
315	} *args = data;
316	int ret;
317
318	nv_ioctl(object, "map size %d\n", size);
319	if (nvif_unpack(args->v0, 0, 0, false)) {
320		nv_ioctl(object, "map vers %d\n", args->v0.version);
321		if (ret = -ENODEV, ofuncs->map) {
322			ret = ofuncs->map(object, &args->v0.handle,
323						  &args->v0.length);
324		}
325	}
326
327	return ret;
328}
329
330static int
331nvkm_ioctl_unmap(struct nvkm_handle *handle, void *data, u32 size)
332{
333	struct nvkm_object *object = handle->object;
334	union {
335		struct nvif_ioctl_unmap none;
336	} *args = data;
337	int ret;
338
339	nv_ioctl(object, "unmap size %d\n", size);
340	if (nvif_unvers(args->none)) {
341		nv_ioctl(object, "unmap\n");
342	}
343
344	return ret;
345}
346
347static int
348nvkm_ioctl_ntfy_new(struct nvkm_handle *handle, void *data, u32 size)
349{
350	struct nvkm_object *object = handle->object;
351	struct nvkm_ofuncs *ofuncs = object->oclass->ofuncs;
352	union {
353		struct nvif_ioctl_ntfy_new_v0 v0;
354	} *args = data;
355	struct nvkm_event *event;
356	int ret;
357
358	nv_ioctl(object, "ntfy new size %d\n", size);
359	if (nvif_unpack(args->v0, 0, 0, true)) {
360		nv_ioctl(object, "ntfy new vers %d event %02x\n",
361			 args->v0.version, args->v0.event);
362		if (ret = -ENODEV, ofuncs->ntfy)
363			ret = ofuncs->ntfy(object, args->v0.event, &event);
364		if (ret == 0) {
365			ret = nvkm_client_notify_new(object, event, data, size);
366			if (ret >= 0) {
367				args->v0.index = ret;
368				ret = 0;
369			}
370		}
371	}
372
373	return ret;
374}
375
376static int
377nvkm_ioctl_ntfy_del(struct nvkm_handle *handle, void *data, u32 size)
378{
379	struct nvkm_client *client = nvkm_client(handle->object);
380	struct nvkm_object *object = handle->object;
381	union {
382		struct nvif_ioctl_ntfy_del_v0 v0;
383	} *args = data;
384	int ret;
385
386	nv_ioctl(object, "ntfy del size %d\n", size);
387	if (nvif_unpack(args->v0, 0, 0, false)) {
388		nv_ioctl(object, "ntfy del vers %d index %d\n",
389			 args->v0.version, args->v0.index);
390		ret = nvkm_client_notify_del(client, args->v0.index);
391	}
392
393	return ret;
394}
395
396static int
397nvkm_ioctl_ntfy_get(struct nvkm_handle *handle, void *data, u32 size)
398{
399	struct nvkm_client *client = nvkm_client(handle->object);
400	struct nvkm_object *object = handle->object;
401	union {
402		struct nvif_ioctl_ntfy_get_v0 v0;
403	} *args = data;
404	int ret;
405
406	nv_ioctl(object, "ntfy get size %d\n", size);
407	if (nvif_unpack(args->v0, 0, 0, false)) {
408		nv_ioctl(object, "ntfy get vers %d index %d\n",
409			 args->v0.version, args->v0.index);
410		ret = nvkm_client_notify_get(client, args->v0.index);
411	}
412
413	return ret;
414}
415
416static int
417nvkm_ioctl_ntfy_put(struct nvkm_handle *handle, void *data, u32 size)
418{
419	struct nvkm_client *client = nvkm_client(handle->object);
420	struct nvkm_object *object = handle->object;
421	union {
422		struct nvif_ioctl_ntfy_put_v0 v0;
423	} *args = data;
424	int ret;
425
426	nv_ioctl(object, "ntfy put size %d\n", size);
427	if (nvif_unpack(args->v0, 0, 0, false)) {
428		nv_ioctl(object, "ntfy put vers %d index %d\n",
429			 args->v0.version, args->v0.index);
430		ret = nvkm_client_notify_put(client, args->v0.index);
431	}
432
433	return ret;
434}
435
436static struct {
437	int version;
438	int (*func)(struct nvkm_handle *, void *, u32);
439}
440nvkm_ioctl_v0[] = {
441	{ 0x00, nvkm_ioctl_nop },
442	{ 0x00, nvkm_ioctl_sclass },
443	{ 0x00, nvkm_ioctl_new },
444	{ 0x00, nvkm_ioctl_del },
445	{ 0x00, nvkm_ioctl_mthd },
446	{ 0x00, nvkm_ioctl_rd },
447	{ 0x00, nvkm_ioctl_wr },
448	{ 0x00, nvkm_ioctl_map },
449	{ 0x00, nvkm_ioctl_unmap },
450	{ 0x00, nvkm_ioctl_ntfy_new },
451	{ 0x00, nvkm_ioctl_ntfy_del },
452	{ 0x00, nvkm_ioctl_ntfy_get },
453	{ 0x00, nvkm_ioctl_ntfy_put },
454};
455
456static int
457nvkm_ioctl_path(struct nvkm_handle *parent, u32 type, u32 nr, u32 *path,
458		void *data, u32 size, u8 owner, u8 *route, u64 *token)
459{
460	struct nvkm_handle *handle = parent;
461	struct nvkm_namedb *namedb;
462	struct nvkm_object *object;
463	int ret;
464
465	while ((object = parent->object), nr--) {
466		nv_ioctl(object, "path 0x%08x\n", path[nr]);
467		if (!nv_iclass(object, NV_PARENT_CLASS)) {
468			nv_debug(object, "cannot have children (path)\n");
469			return -EINVAL;
470		}
471
472		if (!(namedb = (void *)nv_pclass(object, NV_NAMEDB_CLASS)) ||
473		    !(handle = nvkm_namedb_get(namedb, path[nr]))) {
474			nv_debug(object, "handle 0x%08x not found\n", path[nr]);
475			return -ENOENT;
476		}
477		nvkm_namedb_put(handle);
478		parent = handle;
479	}
480
481	if (owner != NVIF_IOCTL_V0_OWNER_ANY && owner != handle->route) {
482		nv_ioctl(object, "object route != owner\n");
483		return -EACCES;
484	}
485	*route = handle->route;
486	*token = handle->token;
487
488	if (ret = -EINVAL, type < ARRAY_SIZE(nvkm_ioctl_v0)) {
489		if (nvkm_ioctl_v0[type].version == 0)
490			ret = nvkm_ioctl_v0[type].func(handle, data, size);
491	}
492
493	return ret;
494}
495
496int
497nvkm_ioctl(struct nvkm_client *client, bool supervisor,
498	   void *data, u32 size, void **hack)
499{
500	union {
501		struct nvif_ioctl_v0 v0;
502	} *args = data;
503	int ret;
504
505	client->super = supervisor;
506	nv_ioctl(client, "size %d\n", size);
507
508	if (nvif_unpack(args->v0, 0, 0, true)) {
509		nv_ioctl(client, "vers %d type %02x path %d owner %02x\n",
510			 args->v0.version, args->v0.type, args->v0.path_nr,
511			 args->v0.owner);
512		ret = nvkm_ioctl_path(client->root, args->v0.type,
513				      args->v0.path_nr, args->v0.path,
514				      data, size, args->v0.owner,
515				      &args->v0.route, &args->v0.token);
516	}
517
518	nv_ioctl(client, "return %d\n", ret);
519	if (hack) {
520		*hack = client->data;
521		client->data = NULL;
522	}
523
524	client->super = false;
525	return ret;
526}
527