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

scsi_proc.c (11359B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * linux/drivers/scsi/scsi_proc.c
      4 *
      5 * The functions in this file provide an interface between
      6 * the PROC file system and the SCSI device drivers
      7 * It is mainly used for debugging, statistics and to pass 
      8 * information directly to the lowlevel driver.
      9 *
     10 * (c) 1995 Michael Neuffer neuffer@goofy.zdv.uni-mainz.de 
     11 * Version: 0.99.8   last change: 95/09/13
     12 * 
     13 * generic command parser provided by: 
     14 * Andreas Heilwagen <crashcar@informatik.uni-koblenz.de>
     15 *
     16 * generic_proc_info() support of xxxx_info() by:
     17 * Michael A. Griffith <grif@acm.org>
     18 */
     19
     20#include <linux/module.h>
     21#include <linux/init.h>
     22#include <linux/string.h>
     23#include <linux/mm.h>
     24#include <linux/proc_fs.h>
     25#include <linux/errno.h>
     26#include <linux/blkdev.h>
     27#include <linux/seq_file.h>
     28#include <linux/mutex.h>
     29#include <linux/gfp.h>
     30#include <linux/uaccess.h>
     31
     32#include <scsi/scsi.h>
     33#include <scsi/scsi_device.h>
     34#include <scsi/scsi_host.h>
     35#include <scsi/scsi_transport.h>
     36
     37#include "scsi_priv.h"
     38#include "scsi_logging.h"
     39
     40
     41/* 4K page size, but our output routines, use some slack for overruns */
     42#define PROC_BLOCK_SIZE (3*1024)
     43
     44static struct proc_dir_entry *proc_scsi;
     45
     46/* Protect sht->present and sht->proc_dir */
     47static DEFINE_MUTEX(global_host_template_mutex);
     48
     49static ssize_t proc_scsi_host_write(struct file *file, const char __user *buf,
     50                           size_t count, loff_t *ppos)
     51{
     52	struct Scsi_Host *shost = pde_data(file_inode(file));
     53	ssize_t ret = -ENOMEM;
     54	char *page;
     55    
     56	if (count > PROC_BLOCK_SIZE)
     57		return -EOVERFLOW;
     58
     59	if (!shost->hostt->write_info)
     60		return -EINVAL;
     61
     62	page = (char *)__get_free_page(GFP_KERNEL);
     63	if (page) {
     64		ret = -EFAULT;
     65		if (copy_from_user(page, buf, count))
     66			goto out;
     67		ret = shost->hostt->write_info(shost, page, count);
     68	}
     69out:
     70	free_page((unsigned long)page);
     71	return ret;
     72}
     73
     74static int proc_scsi_show(struct seq_file *m, void *v)
     75{
     76	struct Scsi_Host *shost = m->private;
     77	return shost->hostt->show_info(m, shost);
     78}
     79
     80static int proc_scsi_host_open(struct inode *inode, struct file *file)
     81{
     82	return single_open_size(file, proc_scsi_show, pde_data(inode),
     83				4 * PAGE_SIZE);
     84}
     85
     86static const struct proc_ops proc_scsi_ops = {
     87	.proc_open	= proc_scsi_host_open,
     88	.proc_release	= single_release,
     89	.proc_read	= seq_read,
     90	.proc_lseek	= seq_lseek,
     91	.proc_write	= proc_scsi_host_write
     92};
     93
     94/**
     95 * scsi_proc_hostdir_add - Create directory in /proc for a scsi host
     96 * @sht: owner of this directory
     97 *
     98 * Sets sht->proc_dir to the new directory.
     99 */
    100
    101void scsi_proc_hostdir_add(struct scsi_host_template *sht)
    102{
    103	if (!sht->show_info)
    104		return;
    105
    106	mutex_lock(&global_host_template_mutex);
    107	if (!sht->present++) {
    108		sht->proc_dir = proc_mkdir(sht->proc_name, proc_scsi);
    109        	if (!sht->proc_dir)
    110			printk(KERN_ERR "%s: proc_mkdir failed for %s\n",
    111			       __func__, sht->proc_name);
    112	}
    113	mutex_unlock(&global_host_template_mutex);
    114}
    115
    116/**
    117 * scsi_proc_hostdir_rm - remove directory in /proc for a scsi host
    118 * @sht: owner of directory
    119 */
    120void scsi_proc_hostdir_rm(struct scsi_host_template *sht)
    121{
    122	if (!sht->show_info)
    123		return;
    124
    125	mutex_lock(&global_host_template_mutex);
    126	if (!--sht->present && sht->proc_dir) {
    127		remove_proc_entry(sht->proc_name, proc_scsi);
    128		sht->proc_dir = NULL;
    129	}
    130	mutex_unlock(&global_host_template_mutex);
    131}
    132
    133
    134/**
    135 * scsi_proc_host_add - Add entry for this host to appropriate /proc dir
    136 * @shost: host to add
    137 */
    138void scsi_proc_host_add(struct Scsi_Host *shost)
    139{
    140	struct scsi_host_template *sht = shost->hostt;
    141	struct proc_dir_entry *p;
    142	char name[10];
    143
    144	if (!sht->proc_dir)
    145		return;
    146
    147	sprintf(name,"%d", shost->host_no);
    148	p = proc_create_data(name, S_IRUGO | S_IWUSR,
    149		sht->proc_dir, &proc_scsi_ops, shost);
    150	if (!p)
    151		printk(KERN_ERR "%s: Failed to register host %d in"
    152		       "%s\n", __func__, shost->host_no,
    153		       sht->proc_name);
    154}
    155
    156/**
    157 * scsi_proc_host_rm - remove this host's entry from /proc
    158 * @shost: which host
    159 */
    160void scsi_proc_host_rm(struct Scsi_Host *shost)
    161{
    162	char name[10];
    163
    164	if (!shost->hostt->proc_dir)
    165		return;
    166
    167	sprintf(name,"%d", shost->host_no);
    168	remove_proc_entry(name, shost->hostt->proc_dir);
    169}
    170/**
    171 * proc_print_scsidevice - return data about this host
    172 * @dev: A scsi device
    173 * @data: &struct seq_file to output to.
    174 *
    175 * Description: prints Host, Channel, Id, Lun, Vendor, Model, Rev, Type,
    176 * and revision.
    177 */
    178static int proc_print_scsidevice(struct device *dev, void *data)
    179{
    180	struct scsi_device *sdev;
    181	struct seq_file *s = data;
    182	int i;
    183
    184	if (!scsi_is_sdev_device(dev))
    185		goto out;
    186
    187	sdev = to_scsi_device(dev);
    188	seq_printf(s,
    189		"Host: scsi%d Channel: %02d Id: %02d Lun: %02llu\n  Vendor: ",
    190		sdev->host->host_no, sdev->channel, sdev->id, sdev->lun);
    191	for (i = 0; i < 8; i++) {
    192		if (sdev->vendor[i] >= 0x20)
    193			seq_putc(s, sdev->vendor[i]);
    194		else
    195			seq_putc(s, ' ');
    196	}
    197
    198	seq_puts(s, " Model: ");
    199	for (i = 0; i < 16; i++) {
    200		if (sdev->model[i] >= 0x20)
    201			seq_putc(s, sdev->model[i]);
    202		else
    203			seq_putc(s, ' ');
    204	}
    205
    206	seq_puts(s, " Rev: ");
    207	for (i = 0; i < 4; i++) {
    208		if (sdev->rev[i] >= 0x20)
    209			seq_putc(s, sdev->rev[i]);
    210		else
    211			seq_putc(s, ' ');
    212	}
    213
    214	seq_putc(s, '\n');
    215
    216	seq_printf(s, "  Type:   %s ", scsi_device_type(sdev->type));
    217	seq_printf(s, "               ANSI  SCSI revision: %02x",
    218			sdev->scsi_level - (sdev->scsi_level > 1));
    219	if (sdev->scsi_level == 2)
    220		seq_puts(s, " CCS\n");
    221	else
    222		seq_putc(s, '\n');
    223
    224out:
    225	return 0;
    226}
    227
    228/**
    229 * scsi_add_single_device - Respond to user request to probe for/add device
    230 * @host: user-supplied decimal integer
    231 * @channel: user-supplied decimal integer
    232 * @id: user-supplied decimal integer
    233 * @lun: user-supplied decimal integer
    234 *
    235 * Description: called by writing "scsi add-single-device" to /proc/scsi/scsi.
    236 *
    237 * does scsi_host_lookup() and either user_scan() if that transport
    238 * type supports it, or else scsi_scan_host_selected()
    239 *
    240 * Note: this seems to be aimed exclusively at SCSI parallel busses.
    241 */
    242
    243static int scsi_add_single_device(uint host, uint channel, uint id, uint lun)
    244{
    245	struct Scsi_Host *shost;
    246	int error = -ENXIO;
    247
    248	shost = scsi_host_lookup(host);
    249	if (!shost)
    250		return error;
    251
    252	if (shost->transportt->user_scan)
    253		error = shost->transportt->user_scan(shost, channel, id, lun);
    254	else
    255		error = scsi_scan_host_selected(shost, channel, id, lun,
    256						SCSI_SCAN_MANUAL);
    257	scsi_host_put(shost);
    258	return error;
    259}
    260
    261/**
    262 * scsi_remove_single_device - Respond to user request to remove a device
    263 * @host: user-supplied decimal integer
    264 * @channel: user-supplied decimal integer
    265 * @id: user-supplied decimal integer
    266 * @lun: user-supplied decimal integer
    267 *
    268 * Description: called by writing "scsi remove-single-device" to
    269 * /proc/scsi/scsi.  Does a scsi_device_lookup() and scsi_remove_device()
    270 */
    271static int scsi_remove_single_device(uint host, uint channel, uint id, uint lun)
    272{
    273	struct scsi_device *sdev;
    274	struct Scsi_Host *shost;
    275	int error = -ENXIO;
    276
    277	shost = scsi_host_lookup(host);
    278	if (!shost)
    279		return error;
    280	sdev = scsi_device_lookup(shost, channel, id, lun);
    281	if (sdev) {
    282		scsi_remove_device(sdev);
    283		scsi_device_put(sdev);
    284		error = 0;
    285	}
    286
    287	scsi_host_put(shost);
    288	return error;
    289}
    290
    291/**
    292 * proc_scsi_write - handle writes to /proc/scsi/scsi
    293 * @file: not used
    294 * @buf: buffer to write
    295 * @length: length of buf, at most PAGE_SIZE
    296 * @ppos: not used
    297 *
    298 * Description: this provides a legacy mechanism to add or remove devices by
    299 * Host, Channel, ID, and Lun.  To use,
    300 * "echo 'scsi add-single-device 0 1 2 3' > /proc/scsi/scsi" or
    301 * "echo 'scsi remove-single-device 0 1 2 3' > /proc/scsi/scsi" with
    302 * "0 1 2 3" replaced by the Host, Channel, Id, and Lun.
    303 *
    304 * Note: this seems to be aimed at parallel SCSI. Most modern busses (USB,
    305 * SATA, Firewire, Fibre Channel, etc) dynamically assign these values to
    306 * provide a unique identifier and nothing more.
    307 */
    308
    309
    310static ssize_t proc_scsi_write(struct file *file, const char __user *buf,
    311			       size_t length, loff_t *ppos)
    312{
    313	int host, channel, id, lun;
    314	char *buffer, *p;
    315	int err;
    316
    317	if (!buf || length > PAGE_SIZE)
    318		return -EINVAL;
    319
    320	buffer = (char *)__get_free_page(GFP_KERNEL);
    321	if (!buffer)
    322		return -ENOMEM;
    323
    324	err = -EFAULT;
    325	if (copy_from_user(buffer, buf, length))
    326		goto out;
    327
    328	err = -EINVAL;
    329	if (length < PAGE_SIZE)
    330		buffer[length] = '\0';
    331	else if (buffer[PAGE_SIZE-1])
    332		goto out;
    333
    334	/*
    335	 * Usage: echo "scsi add-single-device 0 1 2 3" >/proc/scsi/scsi
    336	 * with  "0 1 2 3" replaced by your "Host Channel Id Lun".
    337	 */
    338	if (!strncmp("scsi add-single-device", buffer, 22)) {
    339		p = buffer + 23;
    340
    341		host = simple_strtoul(p, &p, 0);
    342		channel = simple_strtoul(p + 1, &p, 0);
    343		id = simple_strtoul(p + 1, &p, 0);
    344		lun = simple_strtoul(p + 1, &p, 0);
    345
    346		err = scsi_add_single_device(host, channel, id, lun);
    347
    348	/*
    349	 * Usage: echo "scsi remove-single-device 0 1 2 3" >/proc/scsi/scsi
    350	 * with  "0 1 2 3" replaced by your "Host Channel Id Lun".
    351	 */
    352	} else if (!strncmp("scsi remove-single-device", buffer, 25)) {
    353		p = buffer + 26;
    354
    355		host = simple_strtoul(p, &p, 0);
    356		channel = simple_strtoul(p + 1, &p, 0);
    357		id = simple_strtoul(p + 1, &p, 0);
    358		lun = simple_strtoul(p + 1, &p, 0);
    359
    360		err = scsi_remove_single_device(host, channel, id, lun);
    361	}
    362
    363	/*
    364	 * convert success returns so that we return the 
    365	 * number of bytes consumed.
    366	 */
    367	if (!err)
    368		err = length;
    369
    370 out:
    371	free_page((unsigned long)buffer);
    372	return err;
    373}
    374
    375static inline struct device *next_scsi_device(struct device *start)
    376{
    377	struct device *next = bus_find_next_device(&scsi_bus_type, start);
    378
    379	put_device(start);
    380	return next;
    381}
    382
    383static void *scsi_seq_start(struct seq_file *sfile, loff_t *pos)
    384{
    385	struct device *dev = NULL;
    386	loff_t n = *pos;
    387
    388	while ((dev = next_scsi_device(dev))) {
    389		if (!n--)
    390			break;
    391		sfile->private++;
    392	}
    393	return dev;
    394}
    395
    396static void *scsi_seq_next(struct seq_file *sfile, void *v, loff_t *pos)
    397{
    398	(*pos)++;
    399	sfile->private++;
    400	return next_scsi_device(v);
    401}
    402
    403static void scsi_seq_stop(struct seq_file *sfile, void *v)
    404{
    405	put_device(v);
    406}
    407
    408static int scsi_seq_show(struct seq_file *sfile, void *dev)
    409{
    410	if (!sfile->private)
    411		seq_puts(sfile, "Attached devices:\n");
    412
    413	return proc_print_scsidevice(dev, sfile);
    414}
    415
    416static const struct seq_operations scsi_seq_ops = {
    417	.start	= scsi_seq_start,
    418	.next	= scsi_seq_next,
    419	.stop	= scsi_seq_stop,
    420	.show	= scsi_seq_show
    421};
    422
    423/**
    424 * proc_scsi_open - glue function
    425 * @inode: not used
    426 * @file: passed to single_open()
    427 *
    428 * Associates proc_scsi_show with this file
    429 */
    430static int proc_scsi_open(struct inode *inode, struct file *file)
    431{
    432	/*
    433	 * We don't really need this for the write case but it doesn't
    434	 * harm either.
    435	 */
    436	return seq_open(file, &scsi_seq_ops);
    437}
    438
    439static const struct proc_ops scsi_scsi_proc_ops = {
    440	.proc_open	= proc_scsi_open,
    441	.proc_read	= seq_read,
    442	.proc_write	= proc_scsi_write,
    443	.proc_lseek	= seq_lseek,
    444	.proc_release	= seq_release,
    445};
    446
    447/**
    448 * scsi_init_procfs - create scsi and scsi/scsi in procfs
    449 */
    450int __init scsi_init_procfs(void)
    451{
    452	struct proc_dir_entry *pde;
    453
    454	proc_scsi = proc_mkdir("scsi", NULL);
    455	if (!proc_scsi)
    456		goto err1;
    457
    458	pde = proc_create("scsi/scsi", 0, NULL, &scsi_scsi_proc_ops);
    459	if (!pde)
    460		goto err2;
    461
    462	return 0;
    463
    464err2:
    465	remove_proc_entry("scsi", NULL);
    466err1:
    467	return -ENOMEM;
    468}
    469
    470/**
    471 * scsi_exit_procfs - Remove scsi/scsi and scsi from procfs
    472 */
    473void scsi_exit_procfs(void)
    474{
    475	remove_proc_entry("scsi/scsi", NULL);
    476	remove_proc_entry("scsi", NULL);
    477}