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