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