brintos

brintos / linux-shallow public Read only

0
0
Text · 31.2 KiB · 16f4496 Raw
1126 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/* 3 *    Interfaces to retrieve and set PDC Stable options (firmware)4 *5 *    Copyright (C) 2005-2006 Thibaut VARENE <varenet@parisc-linux.org>6 *7 *    DEV NOTE: the PDC Procedures reference states that:8 *    "A minimum of 96 bytes of Stable Storage is required. Providing more than9 *    96 bytes of Stable Storage is optional [...]. Failure to provide the10 *    optional locations from 96 to 192 results in the loss of certain11 *    functionality during boot."12 *13 *    Since locations between 96 and 192 are the various paths, most (if not14 *    all) PA-RISC machines should have them. Anyway, for safety reasons, the15 *    following code can deal with just 96 bytes of Stable Storage, and all16 *    sizes between 96 and 192 bytes (provided they are multiple of struct17 *    pdc_module_path size, eg: 128, 160 and 192) to provide full information.18 *    One last word: there's one path we can always count on: the primary path.19 *    Anything above 224 bytes is used for 'osdep2' OS-dependent storage area.20 *21 *    The first OS-dependent area should always be available. Obviously, this is22 *    not true for the other one. Also bear in mind that reading/writing from/to23 *    osdep2 is much more expensive than from/to osdep1.24 *    NOTE: We do not handle the 2 bytes OS-dep area at 0x5D, nor the first25 *    2 bytes of storage available right after OSID. That's a total of 4 bytes26 *    sacrificed: -ETOOLAZY :P27 *28 *    The current policy wrt file permissions is:29 *	- write: root only30 *	- read: (reading triggers PDC calls) ? root only : everyone31 *    The rationale is that PDC calls could hog (DoS) the machine.32 *33 *	TODO:34 *	- timer/fastsize write calls35 */36 37#undef PDCS_DEBUG38#ifdef PDCS_DEBUG39#define DPRINTK(fmt, args...)	printk(KERN_DEBUG fmt, ## args)40#else41#define DPRINTK(fmt, args...)42#endif43 44#include <linux/module.h>45#include <linux/init.h>46#include <linux/kernel.h>47#include <linux/string.h>48#include <linux/capability.h>49#include <linux/ctype.h>50#include <linux/sysfs.h>51#include <linux/kobject.h>52#include <linux/device.h>53#include <linux/errno.h>54#include <linux/spinlock.h>55 56#include <asm/pdc.h>57#include <asm/page.h>58#include <linux/uaccess.h>59#include <asm/hardware.h>60 61#define PDCS_VERSION	"0.30"62#define PDCS_PREFIX	"PDC Stable Storage"63 64#define PDCS_ADDR_PPRI	0x0065#define PDCS_ADDR_OSID	0x4066#define PDCS_ADDR_OSD1	0x4867#define PDCS_ADDR_DIAG	0x5868#define PDCS_ADDR_FSIZ	0x5C69#define PDCS_ADDR_PCON	0x6070#define PDCS_ADDR_PALT	0x8071#define PDCS_ADDR_PKBD	0xA072#define PDCS_ADDR_OSD2	0xE073 74MODULE_AUTHOR("Thibaut VARENE <varenet@parisc-linux.org>");75MODULE_DESCRIPTION("sysfs interface to HP PDC Stable Storage data");76MODULE_LICENSE("GPL");77MODULE_VERSION(PDCS_VERSION);78 79/* holds Stable Storage size. Initialized once and for all, no lock needed */80static unsigned long pdcs_size __read_mostly;81 82/* holds OS ID. Initialized once and for all, hopefully to 0x0006 */83static u16 pdcs_osid __read_mostly;84 85/* This struct defines what we need to deal with a parisc pdc path entry */86struct pdcspath_entry {87	rwlock_t rw_lock;		/* to protect path entry access */88	short ready;			/* entry record is valid if != 0 */89	unsigned long addr;		/* entry address in stable storage */90	char *name;			/* entry name */91	struct pdc_module_path devpath;	/* device path in parisc representation */92	struct device *dev;		/* corresponding device */93	struct kobject kobj;94};95 96struct pdcspath_attribute {97	struct attribute attr;98	ssize_t (*show)(struct pdcspath_entry *entry, char *buf);99	ssize_t (*store)(struct pdcspath_entry *entry, const char *buf, size_t count);100};101 102#define PDCSPATH_ENTRY(_addr, _name) \103struct pdcspath_entry pdcspath_entry_##_name = { \104	.ready = 0, \105	.addr = _addr, \106	.name = __stringify(_name), \107};108 109#define PDCS_ATTR(_name, _mode, _show, _store) \110struct kobj_attribute pdcs_attr_##_name = { \111	.attr = {.name = __stringify(_name), .mode = _mode}, \112	.show = _show, \113	.store = _store, \114};115 116#define PATHS_ATTR(_name, _mode, _show, _store) \117struct pdcspath_attribute paths_attr_##_name = { \118	.attr = {.name = __stringify(_name), .mode = _mode}, \119	.show = _show, \120	.store = _store, \121};122 123#define to_pdcspath_attribute(_attr) container_of(_attr, struct pdcspath_attribute, attr)124#define to_pdcspath_entry(obj)  container_of(obj, struct pdcspath_entry, kobj)125 126/**127 * pdcspath_fetch - This function populates the path entry structs.128 * @entry: A pointer to an allocated pdcspath_entry.129 * 130 * The general idea is that you don't read from the Stable Storage every time131 * you access the files provided by the facilities. We store a copy of the132 * content of the stable storage WRT various paths in these structs. We read133 * these structs when reading the files, and we will write to these structs when134 * writing to the files, and only then write them back to the Stable Storage.135 *136 * This function expects to be called with @entry->rw_lock write-hold.137 */138static int139pdcspath_fetch(struct pdcspath_entry *entry)140{141	struct pdc_module_path *devpath;142 143	if (!entry)144		return -EINVAL;145 146	devpath = &entry->devpath;147	148	DPRINTK("%s: fetch: 0x%p, 0x%p, addr: 0x%lx\n", __func__,149			entry, devpath, entry->addr);150 151	/* addr, devpath and count must be word aligned */152	if (pdc_stable_read(entry->addr, devpath, sizeof(*devpath)) != PDC_OK)153		return -EIO;154		155	/* Find the matching device.156	   NOTE: hardware_path overlays with pdc_module_path, so the nice cast can157	   be used */158	entry->dev = hwpath_to_device((struct hardware_path *)devpath);159 160	entry->ready = 1;161	162	DPRINTK("%s: device: 0x%p\n", __func__, entry->dev);163	164	return 0;165}166 167/**168 * pdcspath_store - This function writes a path to stable storage.169 * @entry: A pointer to an allocated pdcspath_entry.170 * 171 * It can be used in two ways: either by passing it a preset devpath struct172 * containing an already computed hardware path, or by passing it a device173 * pointer, from which it'll find out the corresponding hardware path.174 * For now we do not handle the case where there's an error in writing to the175 * Stable Storage area, so you'd better not mess up the data :P176 *177 * This function expects to be called with @entry->rw_lock write-hold.178 */179static void180pdcspath_store(struct pdcspath_entry *entry)181{182	struct pdc_module_path *devpath;183 184	BUG_ON(!entry);185 186	devpath = &entry->devpath;187	188	/* We expect the caller to set the ready flag to 0 if the hardware189	   path struct provided is invalid, so that we know we have to fill it.190	   First case, we don't have a preset hwpath... */191	if (!entry->ready) {192		/* ...but we have a device, map it */193		BUG_ON(!entry->dev);194		device_to_hwpath(entry->dev, (struct hardware_path *)devpath);195	}196	/* else, we expect the provided hwpath to be valid. */197	198	DPRINTK("%s: store: 0x%p, 0x%p, addr: 0x%lx\n", __func__,199			entry, devpath, entry->addr);200 201	/* addr, devpath and count must be word aligned */202	if (pdc_stable_write(entry->addr, devpath, sizeof(*devpath)) != PDC_OK)203		WARN(1, KERN_ERR "%s: an error occurred when writing to PDC.\n"204				"It is likely that the Stable Storage data has been corrupted.\n"205				"Please check it carefully upon next reboot.\n", __func__);206		207	/* kobject is already registered */208	entry->ready = 2;209	210	DPRINTK("%s: device: 0x%p\n", __func__, entry->dev);211}212 213/**214 * pdcspath_hwpath_read - This function handles hardware path pretty printing.215 * @entry: An allocated and populated pdscpath_entry struct.216 * @buf: The output buffer to write to.217 * 218 * We will call this function to format the output of the hwpath attribute file.219 */220static ssize_t221pdcspath_hwpath_read(struct pdcspath_entry *entry, char *buf)222{223	char *out = buf;224	struct pdc_module_path *devpath;225	short i;226 227	if (!entry || !buf)228		return -EINVAL;229 230	read_lock(&entry->rw_lock);231	devpath = &entry->devpath;232	i = entry->ready;233	read_unlock(&entry->rw_lock);234 235	if (!i)	/* entry is not ready */236		return -ENODATA;237	238	for (i = 0; i < 6; i++) {239		if (devpath->path.bc[i] < 0)240			continue;241		out += sprintf(out, "%d/", devpath->path.bc[i]);242	}243	out += sprintf(out, "%u\n", (unsigned char)devpath->path.mod);244	245	return out - buf;246}247 248/**249 * pdcspath_hwpath_write - This function handles hardware path modifying.250 * @entry: An allocated and populated pdscpath_entry struct.251 * @buf: The input buffer to read from.252 * @count: The number of bytes to be read.253 * 254 * We will call this function to change the current hardware path.255 * Hardware paths are to be given '/'-delimited, without brackets.256 * We make sure that the provided path actually maps to an existing257 * device, BUT nothing would prevent some foolish user to set the path to some258 * PCI bridge or even a CPU...259 * A better work around would be to make sure we are at the end of a device tree260 * for instance, but it would be IMHO beyond the simple scope of that driver.261 * The aim is to provide a facility. Data correctness is left to userland.262 */263static ssize_t264pdcspath_hwpath_write(struct pdcspath_entry *entry, const char *buf, size_t count)265{266	struct hardware_path hwpath;267	unsigned short i;268	char in[64], *temp;269	struct device *dev;270	int ret;271 272	if (!entry || !buf || !count)273		return -EINVAL;274 275	/* We'll use a local copy of buf */276	count = min_t(size_t, count, sizeof(in)-1);277	strscpy(in, buf, count + 1);278	279	/* Let's clean up the target. 0xff is a blank pattern */280	memset(&hwpath, 0xff, sizeof(hwpath));281	282	/* First, pick the mod field (the last one of the input string) */283	if (!(temp = strrchr(in, '/')))284		return -EINVAL;285			286	hwpath.mod = simple_strtoul(temp+1, NULL, 10);287	in[temp-in] = '\0';	/* truncate the remaining string. just precaution */288	DPRINTK("%s: mod: %d\n", __func__, hwpath.mod);289	290	/* Then, loop for each delimiter, making sure we don't have too many.291	   we write the bc fields in a down-top way. No matter what, we stop292	   before writing the last field. If there are too many fields anyway,293	   then the user is a moron and it'll be caught up later when we'll294	   check the consistency of the given hwpath. */295	for (i=5; ((temp = strrchr(in, '/'))) && (temp-in > 0) && (likely(i)); i--) {296		hwpath.bc[i] = simple_strtoul(temp+1, NULL, 10);297		in[temp-in] = '\0';298		DPRINTK("%s: bc[%d]: %d\n", __func__, i, hwpath.path.bc[i]);299	}300	301	/* Store the final field */		302	hwpath.bc[i] = simple_strtoul(in, NULL, 10);303	DPRINTK("%s: bc[%d]: %d\n", __func__, i, hwpath.path.bc[i]);304	305	/* Now we check that the user isn't trying to lure us */306	if (!(dev = hwpath_to_device((struct hardware_path *)&hwpath))) {307		printk(KERN_WARNING "%s: attempt to set invalid \"%s\" "308			"hardware path: %s\n", __func__, entry->name, buf);309		return -EINVAL;310	}311	312	/* So far so good, let's get in deep */313	write_lock(&entry->rw_lock);314	entry->ready = 0;315	entry->dev = dev;316	317	/* Now, dive in. Write back to the hardware */318	pdcspath_store(entry);319	320	/* Update the symlink to the real device */321	sysfs_remove_link(&entry->kobj, "device");322	write_unlock(&entry->rw_lock);323 324	ret = sysfs_create_link(&entry->kobj, &entry->dev->kobj, "device");325	WARN_ON(ret);326 327	printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" path to \"%s\"\n",328		entry->name, buf);329	330	return count;331}332 333/**334 * pdcspath_layer_read - Extended layer (eg. SCSI ids) pretty printing.335 * @entry: An allocated and populated pdscpath_entry struct.336 * @buf: The output buffer to write to.337 * 338 * We will call this function to format the output of the layer attribute file.339 */340static ssize_t341pdcspath_layer_read(struct pdcspath_entry *entry, char *buf)342{343	char *out = buf;344	struct pdc_module_path *devpath;345	short i;346 347	if (!entry || !buf)348		return -EINVAL;349	350	read_lock(&entry->rw_lock);351	devpath = &entry->devpath;352	i = entry->ready;353	read_unlock(&entry->rw_lock);354 355	if (!i)	/* entry is not ready */356		return -ENODATA;357	358	for (i = 0; i < 6 && devpath->layers[i]; i++)359		out += sprintf(out, "%u ", devpath->layers[i]);360 361	out += sprintf(out, "\n");362	363	return out - buf;364}365 366/**367 * pdcspath_layer_write - This function handles extended layer modifying.368 * @entry: An allocated and populated pdscpath_entry struct.369 * @buf: The input buffer to read from.370 * @count: The number of bytes to be read.371 * 372 * We will call this function to change the current layer value.373 * Layers are to be given '.'-delimited, without brackets.374 * XXX beware we are far less checky WRT input data provided than for hwpath.375 * Potential harm can be done, since there's no way to check the validity of376 * the layer fields.377 */378static ssize_t379pdcspath_layer_write(struct pdcspath_entry *entry, const char *buf, size_t count)380{381	unsigned int layers[6]; /* device-specific info (ctlr#, unit#, ...) */382	unsigned short i;383	char in[64], *temp;384 385	if (!entry || !buf || !count)386		return -EINVAL;387 388	/* We'll use a local copy of buf */389	count = min_t(size_t, count, sizeof(in)-1);390	strscpy(in, buf, count + 1);391	392	/* Let's clean up the target. 0 is a blank pattern */393	memset(&layers, 0, sizeof(layers));394	395	/* First, pick the first layer */396	if (unlikely(!isdigit(*in)))397		return -EINVAL;398	layers[0] = simple_strtoul(in, NULL, 10);399	DPRINTK("%s: layer[0]: %d\n", __func__, layers[0]);400	401	temp = in;402	for (i=1; ((temp = strchr(temp, '.'))) && (likely(i<6)); i++) {403		if (unlikely(!isdigit(*(++temp))))404			return -EINVAL;405		layers[i] = simple_strtoul(temp, NULL, 10);406		DPRINTK("%s: layer[%d]: %d\n", __func__, i, layers[i]);407	}408		409	/* So far so good, let's get in deep */410	write_lock(&entry->rw_lock);411	412	/* First, overwrite the current layers with the new ones, not touching413	   the hardware path. */414	memcpy(&entry->devpath.layers, &layers, sizeof(layers));415	416	/* Now, dive in. Write back to the hardware */417	pdcspath_store(entry);418	write_unlock(&entry->rw_lock);419	420	printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" layers to \"%s\"\n",421		entry->name, buf);422	423	return count;424}425 426/**427 * pdcspath_attr_show - Generic read function call wrapper.428 * @kobj: The kobject to get info from.429 * @attr: The attribute looked upon.430 * @buf: The output buffer.431 */432static ssize_t433pdcspath_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)434{435	struct pdcspath_entry *entry = to_pdcspath_entry(kobj);436	struct pdcspath_attribute *pdcs_attr = to_pdcspath_attribute(attr);437	ssize_t ret = 0;438 439	if (pdcs_attr->show)440		ret = pdcs_attr->show(entry, buf);441 442	return ret;443}444 445/**446 * pdcspath_attr_store - Generic write function call wrapper.447 * @kobj: The kobject to write info to.448 * @attr: The attribute to be modified.449 * @buf: The input buffer.450 * @count: The size of the buffer.451 */452static ssize_t453pdcspath_attr_store(struct kobject *kobj, struct attribute *attr,454			const char *buf, size_t count)455{456	struct pdcspath_entry *entry = to_pdcspath_entry(kobj);457	struct pdcspath_attribute *pdcs_attr = to_pdcspath_attribute(attr);458	ssize_t ret = 0;459 460	if (!capable(CAP_SYS_ADMIN))461		return -EACCES;462 463	if (pdcs_attr->store)464		ret = pdcs_attr->store(entry, buf, count);465 466	return ret;467}468 469static const struct sysfs_ops pdcspath_attr_ops = {470	.show = pdcspath_attr_show,471	.store = pdcspath_attr_store,472};473 474/* These are the two attributes of any PDC path. */475static PATHS_ATTR(hwpath, 0644, pdcspath_hwpath_read, pdcspath_hwpath_write);476static PATHS_ATTR(layer, 0644, pdcspath_layer_read, pdcspath_layer_write);477 478static struct attribute *paths_subsys_attrs[] = {479	&paths_attr_hwpath.attr,480	&paths_attr_layer.attr,481	NULL,482};483ATTRIBUTE_GROUPS(paths_subsys);484 485/* Specific kobject type for our PDC paths */486static const struct kobj_type ktype_pdcspath = {487	.sysfs_ops = &pdcspath_attr_ops,488	.default_groups = paths_subsys_groups,489};490 491/* We hard define the 4 types of path we expect to find */492static PDCSPATH_ENTRY(PDCS_ADDR_PPRI, primary);493static PDCSPATH_ENTRY(PDCS_ADDR_PCON, console);494static PDCSPATH_ENTRY(PDCS_ADDR_PALT, alternative);495static PDCSPATH_ENTRY(PDCS_ADDR_PKBD, keyboard);496 497/* An array containing all PDC paths we will deal with */498static struct pdcspath_entry *pdcspath_entries[] = {499	&pdcspath_entry_primary,500	&pdcspath_entry_alternative,501	&pdcspath_entry_console,502	&pdcspath_entry_keyboard,503	NULL,504};505 506 507/* For more insight of what's going on here, refer to PDC Procedures doc,508 * Section PDC_STABLE */509 510/**511 * pdcs_size_read - Stable Storage size output.512 * @kobj: The kobject used to share data with userspace.513 * @attr: The kobject attributes.514 * @buf: The output buffer to write to.515 */516static ssize_t pdcs_size_read(struct kobject *kobj,517			      struct kobj_attribute *attr,518			      char *buf)519{520	char *out = buf;521 522	if (!buf)523		return -EINVAL;524 525	/* show the size of the stable storage */526	out += sprintf(out, "%ld\n", pdcs_size);527 528	return out - buf;529}530 531/**532 * pdcs_auto_read - Stable Storage autoboot/search flag output.533 * @kobj: The kobject used to share data with userspace.534 * @attr: The kobject attributes.535 * @buf: The output buffer to write to.536 * @knob: The PF_AUTOBOOT or PF_AUTOSEARCH flag537 */538static ssize_t pdcs_auto_read(struct kobject *kobj,539			      struct kobj_attribute *attr,540			      char *buf, int knob)541{542	char *out = buf;543	struct pdcspath_entry *pathentry;544 545	if (!buf)546		return -EINVAL;547 548	/* Current flags are stored in primary boot path entry */549	pathentry = &pdcspath_entry_primary;550 551	read_lock(&pathentry->rw_lock);552	out += sprintf(out, "%s\n", (pathentry->devpath.path.flags & knob) ?553					"On" : "Off");554	read_unlock(&pathentry->rw_lock);555 556	return out - buf;557}558 559/**560 * pdcs_autoboot_read - Stable Storage autoboot flag output.561 * @kobj: The kobject used to share data with userspace.562 * @attr: The kobject attributes.563 * @buf: The output buffer to write to.564 */565static ssize_t pdcs_autoboot_read(struct kobject *kobj,566				  struct kobj_attribute *attr, char *buf)567{568	return pdcs_auto_read(kobj, attr, buf, PF_AUTOBOOT);569}570 571/**572 * pdcs_autosearch_read - Stable Storage autoboot flag output.573 * @kobj: The kobject used to share data with userspace.574 * @attr: The kobject attributes.575 * @buf: The output buffer to write to.576 */577static ssize_t pdcs_autosearch_read(struct kobject *kobj,578				    struct kobj_attribute *attr, char *buf)579{580	return pdcs_auto_read(kobj, attr, buf, PF_AUTOSEARCH);581}582 583/**584 * pdcs_timer_read - Stable Storage timer count output (in seconds).585 * @kobj: The kobject used to share data with userspace.586 * @attr: The kobject attributes.587 * @buf: The output buffer to write to.588 *589 * The value of the timer field correponds to a number of seconds in powers of 2.590 */591static ssize_t pdcs_timer_read(struct kobject *kobj,592			       struct kobj_attribute *attr, char *buf)593{594	char *out = buf;595	struct pdcspath_entry *pathentry;596 597	if (!buf)598		return -EINVAL;599 600	/* Current flags are stored in primary boot path entry */601	pathentry = &pdcspath_entry_primary;602 603	/* print the timer value in seconds */604	read_lock(&pathentry->rw_lock);605	out += sprintf(out, "%u\n", (pathentry->devpath.path.flags & PF_TIMER) ?606				(1 << (pathentry->devpath.path.flags & PF_TIMER)) : 0);607	read_unlock(&pathentry->rw_lock);608 609	return out - buf;610}611 612/**613 * pdcs_osid_read - Stable Storage OS ID register output.614 * @kobj: The kobject used to share data with userspace.615 * @attr: The kobject attributes.616 * @buf: The output buffer to write to.617 */618static ssize_t pdcs_osid_read(struct kobject *kobj,619			      struct kobj_attribute *attr, char *buf)620{621	char *out = buf;622 623	if (!buf)624		return -EINVAL;625 626	out += sprintf(out, "%s dependent data (0x%.4x)\n",627		os_id_to_string(pdcs_osid), pdcs_osid);628 629	return out - buf;630}631 632/**633 * pdcs_osdep1_read - Stable Storage OS-Dependent data area 1 output.634 * @kobj: The kobject used to share data with userspace.635 * @attr: The kobject attributes.636 * @buf: The output buffer to write to.637 *638 * This can hold 16 bytes of OS-Dependent data.639 */640static ssize_t pdcs_osdep1_read(struct kobject *kobj,641				struct kobj_attribute *attr, char *buf)642{643	char *out = buf;644	u32 result[4];645 646	if (!buf)647		return -EINVAL;648 649	if (pdc_stable_read(PDCS_ADDR_OSD1, &result, sizeof(result)) != PDC_OK)650		return -EIO;651 652	out += sprintf(out, "0x%.8x\n", result[0]);653	out += sprintf(out, "0x%.8x\n", result[1]);654	out += sprintf(out, "0x%.8x\n", result[2]);655	out += sprintf(out, "0x%.8x\n", result[3]);656 657	return out - buf;658}659 660/**661 * pdcs_diagnostic_read - Stable Storage Diagnostic register output.662 * @kobj: The kobject used to share data with userspace.663 * @attr: The kobject attributes.664 * @buf: The output buffer to write to.665 *666 * I have NFC how to interpret the content of that register ;-).667 */668static ssize_t pdcs_diagnostic_read(struct kobject *kobj,669				    struct kobj_attribute *attr, char *buf)670{671	char *out = buf;672	u32 result;673 674	if (!buf)675		return -EINVAL;676 677	/* get diagnostic */678	if (pdc_stable_read(PDCS_ADDR_DIAG, &result, sizeof(result)) != PDC_OK)679		return -EIO;680 681	out += sprintf(out, "0x%.4x\n", (result >> 16));682 683	return out - buf;684}685 686/**687 * pdcs_fastsize_read - Stable Storage FastSize register output.688 * @kobj: The kobject used to share data with userspace.689 * @attr: The kobject attributes.690 * @buf: The output buffer to write to.691 *692 * This register holds the amount of system RAM to be tested during boot sequence.693 */694static ssize_t pdcs_fastsize_read(struct kobject *kobj,695				  struct kobj_attribute *attr, char *buf)696{697	char *out = buf;698	u32 result;699 700	if (!buf)701		return -EINVAL;702 703	/* get fast-size */704	if (pdc_stable_read(PDCS_ADDR_FSIZ, &result, sizeof(result)) != PDC_OK)705		return -EIO;706 707	if ((result & 0x0F) < 0x0E)708		out += sprintf(out, "%d kB", (1<<(result & 0x0F))*256);709	else710		out += sprintf(out, "All");711	out += sprintf(out, "\n");712	713	return out - buf;714}715 716/**717 * pdcs_osdep2_read - Stable Storage OS-Dependent data area 2 output.718 * @kobj: The kobject used to share data with userspace.719 * @attr: The kobject attributes.720 * @buf: The output buffer to write to.721 *722 * This can hold pdcs_size - 224 bytes of OS-Dependent data, when available.723 */724static ssize_t pdcs_osdep2_read(struct kobject *kobj,725				struct kobj_attribute *attr, char *buf)726{727	char *out = buf;728	unsigned long size;729	unsigned short i;730	u32 result;731 732	if (unlikely(pdcs_size <= 224))733		return -ENODATA;734 735	size = pdcs_size - 224;736 737	if (!buf)738		return -EINVAL;739 740	for (i=0; i<size; i+=4) {741		if (unlikely(pdc_stable_read(PDCS_ADDR_OSD2 + i, &result,742					sizeof(result)) != PDC_OK))743			return -EIO;744		out += sprintf(out, "0x%.8x\n", result);745	}746 747	return out - buf;748}749 750/**751 * pdcs_auto_write - This function handles autoboot/search flag modifying.752 * @kobj: The kobject used to share data with userspace.753 * @attr: The kobject attributes.754 * @buf: The input buffer to read from.755 * @count: The number of bytes to be read.756 * @knob: The PF_AUTOBOOT or PF_AUTOSEARCH flag757 * 758 * We will call this function to change the current autoboot flag.759 * We expect a precise syntax:760 *	\"n\" (n == 0 or 1) to toggle AutoBoot Off or On761 */762static ssize_t pdcs_auto_write(struct kobject *kobj,763			       struct kobj_attribute *attr, const char *buf,764			       size_t count, int knob)765{766	struct pdcspath_entry *pathentry;767	unsigned char flags;768	char in[8], *temp;769	char c;770 771	if (!capable(CAP_SYS_ADMIN))772		return -EACCES;773 774	if (!buf || !count)775		return -EINVAL;776 777	/* We'll use a local copy of buf */778	count = min_t(size_t, count, sizeof(in)-1);779	strscpy(in, buf, count + 1);780 781	/* Current flags are stored in primary boot path entry */782	pathentry = &pdcspath_entry_primary;783	784	/* Be nice to the existing flag record */785	read_lock(&pathentry->rw_lock);786	flags = pathentry->devpath.path.flags;787	read_unlock(&pathentry->rw_lock);788	789	DPRINTK("%s: flags before: 0x%X\n", __func__, flags);790 791	temp = skip_spaces(in);792 793	c = *temp++ - '0';794	if ((c != 0) && (c != 1))795		goto parse_error;796	if (c == 0)797		flags &= ~knob;798	else799		flags |= knob;800	801	DPRINTK("%s: flags after: 0x%X\n", __func__, flags);802		803	/* So far so good, let's get in deep */804	write_lock(&pathentry->rw_lock);805	806	/* Change the path entry flags first */807	pathentry->devpath.path.flags = flags;808		809	/* Now, dive in. Write back to the hardware */810	pdcspath_store(pathentry);811	write_unlock(&pathentry->rw_lock);812	813	printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" to \"%s\"\n",814		(knob & PF_AUTOBOOT) ? "autoboot" : "autosearch",815		(flags & knob) ? "On" : "Off");816	817	return count;818 819parse_error:820	printk(KERN_WARNING "%s: Parse error: expect \"n\" (n == 0 or 1)\n", __func__);821	return -EINVAL;822}823 824/**825 * pdcs_autoboot_write - This function handles autoboot flag modifying.826 * @kobj: The kobject used to share data with userspace.827 * @attr: The kobject attributes.828 * @buf: The input buffer to read from.829 * @count: The number of bytes to be read.830 *831 * We will call this function to change the current boot flags.832 * We expect a precise syntax:833 *	\"n\" (n == 0 or 1) to toggle AutoSearch Off or On834 */835static ssize_t pdcs_autoboot_write(struct kobject *kobj,836				   struct kobj_attribute *attr,837				   const char *buf, size_t count)838{839	return pdcs_auto_write(kobj, attr, buf, count, PF_AUTOBOOT);840}841 842/**843 * pdcs_autosearch_write - This function handles autosearch flag modifying.844 * @kobj: The kobject used to share data with userspace.845 * @attr: The kobject attributes.846 * @buf: The input buffer to read from.847 * @count: The number of bytes to be read.848 *849 * We will call this function to change the current boot flags.850 * We expect a precise syntax:851 *	\"n\" (n == 0 or 1) to toggle AutoSearch Off or On852 */853static ssize_t pdcs_autosearch_write(struct kobject *kobj,854				     struct kobj_attribute *attr,855				     const char *buf, size_t count)856{857	return pdcs_auto_write(kobj, attr, buf, count, PF_AUTOSEARCH);858}859 860/**861 * pdcs_osdep1_write - Stable Storage OS-Dependent data area 1 input.862 * @kobj: The kobject used to share data with userspace.863 * @attr: The kobject attributes.864 * @buf: The input buffer to read from.865 * @count: The number of bytes to be read.866 *867 * This can store 16 bytes of OS-Dependent data. We use a byte-by-byte868 * write approach. It's up to userspace to deal with it when constructing869 * its input buffer.870 */871static ssize_t pdcs_osdep1_write(struct kobject *kobj,872				 struct kobj_attribute *attr,873				 const char *buf, size_t count)874{875	u8 in[16];876 877	if (!capable(CAP_SYS_ADMIN))878		return -EACCES;879 880	if (!buf || !count)881		return -EINVAL;882 883	if (unlikely(pdcs_osid != OS_ID_LINUX))884		return -EPERM;885 886	if (count > 16)887		return -EMSGSIZE;888 889	/* We'll use a local copy of buf */890	memset(in, 0, 16);891	memcpy(in, buf, count);892 893	if (pdc_stable_write(PDCS_ADDR_OSD1, &in, sizeof(in)) != PDC_OK)894		return -EIO;895 896	return count;897}898 899/**900 * pdcs_osdep2_write - Stable Storage OS-Dependent data area 2 input.901 * @kobj: The kobject used to share data with userspace.902 * @attr: The kobject attributes.903 * @buf: The input buffer to read from.904 * @count: The number of bytes to be read.905 *906 * This can store pdcs_size - 224 bytes of OS-Dependent data. We use a907 * byte-by-byte write approach. It's up to userspace to deal with it when908 * constructing its input buffer.909 */910static ssize_t pdcs_osdep2_write(struct kobject *kobj,911				 struct kobj_attribute *attr,912				 const char *buf, size_t count)913{914	unsigned long size;915	unsigned short i;916	u8 in[4];917 918	if (!capable(CAP_SYS_ADMIN))919		return -EACCES;920 921	if (!buf || !count)922		return -EINVAL;923 924	if (unlikely(pdcs_size <= 224))925		return -ENOSYS;926 927	if (unlikely(pdcs_osid != OS_ID_LINUX))928		return -EPERM;929 930	size = pdcs_size - 224;931 932	if (count > size)933		return -EMSGSIZE;934 935	/* We'll use a local copy of buf */936 937	for (i=0; i<count; i+=4) {938		memset(in, 0, 4);939		memcpy(in, buf+i, (count-i < 4) ? count-i : 4);940		if (unlikely(pdc_stable_write(PDCS_ADDR_OSD2 + i, &in,941					sizeof(in)) != PDC_OK))942			return -EIO;943	}944 945	return count;946}947 948/* The remaining attributes. */949static PDCS_ATTR(size, 0444, pdcs_size_read, NULL);950static PDCS_ATTR(autoboot, 0644, pdcs_autoboot_read, pdcs_autoboot_write);951static PDCS_ATTR(autosearch, 0644, pdcs_autosearch_read, pdcs_autosearch_write);952static PDCS_ATTR(timer, 0444, pdcs_timer_read, NULL);953static PDCS_ATTR(osid, 0444, pdcs_osid_read, NULL);954static PDCS_ATTR(osdep1, 0600, pdcs_osdep1_read, pdcs_osdep1_write);955static PDCS_ATTR(diagnostic, 0400, pdcs_diagnostic_read, NULL);956static PDCS_ATTR(fastsize, 0400, pdcs_fastsize_read, NULL);957static PDCS_ATTR(osdep2, 0600, pdcs_osdep2_read, pdcs_osdep2_write);958 959static struct attribute *pdcs_subsys_attrs[] = {960	&pdcs_attr_size.attr,961	&pdcs_attr_autoboot.attr,962	&pdcs_attr_autosearch.attr,963	&pdcs_attr_timer.attr,964	&pdcs_attr_osid.attr,965	&pdcs_attr_osdep1.attr,966	&pdcs_attr_diagnostic.attr,967	&pdcs_attr_fastsize.attr,968	&pdcs_attr_osdep2.attr,969	NULL,970};971 972static const struct attribute_group pdcs_attr_group = {973	.attrs = pdcs_subsys_attrs,974};975 976static struct kobject *stable_kobj;977static struct kset *paths_kset;978 979/**980 * pdcs_register_pathentries - Prepares path entries kobjects for sysfs usage.981 * 982 * It creates kobjects corresponding to each path entry with nice sysfs983 * links to the real device. This is where the magic takes place: when984 * registering the subsystem attributes during module init, each kobject hereby985 * created will show in the sysfs tree as a folder containing files as defined986 * by path_subsys_attr[].987 */988static inline int __init989pdcs_register_pathentries(void)990{991	unsigned short i;992	struct pdcspath_entry *entry;993	int err;994	995	/* Initialize the entries rw_lock before anything else */996	for (i = 0; (entry = pdcspath_entries[i]); i++)997		rwlock_init(&entry->rw_lock);998 999	for (i = 0; (entry = pdcspath_entries[i]); i++) {1000		write_lock(&entry->rw_lock);1001		err = pdcspath_fetch(entry);1002		write_unlock(&entry->rw_lock);1003 1004		if (err < 0)1005			continue;1006 1007		entry->kobj.kset = paths_kset;1008		err = kobject_init_and_add(&entry->kobj, &ktype_pdcspath, NULL,1009					   "%s", entry->name);1010		if (err) {1011			kobject_put(&entry->kobj);1012			return err;1013		}1014 1015		/* kobject is now registered */1016		write_lock(&entry->rw_lock);1017		entry->ready = 2;1018		write_unlock(&entry->rw_lock);1019		1020		/* Add a nice symlink to the real device */1021		if (entry->dev) {1022			err = sysfs_create_link(&entry->kobj, &entry->dev->kobj, "device");1023			WARN_ON(err);1024		}1025 1026		kobject_uevent(&entry->kobj, KOBJ_ADD);1027	}1028	1029	return 0;1030}1031 1032/**1033 * pdcs_unregister_pathentries - Routine called when unregistering the module.1034 */1035static inline void1036pdcs_unregister_pathentries(void)1037{1038	unsigned short i;1039	struct pdcspath_entry *entry;1040	1041	for (i = 0; (entry = pdcspath_entries[i]); i++) {1042		read_lock(&entry->rw_lock);1043		if (entry->ready >= 2)1044			kobject_put(&entry->kobj);1045		read_unlock(&entry->rw_lock);1046	}1047}1048 1049/*1050 * For now we register the stable subsystem with the firmware subsystem1051 * and the paths subsystem with the stable subsystem1052 */1053static int __init1054pdc_stable_init(void)1055{1056	int rc = 0, error;1057	u32 result;1058 1059	/* find the size of the stable storage */1060	if (pdc_stable_get_size(&pdcs_size) != PDC_OK) 1061		return -ENODEV;1062 1063	/* make sure we have enough data */1064	if (pdcs_size < 96)1065		return -ENODATA;1066 1067	printk(KERN_INFO PDCS_PREFIX " facility v%s\n", PDCS_VERSION);1068 1069	/* get OSID */1070	if (pdc_stable_read(PDCS_ADDR_OSID, &result, sizeof(result)) != PDC_OK)1071		return -EIO;1072 1073	/* the actual result is 16 bits away */1074	pdcs_osid = (u16)(result >> 16);1075 1076	/* For now we'll register the directory at /sys/firmware/stable */1077	stable_kobj = kobject_create_and_add("stable", firmware_kobj);1078	if (!stable_kobj) {1079		rc = -ENOMEM;1080		goto fail_firmreg;1081	}1082 1083	/* Don't forget the root entries */1084	error = sysfs_create_group(stable_kobj, &pdcs_attr_group);1085	if (error) {1086		rc = -ENOMEM;1087		goto fail_ksetreg;1088	}1089 1090	/* register the paths kset as a child of the stable kset */1091	paths_kset = kset_create_and_add("paths", NULL, stable_kobj);1092	if (!paths_kset) {1093		rc = -ENOMEM;1094		goto fail_ksetreg;1095	}1096 1097	/* now we create all "files" for the paths kset */1098	if ((rc = pdcs_register_pathentries()))1099		goto fail_pdcsreg;1100 1101	return rc;1102	1103fail_pdcsreg:1104	pdcs_unregister_pathentries();1105	kset_unregister(paths_kset);1106	1107fail_ksetreg:1108	kobject_put(stable_kobj);1109	1110fail_firmreg:1111	printk(KERN_INFO PDCS_PREFIX " bailing out\n");1112	return rc;1113}1114 1115static void __exit1116pdc_stable_exit(void)1117{1118	pdcs_unregister_pathentries();1119	kset_unregister(paths_kset);1120	kobject_put(stable_kobj);1121}1122 1123 1124module_init(pdc_stable_init);1125module_exit(pdc_stable_exit);1126