diff options
| -rw-r--r-- | arch/parisc/kernel/setup.c | 5 | ||||
| -rw-r--r-- | drivers/parisc/pdc_stable.c | 219 |
2 files changed, 210 insertions, 14 deletions
diff --git a/arch/parisc/kernel/setup.c b/arch/parisc/kernel/setup.c index 4a36ec3f6ac1..8471486a7565 100644 --- a/arch/parisc/kernel/setup.c +++ b/arch/parisc/kernel/setup.c | |||
| @@ -303,6 +303,8 @@ extern void eisa_init(void); | |||
| 303 | 303 | ||
| 304 | static int __init parisc_init(void) | 304 | static int __init parisc_init(void) |
| 305 | { | 305 | { |
| 306 | u32 osid = (0x0006 << 16); | ||
| 307 | |||
| 306 | parisc_proc_mkdir(); | 308 | parisc_proc_mkdir(); |
| 307 | parisc_init_resources(); | 309 | parisc_init_resources(); |
| 308 | do_device_inventory(); /* probe for hardware */ | 310 | do_device_inventory(); /* probe for hardware */ |
| @@ -311,6 +313,9 @@ static int __init parisc_init(void) | |||
| 311 | 313 | ||
| 312 | /* set up a new led state on systems shipped LED State panel */ | 314 | /* set up a new led state on systems shipped LED State panel */ |
| 313 | pdc_chassis_send_status(PDC_CHASSIS_DIRECT_BSTART); | 315 | pdc_chassis_send_status(PDC_CHASSIS_DIRECT_BSTART); |
| 316 | |||
| 317 | /* tell PDC we're Linux. Nevermind failure. */ | ||
| 318 | pdc_stable_write(0x40, &osid, sizeof(osid)); | ||
| 314 | 319 | ||
| 315 | processor_init(); | 320 | processor_init(); |
| 316 | printk(KERN_INFO "CPU(s): %d x %s at %d.%06d MHz\n", | 321 | printk(KERN_INFO "CPU(s): %d x %s at %d.%06d MHz\n", |
diff --git a/drivers/parisc/pdc_stable.c b/drivers/parisc/pdc_stable.c index bbeabe3fc4c6..4b991d50e578 100644 --- a/drivers/parisc/pdc_stable.c +++ b/drivers/parisc/pdc_stable.c | |||
| @@ -28,8 +28,15 @@ | |||
| 28 | * following code can deal with just 96 bytes of Stable Storage, and all | 28 | * following code can deal with just 96 bytes of Stable Storage, and all |
| 29 | * sizes between 96 and 192 bytes (provided they are multiple of struct | 29 | * sizes between 96 and 192 bytes (provided they are multiple of struct |
| 30 | * device_path size, eg: 128, 160 and 192) to provide full information. | 30 | * device_path size, eg: 128, 160 and 192) to provide full information. |
| 31 | * The code makes no use of data above 192 bytes. One last word: there's one | 31 | * One last word: there's one path we can always count on: the primary path. |
| 32 | * path we can always count on: the primary path. | 32 | * Anything above 224 bytes is used for 'osdep2' OS-dependent storage area. |
| 33 | * | ||
| 34 | * The first OS-dependent area should always be available. Obviously, this is | ||
| 35 | * not true for the other one. Also bear in mind that reading/writing from/to | ||
| 36 | * osdep2 is much more expensive than from/to osdep1. | ||
| 37 | * NOTE: We do not handle the 2 bytes OS-dep area at 0x5D, nor the first | ||
| 38 | * 2 bytes of storage available right after OSID. That's a total of 4 bytes | ||
| 39 | * sacrificed: -ETOOLAZY :P | ||
| 33 | * | 40 | * |
| 34 | * The current policy wrt file permissions is: | 41 | * The current policy wrt file permissions is: |
| 35 | * - write: root only | 42 | * - write: root only |
| @@ -64,15 +71,18 @@ | |||
| 64 | #include <asm/uaccess.h> | 71 | #include <asm/uaccess.h> |
| 65 | #include <asm/hardware.h> | 72 | #include <asm/hardware.h> |
| 66 | 73 | ||
| 67 | #define PDCS_VERSION "0.22" | 74 | #define PDCS_VERSION "0.30" |
| 68 | #define PDCS_PREFIX "PDC Stable Storage" | 75 | #define PDCS_PREFIX "PDC Stable Storage" |
| 69 | 76 | ||
| 70 | #define PDCS_ADDR_PPRI 0x00 | 77 | #define PDCS_ADDR_PPRI 0x00 |
| 71 | #define PDCS_ADDR_OSID 0x40 | 78 | #define PDCS_ADDR_OSID 0x40 |
| 79 | #define PDCS_ADDR_OSD1 0x48 | ||
| 80 | #define PDCS_ADDR_DIAG 0x58 | ||
| 72 | #define PDCS_ADDR_FSIZ 0x5C | 81 | #define PDCS_ADDR_FSIZ 0x5C |
| 73 | #define PDCS_ADDR_PCON 0x60 | 82 | #define PDCS_ADDR_PCON 0x60 |
| 74 | #define PDCS_ADDR_PALT 0x80 | 83 | #define PDCS_ADDR_PALT 0x80 |
| 75 | #define PDCS_ADDR_PKBD 0xA0 | 84 | #define PDCS_ADDR_PKBD 0xA0 |
| 85 | #define PDCS_ADDR_OSD2 0xE0 | ||
| 76 | 86 | ||
| 77 | MODULE_AUTHOR("Thibaut VARENE <varenet@parisc-linux.org>"); | 87 | MODULE_AUTHOR("Thibaut VARENE <varenet@parisc-linux.org>"); |
| 78 | MODULE_DESCRIPTION("sysfs interface to HP PDC Stable Storage data"); | 88 | MODULE_DESCRIPTION("sysfs interface to HP PDC Stable Storage data"); |
| @@ -82,6 +92,9 @@ MODULE_VERSION(PDCS_VERSION); | |||
| 82 | /* holds Stable Storage size. Initialized once and for all, no lock needed */ | 92 | /* holds Stable Storage size. Initialized once and for all, no lock needed */ |
| 83 | static unsigned long pdcs_size __read_mostly; | 93 | static unsigned long pdcs_size __read_mostly; |
| 84 | 94 | ||
| 95 | /* holds OS ID. Initialized once and for all, hopefully to 0x0006 */ | ||
| 96 | static u16 pdcs_osid __read_mostly; | ||
| 97 | |||
| 85 | /* This struct defines what we need to deal with a parisc pdc path entry */ | 98 | /* This struct defines what we need to deal with a parisc pdc path entry */ |
| 86 | struct pdcspath_entry { | 99 | struct pdcspath_entry { |
| 87 | rwlock_t rw_lock; /* to protect path entry access */ | 100 | rwlock_t rw_lock; /* to protect path entry access */ |
| @@ -609,27 +622,74 @@ static ssize_t | |||
| 609 | pdcs_osid_read(struct subsystem *entry, char *buf) | 622 | pdcs_osid_read(struct subsystem *entry, char *buf) |
| 610 | { | 623 | { |
| 611 | char *out = buf; | 624 | char *out = buf; |
| 612 | __u32 result; | ||
| 613 | char *tmpstr = NULL; | 625 | char *tmpstr = NULL; |
| 614 | 626 | ||
| 615 | if (!entry || !buf) | 627 | if (!entry || !buf) |
| 616 | return -EINVAL; | 628 | return -EINVAL; |
| 617 | 629 | ||
| 618 | /* get OSID */ | 630 | switch (pdcs_osid) { |
| 619 | if (pdc_stable_read(PDCS_ADDR_OSID, &result, sizeof(result)) != PDC_OK) | ||
| 620 | return -EIO; | ||
| 621 | |||
| 622 | /* the actual result is 16 bits away */ | ||
| 623 | switch (result >> 16) { | ||
| 624 | case 0x0000: tmpstr = "No OS-dependent data"; break; | 631 | case 0x0000: tmpstr = "No OS-dependent data"; break; |
| 625 | case 0x0001: tmpstr = "HP-UX dependent data"; break; | 632 | case 0x0001: tmpstr = "HP-UX dependent data"; break; |
| 626 | case 0x0002: tmpstr = "MPE-iX dependent data"; break; | 633 | case 0x0002: tmpstr = "MPE-iX dependent data"; break; |
| 627 | case 0x0003: tmpstr = "OSF dependent data"; break; | 634 | case 0x0003: tmpstr = "OSF dependent data"; break; |
| 628 | case 0x0004: tmpstr = "HP-RT dependent data"; break; | 635 | case 0x0004: tmpstr = "HP-RT dependent data"; break; |
| 629 | case 0x0005: tmpstr = "Novell Netware dependent data"; break; | 636 | case 0x0005: tmpstr = "Novell Netware dependent data"; break; |
| 637 | case 0x0006: tmpstr = "Linux dependent data"; break; | ||
| 630 | default: tmpstr = "Unknown"; break; | 638 | default: tmpstr = "Unknown"; break; |
| 631 | } | 639 | } |
| 632 | out += sprintf(out, "%s (0x%.4x)\n", tmpstr, (result >> 16)); | 640 | out += sprintf(out, "%s (0x%.4x)\n", tmpstr, pdcs_osid); |
| 641 | |||
| 642 | return out - buf; | ||
| 643 | } | ||
| 644 | |||
| 645 | /** | ||
| 646 | * pdcs_osdep1_read - Stable Storage OS-Dependent data area 1 output. | ||
| 647 | * @entry: An allocated and populated subsytem struct. We don't use it tho. | ||
| 648 | * @buf: The output buffer to write to. | ||
| 649 | * | ||
| 650 | * This can hold 16 bytes of OS-Dependent data. | ||
| 651 | */ | ||
| 652 | static ssize_t | ||
| 653 | pdcs_osdep1_read(struct subsystem *entry, char *buf) | ||
| 654 | { | ||
| 655 | char *out = buf; | ||
| 656 | u32 result[4]; | ||
| 657 | |||
| 658 | if (!entry || !buf) | ||
| 659 | return -EINVAL; | ||
| 660 | |||
| 661 | if (pdc_stable_read(PDCS_ADDR_OSD1, &result, sizeof(result)) != PDC_OK) | ||
| 662 | return -EIO; | ||
| 663 | |||
| 664 | out += sprintf(out, "0x%.8x\n", result[0]); | ||
| 665 | out += sprintf(out, "0x%.8x\n", result[1]); | ||
| 666 | out += sprintf(out, "0x%.8x\n", result[2]); | ||
| 667 | out += sprintf(out, "0x%.8x\n", result[3]); | ||
| 668 | |||
| 669 | return out - buf; | ||
| 670 | } | ||
| 671 | |||
| 672 | /** | ||
| 673 | * pdcs_diagnostic_read - Stable Storage Diagnostic register output. | ||
| 674 | * @entry: An allocated and populated subsytem struct. We don't use it tho. | ||
| 675 | * @buf: The output buffer to write to. | ||
| 676 | * | ||
| 677 | * I have NFC how to interpret the content of that register ;-). | ||
| 678 | */ | ||
| 679 | static ssize_t | ||
| 680 | pdcs_diagnostic_read(struct subsystem *entry, char *buf) | ||
| 681 | { | ||
| 682 | char *out = buf; | ||
| 683 | u32 result; | ||
| 684 | |||
| 685 | if (!entry || !buf) | ||
| 686 | return -EINVAL; | ||
| 687 | |||
| 688 | /* get diagnostic */ | ||
| 689 | if (pdc_stable_read(PDCS_ADDR_DIAG, &result, sizeof(result)) != PDC_OK) | ||
| 690 | return -EIO; | ||
| 691 | |||
| 692 | out += sprintf(out, "0x%.4x\n", (result >> 16)); | ||
| 633 | 693 | ||
| 634 | return out - buf; | 694 | return out - buf; |
| 635 | } | 695 | } |
| @@ -645,7 +705,7 @@ static ssize_t | |||
| 645 | pdcs_fastsize_read(struct subsystem *entry, char *buf) | 705 | pdcs_fastsize_read(struct subsystem *entry, char *buf) |
| 646 | { | 706 | { |
| 647 | char *out = buf; | 707 | char *out = buf; |
| 648 | __u32 result; | 708 | u32 result; |
| 649 | 709 | ||
| 650 | if (!entry || !buf) | 710 | if (!entry || !buf) |
| 651 | return -EINVAL; | 711 | return -EINVAL; |
| @@ -664,6 +724,39 @@ pdcs_fastsize_read(struct subsystem *entry, char *buf) | |||
| 664 | } | 724 | } |
| 665 | 725 | ||
| 666 | /** | 726 | /** |
| 727 | * pdcs_osdep2_read - Stable Storage OS-Dependent data area 2 output. | ||
| 728 | * @entry: An allocated and populated subsytem struct. We don't use it tho. | ||
| 729 | * @buf: The output buffer to write to. | ||
| 730 | * | ||
| 731 | * This can hold pdcs_size - 224 bytes of OS-Dependent data, when available. | ||
| 732 | */ | ||
| 733 | static ssize_t | ||
| 734 | pdcs_osdep2_read(struct subsystem *entry, char *buf) | ||
| 735 | { | ||
| 736 | char *out = buf; | ||
| 737 | unsigned long size; | ||
| 738 | unsigned short i; | ||
| 739 | u32 result; | ||
| 740 | |||
| 741 | if (unlikely(pdcs_size <= 224)) | ||
| 742 | return -ENODATA; | ||
| 743 | |||
| 744 | size = pdcs_size - 224; | ||
| 745 | |||
| 746 | if (!entry || !buf) | ||
| 747 | return -EINVAL; | ||
| 748 | |||
| 749 | for (i=0; i<size; i+=4) { | ||
| 750 | if (unlikely(pdc_stable_read(PDCS_ADDR_OSD2 + i, &result, | ||
| 751 | sizeof(result)) != PDC_OK)) | ||
| 752 | return -EIO; | ||
| 753 | out += sprintf(out, "0x%.8x\n", result); | ||
| 754 | } | ||
| 755 | |||
| 756 | return out - buf; | ||
| 757 | } | ||
| 758 | |||
| 759 | /** | ||
| 667 | * pdcs_auto_write - This function handles autoboot/search flag modifying. | 760 | * pdcs_auto_write - This function handles autoboot/search flag modifying. |
| 668 | * @entry: An allocated and populated subsytem struct. We don't use it tho. | 761 | * @entry: An allocated and populated subsytem struct. We don't use it tho. |
| 669 | * @buf: The input buffer to read from. | 762 | * @buf: The input buffer to read from. |
| @@ -770,13 +863,100 @@ pdcs_autosearch_write(struct subsystem *entry, const char *buf, size_t count) | |||
| 770 | return pdcs_auto_write(entry, buf, count, PF_AUTOSEARCH); | 863 | return pdcs_auto_write(entry, buf, count, PF_AUTOSEARCH); |
| 771 | } | 864 | } |
| 772 | 865 | ||
| 866 | /** | ||
| 867 | * pdcs_osdep1_write - Stable Storage OS-Dependent data area 1 input. | ||
| 868 | * @entry: An allocated and populated subsytem struct. We don't use it tho. | ||
| 869 | * @buf: The input buffer to read from. | ||
| 870 | * @count: The number of bytes to be read. | ||
| 871 | * | ||
| 872 | * This can store 16 bytes of OS-Dependent data. We use a byte-by-byte | ||
| 873 | * write approach. It's up to userspace to deal with it when constructing | ||
| 874 | * its input buffer. | ||
| 875 | */ | ||
| 876 | static ssize_t | ||
| 877 | pdcs_osdep1_write(struct subsystem *entry, const char *buf, size_t count) | ||
| 878 | { | ||
| 879 | u8 in[16]; | ||
| 880 | |||
| 881 | if (!capable(CAP_SYS_ADMIN)) | ||
| 882 | return -EACCES; | ||
| 883 | |||
| 884 | if (!entry || !buf || !count) | ||
| 885 | return -EINVAL; | ||
| 886 | |||
| 887 | if (unlikely(pdcs_osid != 0x0006)) | ||
| 888 | return -EPERM; | ||
| 889 | |||
| 890 | if (count > 16) | ||
| 891 | return -EMSGSIZE; | ||
| 892 | |||
| 893 | /* We'll use a local copy of buf */ | ||
| 894 | memset(in, 0, 16); | ||
| 895 | memcpy(in, buf, count); | ||
| 896 | |||
| 897 | if (pdc_stable_write(PDCS_ADDR_OSD1, &in, sizeof(in)) != PDC_OK) | ||
| 898 | return -EIO; | ||
| 899 | |||
| 900 | return count; | ||
| 901 | } | ||
| 902 | |||
| 903 | /** | ||
| 904 | * pdcs_osdep2_write - Stable Storage OS-Dependent data area 2 input. | ||
| 905 | * @entry: An allocated and populated subsytem struct. We don't use it tho. | ||
| 906 | * @buf: The input buffer to read from. | ||
| 907 | * @count: The number of bytes to be read. | ||
| 908 | * | ||
| 909 | * This can store pdcs_size - 224 bytes of OS-Dependent data. We use a | ||
| 910 | * byte-by-byte write approach. It's up to userspace to deal with it when | ||
| 911 | * constructing its input buffer. | ||
| 912 | */ | ||
| 913 | static ssize_t | ||
| 914 | pdcs_osdep2_write(struct subsystem *entry, const char *buf, size_t count) | ||
| 915 | { | ||
| 916 | unsigned long size; | ||
| 917 | unsigned short i; | ||
| 918 | u8 in[4]; | ||
| 919 | |||
| 920 | if (!capable(CAP_SYS_ADMIN)) | ||
| 921 | return -EACCES; | ||
| 922 | |||
| 923 | if (!entry || !buf || !count) | ||
| 924 | return -EINVAL; | ||
| 925 | |||
| 926 | if (unlikely(pdcs_size <= 224)) | ||
| 927 | return -ENOSYS; | ||
| 928 | |||
| 929 | if (unlikely(pdcs_osid != 0x0006)) | ||
| 930 | return -EPERM; | ||
| 931 | |||
| 932 | size = pdcs_size - 224; | ||
| 933 | |||
| 934 | if (count > size) | ||
| 935 | return -EMSGSIZE; | ||
| 936 | |||
| 937 | /* We'll use a local copy of buf */ | ||
| 938 | |||
| 939 | for (i=0; i<count; i+=4) { | ||
| 940 | memset(in, 0, 4); | ||
| 941 | memcpy(in, buf+i, (count-i < 4) ? count-i : 4); | ||
| 942 | if (unlikely(pdc_stable_write(PDCS_ADDR_OSD2 + i, &in, | ||
| 943 | sizeof(in)) != PDC_OK)) | ||
| 944 | return -EIO; | ||
| 945 | } | ||
| 946 | |||
| 947 | return count; | ||
| 948 | } | ||
| 949 | |||
| 773 | /* The remaining attributes. */ | 950 | /* The remaining attributes. */ |
| 774 | static PDCS_ATTR(size, 0444, pdcs_size_read, NULL); | 951 | static PDCS_ATTR(size, 0444, pdcs_size_read, NULL); |
| 775 | static PDCS_ATTR(autoboot, 0644, pdcs_autoboot_read, pdcs_autoboot_write); | 952 | static PDCS_ATTR(autoboot, 0644, pdcs_autoboot_read, pdcs_autoboot_write); |
| 776 | static PDCS_ATTR(autosearch, 0644, pdcs_autosearch_read, pdcs_autosearch_write); | 953 | static PDCS_ATTR(autosearch, 0644, pdcs_autosearch_read, pdcs_autosearch_write); |
| 777 | static PDCS_ATTR(timer, 0444, pdcs_timer_read, NULL); | 954 | static PDCS_ATTR(timer, 0444, pdcs_timer_read, NULL); |
| 778 | static PDCS_ATTR(osid, 0400, pdcs_osid_read, NULL); | 955 | static PDCS_ATTR(osid, 0444, pdcs_osid_read, NULL); |
| 956 | static PDCS_ATTR(osdep1, 0600, pdcs_osdep1_read, pdcs_osdep1_write); | ||
| 957 | static PDCS_ATTR(diagnostic, 0400, pdcs_diagnostic_read, NULL); | ||
| 779 | static PDCS_ATTR(fastsize, 0400, pdcs_fastsize_read, NULL); | 958 | static PDCS_ATTR(fastsize, 0400, pdcs_fastsize_read, NULL); |
| 959 | static PDCS_ATTR(osdep2, 0600, pdcs_osdep2_read, pdcs_osdep2_write); | ||
| 780 | 960 | ||
| 781 | static struct subsys_attribute *pdcs_subsys_attrs[] = { | 961 | static struct subsys_attribute *pdcs_subsys_attrs[] = { |
| 782 | &pdcs_attr_size, | 962 | &pdcs_attr_size, |
| @@ -784,7 +964,10 @@ static struct subsys_attribute *pdcs_subsys_attrs[] = { | |||
| 784 | &pdcs_attr_autosearch, | 964 | &pdcs_attr_autosearch, |
| 785 | &pdcs_attr_timer, | 965 | &pdcs_attr_timer, |
| 786 | &pdcs_attr_osid, | 966 | &pdcs_attr_osid, |
| 967 | &pdcs_attr_osdep1, | ||
| 968 | &pdcs_attr_diagnostic, | ||
| 787 | &pdcs_attr_fastsize, | 969 | &pdcs_attr_fastsize, |
| 970 | &pdcs_attr_osdep2, | ||
| 788 | NULL, | 971 | NULL, |
| 789 | }; | 972 | }; |
| 790 | 973 | ||
| @@ -865,6 +1048,7 @@ pdc_stable_init(void) | |||
| 865 | { | 1048 | { |
| 866 | struct subsys_attribute *attr; | 1049 | struct subsys_attribute *attr; |
| 867 | int i, rc = 0, error = 0; | 1050 | int i, rc = 0, error = 0; |
| 1051 | u32 result; | ||
| 868 | 1052 | ||
| 869 | /* find the size of the stable storage */ | 1053 | /* find the size of the stable storage */ |
| 870 | if (pdc_stable_get_size(&pdcs_size) != PDC_OK) | 1054 | if (pdc_stable_get_size(&pdcs_size) != PDC_OK) |
| @@ -876,6 +1060,13 @@ pdc_stable_init(void) | |||
| 876 | 1060 | ||
| 877 | printk(KERN_INFO PDCS_PREFIX " facility v%s\n", PDCS_VERSION); | 1061 | printk(KERN_INFO PDCS_PREFIX " facility v%s\n", PDCS_VERSION); |
| 878 | 1062 | ||
| 1063 | /* get OSID */ | ||
| 1064 | if (pdc_stable_read(PDCS_ADDR_OSID, &result, sizeof(result)) != PDC_OK) | ||
| 1065 | return -EIO; | ||
| 1066 | |||
| 1067 | /* the actual result is 16 bits away */ | ||
| 1068 | pdcs_osid = (u16)(result >> 16); | ||
| 1069 | |||
| 879 | /* For now we'll register the stable subsys within this driver */ | 1070 | /* For now we'll register the stable subsys within this driver */ |
| 880 | if ((rc = firmware_register(&stable_subsys))) | 1071 | if ((rc = firmware_register(&stable_subsys))) |
| 881 | goto fail_firmreg; | 1072 | goto fail_firmreg; |
| @@ -887,7 +1078,7 @@ pdc_stable_init(void) | |||
| 887 | 1078 | ||
| 888 | /* register the paths subsys as a subsystem of stable subsys */ | 1079 | /* register the paths subsys as a subsystem of stable subsys */ |
| 889 | kset_set_kset_s(&paths_subsys, stable_subsys); | 1080 | kset_set_kset_s(&paths_subsys, stable_subsys); |
| 890 | if ((rc= subsystem_register(&paths_subsys))) | 1081 | if ((rc = subsystem_register(&paths_subsys))) |
| 891 | goto fail_subsysreg; | 1082 | goto fail_subsysreg; |
| 892 | 1083 | ||
| 893 | /* now we create all "files" for the paths subsys */ | 1084 | /* now we create all "files" for the paths subsys */ |
