diff options
Diffstat (limited to 'arch/s390/kernel/ipl.c')
-rw-r--r-- | arch/s390/kernel/ipl.c | 1035 |
1 files changed, 663 insertions, 372 deletions
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index ce0856d32500..db28cca81fef 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * arch/s390/kernel/ipl.c | 2 | * arch/s390/kernel/ipl.c |
3 | * ipl/reipl/dump support for Linux on s390. | 3 | * ipl/reipl/dump support for Linux on s390. |
4 | * | 4 | * |
5 | * Copyright (C) IBM Corp. 2005,2006 | 5 | * Copyright IBM Corp. 2005,2007 |
6 | * Author(s): Michael Holzheu <holzheu@de.ibm.com> | 6 | * Author(s): Michael Holzheu <holzheu@de.ibm.com> |
7 | * Heiko Carstens <heiko.carstens@de.ibm.com> | 7 | * Heiko Carstens <heiko.carstens@de.ibm.com> |
8 | * Volker Sameske <sameske@de.ibm.com> | 8 | * Volker Sameske <sameske@de.ibm.com> |
@@ -31,6 +31,43 @@ | |||
31 | #define IPL_FCP_DUMP_STR "fcp_dump" | 31 | #define IPL_FCP_DUMP_STR "fcp_dump" |
32 | #define IPL_NSS_STR "nss" | 32 | #define IPL_NSS_STR "nss" |
33 | 33 | ||
34 | #define DUMP_CCW_STR "ccw" | ||
35 | #define DUMP_FCP_STR "fcp" | ||
36 | #define DUMP_NONE_STR "none" | ||
37 | |||
38 | /* | ||
39 | * Four shutdown trigger types are supported: | ||
40 | * - panic | ||
41 | * - halt | ||
42 | * - power off | ||
43 | * - reipl | ||
44 | */ | ||
45 | #define ON_PANIC_STR "on_panic" | ||
46 | #define ON_HALT_STR "on_halt" | ||
47 | #define ON_POFF_STR "on_poff" | ||
48 | #define ON_REIPL_STR "on_reboot" | ||
49 | |||
50 | struct shutdown_action; | ||
51 | struct shutdown_trigger { | ||
52 | char *name; | ||
53 | struct shutdown_action *action; | ||
54 | }; | ||
55 | |||
56 | /* | ||
57 | * Five shutdown action types are supported: | ||
58 | */ | ||
59 | #define SHUTDOWN_ACTION_IPL_STR "ipl" | ||
60 | #define SHUTDOWN_ACTION_REIPL_STR "reipl" | ||
61 | #define SHUTDOWN_ACTION_DUMP_STR "dump" | ||
62 | #define SHUTDOWN_ACTION_VMCMD_STR "vmcmd" | ||
63 | #define SHUTDOWN_ACTION_STOP_STR "stop" | ||
64 | |||
65 | struct shutdown_action { | ||
66 | char *name; | ||
67 | void (*fn) (struct shutdown_trigger *trigger); | ||
68 | int (*init) (void); | ||
69 | }; | ||
70 | |||
34 | static char *ipl_type_str(enum ipl_type type) | 71 | static char *ipl_type_str(enum ipl_type type) |
35 | { | 72 | { |
36 | switch (type) { | 73 | switch (type) { |
@@ -54,10 +91,6 @@ enum dump_type { | |||
54 | DUMP_TYPE_FCP = 4, | 91 | DUMP_TYPE_FCP = 4, |
55 | }; | 92 | }; |
56 | 93 | ||
57 | #define DUMP_NONE_STR "none" | ||
58 | #define DUMP_CCW_STR "ccw" | ||
59 | #define DUMP_FCP_STR "fcp" | ||
60 | |||
61 | static char *dump_type_str(enum dump_type type) | 94 | static char *dump_type_str(enum dump_type type) |
62 | { | 95 | { |
63 | switch (type) { | 96 | switch (type) { |
@@ -99,30 +132,6 @@ enum dump_method { | |||
99 | DUMP_METHOD_FCP_DIAG, | 132 | DUMP_METHOD_FCP_DIAG, |
100 | }; | 133 | }; |
101 | 134 | ||
102 | enum shutdown_action { | ||
103 | SHUTDOWN_REIPL, | ||
104 | SHUTDOWN_DUMP, | ||
105 | SHUTDOWN_STOP, | ||
106 | }; | ||
107 | |||
108 | #define SHUTDOWN_REIPL_STR "reipl" | ||
109 | #define SHUTDOWN_DUMP_STR "dump" | ||
110 | #define SHUTDOWN_STOP_STR "stop" | ||
111 | |||
112 | static char *shutdown_action_str(enum shutdown_action action) | ||
113 | { | ||
114 | switch (action) { | ||
115 | case SHUTDOWN_REIPL: | ||
116 | return SHUTDOWN_REIPL_STR; | ||
117 | case SHUTDOWN_DUMP: | ||
118 | return SHUTDOWN_DUMP_STR; | ||
119 | case SHUTDOWN_STOP: | ||
120 | return SHUTDOWN_STOP_STR; | ||
121 | default: | ||
122 | return NULL; | ||
123 | } | ||
124 | } | ||
125 | |||
126 | static int diag308_set_works = 0; | 135 | static int diag308_set_works = 0; |
127 | 136 | ||
128 | static int reipl_capabilities = IPL_TYPE_UNKNOWN; | 137 | static int reipl_capabilities = IPL_TYPE_UNKNOWN; |
@@ -140,8 +149,6 @@ static enum dump_method dump_method = DUMP_METHOD_NONE; | |||
140 | static struct ipl_parameter_block *dump_block_fcp; | 149 | static struct ipl_parameter_block *dump_block_fcp; |
141 | static struct ipl_parameter_block *dump_block_ccw; | 150 | static struct ipl_parameter_block *dump_block_ccw; |
142 | 151 | ||
143 | static enum shutdown_action on_panic_action = SHUTDOWN_STOP; | ||
144 | |||
145 | static struct sclp_ipl_info sclp_ipl_info; | 152 | static struct sclp_ipl_info sclp_ipl_info; |
146 | 153 | ||
147 | int diag308(unsigned long subcode, void *addr) | 154 | int diag308(unsigned long subcode, void *addr) |
@@ -162,22 +169,25 @@ EXPORT_SYMBOL_GPL(diag308); | |||
162 | /* SYSFS */ | 169 | /* SYSFS */ |
163 | 170 | ||
164 | #define DEFINE_IPL_ATTR_RO(_prefix, _name, _format, _value) \ | 171 | #define DEFINE_IPL_ATTR_RO(_prefix, _name, _format, _value) \ |
165 | static ssize_t sys_##_prefix##_##_name##_show(struct kset *kset, \ | 172 | static ssize_t sys_##_prefix##_##_name##_show(struct kobject *kobj, \ |
173 | struct kobj_attribute *attr, \ | ||
166 | char *page) \ | 174 | char *page) \ |
167 | { \ | 175 | { \ |
168 | return sprintf(page, _format, _value); \ | 176 | return sprintf(page, _format, _value); \ |
169 | } \ | 177 | } \ |
170 | static struct subsys_attribute sys_##_prefix##_##_name##_attr = \ | 178 | static struct kobj_attribute sys_##_prefix##_##_name##_attr = \ |
171 | __ATTR(_name, S_IRUGO, sys_##_prefix##_##_name##_show, NULL); | 179 | __ATTR(_name, S_IRUGO, sys_##_prefix##_##_name##_show, NULL); |
172 | 180 | ||
173 | #define DEFINE_IPL_ATTR_RW(_prefix, _name, _fmt_out, _fmt_in, _value) \ | 181 | #define DEFINE_IPL_ATTR_RW(_prefix, _name, _fmt_out, _fmt_in, _value) \ |
174 | static ssize_t sys_##_prefix##_##_name##_show(struct kset *kset, \ | 182 | static ssize_t sys_##_prefix##_##_name##_show(struct kobject *kobj, \ |
183 | struct kobj_attribute *attr, \ | ||
175 | char *page) \ | 184 | char *page) \ |
176 | { \ | 185 | { \ |
177 | return sprintf(page, _fmt_out, \ | 186 | return sprintf(page, _fmt_out, \ |
178 | (unsigned long long) _value); \ | 187 | (unsigned long long) _value); \ |
179 | } \ | 188 | } \ |
180 | static ssize_t sys_##_prefix##_##_name##_store(struct kset *kset, \ | 189 | static ssize_t sys_##_prefix##_##_name##_store(struct kobject *kobj, \ |
190 | struct kobj_attribute *attr, \ | ||
181 | const char *buf, size_t len) \ | 191 | const char *buf, size_t len) \ |
182 | { \ | 192 | { \ |
183 | unsigned long long value; \ | 193 | unsigned long long value; \ |
@@ -186,25 +196,27 @@ static ssize_t sys_##_prefix##_##_name##_store(struct kset *kset, \ | |||
186 | _value = value; \ | 196 | _value = value; \ |
187 | return len; \ | 197 | return len; \ |
188 | } \ | 198 | } \ |
189 | static struct subsys_attribute sys_##_prefix##_##_name##_attr = \ | 199 | static struct kobj_attribute sys_##_prefix##_##_name##_attr = \ |
190 | __ATTR(_name,(S_IRUGO | S_IWUSR), \ | 200 | __ATTR(_name,(S_IRUGO | S_IWUSR), \ |
191 | sys_##_prefix##_##_name##_show, \ | 201 | sys_##_prefix##_##_name##_show, \ |
192 | sys_##_prefix##_##_name##_store); | 202 | sys_##_prefix##_##_name##_store); |
193 | 203 | ||
194 | #define DEFINE_IPL_ATTR_STR_RW(_prefix, _name, _fmt_out, _fmt_in, _value)\ | 204 | #define DEFINE_IPL_ATTR_STR_RW(_prefix, _name, _fmt_out, _fmt_in, _value)\ |
195 | static ssize_t sys_##_prefix##_##_name##_show(struct kset *kset, \ | 205 | static ssize_t sys_##_prefix##_##_name##_show(struct kobject *kobj, \ |
206 | struct kobj_attribute *attr, \ | ||
196 | char *page) \ | 207 | char *page) \ |
197 | { \ | 208 | { \ |
198 | return sprintf(page, _fmt_out, _value); \ | 209 | return sprintf(page, _fmt_out, _value); \ |
199 | } \ | 210 | } \ |
200 | static ssize_t sys_##_prefix##_##_name##_store(struct kset *kset, \ | 211 | static ssize_t sys_##_prefix##_##_name##_store(struct kobject *kobj, \ |
212 | struct kobj_attribute *attr, \ | ||
201 | const char *buf, size_t len) \ | 213 | const char *buf, size_t len) \ |
202 | { \ | 214 | { \ |
203 | if (sscanf(buf, _fmt_in, _value) != 1) \ | 215 | strncpy(_value, buf, sizeof(_value) - 1); \ |
204 | return -EINVAL; \ | 216 | strstrip(_value); \ |
205 | return len; \ | 217 | return len; \ |
206 | } \ | 218 | } \ |
207 | static struct subsys_attribute sys_##_prefix##_##_name##_attr = \ | 219 | static struct kobj_attribute sys_##_prefix##_##_name##_attr = \ |
208 | __ATTR(_name,(S_IRUGO | S_IWUSR), \ | 220 | __ATTR(_name,(S_IRUGO | S_IWUSR), \ |
209 | sys_##_prefix##_##_name##_show, \ | 221 | sys_##_prefix##_##_name##_show, \ |
210 | sys_##_prefix##_##_name##_store); | 222 | sys_##_prefix##_##_name##_store); |
@@ -240,44 +252,19 @@ static __init enum ipl_type get_ipl_type(void) | |||
240 | return IPL_TYPE_FCP; | 252 | return IPL_TYPE_FCP; |
241 | } | 253 | } |
242 | 254 | ||
243 | void __init setup_ipl_info(void) | ||
244 | { | ||
245 | ipl_info.type = get_ipl_type(); | ||
246 | switch (ipl_info.type) { | ||
247 | case IPL_TYPE_CCW: | ||
248 | ipl_info.data.ccw.dev_id.devno = ipl_devno; | ||
249 | ipl_info.data.ccw.dev_id.ssid = 0; | ||
250 | break; | ||
251 | case IPL_TYPE_FCP: | ||
252 | case IPL_TYPE_FCP_DUMP: | ||
253 | ipl_info.data.fcp.dev_id.devno = | ||
254 | IPL_PARMBLOCK_START->ipl_info.fcp.devno; | ||
255 | ipl_info.data.fcp.dev_id.ssid = 0; | ||
256 | ipl_info.data.fcp.wwpn = IPL_PARMBLOCK_START->ipl_info.fcp.wwpn; | ||
257 | ipl_info.data.fcp.lun = IPL_PARMBLOCK_START->ipl_info.fcp.lun; | ||
258 | break; | ||
259 | case IPL_TYPE_NSS: | ||
260 | strncpy(ipl_info.data.nss.name, kernel_nss_name, | ||
261 | sizeof(ipl_info.data.nss.name)); | ||
262 | break; | ||
263 | case IPL_TYPE_UNKNOWN: | ||
264 | default: | ||
265 | /* We have no info to copy */ | ||
266 | break; | ||
267 | } | ||
268 | } | ||
269 | |||
270 | struct ipl_info ipl_info; | 255 | struct ipl_info ipl_info; |
271 | EXPORT_SYMBOL_GPL(ipl_info); | 256 | EXPORT_SYMBOL_GPL(ipl_info); |
272 | 257 | ||
273 | static ssize_t ipl_type_show(struct kset *kset, char *page) | 258 | static ssize_t ipl_type_show(struct kobject *kobj, struct kobj_attribute *attr, |
259 | char *page) | ||
274 | { | 260 | { |
275 | return sprintf(page, "%s\n", ipl_type_str(ipl_info.type)); | 261 | return sprintf(page, "%s\n", ipl_type_str(ipl_info.type)); |
276 | } | 262 | } |
277 | 263 | ||
278 | static struct subsys_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type); | 264 | static struct kobj_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type); |
279 | 265 | ||
280 | static ssize_t sys_ipl_device_show(struct kset *kset, char *page) | 266 | static ssize_t sys_ipl_device_show(struct kobject *kobj, |
267 | struct kobj_attribute *attr, char *page) | ||
281 | { | 268 | { |
282 | struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START; | 269 | struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START; |
283 | 270 | ||
@@ -292,7 +279,7 @@ static ssize_t sys_ipl_device_show(struct kset *kset, char *page) | |||
292 | } | 279 | } |
293 | } | 280 | } |
294 | 281 | ||
295 | static struct subsys_attribute sys_ipl_device_attr = | 282 | static struct kobj_attribute sys_ipl_device_attr = |
296 | __ATTR(device, S_IRUGO, sys_ipl_device_show, NULL); | 283 | __ATTR(device, S_IRUGO, sys_ipl_device_show, NULL); |
297 | 284 | ||
298 | static ssize_t ipl_parameter_read(struct kobject *kobj, struct bin_attribute *attr, | 285 | static ssize_t ipl_parameter_read(struct kobject *kobj, struct bin_attribute *attr, |
@@ -367,7 +354,8 @@ static struct attribute_group ipl_fcp_attr_group = { | |||
367 | 354 | ||
368 | /* CCW ipl device attributes */ | 355 | /* CCW ipl device attributes */ |
369 | 356 | ||
370 | static ssize_t ipl_ccw_loadparm_show(struct kset *kset, char *page) | 357 | static ssize_t ipl_ccw_loadparm_show(struct kobject *kobj, |
358 | struct kobj_attribute *attr, char *page) | ||
371 | { | 359 | { |
372 | char loadparm[LOADPARM_LEN + 1] = {}; | 360 | char loadparm[LOADPARM_LEN + 1] = {}; |
373 | 361 | ||
@@ -379,7 +367,7 @@ static ssize_t ipl_ccw_loadparm_show(struct kset *kset, char *page) | |||
379 | return sprintf(page, "%s\n", loadparm); | 367 | return sprintf(page, "%s\n", loadparm); |
380 | } | 368 | } |
381 | 369 | ||
382 | static struct subsys_attribute sys_ipl_ccw_loadparm_attr = | 370 | static struct kobj_attribute sys_ipl_ccw_loadparm_attr = |
383 | __ATTR(loadparm, 0444, ipl_ccw_loadparm_show, NULL); | 371 | __ATTR(loadparm, 0444, ipl_ccw_loadparm_show, NULL); |
384 | 372 | ||
385 | static struct attribute *ipl_ccw_attrs[] = { | 373 | static struct attribute *ipl_ccw_attrs[] = { |
@@ -418,10 +406,76 @@ static struct attribute_group ipl_unknown_attr_group = { | |||
418 | .attrs = ipl_unknown_attrs, | 406 | .attrs = ipl_unknown_attrs, |
419 | }; | 407 | }; |
420 | 408 | ||
421 | static decl_subsys(ipl, NULL, NULL); | 409 | static struct kset *ipl_kset; |
410 | |||
411 | static int __init ipl_register_fcp_files(void) | ||
412 | { | ||
413 | int rc; | ||
414 | |||
415 | rc = sysfs_create_group(&ipl_kset->kobj, &ipl_fcp_attr_group); | ||
416 | if (rc) | ||
417 | goto out; | ||
418 | rc = sysfs_create_bin_file(&ipl_kset->kobj, &ipl_parameter_attr); | ||
419 | if (rc) | ||
420 | goto out_ipl_parm; | ||
421 | rc = sysfs_create_bin_file(&ipl_kset->kobj, &ipl_scp_data_attr); | ||
422 | if (!rc) | ||
423 | goto out; | ||
424 | |||
425 | sysfs_remove_bin_file(&ipl_kset->kobj, &ipl_parameter_attr); | ||
426 | |||
427 | out_ipl_parm: | ||
428 | sysfs_remove_group(&ipl_kset->kobj, &ipl_fcp_attr_group); | ||
429 | out: | ||
430 | return rc; | ||
431 | } | ||
432 | |||
433 | static void ipl_run(struct shutdown_trigger *trigger) | ||
434 | { | ||
435 | diag308(DIAG308_IPL, NULL); | ||
436 | if (MACHINE_IS_VM) | ||
437 | __cpcmd("IPL", NULL, 0, NULL); | ||
438 | else if (ipl_info.type == IPL_TYPE_CCW) | ||
439 | reipl_ccw_dev(&ipl_info.data.ccw.dev_id); | ||
440 | } | ||
441 | |||
442 | static int ipl_init(void) | ||
443 | { | ||
444 | int rc; | ||
445 | |||
446 | ipl_kset = kset_create_and_add("ipl", NULL, firmware_kobj); | ||
447 | if (!ipl_kset) { | ||
448 | rc = -ENOMEM; | ||
449 | goto out; | ||
450 | } | ||
451 | switch (ipl_info.type) { | ||
452 | case IPL_TYPE_CCW: | ||
453 | rc = sysfs_create_group(&ipl_kset->kobj, &ipl_ccw_attr_group); | ||
454 | break; | ||
455 | case IPL_TYPE_FCP: | ||
456 | case IPL_TYPE_FCP_DUMP: | ||
457 | rc = ipl_register_fcp_files(); | ||
458 | break; | ||
459 | case IPL_TYPE_NSS: | ||
460 | rc = sysfs_create_group(&ipl_kset->kobj, &ipl_nss_attr_group); | ||
461 | break; | ||
462 | default: | ||
463 | rc = sysfs_create_group(&ipl_kset->kobj, | ||
464 | &ipl_unknown_attr_group); | ||
465 | break; | ||
466 | } | ||
467 | out: | ||
468 | if (rc) | ||
469 | panic("ipl_init failed: rc = %i\n", rc); | ||
470 | |||
471 | return 0; | ||
472 | } | ||
473 | |||
474 | static struct shutdown_action ipl_action = {SHUTDOWN_ACTION_IPL_STR, ipl_run, | ||
475 | ipl_init}; | ||
422 | 476 | ||
423 | /* | 477 | /* |
424 | * reipl section | 478 | * reipl shutdown action: Reboot Linux on shutdown. |
425 | */ | 479 | */ |
426 | 480 | ||
427 | /* FCP reipl device attributes */ | 481 | /* FCP reipl device attributes */ |
@@ -465,7 +519,8 @@ static void reipl_get_ascii_loadparm(char *loadparm) | |||
465 | strstrip(loadparm); | 519 | strstrip(loadparm); |
466 | } | 520 | } |
467 | 521 | ||
468 | static ssize_t reipl_ccw_loadparm_show(struct kset *kset, char *page) | 522 | static ssize_t reipl_ccw_loadparm_show(struct kobject *kobj, |
523 | struct kobj_attribute *attr, char *page) | ||
469 | { | 524 | { |
470 | char buf[LOADPARM_LEN + 1]; | 525 | char buf[LOADPARM_LEN + 1]; |
471 | 526 | ||
@@ -473,7 +528,8 @@ static ssize_t reipl_ccw_loadparm_show(struct kset *kset, char *page) | |||
473 | return sprintf(page, "%s\n", buf); | 528 | return sprintf(page, "%s\n", buf); |
474 | } | 529 | } |
475 | 530 | ||
476 | static ssize_t reipl_ccw_loadparm_store(struct kset *kset, | 531 | static ssize_t reipl_ccw_loadparm_store(struct kobject *kobj, |
532 | struct kobj_attribute *attr, | ||
477 | const char *buf, size_t len) | 533 | const char *buf, size_t len) |
478 | { | 534 | { |
479 | int i, lp_len; | 535 | int i, lp_len; |
@@ -500,7 +556,7 @@ static ssize_t reipl_ccw_loadparm_store(struct kset *kset, | |||
500 | return len; | 556 | return len; |
501 | } | 557 | } |
502 | 558 | ||
503 | static struct subsys_attribute sys_reipl_ccw_loadparm_attr = | 559 | static struct kobj_attribute sys_reipl_ccw_loadparm_attr = |
504 | __ATTR(loadparm, 0644, reipl_ccw_loadparm_show, | 560 | __ATTR(loadparm, 0644, reipl_ccw_loadparm_show, |
505 | reipl_ccw_loadparm_store); | 561 | reipl_ccw_loadparm_store); |
506 | 562 | ||
@@ -539,7 +595,9 @@ static int reipl_set_type(enum ipl_type type) | |||
539 | 595 | ||
540 | switch(type) { | 596 | switch(type) { |
541 | case IPL_TYPE_CCW: | 597 | case IPL_TYPE_CCW: |
542 | if (MACHINE_IS_VM) | 598 | if (diag308_set_works) |
599 | reipl_method = REIPL_METHOD_CCW_DIAG; | ||
600 | else if (MACHINE_IS_VM) | ||
543 | reipl_method = REIPL_METHOD_CCW_VM; | 601 | reipl_method = REIPL_METHOD_CCW_VM; |
544 | else | 602 | else |
545 | reipl_method = REIPL_METHOD_CCW_CIO; | 603 | reipl_method = REIPL_METHOD_CCW_CIO; |
@@ -568,13 +626,15 @@ static int reipl_set_type(enum ipl_type type) | |||
568 | return 0; | 626 | return 0; |
569 | } | 627 | } |
570 | 628 | ||
571 | static ssize_t reipl_type_show(struct kset *kset, char *page) | 629 | static ssize_t reipl_type_show(struct kobject *kobj, |
630 | struct kobj_attribute *attr, char *page) | ||
572 | { | 631 | { |
573 | return sprintf(page, "%s\n", ipl_type_str(reipl_type)); | 632 | return sprintf(page, "%s\n", ipl_type_str(reipl_type)); |
574 | } | 633 | } |
575 | 634 | ||
576 | static ssize_t reipl_type_store(struct kset *kset, const char *buf, | 635 | static ssize_t reipl_type_store(struct kobject *kobj, |
577 | size_t len) | 636 | struct kobj_attribute *attr, |
637 | const char *buf, size_t len) | ||
578 | { | 638 | { |
579 | int rc = -EINVAL; | 639 | int rc = -EINVAL; |
580 | 640 | ||
@@ -587,140 +647,12 @@ static ssize_t reipl_type_store(struct kset *kset, const char *buf, | |||
587 | return (rc != 0) ? rc : len; | 647 | return (rc != 0) ? rc : len; |
588 | } | 648 | } |
589 | 649 | ||
590 | static struct subsys_attribute reipl_type_attr = | 650 | static struct kobj_attribute reipl_type_attr = |
591 | __ATTR(reipl_type, 0644, reipl_type_show, reipl_type_store); | 651 | __ATTR(reipl_type, 0644, reipl_type_show, reipl_type_store); |
592 | |||
593 | static decl_subsys(reipl, NULL, NULL); | ||
594 | |||
595 | /* | ||
596 | * dump section | ||
597 | */ | ||
598 | |||
599 | /* FCP dump device attributes */ | ||
600 | |||
601 | DEFINE_IPL_ATTR_RW(dump_fcp, wwpn, "0x%016llx\n", "%016llx\n", | ||
602 | dump_block_fcp->ipl_info.fcp.wwpn); | ||
603 | DEFINE_IPL_ATTR_RW(dump_fcp, lun, "0x%016llx\n", "%016llx\n", | ||
604 | dump_block_fcp->ipl_info.fcp.lun); | ||
605 | DEFINE_IPL_ATTR_RW(dump_fcp, bootprog, "%lld\n", "%lld\n", | ||
606 | dump_block_fcp->ipl_info.fcp.bootprog); | ||
607 | DEFINE_IPL_ATTR_RW(dump_fcp, br_lba, "%lld\n", "%lld\n", | ||
608 | dump_block_fcp->ipl_info.fcp.br_lba); | ||
609 | DEFINE_IPL_ATTR_RW(dump_fcp, device, "0.0.%04llx\n", "0.0.%llx\n", | ||
610 | dump_block_fcp->ipl_info.fcp.devno); | ||
611 | |||
612 | static struct attribute *dump_fcp_attrs[] = { | ||
613 | &sys_dump_fcp_device_attr.attr, | ||
614 | &sys_dump_fcp_wwpn_attr.attr, | ||
615 | &sys_dump_fcp_lun_attr.attr, | ||
616 | &sys_dump_fcp_bootprog_attr.attr, | ||
617 | &sys_dump_fcp_br_lba_attr.attr, | ||
618 | NULL, | ||
619 | }; | ||
620 | |||
621 | static struct attribute_group dump_fcp_attr_group = { | ||
622 | .name = IPL_FCP_STR, | ||
623 | .attrs = dump_fcp_attrs, | ||
624 | }; | ||
625 | |||
626 | /* CCW dump device attributes */ | ||
627 | |||
628 | DEFINE_IPL_ATTR_RW(dump_ccw, device, "0.0.%04llx\n", "0.0.%llx\n", | ||
629 | dump_block_ccw->ipl_info.ccw.devno); | ||
630 | |||
631 | static struct attribute *dump_ccw_attrs[] = { | ||
632 | &sys_dump_ccw_device_attr.attr, | ||
633 | NULL, | ||
634 | }; | ||
635 | |||
636 | static struct attribute_group dump_ccw_attr_group = { | ||
637 | .name = IPL_CCW_STR, | ||
638 | .attrs = dump_ccw_attrs, | ||
639 | }; | ||
640 | |||
641 | /* dump type */ | ||
642 | |||
643 | static int dump_set_type(enum dump_type type) | ||
644 | { | ||
645 | if (!(dump_capabilities & type)) | ||
646 | return -EINVAL; | ||
647 | switch(type) { | ||
648 | case DUMP_TYPE_CCW: | ||
649 | if (MACHINE_IS_VM) | ||
650 | dump_method = DUMP_METHOD_CCW_VM; | ||
651 | else if (diag308_set_works) | ||
652 | dump_method = DUMP_METHOD_CCW_DIAG; | ||
653 | else | ||
654 | dump_method = DUMP_METHOD_CCW_CIO; | ||
655 | break; | ||
656 | case DUMP_TYPE_FCP: | ||
657 | dump_method = DUMP_METHOD_FCP_DIAG; | ||
658 | break; | ||
659 | default: | ||
660 | dump_method = DUMP_METHOD_NONE; | ||
661 | } | ||
662 | dump_type = type; | ||
663 | return 0; | ||
664 | } | ||
665 | |||
666 | static ssize_t dump_type_show(struct kset *kset, char *page) | ||
667 | { | ||
668 | return sprintf(page, "%s\n", dump_type_str(dump_type)); | ||
669 | } | ||
670 | |||
671 | static ssize_t dump_type_store(struct kset *kset, const char *buf, | ||
672 | size_t len) | ||
673 | { | ||
674 | int rc = -EINVAL; | ||
675 | |||
676 | if (strncmp(buf, DUMP_NONE_STR, strlen(DUMP_NONE_STR)) == 0) | ||
677 | rc = dump_set_type(DUMP_TYPE_NONE); | ||
678 | else if (strncmp(buf, DUMP_CCW_STR, strlen(DUMP_CCW_STR)) == 0) | ||
679 | rc = dump_set_type(DUMP_TYPE_CCW); | ||
680 | else if (strncmp(buf, DUMP_FCP_STR, strlen(DUMP_FCP_STR)) == 0) | ||
681 | rc = dump_set_type(DUMP_TYPE_FCP); | ||
682 | return (rc != 0) ? rc : len; | ||
683 | } | ||
684 | |||
685 | static struct subsys_attribute dump_type_attr = | ||
686 | __ATTR(dump_type, 0644, dump_type_show, dump_type_store); | ||
687 | |||
688 | static decl_subsys(dump, NULL, NULL); | ||
689 | |||
690 | /* | ||
691 | * Shutdown actions section | ||
692 | */ | ||
693 | |||
694 | static decl_subsys(shutdown_actions, NULL, NULL); | ||
695 | |||
696 | /* on panic */ | ||
697 | |||
698 | static ssize_t on_panic_show(struct kset *kset, char *page) | ||
699 | { | ||
700 | return sprintf(page, "%s\n", shutdown_action_str(on_panic_action)); | ||
701 | } | ||
702 | |||
703 | static ssize_t on_panic_store(struct kset *kset, const char *buf, | ||
704 | size_t len) | ||
705 | { | ||
706 | if (strncmp(buf, SHUTDOWN_REIPL_STR, strlen(SHUTDOWN_REIPL_STR)) == 0) | ||
707 | on_panic_action = SHUTDOWN_REIPL; | ||
708 | else if (strncmp(buf, SHUTDOWN_DUMP_STR, | ||
709 | strlen(SHUTDOWN_DUMP_STR)) == 0) | ||
710 | on_panic_action = SHUTDOWN_DUMP; | ||
711 | else if (strncmp(buf, SHUTDOWN_STOP_STR, | ||
712 | strlen(SHUTDOWN_STOP_STR)) == 0) | ||
713 | on_panic_action = SHUTDOWN_STOP; | ||
714 | else | ||
715 | return -EINVAL; | ||
716 | |||
717 | return len; | ||
718 | } | ||
719 | 652 | ||
720 | static struct subsys_attribute on_panic_attr = | 653 | static struct kset *reipl_kset; |
721 | __ATTR(on_panic, 0644, on_panic_show, on_panic_store); | ||
722 | 654 | ||
723 | void do_reipl(void) | 655 | void reipl_run(struct shutdown_trigger *trigger) |
724 | { | 656 | { |
725 | struct ccw_dev_id devid; | 657 | struct ccw_dev_id devid; |
726 | static char buf[100]; | 658 | static char buf[100]; |
@@ -729,8 +661,6 @@ void do_reipl(void) | |||
729 | switch (reipl_method) { | 661 | switch (reipl_method) { |
730 | case REIPL_METHOD_CCW_CIO: | 662 | case REIPL_METHOD_CCW_CIO: |
731 | devid.devno = reipl_block_ccw->ipl_info.ccw.devno; | 663 | devid.devno = reipl_block_ccw->ipl_info.ccw.devno; |
732 | if (ipl_info.type == IPL_TYPE_CCW && devid.devno == ipl_devno) | ||
733 | diag308(DIAG308_IPL, NULL); | ||
734 | devid.ssid = 0; | 664 | devid.ssid = 0; |
735 | reipl_ccw_dev(&devid); | 665 | reipl_ccw_dev(&devid); |
736 | break; | 666 | break; |
@@ -771,98 +701,6 @@ void do_reipl(void) | |||
771 | default: | 701 | default: |
772 | break; | 702 | break; |
773 | } | 703 | } |
774 | signal_processor(smp_processor_id(), sigp_stop_and_store_status); | ||
775 | } | ||
776 | |||
777 | static void do_dump(void) | ||
778 | { | ||
779 | struct ccw_dev_id devid; | ||
780 | static char buf[100]; | ||
781 | |||
782 | switch (dump_method) { | ||
783 | case DUMP_METHOD_CCW_CIO: | ||
784 | smp_send_stop(); | ||
785 | devid.devno = dump_block_ccw->ipl_info.ccw.devno; | ||
786 | devid.ssid = 0; | ||
787 | reipl_ccw_dev(&devid); | ||
788 | break; | ||
789 | case DUMP_METHOD_CCW_VM: | ||
790 | smp_send_stop(); | ||
791 | sprintf(buf, "STORE STATUS"); | ||
792 | __cpcmd(buf, NULL, 0, NULL); | ||
793 | sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno); | ||
794 | __cpcmd(buf, NULL, 0, NULL); | ||
795 | break; | ||
796 | case DUMP_METHOD_CCW_DIAG: | ||
797 | diag308(DIAG308_SET, dump_block_ccw); | ||
798 | diag308(DIAG308_DUMP, NULL); | ||
799 | break; | ||
800 | case DUMP_METHOD_FCP_DIAG: | ||
801 | diag308(DIAG308_SET, dump_block_fcp); | ||
802 | diag308(DIAG308_DUMP, NULL); | ||
803 | break; | ||
804 | case DUMP_METHOD_NONE: | ||
805 | default: | ||
806 | return; | ||
807 | } | ||
808 | printk(KERN_EMERG "Dump failed!\n"); | ||
809 | } | ||
810 | |||
811 | /* init functions */ | ||
812 | |||
813 | static int __init ipl_register_fcp_files(void) | ||
814 | { | ||
815 | int rc; | ||
816 | |||
817 | rc = sysfs_create_group(&ipl_subsys.kobj, | ||
818 | &ipl_fcp_attr_group); | ||
819 | if (rc) | ||
820 | goto out; | ||
821 | rc = sysfs_create_bin_file(&ipl_subsys.kobj, | ||
822 | &ipl_parameter_attr); | ||
823 | if (rc) | ||
824 | goto out_ipl_parm; | ||
825 | rc = sysfs_create_bin_file(&ipl_subsys.kobj, | ||
826 | &ipl_scp_data_attr); | ||
827 | if (!rc) | ||
828 | goto out; | ||
829 | |||
830 | sysfs_remove_bin_file(&ipl_subsys.kobj, &ipl_parameter_attr); | ||
831 | |||
832 | out_ipl_parm: | ||
833 | sysfs_remove_group(&ipl_subsys.kobj, &ipl_fcp_attr_group); | ||
834 | out: | ||
835 | return rc; | ||
836 | } | ||
837 | |||
838 | static int __init ipl_init(void) | ||
839 | { | ||
840 | int rc; | ||
841 | |||
842 | rc = firmware_register(&ipl_subsys); | ||
843 | if (rc) | ||
844 | return rc; | ||
845 | switch (ipl_info.type) { | ||
846 | case IPL_TYPE_CCW: | ||
847 | rc = sysfs_create_group(&ipl_subsys.kobj, | ||
848 | &ipl_ccw_attr_group); | ||
849 | break; | ||
850 | case IPL_TYPE_FCP: | ||
851 | case IPL_TYPE_FCP_DUMP: | ||
852 | rc = ipl_register_fcp_files(); | ||
853 | break; | ||
854 | case IPL_TYPE_NSS: | ||
855 | rc = sysfs_create_group(&ipl_subsys.kobj, | ||
856 | &ipl_nss_attr_group); | ||
857 | break; | ||
858 | default: | ||
859 | rc = sysfs_create_group(&ipl_subsys.kobj, | ||
860 | &ipl_unknown_attr_group); | ||
861 | break; | ||
862 | } | ||
863 | if (rc) | ||
864 | firmware_unregister(&ipl_subsys); | ||
865 | return rc; | ||
866 | } | 704 | } |
867 | 705 | ||
868 | static void __init reipl_probe(void) | 706 | static void __init reipl_probe(void) |
@@ -883,7 +721,7 @@ static int __init reipl_nss_init(void) | |||
883 | 721 | ||
884 | if (!MACHINE_IS_VM) | 722 | if (!MACHINE_IS_VM) |
885 | return 0; | 723 | return 0; |
886 | rc = sysfs_create_group(&reipl_subsys.kobj, &reipl_nss_attr_group); | 724 | rc = sysfs_create_group(&reipl_kset->kobj, &reipl_nss_attr_group); |
887 | if (rc) | 725 | if (rc) |
888 | return rc; | 726 | return rc; |
889 | strncpy(reipl_nss_name, kernel_nss_name, NSS_NAME_SIZE + 1); | 727 | strncpy(reipl_nss_name, kernel_nss_name, NSS_NAME_SIZE + 1); |
@@ -898,7 +736,7 @@ static int __init reipl_ccw_init(void) | |||
898 | reipl_block_ccw = (void *) get_zeroed_page(GFP_KERNEL); | 736 | reipl_block_ccw = (void *) get_zeroed_page(GFP_KERNEL); |
899 | if (!reipl_block_ccw) | 737 | if (!reipl_block_ccw) |
900 | return -ENOMEM; | 738 | return -ENOMEM; |
901 | rc = sysfs_create_group(&reipl_subsys.kobj, &reipl_ccw_attr_group); | 739 | rc = sysfs_create_group(&reipl_kset->kobj, &reipl_ccw_attr_group); |
902 | if (rc) { | 740 | if (rc) { |
903 | free_page((unsigned long)reipl_block_ccw); | 741 | free_page((unsigned long)reipl_block_ccw); |
904 | return rc; | 742 | return rc; |
@@ -907,6 +745,7 @@ static int __init reipl_ccw_init(void) | |||
907 | reipl_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION; | 745 | reipl_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION; |
908 | reipl_block_ccw->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN; | 746 | reipl_block_ccw->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN; |
909 | reipl_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW; | 747 | reipl_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW; |
748 | reipl_block_ccw->hdr.flags = DIAG308_FLAGS_LP_VALID; | ||
910 | /* check if read scp info worked and set loadparm */ | 749 | /* check if read scp info worked and set loadparm */ |
911 | if (sclp_ipl_info.is_valid) | 750 | if (sclp_ipl_info.is_valid) |
912 | memcpy(reipl_block_ccw->ipl_info.ccw.load_param, | 751 | memcpy(reipl_block_ccw->ipl_info.ccw.load_param, |
@@ -915,8 +754,7 @@ static int __init reipl_ccw_init(void) | |||
915 | /* read scp info failed: set empty loadparm (EBCDIC blanks) */ | 754 | /* read scp info failed: set empty loadparm (EBCDIC blanks) */ |
916 | memset(reipl_block_ccw->ipl_info.ccw.load_param, 0x40, | 755 | memset(reipl_block_ccw->ipl_info.ccw.load_param, 0x40, |
917 | LOADPARM_LEN); | 756 | LOADPARM_LEN); |
918 | /* FIXME: check for diag308_set_works when enabling diag ccw reipl */ | 757 | if (!MACHINE_IS_VM && !diag308_set_works) |
919 | if (!MACHINE_IS_VM) | ||
920 | sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO; | 758 | sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO; |
921 | if (ipl_info.type == IPL_TYPE_CCW) | 759 | if (ipl_info.type == IPL_TYPE_CCW) |
922 | reipl_block_ccw->ipl_info.ccw.devno = ipl_devno; | 760 | reipl_block_ccw->ipl_info.ccw.devno = ipl_devno; |
@@ -936,7 +774,7 @@ static int __init reipl_fcp_init(void) | |||
936 | reipl_block_fcp = (void *) get_zeroed_page(GFP_KERNEL); | 774 | reipl_block_fcp = (void *) get_zeroed_page(GFP_KERNEL); |
937 | if (!reipl_block_fcp) | 775 | if (!reipl_block_fcp) |
938 | return -ENOMEM; | 776 | return -ENOMEM; |
939 | rc = sysfs_create_group(&reipl_subsys.kobj, &reipl_fcp_attr_group); | 777 | rc = sysfs_create_group(&reipl_kset->kobj, &reipl_fcp_attr_group); |
940 | if (rc) { | 778 | if (rc) { |
941 | free_page((unsigned long)reipl_block_fcp); | 779 | free_page((unsigned long)reipl_block_fcp); |
942 | return rc; | 780 | return rc; |
@@ -954,16 +792,16 @@ static int __init reipl_fcp_init(void) | |||
954 | return 0; | 792 | return 0; |
955 | } | 793 | } |
956 | 794 | ||
957 | static int __init reipl_init(void) | 795 | static int reipl_init(void) |
958 | { | 796 | { |
959 | int rc; | 797 | int rc; |
960 | 798 | ||
961 | rc = firmware_register(&reipl_subsys); | 799 | reipl_kset = kset_create_and_add("reipl", NULL, firmware_kobj); |
962 | if (rc) | 800 | if (!reipl_kset) |
963 | return rc; | 801 | return -ENOMEM; |
964 | rc = subsys_create_file(&reipl_subsys, &reipl_type_attr); | 802 | rc = sysfs_create_file(&reipl_kset->kobj, &reipl_type_attr.attr); |
965 | if (rc) { | 803 | if (rc) { |
966 | firmware_unregister(&reipl_subsys); | 804 | kset_unregister(reipl_kset); |
967 | return rc; | 805 | return rc; |
968 | } | 806 | } |
969 | rc = reipl_ccw_init(); | 807 | rc = reipl_ccw_init(); |
@@ -981,6 +819,140 @@ static int __init reipl_init(void) | |||
981 | return 0; | 819 | return 0; |
982 | } | 820 | } |
983 | 821 | ||
822 | static struct shutdown_action reipl_action = {SHUTDOWN_ACTION_REIPL_STR, | ||
823 | reipl_run, reipl_init}; | ||
824 | |||
825 | /* | ||
826 | * dump shutdown action: Dump Linux on shutdown. | ||
827 | */ | ||
828 | |||
829 | /* FCP dump device attributes */ | ||
830 | |||
831 | DEFINE_IPL_ATTR_RW(dump_fcp, wwpn, "0x%016llx\n", "%016llx\n", | ||
832 | dump_block_fcp->ipl_info.fcp.wwpn); | ||
833 | DEFINE_IPL_ATTR_RW(dump_fcp, lun, "0x%016llx\n", "%016llx\n", | ||
834 | dump_block_fcp->ipl_info.fcp.lun); | ||
835 | DEFINE_IPL_ATTR_RW(dump_fcp, bootprog, "%lld\n", "%lld\n", | ||
836 | dump_block_fcp->ipl_info.fcp.bootprog); | ||
837 | DEFINE_IPL_ATTR_RW(dump_fcp, br_lba, "%lld\n", "%lld\n", | ||
838 | dump_block_fcp->ipl_info.fcp.br_lba); | ||
839 | DEFINE_IPL_ATTR_RW(dump_fcp, device, "0.0.%04llx\n", "0.0.%llx\n", | ||
840 | dump_block_fcp->ipl_info.fcp.devno); | ||
841 | |||
842 | static struct attribute *dump_fcp_attrs[] = { | ||
843 | &sys_dump_fcp_device_attr.attr, | ||
844 | &sys_dump_fcp_wwpn_attr.attr, | ||
845 | &sys_dump_fcp_lun_attr.attr, | ||
846 | &sys_dump_fcp_bootprog_attr.attr, | ||
847 | &sys_dump_fcp_br_lba_attr.attr, | ||
848 | NULL, | ||
849 | }; | ||
850 | |||
851 | static struct attribute_group dump_fcp_attr_group = { | ||
852 | .name = IPL_FCP_STR, | ||
853 | .attrs = dump_fcp_attrs, | ||
854 | }; | ||
855 | |||
856 | /* CCW dump device attributes */ | ||
857 | |||
858 | DEFINE_IPL_ATTR_RW(dump_ccw, device, "0.0.%04llx\n", "0.0.%llx\n", | ||
859 | dump_block_ccw->ipl_info.ccw.devno); | ||
860 | |||
861 | static struct attribute *dump_ccw_attrs[] = { | ||
862 | &sys_dump_ccw_device_attr.attr, | ||
863 | NULL, | ||
864 | }; | ||
865 | |||
866 | static struct attribute_group dump_ccw_attr_group = { | ||
867 | .name = IPL_CCW_STR, | ||
868 | .attrs = dump_ccw_attrs, | ||
869 | }; | ||
870 | |||
871 | /* dump type */ | ||
872 | |||
873 | static int dump_set_type(enum dump_type type) | ||
874 | { | ||
875 | if (!(dump_capabilities & type)) | ||
876 | return -EINVAL; | ||
877 | switch (type) { | ||
878 | case DUMP_TYPE_CCW: | ||
879 | if (diag308_set_works) | ||
880 | dump_method = DUMP_METHOD_CCW_DIAG; | ||
881 | else if (MACHINE_IS_VM) | ||
882 | dump_method = DUMP_METHOD_CCW_VM; | ||
883 | else | ||
884 | dump_method = DUMP_METHOD_CCW_CIO; | ||
885 | break; | ||
886 | case DUMP_TYPE_FCP: | ||
887 | dump_method = DUMP_METHOD_FCP_DIAG; | ||
888 | break; | ||
889 | default: | ||
890 | dump_method = DUMP_METHOD_NONE; | ||
891 | } | ||
892 | dump_type = type; | ||
893 | return 0; | ||
894 | } | ||
895 | |||
896 | static ssize_t dump_type_show(struct kobject *kobj, | ||
897 | struct kobj_attribute *attr, char *page) | ||
898 | { | ||
899 | return sprintf(page, "%s\n", dump_type_str(dump_type)); | ||
900 | } | ||
901 | |||
902 | static ssize_t dump_type_store(struct kobject *kobj, | ||
903 | struct kobj_attribute *attr, | ||
904 | const char *buf, size_t len) | ||
905 | { | ||
906 | int rc = -EINVAL; | ||
907 | |||
908 | if (strncmp(buf, DUMP_NONE_STR, strlen(DUMP_NONE_STR)) == 0) | ||
909 | rc = dump_set_type(DUMP_TYPE_NONE); | ||
910 | else if (strncmp(buf, DUMP_CCW_STR, strlen(DUMP_CCW_STR)) == 0) | ||
911 | rc = dump_set_type(DUMP_TYPE_CCW); | ||
912 | else if (strncmp(buf, DUMP_FCP_STR, strlen(DUMP_FCP_STR)) == 0) | ||
913 | rc = dump_set_type(DUMP_TYPE_FCP); | ||
914 | return (rc != 0) ? rc : len; | ||
915 | } | ||
916 | |||
917 | static struct kobj_attribute dump_type_attr = | ||
918 | __ATTR(dump_type, 0644, dump_type_show, dump_type_store); | ||
919 | |||
920 | static struct kset *dump_kset; | ||
921 | |||
922 | static void dump_run(struct shutdown_trigger *trigger) | ||
923 | { | ||
924 | struct ccw_dev_id devid; | ||
925 | static char buf[100]; | ||
926 | |||
927 | switch (dump_method) { | ||
928 | case DUMP_METHOD_CCW_CIO: | ||
929 | smp_send_stop(); | ||
930 | devid.devno = dump_block_ccw->ipl_info.ccw.devno; | ||
931 | devid.ssid = 0; | ||
932 | reipl_ccw_dev(&devid); | ||
933 | break; | ||
934 | case DUMP_METHOD_CCW_VM: | ||
935 | smp_send_stop(); | ||
936 | sprintf(buf, "STORE STATUS"); | ||
937 | __cpcmd(buf, NULL, 0, NULL); | ||
938 | sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno); | ||
939 | __cpcmd(buf, NULL, 0, NULL); | ||
940 | break; | ||
941 | case DUMP_METHOD_CCW_DIAG: | ||
942 | diag308(DIAG308_SET, dump_block_ccw); | ||
943 | diag308(DIAG308_DUMP, NULL); | ||
944 | break; | ||
945 | case DUMP_METHOD_FCP_DIAG: | ||
946 | diag308(DIAG308_SET, dump_block_fcp); | ||
947 | diag308(DIAG308_DUMP, NULL); | ||
948 | break; | ||
949 | case DUMP_METHOD_NONE: | ||
950 | default: | ||
951 | return; | ||
952 | } | ||
953 | printk(KERN_EMERG "Dump failed!\n"); | ||
954 | } | ||
955 | |||
984 | static int __init dump_ccw_init(void) | 956 | static int __init dump_ccw_init(void) |
985 | { | 957 | { |
986 | int rc; | 958 | int rc; |
@@ -988,7 +960,7 @@ static int __init dump_ccw_init(void) | |||
988 | dump_block_ccw = (void *) get_zeroed_page(GFP_KERNEL); | 960 | dump_block_ccw = (void *) get_zeroed_page(GFP_KERNEL); |
989 | if (!dump_block_ccw) | 961 | if (!dump_block_ccw) |
990 | return -ENOMEM; | 962 | return -ENOMEM; |
991 | rc = sysfs_create_group(&dump_subsys.kobj, &dump_ccw_attr_group); | 963 | rc = sysfs_create_group(&dump_kset->kobj, &dump_ccw_attr_group); |
992 | if (rc) { | 964 | if (rc) { |
993 | free_page((unsigned long)dump_block_ccw); | 965 | free_page((unsigned long)dump_block_ccw); |
994 | return rc; | 966 | return rc; |
@@ -1012,7 +984,7 @@ static int __init dump_fcp_init(void) | |||
1012 | dump_block_fcp = (void *) get_zeroed_page(GFP_KERNEL); | 984 | dump_block_fcp = (void *) get_zeroed_page(GFP_KERNEL); |
1013 | if (!dump_block_fcp) | 985 | if (!dump_block_fcp) |
1014 | return -ENOMEM; | 986 | return -ENOMEM; |
1015 | rc = sysfs_create_group(&dump_subsys.kobj, &dump_fcp_attr_group); | 987 | rc = sysfs_create_group(&dump_kset->kobj, &dump_fcp_attr_group); |
1016 | if (rc) { | 988 | if (rc) { |
1017 | free_page((unsigned long)dump_block_fcp); | 989 | free_page((unsigned long)dump_block_fcp); |
1018 | return rc; | 990 | return rc; |
@@ -1026,33 +998,16 @@ static int __init dump_fcp_init(void) | |||
1026 | return 0; | 998 | return 0; |
1027 | } | 999 | } |
1028 | 1000 | ||
1029 | #define SHUTDOWN_ON_PANIC_PRIO 0 | 1001 | static int dump_init(void) |
1030 | |||
1031 | static int shutdown_on_panic_notify(struct notifier_block *self, | ||
1032 | unsigned long event, void *data) | ||
1033 | { | ||
1034 | if (on_panic_action == SHUTDOWN_DUMP) | ||
1035 | do_dump(); | ||
1036 | else if (on_panic_action == SHUTDOWN_REIPL) | ||
1037 | do_reipl(); | ||
1038 | return NOTIFY_OK; | ||
1039 | } | ||
1040 | |||
1041 | static struct notifier_block shutdown_on_panic_nb = { | ||
1042 | .notifier_call = shutdown_on_panic_notify, | ||
1043 | .priority = SHUTDOWN_ON_PANIC_PRIO | ||
1044 | }; | ||
1045 | |||
1046 | static int __init dump_init(void) | ||
1047 | { | 1002 | { |
1048 | int rc; | 1003 | int rc; |
1049 | 1004 | ||
1050 | rc = firmware_register(&dump_subsys); | 1005 | dump_kset = kset_create_and_add("dump", NULL, firmware_kobj); |
1051 | if (rc) | 1006 | if (!dump_kset) |
1052 | return rc; | 1007 | return -ENOMEM; |
1053 | rc = subsys_create_file(&dump_subsys, &dump_type_attr); | 1008 | rc = sysfs_create_file(&dump_kset->kobj, &dump_type_attr.attr); |
1054 | if (rc) { | 1009 | if (rc) { |
1055 | firmware_unregister(&dump_subsys); | 1010 | kset_unregister(dump_kset); |
1056 | return rc; | 1011 | return rc; |
1057 | } | 1012 | } |
1058 | rc = dump_ccw_init(); | 1013 | rc = dump_ccw_init(); |
@@ -1065,46 +1020,381 @@ static int __init dump_init(void) | |||
1065 | return 0; | 1020 | return 0; |
1066 | } | 1021 | } |
1067 | 1022 | ||
1068 | static int __init shutdown_actions_init(void) | 1023 | static struct shutdown_action dump_action = {SHUTDOWN_ACTION_DUMP_STR, |
1024 | dump_run, dump_init}; | ||
1025 | |||
1026 | /* | ||
1027 | * vmcmd shutdown action: Trigger vm command on shutdown. | ||
1028 | */ | ||
1029 | |||
1030 | static char vmcmd_on_reboot[128]; | ||
1031 | static char vmcmd_on_panic[128]; | ||
1032 | static char vmcmd_on_halt[128]; | ||
1033 | static char vmcmd_on_poff[128]; | ||
1034 | |||
1035 | DEFINE_IPL_ATTR_STR_RW(vmcmd, on_reboot, "%s\n", "%s\n", vmcmd_on_reboot); | ||
1036 | DEFINE_IPL_ATTR_STR_RW(vmcmd, on_panic, "%s\n", "%s\n", vmcmd_on_panic); | ||
1037 | DEFINE_IPL_ATTR_STR_RW(vmcmd, on_halt, "%s\n", "%s\n", vmcmd_on_halt); | ||
1038 | DEFINE_IPL_ATTR_STR_RW(vmcmd, on_poff, "%s\n", "%s\n", vmcmd_on_poff); | ||
1039 | |||
1040 | static struct attribute *vmcmd_attrs[] = { | ||
1041 | &sys_vmcmd_on_reboot_attr.attr, | ||
1042 | &sys_vmcmd_on_panic_attr.attr, | ||
1043 | &sys_vmcmd_on_halt_attr.attr, | ||
1044 | &sys_vmcmd_on_poff_attr.attr, | ||
1045 | NULL, | ||
1046 | }; | ||
1047 | |||
1048 | static struct attribute_group vmcmd_attr_group = { | ||
1049 | .attrs = vmcmd_attrs, | ||
1050 | }; | ||
1051 | |||
1052 | static struct kset *vmcmd_kset; | ||
1053 | |||
1054 | static void vmcmd_run(struct shutdown_trigger *trigger) | ||
1055 | { | ||
1056 | char *cmd, *next_cmd; | ||
1057 | |||
1058 | if (strcmp(trigger->name, ON_REIPL_STR) == 0) | ||
1059 | cmd = vmcmd_on_reboot; | ||
1060 | else if (strcmp(trigger->name, ON_PANIC_STR) == 0) | ||
1061 | cmd = vmcmd_on_panic; | ||
1062 | else if (strcmp(trigger->name, ON_HALT_STR) == 0) | ||
1063 | cmd = vmcmd_on_halt; | ||
1064 | else if (strcmp(trigger->name, ON_POFF_STR) == 0) | ||
1065 | cmd = vmcmd_on_poff; | ||
1066 | else | ||
1067 | return; | ||
1068 | |||
1069 | if (strlen(cmd) == 0) | ||
1070 | return; | ||
1071 | do { | ||
1072 | next_cmd = strchr(cmd, '\n'); | ||
1073 | if (next_cmd) { | ||
1074 | next_cmd[0] = 0; | ||
1075 | next_cmd += 1; | ||
1076 | } | ||
1077 | __cpcmd(cmd, NULL, 0, NULL); | ||
1078 | cmd = next_cmd; | ||
1079 | } while (cmd != NULL); | ||
1080 | } | ||
1081 | |||
1082 | static int vmcmd_init(void) | ||
1069 | { | 1083 | { |
1070 | int rc; | 1084 | if (!MACHINE_IS_VM) |
1085 | return -ENOTSUPP; | ||
1086 | vmcmd_kset = kset_create_and_add("vmcmd", NULL, firmware_kobj); | ||
1087 | if (!vmcmd_kset) | ||
1088 | return -ENOMEM; | ||
1089 | return sysfs_create_group(&vmcmd_kset->kobj, &vmcmd_attr_group); | ||
1090 | } | ||
1071 | 1091 | ||
1072 | rc = firmware_register(&shutdown_actions_subsys); | 1092 | static struct shutdown_action vmcmd_action = {SHUTDOWN_ACTION_VMCMD_STR, |
1073 | if (rc) | 1093 | vmcmd_run, vmcmd_init}; |
1074 | return rc; | 1094 | |
1075 | rc = subsys_create_file(&shutdown_actions_subsys, &on_panic_attr); | 1095 | /* |
1076 | if (rc) { | 1096 | * stop shutdown action: Stop Linux on shutdown. |
1077 | firmware_unregister(&shutdown_actions_subsys); | 1097 | */ |
1078 | return rc; | 1098 | |
1099 | static void stop_run(struct shutdown_trigger *trigger) | ||
1100 | { | ||
1101 | if (strcmp(trigger->name, ON_PANIC_STR) == 0) | ||
1102 | disabled_wait((unsigned long) __builtin_return_address(0)); | ||
1103 | else { | ||
1104 | signal_processor(smp_processor_id(), sigp_stop); | ||
1105 | for (;;); | ||
1079 | } | 1106 | } |
1080 | atomic_notifier_chain_register(&panic_notifier_list, | ||
1081 | &shutdown_on_panic_nb); | ||
1082 | return 0; | ||
1083 | } | 1107 | } |
1084 | 1108 | ||
1085 | static int __init s390_ipl_init(void) | 1109 | static struct shutdown_action stop_action = {SHUTDOWN_ACTION_STOP_STR, |
1110 | stop_run, NULL}; | ||
1111 | |||
1112 | /* action list */ | ||
1113 | |||
1114 | static struct shutdown_action *shutdown_actions_list[] = { | ||
1115 | &ipl_action, &reipl_action, &dump_action, &vmcmd_action, &stop_action}; | ||
1116 | #define SHUTDOWN_ACTIONS_COUNT (sizeof(shutdown_actions_list) / sizeof(void *)) | ||
1117 | |||
1118 | /* | ||
1119 | * Trigger section | ||
1120 | */ | ||
1121 | |||
1122 | static struct kset *shutdown_actions_kset; | ||
1123 | |||
1124 | static int set_trigger(const char *buf, struct shutdown_trigger *trigger, | ||
1125 | size_t len) | ||
1086 | { | 1126 | { |
1087 | int rc; | 1127 | int i; |
1128 | for (i = 0; i < SHUTDOWN_ACTIONS_COUNT; i++) { | ||
1129 | if (!shutdown_actions_list[i]) | ||
1130 | continue; | ||
1131 | if (strncmp(buf, shutdown_actions_list[i]->name, | ||
1132 | strlen(shutdown_actions_list[i]->name)) == 0) { | ||
1133 | trigger->action = shutdown_actions_list[i]; | ||
1134 | return len; | ||
1135 | } | ||
1136 | } | ||
1137 | return -EINVAL; | ||
1138 | } | ||
1088 | 1139 | ||
1089 | sclp_get_ipl_info(&sclp_ipl_info); | 1140 | /* on reipl */ |
1141 | |||
1142 | static struct shutdown_trigger on_reboot_trigger = {ON_REIPL_STR, | ||
1143 | &reipl_action}; | ||
1144 | |||
1145 | static ssize_t on_reboot_show(struct kobject *kobj, | ||
1146 | struct kobj_attribute *attr, char *page) | ||
1147 | { | ||
1148 | return sprintf(page, "%s\n", on_reboot_trigger.action->name); | ||
1149 | } | ||
1150 | |||
1151 | static ssize_t on_reboot_store(struct kobject *kobj, | ||
1152 | struct kobj_attribute *attr, | ||
1153 | const char *buf, size_t len) | ||
1154 | { | ||
1155 | return set_trigger(buf, &on_reboot_trigger, len); | ||
1156 | } | ||
1157 | |||
1158 | static struct kobj_attribute on_reboot_attr = | ||
1159 | __ATTR(on_reboot, 0644, on_reboot_show, on_reboot_store); | ||
1160 | |||
1161 | static void do_machine_restart(char *__unused) | ||
1162 | { | ||
1163 | smp_send_stop(); | ||
1164 | on_reboot_trigger.action->fn(&on_reboot_trigger); | ||
1165 | reipl_run(NULL); | ||
1166 | } | ||
1167 | void (*_machine_restart)(char *command) = do_machine_restart; | ||
1168 | |||
1169 | /* on panic */ | ||
1170 | |||
1171 | static struct shutdown_trigger on_panic_trigger = {ON_PANIC_STR, &stop_action}; | ||
1172 | |||
1173 | static ssize_t on_panic_show(struct kobject *kobj, | ||
1174 | struct kobj_attribute *attr, char *page) | ||
1175 | { | ||
1176 | return sprintf(page, "%s\n", on_panic_trigger.action->name); | ||
1177 | } | ||
1178 | |||
1179 | static ssize_t on_panic_store(struct kobject *kobj, | ||
1180 | struct kobj_attribute *attr, | ||
1181 | const char *buf, size_t len) | ||
1182 | { | ||
1183 | return set_trigger(buf, &on_panic_trigger, len); | ||
1184 | } | ||
1185 | |||
1186 | static struct kobj_attribute on_panic_attr = | ||
1187 | __ATTR(on_panic, 0644, on_panic_show, on_panic_store); | ||
1188 | |||
1189 | static void do_panic(void) | ||
1190 | { | ||
1191 | on_panic_trigger.action->fn(&on_panic_trigger); | ||
1192 | stop_run(&on_panic_trigger); | ||
1193 | } | ||
1194 | |||
1195 | /* on halt */ | ||
1196 | |||
1197 | static struct shutdown_trigger on_halt_trigger = {ON_HALT_STR, &stop_action}; | ||
1198 | |||
1199 | static ssize_t on_halt_show(struct kobject *kobj, | ||
1200 | struct kobj_attribute *attr, char *page) | ||
1201 | { | ||
1202 | return sprintf(page, "%s\n", on_halt_trigger.action->name); | ||
1203 | } | ||
1204 | |||
1205 | static ssize_t on_halt_store(struct kobject *kobj, | ||
1206 | struct kobj_attribute *attr, | ||
1207 | const char *buf, size_t len) | ||
1208 | { | ||
1209 | return set_trigger(buf, &on_halt_trigger, len); | ||
1210 | } | ||
1211 | |||
1212 | static struct kobj_attribute on_halt_attr = | ||
1213 | __ATTR(on_halt, 0644, on_halt_show, on_halt_store); | ||
1214 | |||
1215 | |||
1216 | static void do_machine_halt(void) | ||
1217 | { | ||
1218 | smp_send_stop(); | ||
1219 | on_halt_trigger.action->fn(&on_halt_trigger); | ||
1220 | stop_run(&on_halt_trigger); | ||
1221 | } | ||
1222 | void (*_machine_halt)(void) = do_machine_halt; | ||
1223 | |||
1224 | /* on power off */ | ||
1225 | |||
1226 | static struct shutdown_trigger on_poff_trigger = {ON_POFF_STR, &stop_action}; | ||
1227 | |||
1228 | static ssize_t on_poff_show(struct kobject *kobj, | ||
1229 | struct kobj_attribute *attr, char *page) | ||
1230 | { | ||
1231 | return sprintf(page, "%s\n", on_poff_trigger.action->name); | ||
1232 | } | ||
1233 | |||
1234 | static ssize_t on_poff_store(struct kobject *kobj, | ||
1235 | struct kobj_attribute *attr, | ||
1236 | const char *buf, size_t len) | ||
1237 | { | ||
1238 | return set_trigger(buf, &on_poff_trigger, len); | ||
1239 | } | ||
1240 | |||
1241 | static struct kobj_attribute on_poff_attr = | ||
1242 | __ATTR(on_poff, 0644, on_poff_show, on_poff_store); | ||
1243 | |||
1244 | |||
1245 | static void do_machine_power_off(void) | ||
1246 | { | ||
1247 | smp_send_stop(); | ||
1248 | on_poff_trigger.action->fn(&on_poff_trigger); | ||
1249 | stop_run(&on_poff_trigger); | ||
1250 | } | ||
1251 | void (*_machine_power_off)(void) = do_machine_power_off; | ||
1252 | |||
1253 | static void __init shutdown_triggers_init(void) | ||
1254 | { | ||
1255 | shutdown_actions_kset = kset_create_and_add("shutdown_actions", NULL, | ||
1256 | firmware_kobj); | ||
1257 | if (!shutdown_actions_kset) | ||
1258 | goto fail; | ||
1259 | if (sysfs_create_file(&shutdown_actions_kset->kobj, | ||
1260 | &on_reboot_attr.attr)) | ||
1261 | goto fail; | ||
1262 | if (sysfs_create_file(&shutdown_actions_kset->kobj, | ||
1263 | &on_panic_attr.attr)) | ||
1264 | goto fail; | ||
1265 | if (sysfs_create_file(&shutdown_actions_kset->kobj, | ||
1266 | &on_halt_attr.attr)) | ||
1267 | goto fail; | ||
1268 | if (sysfs_create_file(&shutdown_actions_kset->kobj, | ||
1269 | &on_poff_attr.attr)) | ||
1270 | goto fail; | ||
1271 | |||
1272 | return; | ||
1273 | fail: | ||
1274 | panic("shutdown_triggers_init failed\n"); | ||
1275 | } | ||
1276 | |||
1277 | static void __init shutdown_actions_init(void) | ||
1278 | { | ||
1279 | int i; | ||
1280 | |||
1281 | for (i = 0; i < SHUTDOWN_ACTIONS_COUNT; i++) { | ||
1282 | if (!shutdown_actions_list[i]->init) | ||
1283 | continue; | ||
1284 | if (shutdown_actions_list[i]->init()) | ||
1285 | shutdown_actions_list[i] = NULL; | ||
1286 | } | ||
1287 | } | ||
1288 | |||
1289 | static int __init s390_ipl_init(void) | ||
1290 | { | ||
1090 | reipl_probe(); | 1291 | reipl_probe(); |
1091 | rc = ipl_init(); | 1292 | sclp_get_ipl_info(&sclp_ipl_info); |
1092 | if (rc) | 1293 | shutdown_actions_init(); |
1093 | return rc; | 1294 | shutdown_triggers_init(); |
1094 | rc = reipl_init(); | ||
1095 | if (rc) | ||
1096 | return rc; | ||
1097 | rc = dump_init(); | ||
1098 | if (rc) | ||
1099 | return rc; | ||
1100 | rc = shutdown_actions_init(); | ||
1101 | if (rc) | ||
1102 | return rc; | ||
1103 | return 0; | 1295 | return 0; |
1104 | } | 1296 | } |
1105 | 1297 | ||
1106 | __initcall(s390_ipl_init); | 1298 | __initcall(s390_ipl_init); |
1107 | 1299 | ||
1300 | static void __init strncpy_skip_quote(char *dst, char *src, int n) | ||
1301 | { | ||
1302 | int sx, dx; | ||
1303 | |||
1304 | dx = 0; | ||
1305 | for (sx = 0; src[sx] != 0; sx++) { | ||
1306 | if (src[sx] == '"') | ||
1307 | continue; | ||
1308 | dst[dx++] = src[sx]; | ||
1309 | if (dx >= n) | ||
1310 | break; | ||
1311 | } | ||
1312 | } | ||
1313 | |||
1314 | static int __init vmcmd_on_reboot_setup(char *str) | ||
1315 | { | ||
1316 | if (!MACHINE_IS_VM) | ||
1317 | return 1; | ||
1318 | strncpy_skip_quote(vmcmd_on_reboot, str, 127); | ||
1319 | vmcmd_on_reboot[127] = 0; | ||
1320 | on_reboot_trigger.action = &vmcmd_action; | ||
1321 | return 1; | ||
1322 | } | ||
1323 | __setup("vmreboot=", vmcmd_on_reboot_setup); | ||
1324 | |||
1325 | static int __init vmcmd_on_panic_setup(char *str) | ||
1326 | { | ||
1327 | if (!MACHINE_IS_VM) | ||
1328 | return 1; | ||
1329 | strncpy_skip_quote(vmcmd_on_panic, str, 127); | ||
1330 | vmcmd_on_panic[127] = 0; | ||
1331 | on_panic_trigger.action = &vmcmd_action; | ||
1332 | return 1; | ||
1333 | } | ||
1334 | __setup("vmpanic=", vmcmd_on_panic_setup); | ||
1335 | |||
1336 | static int __init vmcmd_on_halt_setup(char *str) | ||
1337 | { | ||
1338 | if (!MACHINE_IS_VM) | ||
1339 | return 1; | ||
1340 | strncpy_skip_quote(vmcmd_on_halt, str, 127); | ||
1341 | vmcmd_on_halt[127] = 0; | ||
1342 | on_halt_trigger.action = &vmcmd_action; | ||
1343 | return 1; | ||
1344 | } | ||
1345 | __setup("vmhalt=", vmcmd_on_halt_setup); | ||
1346 | |||
1347 | static int __init vmcmd_on_poff_setup(char *str) | ||
1348 | { | ||
1349 | if (!MACHINE_IS_VM) | ||
1350 | return 1; | ||
1351 | strncpy_skip_quote(vmcmd_on_poff, str, 127); | ||
1352 | vmcmd_on_poff[127] = 0; | ||
1353 | on_poff_trigger.action = &vmcmd_action; | ||
1354 | return 1; | ||
1355 | } | ||
1356 | __setup("vmpoff=", vmcmd_on_poff_setup); | ||
1357 | |||
1358 | static int on_panic_notify(struct notifier_block *self, | ||
1359 | unsigned long event, void *data) | ||
1360 | { | ||
1361 | do_panic(); | ||
1362 | return NOTIFY_OK; | ||
1363 | } | ||
1364 | |||
1365 | static struct notifier_block on_panic_nb = { | ||
1366 | .notifier_call = on_panic_notify, | ||
1367 | .priority = 0, | ||
1368 | }; | ||
1369 | |||
1370 | void __init setup_ipl(void) | ||
1371 | { | ||
1372 | ipl_info.type = get_ipl_type(); | ||
1373 | switch (ipl_info.type) { | ||
1374 | case IPL_TYPE_CCW: | ||
1375 | ipl_info.data.ccw.dev_id.devno = ipl_devno; | ||
1376 | ipl_info.data.ccw.dev_id.ssid = 0; | ||
1377 | break; | ||
1378 | case IPL_TYPE_FCP: | ||
1379 | case IPL_TYPE_FCP_DUMP: | ||
1380 | ipl_info.data.fcp.dev_id.devno = | ||
1381 | IPL_PARMBLOCK_START->ipl_info.fcp.devno; | ||
1382 | ipl_info.data.fcp.dev_id.ssid = 0; | ||
1383 | ipl_info.data.fcp.wwpn = IPL_PARMBLOCK_START->ipl_info.fcp.wwpn; | ||
1384 | ipl_info.data.fcp.lun = IPL_PARMBLOCK_START->ipl_info.fcp.lun; | ||
1385 | break; | ||
1386 | case IPL_TYPE_NSS: | ||
1387 | strncpy(ipl_info.data.nss.name, kernel_nss_name, | ||
1388 | sizeof(ipl_info.data.nss.name)); | ||
1389 | break; | ||
1390 | case IPL_TYPE_UNKNOWN: | ||
1391 | default: | ||
1392 | /* We have no info to copy */ | ||
1393 | break; | ||
1394 | } | ||
1395 | atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb); | ||
1396 | } | ||
1397 | |||
1108 | void __init ipl_save_parameters(void) | 1398 | void __init ipl_save_parameters(void) |
1109 | { | 1399 | { |
1110 | struct cio_iplinfo iplinfo; | 1400 | struct cio_iplinfo iplinfo; |
@@ -1185,3 +1475,4 @@ void s390_reset_system(void) | |||
1185 | 1475 | ||
1186 | do_reset_calls(); | 1476 | do_reset_calls(); |
1187 | } | 1477 | } |
1478 | |||