diff options
author | Ben Skeggs <bskeggs@nvidia.com> | 2024-07-26 14:37:59 +1000 |
---|---|---|
committer | Danilo Krummrich <dakr@kernel.org> | 2024-07-27 03:05:22 +0200 |
commit | ba6b8479c944c50db7e2a81794885896fa6c48a8 (patch) | |
tree | 31b6e8fc23c348d2a65aa54debe39b1113dfe1d9 | |
parent | 2e408ad7a55273b55b14ce28f62111dc1c4f7fb6 (diff) |
drm/nouveau: handle limited nvif ioctl in abi16
nouveau_usif.c was already stripped right back a couple of years ago,
limiting what userspace could do with it.
A follow-on series removes the nvkm side of these interfaces entirely,
in order to make it less of a nightmare to add/change internal APIs in
the future.
Unfortunately. Userspace uses some of this.
Fortunately, userspace only ever ended up using a fraction of the APIs,
so those are reimplemened here in a more direct manner, and return
-EINVAL to userspace for everything else.
v2:
- simplified struct nouveau_abi16_obj
- added a couple of comments
v3:
- comment harder
Signed-off-by: Ben Skeggs <bskeggs@nvidia.com>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20240726043828.58966-9-bskeggs@nvidia.com
-rw-r--r-- | drivers/gpu/drm/nouveau/Kbuild | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_abi16.c | 286 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_abi16.h | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drm.c | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_nvif.c | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_usif.c | 194 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_usif.h | 10 |
7 files changed, 251 insertions, 249 deletions
diff --git a/drivers/gpu/drm/nouveau/Kbuild b/drivers/gpu/drm/nouveau/Kbuild index c32c01827c1d..7b863355c5c6 100644 --- a/drivers/gpu/drm/nouveau/Kbuild +++ b/drivers/gpu/drm/nouveau/Kbuild @@ -25,7 +25,6 @@ nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o nouveau-$(CONFIG_LEDS_CLASS) += nouveau_led.o nouveau-y += nouveau_nvif.o nouveau-$(CONFIG_NOUVEAU_PLATFORM_DRIVER) += nouveau_platform.o -nouveau-y += nouveau_usif.o # userspace <-> nvif nouveau-y += nouveau_vga.o # DRM - memory management diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.c b/drivers/gpu/drm/nouveau/nouveau_abi16.c index ac50c300d2eb..f2f5031d539f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_abi16.c +++ b/drivers/gpu/drm/nouveau/nouveau_abi16.c @@ -52,6 +52,7 @@ nouveau_abi16(struct drm_file *file_priv) abi16->cli = cli; INIT_LIST_HEAD(&abi16->channels); + INIT_LIST_HEAD(&abi16->objects); /* allocate device object targeting client's default * device (ie. the one that belongs to the fd it @@ -88,6 +89,67 @@ nouveau_abi16_put(struct nouveau_abi16 *abi16, int ret) return ret; } +/* Tracks objects created via the DRM_NOUVEAU_NVIF ioctl. + * + * The only two types of object that userspace ever allocated via this + * interface are 'device', in order to retrieve basic device info, and + * 'engine objects', which instantiate HW classes on a channel. + * + * The remainder of what used to be available via DRM_NOUVEAU_NVIF has + * been removed, but these object types need to be tracked to maintain + * compatibility with userspace. + */ +struct nouveau_abi16_obj { + enum nouveau_abi16_obj_type { + DEVICE, + ENGOBJ, + } type; + u64 object; + + struct nvif_object engobj; + + struct list_head head; /* protected by nouveau_abi16.cli.mutex */ +}; + +static struct nouveau_abi16_obj * +nouveau_abi16_obj_find(struct nouveau_abi16 *abi16, u64 object) +{ + struct nouveau_abi16_obj *obj; + + list_for_each_entry(obj, &abi16->objects, head) { + if (obj->object == object) + return obj; + } + + return NULL; +} + +static void +nouveau_abi16_obj_del(struct nouveau_abi16_obj *obj) +{ + list_del(&obj->head); + kfree(obj); +} + +static struct nouveau_abi16_obj * +nouveau_abi16_obj_new(struct nouveau_abi16 *abi16, enum nouveau_abi16_obj_type type, u64 object) +{ + struct nouveau_abi16_obj *obj; + + obj = nouveau_abi16_obj_find(abi16, object); + if (obj) + return ERR_PTR(-EEXIST); + + obj = kzalloc(sizeof(*obj), GFP_KERNEL); + if (!obj) + return ERR_PTR(-ENOMEM); + + obj->type = type; + obj->object = object; + list_add_tail(&obj->head, &abi16->objects); + return obj; +} + s32 nouveau_abi16_swclass(struct nouveau_drm *drm) { @@ -167,6 +229,12 @@ nouveau_abi16_fini(struct nouveau_abi16 *abi16) { struct nouveau_cli *cli = abi16->cli; struct nouveau_abi16_chan *chan, *temp; + struct nouveau_abi16_obj *obj, *tmp; + + /* cleanup objects */ + list_for_each_entry_safe(obj, tmp, &abi16->objects, head) { + nouveau_abi16_obj_del(obj); + } /* cleanup channels */ list_for_each_entry_safe(chan, temp, &abi16->channels, head) { @@ -459,44 +527,6 @@ nouveau_abi16_chan(struct nouveau_abi16 *abi16, int channel) } int -nouveau_abi16_usif(struct drm_file *file_priv, void *data, u32 size) -{ - union { - struct nvif_ioctl_v0 v0; - } *args = data; - struct nouveau_abi16_chan *chan; - struct nouveau_abi16 *abi16; - int ret = -ENOSYS; - - if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) { - switch (args->v0.type) { - case NVIF_IOCTL_V0_NEW: - case NVIF_IOCTL_V0_MTHD: - case NVIF_IOCTL_V0_SCLASS: - break; - default: - return -EACCES; - } - } else - return ret; - - if (!(abi16 = nouveau_abi16(file_priv))) - return -ENOMEM; - - if (args->v0.token != ~0ULL) { - if (!(chan = nouveau_abi16_chan(abi16, args->v0.token))) - return -EINVAL; - args->v0.object = nvif_handle(&chan->chan->user); - args->v0.owner = NVIF_IOCTL_V0_OWNER_ANY; - return 0; - } - - args->v0.object = nvif_handle(&abi16->device.object); - args->v0.owner = NVIF_IOCTL_V0_OWNER_ANY; - return 0; -} - -int nouveau_abi16_ioctl_channel_free(ABI16_IOCTL_ARGS) { struct drm_nouveau_channel_free *req = data; @@ -705,3 +735,183 @@ nouveau_abi16_ioctl_gpuobj_free(ABI16_IOCTL_ARGS) return nouveau_abi16_put(abi16, ret); } + +static int +nouveau_abi16_ioctl_mthd(struct nouveau_abi16 *abi16, struct nvif_ioctl_v0 *ioctl, u32 argc) +{ + struct nouveau_cli *cli = abi16->cli; + struct nvif_ioctl_mthd_v0 *args; + struct nouveau_abi16_obj *obj; + struct nv_device_info_v0 *info; + + if (ioctl->route || argc < sizeof(*args)) + return -EINVAL; + args = (void *)ioctl->data; + argc -= sizeof(*args); + + obj = nouveau_abi16_obj_find(abi16, ioctl->object); + if (!obj || obj->type != DEVICE) + return -EINVAL; + + if (args->method != NV_DEVICE_V0_INFO || + argc != sizeof(*info)) + return -EINVAL; + + info = (void *)args->data; + if (info->version != 0x00) + return -EINVAL; + + info = &cli->device.info; + memcpy(args->data, info, sizeof(*info)); + return 0; +} + +static int +nouveau_abi16_ioctl_del(struct nouveau_abi16 *abi16, struct nvif_ioctl_v0 *ioctl, u32 argc) +{ + struct nouveau_abi16_obj *obj; + + if (ioctl->route || argc) + return -EINVAL; + + obj = nouveau_abi16_obj_find(abi16, ioctl->object); + if (obj) { + if (obj->type == ENGOBJ) + nvif_object_dtor(&obj->engobj); + nouveau_abi16_obj_del(obj); + } + + return 0; +} + +static int +nouveau_abi16_ioctl_new(struct nouveau_abi16 *abi16, struct nvif_ioctl_v0 *ioctl, u32 argc) +{ + struct nvif_ioctl_new_v0 *args; + struct nouveau_abi16_chan *chan; + struct nouveau_abi16_obj *obj; + int ret; + + if (argc < sizeof(*args)) + return -EINVAL; + args = (void *)ioctl->data; + argc -= sizeof(*args); + + if (args->version != 0) + return -EINVAL; + + if (!ioctl->route) { + if (ioctl->object || args->oclass != NV_DEVICE) + return -EINVAL; + + obj = nouveau_abi16_obj_new(abi16, DEVICE, args->object); + if (IS_ERR(obj)) + return PTR_ERR(obj); + + return 0; + } + + chan = nouveau_abi16_chan(abi16, ioctl->token); + if (!chan) + return -EINVAL; + + obj = nouveau_abi16_obj_new(abi16, ENGOBJ, args->object); + if (IS_ERR(obj)) + return PTR_ERR(obj); + + ret = nvif_object_ctor(&chan->chan->user, "abi16EngObj", args->handle, args->oclass, + NULL, 0, &obj->engobj); + if (ret) + nouveau_abi16_obj_del(obj); + + return ret; +} + +static int +nouveau_abi16_ioctl_sclass(struct nouveau_abi16 *abi16, struct nvif_ioctl_v0 *ioctl, u32 argc) +{ + struct nvif_ioctl_sclass_v0 *args; + struct nouveau_abi16_chan *chan; + struct nvif_sclass *sclass; + int ret; + + if (!ioctl->route || argc < sizeof(*args)) + return -EINVAL; + args = (void *)ioctl->data; + argc -= sizeof(*args); + + if (argc != args->count * sizeof(args->oclass[0])) + return -EINVAL; + + chan = nouveau_abi16_chan(abi16, ioctl->token); + if (!chan) + return -EINVAL; + + ret = nvif_object_sclass_get(&chan->chan->user, &sclass); + if (ret < 0) + return ret; + + for (int i = 0; i < min_t(u8, args->count, ret); i++) { + args->oclass[i].oclass = sclass[i].oclass; + args->oclass[i].minver = sclass[i].minver; + args->oclass[i].maxver = sclass[i].maxver; + } + args->count = ret; + + nvif_object_sclass_put(&sclass); + return 0; +} + +int +nouveau_abi16_ioctl(struct drm_file *filp, void __user *user, u32 size) +{ + struct nvif_ioctl_v0 *ioctl; + struct nouveau_abi16 *abi16; + u32 argc = size; + int ret; + + if (argc < sizeof(*ioctl)) + return -EINVAL; + argc -= sizeof(*ioctl); + + ioctl = kmalloc(size, GFP_KERNEL); + if (!ioctl) + return -ENOMEM; + + ret = -EFAULT; + if (copy_from_user(ioctl, user, size)) + goto done_free; + + if (ioctl->version != 0x00 || + (ioctl->route && ioctl->route != 0xff)) { + ret = -EINVAL; + goto done_free; + } + + abi16 = nouveau_abi16_get(filp); + if (unlikely(!abi16)) { + ret = -ENOMEM; + goto done_free; + } + + switch (ioctl->type) { + case NVIF_IOCTL_V0_SCLASS: ret = nouveau_abi16_ioctl_sclass(abi16, ioctl, argc); break; + case NVIF_IOCTL_V0_NEW : ret = nouveau_abi16_ioctl_new (abi16, ioctl, argc); break; + case NVIF_IOCTL_V0_DEL : ret = nouveau_abi16_ioctl_del (abi16, ioctl, argc); break; + case NVIF_IOCTL_V0_MTHD : ret = nouveau_abi16_ioctl_mthd (abi16, ioctl, argc); break; + default: + ret = -EINVAL; + break; + } + + nouveau_abi16_put(abi16, 0); + + if (ret == 0) { + if (copy_to_user(user, ioctl, size)) + ret = -EFAULT; + } + +done_free: + kfree(ioctl); + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.h b/drivers/gpu/drm/nouveau/nouveau_abi16.h index 0a9121e63143..75a883a44e04 100644 --- a/drivers/gpu/drm/nouveau/nouveau_abi16.h +++ b/drivers/gpu/drm/nouveau/nouveau_abi16.h @@ -34,13 +34,14 @@ struct nouveau_abi16 { struct nvif_device device; struct list_head channels; u64 handles; + struct list_head objects; }; struct nouveau_abi16 *nouveau_abi16_get(struct drm_file *); int nouveau_abi16_put(struct nouveau_abi16 *, int); void nouveau_abi16_fini(struct nouveau_abi16 *); s32 nouveau_abi16_swclass(struct nouveau_drm *); -int nouveau_abi16_usif(struct drm_file *, void *data, u32 size); +int nouveau_abi16_ioctl(struct drm_file *, void __user *user, u32 size); #define NOUVEAU_GEM_DOMAIN_VRAM (1 << 1) #define NOUVEAU_GEM_DOMAIN_GART (1 << 2) diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index eb154b44c154..a018b5475478 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -63,7 +63,6 @@ #include "nouveau_abi16.h" #include "nouveau_fence.h" #include "nouveau_debugfs.h" -#include "nouveau_usif.h" #include "nouveau_connector.h" #include "nouveau_platform.h" #include "nouveau_svm.h" @@ -200,7 +199,6 @@ nouveau_cli_fini(struct nouveau_cli *cli) flush_work(&cli->work); WARN_ON(!list_empty(&cli->worker)); - usif_client_fini(cli); if (cli->sched) nouveau_sched_destroy(&cli->sched); if (uvmm) @@ -249,7 +247,6 @@ nouveau_cli_init(struct nouveau_drm *drm, const char *sname, snprintf(cli->name, sizeof(cli->name), "%s", sname); cli->drm = drm; mutex_init(&cli->mutex); - usif_client_init(cli); INIT_WORK(&cli->work, nouveau_cli_work); INIT_LIST_HEAD(&cli->worker); @@ -1267,7 +1264,7 @@ nouveau_drm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) switch (_IOC_NR(cmd) - DRM_COMMAND_BASE) { case DRM_NOUVEAU_NVIF: - ret = usif_ioctl(filp, (void __user *)arg, _IOC_SIZE(cmd)); + ret = nouveau_abi16_ioctl(filp, (void __user *)arg, _IOC_SIZE(cmd)); break; default: ret = drm_ioctl(file, cmd, arg); diff --git a/drivers/gpu/drm/nouveau/nouveau_nvif.c b/drivers/gpu/drm/nouveau/nouveau_nvif.c index 1d49ebdfd5dc..9a7e3f64b79f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_nvif.c +++ b/drivers/gpu/drm/nouveau/nouveau_nvif.c @@ -35,7 +35,6 @@ #include <nvif/ioctl.h> #include "nouveau_drv.h" -#include "nouveau_usif.h" static void nvkm_client_unmap(void *priv, void __iomem *ptr, u32 size) diff --git a/drivers/gpu/drm/nouveau/nouveau_usif.c b/drivers/gpu/drm/nouveau/nouveau_usif.c deleted file mode 100644 index 002d1479ba89..000000000000 --- a/drivers/gpu/drm/nouveau/nouveau_usif.c +++ /dev/null @@ -1,194 +0,0 @@ -/* - * Copyright 2014 Red Hat Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: Ben Skeggs <bskeggs@redhat.com> - */ - -#include "nouveau_drv.h" -#include "nouveau_usif.h" -#include "nouveau_abi16.h" - -#include <nvif/unpack.h> -#include <nvif/client.h> -#include <nvif/ioctl.h> - -#include <nvif/class.h> -#include <nvif/cl0080.h> - -struct usif_object { - struct list_head head; - u8 route; - u64 token; -}; - -static void -usif_object_dtor(struct usif_object *object) -{ - list_del(&object->head); - kfree(object); -} - -static int -usif_object_new(struct drm_file *f, void *data, u32 size, void *argv, u32 argc, bool parent_abi16) -{ - struct nouveau_cli *cli = nouveau_cli(f); - struct nvif_client *client = &cli->base; - union { - struct nvif_ioctl_new_v0 v0; - } *args = data; - struct usif_object *object; - int ret = -ENOSYS; - - if ((ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) - return ret; - - switch (args->v0.oclass) { - case NV_DMA_FROM_MEMORY: - case NV_DMA_TO_MEMORY: - case NV_DMA_IN_MEMORY: - return -EINVAL; - case NV_DEVICE: { - union { - struct nv_device_v0 v0; - } *args = data; - - if ((ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) - return ret; - - args->v0.priv = false; - break; - } - default: - if (!parent_abi16) - return -EINVAL; - break; - } - - if (!(object = kmalloc(sizeof(*object), GFP_KERNEL))) - return -ENOMEM; - list_add(&object->head, &cli->objects); - - object->route = args->v0.route; - object->token = args->v0.token; - args->v0.route = NVDRM_OBJECT_USIF; - args->v0.token = (unsigned long)(void *)object; - ret = nvif_client_ioctl(client, argv, argc); - if (ret) { - usif_object_dtor(object); - return ret; - } - - args->v0.token = object->token; - args->v0.route = object->route; - return 0; -} - -int -usif_ioctl(struct drm_file *filp, void __user *user, u32 argc) -{ - struct nouveau_cli *cli = nouveau_cli(filp); - struct nvif_client *client = &cli->base; - void *data = kmalloc(argc, GFP_KERNEL); - u32 size = argc; - union { - struct nvif_ioctl_v0 v0; - } *argv = data; - struct usif_object *object; - bool abi16 = false; - u8 owner; - int ret; - - if (ret = -ENOMEM, !argv) - goto done; - if (ret = -EFAULT, copy_from_user(argv, user, size)) - goto done; - - if (!(ret = nvif_unpack(-ENOSYS, &data, &size, argv->v0, 0, 0, true))) { - /* block access to objects not created via this interface */ - owner = argv->v0.owner; - if (argv->v0.object == 0ULL && - argv->v0.type != NVIF_IOCTL_V0_DEL) - argv->v0.owner = NVDRM_OBJECT_ANY; /* except client */ - else - argv->v0.owner = NVDRM_OBJECT_USIF; - } else - goto done; - - /* USIF slightly abuses some return-only ioctl members in order - * to provide interoperability with the older ABI16 objects - */ - mutex_lock(&cli->mutex); - if (argv->v0.route) { - if (ret = -EINVAL, argv->v0.route == 0xff) - ret = nouveau_abi16_usif(filp, argv, argc); - if (ret) { - mutex_unlock(&cli->mutex); - goto done; - } - - abi16 = true; - } - - switch (argv->v0.type) { - case NVIF_IOCTL_V0_NEW: - ret = usif_object_new(filp, data, size, argv, argc, abi16); - break; - default: - ret = nvif_client_ioctl(client, argv, argc); - break; - } - if (argv->v0.route == NVDRM_OBJECT_USIF) { - object = (void *)(unsigned long)argv->v0.token; - argv->v0.route = object->route; - argv->v0.token = object->token; - if (ret == 0 && argv->v0.type == NVIF_IOCTL_V0_DEL) { - list_del(&object->head); - kfree(object); - } - } else { - argv->v0.route = NVIF_IOCTL_V0_ROUTE_HIDDEN; - argv->v0.token = 0; - } - argv->v0.owner = owner; - mutex_unlock(&cli->mutex); - - if (copy_to_user(user, argv, argc)) - ret = -EFAULT; -done: - kfree(argv); - return ret; -} - -void -usif_client_fini(struct nouveau_cli *cli) -{ - struct usif_object *object, *otemp; - - list_for_each_entry_safe(object, otemp, &cli->objects, head) { - usif_object_dtor(object); - } -} - -void -usif_client_init(struct nouveau_cli *cli) -{ - INIT_LIST_HEAD(&cli->objects); -} diff --git a/drivers/gpu/drm/nouveau/nouveau_usif.h b/drivers/gpu/drm/nouveau/nouveau_usif.h deleted file mode 100644 index dc90d4a9d0d9..000000000000 --- a/drivers/gpu/drm/nouveau/nouveau_usif.h +++ /dev/null @@ -1,10 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -#ifndef __NOUVEAU_USIF_H__ -#define __NOUVEAU_USIF_H__ - -void usif_client_init(struct nouveau_cli *); -void usif_client_fini(struct nouveau_cli *); -int usif_ioctl(struct drm_file *, void __user *, u32); -int usif_notify(const void *, u32, const void *, u32); - -#endif |