1 // SPDX-License-Identifier: GPL-2.0
3 #include <linux/compat.h>
4 #include <linux/console.h>
6 #include <linux/fbcon.h>
7 #include <linux/major.h>
9 #include "fb_internal.h"
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.
16 * So look up the fb_info using the inode minor number,
17 * and just verify it against the reference we have.
19 static struct fb_info *file_fb_info(struct file *file)
21 struct inode *inode = file_inode(file);
22 int fbidx = iminor(inode);
23 struct fb_info *info = registered_fb[fbidx];
25 if (info != file->private_data)
30 static ssize_t fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
32 struct fb_info *info = file_fb_info(file);
37 if (info->state != FBINFO_STATE_RUNNING)
40 if (info->fbops->fb_read)
41 return info->fbops->fb_read(info, buf, count, ppos);
43 return fb_io_read(info, buf, count, ppos);
46 static ssize_t fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
48 struct fb_info *info = file_fb_info(file);
53 if (info->state != FBINFO_STATE_RUNNING)
56 if (info->fbops->fb_write)
57 return info->fbops->fb_write(info, buf, count, ppos);
59 return fb_io_write(info, buf, count, ppos);
62 static int fb_copyarea_user(struct fb_info *info,
63 struct fb_copyarea *copy)
67 if (copy->dx >= info->var.xres ||
68 copy->sx >= info->var.xres ||
69 copy->width > info->var.xres ||
70 copy->dy >= info->var.yres ||
71 copy->sy >= info->var.yres ||
72 copy->height > info->var.yres ||
73 copy->dx + copy->width > info->var.xres ||
74 copy->sx + copy->width > info->var.xres ||
75 copy->dy + copy->height > info->var.yres ||
76 copy->sy + copy->height > info->var.yres) {
80 info->fbops->fb_copyarea(info, copy);
86 static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
89 const struct fb_ops *fb;
90 struct fb_var_screeninfo var;
91 struct fb_fix_screeninfo fix;
92 struct fb_cmap cmap_from;
93 struct fb_cmap_user cmap;
94 struct fb_copyarea copy;
95 void __user *argp = (void __user *)arg;
99 case FBIOGET_VSCREENINFO:
102 unlock_fb_info(info);
104 ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0;
106 case FBIOPUT_VSCREENINFO:
107 if (copy_from_user(&var, argp, sizeof(var)))
109 /* only for kernel-internal use */
110 var.activate &= ~FB_ACTIVATE_KD_TEXT;
113 ret = fbcon_modechange_possible(info, &var);
115 ret = fb_set_var(info, &var);
117 fbcon_update_vcs(info, var.activate & FB_ACTIVATE_ALL);
118 unlock_fb_info(info);
120 if (!ret && copy_to_user(argp, &var, sizeof(var)))
123 case FBIOGET_FSCREENINFO:
125 memcpy(&fix, &info->fix, sizeof(fix));
126 if (info->flags & FBINFO_HIDE_SMEM_START)
128 unlock_fb_info(info);
130 ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0;
133 if (copy_from_user(&cmap, argp, sizeof(cmap)))
135 ret = fb_set_user_cmap(&cmap, info);
138 if (copy_from_user(&cmap, argp, sizeof(cmap)))
141 cmap_from = info->cmap;
142 unlock_fb_info(info);
143 ret = fb_cmap_to_user(&cmap_from, &cmap);
145 case FBIOPAN_DISPLAY:
146 if (copy_from_user(&var, argp, sizeof(var)))
150 ret = fb_pan_display(info, &var);
151 unlock_fb_info(info);
153 if (ret == 0 && copy_to_user(argp, &var, sizeof(var)))
159 case FBIOGET_CON2FBMAP:
160 ret = fbcon_get_con2fb_map_ioctl(argp);
162 case FBIOPUT_CON2FBMAP:
163 ret = fbcon_set_con2fb_map_ioctl(argp);
166 if (arg > FB_BLANK_POWERDOWN)
170 ret = fb_blank(info, arg);
171 /* might again call into fb_blank */
172 fbcon_fb_blanked(info, arg);
173 unlock_fb_info(info);
177 if (info->flags & FBINFO_HWACCEL_COPYAREA) {
178 /* only provide this ioctl if it is accelerated */
179 if (copy_from_user(©, argp, sizeof(copy)))
181 ret = fb_copyarea_user(info, ©);
189 ret = fb->fb_ioctl(info, cmd, arg);
192 unlock_fb_info(info);
197 static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
199 struct fb_info *info = file_fb_info(file);
203 return do_fb_ioctl(info, cmd, arg);
207 struct fb_fix_screeninfo32 {
209 compat_caddr_t smem_start;
218 compat_caddr_t mmio_start;
228 compat_caddr_t green;
230 compat_caddr_t transp;
233 static int fb_getput_cmap(struct fb_info *info, unsigned int cmd,
236 struct fb_cmap32 cmap32;
237 struct fb_cmap cmap_from;
238 struct fb_cmap_user cmap;
240 if (copy_from_user(&cmap32, compat_ptr(arg), sizeof(cmap32)))
243 cmap = (struct fb_cmap_user) {
244 .start = cmap32.start,
246 .red = compat_ptr(cmap32.red),
247 .green = compat_ptr(cmap32.green),
248 .blue = compat_ptr(cmap32.blue),
249 .transp = compat_ptr(cmap32.transp),
252 if (cmd == FBIOPUTCMAP)
253 return fb_set_user_cmap(&cmap, info);
256 cmap_from = info->cmap;
257 unlock_fb_info(info);
259 return fb_cmap_to_user(&cmap_from, &cmap);
262 static int do_fscreeninfo_to_user(struct fb_fix_screeninfo *fix,
263 struct fb_fix_screeninfo32 __user *fix32)
268 err = copy_to_user(&fix32->id, &fix->id, sizeof(fix32->id));
270 data = (__u32) (unsigned long) fix->smem_start;
271 err |= put_user(data, &fix32->smem_start);
273 err |= put_user(fix->smem_len, &fix32->smem_len);
274 err |= put_user(fix->type, &fix32->type);
275 err |= put_user(fix->type_aux, &fix32->type_aux);
276 err |= put_user(fix->visual, &fix32->visual);
277 err |= put_user(fix->xpanstep, &fix32->xpanstep);
278 err |= put_user(fix->ypanstep, &fix32->ypanstep);
279 err |= put_user(fix->ywrapstep, &fix32->ywrapstep);
280 err |= put_user(fix->line_length, &fix32->line_length);
282 data = (__u32) (unsigned long) fix->mmio_start;
283 err |= put_user(data, &fix32->mmio_start);
285 err |= put_user(fix->mmio_len, &fix32->mmio_len);
286 err |= put_user(fix->accel, &fix32->accel);
287 err |= copy_to_user(fix32->reserved, fix->reserved,
288 sizeof(fix->reserved));
295 static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd,
298 struct fb_fix_screeninfo fix;
302 if (info->flags & FBINFO_HIDE_SMEM_START)
304 unlock_fb_info(info);
305 return do_fscreeninfo_to_user(&fix, compat_ptr(arg));
308 static long fb_compat_ioctl(struct file *file, unsigned int cmd,
311 struct fb_info *info = file_fb_info(file);
312 const struct fb_ops *fb;
313 long ret = -ENOIOCTLCMD;
319 case FBIOGET_VSCREENINFO:
320 case FBIOPUT_VSCREENINFO:
321 case FBIOPAN_DISPLAY:
322 case FBIOGET_CON2FBMAP:
323 case FBIOPUT_CON2FBMAP:
325 arg = (unsigned long) compat_ptr(arg);
328 ret = do_fb_ioctl(info, cmd, arg);
331 case FBIOGET_FSCREENINFO:
332 ret = fb_get_fscreeninfo(info, cmd, arg);
337 ret = fb_getput_cmap(info, cmd, arg);
341 if (fb->fb_compat_ioctl)
342 ret = fb->fb_compat_ioctl(info, cmd, arg);
349 static int fb_mmap(struct file *file, struct vm_area_struct *vma)
351 struct fb_info *info = file_fb_info(file);
352 unsigned long mmio_pgoff;
358 mutex_lock(&info->mm_lock);
360 if (info->fbops->fb_mmap) {
364 * The framebuffer needs to be accessed decrypted, be sure
365 * SME protection is removed ahead of the call
367 vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
368 res = info->fbops->fb_mmap(info, vma);
369 mutex_unlock(&info->mm_lock);
371 #if IS_ENABLED(CONFIG_FB_DEFERRED_IO)
372 } else if (info->fbdefio) {
374 * FB deferred I/O wants you to handle mmap in your drivers. At a
375 * minimum, point struct fb_ops.fb_mmap to fb_deferred_io_mmap().
377 dev_warn_once(info->dev, "fbdev mmap not set up for deferred I/O.\n");
378 mutex_unlock(&info->mm_lock);
384 * Ugh. This can be either the frame buffer mapping, or
385 * if pgoff points past it, the mmio mapping.
387 start = info->fix.smem_start;
388 len = info->fix.smem_len;
389 mmio_pgoff = PAGE_ALIGN((start & ~PAGE_MASK) + len) >> PAGE_SHIFT;
390 if (vma->vm_pgoff >= mmio_pgoff) {
391 if (info->var.accel_flags) {
392 mutex_unlock(&info->mm_lock);
396 vma->vm_pgoff -= mmio_pgoff;
397 start = info->fix.mmio_start;
398 len = info->fix.mmio_len;
400 mutex_unlock(&info->mm_lock);
402 vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
403 fb_pgprotect(file, vma, start);
405 return vm_iomap_memory(vma, start, len);
408 static int fb_open(struct inode *inode, struct file *file)
409 __acquires(&info->lock)
410 __releases(&info->lock)
412 int fbidx = iminor(inode);
413 struct fb_info *info;
416 info = get_fb_info(fbidx);
418 request_module("fb%d", fbidx);
419 info = get_fb_info(fbidx);
424 return PTR_ERR(info);
427 if (!try_module_get(info->fbops->owner)) {
431 file->private_data = info;
432 if (info->fbops->fb_open) {
433 res = info->fbops->fb_open(info, 1);
435 module_put(info->fbops->owner);
437 #ifdef CONFIG_FB_DEFERRED_IO
439 fb_deferred_io_open(info, inode, file);
442 unlock_fb_info(info);
448 static int fb_release(struct inode *inode, struct file *file)
449 __acquires(&info->lock)
450 __releases(&info->lock)
452 struct fb_info * const info = file->private_data;
455 #if IS_ENABLED(CONFIG_FB_DEFERRED_IO)
457 fb_deferred_io_release(info);
459 if (info->fbops->fb_release)
460 info->fbops->fb_release(info, 1);
461 module_put(info->fbops->owner);
462 unlock_fb_info(info);
467 #if defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && !defined(CONFIG_MMU)
468 static unsigned long get_fb_unmapped_area(struct file *filp,
469 unsigned long addr, unsigned long len,
470 unsigned long pgoff, unsigned long flags)
472 struct fb_info * const info = filp->private_data;
473 unsigned long fb_size = PAGE_ALIGN(info->fix.smem_len);
475 if (pgoff > fb_size || len > fb_size - pgoff)
478 return (unsigned long)info->screen_base + pgoff;
482 static const struct file_operations fb_fops = {
483 .owner = THIS_MODULE,
486 .unlocked_ioctl = fb_ioctl,
488 .compat_ioctl = fb_compat_ioctl,
492 .release = fb_release,
493 #if defined(HAVE_ARCH_FB_UNMAPPED_AREA) || \
494 (defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && \
495 !defined(CONFIG_MMU))
496 .get_unmapped_area = get_fb_unmapped_area,
498 #ifdef CONFIG_FB_DEFERRED_IO
499 .fsync = fb_deferred_io_fsync,
501 .llseek = default_llseek,
504 int fb_register_chrdev(void)
508 ret = register_chrdev(FB_MAJOR, "fb", &fb_fops);
510 pr_err("Unable to get major %d for fb devs\n", FB_MAJOR);
517 void fb_unregister_chrdev(void)
519 unregister_chrdev(FB_MAJOR, "fb");