1 // SPDX-License-Identifier: GPL-2.0 2 3 #include <linux/compat.h> 4 #include <linux/console.h> 5 #include <linux/fb.h> 6 #include <linux/fbcon.h> 7 #include <linux/major.h> 8 9 #include "fb_internal.h" 10 11 /* 12 * We hold a reference to the fb_info in file->private_data, 13 * but if the current registered fb has changed, we don't 14 * actually want to use it. 15 * 16 * So look up the fb_info using the inode minor number, 17 * and just verify it against the reference we have. 18 */ 19 static struct fb_info *file_fb_info(struct file *file) 20 { 21 struct inode *inode = file_inode(file); 22 int fbidx = iminor(inode); 23 struct fb_info *info = registered_fb[fbidx]; 24 25 if (info != file->private_data) 26 info = NULL; 27 return info; 28 } 29 30 static ssize_t fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) 31 { 32 struct fb_info *info = file_fb_info(file); 33 34 if (!info) 35 return -ENODEV; 36 37 if (info->state != FBINFO_STATE_RUNNING) 38 return -EPERM; 39 40 if (info->fbops->fb_read) 41 return info->fbops->fb_read(info, buf, count, ppos); 42 43 return fb_io_read(info, buf, count, ppos); 44 } 45 46 static ssize_t fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) 47 { 48 struct fb_info *info = file_fb_info(file); 49 50 if (!info) 51 return -ENODEV; 52 53 if (info->state != FBINFO_STATE_RUNNING) 54 return -EPERM; 55 56 if (info->fbops->fb_write) 57 return info->fbops->fb_write(info, buf, count, ppos); 58 59 return fb_io_write(info, buf, count, ppos); 60 } 61 62 static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, 63 unsigned long arg) 64 { 65 const struct fb_ops *fb; 66 struct fb_var_screeninfo var; 67 struct fb_fix_screeninfo fix; 68 struct fb_cmap cmap_from; 69 struct fb_cmap_user cmap; 70 void __user *argp = (void __user *)arg; 71 long ret = 0; 72 73 switch (cmd) { 74 case FBIOGET_VSCREENINFO: 75 lock_fb_info(info); 76 var = info->var; 77 unlock_fb_info(info); 78 79 ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0; 80 break; 81 case FBIOPUT_VSCREENINFO: 82 if (copy_from_user(&var, argp, sizeof(var))) 83 return -EFAULT; 84 /* only for kernel-internal use */ 85 var.activate &= ~FB_ACTIVATE_KD_TEXT; 86 console_lock(); 87 lock_fb_info(info); 88 ret = fbcon_modechange_possible(info, &var); 89 if (!ret) 90 ret = fb_set_var(info, &var); 91 if (!ret) 92 fbcon_update_vcs(info, var.activate & FB_ACTIVATE_ALL); 93 unlock_fb_info(info); 94 console_unlock(); 95 if (!ret && copy_to_user(argp, &var, sizeof(var))) 96 ret = -EFAULT; 97 break; 98 case FBIOGET_FSCREENINFO: 99 lock_fb_info(info); 100 memcpy(&fix, &info->fix, sizeof(fix)); 101 if (info->flags & FBINFO_HIDE_SMEM_START) 102 fix.smem_start = 0; 103 unlock_fb_info(info); 104 105 ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0; 106 break; 107 case FBIOPUTCMAP: 108 if (copy_from_user(&cmap, argp, sizeof(cmap))) 109 return -EFAULT; 110 ret = fb_set_user_cmap(&cmap, info); 111 break; 112 case FBIOGETCMAP: 113 if (copy_from_user(&cmap, argp, sizeof(cmap))) 114 return -EFAULT; 115 lock_fb_info(info); 116 cmap_from = info->cmap; 117 unlock_fb_info(info); 118 ret = fb_cmap_to_user(&cmap_from, &cmap); 119 break; 120 case FBIOPAN_DISPLAY: 121 if (copy_from_user(&var, argp, sizeof(var))) 122 return -EFAULT; 123 console_lock(); 124 lock_fb_info(info); 125 ret = fb_pan_display(info, &var); 126 unlock_fb_info(info); 127 console_unlock(); 128 if (ret == 0 && copy_to_user(argp, &var, sizeof(var))) 129 return -EFAULT; 130 break; 131 case FBIO_CURSOR: 132 ret = -EINVAL; 133 break; 134 case FBIOGET_CON2FBMAP: 135 ret = fbcon_get_con2fb_map_ioctl(argp); 136 break; 137 case FBIOPUT_CON2FBMAP: 138 ret = fbcon_set_con2fb_map_ioctl(argp); 139 break; 140 case FBIOBLANK: 141 if (arg > FB_BLANK_POWERDOWN) 142 return -EINVAL; 143 console_lock(); 144 lock_fb_info(info); 145 ret = fb_blank(info, arg); 146 /* might again call into fb_blank */ 147 fbcon_fb_blanked(info, arg); 148 unlock_fb_info(info); 149 console_unlock(); 150 break; 151 default: 152 lock_fb_info(info); 153 fb = info->fbops; 154 if (fb->fb_ioctl) 155 ret = fb->fb_ioctl(info, cmd, arg); 156 else 157 ret = -ENOTTY; 158 unlock_fb_info(info); 159 } 160 return ret; 161 } 162 163 static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 164 { 165 struct fb_info *info = file_fb_info(file); 166 167 if (!info) 168 return -ENODEV; 169 return do_fb_ioctl(info, cmd, arg); 170 } 171 172 #ifdef CONFIG_COMPAT 173 struct fb_fix_screeninfo32 { 174 char id[16]; 175 compat_caddr_t smem_start; 176 u32 smem_len; 177 u32 type; 178 u32 type_aux; 179 u32 visual; 180 u16 xpanstep; 181 u16 ypanstep; 182 u16 ywrapstep; 183 u32 line_length; 184 compat_caddr_t mmio_start; 185 u32 mmio_len; 186 u32 accel; 187 u16 reserved[3]; 188 }; 189 190 struct fb_cmap32 { 191 u32 start; 192 u32 len; 193 compat_caddr_t red; 194 compat_caddr_t green; 195 compat_caddr_t blue; 196 compat_caddr_t transp; 197 }; 198 199 static int fb_getput_cmap(struct fb_info *info, unsigned int cmd, 200 unsigned long arg) 201 { 202 struct fb_cmap32 cmap32; 203 struct fb_cmap cmap_from; 204 struct fb_cmap_user cmap; 205 206 if (copy_from_user(&cmap32, compat_ptr(arg), sizeof(cmap32))) 207 return -EFAULT; 208 209 cmap = (struct fb_cmap_user) { 210 .start = cmap32.start, 211 .len = cmap32.len, 212 .red = compat_ptr(cmap32.red), 213 .green = compat_ptr(cmap32.green), 214 .blue = compat_ptr(cmap32.blue), 215 .transp = compat_ptr(cmap32.transp), 216 }; 217 218 if (cmd == FBIOPUTCMAP) 219 return fb_set_user_cmap(&cmap, info); 220 221 lock_fb_info(info); 222 cmap_from = info->cmap; 223 unlock_fb_info(info); 224 225 return fb_cmap_to_user(&cmap_from, &cmap); 226 } 227 228 static int do_fscreeninfo_to_user(struct fb_fix_screeninfo *fix, 229 struct fb_fix_screeninfo32 __user *fix32) 230 { 231 __u32 data; 232 int err; 233 234 err = copy_to_user(&fix32->id, &fix->id, sizeof(fix32->id)); 235 236 data = (__u32) (unsigned long) fix->smem_start; 237 err |= put_user(data, &fix32->smem_start); 238 239 err |= put_user(fix->smem_len, &fix32->smem_len); 240 err |= put_user(fix->type, &fix32->type); 241 err |= put_user(fix->type_aux, &fix32->type_aux); 242 err |= put_user(fix->visual, &fix32->visual); 243 err |= put_user(fix->xpanstep, &fix32->xpanstep); 244 err |= put_user(fix->ypanstep, &fix32->ypanstep); 245 err |= put_user(fix->ywrapstep, &fix32->ywrapstep); 246 err |= put_user(fix->line_length, &fix32->line_length); 247 248 data = (__u32) (unsigned long) fix->mmio_start; 249 err |= put_user(data, &fix32->mmio_start); 250 251 err |= put_user(fix->mmio_len, &fix32->mmio_len); 252 err |= put_user(fix->accel, &fix32->accel); 253 err |= copy_to_user(fix32->reserved, fix->reserved, 254 sizeof(fix->reserved)); 255 256 if (err) 257 return -EFAULT; 258 return 0; 259 } 260 261 static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd, 262 unsigned long arg) 263 { 264 struct fb_fix_screeninfo fix; 265 266 lock_fb_info(info); 267 fix = info->fix; 268 if (info->flags & FBINFO_HIDE_SMEM_START) 269 fix.smem_start = 0; 270 unlock_fb_info(info); 271 return do_fscreeninfo_to_user(&fix, compat_ptr(arg)); 272 } 273 274 static long fb_compat_ioctl(struct file *file, unsigned int cmd, 275 unsigned long arg) 276 { 277 struct fb_info *info = file_fb_info(file); 278 const struct fb_ops *fb; 279 long ret = -ENOIOCTLCMD; 280 281 if (!info) 282 return -ENODEV; 283 fb = info->fbops; 284 switch (cmd) { 285 case FBIOGET_VSCREENINFO: 286 case FBIOPUT_VSCREENINFO: 287 case FBIOPAN_DISPLAY: 288 case FBIOGET_CON2FBMAP: 289 case FBIOPUT_CON2FBMAP: 290 arg = (unsigned long) compat_ptr(arg); 291 fallthrough; 292 case FBIOBLANK: 293 ret = do_fb_ioctl(info, cmd, arg); 294 break; 295 296 case FBIOGET_FSCREENINFO: 297 ret = fb_get_fscreeninfo(info, cmd, arg); 298 break; 299 300 case FBIOGETCMAP: 301 case FBIOPUTCMAP: 302 ret = fb_getput_cmap(info, cmd, arg); 303 break; 304 305 default: 306 if (fb->fb_compat_ioctl) 307 ret = fb->fb_compat_ioctl(info, cmd, arg); 308 break; 309 } 310 return ret; 311 } 312 #endif 313 314 static int fb_mmap(struct file *file, struct vm_area_struct *vma) 315 { 316 struct fb_info *info = file_fb_info(file); 317 unsigned long mmio_pgoff; 318 unsigned long start; 319 u32 len; 320 321 if (!info) 322 return -ENODEV; 323 mutex_lock(&info->mm_lock); 324 325 if (info->fbops->fb_mmap) { 326 int res; 327 328 /* 329 * The framebuffer needs to be accessed decrypted, be sure 330 * SME protection is removed ahead of the call 331 */ 332 vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot); 333 res = info->fbops->fb_mmap(info, vma); 334 mutex_unlock(&info->mm_lock); 335 return res; 336 #if IS_ENABLED(CONFIG_FB_DEFERRED_IO) 337 } else if (info->fbdefio) { 338 /* 339 * FB deferred I/O wants you to handle mmap in your drivers. At a 340 * minimum, point struct fb_ops.fb_mmap to fb_deferred_io_mmap(). 341 */ 342 dev_warn_once(info->dev, "fbdev mmap not set up for deferred I/O.\n"); 343 mutex_unlock(&info->mm_lock); 344 return -ENODEV; 345 #endif 346 } 347 348 /* 349 * Ugh. This can be either the frame buffer mapping, or 350 * if pgoff points past it, the mmio mapping. 351 */ 352 start = info->fix.smem_start; 353 len = info->fix.smem_len; 354 mmio_pgoff = PAGE_ALIGN((start & ~PAGE_MASK) + len) >> PAGE_SHIFT; 355 if (vma->vm_pgoff >= mmio_pgoff) { 356 if (info->var.accel_flags) { 357 mutex_unlock(&info->mm_lock); 358 return -EINVAL; 359 } 360 361 vma->vm_pgoff -= mmio_pgoff; 362 start = info->fix.mmio_start; 363 len = info->fix.mmio_len; 364 } 365 mutex_unlock(&info->mm_lock); 366 367 vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); 368 fb_pgprotect(file, vma, start); 369 370 return vm_iomap_memory(vma, start, len); 371 } 372 373 static int fb_open(struct inode *inode, struct file *file) 374 __acquires(&info->lock) 375 __releases(&info->lock) 376 { 377 int fbidx = iminor(inode); 378 struct fb_info *info; 379 int res = 0; 380 381 info = get_fb_info(fbidx); 382 if (!info) { 383 request_module("fb%d", fbidx); 384 info = get_fb_info(fbidx); 385 if (!info) 386 return -ENODEV; 387 } 388 if (IS_ERR(info)) 389 return PTR_ERR(info); 390 391 lock_fb_info(info); 392 if (!try_module_get(info->fbops->owner)) { 393 res = -ENODEV; 394 goto out; 395 } 396 file->private_data = info; 397 if (info->fbops->fb_open) { 398 res = info->fbops->fb_open(info, 1); 399 if (res) 400 module_put(info->fbops->owner); 401 } 402 #ifdef CONFIG_FB_DEFERRED_IO 403 if (info->fbdefio) 404 fb_deferred_io_open(info, inode, file); 405 #endif 406 out: 407 unlock_fb_info(info); 408 if (res) 409 put_fb_info(info); 410 return res; 411 } 412 413 static int fb_release(struct inode *inode, struct file *file) 414 __acquires(&info->lock) 415 __releases(&info->lock) 416 { 417 struct fb_info * const info = file->private_data; 418 419 lock_fb_info(info); 420 #if IS_ENABLED(CONFIG_FB_DEFERRED_IO) 421 if (info->fbdefio) 422 fb_deferred_io_release(info); 423 #endif 424 if (info->fbops->fb_release) 425 info->fbops->fb_release(info, 1); 426 module_put(info->fbops->owner); 427 unlock_fb_info(info); 428 put_fb_info(info); 429 return 0; 430 } 431 432 #if defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && !defined(CONFIG_MMU) 433 static unsigned long get_fb_unmapped_area(struct file *filp, 434 unsigned long addr, unsigned long len, 435 unsigned long pgoff, unsigned long flags) 436 { 437 struct fb_info * const info = filp->private_data; 438 unsigned long fb_size = PAGE_ALIGN(info->fix.smem_len); 439 440 if (pgoff > fb_size || len > fb_size - pgoff) 441 return -EINVAL; 442 443 return (unsigned long)info->screen_base + pgoff; 444 } 445 #endif 446 447 static const struct file_operations fb_fops = { 448 .owner = THIS_MODULE, 449 .read = fb_read, 450 .write = fb_write, 451 .unlocked_ioctl = fb_ioctl, 452 #ifdef CONFIG_COMPAT 453 .compat_ioctl = fb_compat_ioctl, 454 #endif 455 .mmap = fb_mmap, 456 .open = fb_open, 457 .release = fb_release, 458 #if defined(HAVE_ARCH_FB_UNMAPPED_AREA) || \ 459 (defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && \ 460 !defined(CONFIG_MMU)) 461 .get_unmapped_area = get_fb_unmapped_area, 462 #endif 463 #ifdef CONFIG_FB_DEFERRED_IO 464 .fsync = fb_deferred_io_fsync, 465 #endif 466 .llseek = default_llseek, 467 }; 468 469 int fb_register_chrdev(void) 470 { 471 int ret; 472 473 ret = register_chrdev(FB_MAJOR, "fb", &fb_fops); 474 if (ret) { 475 pr_err("Unable to get major %d for fb devs\n", FB_MAJOR); 476 return ret; 477 } 478 479 return ret; 480 } 481 482 void fb_unregister_chrdev(void) 483 { 484 unregister_chrdev(FB_MAJOR, "fb"); 485 } 486