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 */
file_fb_info(struct file * file)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
fb_read(struct file * file,char __user * buf,size_t count,loff_t * ppos)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
fb_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)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
do_fb_ioctl(struct fb_info * info,unsigned int cmd,unsigned long arg)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
fb_ioctl(struct file * file,unsigned int cmd,unsigned long arg)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
fb_getput_cmap(struct fb_info * info,unsigned int cmd,unsigned long arg)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
do_fscreeninfo_to_user(struct fb_fix_screeninfo * fix,struct fb_fix_screeninfo32 __user * fix32)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
fb_get_fscreeninfo(struct fb_info * info,unsigned int cmd,unsigned long arg)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
fb_compat_ioctl(struct file * file,unsigned int cmd,unsigned long arg)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
fb_mmap(struct file * file,struct vm_area_struct * vma)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
fb_open(struct inode * inode,struct file * file)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
fb_release(struct inode * inode,struct file * file)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)
get_fb_unmapped_area(struct file * filp,unsigned long addr,unsigned long len,unsigned long pgoff,unsigned long flags)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
fb_register_chrdev(void)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
fb_unregister_chrdev(void)482 void fb_unregister_chrdev(void)
483 {
484 unregister_chrdev(FB_MAJOR, "fb");
485 }
486