xref: /linux/drivers/video/fbdev/core/fb_chrdev.c (revision fd7d598270724cc787982ea48bbe17ad383a8b7f)
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 	vma->vm_page_prot = pgprot_framebuffer(vma->vm_page_prot, vma->vm_start,
369 					       vma->vm_end, start);
370 
371 	return vm_iomap_memory(vma, start, len);
372 }
373 
374 static int fb_open(struct inode *inode, struct file *file)
375 __acquires(&info->lock)
376 __releases(&info->lock)
377 {
378 	int fbidx = iminor(inode);
379 	struct fb_info *info;
380 	int res = 0;
381 
382 	info = get_fb_info(fbidx);
383 	if (!info) {
384 		request_module("fb%d", fbidx);
385 		info = get_fb_info(fbidx);
386 		if (!info)
387 			return -ENODEV;
388 	}
389 	if (IS_ERR(info))
390 		return PTR_ERR(info);
391 
392 	lock_fb_info(info);
393 	if (!try_module_get(info->fbops->owner)) {
394 		res = -ENODEV;
395 		goto out;
396 	}
397 	file->private_data = info;
398 	if (info->fbops->fb_open) {
399 		res = info->fbops->fb_open(info, 1);
400 		if (res)
401 			module_put(info->fbops->owner);
402 	}
403 #ifdef CONFIG_FB_DEFERRED_IO
404 	if (info->fbdefio)
405 		fb_deferred_io_open(info, inode, file);
406 #endif
407 out:
408 	unlock_fb_info(info);
409 	if (res)
410 		put_fb_info(info);
411 	return res;
412 }
413 
414 static int fb_release(struct inode *inode, struct file *file)
415 __acquires(&info->lock)
416 __releases(&info->lock)
417 {
418 	struct fb_info * const info = file->private_data;
419 
420 	lock_fb_info(info);
421 #if IS_ENABLED(CONFIG_FB_DEFERRED_IO)
422 	if (info->fbdefio)
423 		fb_deferred_io_release(info);
424 #endif
425 	if (info->fbops->fb_release)
426 		info->fbops->fb_release(info, 1);
427 	module_put(info->fbops->owner);
428 	unlock_fb_info(info);
429 	put_fb_info(info);
430 	return 0;
431 }
432 
433 #if defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && !defined(CONFIG_MMU)
434 static unsigned long get_fb_unmapped_area(struct file *filp,
435 				   unsigned long addr, unsigned long len,
436 				   unsigned long pgoff, unsigned long flags)
437 {
438 	struct fb_info * const info = filp->private_data;
439 	unsigned long fb_size = PAGE_ALIGN(info->fix.smem_len);
440 
441 	if (pgoff > fb_size || len > fb_size - pgoff)
442 		return -EINVAL;
443 
444 	return (unsigned long)info->screen_base + pgoff;
445 }
446 #endif
447 
448 static const struct file_operations fb_fops = {
449 	.owner = THIS_MODULE,
450 	.read = fb_read,
451 	.write = fb_write,
452 	.unlocked_ioctl = fb_ioctl,
453 #ifdef CONFIG_COMPAT
454 	.compat_ioctl = fb_compat_ioctl,
455 #endif
456 	.mmap = fb_mmap,
457 	.open = fb_open,
458 	.release = fb_release,
459 #if defined(HAVE_ARCH_FB_UNMAPPED_AREA) || \
460 	(defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && \
461 	 !defined(CONFIG_MMU))
462 	.get_unmapped_area = get_fb_unmapped_area,
463 #endif
464 #ifdef CONFIG_FB_DEFERRED_IO
465 	.fsync = fb_deferred_io_fsync,
466 #endif
467 	.llseek = default_llseek,
468 };
469 
470 int fb_register_chrdev(void)
471 {
472 	int ret;
473 
474 	ret = register_chrdev(FB_MAJOR, "fb", &fb_fops);
475 	if (ret) {
476 		pr_err("Unable to get major %d for fb devs\n", FB_MAJOR);
477 		return ret;
478 	}
479 
480 	return ret;
481 }
482 
483 void fb_unregister_chrdev(void)
484 {
485 	unregister_chrdev(FB_MAJOR, "fb");
486 }
487