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