Home | History | Annotate | Line # | Download | only in core
      1 /*	$NetBSD: nouveau_nvkm_core_ioctl.c,v 1.7 2021/12/19 12:40:21 riastradh Exp $	*/
      2 
      3 /*
      4  * Copyright 2014 Red Hat Inc.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the "Software"),
      8  * to deal in the Software without restriction, including without limitation
      9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     10  * and/or sell copies of the Software, and to permit persons to whom the
     11  * Software is furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be included in
     14  * all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     19  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
     20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     22  * OTHER DEALINGS IN THE SOFTWARE.
     23  *
     24  * Authors: Ben Skeggs <bskeggs (at) redhat.com>
     25  */
     26 #include <sys/cdefs.h>
     27 __KERNEL_RCSID(0, "$NetBSD: nouveau_nvkm_core_ioctl.c,v 1.7 2021/12/19 12:40:21 riastradh Exp $");
     28 
     29 #include <core/ioctl.h>
     30 #include <core/client.h>
     31 #include <core/engine.h>
     32 
     33 #include <nvif/unpack.h>
     34 #include <nvif/ioctl.h>
     35 
     36 static int
     37 nvkm_ioctl_nop(struct nvkm_client *client,
     38 	       struct nvkm_object *object, void *data, u32 size)
     39 {
     40 	union {
     41 		struct nvif_ioctl_nop_v0 v0;
     42 	} *args = data;
     43 	int ret = -ENOSYS;
     44 
     45 	nvif_ioctl(object, "nop size %d\n", size);
     46 	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
     47 		nvif_ioctl(object, "nop vers %"PRId64"\n", args->v0.version);
     48 		args->v0.version = NVIF_VERSION_LATEST;
     49 	}
     50 
     51 	return ret;
     52 }
     53 
     54 static int
     55 nvkm_ioctl_sclass(struct nvkm_client *client,
     56 		  struct nvkm_object *object, void *data, u32 size)
     57 {
     58 	union {
     59 		struct nvif_ioctl_sclass_v0 v0;
     60 	} *args = data;
     61 	struct nvkm_oclass oclass = { .client = client };
     62 	int ret = -ENOSYS, i = 0;
     63 
     64 	nvif_ioctl(object, "sclass size %d\n", size);
     65 	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
     66 		nvif_ioctl(object, "sclass vers %d count %d\n",
     67 			   args->v0.version, args->v0.count);
     68 		if (size != args->v0.count * sizeof(args->v0.oclass[0]))
     69 			return -EINVAL;
     70 
     71 		while (object->func->sclass &&
     72 		       object->func->sclass(object, i, &oclass) >= 0) {
     73 			if (i < args->v0.count) {
     74 				args->v0.oclass[i].oclass = oclass.base.oclass;
     75 				args->v0.oclass[i].minver = oclass.base.minver;
     76 				args->v0.oclass[i].maxver = oclass.base.maxver;
     77 			}
     78 			i++;
     79 		}
     80 
     81 		args->v0.count = i;
     82 	}
     83 
     84 	return ret;
     85 }
     86 
     87 static int
     88 nvkm_ioctl_new(struct nvkm_client *client,
     89 	       struct nvkm_object *parent, void *data, u32 size)
     90 {
     91 	union {
     92 		struct nvif_ioctl_new_v0 v0;
     93 	} *args = data;
     94 	struct nvkm_object *object = NULL;
     95 	struct nvkm_oclass oclass;
     96 	int ret = -ENOSYS, i = 0;
     97 
     98 	nvif_ioctl(parent, "new size %d\n", size);
     99 	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
    100 		nvif_ioctl(parent, "new vers %d handle %08x class %08x "
    101 				   "route %02x token %"PRIx64" object %016"PRIx64"\n",
    102 			   args->v0.version, args->v0.handle, args->v0.oclass,
    103 			   args->v0.route, args->v0.token, args->v0.object);
    104 	} else
    105 		return ret;
    106 
    107 	if (!parent->func->sclass) {
    108 		nvif_ioctl(parent, "cannot have children\n");
    109 		return -EINVAL;
    110 	}
    111 
    112 	do {
    113 		memset(&oclass, 0x00, sizeof(oclass));
    114 		oclass.handle = args->v0.handle;
    115 		oclass.route  = args->v0.route;
    116 		oclass.token  = args->v0.token;
    117 		oclass.object = args->v0.object;
    118 		oclass.client = client;
    119 		oclass.parent = parent;
    120 		ret = parent->func->sclass(parent, i++, &oclass);
    121 		if (ret)
    122 			return ret;
    123 	} while (oclass.base.oclass != args->v0.oclass);
    124 
    125 	if (oclass.engine) {
    126 		oclass.engine = nvkm_engine_ref(oclass.engine);
    127 		if (IS_ERR(oclass.engine))
    128 			return PTR_ERR(oclass.engine);
    129 	}
    130 
    131 	ret = oclass.ctor(&oclass, data, size, &object);
    132 	nvkm_engine_unref(&oclass.engine);
    133 	if (ret == 0) {
    134 		ret = nvkm_object_init(object);
    135 		if (ret == 0) {
    136 			list_add(&object->head, &parent->tree);
    137 			if (nvkm_object_insert(object)) {
    138 				client->data = object;
    139 				return 0;
    140 			}
    141 			ret = -EEXIST;
    142 		}
    143 		nvkm_object_fini(object, false);
    144 	}
    145 
    146 	nvkm_object_del(&object);
    147 	return ret;
    148 }
    149 
    150 static int
    151 nvkm_ioctl_del(struct nvkm_client *client,
    152 	       struct nvkm_object *object, void *data, u32 size)
    153 {
    154 	union {
    155 		struct nvif_ioctl_del none;
    156 	} *args = data;
    157 	int ret = -ENOSYS;
    158 
    159 	nvif_ioctl(object, "delete size %d\n", size);
    160 	if (!(ret = nvif_unvers(ret, &data, &size, args->none))) {
    161 		nvif_ioctl(object, "delete\n");
    162 		nvkm_object_fini(object, false);
    163 		nvkm_object_del(&object);
    164 	}
    165 
    166 	return ret ? ret : 1;
    167 }
    168 
    169 static int
    170 nvkm_ioctl_mthd(struct nvkm_client *client,
    171 		struct nvkm_object *object, void *data, u32 size)
    172 {
    173 	union {
    174 		struct nvif_ioctl_mthd_v0 v0;
    175 	} *args = data;
    176 	int ret = -ENOSYS;
    177 
    178 	nvif_ioctl(object, "mthd size %d\n", size);
    179 	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
    180 		nvif_ioctl(object, "mthd vers %d mthd %02x\n",
    181 			   args->v0.version, args->v0.method);
    182 		ret = nvkm_object_mthd(object, args->v0.method, data, size);
    183 	}
    184 
    185 	return ret;
    186 }
    187 
    188 
    189 static int
    190 nvkm_ioctl_rd(struct nvkm_client *client,
    191 	      struct nvkm_object *object, void *data, u32 size)
    192 {
    193 	union {
    194 		struct nvif_ioctl_rd_v0 v0;
    195 	} *args = data;
    196 	union {
    197 		u8  b08;
    198 		u16 b16;
    199 		u32 b32;
    200 	} v;
    201 	int ret = -ENOSYS;
    202 
    203 	nvif_ioctl(object, "rd size %d\n", size);
    204 	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
    205 		nvif_ioctl(object, "rd vers %d size %d addr %016"PRIx64"\n",
    206 			   args->v0.version, args->v0.size, args->v0.addr);
    207 		switch (args->v0.size) {
    208 		case 1:
    209 			ret = nvkm_object_rd08(object, args->v0.addr, &v.b08);
    210 			args->v0.data = v.b08;
    211 			break;
    212 		case 2:
    213 			ret = nvkm_object_rd16(object, args->v0.addr, &v.b16);
    214 			args->v0.data = v.b16;
    215 			break;
    216 		case 4:
    217 			ret = nvkm_object_rd32(object, args->v0.addr, &v.b32);
    218 			args->v0.data = v.b32;
    219 			break;
    220 		default:
    221 			ret = -EINVAL;
    222 			break;
    223 		}
    224 	}
    225 
    226 	return ret;
    227 }
    228 
    229 static int
    230 nvkm_ioctl_wr(struct nvkm_client *client,
    231 	      struct nvkm_object *object, void *data, u32 size)
    232 {
    233 	union {
    234 		struct nvif_ioctl_wr_v0 v0;
    235 	} *args = data;
    236 	int ret = -ENOSYS;
    237 
    238 	nvif_ioctl(object, "wr size %d\n", size);
    239 	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
    240 		nvif_ioctl(object,
    241 			   "wr vers %d size %d addr %016"PRIx64" data %08x\n",
    242 			   args->v0.version, args->v0.size, args->v0.addr,
    243 			   args->v0.data);
    244 	} else
    245 		return ret;
    246 
    247 	switch (args->v0.size) {
    248 	case 1: return nvkm_object_wr08(object, args->v0.addr, args->v0.data);
    249 	case 2: return nvkm_object_wr16(object, args->v0.addr, args->v0.data);
    250 	case 4: return nvkm_object_wr32(object, args->v0.addr, args->v0.data);
    251 	default:
    252 		break;
    253 	}
    254 
    255 	return -EINVAL;
    256 }
    257 
    258 static int
    259 nvkm_ioctl_map(struct nvkm_client *client,
    260 	       struct nvkm_object *object, void *data, u32 size)
    261 {
    262 	union {
    263 		struct nvif_ioctl_map_v0 v0;
    264 	} *args = data;
    265 	enum nvkm_object_map type;
    266 	int ret = -ENOSYS;
    267 
    268 	nvif_ioctl(object, "map size %d\n", size);
    269 	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
    270 		nvif_ioctl(object, "map vers %d\n", args->v0.version);
    271 		bus_space_tag_t dummy __unused;
    272 		ret = nvkm_object_map(object, data, size, &type, &dummy,
    273 				      &args->v0.handle,
    274 				      &args->v0.length);
    275 		if (type == NVKM_OBJECT_MAP_IO)
    276 			args->v0.type = NVIF_IOCTL_MAP_V0_IO;
    277 		else
    278 			args->v0.type = NVIF_IOCTL_MAP_V0_VA;
    279 	}
    280 
    281 	return ret;
    282 }
    283 
    284 static int
    285 nvkm_ioctl_map_netbsd(struct nvkm_client *client, struct nvkm_object *object,
    286     void *data, u32 size)
    287 {
    288 	union {
    289 		struct nvif_ioctl_map_netbsd_v0 v0;
    290 	} *args = data;
    291 	enum nvkm_object_map type;
    292 	int ret = -ENOSYS;
    293 
    294 	nvif_ioctl(object, "map size %d\n", size);
    295 	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
    296 		nvif_ioctl(object, "map vers %d\n", args->v0.version);
    297 		ret = nvkm_object_map(object, data, size, &type, &args->v0.tag,
    298 				      &args->v0.handle,
    299 				      &args->v0.length);
    300 		if (type == NVKM_OBJECT_MAP_IO)
    301 			args->v0.type = NVIF_IOCTL_MAP_V0_IO;
    302 		else
    303 			args->v0.type = NVIF_IOCTL_MAP_V0_VA;
    304 	}
    305 
    306 	return ret;
    307 }
    308 
    309 static int
    310 nvkm_ioctl_unmap(struct nvkm_client *client,
    311 		 struct nvkm_object *object, void *data, u32 size)
    312 {
    313 	union {
    314 		struct nvif_ioctl_unmap none;
    315 	} *args = data;
    316 	int ret = -ENOSYS;
    317 
    318 	nvif_ioctl(object, "unmap size %d\n", size);
    319 	if (!(ret = nvif_unvers(ret, &data, &size, args->none))) {
    320 		nvif_ioctl(object, "unmap\n");
    321 		ret = nvkm_object_unmap(object);
    322 	}
    323 
    324 	return ret;
    325 }
    326 
    327 static int
    328 nvkm_ioctl_ntfy_new(struct nvkm_client *client,
    329 		    struct nvkm_object *object, void *data, u32 size)
    330 {
    331 	union {
    332 		struct nvif_ioctl_ntfy_new_v0 v0;
    333 	} *args = data;
    334 	struct nvkm_event *event;
    335 	int ret = -ENOSYS;
    336 
    337 	nvif_ioctl(object, "ntfy new size %d\n", size);
    338 	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
    339 		nvif_ioctl(object, "ntfy new vers %d event %02x\n",
    340 			   args->v0.version, args->v0.event);
    341 		ret = nvkm_object_ntfy(object, args->v0.event, &event);
    342 		if (ret == 0) {
    343 			ret = nvkm_client_notify_new(object, event, data, size);
    344 			if (ret >= 0) {
    345 				args->v0.index = ret;
    346 				ret = 0;
    347 			}
    348 		}
    349 	}
    350 
    351 	return ret;
    352 }
    353 
    354 static int
    355 nvkm_ioctl_ntfy_del(struct nvkm_client *client,
    356 		    struct nvkm_object *object, void *data, u32 size)
    357 {
    358 	union {
    359 		struct nvif_ioctl_ntfy_del_v0 v0;
    360 	} *args = data;
    361 	int ret = -ENOSYS;
    362 
    363 	nvif_ioctl(object, "ntfy del size %d\n", size);
    364 	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
    365 		nvif_ioctl(object, "ntfy del vers %d index %d\n",
    366 			   args->v0.version, args->v0.index);
    367 		ret = nvkm_client_notify_del(client, args->v0.index);
    368 	}
    369 
    370 	return ret;
    371 }
    372 
    373 static int
    374 nvkm_ioctl_ntfy_get(struct nvkm_client *client,
    375 		    struct nvkm_object *object, void *data, u32 size)
    376 {
    377 	union {
    378 		struct nvif_ioctl_ntfy_get_v0 v0;
    379 	} *args = data;
    380 	int ret = -ENOSYS;
    381 
    382 	nvif_ioctl(object, "ntfy get size %d\n", size);
    383 	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
    384 		nvif_ioctl(object, "ntfy get vers %d index %d\n",
    385 			   args->v0.version, args->v0.index);
    386 		ret = nvkm_client_notify_get(client, args->v0.index);
    387 	}
    388 
    389 	return ret;
    390 }
    391 
    392 static int
    393 nvkm_ioctl_ntfy_put(struct nvkm_client *client,
    394 		    struct nvkm_object *object, void *data, u32 size)
    395 {
    396 	union {
    397 		struct nvif_ioctl_ntfy_put_v0 v0;
    398 	} *args = data;
    399 	int ret = -ENOSYS;
    400 
    401 	nvif_ioctl(object, "ntfy put size %d\n", size);
    402 	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
    403 		nvif_ioctl(object, "ntfy put vers %d index %d\n",
    404 			   args->v0.version, args->v0.index);
    405 		ret = nvkm_client_notify_put(client, args->v0.index);
    406 	}
    407 
    408 	return ret;
    409 }
    410 
    411 static struct {
    412 	int version;
    413 	int (*func)(struct nvkm_client *, struct nvkm_object *, void *, u32);
    414 }
    415 nvkm_ioctl_v0[] = {
    416 	{ 0x00, nvkm_ioctl_nop },
    417 	{ 0x00, nvkm_ioctl_sclass },
    418 	{ 0x00, nvkm_ioctl_new },
    419 	{ 0x00, nvkm_ioctl_del },
    420 	{ 0x00, nvkm_ioctl_mthd },
    421 	{ 0x00, nvkm_ioctl_rd },
    422 	{ 0x00, nvkm_ioctl_wr },
    423 	{ 0x00, nvkm_ioctl_map },
    424 	{ 0x00, nvkm_ioctl_unmap },
    425 	{ 0x00, nvkm_ioctl_ntfy_new },
    426 	{ 0x00, nvkm_ioctl_ntfy_del },
    427 	{ 0x00, nvkm_ioctl_ntfy_get },
    428 	{ 0x00, nvkm_ioctl_ntfy_put },
    429 	{ 0x00, nvkm_ioctl_map_netbsd },
    430 };
    431 
    432 static int
    433 nvkm_ioctl_path(struct nvkm_client *client, u64 handle, u32 type,
    434 		void *data, u32 size, u8 owner, u8 *route, u64 *token)
    435 {
    436 	struct nvkm_object *object;
    437 	int ret;
    438 
    439 	object = nvkm_object_search(client, handle, NULL);
    440 	if (IS_ERR(object)) {
    441 		nvif_ioctl(&client->object, "object not found\n");
    442 		return PTR_ERR(object);
    443 	}
    444 
    445 	if (owner != NVIF_IOCTL_V0_OWNER_ANY && owner != object->route) {
    446 		nvif_ioctl(&client->object, "route != owner\n");
    447 		return -EACCES;
    448 	}
    449 	*route = object->route;
    450 	*token = object->token;
    451 
    452 	if (ret = -EINVAL, type < ARRAY_SIZE(nvkm_ioctl_v0)) {
    453 		if (nvkm_ioctl_v0[type].version == 0)
    454 			ret = nvkm_ioctl_v0[type].func(client, object, data, size);
    455 	}
    456 
    457 	return ret;
    458 }
    459 
    460 int
    461 nvkm_ioctl(struct nvkm_client *client, bool supervisor,
    462 	   void *data, u32 size, void **hack)
    463 {
    464 	struct nvkm_object *object = &client->object;
    465 	union {
    466 		struct nvif_ioctl_v0 v0;
    467 	} *args = data;
    468 	int ret = -ENOSYS;
    469 
    470 	client->super = supervisor;
    471 	nvif_ioctl(object, "size %d\n", size);
    472 
    473 	if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
    474 		nvif_ioctl(object,
    475 			   "vers %d type %02x object %016"PRIx64" owner %02x\n",
    476 			   args->v0.version, args->v0.type, args->v0.object,
    477 			   args->v0.owner);
    478 		ret = nvkm_ioctl_path(client, args->v0.object, args->v0.type,
    479 				      data, size, args->v0.owner,
    480 				      &args->v0.route, &args->v0.token);
    481 	}
    482 
    483 	if (ret != 1) {
    484 		nvif_ioctl(object, "return %d\n", ret);
    485 		if (hack) {
    486 			*hack = client->data;
    487 			client->data = NULL;
    488 		}
    489 	}
    490 #ifdef __NetBSD__
    491 	else
    492 		ret = 0;
    493 #endif
    494 
    495 	return ret;
    496 }
    497