欢迎来到虫虫下载站 | 资源下载 资源专辑 关于我们
虫虫下载站

pdc_stable.c

linux 内核源代码
C
第 1 页 / 共 3 页
字号:
/*  *    Interfaces to retrieve and set PDC Stable options (firmware) * *    Copyright (C) 2005-2006 Thibaut VARENE <varenet@parisc-linux.org> * *    This program is free software; you can redistribute it and/or modify *    it under the terms of the GNU General Public License, version 2, as *    published by the Free Software Foundation. * *    This program is distributed in the hope that it will be useful, *    but WITHOUT ANY WARRANTY; without even the implied warranty of *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *    GNU General Public License for more details. * *    You should have received a copy of the GNU General Public License *    along with this program; if not, write to the Free Software *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA * * *    DEV NOTE: the PDC Procedures reference states that: *    "A minimum of 96 bytes of Stable Storage is required. Providing more than *    96 bytes of Stable Storage is optional [...]. Failure to provide the *    optional locations from 96 to 192 results in the loss of certain *    functionality during boot." * *    Since locations between 96 and 192 are the various paths, most (if not *    all) PA-RISC machines should have them. Anyway, for safety reasons, the *    following code can deal with just 96 bytes of Stable Storage, and all *    sizes between 96 and 192 bytes (provided they are multiple of struct *    device_path size, eg: 128, 160 and 192) to provide full information. *    One last word: there's one path we can always count on: the primary path. *    Anything above 224 bytes is used for 'osdep2' OS-dependent storage area. * *    The first OS-dependent area should always be available. Obviously, this is *    not true for the other one. Also bear in mind that reading/writing from/to *    osdep2 is much more expensive than from/to osdep1. *    NOTE: We do not handle the 2 bytes OS-dep area at 0x5D, nor the first *    2 bytes of storage available right after OSID. That's a total of 4 bytes *    sacrificed: -ETOOLAZY :P * *    The current policy wrt file permissions is: *	- write: root only *	- read: (reading triggers PDC calls) ? root only : everyone *    The rationale is that PDC calls could hog (DoS) the machine. * *	TODO: *	- timer/fastsize write calls */#undef PDCS_DEBUG#ifdef PDCS_DEBUG#define DPRINTK(fmt, args...)	printk(KERN_DEBUG fmt, ## args)#else#define DPRINTK(fmt, args...)#endif#include <linux/module.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/capability.h>#include <linux/ctype.h>#include <linux/sysfs.h>#include <linux/kobject.h>#include <linux/device.h>#include <linux/errno.h>#include <linux/spinlock.h>#include <asm/pdc.h>#include <asm/page.h>#include <asm/uaccess.h>#include <asm/hardware.h>#define PDCS_VERSION	"0.30"#define PDCS_PREFIX	"PDC Stable Storage"#define PDCS_ADDR_PPRI	0x00#define PDCS_ADDR_OSID	0x40#define PDCS_ADDR_OSD1	0x48#define PDCS_ADDR_DIAG	0x58#define PDCS_ADDR_FSIZ	0x5C#define PDCS_ADDR_PCON	0x60#define PDCS_ADDR_PALT	0x80#define PDCS_ADDR_PKBD	0xA0#define PDCS_ADDR_OSD2	0xE0MODULE_AUTHOR("Thibaut VARENE <varenet@parisc-linux.org>");MODULE_DESCRIPTION("sysfs interface to HP PDC Stable Storage data");MODULE_LICENSE("GPL");MODULE_VERSION(PDCS_VERSION);/* holds Stable Storage size. Initialized once and for all, no lock needed */static unsigned long pdcs_size __read_mostly;/* holds OS ID. Initialized once and for all, hopefully to 0x0006 */static u16 pdcs_osid __read_mostly;/* This struct defines what we need to deal with a parisc pdc path entry */struct pdcspath_entry {	rwlock_t rw_lock;		/* to protect path entry access */	short ready;			/* entry record is valid if != 0 */	unsigned long addr;		/* entry address in stable storage */	char *name;			/* entry name */	struct device_path devpath;	/* device path in parisc representation */	struct device *dev;		/* corresponding device */	struct kobject kobj;};struct pdcspath_attribute {	struct attribute attr;	ssize_t (*show)(struct pdcspath_entry *entry, char *buf);	ssize_t (*store)(struct pdcspath_entry *entry, const char *buf, size_t count);};#define PDCSPATH_ENTRY(_addr, _name) \struct pdcspath_entry pdcspath_entry_##_name = { \	.ready = 0, \	.addr = _addr, \	.name = __stringify(_name), \};#define PDCS_ATTR(_name, _mode, _show, _store) \struct subsys_attribute pdcs_attr_##_name = { \	.attr = {.name = __stringify(_name), .mode = _mode}, \	.show = _show, \	.store = _store, \};#define PATHS_ATTR(_name, _mode, _show, _store) \struct pdcspath_attribute paths_attr_##_name = { \	.attr = {.name = __stringify(_name), .mode = _mode}, \	.show = _show, \	.store = _store, \};#define to_pdcspath_attribute(_attr) container_of(_attr, struct pdcspath_attribute, attr)#define to_pdcspath_entry(obj)  container_of(obj, struct pdcspath_entry, kobj)/** * pdcspath_fetch - This function populates the path entry structs. * @entry: A pointer to an allocated pdcspath_entry. *  * The general idea is that you don't read from the Stable Storage every time * you access the files provided by the facilites. We store a copy of the * content of the stable storage WRT various paths in these structs. We read * these structs when reading the files, and we will write to these structs when * writing to the files, and only then write them back to the Stable Storage. * * This function expects to be called with @entry->rw_lock write-hold. */static intpdcspath_fetch(struct pdcspath_entry *entry){	struct device_path *devpath;	if (!entry)		return -EINVAL;	devpath = &entry->devpath;		DPRINTK("%s: fetch: 0x%p, 0x%p, addr: 0x%lx\n", __func__,			entry, devpath, entry->addr);	/* addr, devpath and count must be word aligned */	if (pdc_stable_read(entry->addr, devpath, sizeof(*devpath)) != PDC_OK)		return -EIO;			/* Find the matching device.	   NOTE: hardware_path overlays with device_path, so the nice cast can	   be used */	entry->dev = hwpath_to_device((struct hardware_path *)devpath);	entry->ready = 1;		DPRINTK("%s: device: 0x%p\n", __func__, entry->dev);		return 0;}/** * pdcspath_store - This function writes a path to stable storage. * @entry: A pointer to an allocated pdcspath_entry. *  * It can be used in two ways: either by passing it a preset devpath struct * containing an already computed hardware path, or by passing it a device * pointer, from which it'll find out the corresponding hardware path. * For now we do not handle the case where there's an error in writing to the * Stable Storage area, so you'd better not mess up the data :P * * This function expects to be called with @entry->rw_lock write-hold. */static voidpdcspath_store(struct pdcspath_entry *entry){	struct device_path *devpath;	BUG_ON(!entry);	devpath = &entry->devpath;		/* We expect the caller to set the ready flag to 0 if the hardware	   path struct provided is invalid, so that we know we have to fill it.	   First case, we don't have a preset hwpath... */	if (!entry->ready) {		/* ...but we have a device, map it */		BUG_ON(!entry->dev);		device_to_hwpath(entry->dev, (struct hardware_path *)devpath);	}	/* else, we expect the provided hwpath to be valid. */		DPRINTK("%s: store: 0x%p, 0x%p, addr: 0x%lx\n", __func__,			entry, devpath, entry->addr);	/* addr, devpath and count must be word aligned */	if (pdc_stable_write(entry->addr, devpath, sizeof(*devpath)) != PDC_OK) {		printk(KERN_ERR "%s: an error occured when writing to PDC.\n"				"It is likely that the Stable Storage data has been corrupted.\n"				"Please check it carefully upon next reboot.\n", __func__);		WARN_ON(1);	}			/* kobject is already registered */	entry->ready = 2;		DPRINTK("%s: device: 0x%p\n", __func__, entry->dev);}/** * pdcspath_hwpath_read - This function handles hardware path pretty printing. * @entry: An allocated and populated pdscpath_entry struct. * @buf: The output buffer to write to. *  * We will call this function to format the output of the hwpath attribute file. */static ssize_tpdcspath_hwpath_read(struct pdcspath_entry *entry, char *buf){	char *out = buf;	struct device_path *devpath;	short i;	if (!entry || !buf)		return -EINVAL;	read_lock(&entry->rw_lock);	devpath = &entry->devpath;	i = entry->ready;	read_unlock(&entry->rw_lock);	if (!i)	/* entry is not ready */		return -ENODATA;		for (i = 0; i < 6; i++) {		if (devpath->bc[i] >= 128)			continue;		out += sprintf(out, "%u/", (unsigned char)devpath->bc[i]);	}	out += sprintf(out, "%u\n", (unsigned char)devpath->mod);		return out - buf;}/** * pdcspath_hwpath_write - This function handles hardware path modifying. * @entry: An allocated and populated pdscpath_entry struct. * @buf: The input buffer to read from. * @count: The number of bytes to be read. *  * We will call this function to change the current hardware path. * Hardware paths are to be given '/'-delimited, without brackets. * We make sure that the provided path actually maps to an existing * device, BUT nothing would prevent some foolish user to set the path to some * PCI bridge or even a CPU... * A better work around would be to make sure we are at the end of a device tree * for instance, but it would be IMHO beyond the simple scope of that driver. * The aim is to provide a facility. Data correctness is left to userland. */static ssize_tpdcspath_hwpath_write(struct pdcspath_entry *entry, const char *buf, size_t count){	struct hardware_path hwpath;	unsigned short i;	char in[count+1], *temp;	struct device *dev;	int ret;	if (!entry || !buf || !count)		return -EINVAL;	/* We'll use a local copy of buf */	memset(in, 0, count+1);	strncpy(in, buf, count);		/* Let's clean up the target. 0xff is a blank pattern */	memset(&hwpath, 0xff, sizeof(hwpath));		/* First, pick the mod field (the last one of the input string) */	if (!(temp = strrchr(in, '/')))		return -EINVAL;				hwpath.mod = simple_strtoul(temp+1, NULL, 10);	in[temp-in] = '\0';	/* truncate the remaining string. just precaution */	DPRINTK("%s: mod: %d\n", __func__, hwpath.mod);		/* Then, loop for each delimiter, making sure we don't have too many.	   we write the bc fields in a down-top way. No matter what, we stop	   before writing the last field. If there are too many fields anyway,	   then the user is a moron and it'll be caught up later when we'll	   check the consistency of the given hwpath. */	for (i=5; ((temp = strrchr(in, '/'))) && (temp-in > 0) && (likely(i)); i--) {		hwpath.bc[i] = simple_strtoul(temp+1, NULL, 10);		in[temp-in] = '\0';		DPRINTK("%s: bc[%d]: %d\n", __func__, i, hwpath.bc[i]);	}		/* Store the final field */			hwpath.bc[i] = simple_strtoul(in, NULL, 10);	DPRINTK("%s: bc[%d]: %d\n", __func__, i, hwpath.bc[i]);		/* Now we check that the user isn't trying to lure us */	if (!(dev = hwpath_to_device((struct hardware_path *)&hwpath))) {		printk(KERN_WARNING "%s: attempt to set invalid \"%s\" "			"hardware path: %s\n", __func__, entry->name, buf);		return -EINVAL;	}		/* So far so good, let's get in deep */	write_lock(&entry->rw_lock);	entry->ready = 0;	entry->dev = dev;		/* Now, dive in. Write back to the hardware */	pdcspath_store(entry);		/* Update the symlink to the real device */	sysfs_remove_link(&entry->kobj, "device");	ret = sysfs_create_link(&entry->kobj, &entry->dev->kobj, "device");	WARN_ON(ret);	write_unlock(&entry->rw_lock);		printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" path to \"%s\"\n",		entry->name, buf);		return count;}/** * pdcspath_layer_read - Extended layer (eg. SCSI ids) pretty printing. * @entry: An allocated and populated pdscpath_entry struct. * @buf: The output buffer to write to. *  * We will call this function to format the output of the layer attribute file. */static ssize_tpdcspath_layer_read(struct pdcspath_entry *entry, char *buf){	char *out = buf;	struct device_path *devpath;	short i;	if (!entry || !buf)		return -EINVAL;		read_lock(&entry->rw_lock);	devpath = &entry->devpath;	i = entry->ready;	read_unlock(&entry->rw_lock);	if (!i)	/* entry is not ready */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -