diff options
Diffstat (limited to 'drivers/parisc/pdc_stable.c')
| -rw-r--r-- | drivers/parisc/pdc_stable.c | 356 |
1 files changed, 262 insertions, 94 deletions
diff --git a/drivers/parisc/pdc_stable.c b/drivers/parisc/pdc_stable.c index 42a3c54e8e6c..a28e17898fbd 100644 --- a/drivers/parisc/pdc_stable.c +++ b/drivers/parisc/pdc_stable.c | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Interfaces to retrieve and set PDC Stable options (firmware) | 2 | * Interfaces to retrieve and set PDC Stable options (firmware) |
| 3 | * | 3 | * |
| 4 | * Copyright (C) 2005 Thibaut VARENE <varenet@parisc-linux.org> | 4 | * Copyright (C) 2005-2006 Thibaut VARENE <varenet@parisc-linux.org> |
| 5 | * | 5 | * |
| 6 | * This program is free software; you can redistribute it and/or modify | 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | * it under the terms of the GNU General Public License as published by | 7 | * it under the terms of the GNU General Public License as published by |
| @@ -26,11 +26,19 @@ | |||
| 26 | * | 26 | * |
| 27 | * Since locations between 96 and 192 are the various paths, most (if not | 27 | * Since locations between 96 and 192 are the various paths, most (if not |
| 28 | * all) PA-RISC machines should have them. Anyway, for safety reasons, the | 28 | * all) PA-RISC machines should have them. Anyway, for safety reasons, the |
| 29 | * following code can deal with only 96 bytes of Stable Storage, and all | 29 | * following code can deal with just 96 bytes of Stable Storage, and all |
| 30 | * sizes between 96 and 192 bytes (provided they are multiple of struct | 30 | * sizes between 96 and 192 bytes (provided they are multiple of struct |
| 31 | * device_path size, eg: 128, 160 and 192) to provide full information. | 31 | * device_path size, eg: 128, 160 and 192) to provide full information. |
| 32 | * The code makes no use of data above 192 bytes. One last word: there's one | 32 | * The code makes no use of data above 192 bytes. One last word: there's one |
| 33 | * path we can always count on: the primary path. | 33 | * path we can always count on: the primary path. |
| 34 | * | ||
| 35 | * The current policy wrt file permissions is: | ||
| 36 | * - write: root only | ||
| 37 | * - read: (reading triggers PDC calls) ? root only : everyone | ||
| 38 | * The rationale is that PDC calls could hog (DoS) the machine. | ||
| 39 | * | ||
| 40 | * TODO: | ||
| 41 | * - timer/fastsize write calls | ||
| 34 | */ | 42 | */ |
| 35 | 43 | ||
| 36 | #undef PDCS_DEBUG | 44 | #undef PDCS_DEBUG |
| @@ -50,13 +58,15 @@ | |||
| 50 | #include <linux/kobject.h> | 58 | #include <linux/kobject.h> |
| 51 | #include <linux/device.h> | 59 | #include <linux/device.h> |
| 52 | #include <linux/errno.h> | 60 | #include <linux/errno.h> |
| 61 | #include <linux/spinlock.h> | ||
| 53 | 62 | ||
| 54 | #include <asm/pdc.h> | 63 | #include <asm/pdc.h> |
| 55 | #include <asm/page.h> | 64 | #include <asm/page.h> |
| 56 | #include <asm/uaccess.h> | 65 | #include <asm/uaccess.h> |
| 57 | #include <asm/hardware.h> | 66 | #include <asm/hardware.h> |
| 58 | 67 | ||
| 59 | #define PDCS_VERSION "0.10" | 68 | #define PDCS_VERSION "0.22" |
| 69 | #define PDCS_PREFIX "PDC Stable Storage" | ||
| 60 | 70 | ||
| 61 | #define PDCS_ADDR_PPRI 0x00 | 71 | #define PDCS_ADDR_PPRI 0x00 |
| 62 | #define PDCS_ADDR_OSID 0x40 | 72 | #define PDCS_ADDR_OSID 0x40 |
| @@ -70,10 +80,12 @@ MODULE_DESCRIPTION("sysfs interface to HP PDC Stable Storage data"); | |||
| 70 | MODULE_LICENSE("GPL"); | 80 | MODULE_LICENSE("GPL"); |
| 71 | MODULE_VERSION(PDCS_VERSION); | 81 | MODULE_VERSION(PDCS_VERSION); |
| 72 | 82 | ||
| 83 | /* holds Stable Storage size. Initialized once and for all, no lock needed */ | ||
| 73 | static unsigned long pdcs_size __read_mostly; | 84 | static unsigned long pdcs_size __read_mostly; |
| 74 | 85 | ||
| 75 | /* This struct defines what we need to deal with a parisc pdc path entry */ | 86 | /* This struct defines what we need to deal with a parisc pdc path entry */ |
| 76 | struct pdcspath_entry { | 87 | struct pdcspath_entry { |
| 88 | rwlock_t rw_lock; /* to protect path entry access */ | ||
| 77 | short ready; /* entry record is valid if != 0 */ | 89 | short ready; /* entry record is valid if != 0 */ |
| 78 | unsigned long addr; /* entry address in stable storage */ | 90 | unsigned long addr; /* entry address in stable storage */ |
| 79 | char *name; /* entry name */ | 91 | char *name; /* entry name */ |
| @@ -121,6 +133,8 @@ struct pdcspath_attribute paths_attr_##_name = { \ | |||
| 121 | * content of the stable storage WRT various paths in these structs. We read | 133 | * content of the stable storage WRT various paths in these structs. We read |
| 122 | * these structs when reading the files, and we will write to these structs when | 134 | * these structs when reading the files, and we will write to these structs when |
| 123 | * writing to the files, and only then write them back to the Stable Storage. | 135 | * writing to the files, and only then write them back to the Stable Storage. |
| 136 | * | ||
| 137 | * This function expects to be called with @entry->rw_lock write-hold. | ||
| 124 | */ | 138 | */ |
| 125 | static int | 139 | static int |
| 126 | pdcspath_fetch(struct pdcspath_entry *entry) | 140 | pdcspath_fetch(struct pdcspath_entry *entry) |
| @@ -160,14 +174,15 @@ pdcspath_fetch(struct pdcspath_entry *entry) | |||
| 160 | * pointer, from which it'll find out the corresponding hardware path. | 174 | * pointer, from which it'll find out the corresponding hardware path. |
| 161 | * For now we do not handle the case where there's an error in writing to the | 175 | * For now we do not handle the case where there's an error in writing to the |
| 162 | * Stable Storage area, so you'd better not mess up the data :P | 176 | * Stable Storage area, so you'd better not mess up the data :P |
| 177 | * | ||
| 178 | * This function expects to be called with @entry->rw_lock write-hold. | ||
| 163 | */ | 179 | */ |
| 164 | static int | 180 | static void |
| 165 | pdcspath_store(struct pdcspath_entry *entry) | 181 | pdcspath_store(struct pdcspath_entry *entry) |
| 166 | { | 182 | { |
| 167 | struct device_path *devpath; | 183 | struct device_path *devpath; |
| 168 | 184 | ||
| 169 | if (!entry) | 185 | BUG_ON(!entry); |
| 170 | return -EINVAL; | ||
| 171 | 186 | ||
| 172 | devpath = &entry->devpath; | 187 | devpath = &entry->devpath; |
| 173 | 188 | ||
| @@ -176,10 +191,8 @@ pdcspath_store(struct pdcspath_entry *entry) | |||
| 176 | First case, we don't have a preset hwpath... */ | 191 | First case, we don't have a preset hwpath... */ |
| 177 | if (!entry->ready) { | 192 | if (!entry->ready) { |
| 178 | /* ...but we have a device, map it */ | 193 | /* ...but we have a device, map it */ |
| 179 | if (entry->dev) | 194 | BUG_ON(!entry->dev); |
| 180 | device_to_hwpath(entry->dev, (struct hardware_path *)devpath); | 195 | device_to_hwpath(entry->dev, (struct hardware_path *)devpath); |
| 181 | else | ||
| 182 | return -EINVAL; | ||
| 183 | } | 196 | } |
| 184 | /* else, we expect the provided hwpath to be valid. */ | 197 | /* else, we expect the provided hwpath to be valid. */ |
| 185 | 198 | ||
| @@ -191,15 +204,13 @@ pdcspath_store(struct pdcspath_entry *entry) | |||
| 191 | printk(KERN_ERR "%s: an error occured when writing to PDC.\n" | 204 | printk(KERN_ERR "%s: an error occured when writing to PDC.\n" |
| 192 | "It is likely that the Stable Storage data has been corrupted.\n" | 205 | "It is likely that the Stable Storage data has been corrupted.\n" |
| 193 | "Please check it carefully upon next reboot.\n", __func__); | 206 | "Please check it carefully upon next reboot.\n", __func__); |
| 194 | return -EIO; | 207 | WARN_ON(1); |
| 195 | } | 208 | } |
| 196 | 209 | ||
| 197 | /* kobject is already registered */ | 210 | /* kobject is already registered */ |
| 198 | entry->ready = 2; | 211 | entry->ready = 2; |
| 199 | 212 | ||
| 200 | DPRINTK("%s: device: 0x%p\n", __func__, entry->dev); | 213 | DPRINTK("%s: device: 0x%p\n", __func__, entry->dev); |
| 201 | |||
| 202 | return 0; | ||
| 203 | } | 214 | } |
| 204 | 215 | ||
| 205 | /** | 216 | /** |
| @@ -214,14 +225,17 @@ pdcspath_hwpath_read(struct pdcspath_entry *entry, char *buf) | |||
| 214 | { | 225 | { |
| 215 | char *out = buf; | 226 | char *out = buf; |
| 216 | struct device_path *devpath; | 227 | struct device_path *devpath; |
| 217 | unsigned short i; | 228 | short i; |
| 218 | 229 | ||
| 219 | if (!entry || !buf) | 230 | if (!entry || !buf) |
| 220 | return -EINVAL; | 231 | return -EINVAL; |
| 221 | 232 | ||
| 233 | read_lock(&entry->rw_lock); | ||
| 222 | devpath = &entry->devpath; | 234 | devpath = &entry->devpath; |
| 235 | i = entry->ready; | ||
| 236 | read_unlock(&entry->rw_lock); | ||
| 223 | 237 | ||
| 224 | if (!entry->ready) | 238 | if (!i) /* entry is not ready */ |
| 225 | return -ENODATA; | 239 | return -ENODATA; |
| 226 | 240 | ||
| 227 | for (i = 0; i < 6; i++) { | 241 | for (i = 0; i < 6; i++) { |
| @@ -242,7 +256,7 @@ pdcspath_hwpath_read(struct pdcspath_entry *entry, char *buf) | |||
| 242 | * | 256 | * |
| 243 | * We will call this function to change the current hardware path. | 257 | * We will call this function to change the current hardware path. |
| 244 | * Hardware paths are to be given '/'-delimited, without brackets. | 258 | * Hardware paths are to be given '/'-delimited, without brackets. |
| 245 | * We take care to make sure that the provided path actually maps to an existing | 259 | * We make sure that the provided path actually maps to an existing |
| 246 | * device, BUT nothing would prevent some foolish user to set the path to some | 260 | * device, BUT nothing would prevent some foolish user to set the path to some |
| 247 | * PCI bridge or even a CPU... | 261 | * PCI bridge or even a CPU... |
| 248 | * A better work around would be to make sure we are at the end of a device tree | 262 | * A better work around would be to make sure we are at the end of a device tree |
| @@ -298,17 +312,19 @@ pdcspath_hwpath_write(struct pdcspath_entry *entry, const char *buf, size_t coun | |||
| 298 | } | 312 | } |
| 299 | 313 | ||
| 300 | /* So far so good, let's get in deep */ | 314 | /* So far so good, let's get in deep */ |
| 315 | write_lock(&entry->rw_lock); | ||
| 301 | entry->ready = 0; | 316 | entry->ready = 0; |
| 302 | entry->dev = dev; | 317 | entry->dev = dev; |
| 303 | 318 | ||
| 304 | /* Now, dive in. Write back to the hardware */ | 319 | /* Now, dive in. Write back to the hardware */ |
| 305 | WARN_ON(pdcspath_store(entry)); /* this warn should *NEVER* happen */ | 320 | pdcspath_store(entry); |
| 306 | 321 | ||
| 307 | /* Update the symlink to the real device */ | 322 | /* Update the symlink to the real device */ |
| 308 | sysfs_remove_link(&entry->kobj, "device"); | 323 | sysfs_remove_link(&entry->kobj, "device"); |
| 309 | sysfs_create_link(&entry->kobj, &entry->dev->kobj, "device"); | 324 | sysfs_create_link(&entry->kobj, &entry->dev->kobj, "device"); |
| 325 | write_unlock(&entry->rw_lock); | ||
| 310 | 326 | ||
| 311 | printk(KERN_INFO "PDC Stable Storage: changed \"%s\" path to \"%s\"\n", | 327 | printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" path to \"%s\"\n", |
| 312 | entry->name, buf); | 328 | entry->name, buf); |
| 313 | 329 | ||
| 314 | return count; | 330 | return count; |
| @@ -326,14 +342,17 @@ pdcspath_layer_read(struct pdcspath_entry *entry, char *buf) | |||
| 326 | { | 342 | { |
| 327 | char *out = buf; | 343 | char *out = buf; |
| 328 | struct device_path *devpath; | 344 | struct device_path *devpath; |
| 329 | unsigned short i; | 345 | short i; |
| 330 | 346 | ||
| 331 | if (!entry || !buf) | 347 | if (!entry || !buf) |
| 332 | return -EINVAL; | 348 | return -EINVAL; |
| 333 | 349 | ||
| 350 | read_lock(&entry->rw_lock); | ||
| 334 | devpath = &entry->devpath; | 351 | devpath = &entry->devpath; |
| 352 | i = entry->ready; | ||
| 353 | read_unlock(&entry->rw_lock); | ||
| 335 | 354 | ||
| 336 | if (!entry->ready) | 355 | if (!i) /* entry is not ready */ |
| 337 | return -ENODATA; | 356 | return -ENODATA; |
| 338 | 357 | ||
| 339 | for (i = 0; devpath->layers[i] && (likely(i < 6)); i++) | 358 | for (i = 0; devpath->layers[i] && (likely(i < 6)); i++) |
| @@ -388,15 +407,17 @@ pdcspath_layer_write(struct pdcspath_entry *entry, const char *buf, size_t count | |||
| 388 | } | 407 | } |
| 389 | 408 | ||
| 390 | /* So far so good, let's get in deep */ | 409 | /* So far so good, let's get in deep */ |
| 410 | write_lock(&entry->rw_lock); | ||
| 391 | 411 | ||
| 392 | /* First, overwrite the current layers with the new ones, not touching | 412 | /* First, overwrite the current layers with the new ones, not touching |
| 393 | the hardware path. */ | 413 | the hardware path. */ |
| 394 | memcpy(&entry->devpath.layers, &layers, sizeof(layers)); | 414 | memcpy(&entry->devpath.layers, &layers, sizeof(layers)); |
| 395 | 415 | ||
| 396 | /* Now, dive in. Write back to the hardware */ | 416 | /* Now, dive in. Write back to the hardware */ |
| 397 | WARN_ON(pdcspath_store(entry)); /* this warn should *NEVER* happen */ | 417 | pdcspath_store(entry); |
| 418 | write_unlock(&entry->rw_lock); | ||
| 398 | 419 | ||
| 399 | printk(KERN_INFO "PDC Stable Storage: changed \"%s\" layers to \"%s\"\n", | 420 | printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" layers to \"%s\"\n", |
| 400 | entry->name, buf); | 421 | entry->name, buf); |
| 401 | 422 | ||
| 402 | return count; | 423 | return count; |
| @@ -415,9 +436,6 @@ pdcspath_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) | |||
| 415 | struct pdcspath_attribute *pdcs_attr = to_pdcspath_attribute(attr); | 436 | struct pdcspath_attribute *pdcs_attr = to_pdcspath_attribute(attr); |
| 416 | ssize_t ret = 0; | 437 | ssize_t ret = 0; |
| 417 | 438 | ||
| 418 | if (!capable(CAP_SYS_ADMIN)) | ||
| 419 | return -EACCES; | ||
| 420 | |||
| 421 | if (pdcs_attr->show) | 439 | if (pdcs_attr->show) |
| 422 | ret = pdcs_attr->show(entry, buf); | 440 | ret = pdcs_attr->show(entry, buf); |
| 423 | 441 | ||
| @@ -454,8 +472,8 @@ static struct sysfs_ops pdcspath_attr_ops = { | |||
| 454 | }; | 472 | }; |
| 455 | 473 | ||
| 456 | /* These are the two attributes of any PDC path. */ | 474 | /* These are the two attributes of any PDC path. */ |
| 457 | static PATHS_ATTR(hwpath, 0600, pdcspath_hwpath_read, pdcspath_hwpath_write); | 475 | static PATHS_ATTR(hwpath, 0644, pdcspath_hwpath_read, pdcspath_hwpath_write); |
| 458 | static PATHS_ATTR(layer, 0600, pdcspath_layer_read, pdcspath_layer_write); | 476 | static PATHS_ATTR(layer, 0644, pdcspath_layer_read, pdcspath_layer_write); |
| 459 | 477 | ||
| 460 | static struct attribute *paths_subsys_attrs[] = { | 478 | static struct attribute *paths_subsys_attrs[] = { |
| 461 | &paths_attr_hwpath.attr, | 479 | &paths_attr_hwpath.attr, |
| @@ -484,36 +502,119 @@ static struct pdcspath_entry *pdcspath_entries[] = { | |||
| 484 | NULL, | 502 | NULL, |
| 485 | }; | 503 | }; |
| 486 | 504 | ||
| 505 | |||
| 506 | /* For more insight of what's going on here, refer to PDC Procedures doc, | ||
| 507 | * Section PDC_STABLE */ | ||
| 508 | |||
| 487 | /** | 509 | /** |
| 488 | * pdcs_info_read - Pretty printing of the remaining useful data. | 510 | * pdcs_size_read - Stable Storage size output. |
| 489 | * @entry: An allocated and populated subsytem struct. We don't use it tho. | 511 | * @entry: An allocated and populated subsytem struct. We don't use it tho. |
| 490 | * @buf: The output buffer to write to. | 512 | * @buf: The output buffer to write to. |
| 491 | * | ||
| 492 | * We will call this function to format the output of the 'info' attribute file. | ||
| 493 | * Please refer to PDC Procedures documentation, section PDC_STABLE to get a | ||
| 494 | * better insight of what we're doing here. | ||
| 495 | */ | 513 | */ |
| 496 | static ssize_t | 514 | static ssize_t |
| 497 | pdcs_info_read(struct subsystem *entry, char *buf) | 515 | pdcs_size_read(struct subsystem *entry, char *buf) |
| 498 | { | 516 | { |
| 499 | char *out = buf; | 517 | char *out = buf; |
| 500 | __u32 result; | ||
| 501 | struct device_path devpath; | ||
| 502 | char *tmpstr = NULL; | ||
| 503 | 518 | ||
| 504 | if (!entry || !buf) | 519 | if (!entry || !buf) |
| 505 | return -EINVAL; | 520 | return -EINVAL; |
| 506 | 521 | ||
| 507 | /* show the size of the stable storage */ | 522 | /* show the size of the stable storage */ |
| 508 | out += sprintf(out, "Stable Storage size: %ld bytes\n", pdcs_size); | 523 | out += sprintf(out, "%ld\n", pdcs_size); |
| 509 | 524 | ||
| 510 | /* deal with flags */ | 525 | return out - buf; |
| 511 | if (pdc_stable_read(PDCS_ADDR_PPRI, &devpath, sizeof(devpath)) != PDC_OK) | 526 | } |
| 512 | return -EIO; | 527 | |
| 528 | /** | ||
| 529 | * pdcs_auto_read - Stable Storage autoboot/search flag output. | ||
| 530 | * @entry: An allocated and populated subsytem struct. We don't use it tho. | ||
| 531 | * @buf: The output buffer to write to. | ||
| 532 | * @knob: The PF_AUTOBOOT or PF_AUTOSEARCH flag | ||
| 533 | */ | ||
| 534 | static ssize_t | ||
| 535 | pdcs_auto_read(struct subsystem *entry, char *buf, int knob) | ||
| 536 | { | ||
| 537 | char *out = buf; | ||
| 538 | struct pdcspath_entry *pathentry; | ||
| 513 | 539 | ||
| 514 | out += sprintf(out, "Autoboot: %s\n", (devpath.flags & PF_AUTOBOOT) ? "On" : "Off"); | 540 | if (!entry || !buf) |
| 515 | out += sprintf(out, "Autosearch: %s\n", (devpath.flags & PF_AUTOSEARCH) ? "On" : "Off"); | 541 | return -EINVAL; |
| 516 | out += sprintf(out, "Timer: %u s\n", (devpath.flags & PF_TIMER) ? (1 << (devpath.flags & PF_TIMER)) : 0); | 542 | |
| 543 | /* Current flags are stored in primary boot path entry */ | ||
| 544 | pathentry = &pdcspath_entry_primary; | ||
| 545 | |||
| 546 | read_lock(&pathentry->rw_lock); | ||
| 547 | out += sprintf(out, "%s\n", (pathentry->devpath.flags & knob) ? | ||
| 548 | "On" : "Off"); | ||
| 549 | read_unlock(&pathentry->rw_lock); | ||
| 550 | |||
| 551 | return out - buf; | ||
| 552 | } | ||
| 553 | |||
| 554 | /** | ||
| 555 | * pdcs_autoboot_read - Stable Storage autoboot flag output. | ||
| 556 | * @entry: An allocated and populated subsytem struct. We don't use it tho. | ||
| 557 | * @buf: The output buffer to write to. | ||
| 558 | */ | ||
| 559 | static inline ssize_t | ||
| 560 | pdcs_autoboot_read(struct subsystem *entry, char *buf) | ||
| 561 | { | ||
| 562 | return pdcs_auto_read(entry, buf, PF_AUTOBOOT); | ||
| 563 | } | ||
| 564 | |||
| 565 | /** | ||
| 566 | * pdcs_autosearch_read - Stable Storage autoboot flag output. | ||
| 567 | * @entry: An allocated and populated subsytem struct. We don't use it tho. | ||
| 568 | * @buf: The output buffer to write to. | ||
| 569 | */ | ||
| 570 | static inline ssize_t | ||
| 571 | pdcs_autosearch_read(struct subsystem *entry, char *buf) | ||
| 572 | { | ||
| 573 | return pdcs_auto_read(entry, buf, PF_AUTOSEARCH); | ||
| 574 | } | ||
| 575 | |||
| 576 | /** | ||
| 577 | * pdcs_timer_read - Stable Storage timer count output (in seconds). | ||
| 578 | * @entry: An allocated and populated subsytem struct. We don't use it tho. | ||
| 579 | * @buf: The output buffer to write to. | ||
| 580 | * | ||
| 581 | * The value of the timer field correponds to a number of seconds in powers of 2. | ||
| 582 | */ | ||
| 583 | static ssize_t | ||
| 584 | pdcs_timer_read(struct subsystem *entry, char *buf) | ||
| 585 | { | ||
| 586 | char *out = buf; | ||
| 587 | struct pdcspath_entry *pathentry; | ||
| 588 | |||
| 589 | if (!entry || !buf) | ||
| 590 | return -EINVAL; | ||
| 591 | |||
| 592 | /* Current flags are stored in primary boot path entry */ | ||
| 593 | pathentry = &pdcspath_entry_primary; | ||
| 594 | |||
| 595 | /* print the timer value in seconds */ | ||
| 596 | read_lock(&pathentry->rw_lock); | ||
| 597 | out += sprintf(out, "%u\n", (pathentry->devpath.flags & PF_TIMER) ? | ||
| 598 | (1 << (pathentry->devpath.flags & PF_TIMER)) : 0); | ||
| 599 | read_unlock(&pathentry->rw_lock); | ||
| 600 | |||
| 601 | return out - buf; | ||
| 602 | } | ||
| 603 | |||
| 604 | /** | ||
| 605 | * pdcs_osid_read - Stable Storage OS ID register output. | ||
| 606 | * @entry: An allocated and populated subsytem struct. We don't use it tho. | ||
| 607 | * @buf: The output buffer to write to. | ||
| 608 | */ | ||
| 609 | static ssize_t | ||
| 610 | pdcs_osid_read(struct subsystem *entry, char *buf) | ||
| 611 | { | ||
| 612 | char *out = buf; | ||
| 613 | __u32 result; | ||
| 614 | char *tmpstr = NULL; | ||
| 615 | |||
| 616 | if (!entry || !buf) | ||
| 617 | return -EINVAL; | ||
| 517 | 618 | ||
| 518 | /* get OSID */ | 619 | /* get OSID */ |
| 519 | if (pdc_stable_read(PDCS_ADDR_OSID, &result, sizeof(result)) != PDC_OK) | 620 | if (pdc_stable_read(PDCS_ADDR_OSID, &result, sizeof(result)) != PDC_OK) |
| @@ -529,13 +630,31 @@ pdcs_info_read(struct subsystem *entry, char *buf) | |||
| 529 | case 0x0005: tmpstr = "Novell Netware dependent data"; break; | 630 | case 0x0005: tmpstr = "Novell Netware dependent data"; break; |
| 530 | default: tmpstr = "Unknown"; break; | 631 | default: tmpstr = "Unknown"; break; |
| 531 | } | 632 | } |
| 532 | out += sprintf(out, "OS ID: %s (0x%.4x)\n", tmpstr, (result >> 16)); | 633 | out += sprintf(out, "%s (0x%.4x)\n", tmpstr, (result >> 16)); |
| 634 | |||
| 635 | return out - buf; | ||
| 636 | } | ||
| 637 | |||
| 638 | /** | ||
| 639 | * pdcs_fastsize_read - Stable Storage FastSize register output. | ||
| 640 | * @entry: An allocated and populated subsytem struct. We don't use it tho. | ||
| 641 | * @buf: The output buffer to write to. | ||
| 642 | * | ||
| 643 | * This register holds the amount of system RAM to be tested during boot sequence. | ||
| 644 | */ | ||
| 645 | static ssize_t | ||
| 646 | pdcs_fastsize_read(struct subsystem *entry, char *buf) | ||
| 647 | { | ||
| 648 | char *out = buf; | ||
| 649 | __u32 result; | ||
| 650 | |||
| 651 | if (!entry || !buf) | ||
| 652 | return -EINVAL; | ||
| 533 | 653 | ||
| 534 | /* get fast-size */ | 654 | /* get fast-size */ |
| 535 | if (pdc_stable_read(PDCS_ADDR_FSIZ, &result, sizeof(result)) != PDC_OK) | 655 | if (pdc_stable_read(PDCS_ADDR_FSIZ, &result, sizeof(result)) != PDC_OK) |
| 536 | return -EIO; | 656 | return -EIO; |
| 537 | 657 | ||
| 538 | out += sprintf(out, "Memory tested: "); | ||
| 539 | if ((result & 0x0F) < 0x0E) | 658 | if ((result & 0x0F) < 0x0E) |
| 540 | out += sprintf(out, "%d kB", (1<<(result & 0x0F))*256); | 659 | out += sprintf(out, "%d kB", (1<<(result & 0x0F))*256); |
| 541 | else | 660 | else |
| @@ -546,22 +665,18 @@ pdcs_info_read(struct subsystem *entry, char *buf) | |||
| 546 | } | 665 | } |
| 547 | 666 | ||
| 548 | /** | 667 | /** |
| 549 | * pdcs_info_write - This function handles boot flag modifying. | 668 | * pdcs_auto_write - This function handles autoboot/search flag modifying. |
| 550 | * @entry: An allocated and populated subsytem struct. We don't use it tho. | 669 | * @entry: An allocated and populated subsytem struct. We don't use it tho. |
| 551 | * @buf: The input buffer to read from. | 670 | * @buf: The input buffer to read from. |
| 552 | * @count: The number of bytes to be read. | 671 | * @count: The number of bytes to be read. |
| 672 | * @knob: The PF_AUTOBOOT or PF_AUTOSEARCH flag | ||
| 553 | * | 673 | * |
| 554 | * We will call this function to change the current boot flags. | 674 | * We will call this function to change the current autoboot flag. |
| 555 | * We expect a precise syntax: | 675 | * We expect a precise syntax: |
| 556 | * \"n n\" (n == 0 or 1) to toggle respectively AutoBoot and AutoSearch | 676 | * \"n\" (n == 0 or 1) to toggle AutoBoot Off or On |
| 557 | * | ||
| 558 | * As of now there is no incentive on my side to provide more "knobs" to that | ||
| 559 | * interface, since modifying the rest of the data is pretty meaningless when | ||
| 560 | * the machine is running and for the expected use of that facility, such as | ||
| 561 | * PALO setting up the boot disk when installing a Linux distribution... | ||
| 562 | */ | 677 | */ |
| 563 | static ssize_t | 678 | static ssize_t |
| 564 | pdcs_info_write(struct subsystem *entry, const char *buf, size_t count) | 679 | pdcs_auto_write(struct subsystem *entry, const char *buf, size_t count, int knob) |
| 565 | { | 680 | { |
| 566 | struct pdcspath_entry *pathentry; | 681 | struct pdcspath_entry *pathentry; |
| 567 | unsigned char flags; | 682 | unsigned char flags; |
| @@ -582,7 +697,9 @@ pdcs_info_write(struct subsystem *entry, const char *buf, size_t count) | |||
| 582 | pathentry = &pdcspath_entry_primary; | 697 | pathentry = &pdcspath_entry_primary; |
| 583 | 698 | ||
| 584 | /* Be nice to the existing flag record */ | 699 | /* Be nice to the existing flag record */ |
| 700 | read_lock(&pathentry->rw_lock); | ||
| 585 | flags = pathentry->devpath.flags; | 701 | flags = pathentry->devpath.flags; |
| 702 | read_unlock(&pathentry->rw_lock); | ||
| 586 | 703 | ||
| 587 | DPRINTK("%s: flags before: 0x%X\n", __func__, flags); | 704 | DPRINTK("%s: flags before: 0x%X\n", __func__, flags); |
| 588 | 705 | ||
| @@ -595,50 +712,85 @@ pdcs_info_write(struct subsystem *entry, const char *buf, size_t count) | |||
| 595 | if ((c != 0) && (c != 1)) | 712 | if ((c != 0) && (c != 1)) |
| 596 | goto parse_error; | 713 | goto parse_error; |
| 597 | if (c == 0) | 714 | if (c == 0) |
| 598 | flags &= ~PF_AUTOBOOT; | 715 | flags &= ~knob; |
| 599 | else | 716 | else |
| 600 | flags |= PF_AUTOBOOT; | 717 | flags |= knob; |
| 601 | |||
| 602 | if (*temp++ != ' ') | ||
| 603 | goto parse_error; | ||
| 604 | |||
| 605 | c = *temp++ - '0'; | ||
| 606 | if ((c != 0) && (c != 1)) | ||
| 607 | goto parse_error; | ||
| 608 | if (c == 0) | ||
| 609 | flags &= ~PF_AUTOSEARCH; | ||
| 610 | else | ||
| 611 | flags |= PF_AUTOSEARCH; | ||
| 612 | 718 | ||
| 613 | DPRINTK("%s: flags after: 0x%X\n", __func__, flags); | 719 | DPRINTK("%s: flags after: 0x%X\n", __func__, flags); |
| 614 | 720 | ||
| 615 | /* So far so good, let's get in deep */ | 721 | /* So far so good, let's get in deep */ |
| 722 | write_lock(&pathentry->rw_lock); | ||
| 616 | 723 | ||
| 617 | /* Change the path entry flags first */ | 724 | /* Change the path entry flags first */ |
| 618 | pathentry->devpath.flags = flags; | 725 | pathentry->devpath.flags = flags; |
| 619 | 726 | ||
| 620 | /* Now, dive in. Write back to the hardware */ | 727 | /* Now, dive in. Write back to the hardware */ |
| 621 | WARN_ON(pdcspath_store(pathentry)); /* this warn should *NEVER* happen */ | 728 | pdcspath_store(pathentry); |
| 729 | write_unlock(&pathentry->rw_lock); | ||
| 622 | 730 | ||
| 623 | printk(KERN_INFO "PDC Stable Storage: changed flags to \"%s\"\n", buf); | 731 | printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" to \"%s\"\n", |
| 732 | (knob & PF_AUTOBOOT) ? "autoboot" : "autosearch", | ||
| 733 | (flags & knob) ? "On" : "Off"); | ||
| 624 | 734 | ||
| 625 | return count; | 735 | return count; |
| 626 | 736 | ||
| 627 | parse_error: | 737 | parse_error: |
| 628 | printk(KERN_WARNING "%s: Parse error: expect \"n n\" (n == 0 or 1) for AB and AS\n", __func__); | 738 | printk(KERN_WARNING "%s: Parse error: expect \"n\" (n == 0 or 1)\n", __func__); |
| 629 | return -EINVAL; | 739 | return -EINVAL; |
| 630 | } | 740 | } |
| 631 | 741 | ||
| 632 | /* The last attribute (the 'root' one actually) with all remaining data. */ | 742 | /** |
| 633 | static PDCS_ATTR(info, 0600, pdcs_info_read, pdcs_info_write); | 743 | * pdcs_autoboot_write - This function handles autoboot flag modifying. |
| 744 | * @entry: An allocated and populated subsytem struct. We don't use it tho. | ||
| 745 | * @buf: The input buffer to read from. | ||
| 746 | * @count: The number of bytes to be read. | ||
| 747 | * | ||
| 748 | * We will call this function to change the current boot flags. | ||
| 749 | * We expect a precise syntax: | ||
| 750 | * \"n\" (n == 0 or 1) to toggle AutoSearch Off or On | ||
| 751 | */ | ||
| 752 | static inline ssize_t | ||
| 753 | pdcs_autoboot_write(struct subsystem *entry, const char *buf, size_t count) | ||
| 754 | { | ||
| 755 | return pdcs_auto_write(entry, buf, count, PF_AUTOBOOT); | ||
| 756 | } | ||
| 757 | |||
| 758 | /** | ||
| 759 | * pdcs_autosearch_write - This function handles autosearch flag modifying. | ||
| 760 | * @entry: An allocated and populated subsytem struct. We don't use it tho. | ||
| 761 | * @buf: The input buffer to read from. | ||
| 762 | * @count: The number of bytes to be read. | ||
| 763 | * | ||
| 764 | * We will call this function to change the current boot flags. | ||
| 765 | * We expect a precise syntax: | ||
| 766 | * \"n\" (n == 0 or 1) to toggle AutoSearch Off or On | ||
| 767 | */ | ||
| 768 | static inline ssize_t | ||
| 769 | pdcs_autosearch_write(struct subsystem *entry, const char *buf, size_t count) | ||
| 770 | { | ||
| 771 | return pdcs_auto_write(entry, buf, count, PF_AUTOSEARCH); | ||
| 772 | } | ||
| 773 | |||
| 774 | /* The remaining attributes. */ | ||
| 775 | static PDCS_ATTR(size, 0444, pdcs_size_read, NULL); | ||
| 776 | static PDCS_ATTR(autoboot, 0644, pdcs_autoboot_read, pdcs_autoboot_write); | ||
| 777 | static PDCS_ATTR(autosearch, 0644, pdcs_autosearch_read, pdcs_autosearch_write); | ||
| 778 | static PDCS_ATTR(timer, 0444, pdcs_timer_read, NULL); | ||
| 779 | static PDCS_ATTR(osid, 0400, pdcs_osid_read, NULL); | ||
| 780 | static PDCS_ATTR(fastsize, 0400, pdcs_fastsize_read, NULL); | ||
| 634 | 781 | ||
| 635 | static struct subsys_attribute *pdcs_subsys_attrs[] = { | 782 | static struct subsys_attribute *pdcs_subsys_attrs[] = { |
| 636 | &pdcs_attr_info, | 783 | &pdcs_attr_size, |
| 637 | NULL, /* maybe more in the future? */ | 784 | &pdcs_attr_autoboot, |
| 785 | &pdcs_attr_autosearch, | ||
| 786 | &pdcs_attr_timer, | ||
| 787 | &pdcs_attr_osid, | ||
| 788 | &pdcs_attr_fastsize, | ||
| 789 | NULL, | ||
| 638 | }; | 790 | }; |
| 639 | 791 | ||
| 640 | static decl_subsys(paths, &ktype_pdcspath, NULL); | 792 | static decl_subsys(paths, &ktype_pdcspath, NULL); |
| 641 | static decl_subsys(pdc, NULL, NULL); | 793 | static decl_subsys(stable, NULL, NULL); |
| 642 | 794 | ||
| 643 | /** | 795 | /** |
| 644 | * pdcs_register_pathentries - Prepares path entries kobjects for sysfs usage. | 796 | * pdcs_register_pathentries - Prepares path entries kobjects for sysfs usage. |
| @@ -656,8 +808,16 @@ pdcs_register_pathentries(void) | |||
| 656 | struct pdcspath_entry *entry; | 808 | struct pdcspath_entry *entry; |
| 657 | int err; | 809 | int err; |
| 658 | 810 | ||
| 811 | /* Initialize the entries rw_lock before anything else */ | ||
| 812 | for (i = 0; (entry = pdcspath_entries[i]); i++) | ||
| 813 | rwlock_init(&entry->rw_lock); | ||
| 814 | |||
| 659 | for (i = 0; (entry = pdcspath_entries[i]); i++) { | 815 | for (i = 0; (entry = pdcspath_entries[i]); i++) { |
| 660 | if (pdcspath_fetch(entry) < 0) | 816 | write_lock(&entry->rw_lock); |
| 817 | err = pdcspath_fetch(entry); | ||
| 818 | write_unlock(&entry->rw_lock); | ||
| 819 | |||
| 820 | if (err < 0) | ||
| 661 | continue; | 821 | continue; |
| 662 | 822 | ||
| 663 | if ((err = kobject_set_name(&entry->kobj, "%s", entry->name))) | 823 | if ((err = kobject_set_name(&entry->kobj, "%s", entry->name))) |
| @@ -667,13 +827,14 @@ pdcs_register_pathentries(void) | |||
| 667 | return err; | 827 | return err; |
| 668 | 828 | ||
| 669 | /* kobject is now registered */ | 829 | /* kobject is now registered */ |
| 830 | write_lock(&entry->rw_lock); | ||
| 670 | entry->ready = 2; | 831 | entry->ready = 2; |
| 671 | 832 | ||
| 672 | if (!entry->dev) | ||
| 673 | continue; | ||
| 674 | |||
| 675 | /* Add a nice symlink to the real device */ | 833 | /* Add a nice symlink to the real device */ |
| 676 | sysfs_create_link(&entry->kobj, &entry->dev->kobj, "device"); | 834 | if (entry->dev) |
| 835 | sysfs_create_link(&entry->kobj, &entry->dev->kobj, "device"); | ||
| 836 | |||
| 837 | write_unlock(&entry->rw_lock); | ||
| 677 | } | 838 | } |
| 678 | 839 | ||
| 679 | return 0; | 840 | return 0; |
| @@ -688,14 +849,17 @@ pdcs_unregister_pathentries(void) | |||
| 688 | unsigned short i; | 849 | unsigned short i; |
| 689 | struct pdcspath_entry *entry; | 850 | struct pdcspath_entry *entry; |
| 690 | 851 | ||
| 691 | for (i = 0; (entry = pdcspath_entries[i]); i++) | 852 | for (i = 0; (entry = pdcspath_entries[i]); i++) { |
| 853 | read_lock(&entry->rw_lock); | ||
| 692 | if (entry->ready >= 2) | 854 | if (entry->ready >= 2) |
| 693 | kobject_unregister(&entry->kobj); | 855 | kobject_unregister(&entry->kobj); |
| 856 | read_unlock(&entry->rw_lock); | ||
| 857 | } | ||
| 694 | } | 858 | } |
| 695 | 859 | ||
| 696 | /* | 860 | /* |
| 697 | * For now we register the pdc subsystem with the firmware subsystem | 861 | * For now we register the stable subsystem with the firmware subsystem |
| 698 | * and the paths subsystem with the pdc subsystem | 862 | * and the paths subsystem with the stable subsystem |
| 699 | */ | 863 | */ |
| 700 | static int __init | 864 | static int __init |
| 701 | pdc_stable_init(void) | 865 | pdc_stable_init(void) |
| @@ -707,19 +871,23 @@ pdc_stable_init(void) | |||
| 707 | if (pdc_stable_get_size(&pdcs_size) != PDC_OK) | 871 | if (pdc_stable_get_size(&pdcs_size) != PDC_OK) |
| 708 | return -ENODEV; | 872 | return -ENODEV; |
| 709 | 873 | ||
| 710 | printk(KERN_INFO "PDC Stable Storage facility v%s\n", PDCS_VERSION); | 874 | /* make sure we have enough data */ |
| 875 | if (pdcs_size < 96) | ||
| 876 | return -ENODATA; | ||
| 877 | |||
| 878 | printk(KERN_INFO PDCS_PREFIX " facility v%s\n", PDCS_VERSION); | ||
| 711 | 879 | ||
| 712 | /* For now we'll register the pdc subsys within this driver */ | 880 | /* For now we'll register the stable subsys within this driver */ |
| 713 | if ((rc = firmware_register(&pdc_subsys))) | 881 | if ((rc = firmware_register(&stable_subsys))) |
| 714 | goto fail_firmreg; | 882 | goto fail_firmreg; |
| 715 | 883 | ||
| 716 | /* Don't forget the info entry */ | 884 | /* Don't forget the root entries */ |
| 717 | for (i = 0; (attr = pdcs_subsys_attrs[i]) && !error; i++) | 885 | for (i = 0; (attr = pdcs_subsys_attrs[i]) && !error; i++) |
| 718 | if (attr->show) | 886 | if (attr->show) |
| 719 | error = subsys_create_file(&pdc_subsys, attr); | 887 | error = subsys_create_file(&stable_subsys, attr); |
| 720 | 888 | ||
| 721 | /* register the paths subsys as a subsystem of pdc subsys */ | 889 | /* register the paths subsys as a subsystem of stable subsys */ |
| 722 | kset_set_kset_s(&paths_subsys, pdc_subsys); | 890 | kset_set_kset_s(&paths_subsys, stable_subsys); |
| 723 | if ((rc= subsystem_register(&paths_subsys))) | 891 | if ((rc= subsystem_register(&paths_subsys))) |
| 724 | goto fail_subsysreg; | 892 | goto fail_subsysreg; |
| 725 | 893 | ||
| @@ -734,10 +902,10 @@ fail_pdcsreg: | |||
| 734 | subsystem_unregister(&paths_subsys); | 902 | subsystem_unregister(&paths_subsys); |
| 735 | 903 | ||
| 736 | fail_subsysreg: | 904 | fail_subsysreg: |
| 737 | firmware_unregister(&pdc_subsys); | 905 | firmware_unregister(&stable_subsys); |
| 738 | 906 | ||
| 739 | fail_firmreg: | 907 | fail_firmreg: |
| 740 | printk(KERN_INFO "PDC Stable Storage bailing out\n"); | 908 | printk(KERN_INFO PDCS_PREFIX " bailing out\n"); |
| 741 | return rc; | 909 | return rc; |
| 742 | } | 910 | } |
| 743 | 911 | ||
| @@ -747,7 +915,7 @@ pdc_stable_exit(void) | |||
| 747 | pdcs_unregister_pathentries(); | 915 | pdcs_unregister_pathentries(); |
| 748 | subsystem_unregister(&paths_subsys); | 916 | subsystem_unregister(&paths_subsys); |
| 749 | 917 | ||
| 750 | firmware_unregister(&pdc_subsys); | 918 | firmware_unregister(&stable_subsys); |
| 751 | } | 919 | } |
| 752 | 920 | ||
| 753 | 921 | ||
