diff options
Diffstat (limited to 'drivers/parisc/pdc_stable.c')
| -rw-r--r-- | drivers/parisc/pdc_stable.c | 223 |
1 files changed, 202 insertions, 21 deletions
diff --git a/drivers/parisc/pdc_stable.c b/drivers/parisc/pdc_stable.c index bbeabe3fc4c6..ea1b7a63598e 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,64 @@ 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; | ||
| 614 | 625 | ||
| 615 | if (!entry || !buf) | 626 | if (!entry || !buf) |
| 616 | return -EINVAL; | 627 | return -EINVAL; |
| 617 | 628 | ||
| 618 | /* get OSID */ | 629 | out += sprintf(out, "%s dependent data (0x%.4x)\n", |
| 619 | if (pdc_stable_read(PDCS_ADDR_OSID, &result, sizeof(result)) != PDC_OK) | 630 | os_id_to_string(pdcs_osid), pdcs_osid); |
| 631 | |||
| 632 | return out - buf; | ||
| 633 | } | ||
| 634 | |||
| 635 | /** | ||
| 636 | * pdcs_osdep1_read - Stable Storage OS-Dependent data area 1 output. | ||
| 637 | * @entry: An allocated and populated subsytem struct. We don't use it tho. | ||
| 638 | * @buf: The output buffer to write to. | ||
| 639 | * | ||
| 640 | * This can hold 16 bytes of OS-Dependent data. | ||
| 641 | */ | ||
| 642 | static ssize_t | ||
| 643 | pdcs_osdep1_read(struct subsystem *entry, char *buf) | ||
| 644 | { | ||
| 645 | char *out = buf; | ||
| 646 | u32 result[4]; | ||
| 647 | |||
| 648 | if (!entry || !buf) | ||
| 649 | return -EINVAL; | ||
| 650 | |||
| 651 | if (pdc_stable_read(PDCS_ADDR_OSD1, &result, sizeof(result)) != PDC_OK) | ||
| 620 | return -EIO; | 652 | return -EIO; |
| 621 | 653 | ||
| 622 | /* the actual result is 16 bits away */ | 654 | out += sprintf(out, "0x%.8x\n", result[0]); |
| 623 | switch (result >> 16) { | 655 | out += sprintf(out, "0x%.8x\n", result[1]); |
| 624 | case 0x0000: tmpstr = "No OS-dependent data"; break; | 656 | out += sprintf(out, "0x%.8x\n", result[2]); |
| 625 | case 0x0001: tmpstr = "HP-UX dependent data"; break; | 657 | out += sprintf(out, "0x%.8x\n", result[3]); |
| 626 | case 0x0002: tmpstr = "MPE-iX dependent data"; break; | 658 | |
| 627 | case 0x0003: tmpstr = "OSF dependent data"; break; | 659 | return out - buf; |
| 628 | case 0x0004: tmpstr = "HP-RT dependent data"; break; | 660 | } |
| 629 | case 0x0005: tmpstr = "Novell Netware dependent data"; break; | 661 | |
| 630 | default: tmpstr = "Unknown"; break; | 662 | /** |
| 631 | } | 663 | * pdcs_diagnostic_read - Stable Storage Diagnostic register output. |
| 632 | out += sprintf(out, "%s (0x%.4x)\n", tmpstr, (result >> 16)); | 664 | * @entry: An allocated and populated subsytem struct. We don't use it tho. |
| 665 | * @buf: The output buffer to write to. | ||
| 666 | * | ||
| 667 | * I have NFC how to interpret the content of that register ;-). | ||
| 668 | */ | ||
| 669 | static ssize_t | ||
| 670 | pdcs_diagnostic_read(struct subsystem *entry, char *buf) | ||
| 671 | { | ||
| 672 | char *out = buf; | ||
| 673 | u32 result; | ||
| 674 | |||
| 675 | if (!entry || !buf) | ||
| 676 | return -EINVAL; | ||
| 677 | |||
| 678 | /* get diagnostic */ | ||
| 679 | if (pdc_stable_read(PDCS_ADDR_DIAG, &result, sizeof(result)) != PDC_OK) | ||
| 680 | return -EIO; | ||
| 681 | |||
| 682 | out += sprintf(out, "0x%.4x\n", (result >> 16)); | ||
| 633 | 683 | ||
| 634 | return out - buf; | 684 | return out - buf; |
| 635 | } | 685 | } |
| @@ -645,7 +695,7 @@ static ssize_t | |||
| 645 | pdcs_fastsize_read(struct subsystem *entry, char *buf) | 695 | pdcs_fastsize_read(struct subsystem *entry, char *buf) |
| 646 | { | 696 | { |
| 647 | char *out = buf; | 697 | char *out = buf; |
| 648 | __u32 result; | 698 | u32 result; |
| 649 | 699 | ||
| 650 | if (!entry || !buf) | 700 | if (!entry || !buf) |
| 651 | return -EINVAL; | 701 | return -EINVAL; |
| @@ -664,6 +714,39 @@ pdcs_fastsize_read(struct subsystem *entry, char *buf) | |||
| 664 | } | 714 | } |
| 665 | 715 | ||
| 666 | /** | 716 | /** |
| 717 | * pdcs_osdep2_read - Stable Storage OS-Dependent data area 2 output. | ||
| 718 | * @entry: An allocated and populated subsytem struct. We don't use it tho. | ||
| 719 | * @buf: The output buffer to write to. | ||
| 720 | * | ||
| 721 | * This can hold pdcs_size - 224 bytes of OS-Dependent data, when available. | ||
| 722 | */ | ||
| 723 | static ssize_t | ||
| 724 | pdcs_osdep2_read(struct subsystem *entry, char *buf) | ||
| 725 | { | ||
| 726 | char *out = buf; | ||
| 727 | unsigned long size; | ||
| 728 | unsigned short i; | ||
| 729 | u32 result; | ||
| 730 | |||
| 731 | if (unlikely(pdcs_size <= 224)) | ||
| 732 | return -ENODATA; | ||
| 733 | |||
| 734 | size = pdcs_size - 224; | ||
| 735 | |||
| 736 | if (!entry || !buf) | ||
| 737 | return -EINVAL; | ||
| 738 | |||
| 739 | for (i=0; i<size; i+=4) { | ||
| 740 | if (unlikely(pdc_stable_read(PDCS_ADDR_OSD2 + i, &result, | ||
| 741 | sizeof(result)) != PDC_OK)) | ||
| 742 | return -EIO; | ||
| 743 | out += sprintf(out, "0x%.8x\n", result); | ||
| 744 | } | ||
| 745 | |||
| 746 | return out - buf; | ||
| 747 | } | ||
| 748 | |||
| 749 | /** | ||
| 667 | * pdcs_auto_write - This function handles autoboot/search flag modifying. | 750 | * pdcs_auto_write - This function handles autoboot/search flag modifying. |
| 668 | * @entry: An allocated and populated subsytem struct. We don't use it tho. | 751 | * @entry: An allocated and populated subsytem struct. We don't use it tho. |
| 669 | * @buf: The input buffer to read from. | 752 | * @buf: The input buffer to read from. |
| @@ -770,13 +853,100 @@ pdcs_autosearch_write(struct subsystem *entry, const char *buf, size_t count) | |||
| 770 | return pdcs_auto_write(entry, buf, count, PF_AUTOSEARCH); | 853 | return pdcs_auto_write(entry, buf, count, PF_AUTOSEARCH); |
| 771 | } | 854 | } |
| 772 | 855 | ||
| 856 | /** | ||
| 857 | * pdcs_osdep1_write - Stable Storage OS-Dependent data area 1 input. | ||
| 858 | * @entry: An allocated and populated subsytem struct. We don't use it tho. | ||
| 859 | * @buf: The input buffer to read from. | ||
| 860 | * @count: The number of bytes to be read. | ||
| 861 | * | ||
| 862 | * This can store 16 bytes of OS-Dependent data. We use a byte-by-byte | ||
| 863 | * write approach. It's up to userspace to deal with it when constructing | ||
| 864 | * its input buffer. | ||
| 865 | */ | ||
| 866 | static ssize_t | ||
| 867 | pdcs_osdep1_write(struct subsystem *entry, const char *buf, size_t count) | ||
| 868 | { | ||
| 869 | u8 in[16]; | ||
| 870 | |||
| 871 | if (!capable(CAP_SYS_ADMIN)) | ||
| 872 | return -EACCES; | ||
| 873 | |||
| 874 | if (!entry || !buf || !count) | ||
| 875 | return -EINVAL; | ||
| 876 | |||
| 877 | if (unlikely(pdcs_osid != OS_ID_LINUX)) | ||
| 878 | return -EPERM; | ||
| 879 | |||
| 880 | if (count > 16) | ||
| 881 | return -EMSGSIZE; | ||
| 882 | |||
| 883 | /* We'll use a local copy of buf */ | ||
| 884 | memset(in, 0, 16); | ||
| 885 | memcpy(in, buf, count); | ||
| 886 | |||
| 887 | if (pdc_stable_write(PDCS_ADDR_OSD1, &in, sizeof(in)) != PDC_OK) | ||
| 888 | return -EIO; | ||
| 889 | |||
| 890 | return count; | ||
| 891 | } | ||
| 892 | |||
| 893 | /** | ||
| 894 | * pdcs_osdep2_write - Stable Storage OS-Dependent data area 2 input. | ||
| 895 | * @entry: An allocated and populated subsytem struct. We don't use it tho. | ||
| 896 | * @buf: The input buffer to read from. | ||
| 897 | * @count: The number of bytes to be read. | ||
| 898 | * | ||
| 899 | * This can store pdcs_size - 224 bytes of OS-Dependent data. We use a | ||
| 900 | * byte-by-byte write approach. It's up to userspace to deal with it when | ||
| 901 | * constructing its input buffer. | ||
| 902 | */ | ||
| 903 | static ssize_t | ||
| 904 | pdcs_osdep2_write(struct subsystem *entry, const char *buf, size_t count) | ||
| 905 | { | ||
| 906 | unsigned long size; | ||
| 907 | unsigned short i; | ||
| 908 | u8 in[4]; | ||
| 909 | |||
| 910 | if (!capable(CAP_SYS_ADMIN)) | ||
| 911 | return -EACCES; | ||
| 912 | |||
| 913 | if (!entry || !buf || !count) | ||
| 914 | return -EINVAL; | ||
| 915 | |||
| 916 | if (unlikely(pdcs_size <= 224)) | ||
| 917 | return -ENOSYS; | ||
| 918 | |||
| 919 | if (unlikely(pdcs_osid != OS_ID_LINUX)) | ||
| 920 | return -EPERM; | ||
| 921 | |||
| 922 | size = pdcs_size - 224; | ||
| 923 | |||
| 924 | if (count > size) | ||
| 925 | return -EMSGSIZE; | ||
| 926 | |||
| 927 | /* We'll use a local copy of buf */ | ||
| 928 | |||
| 929 | for (i=0; i<count; i+=4) { | ||
| 930 | memset(in, 0, 4); | ||
| 931 | memcpy(in, buf+i, (count-i < 4) ? count-i : 4); | ||
| 932 | if (unlikely(pdc_stable_write(PDCS_ADDR_OSD2 + i, &in, | ||
| 933 | sizeof(in)) != PDC_OK)) | ||
| 934 | return -EIO; | ||
| 935 | } | ||
| 936 | |||
| 937 | return count; | ||
| 938 | } | ||
| 939 | |||
| 773 | /* The remaining attributes. */ | 940 | /* The remaining attributes. */ |
| 774 | static PDCS_ATTR(size, 0444, pdcs_size_read, NULL); | 941 | static PDCS_ATTR(size, 0444, pdcs_size_read, NULL); |
| 775 | static PDCS_ATTR(autoboot, 0644, pdcs_autoboot_read, pdcs_autoboot_write); | 942 | static PDCS_ATTR(autoboot, 0644, pdcs_autoboot_read, pdcs_autoboot_write); |
| 776 | static PDCS_ATTR(autosearch, 0644, pdcs_autosearch_read, pdcs_autosearch_write); | 943 | static PDCS_ATTR(autosearch, 0644, pdcs_autosearch_read, pdcs_autosearch_write); |
| 777 | static PDCS_ATTR(timer, 0444, pdcs_timer_read, NULL); | 944 | static PDCS_ATTR(timer, 0444, pdcs_timer_read, NULL); |
| 778 | static PDCS_ATTR(osid, 0400, pdcs_osid_read, NULL); | 945 | static PDCS_ATTR(osid, 0444, pdcs_osid_read, NULL); |
| 946 | static PDCS_ATTR(osdep1, 0600, pdcs_osdep1_read, pdcs_osdep1_write); | ||
| 947 | static PDCS_ATTR(diagnostic, 0400, pdcs_diagnostic_read, NULL); | ||
| 779 | static PDCS_ATTR(fastsize, 0400, pdcs_fastsize_read, NULL); | 948 | static PDCS_ATTR(fastsize, 0400, pdcs_fastsize_read, NULL); |
| 949 | static PDCS_ATTR(osdep2, 0600, pdcs_osdep2_read, pdcs_osdep2_write); | ||
| 780 | 950 | ||
| 781 | static struct subsys_attribute *pdcs_subsys_attrs[] = { | 951 | static struct subsys_attribute *pdcs_subsys_attrs[] = { |
| 782 | &pdcs_attr_size, | 952 | &pdcs_attr_size, |
| @@ -784,7 +954,10 @@ static struct subsys_attribute *pdcs_subsys_attrs[] = { | |||
| 784 | &pdcs_attr_autosearch, | 954 | &pdcs_attr_autosearch, |
| 785 | &pdcs_attr_timer, | 955 | &pdcs_attr_timer, |
| 786 | &pdcs_attr_osid, | 956 | &pdcs_attr_osid, |
| 957 | &pdcs_attr_osdep1, | ||
| 958 | &pdcs_attr_diagnostic, | ||
| 787 | &pdcs_attr_fastsize, | 959 | &pdcs_attr_fastsize, |
| 960 | &pdcs_attr_osdep2, | ||
| 788 | NULL, | 961 | NULL, |
| 789 | }; | 962 | }; |
| 790 | 963 | ||
| @@ -865,6 +1038,7 @@ pdc_stable_init(void) | |||
| 865 | { | 1038 | { |
| 866 | struct subsys_attribute *attr; | 1039 | struct subsys_attribute *attr; |
| 867 | int i, rc = 0, error = 0; | 1040 | int i, rc = 0, error = 0; |
| 1041 | u32 result; | ||
| 868 | 1042 | ||
| 869 | /* find the size of the stable storage */ | 1043 | /* find the size of the stable storage */ |
| 870 | if (pdc_stable_get_size(&pdcs_size) != PDC_OK) | 1044 | if (pdc_stable_get_size(&pdcs_size) != PDC_OK) |
| @@ -876,6 +1050,13 @@ pdc_stable_init(void) | |||
| 876 | 1050 | ||
| 877 | printk(KERN_INFO PDCS_PREFIX " facility v%s\n", PDCS_VERSION); | 1051 | printk(KERN_INFO PDCS_PREFIX " facility v%s\n", PDCS_VERSION); |
| 878 | 1052 | ||
| 1053 | /* get OSID */ | ||
| 1054 | if (pdc_stable_read(PDCS_ADDR_OSID, &result, sizeof(result)) != PDC_OK) | ||
| 1055 | return -EIO; | ||
| 1056 | |||
| 1057 | /* the actual result is 16 bits away */ | ||
| 1058 | pdcs_osid = (u16)(result >> 16); | ||
| 1059 | |||
| 879 | /* For now we'll register the stable subsys within this driver */ | 1060 | /* For now we'll register the stable subsys within this driver */ |
| 880 | if ((rc = firmware_register(&stable_subsys))) | 1061 | if ((rc = firmware_register(&stable_subsys))) |
| 881 | goto fail_firmreg; | 1062 | goto fail_firmreg; |
| @@ -887,7 +1068,7 @@ pdc_stable_init(void) | |||
| 887 | 1068 | ||
| 888 | /* register the paths subsys as a subsystem of stable subsys */ | 1069 | /* register the paths subsys as a subsystem of stable subsys */ |
| 889 | kset_set_kset_s(&paths_subsys, stable_subsys); | 1070 | kset_set_kset_s(&paths_subsys, stable_subsys); |
| 890 | if ((rc= subsystem_register(&paths_subsys))) | 1071 | if ((rc = subsystem_register(&paths_subsys))) |
| 891 | goto fail_subsysreg; | 1072 | goto fail_subsysreg; |
| 892 | 1073 | ||
| 893 | /* now we create all "files" for the paths subsys */ | 1074 | /* now we create all "files" for the paths subsys */ |
