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 31 static int 32 nvkm_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 49 static int 50 nvkm_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; 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 82 static int 83 nvkm_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 145 static int 146 nvkm_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 164 static int 165 nvkm_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 184 static int 185 nvkm_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 224 static int 225 nvkm_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 253 static int 254 nvkm_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 int ret = -ENOSYS; 261 262 nvif_ioctl(object, "map size %d\n", size); 263 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { 264 nvif_ioctl(object, "map vers %d\n", args->v0.version); 265 ret = nvkm_object_map(object, &args->v0.handle, 266 &args->v0.length); 267 } 268 269 return ret; 270 } 271 272 static int 273 nvkm_ioctl_unmap(struct nvkm_client *client, 274 struct nvkm_object *object, void *data, u32 size) 275 { 276 union { 277 struct nvif_ioctl_unmap none; 278 } *args = data; 279 int ret = -ENOSYS; 280 281 nvif_ioctl(object, "unmap size %d\n", size); 282 if (!(ret = nvif_unvers(ret, &data, &size, args->none))) { 283 nvif_ioctl(object, "unmap\n"); 284 } 285 286 return ret; 287 } 288 289 static int 290 nvkm_ioctl_ntfy_new(struct nvkm_client *client, 291 struct nvkm_object *object, void *data, u32 size) 292 { 293 union { 294 struct nvif_ioctl_ntfy_new_v0 v0; 295 } *args = data; 296 struct nvkm_event *event; 297 int ret = -ENOSYS; 298 299 nvif_ioctl(object, "ntfy new size %d\n", size); 300 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { 301 nvif_ioctl(object, "ntfy new vers %d event %02x\n", 302 args->v0.version, args->v0.event); 303 ret = nvkm_object_ntfy(object, args->v0.event, &event); 304 if (ret == 0) { 305 ret = nvkm_client_notify_new(object, event, data, size); 306 if (ret >= 0) { 307 args->v0.index = ret; 308 ret = 0; 309 } 310 } 311 } 312 313 return ret; 314 } 315 316 static int 317 nvkm_ioctl_ntfy_del(struct nvkm_client *client, 318 struct nvkm_object *object, void *data, u32 size) 319 { 320 union { 321 struct nvif_ioctl_ntfy_del_v0 v0; 322 } *args = data; 323 int ret = -ENOSYS; 324 325 nvif_ioctl(object, "ntfy del size %d\n", size); 326 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { 327 nvif_ioctl(object, "ntfy del vers %d index %d\n", 328 args->v0.version, args->v0.index); 329 ret = nvkm_client_notify_del(client, args->v0.index); 330 } 331 332 return ret; 333 } 334 335 static int 336 nvkm_ioctl_ntfy_get(struct nvkm_client *client, 337 struct nvkm_object *object, void *data, u32 size) 338 { 339 union { 340 struct nvif_ioctl_ntfy_get_v0 v0; 341 } *args = data; 342 int ret = -ENOSYS; 343 344 nvif_ioctl(object, "ntfy get size %d\n", size); 345 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { 346 nvif_ioctl(object, "ntfy get vers %d index %d\n", 347 args->v0.version, args->v0.index); 348 ret = nvkm_client_notify_get(client, args->v0.index); 349 } 350 351 return ret; 352 } 353 354 static int 355 nvkm_ioctl_ntfy_put(struct nvkm_client *client, 356 struct nvkm_object *object, void *data, u32 size) 357 { 358 union { 359 struct nvif_ioctl_ntfy_put_v0 v0; 360 } *args = data; 361 int ret = -ENOSYS; 362 363 nvif_ioctl(object, "ntfy put size %d\n", size); 364 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) { 365 nvif_ioctl(object, "ntfy put vers %d index %d\n", 366 args->v0.version, args->v0.index); 367 ret = nvkm_client_notify_put(client, args->v0.index); 368 } 369 370 return ret; 371 } 372 373 static struct { 374 int version; 375 int (*func)(struct nvkm_client *, struct nvkm_object *, void *, u32); 376 } 377 nvkm_ioctl_v0[] = { 378 { 0x00, nvkm_ioctl_nop }, 379 { 0x00, nvkm_ioctl_sclass }, 380 { 0x00, nvkm_ioctl_new }, 381 { 0x00, nvkm_ioctl_del }, 382 { 0x00, nvkm_ioctl_mthd }, 383 { 0x00, nvkm_ioctl_rd }, 384 { 0x00, nvkm_ioctl_wr }, 385 { 0x00, nvkm_ioctl_map }, 386 { 0x00, nvkm_ioctl_unmap }, 387 { 0x00, nvkm_ioctl_ntfy_new }, 388 { 0x00, nvkm_ioctl_ntfy_del }, 389 { 0x00, nvkm_ioctl_ntfy_get }, 390 { 0x00, nvkm_ioctl_ntfy_put }, 391 }; 392 393 static int 394 nvkm_ioctl_path(struct nvkm_client *client, u64 handle, u32 type, 395 void *data, u32 size, u8 owner, u8 *route, u64 *token) 396 { 397 struct nvkm_object *object; 398 int ret; 399 400 object = nvkm_object_search(client, handle, NULL); 401 if (IS_ERR(object)) { 402 nvif_ioctl(&client->object, "object not found\n"); 403 return PTR_ERR(object); 404 } 405 406 if (owner != NVIF_IOCTL_V0_OWNER_ANY && owner != object->route) { 407 nvif_ioctl(&client->object, "route != owner\n"); 408 return -EACCES; 409 } 410 *route = object->route; 411 *token = object->token; 412 413 if (ret = -EINVAL, type < ARRAY_SIZE(nvkm_ioctl_v0)) { 414 if (nvkm_ioctl_v0[type].version == 0) 415 ret = nvkm_ioctl_v0[type].func(client, object, data, size); 416 } 417 418 return ret; 419 } 420 421 int 422 nvkm_ioctl(struct nvkm_client *client, bool supervisor, 423 void *data, u32 size, void **hack) 424 { 425 struct nvkm_object *object = &client->object; 426 union { 427 struct nvif_ioctl_v0 v0; 428 } *args = data; 429 int ret = -ENOSYS; 430 431 client->super = supervisor; 432 nvif_ioctl(object, "size %d\n", size); 433 434 if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { 435 nvif_ioctl(object, 436 "vers %d type %02x object %016llx owner %02x\n", 437 args->v0.version, args->v0.type, args->v0.object, 438 args->v0.owner); 439 ret = nvkm_ioctl_path(client, args->v0.object, args->v0.type, 440 data, size, args->v0.owner, 441 &args->v0.route, &args->v0.token); 442 } 443 444 if (ret != 1) { 445 nvif_ioctl(object, "return %d\n", ret); 446 if (hack) { 447 *hack = client->data; 448 client->data = NULL; 449 } 450 } 451 452 return ret; 453 } 454