| 1 | /* | 
|---|
| 2 | * Created: Fri Jan  8 09:01:26 1999 by faith@valinux.com | 
|---|
| 3 | * | 
|---|
| 4 | * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. | 
|---|
| 5 | * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. | 
|---|
| 6 | * All Rights Reserved. | 
|---|
| 7 | * | 
|---|
| 8 | * Author Rickard E. (Rik) Faith <faith@valinux.com> | 
|---|
| 9 | * Author Gareth Hughes <gareth@valinux.com> | 
|---|
| 10 | * | 
|---|
| 11 | * Permission is hereby granted, free of charge, to any person obtaining a | 
|---|
| 12 | * copy of this software and associated documentation files (the "Software"), | 
|---|
| 13 | * to deal in the Software without restriction, including without limitation | 
|---|
| 14 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | 
|---|
| 15 | * and/or sell copies of the Software, and to permit persons to whom the | 
|---|
| 16 | * Software is furnished to do so, subject to the following conditions: | 
|---|
| 17 | * | 
|---|
| 18 | * The above copyright notice and this permission notice (including the next | 
|---|
| 19 | * paragraph) shall be included in all copies or substantial portions of the | 
|---|
| 20 | * Software. | 
|---|
| 21 | * | 
|---|
| 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
|---|
| 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
|---|
| 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL | 
|---|
| 25 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | 
|---|
| 26 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | 
|---|
| 27 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | 
|---|
| 28 | * OTHER DEALINGS IN THE SOFTWARE. | 
|---|
| 29 | */ | 
|---|
| 30 |  | 
|---|
| 31 | #include <linux/export.h> | 
|---|
| 32 | #include <linux/nospec.h> | 
|---|
| 33 | #include <linux/pci.h> | 
|---|
| 34 | #include <linux/uaccess.h> | 
|---|
| 35 |  | 
|---|
| 36 | #include <drm/drm_auth.h> | 
|---|
| 37 | #include <drm/drm_crtc.h> | 
|---|
| 38 | #include <drm/drm_drv.h> | 
|---|
| 39 | #include <drm/drm_file.h> | 
|---|
| 40 | #include <drm/drm_ioctl.h> | 
|---|
| 41 | #include <drm/drm_print.h> | 
|---|
| 42 |  | 
|---|
| 43 | #include "drm_crtc_internal.h" | 
|---|
| 44 | #include "drm_internal.h" | 
|---|
| 45 |  | 
|---|
| 46 | /** | 
|---|
| 47 | * DOC: getunique and setversion story | 
|---|
| 48 | * | 
|---|
| 49 | * BEWARE THE DRAGONS! MIND THE TRAPDOORS! | 
|---|
| 50 | * | 
|---|
| 51 | * In an attempt to warn anyone else who's trying to figure out what's going | 
|---|
| 52 | * on here, I'll try to summarize the story. First things first, let's clear up | 
|---|
| 53 | * the names, because the kernel internals, libdrm and the ioctls are all named | 
|---|
| 54 | * differently: | 
|---|
| 55 | * | 
|---|
| 56 | *  - GET_UNIQUE ioctl, implemented by drm_getunique is wrapped up in libdrm | 
|---|
| 57 | *    through the drmGetBusid function. | 
|---|
| 58 | *  - The libdrm drmSetBusid function is backed by the SET_UNIQUE ioctl. All | 
|---|
| 59 | *    that code is nerved in the kernel with drm_invalid_op(). | 
|---|
| 60 | *  - The internal set_busid kernel functions and driver callbacks are | 
|---|
| 61 | *    exclusively use by the SET_VERSION ioctl, because only drm 1.0 (which is | 
|---|
| 62 | *    nerved) allowed userspace to set the busid through the above ioctl. | 
|---|
| 63 | *  - Other ioctls and functions involved are named consistently. | 
|---|
| 64 | * | 
|---|
| 65 | * For anyone wondering what's the difference between drm 1.1 and 1.4: Correctly | 
|---|
| 66 | * handling pci domains in the busid on ppc. Doing this correctly was only | 
|---|
| 67 | * implemented in libdrm in 2010, hence can't be nerved yet. No one knows what's | 
|---|
| 68 | * special with drm 1.2 and 1.3. | 
|---|
| 69 | * | 
|---|
| 70 | * Now the actual horror story of how device lookup in drm works. At large, | 
|---|
| 71 | * there's 2 different ways, either by busid, or by device driver name. | 
|---|
| 72 | * | 
|---|
| 73 | * Opening by busid is fairly simple: | 
|---|
| 74 | * | 
|---|
| 75 | * 1. First call SET_VERSION to make sure pci domains are handled properly. As a | 
|---|
| 76 | *    side-effect this fills out the unique name in the master structure. | 
|---|
| 77 | * 2. Call GET_UNIQUE to read out the unique name from the master structure, | 
|---|
| 78 | *    which matches the busid thanks to step 1. If it doesn't, proceed to try | 
|---|
| 79 | *    the next device node. | 
|---|
| 80 | * | 
|---|
| 81 | * Opening by name is slightly different: | 
|---|
| 82 | * | 
|---|
| 83 | * 1. Directly call VERSION to get the version and to match against the driver | 
|---|
| 84 | *    name returned by that ioctl. Note that SET_VERSION is not called, which | 
|---|
| 85 | *    means the unique name for the master node just opening is _not_ filled | 
|---|
| 86 | *    out. This despite that with current drm device nodes are always bound to | 
|---|
| 87 | *    one device, and can't be runtime assigned like with drm 1.0. | 
|---|
| 88 | * 2. Match driver name. If it mismatches, proceed to the next device node. | 
|---|
| 89 | * 3. Call GET_UNIQUE, and check whether the unique name has length zero (by | 
|---|
| 90 | *    checking that the first byte in the string is 0). If that's not the case | 
|---|
| 91 | *    libdrm skips and proceeds to the next device node. Probably this is just | 
|---|
| 92 | *    copypasta from drm 1.0 times where a set unique name meant that the driver | 
|---|
| 93 | *    was in use already, but that's just conjecture. | 
|---|
| 94 | * | 
|---|
| 95 | * Long story short: To keep the open by name logic working, GET_UNIQUE must | 
|---|
| 96 | * _not_ return a unique string when SET_VERSION hasn't been called yet, | 
|---|
| 97 | * otherwise libdrm breaks. Even when that unique string can't ever change, and | 
|---|
| 98 | * is totally irrelevant for actually opening the device because runtime | 
|---|
| 99 | * assignable device instances were only support in drm 1.0, which is long dead. | 
|---|
| 100 | * But the libdrm code in drmOpenByName somehow survived, hence this can't be | 
|---|
| 101 | * broken. | 
|---|
| 102 | */ | 
|---|
| 103 |  | 
|---|
| 104 | /* | 
|---|
| 105 | * Get the bus id. | 
|---|
| 106 | * | 
|---|
| 107 | * \param inode device inode. | 
|---|
| 108 | * \param file_priv DRM file private. | 
|---|
| 109 | * \param cmd command. | 
|---|
| 110 | * \param arg user argument, pointing to a drm_unique structure. | 
|---|
| 111 | * \return zero on success or a negative number on failure. | 
|---|
| 112 | * | 
|---|
| 113 | * Copies the bus id from drm_device::unique into user space. | 
|---|
| 114 | */ | 
|---|
| 115 | int drm_getunique(struct drm_device *dev, void *data, | 
|---|
| 116 | struct drm_file *file_priv) | 
|---|
| 117 | { | 
|---|
| 118 | struct drm_unique *u = data; | 
|---|
| 119 | struct drm_master *master; | 
|---|
| 120 |  | 
|---|
| 121 | mutex_lock(lock: &dev->master_mutex); | 
|---|
| 122 | master = file_priv->master; | 
|---|
| 123 | if (u->unique_len >= master->unique_len) { | 
|---|
| 124 | if (copy_to_user(to: u->unique, from: master->unique, n: master->unique_len)) { | 
|---|
| 125 | mutex_unlock(lock: &dev->master_mutex); | 
|---|
| 126 | return -EFAULT; | 
|---|
| 127 | } | 
|---|
| 128 | } | 
|---|
| 129 | u->unique_len = master->unique_len; | 
|---|
| 130 | mutex_unlock(lock: &dev->master_mutex); | 
|---|
| 131 |  | 
|---|
| 132 | return 0; | 
|---|
| 133 | } | 
|---|
| 134 |  | 
|---|
| 135 | static void | 
|---|
| 136 | drm_unset_busid(struct drm_device *dev, | 
|---|
| 137 | struct drm_master *master) | 
|---|
| 138 | { | 
|---|
| 139 | kfree(objp: master->unique); | 
|---|
| 140 | master->unique = NULL; | 
|---|
| 141 | master->unique_len = 0; | 
|---|
| 142 | } | 
|---|
| 143 |  | 
|---|
| 144 | static int drm_set_busid(struct drm_device *dev, struct drm_file *file_priv) | 
|---|
| 145 | { | 
|---|
| 146 | struct drm_master *master = file_priv->master; | 
|---|
| 147 | int ret; | 
|---|
| 148 |  | 
|---|
| 149 | if (master->unique != NULL) | 
|---|
| 150 | drm_unset_busid(dev, master); | 
|---|
| 151 |  | 
|---|
| 152 | if (dev->dev && dev_is_pci(dev->dev)) { | 
|---|
| 153 | ret = drm_pci_set_busid(dev, master); | 
|---|
| 154 | if (ret) { | 
|---|
| 155 | drm_unset_busid(dev, master); | 
|---|
| 156 | return ret; | 
|---|
| 157 | } | 
|---|
| 158 | } else { | 
|---|
| 159 | WARN_ON(!dev->unique); | 
|---|
| 160 | master->unique = kstrdup(s: dev->unique, GFP_KERNEL); | 
|---|
| 161 | if (master->unique) | 
|---|
| 162 | master->unique_len = strlen(dev->unique); | 
|---|
| 163 | } | 
|---|
| 164 |  | 
|---|
| 165 | return 0; | 
|---|
| 166 | } | 
|---|
| 167 |  | 
|---|
| 168 | /* | 
|---|
| 169 | * Get client information. | 
|---|
| 170 | * | 
|---|
| 171 | * \param inode device inode. | 
|---|
| 172 | * \param file_priv DRM file private. | 
|---|
| 173 | * \param cmd command. | 
|---|
| 174 | * \param arg user argument, pointing to a drm_client structure. | 
|---|
| 175 | * | 
|---|
| 176 | * \return zero on success or a negative number on failure. | 
|---|
| 177 | * | 
|---|
| 178 | * Searches for the client with the specified index and copies its information | 
|---|
| 179 | * into userspace | 
|---|
| 180 | */ | 
|---|
| 181 | int drm_getclient(struct drm_device *dev, void *data, | 
|---|
| 182 | struct drm_file *file_priv) | 
|---|
| 183 | { | 
|---|
| 184 | struct drm_client *client = data; | 
|---|
| 185 |  | 
|---|
| 186 | /* | 
|---|
| 187 | * Hollowed-out getclient ioctl to keep some dead old drm tests/tools | 
|---|
| 188 | * not breaking completely. Userspace tools stop enumerating one they | 
|---|
| 189 | * get -EINVAL, hence this is the return value we need to hand back for | 
|---|
| 190 | * no clients tracked. | 
|---|
| 191 | * | 
|---|
| 192 | * Unfortunately some clients (*cough* libva *cough*) use this in a fun | 
|---|
| 193 | * attempt to figure out whether they're authenticated or not. Since | 
|---|
| 194 | * that's the only thing they care about, give it to the directly | 
|---|
| 195 | * instead of walking one giant list. | 
|---|
| 196 | */ | 
|---|
| 197 | if (client->idx == 0) { | 
|---|
| 198 | client->auth = file_priv->authenticated; | 
|---|
| 199 | client->pid = task_pid_vnr(current); | 
|---|
| 200 | client->uid = overflowuid; | 
|---|
| 201 | client->magic = 0; | 
|---|
| 202 | client->iocs = 0; | 
|---|
| 203 |  | 
|---|
| 204 | return 0; | 
|---|
| 205 | } else { | 
|---|
| 206 | return -EINVAL; | 
|---|
| 207 | } | 
|---|
| 208 | } | 
|---|
| 209 |  | 
|---|
| 210 | /* | 
|---|
| 211 | * Get statistics information. | 
|---|
| 212 | * | 
|---|
| 213 | * \param inode device inode. | 
|---|
| 214 | * \param file_priv DRM file private. | 
|---|
| 215 | * \param cmd command. | 
|---|
| 216 | * \param arg user argument, pointing to a drm_stats structure. | 
|---|
| 217 | * | 
|---|
| 218 | * \return zero on success or a negative number on failure. | 
|---|
| 219 | */ | 
|---|
| 220 | static int drm_getstats(struct drm_device *dev, void *data, | 
|---|
| 221 | struct drm_file *file_priv) | 
|---|
| 222 | { | 
|---|
| 223 | struct drm_stats *stats = data; | 
|---|
| 224 |  | 
|---|
| 225 | /* Clear stats to prevent userspace from eating its stack garbage. */ | 
|---|
| 226 | memset(s: stats, c: 0, n: sizeof(*stats)); | 
|---|
| 227 |  | 
|---|
| 228 | return 0; | 
|---|
| 229 | } | 
|---|
| 230 |  | 
|---|
| 231 | /* | 
|---|
| 232 | * Get device/driver capabilities | 
|---|
| 233 | */ | 
|---|
| 234 | static int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_priv) | 
|---|
| 235 | { | 
|---|
| 236 | struct drm_get_cap *req = data; | 
|---|
| 237 | struct drm_crtc *crtc; | 
|---|
| 238 |  | 
|---|
| 239 | req->value = 0; | 
|---|
| 240 |  | 
|---|
| 241 | /* Only some caps make sense with UMS/render-only drivers. */ | 
|---|
| 242 | switch (req->capability) { | 
|---|
| 243 | case DRM_CAP_TIMESTAMP_MONOTONIC: | 
|---|
| 244 | req->value = 1; | 
|---|
| 245 | return 0; | 
|---|
| 246 | case DRM_CAP_PRIME: | 
|---|
| 247 | req->value = DRM_PRIME_CAP_IMPORT | DRM_PRIME_CAP_EXPORT; | 
|---|
| 248 | return 0; | 
|---|
| 249 | case DRM_CAP_SYNCOBJ: | 
|---|
| 250 | req->value = drm_core_check_feature(dev, feature: DRIVER_SYNCOBJ); | 
|---|
| 251 | return 0; | 
|---|
| 252 | case DRM_CAP_SYNCOBJ_TIMELINE: | 
|---|
| 253 | req->value = drm_core_check_feature(dev, feature: DRIVER_SYNCOBJ_TIMELINE); | 
|---|
| 254 | return 0; | 
|---|
| 255 | } | 
|---|
| 256 |  | 
|---|
| 257 | /* Other caps only work with KMS drivers */ | 
|---|
| 258 | if (!drm_core_check_feature(dev, feature: DRIVER_MODESET)) | 
|---|
| 259 | return -EOPNOTSUPP; | 
|---|
| 260 |  | 
|---|
| 261 | switch (req->capability) { | 
|---|
| 262 | case DRM_CAP_DUMB_BUFFER: | 
|---|
| 263 | if (dev->driver->dumb_create) | 
|---|
| 264 | req->value = 1; | 
|---|
| 265 | break; | 
|---|
| 266 | case DRM_CAP_VBLANK_HIGH_CRTC: | 
|---|
| 267 | req->value = 1; | 
|---|
| 268 | break; | 
|---|
| 269 | case DRM_CAP_DUMB_PREFERRED_DEPTH: | 
|---|
| 270 | req->value = dev->mode_config.preferred_depth; | 
|---|
| 271 | break; | 
|---|
| 272 | case DRM_CAP_DUMB_PREFER_SHADOW: | 
|---|
| 273 | req->value = dev->mode_config.prefer_shadow; | 
|---|
| 274 | break; | 
|---|
| 275 | case DRM_CAP_ASYNC_PAGE_FLIP: | 
|---|
| 276 | req->value = dev->mode_config.async_page_flip; | 
|---|
| 277 | break; | 
|---|
| 278 | case DRM_CAP_PAGE_FLIP_TARGET: | 
|---|
| 279 | req->value = 1; | 
|---|
| 280 | drm_for_each_crtc(crtc, dev) { | 
|---|
| 281 | if (!crtc->funcs->page_flip_target) | 
|---|
| 282 | req->value = 0; | 
|---|
| 283 | } | 
|---|
| 284 | break; | 
|---|
| 285 | case DRM_CAP_CURSOR_WIDTH: | 
|---|
| 286 | if (dev->mode_config.cursor_width) | 
|---|
| 287 | req->value = dev->mode_config.cursor_width; | 
|---|
| 288 | else | 
|---|
| 289 | req->value = 64; | 
|---|
| 290 | break; | 
|---|
| 291 | case DRM_CAP_CURSOR_HEIGHT: | 
|---|
| 292 | if (dev->mode_config.cursor_height) | 
|---|
| 293 | req->value = dev->mode_config.cursor_height; | 
|---|
| 294 | else | 
|---|
| 295 | req->value = 64; | 
|---|
| 296 | break; | 
|---|
| 297 | case DRM_CAP_ADDFB2_MODIFIERS: | 
|---|
| 298 | req->value = !dev->mode_config.fb_modifiers_not_supported; | 
|---|
| 299 | break; | 
|---|
| 300 | case DRM_CAP_CRTC_IN_VBLANK_EVENT: | 
|---|
| 301 | req->value = 1; | 
|---|
| 302 | break; | 
|---|
| 303 | case DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP: | 
|---|
| 304 | req->value = drm_core_check_feature(dev, feature: DRIVER_ATOMIC) && | 
|---|
| 305 | dev->mode_config.async_page_flip; | 
|---|
| 306 | break; | 
|---|
| 307 | default: | 
|---|
| 308 | return -EINVAL; | 
|---|
| 309 | } | 
|---|
| 310 | return 0; | 
|---|
| 311 | } | 
|---|
| 312 |  | 
|---|
| 313 | /* | 
|---|
| 314 | * Set device/driver capabilities | 
|---|
| 315 | */ | 
|---|
| 316 | static int | 
|---|
| 317 | drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv) | 
|---|
| 318 | { | 
|---|
| 319 | struct drm_set_client_cap *req = data; | 
|---|
| 320 |  | 
|---|
| 321 | /* No render-only settable capabilities for now */ | 
|---|
| 322 |  | 
|---|
| 323 | /* Below caps that only works with KMS drivers */ | 
|---|
| 324 | if (!drm_core_check_feature(dev, feature: DRIVER_MODESET)) | 
|---|
| 325 | return -EOPNOTSUPP; | 
|---|
| 326 |  | 
|---|
| 327 | switch (req->capability) { | 
|---|
| 328 | case DRM_CLIENT_CAP_STEREO_3D: | 
|---|
| 329 | if (req->value > 1) | 
|---|
| 330 | return -EINVAL; | 
|---|
| 331 | file_priv->stereo_allowed = req->value; | 
|---|
| 332 | break; | 
|---|
| 333 | case DRM_CLIENT_CAP_UNIVERSAL_PLANES: | 
|---|
| 334 | if (req->value > 1) | 
|---|
| 335 | return -EINVAL; | 
|---|
| 336 | file_priv->universal_planes = req->value; | 
|---|
| 337 | break; | 
|---|
| 338 | case DRM_CLIENT_CAP_ATOMIC: | 
|---|
| 339 | if (!drm_core_check_feature(dev, feature: DRIVER_ATOMIC)) | 
|---|
| 340 | return -EOPNOTSUPP; | 
|---|
| 341 | /* The modesetting DDX has a totally broken idea of atomic. */ | 
|---|
| 342 | if (current->comm[0] == 'X' && req->value == 1) { | 
|---|
| 343 | pr_info( "broken atomic modeset userspace detected, disabling atomic\n"); | 
|---|
| 344 | return -EOPNOTSUPP; | 
|---|
| 345 | } | 
|---|
| 346 | if (req->value > 2) | 
|---|
| 347 | return -EINVAL; | 
|---|
| 348 | file_priv->atomic = req->value; | 
|---|
| 349 | file_priv->universal_planes = req->value; | 
|---|
| 350 | /* | 
|---|
| 351 | * No atomic user-space blows up on aspect ratio mode bits. | 
|---|
| 352 | */ | 
|---|
| 353 | file_priv->aspect_ratio_allowed = req->value; | 
|---|
| 354 | break; | 
|---|
| 355 | case DRM_CLIENT_CAP_ASPECT_RATIO: | 
|---|
| 356 | if (req->value > 1) | 
|---|
| 357 | return -EINVAL; | 
|---|
| 358 | file_priv->aspect_ratio_allowed = req->value; | 
|---|
| 359 | break; | 
|---|
| 360 | case DRM_CLIENT_CAP_WRITEBACK_CONNECTORS: | 
|---|
| 361 | if (!file_priv->atomic) | 
|---|
| 362 | return -EINVAL; | 
|---|
| 363 | if (req->value > 1) | 
|---|
| 364 | return -EINVAL; | 
|---|
| 365 | file_priv->writeback_connectors = req->value; | 
|---|
| 366 | break; | 
|---|
| 367 | case DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT: | 
|---|
| 368 | if (!drm_core_check_feature(dev, feature: DRIVER_CURSOR_HOTSPOT)) | 
|---|
| 369 | return -EOPNOTSUPP; | 
|---|
| 370 | if (!file_priv->atomic) | 
|---|
| 371 | return -EINVAL; | 
|---|
| 372 | if (req->value > 1) | 
|---|
| 373 | return -EINVAL; | 
|---|
| 374 | file_priv->supports_virtualized_cursor_plane = req->value; | 
|---|
| 375 | break; | 
|---|
| 376 | default: | 
|---|
| 377 | return -EINVAL; | 
|---|
| 378 | } | 
|---|
| 379 |  | 
|---|
| 380 | return 0; | 
|---|
| 381 | } | 
|---|
| 382 |  | 
|---|
| 383 | /* | 
|---|
| 384 | * Setversion ioctl. | 
|---|
| 385 | * | 
|---|
| 386 | * \param inode device inode. | 
|---|
| 387 | * \param file_priv DRM file private. | 
|---|
| 388 | * \param cmd command. | 
|---|
| 389 | * \param arg user argument, pointing to a drm_lock structure. | 
|---|
| 390 | * \return zero on success or negative number on failure. | 
|---|
| 391 | * | 
|---|
| 392 | * Sets the requested interface version | 
|---|
| 393 | */ | 
|---|
| 394 | static int drm_setversion(struct drm_device *dev, void *data, struct drm_file *file_priv) | 
|---|
| 395 | { | 
|---|
| 396 | struct drm_set_version *sv = data; | 
|---|
| 397 | int if_version, retcode = 0; | 
|---|
| 398 |  | 
|---|
| 399 | mutex_lock(lock: &dev->master_mutex); | 
|---|
| 400 | if (sv->drm_di_major != -1) { | 
|---|
| 401 | if (sv->drm_di_major != DRM_IF_MAJOR || | 
|---|
| 402 | sv->drm_di_minor < 0 || sv->drm_di_minor > DRM_IF_MINOR) { | 
|---|
| 403 | retcode = -EINVAL; | 
|---|
| 404 | goto done; | 
|---|
| 405 | } | 
|---|
| 406 | if_version = DRM_IF_VERSION(sv->drm_di_major, | 
|---|
| 407 | sv->drm_di_minor); | 
|---|
| 408 | dev->if_version = max(if_version, dev->if_version); | 
|---|
| 409 | if (sv->drm_di_minor >= 1) { | 
|---|
| 410 | /* | 
|---|
| 411 | * Version 1.1 includes tying of DRM to specific device | 
|---|
| 412 | * Version 1.4 has proper PCI domain support | 
|---|
| 413 | */ | 
|---|
| 414 | retcode = drm_set_busid(dev, file_priv); | 
|---|
| 415 | if (retcode) | 
|---|
| 416 | goto done; | 
|---|
| 417 | } | 
|---|
| 418 | } | 
|---|
| 419 |  | 
|---|
| 420 | if (sv->drm_dd_major != -1) { | 
|---|
| 421 | if (sv->drm_dd_major != dev->driver->major || | 
|---|
| 422 | sv->drm_dd_minor < 0 || sv->drm_dd_minor > | 
|---|
| 423 | dev->driver->minor) { | 
|---|
| 424 | retcode = -EINVAL; | 
|---|
| 425 | goto done; | 
|---|
| 426 | } | 
|---|
| 427 | } | 
|---|
| 428 |  | 
|---|
| 429 | done: | 
|---|
| 430 | sv->drm_di_major = DRM_IF_MAJOR; | 
|---|
| 431 | sv->drm_di_minor = DRM_IF_MINOR; | 
|---|
| 432 | sv->drm_dd_major = dev->driver->major; | 
|---|
| 433 | sv->drm_dd_minor = dev->driver->minor; | 
|---|
| 434 | mutex_unlock(lock: &dev->master_mutex); | 
|---|
| 435 |  | 
|---|
| 436 | return retcode; | 
|---|
| 437 | } | 
|---|
| 438 |  | 
|---|
| 439 | /** | 
|---|
| 440 | * drm_noop - DRM no-op ioctl implementation | 
|---|
| 441 | * @dev: DRM device for the ioctl | 
|---|
| 442 | * @data: data pointer for the ioctl | 
|---|
| 443 | * @file_priv: DRM file for the ioctl call | 
|---|
| 444 | * | 
|---|
| 445 | * This no-op implementation for drm ioctls is useful for deprecated | 
|---|
| 446 | * functionality where we can't return a failure code because existing userspace | 
|---|
| 447 | * checks the result of the ioctl, but doesn't care about the action. | 
|---|
| 448 | * | 
|---|
| 449 | * Always returns successfully with 0. | 
|---|
| 450 | */ | 
|---|
| 451 | int drm_noop(struct drm_device *dev, void *data, | 
|---|
| 452 | struct drm_file *file_priv) | 
|---|
| 453 | { | 
|---|
| 454 | drm_dbg_core(dev, "\n"); | 
|---|
| 455 | return 0; | 
|---|
| 456 | } | 
|---|
| 457 | EXPORT_SYMBOL(drm_noop); | 
|---|
| 458 |  | 
|---|
| 459 | /** | 
|---|
| 460 | * drm_invalid_op - DRM invalid ioctl implementation | 
|---|
| 461 | * @dev: DRM device for the ioctl | 
|---|
| 462 | * @data: data pointer for the ioctl | 
|---|
| 463 | * @file_priv: DRM file for the ioctl call | 
|---|
| 464 | * | 
|---|
| 465 | * This no-op implementation for drm ioctls is useful for deprecated | 
|---|
| 466 | * functionality where we really don't want to allow userspace to call the ioctl | 
|---|
| 467 | * any more. This is the case for old ums interfaces for drivers that | 
|---|
| 468 | * transitioned to kms gradually and so kept the old legacy tables around. This | 
|---|
| 469 | * only applies to radeon and i915 kms drivers, other drivers shouldn't need to | 
|---|
| 470 | * use this function. | 
|---|
| 471 | * | 
|---|
| 472 | * Always fails with a return value of -EINVAL. | 
|---|
| 473 | */ | 
|---|
| 474 | int drm_invalid_op(struct drm_device *dev, void *data, | 
|---|
| 475 | struct drm_file *file_priv) | 
|---|
| 476 | { | 
|---|
| 477 | return -EINVAL; | 
|---|
| 478 | } | 
|---|
| 479 | EXPORT_SYMBOL(drm_invalid_op); | 
|---|
| 480 |  | 
|---|
| 481 | /* | 
|---|
| 482 | * Copy and IOCTL return string to user space | 
|---|
| 483 | */ | 
|---|
| 484 | static int drm_copy_field(char __user *buf, size_t *buf_len, const char *value) | 
|---|
| 485 | { | 
|---|
| 486 | size_t len; | 
|---|
| 487 |  | 
|---|
| 488 | /* don't attempt to copy a NULL pointer */ | 
|---|
| 489 | if (WARN_ONCE(!value, "BUG: the value to copy was not set!")) { | 
|---|
| 490 | *buf_len = 0; | 
|---|
| 491 | return 0; | 
|---|
| 492 | } | 
|---|
| 493 |  | 
|---|
| 494 | /* don't overflow userbuf */ | 
|---|
| 495 | len = strlen(value); | 
|---|
| 496 | if (len > *buf_len) | 
|---|
| 497 | len = *buf_len; | 
|---|
| 498 |  | 
|---|
| 499 | /* let userspace know exact length of driver value (which could be | 
|---|
| 500 | * larger than the userspace-supplied buffer) */ | 
|---|
| 501 | *buf_len = strlen(value); | 
|---|
| 502 |  | 
|---|
| 503 | /* finally, try filling in the userbuf */ | 
|---|
| 504 | if (len && buf) | 
|---|
| 505 | if (copy_to_user(to: buf, from: value, n: len)) | 
|---|
| 506 | return -EFAULT; | 
|---|
| 507 | return 0; | 
|---|
| 508 | } | 
|---|
| 509 |  | 
|---|
| 510 | /* | 
|---|
| 511 | * Get version information | 
|---|
| 512 | * | 
|---|
| 513 | * \param inode device inode. | 
|---|
| 514 | * \param filp file pointer. | 
|---|
| 515 | * \param cmd command. | 
|---|
| 516 | * \param arg user argument, pointing to a drm_version structure. | 
|---|
| 517 | * \return zero on success or negative number on failure. | 
|---|
| 518 | * | 
|---|
| 519 | * Fills in the version information in \p arg. | 
|---|
| 520 | */ | 
|---|
| 521 | int drm_version(struct drm_device *dev, void *data, | 
|---|
| 522 | struct drm_file *file_priv) | 
|---|
| 523 | { | 
|---|
| 524 | struct drm_version *version = data; | 
|---|
| 525 | int err; | 
|---|
| 526 |  | 
|---|
| 527 | version->version_major = dev->driver->major; | 
|---|
| 528 | version->version_minor = dev->driver->minor; | 
|---|
| 529 | version->version_patchlevel = dev->driver->patchlevel; | 
|---|
| 530 | err = drm_copy_field(buf: version->name, buf_len: &version->name_len, | 
|---|
| 531 | value: dev->driver->name); | 
|---|
| 532 |  | 
|---|
| 533 | /* Driver date is deprecated. Userspace expects a non-empty string. */ | 
|---|
| 534 | if (!err) | 
|---|
| 535 | err = drm_copy_field(buf: version->date, buf_len: &version->date_len, value: "0"); | 
|---|
| 536 | if (!err) | 
|---|
| 537 | err = drm_copy_field(buf: version->desc, buf_len: &version->desc_len, | 
|---|
| 538 | value: dev->driver->desc); | 
|---|
| 539 |  | 
|---|
| 540 | return err; | 
|---|
| 541 | } | 
|---|
| 542 |  | 
|---|
| 543 | /* | 
|---|
| 544 | * Check if the passed string contains control char or spaces or | 
|---|
| 545 | * anything that would mess up a formatted output. | 
|---|
| 546 | */ | 
|---|
| 547 | static int drm_validate_value_string(const char *value, size_t len) | 
|---|
| 548 | { | 
|---|
| 549 | int i; | 
|---|
| 550 |  | 
|---|
| 551 | for (i = 0; i < len; i++) { | 
|---|
| 552 | if (!isascii(value[i]) || !isgraph(value[i])) | 
|---|
| 553 | return -EINVAL; | 
|---|
| 554 | } | 
|---|
| 555 | return 0; | 
|---|
| 556 | } | 
|---|
| 557 |  | 
|---|
| 558 | static int drm_set_client_name(struct drm_device *dev, void *data, | 
|---|
| 559 | struct drm_file *file_priv) | 
|---|
| 560 | { | 
|---|
| 561 | struct drm_set_client_name *name = data; | 
|---|
| 562 | size_t len = name->name_len; | 
|---|
| 563 | void __user *user_ptr; | 
|---|
| 564 | char *new_name; | 
|---|
| 565 |  | 
|---|
| 566 | if (len > DRM_CLIENT_NAME_MAX_LEN) { | 
|---|
| 567 | return -EINVAL; | 
|---|
| 568 | } else if (len) { | 
|---|
| 569 | user_ptr = u64_to_user_ptr(name->name); | 
|---|
| 570 |  | 
|---|
| 571 | new_name = memdup_user_nul(user_ptr, len); | 
|---|
| 572 | if (IS_ERR(ptr: new_name)) | 
|---|
| 573 | return PTR_ERR(ptr: new_name); | 
|---|
| 574 |  | 
|---|
| 575 | if (strlen(new_name) != len || | 
|---|
| 576 | drm_validate_value_string(value: new_name, len) < 0) { | 
|---|
| 577 | kfree(objp: new_name); | 
|---|
| 578 | return -EINVAL; | 
|---|
| 579 | } | 
|---|
| 580 | } else { | 
|---|
| 581 | new_name = NULL; | 
|---|
| 582 | } | 
|---|
| 583 |  | 
|---|
| 584 | mutex_lock(lock: &file_priv->client_name_lock); | 
|---|
| 585 | kfree(objp: file_priv->client_name); | 
|---|
| 586 | file_priv->client_name = new_name; | 
|---|
| 587 | mutex_unlock(lock: &file_priv->client_name_lock); | 
|---|
| 588 |  | 
|---|
| 589 | return 0; | 
|---|
| 590 | } | 
|---|
| 591 |  | 
|---|
| 592 | static int drm_ioctl_permit(u32 flags, struct drm_file *file_priv) | 
|---|
| 593 | { | 
|---|
| 594 | /* ROOT_ONLY is only for CAP_SYS_ADMIN */ | 
|---|
| 595 | if (unlikely((flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN))) | 
|---|
| 596 | return -EACCES; | 
|---|
| 597 |  | 
|---|
| 598 | /* AUTH is only for authenticated or render client */ | 
|---|
| 599 | if (unlikely((flags & DRM_AUTH) && !drm_is_render_client(file_priv) && | 
|---|
| 600 | !file_priv->authenticated)) | 
|---|
| 601 | return -EACCES; | 
|---|
| 602 |  | 
|---|
| 603 | /* MASTER is only for master or control clients */ | 
|---|
| 604 | if (unlikely((flags & DRM_MASTER) && | 
|---|
| 605 | !drm_is_current_master(file_priv))) | 
|---|
| 606 | return -EACCES; | 
|---|
| 607 |  | 
|---|
| 608 | /* Render clients must be explicitly allowed */ | 
|---|
| 609 | if (unlikely(!(flags & DRM_RENDER_ALLOW) && | 
|---|
| 610 | drm_is_render_client(file_priv))) | 
|---|
| 611 | return -EACCES; | 
|---|
| 612 |  | 
|---|
| 613 | return 0; | 
|---|
| 614 | } | 
|---|
| 615 |  | 
|---|
| 616 | #define DRM_IOCTL_DEF(ioctl, _func, _flags)	\ | 
|---|
| 617 | [DRM_IOCTL_NR(ioctl)] = {		\ | 
|---|
| 618 | .cmd = ioctl,			\ | 
|---|
| 619 | .func = _func,			\ | 
|---|
| 620 | .flags = _flags,		\ | 
|---|
| 621 | .name = #ioctl			\ | 
|---|
| 622 | } | 
|---|
| 623 |  | 
|---|
| 624 | /* Ioctl table */ | 
|---|
| 625 | static const struct drm_ioctl_desc drm_ioctls[] = { | 
|---|
| 626 | DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, DRM_RENDER_ALLOW), | 
|---|
| 627 | DRM_IOCTL_DEF(DRM_IOCTL_GET_UNIQUE, drm_getunique, 0), | 
|---|
| 628 | DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, 0), | 
|---|
| 629 |  | 
|---|
| 630 | DRM_IOCTL_DEF(DRM_IOCTL_GET_CLIENT, drm_getclient, 0), | 
|---|
| 631 | DRM_IOCTL_DEF(DRM_IOCTL_GET_STATS, drm_getstats, 0), | 
|---|
| 632 | DRM_IOCTL_DEF(DRM_IOCTL_GET_CAP, drm_getcap, DRM_RENDER_ALLOW), | 
|---|
| 633 | DRM_IOCTL_DEF(DRM_IOCTL_SET_CLIENT_CAP, drm_setclientcap, 0), | 
|---|
| 634 | DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_MASTER), | 
|---|
| 635 |  | 
|---|
| 636 | DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_invalid_op, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), | 
|---|
| 637 | DRM_IOCTL_DEF(DRM_IOCTL_BLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), | 
|---|
| 638 | DRM_IOCTL_DEF(DRM_IOCTL_UNBLOCK, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), | 
|---|
| 639 | DRM_IOCTL_DEF(DRM_IOCTL_AUTH_MAGIC, drm_authmagic, DRM_MASTER), | 
|---|
| 640 |  | 
|---|
| 641 | DRM_IOCTL_DEF(DRM_IOCTL_SET_MASTER, drm_setmaster_ioctl, 0), | 
|---|
| 642 | DRM_IOCTL_DEF(DRM_IOCTL_DROP_MASTER, drm_dropmaster_ioctl, 0), | 
|---|
| 643 |  | 
|---|
| 644 | DRM_IOCTL_DEF(DRM_IOCTL_ADD_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), | 
|---|
| 645 | DRM_IOCTL_DEF(DRM_IOCTL_RM_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), | 
|---|
| 646 |  | 
|---|
| 647 | DRM_IOCTL_DEF(DRM_IOCTL_FINISH, drm_noop, DRM_AUTH), | 
|---|
| 648 |  | 
|---|
| 649 | DRM_IOCTL_DEF(DRM_IOCTL_WAIT_VBLANK, drm_wait_vblank_ioctl, 0), | 
|---|
| 650 |  | 
|---|
| 651 | DRM_IOCTL_DEF(DRM_IOCTL_UPDATE_DRAW, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), | 
|---|
| 652 |  | 
|---|
| 653 | DRM_IOCTL_DEF(DRM_IOCTL_GEM_CLOSE, drm_gem_close_ioctl, DRM_RENDER_ALLOW), | 
|---|
| 654 | DRM_IOCTL_DEF(DRM_IOCTL_GEM_FLINK, drm_gem_flink_ioctl, DRM_AUTH), | 
|---|
| 655 | DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH), | 
|---|
| 656 | DRM_IOCTL_DEF(DRM_IOCTL_GEM_CHANGE_HANDLE, drm_gem_change_handle_ioctl, DRM_RENDER_ALLOW), | 
|---|
| 657 |  | 
|---|
| 658 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, 0), | 
|---|
| 659 |  | 
|---|
| 660 | DRM_IOCTL_DEF(DRM_IOCTL_PRIME_HANDLE_TO_FD, drm_prime_handle_to_fd_ioctl, DRM_RENDER_ALLOW), | 
|---|
| 661 | DRM_IOCTL_DEF(DRM_IOCTL_PRIME_FD_TO_HANDLE, drm_prime_fd_to_handle_ioctl, DRM_RENDER_ALLOW), | 
|---|
| 662 |  | 
|---|
| 663 | DRM_IOCTL_DEF(DRM_IOCTL_SET_CLIENT_NAME, drm_set_client_name, DRM_RENDER_ALLOW), | 
|---|
| 664 |  | 
|---|
| 665 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANERESOURCES, drm_mode_getplane_res, 0), | 
|---|
| 666 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, 0), | 
|---|
| 667 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER), | 
|---|
| 668 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANE, drm_mode_getplane, 0), | 
|---|
| 669 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPLANE, drm_mode_setplane, DRM_MASTER), | 
|---|
| 670 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER), | 
|---|
| 671 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, 0), | 
|---|
| 672 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETGAMMA, drm_mode_gamma_set_ioctl, DRM_MASTER), | 
|---|
| 673 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETENCODER, drm_mode_getencoder, 0), | 
|---|
| 674 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, 0), | 
|---|
| 675 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATTACHMODE, drm_noop, DRM_MASTER), | 
|---|
| 676 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_DETACHMODE, drm_noop, DRM_MASTER), | 
|---|
| 677 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, 0), | 
|---|
| 678 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPROPERTY, drm_connector_property_set_ioctl, DRM_MASTER), | 
|---|
| 679 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, 0), | 
|---|
| 680 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, 0), | 
|---|
| 681 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB2, drm_mode_getfb2_ioctl, 0), | 
|---|
| 682 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb_ioctl, 0), | 
|---|
| 683 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2_ioctl, 0), | 
|---|
| 684 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb_ioctl, 0), | 
|---|
| 685 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_CLOSEFB, drm_mode_closefb_ioctl, 0), | 
|---|
| 686 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER), | 
|---|
| 687 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER), | 
|---|
| 688 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, 0), | 
|---|
| 689 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_MAP_DUMB, drm_mode_mmap_dumb_ioctl, 0), | 
|---|
| 690 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, 0), | 
|---|
| 691 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, 0), | 
|---|
| 692 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTY, drm_mode_obj_set_property_ioctl, DRM_MASTER), | 
|---|
| 693 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR2, drm_mode_cursor2_ioctl, DRM_MASTER), | 
|---|
| 694 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATOMIC, drm_mode_atomic_ioctl, DRM_MASTER), | 
|---|
| 695 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATEPROPBLOB, drm_mode_createblob_ioctl, 0), | 
|---|
| 696 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROYPROPBLOB, drm_mode_destroyblob_ioctl, 0), | 
|---|
| 697 |  | 
|---|
| 698 | DRM_IOCTL_DEF(DRM_IOCTL_SYNCOBJ_CREATE, drm_syncobj_create_ioctl, | 
|---|
| 699 | DRM_RENDER_ALLOW), | 
|---|
| 700 | DRM_IOCTL_DEF(DRM_IOCTL_SYNCOBJ_DESTROY, drm_syncobj_destroy_ioctl, | 
|---|
| 701 | DRM_RENDER_ALLOW), | 
|---|
| 702 | DRM_IOCTL_DEF(DRM_IOCTL_SYNCOBJ_HANDLE_TO_FD, drm_syncobj_handle_to_fd_ioctl, | 
|---|
| 703 | DRM_RENDER_ALLOW), | 
|---|
| 704 | DRM_IOCTL_DEF(DRM_IOCTL_SYNCOBJ_FD_TO_HANDLE, drm_syncobj_fd_to_handle_ioctl, | 
|---|
| 705 | DRM_RENDER_ALLOW), | 
|---|
| 706 | DRM_IOCTL_DEF(DRM_IOCTL_SYNCOBJ_TRANSFER, drm_syncobj_transfer_ioctl, | 
|---|
| 707 | DRM_RENDER_ALLOW), | 
|---|
| 708 | DRM_IOCTL_DEF(DRM_IOCTL_SYNCOBJ_WAIT, drm_syncobj_wait_ioctl, | 
|---|
| 709 | DRM_RENDER_ALLOW), | 
|---|
| 710 | DRM_IOCTL_DEF(DRM_IOCTL_SYNCOBJ_TIMELINE_WAIT, drm_syncobj_timeline_wait_ioctl, | 
|---|
| 711 | DRM_RENDER_ALLOW), | 
|---|
| 712 | DRM_IOCTL_DEF(DRM_IOCTL_SYNCOBJ_EVENTFD, drm_syncobj_eventfd_ioctl, | 
|---|
| 713 | DRM_RENDER_ALLOW), | 
|---|
| 714 | DRM_IOCTL_DEF(DRM_IOCTL_SYNCOBJ_RESET, drm_syncobj_reset_ioctl, | 
|---|
| 715 | DRM_RENDER_ALLOW), | 
|---|
| 716 | DRM_IOCTL_DEF(DRM_IOCTL_SYNCOBJ_SIGNAL, drm_syncobj_signal_ioctl, | 
|---|
| 717 | DRM_RENDER_ALLOW), | 
|---|
| 718 | DRM_IOCTL_DEF(DRM_IOCTL_SYNCOBJ_TIMELINE_SIGNAL, drm_syncobj_timeline_signal_ioctl, | 
|---|
| 719 | DRM_RENDER_ALLOW), | 
|---|
| 720 | DRM_IOCTL_DEF(DRM_IOCTL_SYNCOBJ_QUERY, drm_syncobj_query_ioctl, | 
|---|
| 721 | DRM_RENDER_ALLOW), | 
|---|
| 722 | DRM_IOCTL_DEF(DRM_IOCTL_CRTC_GET_SEQUENCE, drm_crtc_get_sequence_ioctl, 0), | 
|---|
| 723 | DRM_IOCTL_DEF(DRM_IOCTL_CRTC_QUEUE_SEQUENCE, drm_crtc_queue_sequence_ioctl, 0), | 
|---|
| 724 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_LEASE, drm_mode_create_lease_ioctl, DRM_MASTER), | 
|---|
| 725 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_LIST_LESSEES, drm_mode_list_lessees_ioctl, DRM_MASTER), | 
|---|
| 726 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_GET_LEASE, drm_mode_get_lease_ioctl, DRM_MASTER), | 
|---|
| 727 | DRM_IOCTL_DEF(DRM_IOCTL_MODE_REVOKE_LEASE, drm_mode_revoke_lease_ioctl, DRM_MASTER), | 
|---|
| 728 | }; | 
|---|
| 729 |  | 
|---|
| 730 | #define DRM_CORE_IOCTL_COUNT	ARRAY_SIZE(drm_ioctls) | 
|---|
| 731 |  | 
|---|
| 732 | /** | 
|---|
| 733 | * DOC: driver specific ioctls | 
|---|
| 734 | * | 
|---|
| 735 | * First things first, driver private IOCTLs should only be needed for drivers | 
|---|
| 736 | * supporting rendering. Kernel modesetting is all standardized, and extended | 
|---|
| 737 | * through properties. There are a few exceptions in some existing drivers, | 
|---|
| 738 | * which define IOCTL for use by the display DRM master, but they all predate | 
|---|
| 739 | * properties. | 
|---|
| 740 | * | 
|---|
| 741 | * Now if you do have a render driver you always have to support it through | 
|---|
| 742 | * driver private properties. There's a few steps needed to wire all the things | 
|---|
| 743 | * up. | 
|---|
| 744 | * | 
|---|
| 745 | * First you need to define the structure for your IOCTL in your driver private | 
|---|
| 746 | * UAPI header in ``include/uapi/drm/my_driver_drm.h``:: | 
|---|
| 747 | * | 
|---|
| 748 | *     struct my_driver_operation { | 
|---|
| 749 | *             u32 some_thing; | 
|---|
| 750 | *             u32 another_thing; | 
|---|
| 751 | *     }; | 
|---|
| 752 | * | 
|---|
| 753 | * Please make sure that you follow all the best practices from | 
|---|
| 754 | * ``Documentation/process/botching-up-ioctls.rst``. Note that drm_ioctl() | 
|---|
| 755 | * automatically zero-extends structures, hence make sure you can add more stuff | 
|---|
| 756 | * at the end, i.e. don't put a variable sized array there. | 
|---|
| 757 | * | 
|---|
| 758 | * Then you need to define your IOCTL number, using one of DRM_IO(), DRM_IOR(), | 
|---|
| 759 | * DRM_IOW() or DRM_IOWR(). It must start with the DRM_IOCTL\_ prefix:: | 
|---|
| 760 | * | 
|---|
| 761 | *     ##define DRM_IOCTL_MY_DRIVER_OPERATION \ | 
|---|
| 762 | *         DRM_IOW(DRM_COMMAND_BASE, struct my_driver_operation) | 
|---|
| 763 | * | 
|---|
| 764 | * DRM driver private IOCTL must be in the range from DRM_COMMAND_BASE to | 
|---|
| 765 | * DRM_COMMAND_END. Finally you need an array of &struct drm_ioctl_desc to wire | 
|---|
| 766 | * up the handlers and set the access rights:: | 
|---|
| 767 | * | 
|---|
| 768 | *     static const struct drm_ioctl_desc my_driver_ioctls[] = { | 
|---|
| 769 | *         DRM_IOCTL_DEF_DRV(MY_DRIVER_OPERATION, my_driver_operation, | 
|---|
| 770 | *                 DRM_AUTH|DRM_RENDER_ALLOW), | 
|---|
| 771 | *     }; | 
|---|
| 772 | * | 
|---|
| 773 | * And then assign this to the &drm_driver.ioctls field in your driver | 
|---|
| 774 | * structure. | 
|---|
| 775 | * | 
|---|
| 776 | * See the separate chapter on :ref:`file operations<drm_driver_fops>` for how | 
|---|
| 777 | * the driver-specific IOCTLs are wired up. | 
|---|
| 778 | */ | 
|---|
| 779 |  | 
|---|
| 780 | long drm_ioctl_kernel(struct file *file, drm_ioctl_t *func, void *kdata, | 
|---|
| 781 | u32 flags) | 
|---|
| 782 | { | 
|---|
| 783 | struct drm_file *file_priv = file->private_data; | 
|---|
| 784 | struct drm_device *dev = file_priv->minor->dev; | 
|---|
| 785 | int ret; | 
|---|
| 786 |  | 
|---|
| 787 | /* Update drm_file owner if fd was passed along. */ | 
|---|
| 788 | drm_file_update_pid(file_priv); | 
|---|
| 789 |  | 
|---|
| 790 | if (drm_dev_is_unplugged(dev)) | 
|---|
| 791 | return -ENODEV; | 
|---|
| 792 |  | 
|---|
| 793 | ret = drm_ioctl_permit(flags, file_priv); | 
|---|
| 794 | if (unlikely(ret)) | 
|---|
| 795 | return ret; | 
|---|
| 796 |  | 
|---|
| 797 | return func(dev, kdata, file_priv); | 
|---|
| 798 | } | 
|---|
| 799 | EXPORT_SYMBOL(drm_ioctl_kernel); | 
|---|
| 800 |  | 
|---|
| 801 | /** | 
|---|
| 802 | * drm_ioctl - ioctl callback implementation for DRM drivers | 
|---|
| 803 | * @filp: file this ioctl is called on | 
|---|
| 804 | * @cmd: ioctl cmd number | 
|---|
| 805 | * @arg: user argument | 
|---|
| 806 | * | 
|---|
| 807 | * Looks up the ioctl function in the DRM core and the driver dispatch table, | 
|---|
| 808 | * stored in &drm_driver.ioctls. It checks for necessary permission by calling | 
|---|
| 809 | * drm_ioctl_permit(), and dispatches to the respective function. | 
|---|
| 810 | * | 
|---|
| 811 | * Returns: | 
|---|
| 812 | * Zero on success, negative error code on failure. | 
|---|
| 813 | */ | 
|---|
| 814 | long drm_ioctl(struct file *filp, | 
|---|
| 815 | unsigned int cmd, unsigned long arg) | 
|---|
| 816 | { | 
|---|
| 817 | struct drm_file *file_priv = filp->private_data; | 
|---|
| 818 | struct drm_device *dev; | 
|---|
| 819 | const struct drm_ioctl_desc *ioctl = NULL; | 
|---|
| 820 | drm_ioctl_t *func; | 
|---|
| 821 | unsigned int nr = DRM_IOCTL_NR(cmd); | 
|---|
| 822 | int retcode = -EINVAL; | 
|---|
| 823 | char stack_kdata[128]; | 
|---|
| 824 | char *kdata = NULL; | 
|---|
| 825 | unsigned int in_size, out_size, drv_size, ksize; | 
|---|
| 826 | bool is_driver_ioctl; | 
|---|
| 827 |  | 
|---|
| 828 | dev = file_priv->minor->dev; | 
|---|
| 829 |  | 
|---|
| 830 | if (drm_dev_is_unplugged(dev)) | 
|---|
| 831 | return -ENODEV; | 
|---|
| 832 |  | 
|---|
| 833 | if (DRM_IOCTL_TYPE(cmd) != DRM_IOCTL_BASE) | 
|---|
| 834 | return -ENOTTY; | 
|---|
| 835 |  | 
|---|
| 836 | is_driver_ioctl = nr >= DRM_COMMAND_BASE && nr < DRM_COMMAND_END; | 
|---|
| 837 |  | 
|---|
| 838 | if (is_driver_ioctl) { | 
|---|
| 839 | /* driver ioctl */ | 
|---|
| 840 | unsigned int index = nr - DRM_COMMAND_BASE; | 
|---|
| 841 |  | 
|---|
| 842 | if (index >= dev->driver->num_ioctls) | 
|---|
| 843 | goto err_i1; | 
|---|
| 844 | index = array_index_nospec(index, dev->driver->num_ioctls); | 
|---|
| 845 | ioctl = &dev->driver->ioctls[index]; | 
|---|
| 846 | } else { | 
|---|
| 847 | /* core ioctl */ | 
|---|
| 848 | if (nr >= DRM_CORE_IOCTL_COUNT) | 
|---|
| 849 | goto err_i1; | 
|---|
| 850 | nr = array_index_nospec(nr, DRM_CORE_IOCTL_COUNT); | 
|---|
| 851 | ioctl = &drm_ioctls[nr]; | 
|---|
| 852 | } | 
|---|
| 853 |  | 
|---|
| 854 | drv_size = _IOC_SIZE(ioctl->cmd); | 
|---|
| 855 | out_size = in_size = _IOC_SIZE(cmd); | 
|---|
| 856 | if ((cmd & ioctl->cmd & IOC_IN) == 0) | 
|---|
| 857 | in_size = 0; | 
|---|
| 858 | if ((cmd & ioctl->cmd & IOC_OUT) == 0) | 
|---|
| 859 | out_size = 0; | 
|---|
| 860 | ksize = max(max(in_size, out_size), drv_size); | 
|---|
| 861 |  | 
|---|
| 862 | drm_dbg_core(dev, "comm=\"%s\" pid=%d, dev=0x%lx, auth=%d, %s\n", | 
|---|
| 863 | current->comm, task_pid_nr(current), | 
|---|
| 864 | (long)old_encode_dev(file_priv->minor->kdev->devt), | 
|---|
| 865 | file_priv->authenticated, ioctl->name); | 
|---|
| 866 |  | 
|---|
| 867 | /* Do not trust userspace, use our own definition */ | 
|---|
| 868 | func = ioctl->func; | 
|---|
| 869 |  | 
|---|
| 870 | if (unlikely(!func)) { | 
|---|
| 871 | drm_dbg_core(dev, "no function\n"); | 
|---|
| 872 | retcode = -EINVAL; | 
|---|
| 873 | goto err_i1; | 
|---|
| 874 | } | 
|---|
| 875 |  | 
|---|
| 876 | if (ksize <= sizeof(stack_kdata)) { | 
|---|
| 877 | kdata = stack_kdata; | 
|---|
| 878 | } else { | 
|---|
| 879 | kdata = kmalloc(ksize, GFP_KERNEL); | 
|---|
| 880 | if (!kdata) { | 
|---|
| 881 | retcode = -ENOMEM; | 
|---|
| 882 | goto err_i1; | 
|---|
| 883 | } | 
|---|
| 884 | } | 
|---|
| 885 |  | 
|---|
| 886 | if (copy_from_user(to: kdata, from: (void __user *)arg, n: in_size) != 0) { | 
|---|
| 887 | retcode = -EFAULT; | 
|---|
| 888 | goto err_i1; | 
|---|
| 889 | } | 
|---|
| 890 |  | 
|---|
| 891 | if (ksize > in_size) | 
|---|
| 892 | memset(s: kdata + in_size, c: 0, n: ksize - in_size); | 
|---|
| 893 |  | 
|---|
| 894 | retcode = drm_ioctl_kernel(filp, func, kdata, ioctl->flags); | 
|---|
| 895 | if (copy_to_user(to: (void __user *)arg, from: kdata, n: out_size) != 0) | 
|---|
| 896 | retcode = -EFAULT; | 
|---|
| 897 |  | 
|---|
| 898 | err_i1: | 
|---|
| 899 | if (!ioctl) | 
|---|
| 900 | drm_dbg_core(dev, | 
|---|
| 901 | "invalid ioctl: comm=\"%s\", pid=%d, dev=0x%lx, auth=%d, cmd=0x%02x, nr=0x%02x\n", | 
|---|
| 902 | current->comm, task_pid_nr(current), | 
|---|
| 903 | (long)old_encode_dev(file_priv->minor->kdev->devt), | 
|---|
| 904 | file_priv->authenticated, cmd, nr); | 
|---|
| 905 |  | 
|---|
| 906 | if (kdata != stack_kdata) | 
|---|
| 907 | kfree(objp: kdata); | 
|---|
| 908 | if (retcode) | 
|---|
| 909 | drm_dbg_core(dev, "comm=\"%s\", pid=%d, ret=%d\n", | 
|---|
| 910 | current->comm, task_pid_nr(current), retcode); | 
|---|
| 911 | return retcode; | 
|---|
| 912 | } | 
|---|
| 913 | EXPORT_SYMBOL(drm_ioctl); | 
|---|
| 914 |  | 
|---|
| 915 | /** | 
|---|
| 916 | * drm_ioctl_flags - Check for core ioctl and return ioctl permission flags | 
|---|
| 917 | * @nr: ioctl number | 
|---|
| 918 | * @flags: where to return the ioctl permission flags | 
|---|
| 919 | * | 
|---|
| 920 | * This ioctl is only used by the vmwgfx driver to augment the access checks | 
|---|
| 921 | * done by the drm core and insofar a pretty decent layering violation. This | 
|---|
| 922 | * shouldn't be used by any drivers. | 
|---|
| 923 | * | 
|---|
| 924 | * Returns: | 
|---|
| 925 | * True if the @nr corresponds to a DRM core ioctl number, false otherwise. | 
|---|
| 926 | */ | 
|---|
| 927 | bool drm_ioctl_flags(unsigned int nr, unsigned int *flags) | 
|---|
| 928 | { | 
|---|
| 929 | if (nr >= DRM_COMMAND_BASE && nr < DRM_COMMAND_END) | 
|---|
| 930 | return false; | 
|---|
| 931 |  | 
|---|
| 932 | if (nr >= DRM_CORE_IOCTL_COUNT) | 
|---|
| 933 | return false; | 
|---|
| 934 | nr = array_index_nospec(nr, DRM_CORE_IOCTL_COUNT); | 
|---|
| 935 |  | 
|---|
| 936 | *flags = drm_ioctls[nr].flags; | 
|---|
| 937 | return true; | 
|---|
| 938 | } | 
|---|
| 939 | EXPORT_SYMBOL(drm_ioctl_flags); | 
|---|
| 940 |  | 
|---|