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/event.h>
28 
29 #include <nvif/unpack.h>
30 #include <nvif/ioctl.h>
31 
32 static int
33 nvkm_ioctl_nop(struct nvkm_client *client,
34 	       struct nvkm_object *object, void *data, u32 size)
35 {
36 	union {
37 		struct nvif_ioctl_nop_v0 v0;
38 	} *args = data;
39 	int ret = -ENOSYS;
40 
41 	nvif_ioctl(object, "nop size %d\n", size);
42 	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
43 		nvif_ioctl(object, "nop vers %lld\n", args->v0.version);
44 		args->v0.version = NVIF_VERSION_LATEST;
45 	}
46 
47 	return ret;
48 }
49 
50 static int
51 nvkm_ioctl_sclass(struct nvkm_client *client,
52 		  struct nvkm_object *object, void *data, u32 size)
53 {
54 	union {
55 		struct nvif_ioctl_sclass_v0 v0;
56 	} *args = data;
57 	struct nvkm_oclass oclass = { .client = client };
58 	int ret = -ENOSYS, i = 0;
59 
60 	nvif_ioctl(object, "sclass size %d\n", size);
61 	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
62 		nvif_ioctl(object, "sclass vers %d count %d\n",
63 			   args->v0.version, args->v0.count);
64 		if (size != args->v0.count * sizeof(args->v0.oclass[0]))
65 			return -EINVAL;
66 
67 		while (object->func->sclass &&
68 		       object->func->sclass(object, i, &oclass) >= 0) {
69 			if (i < args->v0.count) {
70 				args->v0.oclass[i].oclass = oclass.base.oclass;
71 				args->v0.oclass[i].minver = oclass.base.minver;
72 				args->v0.oclass[i].maxver = oclass.base.maxver;
73 			}
74 			i++;
75 		}
76 
77 		args->v0.count = i;
78 	}
79 
80 	return ret;
81 }
82 
83 static int
84 nvkm_ioctl_new(struct nvkm_client *client,
85 	       struct nvkm_object *parent, void *data, u32 size)
86 {
87 	union {
88 		struct nvif_ioctl_new_v0 v0;
89 	} *args = data;
90 	struct nvkm_object *object = NULL;
91 	struct nvkm_oclass oclass;
92 	int ret = -ENOSYS, i = 0;
93 
94 	nvif_ioctl(parent, "new size %d\n", size);
95 	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
96 		nvif_ioctl(parent, "new vers %d handle %08x class %08x "
97 				   "route %02x token %llx object %016llx\n",
98 			   args->v0.version, args->v0.handle, args->v0.oclass,
99 			   args->v0.route, args->v0.token, args->v0.object);
100 	} else
101 		return ret;
102 
103 	if (!parent->func->sclass) {
104 		nvif_ioctl(parent, "cannot have children\n");
105 		return -EINVAL;
106 	}
107 
108 	do {
109 		memset(&oclass, 0x00, sizeof(oclass));
110 		oclass.handle = args->v0.handle;
111 		oclass.route  = args->v0.route;
112 		oclass.token  = args->v0.token;
113 		oclass.object = args->v0.object;
114 		oclass.client = client;
115 		oclass.parent = parent;
116 		ret = parent->func->sclass(parent, i++, &oclass);
117 		if (ret)
118 			return ret;
119 	} while (oclass.base.oclass != args->v0.oclass);
120 
121 	if (oclass.engine) {
122 		oclass.engine = nvkm_engine_ref(oclass.engine);
123 		if (IS_ERR(oclass.engine))
124 			return PTR_ERR(oclass.engine);
125 	}
126 
127 	ret = oclass.ctor(&oclass, data, size, &object);
128 	nvkm_engine_unref(&oclass.engine);
129 	if (ret == 0) {
130 		ret = nvkm_object_init(object);
131 		if (ret == 0) {
132 			list_add_tail(&object->head, &parent->tree);
133 			if (nvkm_object_insert(object)) {
134 				client->data = object;
135 				return 0;
136 			}
137 			ret = -EEXIST;
138 		}
139 		nvkm_object_fini(object, false);
140 	}
141 
142 	nvkm_object_del(&object);
143 	return ret;
144 }
145 
146 static int
147 nvkm_ioctl_del(struct nvkm_client *client,
148 	       struct nvkm_object *object, void *data, u32 size)
149 {
150 	union {
151 		struct nvif_ioctl_del none;
152 	} *args = data;
153 	int ret = -ENOSYS;
154 
155 	nvif_ioctl(object, "delete size %d\n", size);
156 	if (!(ret = nvif_unvers(ret, &data, &size, args->none))) {
157 		nvif_ioctl(object, "delete\n");
158 		nvkm_object_fini(object, false);
159 		nvkm_object_del(&object);
160 	}
161 
162 	return ret ? ret : 1;
163 }
164 
165 static int
166 nvkm_ioctl_mthd(struct nvkm_client *client,
167 		struct nvkm_object *object, void *data, u32 size)
168 {
169 	union {
170 		struct nvif_ioctl_mthd_v0 v0;
171 	} *args = data;
172 	int ret = -ENOSYS;
173 
174 	nvif_ioctl(object, "mthd size %d\n", size);
175 	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
176 		nvif_ioctl(object, "mthd vers %d mthd %02x\n",
177 			   args->v0.version, args->v0.method);
178 		ret = nvkm_object_mthd(object, args->v0.method, data, size);
179 	}
180 
181 	return ret;
182 }
183 
184 
185 static int
186 nvkm_ioctl_rd(struct nvkm_client *client,
187 	      struct nvkm_object *object, void *data, u32 size)
188 {
189 	union {
190 		struct nvif_ioctl_rd_v0 v0;
191 	} *args = data;
192 	union {
193 		u8  b08;
194 		u16 b16;
195 		u32 b32;
196 	} v;
197 	int ret = -ENOSYS;
198 
199 	nvif_ioctl(object, "rd size %d\n", size);
200 	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
201 		nvif_ioctl(object, "rd vers %d size %d addr %016llx\n",
202 			   args->v0.version, args->v0.size, args->v0.addr);
203 		switch (args->v0.size) {
204 		case 1:
205 			ret = nvkm_object_rd08(object, args->v0.addr, &v.b08);
206 			args->v0.data = v.b08;
207 			break;
208 		case 2:
209 			ret = nvkm_object_rd16(object, args->v0.addr, &v.b16);
210 			args->v0.data = v.b16;
211 			break;
212 		case 4:
213 			ret = nvkm_object_rd32(object, args->v0.addr, &v.b32);
214 			args->v0.data = v.b32;
215 			break;
216 		default:
217 			ret = -EINVAL;
218 			break;
219 		}
220 	}
221 
222 	return ret;
223 }
224 
225 static int
226 nvkm_ioctl_wr(struct nvkm_client *client,
227 	      struct nvkm_object *object, void *data, u32 size)
228 {
229 	union {
230 		struct nvif_ioctl_wr_v0 v0;
231 	} *args = data;
232 	int ret = -ENOSYS;
233 
234 	nvif_ioctl(object, "wr size %d\n", size);
235 	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
236 		nvif_ioctl(object,
237 			   "wr vers %d size %d addr %016llx data %08x\n",
238 			   args->v0.version, args->v0.size, args->v0.addr,
239 			   args->v0.data);
240 	} else
241 		return ret;
242 
243 	switch (args->v0.size) {
244 	case 1: return nvkm_object_wr08(object, args->v0.addr, args->v0.data);
245 	case 2: return nvkm_object_wr16(object, args->v0.addr, args->v0.data);
246 	case 4: return nvkm_object_wr32(object, args->v0.addr, args->v0.data);
247 	default:
248 		break;
249 	}
250 
251 	return -EINVAL;
252 }
253 
254 static int
255 nvkm_ioctl_map(struct nvkm_client *client,
256 	       struct nvkm_object *object, void *data, u32 size)
257 {
258 	union {
259 		struct nvif_ioctl_map_v0 v0;
260 	} *args = data;
261 	enum nvkm_object_map type;
262 	int ret = -ENOSYS;
263 
264 	nvif_ioctl(object, "map size %d\n", size);
265 	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
266 		nvif_ioctl(object, "map vers %d\n", args->v0.version);
267 		ret = nvkm_object_map(object, data, size, &type,
268 				      &args->v0.handle,
269 				      &args->v0.length);
270 		if (type == NVKM_OBJECT_MAP_IO)
271 			args->v0.type = NVIF_IOCTL_MAP_V0_IO;
272 		else
273 			args->v0.type = NVIF_IOCTL_MAP_V0_VA;
274 	}
275 
276 	return ret;
277 }
278 
279 static int
280 nvkm_ioctl_unmap(struct nvkm_client *client,
281 		 struct nvkm_object *object, void *data, u32 size)
282 {
283 	union {
284 		struct nvif_ioctl_unmap none;
285 	} *args = data;
286 	int ret = -ENOSYS;
287 
288 	nvif_ioctl(object, "unmap size %d\n", size);
289 	if (!(ret = nvif_unvers(ret, &data, &size, args->none))) {
290 		nvif_ioctl(object, "unmap\n");
291 		ret = nvkm_object_unmap(object);
292 	}
293 
294 	return ret;
295 }
296 
297 static int
298 nvkm_ioctl_ntfy_new(struct nvkm_client *client,
299 		    struct nvkm_object *object, void *data, u32 size)
300 {
301 	union {
302 		struct nvif_ioctl_ntfy_new_v0 v0;
303 	} *args = data;
304 	struct nvkm_event *event;
305 	int ret = -ENOSYS;
306 
307 	nvif_ioctl(object, "ntfy new size %d\n", size);
308 	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
309 		nvif_ioctl(object, "ntfy new vers %d event %02x\n",
310 			   args->v0.version, args->v0.event);
311 		ret = nvkm_object_ntfy(object, args->v0.event, &event);
312 		if (ret == 0) {
313 			ret = nvkm_client_notify_new(object, event, data, size);
314 			if (ret >= 0) {
315 				args->v0.index = ret;
316 				ret = 0;
317 			}
318 		}
319 	}
320 
321 	return ret;
322 }
323 
324 static int
325 nvkm_ioctl_ntfy_del(struct nvkm_client *client,
326 		    struct nvkm_object *object, void *data, u32 size)
327 {
328 	union {
329 		struct nvif_ioctl_ntfy_del_v0 v0;
330 	} *args = data;
331 	int ret = -ENOSYS;
332 
333 	nvif_ioctl(object, "ntfy del size %d\n", size);
334 	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
335 		nvif_ioctl(object, "ntfy del vers %d index %d\n",
336 			   args->v0.version, args->v0.index);
337 		ret = nvkm_client_notify_del(client, args->v0.index);
338 	}
339 
340 	return ret;
341 }
342 
343 static int
344 nvkm_ioctl_ntfy_get(struct nvkm_client *client,
345 		    struct nvkm_object *object, void *data, u32 size)
346 {
347 	union {
348 		struct nvif_ioctl_ntfy_get_v0 v0;
349 	} *args = data;
350 	int ret = -ENOSYS;
351 
352 	nvif_ioctl(object, "ntfy get size %d\n", size);
353 	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
354 		nvif_ioctl(object, "ntfy get vers %d index %d\n",
355 			   args->v0.version, args->v0.index);
356 		ret = nvkm_client_notify_get(client, args->v0.index);
357 	}
358 
359 	return ret;
360 }
361 
362 static int
363 nvkm_ioctl_ntfy_put(struct nvkm_client *client,
364 		    struct nvkm_object *object, void *data, u32 size)
365 {
366 	union {
367 		struct nvif_ioctl_ntfy_put_v0 v0;
368 	} *args = data;
369 	int ret = -ENOSYS;
370 
371 	nvif_ioctl(object, "ntfy put size %d\n", size);
372 	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
373 		nvif_ioctl(object, "ntfy put vers %d index %d\n",
374 			   args->v0.version, args->v0.index);
375 		ret = nvkm_client_notify_put(client, args->v0.index);
376 	}
377 
378 	return ret;
379 }
380 
381 static struct {
382 	int version;
383 	int (*func)(struct nvkm_client *, struct nvkm_object *, void *, u32);
384 }
385 nvkm_ioctl_v0[] = {
386 	{ 0x00, nvkm_ioctl_nop },
387 	{ 0x00, nvkm_ioctl_sclass },
388 	{ 0x00, nvkm_ioctl_new },
389 	{ 0x00, nvkm_ioctl_del },
390 	{ 0x00, nvkm_ioctl_mthd },
391 	{ 0x00, nvkm_ioctl_rd },
392 	{ 0x00, nvkm_ioctl_wr },
393 	{ 0x00, nvkm_ioctl_map },
394 	{ 0x00, nvkm_ioctl_unmap },
395 	{ 0x00, nvkm_ioctl_ntfy_new },
396 	{ 0x00, nvkm_ioctl_ntfy_del },
397 	{ 0x00, nvkm_ioctl_ntfy_get },
398 	{ 0x00, nvkm_ioctl_ntfy_put },
399 };
400 
401 static int
402 nvkm_ioctl_path(struct nvkm_client *client, u64 handle, u32 type,
403 		void *data, u32 size, u8 owner, u8 *route, u64 *token)
404 {
405 	struct nvkm_object *object;
406 	int ret;
407 
408 	object = nvkm_object_search(client, handle, NULL);
409 	if (IS_ERR(object)) {
410 		nvif_ioctl(&client->object, "object not found\n");
411 		return PTR_ERR(object);
412 	}
413 
414 	if (owner != NVIF_IOCTL_V0_OWNER_ANY && owner != object->route) {
415 		nvif_ioctl(&client->object, "route != owner\n");
416 		return -EACCES;
417 	}
418 	*route = object->route;
419 	*token = object->token;
420 
421 	if (ret = -EINVAL, type < ARRAY_SIZE(nvkm_ioctl_v0)) {
422 		if (nvkm_ioctl_v0[type].version == 0)
423 			ret = nvkm_ioctl_v0[type].func(client, object, data, size);
424 	}
425 
426 	return ret;
427 }
428 
429 int
430 nvkm_ioctl(struct nvkm_client *client, void *data, u32 size, void **hack)
431 {
432 	struct nvkm_object *object = &client->object;
433 	union {
434 		struct nvif_ioctl_v0 v0;
435 	} *args = data;
436 	int ret = -ENOSYS;
437 
438 	nvif_ioctl(object, "size %d\n", size);
439 
440 	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
441 		nvif_ioctl(object,
442 			   "vers %d type %02x object %016llx owner %02x\n",
443 			   args->v0.version, args->v0.type, args->v0.object,
444 			   args->v0.owner);
445 		ret = nvkm_ioctl_path(client, args->v0.object, args->v0.type,
446 				      data, size, args->v0.owner,
447 				      &args->v0.route, &args->v0.token);
448 	}
449 
450 	if (ret != 1) {
451 		nvif_ioctl(object, "return %d\n", ret);
452 		if (hack) {
453 			*hack = client->data;
454 			client->data = NULL;
455 		}
456 	}
457 
458 	return ret;
459 }
460