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/major.h> 7 8 #include "fb_internal.h" 9 #include "fbcon.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 = fb_set_var_from_user(info, &var); 89 unlock_fb_info(info); 90 console_unlock(); 91 if (!ret && copy_to_user(argp, &var, sizeof(var))) 92 ret = -EFAULT; 93 break; 94 case FBIOGET_FSCREENINFO: 95 lock_fb_info(info); 96 memcpy(&fix, &info->fix, sizeof(fix)); 97 if (info->flags & FBINFO_HIDE_SMEM_START) 98 fix.smem_start = 0; 99 unlock_fb_info(info); 100 101 ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0; 102 break; 103 case FBIOPUTCMAP: 104 if (copy_from_user(&cmap, argp, sizeof(cmap))) 105 return -EFAULT; 106 ret = fb_set_user_cmap(&cmap, info); 107 break; 108 case FBIOGETCMAP: 109 if (copy_from_user(&cmap, argp, sizeof(cmap))) 110 return -EFAULT; 111 lock_fb_info(info); 112 cmap_from = info->cmap; 113 unlock_fb_info(info); 114 ret = fb_cmap_to_user(&cmap_from, &cmap); 115 break; 116 case FBIOPAN_DISPLAY: 117 if (copy_from_user(&var, argp, sizeof(var))) 118 return -EFAULT; 119 console_lock(); 120 lock_fb_info(info); 121 ret = fb_pan_display(info, &var); 122 unlock_fb_info(info); 123 console_unlock(); 124 if (ret == 0 && copy_to_user(argp, &var, sizeof(var))) 125 return -EFAULT; 126 break; 127 case FBIO_CURSOR: 128 ret = -EINVAL; 129 break; 130 case FBIOGET_CON2FBMAP: 131 ret = fbcon_get_con2fb_map_ioctl(argp); 132 break; 133 case FBIOPUT_CON2FBMAP: 134 ret = fbcon_set_con2fb_map_ioctl(argp); 135 break; 136 case FBIOBLANK: 137 if (arg > FB_BLANK_POWERDOWN) 138 return -EINVAL; 139 console_lock(); 140 lock_fb_info(info); 141 ret = fb_blank_from_user(info, arg); 142 unlock_fb_info(info); 143 console_unlock(); 144 break; 145 default: 146 lock_fb_info(info); 147 fb = info->fbops; 148 if (fb->fb_ioctl) 149 ret = fb->fb_ioctl(info, cmd, arg); 150 else 151 ret = -ENOTTY; 152 unlock_fb_info(info); 153 } 154 return ret; 155 } 156 157 static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 158 { 159 struct fb_info *info = file_fb_info(file); 160 161 if (!info) 162 return -ENODEV; 163 return do_fb_ioctl(info, cmd, arg); 164 } 165 166 #ifdef CONFIG_COMPAT 167 struct fb_fix_screeninfo32 { 168 char id[16]; 169 compat_caddr_t smem_start; 170 u32 smem_len; 171 u32 type; 172 u32 type_aux; 173 u32 visual; 174 u16 xpanstep; 175 u16 ypanstep; 176 u16 ywrapstep; 177 u32 line_length; 178 compat_caddr_t mmio_start; 179 u32 mmio_len; 180 u32 accel; 181 u16 reserved[3]; 182 }; 183 184 struct fb_cmap32 { 185 u32 start; 186 u32 len; 187 compat_caddr_t red; 188 compat_caddr_t green; 189 compat_caddr_t blue; 190 compat_caddr_t transp; 191 }; 192 193 static int fb_getput_cmap(struct fb_info *info, unsigned int cmd, 194 unsigned long arg) 195 { 196 struct fb_cmap32 cmap32; 197 struct fb_cmap cmap_from; 198 struct fb_cmap_user cmap; 199 200 if (copy_from_user(&cmap32, compat_ptr(arg), sizeof(cmap32))) 201 return -EFAULT; 202 203 cmap = (struct fb_cmap_user) { 204 .start = cmap32.start, 205 .len = cmap32.len, 206 .red = compat_ptr(cmap32.red), 207 .green = compat_ptr(cmap32.green), 208 .blue = compat_ptr(cmap32.blue), 209 .transp = compat_ptr(cmap32.transp), 210 }; 211 212 if (cmd == FBIOPUTCMAP) 213 return fb_set_user_cmap(&cmap, info); 214 215 lock_fb_info(info); 216 cmap_from = info->cmap; 217 unlock_fb_info(info); 218 219 return fb_cmap_to_user(&cmap_from, &cmap); 220 } 221 222 static int do_fscreeninfo_to_user(struct fb_fix_screeninfo *fix, 223 struct fb_fix_screeninfo32 __user *fix32) 224 { 225 __u32 data; 226 int err; 227 228 err = copy_to_user(&fix32->id, &fix->id, sizeof(fix32->id)); 229 230 data = (__u32) (unsigned long) fix->smem_start; 231 err |= put_user(data, &fix32->smem_start); 232 233 err |= put_user(fix->smem_len, &fix32->smem_len); 234 err |= put_user(fix->type, &fix32->type); 235 err |= put_user(fix->type_aux, &fix32->type_aux); 236 err |= put_user(fix->visual, &fix32->visual); 237 err |= put_user(fix->xpanstep, &fix32->xpanstep); 238 err |= put_user(fix->ypanstep, &fix32->ypanstep); 239 err |= put_user(fix->ywrapstep, &fix32->ywrapstep); 240 err |= put_user(fix->line_length, &fix32->line_length); 241 242 data = (__u32) (unsigned long) fix->mmio_start; 243 err |= put_user(data, &fix32->mmio_start); 244 245 err |= put_user(fix->mmio_len, &fix32->mmio_len); 246 err |= put_user(fix->accel, &fix32->accel); 247 err |= copy_to_user(fix32->reserved, fix->reserved, 248 sizeof(fix->reserved)); 249 250 if (err) 251 return -EFAULT; 252 return 0; 253 } 254 255 static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd, 256 unsigned long arg) 257 { 258 struct fb_fix_screeninfo fix; 259 260 lock_fb_info(info); 261 fix = info->fix; 262 if (info->flags & FBINFO_HIDE_SMEM_START) 263 fix.smem_start = 0; 264 unlock_fb_info(info); 265 return do_fscreeninfo_to_user(&fix, compat_ptr(arg)); 266 } 267 268 static long fb_compat_ioctl(struct file *file, unsigned int cmd, 269 unsigned long arg) 270 { 271 struct fb_info *info = file_fb_info(file); 272 const struct fb_ops *fb; 273 long ret = -ENOIOCTLCMD; 274 275 if (!info) 276 return -ENODEV; 277 fb = info->fbops; 278 switch (cmd) { 279 case FBIOGET_VSCREENINFO: 280 case FBIOPUT_VSCREENINFO: 281 case FBIOPAN_DISPLAY: 282 case FBIOGET_CON2FBMAP: 283 case FBIOPUT_CON2FBMAP: 284 arg = (unsigned long) compat_ptr(arg); 285 fallthrough; 286 case FBIOBLANK: 287 ret = do_fb_ioctl(info, cmd, arg); 288 break; 289 290 case FBIOGET_FSCREENINFO: 291 ret = fb_get_fscreeninfo(info, cmd, arg); 292 break; 293 294 case FBIOGETCMAP: 295 case FBIOPUTCMAP: 296 ret = fb_getput_cmap(info, cmd, arg); 297 break; 298 299 default: 300 if (fb->fb_compat_ioctl) 301 ret = fb->fb_compat_ioctl(info, cmd, arg); 302 break; 303 } 304 return ret; 305 } 306 #endif 307 308 static int fb_mmap(struct file *file, struct vm_area_struct *vma) 309 { 310 struct fb_info *info = file_fb_info(file); 311 int res; 312 313 if (!info) 314 return -ENODEV; 315 316 if (fb_WARN_ON_ONCE(info, !info->fbops->fb_mmap)) 317 return -ENODEV; 318 319 mutex_lock(&info->mm_lock); 320 res = info->fbops->fb_mmap(info, vma); 321 mutex_unlock(&info->mm_lock); 322 323 return res; 324 } 325 326 static int fb_open(struct inode *inode, struct file *file) 327 __acquires(&info->lock) 328 __releases(&info->lock) 329 { 330 int fbidx = iminor(inode); 331 struct fb_info *info; 332 int res = 0; 333 334 info = get_fb_info(fbidx); 335 if (!info) { 336 request_module("fb%d", fbidx); 337 info = get_fb_info(fbidx); 338 if (!info) 339 return -ENODEV; 340 } 341 if (IS_ERR(info)) 342 return PTR_ERR(info); 343 344 lock_fb_info(info); 345 if (!try_module_get(info->fbops->owner)) { 346 res = -ENODEV; 347 goto out; 348 } 349 file->private_data = info; 350 if (info->fbops->fb_open) { 351 res = info->fbops->fb_open(info, 1); 352 if (res) 353 module_put(info->fbops->owner); 354 } 355 #ifdef CONFIG_FB_DEFERRED_IO 356 if (info->fbdefio) 357 fb_deferred_io_open(info, inode, file); 358 #endif 359 out: 360 unlock_fb_info(info); 361 if (res) 362 put_fb_info(info); 363 return res; 364 } 365 366 static int fb_release(struct inode *inode, struct file *file) 367 __acquires(&info->lock) 368 __releases(&info->lock) 369 { 370 struct fb_info * const info = file->private_data; 371 372 lock_fb_info(info); 373 #if IS_ENABLED(CONFIG_FB_DEFERRED_IO) 374 if (info->fbdefio) 375 fb_deferred_io_release(info); 376 #endif 377 if (info->fbops->fb_release) 378 info->fbops->fb_release(info, 1); 379 module_put(info->fbops->owner); 380 unlock_fb_info(info); 381 put_fb_info(info); 382 return 0; 383 } 384 385 #if defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && !defined(CONFIG_MMU) 386 static unsigned long get_fb_unmapped_area(struct file *filp, 387 unsigned long addr, unsigned long len, 388 unsigned long pgoff, unsigned long flags) 389 { 390 struct fb_info * const info = filp->private_data; 391 unsigned long fb_size = PAGE_ALIGN(info->fix.smem_len); 392 393 if (pgoff > fb_size || len > fb_size - pgoff) 394 return -EINVAL; 395 396 return (unsigned long)info->screen_base + pgoff; 397 } 398 #endif 399 400 static const struct file_operations fb_fops = { 401 .owner = THIS_MODULE, 402 .read = fb_read, 403 .write = fb_write, 404 .unlocked_ioctl = fb_ioctl, 405 #ifdef CONFIG_COMPAT 406 .compat_ioctl = fb_compat_ioctl, 407 #endif 408 .mmap = fb_mmap, 409 .open = fb_open, 410 .release = fb_release, 411 #if defined(HAVE_ARCH_FB_UNMAPPED_AREA) || \ 412 (defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && \ 413 !defined(CONFIG_MMU)) 414 .get_unmapped_area = get_fb_unmapped_area, 415 #endif 416 #ifdef CONFIG_FB_DEFERRED_IO 417 .fsync = fb_deferred_io_fsync, 418 #endif 419 .llseek = default_llseek, 420 }; 421 422 int fb_register_chrdev(void) 423 { 424 int ret; 425 426 ret = register_chrdev(FB_MAJOR, "fb", &fb_fops); 427 if (ret) { 428 pr_err("Unable to get major %d for fb devs\n", FB_MAJOR); 429 return ret; 430 } 431 432 return ret; 433 } 434 435 void fb_unregister_chrdev(void) 436 { 437 unregister_chrdev(FB_MAJOR, "fb"); 438 } 439