cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

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}