lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <5265C0C8.10609@linux.intel.com>
Date:	Mon, 21 Oct 2013 17:03:20 -0700
From:	Bin Gao <bin.gao@...ux.intel.com>
To:	Arnd Bergmann <arnd@...db.de>,
	Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
	linux-kernel@...r.kernel.org
Subject: [PATCH 1/4] drivers/misc: add rawio framework driver

Rawio provides a framework to read/write registers from a bus, including
pci, i2c, I/O device(memory mapped), etc. based on debug fs.
Rawio bus drivers implement the read/write operation on a specific bus
on top of the rawio framework driver.
They are designed to help device driver and kernel debugging on
embedded systems.

Signed-off-by: Bin Gao <bin.gao@...el.com>
---
 drivers/misc/Kconfig        |   1 +
 drivers/misc/Makefile       |   1 +
 drivers/misc/rawio/Kconfig  |  21 ++
 drivers/misc/rawio/Makefile |   1 +
 drivers/misc/rawio/rawio.c  | 514
++++++++++++++++++++++++++++++++++++++++++++
 include/linux/rawio.h       |  78 +++++++
 6 files changed, 616 insertions(+)
 create mode 100644 drivers/misc/rawio/Kconfig
 create mode 100644 drivers/misc/rawio/Makefile
 create mode 100644 drivers/misc/rawio/rawio.c
 create mode 100644 include/linux/rawio.h

diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 8dacd4c..1afbe4e 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -537,4 +537,5 @@ source "drivers/misc/carma/Kconfig"
 source "drivers/misc/altera-stapl/Kconfig"
 source "drivers/misc/mei/Kconfig"
 source "drivers/misc/vmw_vmci/Kconfig"
+source "drivers/misc/rawio/Kconfig"
 endmenu
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index c235d5b..3bc116b 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -53,3 +53,4 @@ obj-$(CONFIG_INTEL_MEI)		+= mei/
 obj-$(CONFIG_VMWARE_VMCI)	+= vmw_vmci/
 obj-$(CONFIG_LATTICE_ECP3_CONFIG)	+= lattice-ecp3-config.o
 obj-$(CONFIG_SRAM)		+= sram.o
