cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

misc.c (7017B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * linux/drivers/char/misc.c
      4 *
      5 * Generic misc open routine by Johan Myreen
      6 *
      7 * Based on code from Linus
      8 *
      9 * Teemu Rantanen's Microsoft Busmouse support and Derrick Cole's
     10 *   changes incorporated into 0.97pl4
     11 *   by Peter Cervasio (pete%q106fm.uucp@wupost.wustl.edu) (08SEP92)
     12 *   See busmouse.c for particulars.
     13 *
     14 * Made things a lot mode modular - easy to compile in just one or two
     15 * of the misc drivers, as they are now completely independent. Linus.
     16 *
     17 * Support for loadable modules. 8-Sep-95 Philip Blundell <pjb27@cam.ac.uk>
     18 *
     19 * Fixed a failing symbol register to free the device registration
     20 *		Alan Cox <alan@lxorguk.ukuu.org.uk> 21-Jan-96
     21 *
     22 * Dynamic minors and /proc/mice by Alessandro Rubini. 26-Mar-96
     23 *
     24 * Renamed to misc and miscdevice to be more accurate. Alan Cox 26-Mar-96
     25 *
     26 * Handling of mouse minor numbers for kerneld:
     27 *  Idea by Jacques Gelinas <jack@solucorp.qc.ca>,
     28 *  adapted by Bjorn Ekwall <bj0rn@blox.se>
     29 *  corrected by Alan Cox <alan@lxorguk.ukuu.org.uk>
     30 *
     31 * Changes for kmod (from kerneld):
     32 *	Cyrus Durgin <cider@speakeasy.org>
     33 *
     34 * Added devfs support. Richard Gooch <rgooch@atnf.csiro.au>  10-Jan-1998
     35 */
     36
     37#include <linux/module.h>
     38
     39#include <linux/fs.h>
     40#include <linux/errno.h>
     41#include <linux/miscdevice.h>
     42#include <linux/kernel.h>
     43#include <linux/major.h>
     44#include <linux/mutex.h>
     45#include <linux/proc_fs.h>
     46#include <linux/seq_file.h>
     47#include <linux/stat.h>
     48#include <linux/init.h>
     49#include <linux/device.h>
     50#include <linux/tty.h>
     51#include <linux/kmod.h>
     52#include <linux/gfp.h>
     53
     54/*
     55 * Head entry for the doubly linked miscdevice list
     56 */
     57static LIST_HEAD(misc_list);
     58static DEFINE_MUTEX(misc_mtx);
     59
     60/*
     61 * Assigned numbers, used for dynamic minors
     62 */
     63#define DYNAMIC_MINORS 128 /* like dynamic majors */
     64static DECLARE_BITMAP(misc_minors, DYNAMIC_MINORS);
     65
     66#ifdef CONFIG_PROC_FS
     67static void *misc_seq_start(struct seq_file *seq, loff_t *pos)
     68{
     69	mutex_lock(&misc_mtx);
     70	return seq_list_start(&misc_list, *pos);
     71}
     72
     73static void *misc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
     74{
     75	return seq_list_next(v, &misc_list, pos);
     76}
     77
     78static void misc_seq_stop(struct seq_file *seq, void *v)
     79{
     80	mutex_unlock(&misc_mtx);
     81}
     82
     83static int misc_seq_show(struct seq_file *seq, void *v)
     84{
     85	const struct miscdevice *p = list_entry(v, struct miscdevice, list);
     86
     87	seq_printf(seq, "%3i %s\n", p->minor, p->name ? p->name : "");
     88	return 0;
     89}
     90
     91
     92static const struct seq_operations misc_seq_ops = {
     93	.start = misc_seq_start,
     94	.next  = misc_seq_next,
     95	.stop  = misc_seq_stop,
     96	.show  = misc_seq_show,
     97};
     98#endif
     99
    100static int misc_open(struct inode *inode, struct file *file)
    101{
    102	int minor = iminor(inode);
    103	struct miscdevice *c = NULL, *iter;
    104	int err = -ENODEV;
    105	const struct file_operations *new_fops = NULL;
    106
    107	mutex_lock(&misc_mtx);
    108
    109	list_for_each_entry(iter, &misc_list, list) {
    110		if (iter->minor != minor)
    111			continue;
    112		c = iter;
    113		new_fops = fops_get(iter->fops);
    114		break;
    115	}
    116
    117	if (!new_fops) {
    118		mutex_unlock(&misc_mtx);
    119		request_module("char-major-%d-%d", MISC_MAJOR, minor);
    120		mutex_lock(&misc_mtx);
    121
    122		list_for_each_entry(iter, &misc_list, list) {
    123			if (iter->minor != minor)
    124				continue;
    125			c = iter;
    126			new_fops = fops_get(iter->fops);
    127			break;
    128		}
    129		if (!new_fops)
    130			goto fail;
    131	}
    132
    133	/*
    134	 * Place the miscdevice in the file's
    135	 * private_data so it can be used by the
    136	 * file operations, including f_op->open below
    137	 */
    138	file->private_data = c;
    139
    140	err = 0;
    141	replace_fops(file, new_fops);
    142	if (file->f_op->open)
    143		err = file->f_op->open(inode, file);
    144fail:
    145	mutex_unlock(&misc_mtx);
    146	return err;
    147}
    148
    149static struct class *misc_class;
    150
    151static const struct file_operations misc_fops = {
    152	.owner		= THIS_MODULE,
    153	.open		= misc_open,
    154	.llseek		= noop_llseek,
    155};
    156
    157/**
    158 *	misc_register	-	register a miscellaneous device
    159 *	@misc: device structure
    160 *
    161 *	Register a miscellaneous device with the kernel. If the minor
    162 *	number is set to %MISC_DYNAMIC_MINOR a minor number is assigned
    163 *	and placed in the minor field of the structure. For other cases
    164 *	the minor number requested is used.
    165 *
    166 *	The structure passed is linked into the kernel and may not be
    167 *	destroyed until it has been unregistered. By default, an open()
    168 *	syscall to the device sets file->private_data to point to the
    169 *	structure. Drivers don't need open in fops for this.
    170 *
    171 *	A zero is returned on success and a negative errno code for
    172 *	failure.
    173 */
    174
    175int misc_register(struct miscdevice *misc)
    176{
    177	dev_t dev;
    178	int err = 0;
    179	bool is_dynamic = (misc->minor == MISC_DYNAMIC_MINOR);
    180
    181	INIT_LIST_HEAD(&misc->list);
    182
    183	mutex_lock(&misc_mtx);
    184
    185	if (is_dynamic) {
    186		int i = find_first_zero_bit(misc_minors, DYNAMIC_MINORS);
    187
    188		if (i >= DYNAMIC_MINORS) {
    189			err = -EBUSY;
    190			goto out;
    191		}
    192		misc->minor = DYNAMIC_MINORS - i - 1;
    193		set_bit(i, misc_minors);
    194	} else {
    195		struct miscdevice *c;
    196
    197		list_for_each_entry(c, &misc_list, list) {
    198			if (c->minor == misc->minor) {
    199				err = -EBUSY;
    200				goto out;
    201			}
    202		}
    203	}
    204
    205	dev = MKDEV(MISC_MAJOR, misc->minor);
    206
    207	misc->this_device =
    208		device_create_with_groups(misc_class, misc->parent, dev,
    209					  misc, misc->groups, "%s", misc->name);
    210	if (IS_ERR(misc->this_device)) {
    211		if (is_dynamic) {
    212			int i = DYNAMIC_MINORS - misc->minor - 1;
    213
    214			if (i < DYNAMIC_MINORS && i >= 0)
    215				clear_bit(i, misc_minors);
    216			misc->minor = MISC_DYNAMIC_MINOR;
    217		}
    218		err = PTR_ERR(misc->this_device);
    219		goto out;
    220	}
    221
    222	/*
    223	 * Add it to the front, so that later devices can "override"
    224	 * earlier defaults
    225	 */
    226	list_add(&misc->list, &misc_list);
    227 out:
    228	mutex_unlock(&misc_mtx);
    229	return err;
    230}
    231EXPORT_SYMBOL(misc_register);
    232
    233/**
    234 *	misc_deregister - unregister a miscellaneous device
    235 *	@misc: device to unregister
    236 *
    237 *	Unregister a miscellaneous device that was previously
    238 *	successfully registered with misc_register().
    239 */
    240
    241void misc_deregister(struct miscdevice *misc)
    242{
    243	int i = DYNAMIC_MINORS - misc->minor - 1;
    244
    245	if (WARN_ON(list_empty(&misc->list)))
    246		return;
    247
    248	mutex_lock(&misc_mtx);
    249	list_del(&misc->list);
    250	device_destroy(misc_class, MKDEV(MISC_MAJOR, misc->minor));
    251	if (i < DYNAMIC_MINORS && i >= 0)
    252		clear_bit(i, misc_minors);
    253	mutex_unlock(&misc_mtx);
    254}
    255EXPORT_SYMBOL(misc_deregister);
    256
    257static char *misc_devnode(struct device *dev, umode_t *mode)
    258{
    259	struct miscdevice *c = dev_get_drvdata(dev);
    260
    261	if (mode && c->mode)
    262		*mode = c->mode;
    263	if (c->nodename)
    264		return kstrdup(c->nodename, GFP_KERNEL);
    265	return NULL;
    266}
    267
    268static int __init misc_init(void)
    269{
    270	int err;
    271	struct proc_dir_entry *ret;
    272
    273	ret = proc_create_seq("misc", 0, NULL, &misc_seq_ops);
    274	misc_class = class_create(THIS_MODULE, "misc");
    275	err = PTR_ERR(misc_class);
    276	if (IS_ERR(misc_class))
    277		goto fail_remove;
    278
    279	err = -EIO;
    280	if (register_chrdev(MISC_MAJOR, "misc", &misc_fops))
    281		goto fail_printk;
    282	misc_class->devnode = misc_devnode;
    283	return 0;
    284
    285fail_printk:
    286	pr_err("unable to get major %d for misc devices\n", MISC_MAJOR);
    287	class_destroy(misc_class);
    288fail_remove:
    289	if (ret)
    290		remove_proc_entry("misc", NULL);
    291	return err;
    292}
    293subsys_initcall(misc_init);