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 (fb_WARN_ON_ONCE(info, !info->fbops->fb_read)) 38 return -EINVAL; 39 40 if (info->state != FBINFO_STATE_RUNNING) 41 return -EPERM; 42 43 return info->fbops->fb_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 (fb_WARN_ON_ONCE(info, !info->fbops->fb_write)) 54 return -EINVAL; 55 56 if (info->state != FBINFO_STATE_RUNNING) 57 return -EPERM; 58 59 return info->fbops->fb_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 int res; 318 319 if (!info) 320 return -ENODEV; 321 322 if (fb_WARN_ON_ONCE(info, !info->fbops->fb_mmap)) 323 return -ENODEV; 324 325 mutex_lock(&info->mm_lock); 326 res = info->fbops->fb_mmap(info, vma); 327 mutex_unlock(&info->mm_lock); 328 329 return res; 330 } 331 332 static int fb_open(struct inode *inode, struct file *file) 333 __acquires(&info->lock) 334 __releases(&info->lock) 335 { 336 int fbidx = iminor(inode); 337 struct fb_info *info; 338 int res = 0; 339 340 info = get_fb_info(fbidx); 341 if (!info) { 342 request_module("fb%d", fbidx); 343 info = get_fb_info(fbidx); 344 if (!info) 345 return -ENODEV; 346 } 347 if (IS_ERR(info)) 348 return PTR_ERR(info); 349 350 lock_fb_info(info); 351 if (!try_module_get(info->fbops->owner)) { 352 res = -ENODEV; 353 goto out; 354 } 355 file->private_data = info; 356 if (info->fbops->fb_open) { 357 res = info->fbops->fb_open(info, 1); 358 if (res) 359 module_put(info->fbops->owner); 360 } 361 #ifdef CONFIG_FB_DEFERRED_IO 362 if (info->fbdefio) 363 fb_deferred_io_open(info, inode, file); 364 #endif 365 out: 366 unlock_fb_info(info); 367 if (res) 368 put_fb_info(info); 369 return res; 370 } 371 372 static int fb_release(struct inode *inode, struct file *file) 373 __acquires(&info->lock) 374 __releases(&info->lock) 375 { 376 struct fb_info * const info = file->private_data; 377 378 lock_fb_info(info); 379 #if IS_ENABLED(CONFIG_FB_DEFERRED_IO) 380 if (info->fbdefio) 381 fb_deferred_io_release(info); 382 #endif 383 if (info->fbops->fb_release) 384 info->fbops->fb_release(info, 1); 385 module_put(info->fbops->owner); 386 unlock_fb_info(info); 387 put_fb_info(info); 388 return 0; 389 } 390 391 #if defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && !defined(CONFIG_MMU) 392 static unsigned long get_fb_unmapped_area(struct file *filp, 393 unsigned long addr, unsigned long len, 394 unsigned long pgoff, unsigned long flags) 395 { 396 struct fb_info * const info = filp->private_data; 397 unsigned long fb_size = PAGE_ALIGN(info->fix.smem_len); 398 399 if (pgoff > fb_size || len > fb_size - pgoff) 400 return -EINVAL; 401 402 return (unsigned long)info->screen_base + pgoff; 403 } 404 #endif 405 406 static const struct file_operations fb_fops = { 407 .owner = THIS_MODULE, 408 .read = fb_read, 409 .write = fb_write, 410 .unlocked_ioctl = fb_ioctl, 411 #ifdef CONFIG_COMPAT 412 .compat_ioctl = fb_compat_ioctl, 413 #endif 414 .mmap = fb_mmap, 415 .open = fb_open, 416 .release = fb_release, 417 #if defined(HAVE_ARCH_FB_UNMAPPED_AREA) || \ 418 (defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && \ 419 !defined(CONFIG_MMU)) 420 .get_unmapped_area = get_fb_unmapped_area, 421 #endif 422 #ifdef CONFIG_FB_DEFERRED_IO 423 .fsync = fb_deferred_io_fsync, 424 #endif 425 .llseek = default_llseek, 426 }; 427 428 int fb_register_chrdev(void) 429 { 430 int ret; 431 432 ret = register_chrdev(FB_MAJOR, "fb", &fb_fops); 433 if (ret) { 434 pr_err("Unable to get major %d for fb devs\n", FB_MAJOR); 435 return ret; 436 } 437 438 return ret; 439 } 440 441 void fb_unregister_chrdev(void) 442 { 443 unregister_chrdev(FB_MAJOR, "fb"); 444 } 445