brintos

brintos / linux-shallow public Read only

0
0
Text · 9.9 KiB · 4ebd16b Raw
445 lines · c
1// SPDX-License-Identifier: GPL-2.02 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't14 * 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 */19static 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 30static 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 46static 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 62static 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		else157			ret = -ENOTTY;158		unlock_fb_info(info);159	}160	return ret;161}162 163static 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_COMPAT173struct 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 190struct 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 199static 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 228static 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 261static 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 274static 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#endif313 314static 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 332static 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_IO362	if (info->fbdefio)363		fb_deferred_io_open(info, inode, file);364#endif365out:366	unlock_fb_info(info);367	if (res)368		put_fb_info(info);369	return res;370}371 372static 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#endif383	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)392static 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#endif405 406static 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_COMPAT412	.compat_ioctl = fb_compat_ioctl,413#endif414	.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#endif422#ifdef CONFIG_FB_DEFERRED_IO423	.fsync = fb_deferred_io_fsync,424#endif425	.llseek = default_llseek,426};427 428int 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 441void fb_unregister_chrdev(void)442{443	unregister_chrdev(FB_MAJOR, "fb");444}445