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 
33 static int
nvkm_ioctl_nop(struct nvkm_handle * handle,void * data,u32 size)34 nvkm_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 
50 static int
nvkm_ioctl_sclass(struct nvkm_handle * handle,void * data,u32 size)51 nvkm_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 
83 static int
nvkm_ioctl_new(struct nvkm_handle * handle,void * data,u32 size)84 nvkm_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 
169 fail_handle:
170 	nvkm_object_dec(object, false);
171 fail_init:
172 	nvkm_object_ref(NULL, &object);
173 fail_ctor:
174 	nvkm_object_ref(NULL, &engctx);
175 fail_engctx:
176 	if (engine)
177 		nvkm_object_dec(engine, false);
178 fail_class:
179 	return ret;
180 }
181 
182 static int
nvkm_ioctl_del(struct nvkm_handle * handle,void * data,u32 size)183 nvkm_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 
201 static int
nvkm_ioctl_mthd(struct nvkm_handle * handle,void * data,u32 size)202 nvkm_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 
223 static int
nvkm_ioctl_rd(struct nvkm_handle * handle,void * data,u32 size)224 nvkm_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 
265 static int
nvkm_ioctl_wr(struct nvkm_handle * handle,void * data,u32 size)266 nvkm_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 
308 static int
nvkm_ioctl_map(struct nvkm_handle * handle,void * data,u32 size)309 nvkm_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 
330 static int
nvkm_ioctl_unmap(struct nvkm_handle * handle,void * data,u32 size)331 nvkm_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 
347 static int
nvkm_ioctl_ntfy_new(struct nvkm_handle * handle,void * data,u32 size)348 nvkm_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 
376 static int
nvkm_ioctl_ntfy_del(struct nvkm_handle * handle,void * data,u32 size)377 nvkm_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 
396 static int
nvkm_ioctl_ntfy_get(struct nvkm_handle * handle,void * data,u32 size)397 nvkm_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 
416 static int
nvkm_ioctl_ntfy_put(struct nvkm_handle * handle,void * data,u32 size)417 nvkm_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 
436 static struct {
437 	int version;
438 	int (*func)(struct nvkm_handle *, void *, u32);
439 }
440 nvkm_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 
456 static int
nvkm_ioctl_path(struct nvkm_handle * parent,u32 type,u32 nr,u32 * path,void * data,u32 size,u8 owner,u8 * route,u64 * token)457 nvkm_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 
496 int
nvkm_ioctl(struct nvkm_client * client,bool supervisor,void * data,u32 size,void ** hack)497 nvkm_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