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
nvkm_ioctl_nop(struct nvkm_client * client,struct nvkm_object * object,void * data,u32 size)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
nvkm_ioctl_sclass(struct nvkm_client * client,struct nvkm_object * object,void * data,u32 size)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
nvkm_ioctl_new(struct nvkm_client * client,struct nvkm_object * parent,void * data,u32 size)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
nvkm_ioctl_del(struct nvkm_client * client,struct nvkm_object * object,void * data,u32 size)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
nvkm_ioctl_mthd(struct nvkm_client * client,struct nvkm_object * object,void * data,u32 size)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
nvkm_ioctl_rd(struct nvkm_client * client,struct nvkm_object * object,void * data,u32 size)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
nvkm_ioctl_wr(struct nvkm_client * client,struct nvkm_object * object,void * data,u32 size)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
nvkm_ioctl_map(struct nvkm_client * client,struct nvkm_object * object,void * data,u32 size)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
nvkm_ioctl_unmap(struct nvkm_client * client,struct nvkm_object * object,void * data,u32 size)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
nvkm_ioctl_ntfy_new(struct nvkm_client * client,struct nvkm_object * object,void * data,u32 size)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
nvkm_ioctl_ntfy_del(struct nvkm_client * client,struct nvkm_object * object,void * data,u32 size)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
nvkm_ioctl_ntfy_get(struct nvkm_client * client,struct nvkm_object * object,void * data,u32 size)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
nvkm_ioctl_ntfy_put(struct nvkm_client * client,struct nvkm_object * object,void * data,u32 size)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
nvkm_ioctl_path(struct nvkm_client * client,u64 handle,u32 type,void * data,u32 size,u8 owner,u8 * route,u64 * token)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
nvkm_ioctl(struct nvkm_client * client,void * data,u32 size,void ** hack)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