+obj-$(CONFIG_RAWIO)		+= rawio/
diff --git a/drivers/misc/rawio/Kconfig b/drivers/misc/rawio/Kconfig
new file mode 100644
index 0000000..fd4272e
--- /dev/null
+++ b/drivers/misc/rawio/Kconfig
@@ -0,0 +1,21 @@
+#
+# rawio utility drivers
+#
+
+menuconfig RAWIO
+	tristate "Debug fs based raw io device read/write framework "
+	depends on DEBUG_FS
+	default no
+	help
+	  This option enables support for reading or writing registers/memory
+	  region in a io device via debug fs.
+	  With this option and related rawio driver options enabled, you could
+	  read configuration space of a PCI device, registers of a memory
+	  mapped or port mapped device, registers of a i2c device, etc.
+	  This is the just the framework driver. You need enable more
+	  options to support specific device types.
+
+	  To compile this driver as a module, choose M: the module will
+	  be called rawio.
+
+	  If you are not sure, say N here.
diff --git a/drivers/misc/rawio/Makefile b/drivers/misc/rawio/Makefile
new file mode 100644
index 0000000..c21453c
--- /dev/null
+++ b/drivers/misc/rawio/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_RAWIO)		+= rawio.o
diff --git a/drivers/misc/rawio/rawio.c b/drivers/misc/rawio/rawio.c
new file mode 100644
index 0000000..a05b493
--- /dev/null
+++ b/drivers/misc/rawio/rawio.c
@@ -0,0 +1,514 @@
+/*
+ * rawio.c - a debugfs based framework for reading/writing registers
+ * from a I/O device.
+ * With pluggable rawio drivers, it can support PCI devices, I2C devices,
+ * memory mapped I/O devices, etc.
+ * It's designed for helping debug Linux device drivers on embedded
system or
+ * SoC platforms.
+ *
+ * Copyright (c) 2013 Bin Gao <bin.gao@...el.com>
+ *
+ * This file is released under the GPLv2
+ *
+ *
+ * Two files are created in debugfs root folder: rawio_cmd and
rawio_output.
+ * To read or write via the rawio debugfs interface, first echo a rawio
+ * command to the file rawio_cmd, then cat the file rawio_output:
+ * $ echo "<rawio command>" > /sys/kernel/debug/rawio_cmd
+ * $ cat /sys/kernel/debug/rawio_output
+ * The cat command is required for both read and write operations.
+ * For details of rawio command format, see specific rawio drivers.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/err.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/rawio.h>
+
+#define SHOW_NUM_PER_LINE	(32 / active_width)
+#define LINE_WIDTH		32
+#define IS_WHITESPACE(c)	((c) == ' ' || (c) == '\t' || (c) == '\n')
+
+static struct dentry *rawio_cmd_dentry, *rawio_output_dentry;
+static char rawio_cmd_buf[RAWIO_CMD_LEN], rawio_err_buf[RAWIO_ERR_LEN + 1];
+static DEFINE_MUTEX(rawio_lock);
+static LIST_HEAD(rawio_driver_head);
+static struct rawio_driver *active_driver;
+static enum width active_width;
+static enum ops active_ops;
+static u64 args_val[RAWIO_ARGS_MAX];
+static u8 args_postfix[RAWIO_ARGS_MAX];
+static int num_args_val;
+
+static void store_value(u64 *where, void *value, enum type type)
+{
+	switch (type) {
+	case TYPE_U8:
+		*(u8 *)where = *(u8 *)value;
+		break;
+	case TYPE_U16:
+		*(u16 *)where = *(u16 *)value;
+		break;
+	case TYPE_U32:
+		*(u32 *)where = *(u32 *)value;
+		break;
+	case TYPE_U64:
+		*where = *(u64 *)value;
+		break;
+	case TYPE_S8:
+		*(s8 *)where = *(s8 *)value;
+		break;
+	case TYPE_S16:
+		*(s16 *)where = *(s16 *)value;
+		break;
+	case TYPE_S32:
+		*(s32 *)where = *(s32 *)value;
+		break;
+	case TYPE_S64:
+		*(s64 *)where = *(s64 *)value;
+		break;
+	default:
+		break;
+	}
+}
+
+int rawio_register_driver(struct rawio_driver *driver)
+{
+	mutex_lock(&rawio_lock);
+	list_add_tail(&driver->list, &rawio_driver_head);
+	mutex_unlock(&rawio_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rawio_register_driver);
+
+int rawio_unregister_driver(struct rawio_driver *driver)
+{
+	mutex_lock(&rawio_lock);
+	list_del(&driver->list);
+	mutex_unlock(&rawio_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rawio_unregister_driver);
+
+void rawio_err(const char *fmt, ...)
+{
+	va_list args;
+	va_start(args, fmt);
+	vsnprintf(rawio_err_buf, RAWIO_ERR_LEN, fmt, args);
+	va_end(args);
+}
+EXPORT_SYMBOL_GPL(rawio_err);
+
+static int parse_arguments(char *input, char **args)
+{
+	int count, located;
+	char *p = input;
+	int input_len = strlen(input);
+
+	count = 0;
+	located = 0;
+	while (*p != 0) {
+		if (p - input >= input_len)
+			break;
+
+		/* Locate the first character of a argument */
+		if (!IS_WHITESPACE(*p)) {
+			if (!located) {
+				located = 1;
+				args[count++] = p;
+				if (count > RAWIO_ARGS_MAX)
+					break;
+			}
+		} else {
+			if (located) {
+				*p = 0;
+				located = 0;
+			}
+		}
+		p++;
+	}
+
+	return count;
+}
+
+static int parse_driver_args(struct rawio_driver *driver, char **arg_list,
+		int num_args, enum ops ops, u64 *arg_val, u8 *postfix)
+{
+	int i;
+	size_t str_len;
+	enum type type;
+	u64 value;
+	char *str;
+	char *args_postfix;
+
+	for (i = 0; i < num_args; i++) {
+		switch (ops) {
+		case OPS_RD:
+			type = driver->args_rd_types[i];
+			args_postfix = driver->args_rd_postfix;
+			break;
+		case OPS_WR:
+			type = driver->args_wr_types[i];
+			args_postfix = driver->args_wr_postfix;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		if (args_postfix[i]) {
+			str = (char *) arg_list[i];
+			str_len = strlen(str);
+			if (str[str_len - 1] == args_postfix[i]) {
+				postfix[i] = 1;
+				str[str_len - 1] = 0;
+			} else {
+				postfix[i] = 0;
+			}
+		}
+
+		if (kstrtou64(arg_list[i], 0, &value))
+				goto failed;
+		store_value(arg_val + i, &value, type);
+	}
+
+	return 0;
+
+failed:
+	snprintf(rawio_err_buf, RAWIO_ERR_LEN,
+		"invalid argument %s, usage:\n", arg_list[i]);
+	strncat(rawio_err_buf, driver->help, RAWIO_ERR_LEN -
+					strlen(rawio_err_buf));
+	return -EINVAL;
+}
+
+static struct rawio_driver *find_driver(const char *name)
+{
+	struct rawio_driver *driver;
+
+	mutex_lock(&rawio_lock);
+	list_for_each_entry(driver, &rawio_driver_head, list) {
+		if (!strncmp(driver->name, name, strlen(name))) {
+			mutex_unlock(&rawio_lock);
+			return driver;
+		}
+	}
+	mutex_unlock(&rawio_lock);
+
+	return NULL;
+}
+
+static ssize_t rawio_cmd_write(struct file *file, const char __user *buf,
+				size_t len, loff_t *offset)
+{
+	char cmd[RAWIO_CMD_LEN];
+	char *arg_list[RAWIO_ARGS_MAX];
+	int num_args;
+	enum ops ops;
+	enum width width;
+	struct rawio_driver *driver;
+
+	rawio_err_buf[0] = 0;
+
+	if (len >= RAWIO_CMD_LEN) {
+		snprintf(rawio_err_buf, RAWIO_ERR_LEN, "command is too long.\n"
+					"max allowed command length is %d\n",
+							RAWIO_CMD_LEN);
+		goto done;
+	}
+
+	if (copy_from_user(cmd, buf, len)) {
+		snprintf(rawio_err_buf, RAWIO_ERR_LEN,
+			"copy_from_user() failed.\n");
+		goto done;
+	}
+	cmd[len] = 0;
+
+	rawio_cmd_buf[0] = 0;
+	strncpy(rawio_cmd_buf, cmd, len);
+	rawio_cmd_buf[len] = 0;
+
+	num_args = parse_arguments(cmd, arg_list);
+	if (num_args < RAWIO_ARGS_MIN) {
+		snprintf(rawio_err_buf, RAWIO_ERR_LEN,
+			"invalid command(too few arguments)\n");
+		goto done;
+	}
+	if (num_args > RAWIO_ARGS_MAX) {
+		snprintf(rawio_err_buf, RAWIO_ERR_LEN,
+			"invalid command(too many arguments)\n");
+		goto done;
+	}
+
+	/* arg 0: ops(read/write) and width (8/16/32/64 bit) */
+	if (arg_list[0][0] == 'r')
+		ops = OPS_RD;
+	else if (arg_list[0][0] == 'w')
+		ops = OPS_WR;
+	else {
+		snprintf(rawio_err_buf, RAWIO_ERR_LEN,
+			"invalid operation: %c, only r and w are supported\n",
+							 arg_list[0][0]);
+		goto done;
+	}
+
+	if (strlen(arg_list[0]) >= 3) {
+		snprintf(rawio_err_buf, RAWIO_ERR_LEN,
+			"invalid bus width: %s, only 1 2 4 8 are supported\n",
+							 arg_list[0] + 1);
+		goto done;
+	}
+
+	if (strlen(arg_list[0]) == 1)
+		width = WIDTH_DEFAULT;
+	else {
+		switch (arg_list[0][1]) {
+		case '1':
+			width = WIDTH_1;
+			break;
+		case '2':
+			width = WIDTH_2;
+			break;
+		case '4':
+			width = WIDTH_4;
+			break;
+		case '8':
+			width = WIDTH_8;
+			break;
+		default:
+			snprintf(rawio_err_buf, RAWIO_ERR_LEN,
+				"invalid bus width: %c, only 1 2 4 8 are supported\n",
+								arg_list[0][1]);
+			goto done;
+		}
+	}
+
+	/* arg1: driver name */
+	driver = find_driver(arg_list[1]);
+	if (!driver) {
+		snprintf(rawio_err_buf, RAWIO_ERR_LEN,
+			"unsupported driver type: %s\n", arg_list[1]);
+		goto done;
+	}
+
+	if (width == WIDTH_DEFAULT)
+		width = driver->default_width;
+
+	if (!(width & driver->supported_width)) {
+		snprintf(rawio_err_buf, RAWIO_ERR_LEN,
+			"unsupported driver width: %s\n", arg_list[0]);
+		goto done;
+	}
+
+	/* arg2, ..., argn: driver specific arguments */
+	num_args = num_args - 2;
+	if (((ops == OPS_RD) && (num_args > driver->args_rd_max_num)) ||
+		((ops == OPS_WR) && (num_args > driver->args_wr_max_num))) {
+		snprintf(rawio_err_buf, RAWIO_ERR_LEN,
+			"too many arguments, usage:\n");
+		strncat(rawio_err_buf, driver->help, RAWIO_ERR_LEN -
+						strlen(rawio_err_buf));
+		goto done;
+	}
+	if (((ops == OPS_RD) && (num_args < driver->args_rd_min_num)) ||
+		((ops == OPS_WR) && (num_args < driver->args_wr_min_num))) {
+		snprintf(rawio_err_buf, RAWIO_ERR_LEN,
+			"too few arguments, usage:\n");
+		strncat(rawio_err_buf, driver->help, RAWIO_ERR_LEN -
+						strlen(rawio_err_buf));
+		goto done;
+	}
+
+	if (parse_driver_args(driver, arg_list + 2, num_args, ops,
+				args_val, args_postfix))
+		goto done;
+
+	active_driver = driver;
+	active_width = width;
+	active_ops = ops;
+	num_args_val = num_args;
+done:
+	return len;
+}
+
+static int rawio_output_show(struct seq_file *s, void *unused)
+{
+	u32 start, end, start_nature, end_nature;
+	int ret, i, comp1, comp2, output_len;
+	void *output;
+	char seq_buf[16];
+
+	mutex_lock(&rawio_lock);
+
+	if (strlen(rawio_err_buf) > 0) {
+		seq_puts(s, rawio_err_buf);
+		mutex_unlock(&rawio_lock);
+		return 0;
+	}
+
+	active_driver->s = s;
+
+	if (active_ops == OPS_WR) {
+		ret = active_driver->ops->write(active_driver, active_width,
+				args_val, args_postfix, num_args_val);
+		if (ret)
+			seq_puts(s, rawio_err_buf);
+		else
+			seq_puts(s, "write succeeded.\n");
+
+		mutex_unlock(&rawio_lock);
+		return 0;
+	}
+
+	if (active_driver->ops->read_and_show) {
+		ret = active_driver->ops->read_and_show(active_driver,
+			active_width, args_val, args_postfix, num_args_val);
+		if (ret)
+			seq_puts(s, rawio_err_buf);
+		mutex_unlock(&rawio_lock);
+		return 0;
+	}
+
+	ret = active_driver->ops->read(active_driver, active_width, args_val,
+			args_postfix, num_args_val, &output, &output_len);
+	if (ret) {
+		seq_puts(s, rawio_err_buf);
+		mutex_unlock(&rawio_lock);
+		return 0;
+	}
+
+	start_nature = (u32)args_val[active_driver->addr_pos];
+	start = (start_nature / LINE_WIDTH) * LINE_WIDTH;
+	end_nature = start_nature + (output_len - 1) * active_width;
+	end = (end_nature / LINE_WIDTH + 1) * LINE_WIDTH - active_width;
+	comp1 = (start_nature - start) / active_width;
+	comp2 = (end - end_nature) / active_width;
+
+	mutex_unlock(&rawio_lock);
+
+	for (i = 0; i < comp1 + comp2 + output_len; i++) {
+		if ((i % SHOW_NUM_PER_LINE) == 0) {
+			snprintf(seq_buf, sizeof(seq_buf), "[%08x]",
+						(u32)start + i * 4);
+			seq_puts(s, seq_buf);
+		}
+		if (i < comp1 || i >= output_len + comp1) {
+			switch (active_width) {
+			case WIDTH_8:
+				seq_puts(s, " ****************");
+				break;
+			case WIDTH_4:
+				seq_puts(s, " ********");
+				break;
+			case WIDTH_2:
+				seq_puts(s, " ****");
+				break;
+			case WIDTH_1:
+				seq_puts(s, " **");
+				break;
+			default:
+				break;
+			}
+		} else {
+			switch (active_width) {
+			case WIDTH_8:
+				snprintf(seq_buf, sizeof(seq_buf), "[%016llx]",
+					*((u64 *)output + i - comp1));
+				seq_puts(s, seq_buf);
+				break;
+			case WIDTH_4:
+				snprintf(seq_buf, sizeof(seq_buf), " %08x",
+					*((u32 *)output + i - comp1));
+				seq_puts(s, seq_buf);
+				break;
+			case WIDTH_2:
+				snprintf(seq_buf, sizeof(seq_buf), " %04x",
+					*((u16 *)output + i - comp1));
+				seq_puts(s, seq_buf);
+				break;
+			case WIDTH_1:
+				snprintf(seq_buf, sizeof(seq_buf), " %02x",
+					*((u8 *)output + i - comp1));
+				seq_puts(s, seq_buf);
+				break;
+			default:
+				break;
+			}
+		}
+
+		if ((i + 1) % SHOW_NUM_PER_LINE == 0)
+			seq_puts(s, "\n");
+	}
+
+	kfree(output);
+	return 0;
+}
+
+static int rawio_cmd_show(struct seq_file *s, void *unused)
+{
+	seq_puts(s, rawio_cmd_buf);
+	return 0;
+}
+
+static int rawio_cmd_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, rawio_cmd_show, NULL);
+}
+
+static const struct file_operations rawio_cmd_fops = {
+	.owner		= THIS_MODULE,
+	.open		= rawio_cmd_open,
+	.read		= seq_read,
+	.write		= rawio_cmd_write,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int rawio_output_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, rawio_output_show, NULL);
+}
+
+static const struct file_operations rawio_output_fops = {
+	.owner		= THIS_MODULE,
+	.open		= rawio_output_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int __init rawio_init(void)
+{
+	rawio_cmd_dentry = debugfs_create_file("rawio_cmd",
+		S_IFREG | S_IRUGO | S_IWUSR, NULL, NULL, &rawio_cmd_fops);
+	rawio_output_dentry = debugfs_create_file("rawio_output",
+		S_IFREG | S_IRUGO, NULL, NULL, &rawio_output_fops);
+	if (!rawio_cmd_dentry || !rawio_output_dentry) {
+		pr_err("rawio: can't create debugfs node\n");
+		return -EFAULT;
+	}
+
+	return 0;
+}
+module_init(rawio_init);
+
+static void __exit rawio_exit(void)
+{
+	debugfs_remove(rawio_cmd_dentry);
+	debugfs_remove(rawio_output_dentry);
+}
+module_exit(rawio_exit);
+
+MODULE_DESCRIPTION("Raw IO read/write utility framework driver");
+MODULE_VERSION("1.0");
+MODULE_AUTHOR("Bin Gao <bin.gao@...el.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/rawio.h b/include/linux/rawio.h
new file mode 100644
index 0000000..8f62851
--- /dev/null
+++ b/include/linux/rawio.h
@@ -0,0 +1,78 @@
+#ifndef RAWIO_H
+#define RAWIO_H
+
+#define RAWIO_DRVNAME_LEN	8
+#define RAWIO_ERR_LEN		256
+#define RAWIO_CMD_LEN		96
+#define RAWIO_HELP_LEN		128
+#define RAWIO_ARGS_MIN		3
+#define RAWIO_ARGS_MAX		10
+
+enum type {
+	TYPE_STR = 0,
+	TYPE_U8,
+	TYPE_U16,
+	TYPE_U32,
+	TYPE_U64,
+	TYPE_S8,
+	TYPE_S16,
+	TYPE_S32,
+	TYPE_S64,
+};
+
+/* read/write width: 1, 2, 4 or 8 bytes */
+enum width {
+	WIDTH_DEFAULT = 0,
+	WIDTH_1 = 1,
+	WIDTH_2 = 2,
+	WIDTH_4 = 4,
+	WIDTH_8 = 8,
+};
+
+enum ops {
+	OPS_RD = 1,	/* read */
+	OPS_WR,		/* write */
+};
+
+struct rawio_driver {
+	struct list_head list;
+	char name[RAWIO_DRVNAME_LEN];
+
+	int args_rd_max_num; /* max args for read(including optional args) */
+	enum type args_rd_types[RAWIO_ARGS_MAX]; /* type of each arg */
+	int args_rd_min_num; /* min args for read */
+	char args_rd_postfix[RAWIO_ARGS_MAX]; /* read args postfix */
+
+	int args_wr_max_num; /* max args for write(including optional args) */
+	enum type args_wr_types[RAWIO_ARGS_MAX]; /* type of each arg */
+	int args_wr_min_num; /* min args for write */
+	char args_wr_postfix[RAWIO_ARGS_MAX]; /* write args postfix */
+
+	/* index of argument that specifies the register or memory address */
+	int addr_pos;
+
+	unsigned int supported_width;
+	enum width default_width;
+	char help[RAWIO_HELP_LEN];
+	struct rawio_ops *ops;
+	struct seq_file *s;
+};
+
+struct rawio_ops {
+	/* driver reads io device and returns the data to framework */
+	int (*read) (struct rawio_driver *drv, int width,
+		u64 *input, u8 *postfix, int input_num,
+		void **output, int *output_num);
+	/* driver reads io device and shows the data */
+	int (*read_and_show) (struct rawio_driver *drv, int width,
+		u64 *input, u8 *postfix, int input_num);
+	/* driver writes data passed from framework to io device */
+	int (*write) (struct rawio_driver *driver, int width,
+		u64 *input, u8 *postfix, int input_num);
+};
+
+int rawio_register_driver(struct rawio_driver *drv);
+int rawio_unregister_driver(struct rawio_driver *drv);
+void rawio_err(const char *fmt, ...);
+
+#endif
-- 
1.8.1.2

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