diff options
Diffstat (limited to 'arch/s390/kernel/ipl.c')
-rw-r--r-- | arch/s390/kernel/ipl.c | 139 |
1 files changed, 105 insertions, 34 deletions
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index 9e9972e8a52b..5a863a3bf10c 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c | |||
@@ -14,32 +14,41 @@ | |||
14 | #include <linux/delay.h> | 14 | #include <linux/delay.h> |
15 | #include <linux/reboot.h> | 15 | #include <linux/reboot.h> |
16 | #include <linux/ctype.h> | 16 | #include <linux/ctype.h> |
17 | #include <asm/ipl.h> | ||
17 | #include <asm/smp.h> | 18 | #include <asm/smp.h> |
18 | #include <asm/setup.h> | 19 | #include <asm/setup.h> |
19 | #include <asm/cpcmd.h> | 20 | #include <asm/cpcmd.h> |
20 | #include <asm/cio.h> | 21 | #include <asm/cio.h> |
21 | #include <asm/ebcdic.h> | 22 | #include <asm/ebcdic.h> |
22 | #include <asm/reset.h> | 23 | #include <asm/reset.h> |
24 | #include <asm/sclp.h> | ||
23 | 25 | ||
24 | #define IPL_PARM_BLOCK_VERSION 0 | 26 | #define IPL_PARM_BLOCK_VERSION 0 |
25 | #define LOADPARM_LEN 8 | ||
26 | 27 | ||
27 | extern char s390_readinfo_sccb[]; | 28 | #define SCCB_VALID (s390_readinfo_sccb.header.response_code == 0x10) |
28 | #define SCCB_VALID (*((__u16*)&s390_readinfo_sccb[6]) == 0x0010) | 29 | #define SCCB_LOADPARM (&s390_readinfo_sccb.loadparm) |
29 | #define SCCB_LOADPARM (&s390_readinfo_sccb[24]) | 30 | #define SCCB_FLAG (s390_readinfo_sccb.flags) |
30 | #define SCCB_FLAG (s390_readinfo_sccb[91]) | ||
31 | 31 | ||
32 | enum ipl_type { | 32 | enum ipl_type { |
33 | IPL_TYPE_NONE = 1, | 33 | IPL_TYPE_NONE = 1, |
34 | IPL_TYPE_UNKNOWN = 2, | 34 | IPL_TYPE_UNKNOWN = 2, |
35 | IPL_TYPE_CCW = 4, | 35 | IPL_TYPE_CCW = 4, |
36 | IPL_TYPE_FCP = 8, | 36 | IPL_TYPE_FCP = 8, |
37 | IPL_TYPE_NSS = 16, | ||
37 | }; | 38 | }; |
38 | 39 | ||
39 | #define IPL_NONE_STR "none" | 40 | #define IPL_NONE_STR "none" |
40 | #define IPL_UNKNOWN_STR "unknown" | 41 | #define IPL_UNKNOWN_STR "unknown" |
41 | #define IPL_CCW_STR "ccw" | 42 | #define IPL_CCW_STR "ccw" |
42 | #define IPL_FCP_STR "fcp" | 43 | #define IPL_FCP_STR "fcp" |
44 | #define IPL_NSS_STR "nss" | ||
45 | |||
46 | /* | ||
47 | * Must be in data section since the bss section | ||
48 | * is not cleared when these are accessed. | ||
49 | */ | ||
50 | u16 ipl_devno __attribute__((__section__(".data"))) = 0; | ||
51 | u32 ipl_flags __attribute__((__section__(".data"))) = 0; | ||
43 | 52 | ||
44 | static char *ipl_type_str(enum ipl_type type) | 53 | static char *ipl_type_str(enum ipl_type type) |
45 | { | 54 | { |
@@ -50,6 +59,8 @@ static char *ipl_type_str(enum ipl_type type) | |||
50 | return IPL_CCW_STR; | 59 | return IPL_CCW_STR; |
51 | case IPL_TYPE_FCP: | 60 | case IPL_TYPE_FCP: |
52 | return IPL_FCP_STR; | 61 | return IPL_FCP_STR; |
62 | case IPL_TYPE_NSS: | ||
63 | return IPL_NSS_STR; | ||
53 | case IPL_TYPE_UNKNOWN: | 64 | case IPL_TYPE_UNKNOWN: |
54 | default: | 65 | default: |
55 | return IPL_UNKNOWN_STR; | 66 | return IPL_UNKNOWN_STR; |
@@ -64,6 +75,7 @@ enum ipl_method { | |||
64 | IPL_METHOD_FCP_RO_DIAG, | 75 | IPL_METHOD_FCP_RO_DIAG, |
65 | IPL_METHOD_FCP_RW_DIAG, | 76 | IPL_METHOD_FCP_RW_DIAG, |
66 | IPL_METHOD_FCP_RO_VM, | 77 | IPL_METHOD_FCP_RO_VM, |
78 | IPL_METHOD_NSS, | ||
67 | }; | 79 | }; |
68 | 80 | ||
69 | enum shutdown_action { | 81 | enum shutdown_action { |
@@ -86,39 +98,21 @@ static char *shutdown_action_str(enum shutdown_action action) | |||
86 | case SHUTDOWN_STOP: | 98 | case SHUTDOWN_STOP: |
87 | return SHUTDOWN_STOP_STR; | 99 | return SHUTDOWN_STOP_STR; |
88 | default: | 100 | default: |
89 | BUG(); | 101 | return NULL; |
90 | } | 102 | } |
91 | } | 103 | } |
92 | 104 | ||
93 | enum diag308_subcode { | ||
94 | DIAG308_IPL = 3, | ||
95 | DIAG308_DUMP = 4, | ||
96 | DIAG308_SET = 5, | ||
97 | DIAG308_STORE = 6, | ||
98 | }; | ||
99 | |||
100 | enum diag308_ipl_type { | ||
101 | DIAG308_IPL_TYPE_FCP = 0, | ||
102 | DIAG308_IPL_TYPE_CCW = 2, | ||
103 | }; | ||
104 | |||
105 | enum diag308_opt { | ||
106 | DIAG308_IPL_OPT_IPL = 0x10, | ||
107 | DIAG308_IPL_OPT_DUMP = 0x20, | ||
108 | }; | ||
109 | |||
110 | enum diag308_rc { | ||
111 | DIAG308_RC_OK = 1, | ||
112 | }; | ||
113 | |||
114 | static int diag308_set_works = 0; | 105 | static int diag308_set_works = 0; |
115 | 106 | ||
116 | static int reipl_capabilities = IPL_TYPE_UNKNOWN; | 107 | static int reipl_capabilities = IPL_TYPE_UNKNOWN; |
108 | |||
117 | static enum ipl_type reipl_type = IPL_TYPE_UNKNOWN; | 109 | static enum ipl_type reipl_type = IPL_TYPE_UNKNOWN; |
118 | static enum ipl_method reipl_method = IPL_METHOD_NONE; | 110 | static enum ipl_method reipl_method = IPL_METHOD_NONE; |
119 | static struct ipl_parameter_block *reipl_block_fcp; | 111 | static struct ipl_parameter_block *reipl_block_fcp; |
120 | static struct ipl_parameter_block *reipl_block_ccw; | 112 | static struct ipl_parameter_block *reipl_block_ccw; |
121 | 113 | ||
114 | static char reipl_nss_name[NSS_NAME_SIZE + 1]; | ||
115 | |||
122 | static int dump_capabilities = IPL_TYPE_NONE; | 116 | static int dump_capabilities = IPL_TYPE_NONE; |
123 | static enum ipl_type dump_type = IPL_TYPE_NONE; | 117 | static enum ipl_type dump_type = IPL_TYPE_NONE; |
124 | static enum ipl_method dump_method = IPL_METHOD_NONE; | 118 | static enum ipl_method dump_method = IPL_METHOD_NONE; |
@@ -127,7 +121,7 @@ static struct ipl_parameter_block *dump_block_ccw; | |||
127 | 121 | ||
128 | static enum shutdown_action on_panic_action = SHUTDOWN_STOP; | 122 | static enum shutdown_action on_panic_action = SHUTDOWN_STOP; |
129 | 123 | ||
130 | static int diag308(unsigned long subcode, void *addr) | 124 | int diag308(unsigned long subcode, void *addr) |
131 | { | 125 | { |
132 | register unsigned long _addr asm("0") = (unsigned long) addr; | 126 | register unsigned long _addr asm("0") = (unsigned long) addr; |
133 | register unsigned long _rc asm("1") = 0; | 127 | register unsigned long _rc asm("1") = 0; |
@@ -173,6 +167,24 @@ static struct subsys_attribute sys_##_prefix##_##_name##_attr = \ | |||
173 | sys_##_prefix##_##_name##_show, \ | 167 | sys_##_prefix##_##_name##_show, \ |
174 | sys_##_prefix##_##_name##_store); | 168 | sys_##_prefix##_##_name##_store); |
175 | 169 | ||
170 | #define DEFINE_IPL_ATTR_STR_RW(_prefix, _name, _fmt_out, _fmt_in, _value)\ | ||
171 | static ssize_t sys_##_prefix##_##_name##_show(struct subsystem *subsys, \ | ||
172 | char *page) \ | ||
173 | { \ | ||
174 | return sprintf(page, _fmt_out, _value); \ | ||
175 | } \ | ||
176 | static ssize_t sys_##_prefix##_##_name##_store(struct subsystem *subsys,\ | ||
177 | const char *buf, size_t len) \ | ||
178 | { \ | ||
179 | if (sscanf(buf, _fmt_in, _value) != 1) \ | ||
180 | return -EINVAL; \ | ||
181 | return len; \ | ||
182 | } \ | ||
183 | static struct subsys_attribute sys_##_prefix##_##_name##_attr = \ | ||
184 | __ATTR(_name,(S_IRUGO | S_IWUSR), \ | ||
185 | sys_##_prefix##_##_name##_show, \ | ||
186 | sys_##_prefix##_##_name##_store); | ||
187 | |||
176 | static void make_attrs_ro(struct attribute **attrs) | 188 | static void make_attrs_ro(struct attribute **attrs) |
177 | { | 189 | { |
178 | while (*attrs) { | 190 | while (*attrs) { |
@@ -189,6 +201,8 @@ static enum ipl_type ipl_get_type(void) | |||
189 | { | 201 | { |
190 | struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START; | 202 | struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START; |
191 | 203 | ||
204 | if (ipl_flags & IPL_NSS_VALID) | ||
205 | return IPL_TYPE_NSS; | ||
192 | if (!(ipl_flags & IPL_DEVNO_VALID)) | 206 | if (!(ipl_flags & IPL_DEVNO_VALID)) |
193 | return IPL_TYPE_UNKNOWN; | 207 | return IPL_TYPE_UNKNOWN; |
194 | if (!(ipl_flags & IPL_PARMBLOCK_VALID)) | 208 | if (!(ipl_flags & IPL_PARMBLOCK_VALID)) |
@@ -324,6 +338,20 @@ static struct attribute_group ipl_ccw_attr_group = { | |||
324 | .attrs = ipl_ccw_attrs, | 338 | .attrs = ipl_ccw_attrs, |
325 | }; | 339 | }; |
326 | 340 | ||
341 | /* NSS ipl device attributes */ | ||
342 | |||
343 | DEFINE_IPL_ATTR_RO(ipl_nss, name, "%s\n", kernel_nss_name); | ||
344 | |||
345 | static struct attribute *ipl_nss_attrs[] = { | ||
346 | &sys_ipl_type_attr.attr, | ||
347 | &sys_ipl_nss_name_attr.attr, | ||
348 | NULL, | ||
349 | }; | ||
350 | |||
351 | static struct attribute_group ipl_nss_attr_group = { | ||
352 | .attrs = ipl_nss_attrs, | ||
353 | }; | ||
354 | |||
327 | /* UNKNOWN ipl device attributes */ | 355 | /* UNKNOWN ipl device attributes */ |
328 | 356 | ||
329 | static struct attribute *ipl_unknown_attrs[] = { | 357 | static struct attribute *ipl_unknown_attrs[] = { |
@@ -432,6 +460,21 @@ static struct attribute_group reipl_ccw_attr_group = { | |||
432 | .attrs = reipl_ccw_attrs, | 460 | .attrs = reipl_ccw_attrs, |
433 | }; | 461 | }; |
434 | 462 | ||
463 | |||
464 | /* NSS reipl device attributes */ | ||
465 | |||
466 | DEFINE_IPL_ATTR_STR_RW(reipl_nss, name, "%s\n", "%s\n", reipl_nss_name); | ||
467 | |||
468 | static struct attribute *reipl_nss_attrs[] = { | ||
469 | &sys_reipl_nss_name_attr.attr, | ||
470 | NULL, | ||
471 | }; | ||
472 | |||
473 | static struct attribute_group reipl_nss_attr_group = { | ||
474 | .name = IPL_NSS_STR, | ||
475 | .attrs = reipl_nss_attrs, | ||
476 | }; | ||
477 | |||
435 | /* reipl type */ | 478 | /* reipl type */ |
436 | 479 | ||
437 | static int reipl_set_type(enum ipl_type type) | 480 | static int reipl_set_type(enum ipl_type type) |
@@ -454,6 +497,9 @@ static int reipl_set_type(enum ipl_type type) | |||
454 | else | 497 | else |
455 | reipl_method = IPL_METHOD_FCP_RO_DIAG; | 498 | reipl_method = IPL_METHOD_FCP_RO_DIAG; |
456 | break; | 499 | break; |
500 | case IPL_TYPE_NSS: | ||
501 | reipl_method = IPL_METHOD_NSS; | ||
502 | break; | ||
457 | default: | 503 | default: |
458 | reipl_method = IPL_METHOD_NONE; | 504 | reipl_method = IPL_METHOD_NONE; |
459 | } | 505 | } |
@@ -475,6 +521,8 @@ static ssize_t reipl_type_store(struct subsystem *subsys, const char *buf, | |||
475 | rc = reipl_set_type(IPL_TYPE_CCW); | 521 | rc = reipl_set_type(IPL_TYPE_CCW); |
476 | else if (strncmp(buf, IPL_FCP_STR, strlen(IPL_FCP_STR)) == 0) | 522 | else if (strncmp(buf, IPL_FCP_STR, strlen(IPL_FCP_STR)) == 0) |
477 | rc = reipl_set_type(IPL_TYPE_FCP); | 523 | rc = reipl_set_type(IPL_TYPE_FCP); |
524 | else if (strncmp(buf, IPL_NSS_STR, strlen(IPL_NSS_STR)) == 0) | ||
525 | rc = reipl_set_type(IPL_TYPE_NSS); | ||
478 | return (rc != 0) ? rc : len; | 526 | return (rc != 0) ? rc : len; |
479 | } | 527 | } |
480 | 528 | ||
@@ -647,6 +695,10 @@ void do_reipl(void) | |||
647 | case IPL_METHOD_FCP_RO_VM: | 695 | case IPL_METHOD_FCP_RO_VM: |
648 | __cpcmd("IPL", NULL, 0, NULL); | 696 | __cpcmd("IPL", NULL, 0, NULL); |
649 | break; | 697 | break; |
698 | case IPL_METHOD_NSS: | ||
699 | sprintf(buf, "IPL %s", reipl_nss_name); | ||
700 | __cpcmd(buf, NULL, 0, NULL); | ||
701 | break; | ||
650 | case IPL_METHOD_NONE: | 702 | case IPL_METHOD_NONE: |
651 | default: | 703 | default: |
652 | if (MACHINE_IS_VM) | 704 | if (MACHINE_IS_VM) |
@@ -733,6 +785,10 @@ static int __init ipl_init(void) | |||
733 | case IPL_TYPE_FCP: | 785 | case IPL_TYPE_FCP: |
734 | rc = ipl_register_fcp_files(); | 786 | rc = ipl_register_fcp_files(); |
735 | break; | 787 | break; |
788 | case IPL_TYPE_NSS: | ||
789 | rc = sysfs_create_group(&ipl_subsys.kset.kobj, | ||
790 | &ipl_nss_attr_group); | ||
791 | break; | ||
736 | default: | 792 | default: |
737 | rc = sysfs_create_group(&ipl_subsys.kset.kobj, | 793 | rc = sysfs_create_group(&ipl_subsys.kset.kobj, |
738 | &ipl_unknown_attr_group); | 794 | &ipl_unknown_attr_group); |
@@ -755,6 +811,20 @@ static void __init reipl_probe(void) | |||
755 | free_page((unsigned long)buffer); | 811 | free_page((unsigned long)buffer); |
756 | } | 812 | } |
757 | 813 | ||
814 | static int __init reipl_nss_init(void) | ||
815 | { | ||
816 | int rc; | ||
817 | |||
818 | if (!MACHINE_IS_VM) | ||
819 | return 0; | ||
820 | rc = sysfs_create_group(&reipl_subsys.kset.kobj, &reipl_nss_attr_group); | ||
821 | if (rc) | ||
822 | return rc; | ||
823 | strncpy(reipl_nss_name, kernel_nss_name, NSS_NAME_SIZE + 1); | ||
824 | reipl_capabilities |= IPL_TYPE_NSS; | ||
825 | return 0; | ||
826 | } | ||
827 | |||
758 | static int __init reipl_ccw_init(void) | 828 | static int __init reipl_ccw_init(void) |
759 | { | 829 | { |
760 | int rc; | 830 | int rc; |
@@ -837,6 +907,9 @@ static int __init reipl_init(void) | |||
837 | rc = reipl_fcp_init(); | 907 | rc = reipl_fcp_init(); |
838 | if (rc) | 908 | if (rc) |
839 | return rc; | 909 | return rc; |
910 | rc = reipl_nss_init(); | ||
911 | if (rc) | ||
912 | return rc; | ||
840 | rc = reipl_set_type(ipl_get_type()); | 913 | rc = reipl_set_type(ipl_get_type()); |
841 | if (rc) | 914 | if (rc) |
842 | return rc; | 915 | return rc; |
@@ -993,8 +1066,6 @@ static void do_reset_calls(void) | |||
993 | reset->fn(); | 1066 | reset->fn(); |
994 | } | 1067 | } |
995 | 1068 | ||
996 | extern void reset_mcck_handler(void); | ||
997 | extern void reset_pgm_handler(void); | ||
998 | extern __u32 dump_prefix_page; | 1069 | extern __u32 dump_prefix_page; |
999 | 1070 | ||
1000 | void s390_reset_system(void) | 1071 | void s390_reset_system(void) |
@@ -1016,14 +1087,14 @@ void s390_reset_system(void) | |||
1016 | __ctl_clear_bit(0,28); | 1087 | __ctl_clear_bit(0,28); |
1017 | 1088 | ||
1018 | /* Set new machine check handler */ | 1089 | /* Set new machine check handler */ |
1019 | S390_lowcore.mcck_new_psw.mask = PSW_KERNEL_BITS & ~PSW_MASK_MCHECK; | 1090 | S390_lowcore.mcck_new_psw.mask = psw_kernel_bits & ~PSW_MASK_MCHECK; |
1020 | S390_lowcore.mcck_new_psw.addr = | 1091 | S390_lowcore.mcck_new_psw.addr = |
1021 | PSW_ADDR_AMODE | (unsigned long) &reset_mcck_handler; | 1092 | PSW_ADDR_AMODE | (unsigned long) s390_base_mcck_handler; |
1022 | 1093 | ||
1023 | /* Set new program check handler */ | 1094 | /* Set new program check handler */ |
1024 | S390_lowcore.program_new_psw.mask = PSW_KERNEL_BITS & ~PSW_MASK_MCHECK; | 1095 | S390_lowcore.program_new_psw.mask = psw_kernel_bits & ~PSW_MASK_MCHECK; |
1025 | S390_lowcore.program_new_psw.addr = | 1096 | S390_lowcore.program_new_psw.addr = |
1026 | PSW_ADDR_AMODE | (unsigned long) &reset_pgm_handler; | 1097 | PSW_ADDR_AMODE | (unsigned long) s390_base_pgm_handler; |
1027 | 1098 | ||
1028 | do_reset_calls(); | 1099 | do_reset_calls(); |
1029 | } | 1100 | } |