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 25 #include "nouveau_drv.h" 26 #include "nouveau_usif.h" 27 #include "nouveau_abi16.h" 28 29 #include <nvif/unpack.h> 30 #include <nvif/client.h> 31 #include <nvif/ioctl.h> 32 33 #include <nvif/class.h> 34 #include <nvif/cl0080.h> 35 36 struct usif_object { 37 struct list_head head; 38 u8 route; 39 u64 token; 40 }; 41 42 static void 43 usif_object_dtor(struct usif_object *object) 44 { 45 list_del(&object->head); 46 kfree(object); 47 } 48 49 static int 50 usif_object_new(struct drm_file *f, void *data, u32 size, void *argv, u32 argc, bool parent_abi16) 51 { 52 struct nouveau_cli *cli = nouveau_cli(f); 53 struct nvif_client *client = &cli->base; 54 union { 55 struct nvif_ioctl_new_v0 v0; 56 } *args = data; 57 struct usif_object *object; 58 int ret = -ENOSYS; 59 60 if ((ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) 61 return ret; 62 63 switch (args->v0.oclass) { 64 case NV_DMA_FROM_MEMORY: 65 case NV_DMA_TO_MEMORY: 66 case NV_DMA_IN_MEMORY: 67 return -EINVAL; 68 case NV_DEVICE: { 69 union { 70 struct nv_device_v0 v0; 71 } *args = data; 72 73 if ((ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) 74 return ret; 75 76 args->v0.priv = false; 77 break; 78 } 79 default: 80 if (!parent_abi16) 81 return -EINVAL; 82 break; 83 } 84 85 if (!(object = kmalloc(sizeof(*object), GFP_KERNEL))) 86 return -ENOMEM; 87 list_add(&object->head, &cli->objects); 88 89 object->route = args->v0.route; 90 object->token = args->v0.token; 91 args->v0.route = NVDRM_OBJECT_USIF; 92 args->v0.token = (unsigned long)(void *)object; 93 ret = nvif_client_ioctl(client, argv, argc); 94 if (ret) { 95 usif_object_dtor(object); 96 return ret; 97 } 98 99 args->v0.token = object->token; 100 args->v0.route = object->route; 101 return 0; 102 } 103 104 int 105 usif_ioctl(struct drm_file *filp, void __user *user, u32 argc) 106 { 107 struct nouveau_cli *cli = nouveau_cli(filp); 108 struct nvif_client *client = &cli->base; 109 void *data = kmalloc(argc, GFP_KERNEL); 110 u32 size = argc; 111 union { 112 struct nvif_ioctl_v0 v0; 113 } *argv = data; 114 struct usif_object *object; 115 bool abi16 = false; 116 u8 owner; 117 int ret; 118 119 if (ret = -ENOMEM, !argv) 120 goto done; 121 if (ret = -EFAULT, copy_from_user(argv, user, size)) 122 goto done; 123 124 if (!(ret = nvif_unpack(-ENOSYS, &data, &size, argv->v0, 0, 0, true))) { 125 /* block access to objects not created via this interface */ 126 owner = argv->v0.owner; 127 if (argv->v0.object == 0ULL && 128 argv->v0.type != NVIF_IOCTL_V0_DEL) 129 argv->v0.owner = NVDRM_OBJECT_ANY; /* except client */ 130 else 131 argv->v0.owner = NVDRM_OBJECT_USIF; 132 } else 133 goto done; 134 135 /* USIF slightly abuses some return-only ioctl members in order 136 * to provide interoperability with the older ABI16 objects 137 */ 138 mutex_lock(&cli->mutex); 139 if (argv->v0.route) { 140 if (ret = -EINVAL, argv->v0.route == 0xff) 141 ret = nouveau_abi16_usif(filp, argv, argc); 142 if (ret) { 143 mutex_unlock(&cli->mutex); 144 goto done; 145 } 146 147 abi16 = true; 148 } 149 150 switch (argv->v0.type) { 151 case NVIF_IOCTL_V0_NEW: 152 ret = usif_object_new(filp, data, size, argv, argc, abi16); 153 break; 154 default: 155 ret = nvif_client_ioctl(client, argv, argc); 156 break; 157 } 158 if (argv->v0.route == NVDRM_OBJECT_USIF) { 159 object = (void *)(unsigned long)argv->v0.token; 160 argv->v0.route = object->route; 161 argv->v0.token = object->token; 162 if (ret == 0 && argv->v0.type == NVIF_IOCTL_V0_DEL) { 163 list_del(&object->head); 164 kfree(object); 165 } 166 } else { 167 argv->v0.route = NVIF_IOCTL_V0_ROUTE_HIDDEN; 168 argv->v0.token = 0; 169 } 170 argv->v0.owner = owner; 171 mutex_unlock(&cli->mutex); 172 173 if (copy_to_user(user, argv, argc)) 174 ret = -EFAULT; 175 done: 176 kfree(argv); 177 return ret; 178 } 179 180 void 181 usif_client_fini(struct nouveau_cli *cli) 182 { 183 struct usif_object *object, *otemp; 184 185 list_for_each_entry_safe(object, otemp, &cli->objects, head) { 186 usif_object_dtor(object); 187 } 188 } 189 190 void 191 usif_client_init(struct nouveau_cli *cli) 192 { 193 INIT_LIST_HEAD(&cli->objects); 194 } 195