diff options
Diffstat (limited to 'arch/s390/kernel/ipl.c')
-rw-r--r-- | arch/s390/kernel/ipl.c | 948 |
1 files changed, 611 insertions, 337 deletions
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index b97694fa62ec..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) |
@@ -205,8 +212,8 @@ static ssize_t sys_##_prefix##_##_name##_store(struct kobject *kobj, \ | |||
205 | struct kobj_attribute *attr, \ | 212 | struct kobj_attribute *attr, \ |
206 | const char *buf, size_t len) \ | 213 | const char *buf, size_t len) \ |
207 | { \ | 214 | { \ |
208 | if (sscanf(buf, _fmt_in, _value) != 1) \ | 215 | strncpy(_value, buf, sizeof(_value) - 1); \ |
209 | return -EINVAL; \ | 216 | strstrip(_value); \ |
210 | return len; \ | 217 | return len; \ |
211 | } \ | 218 | } \ |
212 | static struct kobj_attribute sys_##_prefix##_##_name##_attr = \ | 219 | static struct kobj_attribute sys_##_prefix##_##_name##_attr = \ |
@@ -245,33 +252,6 @@ static __init enum ipl_type get_ipl_type(void) | |||
245 | return IPL_TYPE_FCP; | 252 | return IPL_TYPE_FCP; |
246 | } | 253 | } |
247 | 254 | ||
248 | void __init setup_ipl_info(void) | ||
249 | { | ||
250 | ipl_info.type = get_ipl_type(); | ||
251 | switch (ipl_info.type) { | ||
252 | case IPL_TYPE_CCW: | ||
253 | ipl_info.data.ccw.dev_id.devno = ipl_devno; | ||
254 | ipl_info.data.ccw.dev_id.ssid = 0; | ||
255 | break; | ||
256 | case IPL_TYPE_FCP: | ||
257 | case IPL_TYPE_FCP_DUMP: | ||
258 | ipl_info.data.fcp.dev_id.devno = | ||
259 | IPL_PARMBLOCK_START->ipl_info.fcp.devno; | ||
260 | ipl_info.data.fcp.dev_id.ssid = 0; | ||
261 | ipl_info.data.fcp.wwpn = IPL_PARMBLOCK_START->ipl_info.fcp.wwpn; | ||
262 | ipl_info.data.fcp.lun = IPL_PARMBLOCK_START->ipl_info.fcp.lun; | ||
263 | break; | ||
264 | case IPL_TYPE_NSS: | ||
265 | strncpy(ipl_info.data.nss.name, kernel_nss_name, | ||
266 | sizeof(ipl_info.data.nss.name)); | ||
267 | break; | ||
268 | case IPL_TYPE_UNKNOWN: | ||
269 | default: | ||
270 | /* We have no info to copy */ | ||
271 | break; | ||
272 | } | ||
273 | } | ||
274 | |||
275 | struct ipl_info ipl_info; | 255 | struct ipl_info ipl_info; |
276 | EXPORT_SYMBOL_GPL(ipl_info); | 256 | EXPORT_SYMBOL_GPL(ipl_info); |
277 | 257 | ||
@@ -428,8 +408,74 @@ static struct attribute_group ipl_unknown_attr_group = { | |||
428 | 408 | ||
429 | static struct kset *ipl_kset; | 409 | static struct kset *ipl_kset; |
430 | 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}; | ||
476 | |||
431 | /* | 477 | /* |
432 | * reipl section | 478 | * reipl shutdown action: Reboot Linux on shutdown. |
433 | */ | 479 | */ |
434 | 480 | ||
435 | /* FCP reipl device attributes */ | 481 | /* FCP reipl device attributes */ |
@@ -549,7 +595,9 @@ static int reipl_set_type(enum ipl_type type) | |||
549 | 595 | ||
550 | switch(type) { | 596 | switch(type) { |
551 | case IPL_TYPE_CCW: | 597 | case IPL_TYPE_CCW: |
552 | if (MACHINE_IS_VM) | 598 | if (diag308_set_works) |
599 | reipl_method = REIPL_METHOD_CCW_DIAG; | ||
600 | else if (MACHINE_IS_VM) | ||
553 | reipl_method = REIPL_METHOD_CCW_VM; | 601 | reipl_method = REIPL_METHOD_CCW_VM; |
554 | else | 602 | else |
555 | reipl_method = REIPL_METHOD_CCW_CIO; | 603 | reipl_method = REIPL_METHOD_CCW_CIO; |
@@ -600,143 +648,11 @@ static ssize_t reipl_type_store(struct kobject *kobj, | |||
600 | } | 648 | } |
601 | 649 | ||
602 | static struct kobj_attribute reipl_type_attr = | 650 | static struct kobj_attribute reipl_type_attr = |
603 | __ATTR(reipl_type, 0644, reipl_type_show, reipl_type_store); | 651 | __ATTR(reipl_type, 0644, reipl_type_show, reipl_type_store); |
604 | 652 | ||
605 | static struct kset *reipl_kset; | 653 | static struct kset *reipl_kset; |
606 | 654 | ||
607 | /* | 655 | void reipl_run(struct shutdown_trigger *trigger) |
608 | * dump section | ||
609 | */ | ||
610 | |||
611 | /* FCP dump device attributes */ | ||
612 | |||
613 | DEFINE_IPL_ATTR_RW(dump_fcp, wwpn, "0x%016llx\n", "%016llx\n", | ||
614 | dump_block_fcp->ipl_info.fcp.wwpn); | ||
615 | DEFINE_IPL_ATTR_RW(dump_fcp, lun, "0x%016llx\n", "%016llx\n", | ||
616 | dump_block_fcp->ipl_info.fcp.lun); | ||
617 | DEFINE_IPL_ATTR_RW(dump_fcp, bootprog, "%lld\n", "%lld\n", | ||
618 | dump_block_fcp->ipl_info.fcp.bootprog); | ||
619 | DEFINE_IPL_ATTR_RW(dump_fcp, br_lba, "%lld\n", "%lld\n", | ||
620 | dump_block_fcp->ipl_info.fcp.br_lba); | ||
621 | DEFINE_IPL_ATTR_RW(dump_fcp, device, "0.0.%04llx\n", "0.0.%llx\n", | ||
622 | dump_block_fcp->ipl_info.fcp.devno); | ||
623 | |||
624 | static struct attribute *dump_fcp_attrs[] = { | ||
625 | &sys_dump_fcp_device_attr.attr, | ||
626 | &sys_dump_fcp_wwpn_attr.attr, | ||
627 | &sys_dump_fcp_lun_attr.attr, | ||
628 | &sys_dump_fcp_bootprog_attr.attr, | ||
629 | &sys_dump_fcp_br_lba_attr.attr, | ||
630 | NULL, | ||
631 | }; | ||
632 | |||
633 | static struct attribute_group dump_fcp_attr_group = { | ||
634 | .name = IPL_FCP_STR, | ||
635 | .attrs = dump_fcp_attrs, | ||
636 | }; | ||
637 | |||
638 | /* CCW dump device attributes */ | ||
639 | |||
640 | DEFINE_IPL_ATTR_RW(dump_ccw, device, "0.0.%04llx\n", "0.0.%llx\n", | ||
641 | dump_block_ccw->ipl_info.ccw.devno); | ||
642 | |||
643 | static struct attribute *dump_ccw_attrs[] = { | ||
644 | &sys_dump_ccw_device_attr.attr, | ||
645 | NULL, | ||
646 | }; | ||
647 | |||
648 | static struct attribute_group dump_ccw_attr_group = { | ||
649 | .name = IPL_CCW_STR, | ||
650 | .attrs = dump_ccw_attrs, | ||
651 | }; | ||
652 | |||
653 | /* dump type */ | ||
654 | |||
655 | static int dump_set_type(enum dump_type type) | ||
656 | { | ||
657 | if (!(dump_capabilities & type)) | ||
658 | return -EINVAL; | ||
659 | switch(type) { | ||
660 | case DUMP_TYPE_CCW: | ||
661 | if (MACHINE_IS_VM) | ||
662 | dump_method = DUMP_METHOD_CCW_VM; | ||
663 | else if (diag308_set_works) | ||
664 | dump_method = DUMP_METHOD_CCW_DIAG; | ||
665 | else | ||
666 | dump_method = DUMP_METHOD_CCW_CIO; | ||
667 | break; | ||
668 | case DUMP_TYPE_FCP: | ||
669 | dump_method = DUMP_METHOD_FCP_DIAG; | ||
670 | break; | ||
671 | default: | ||
672 | dump_method = DUMP_METHOD_NONE; | ||
673 | } | ||
674 | dump_type = type; | ||
675 | return 0; | ||
676 | } | ||
677 | |||
678 | static ssize_t dump_type_show(struct kobject *kobj, | ||
679 | struct kobj_attribute *attr, char *page) | ||
680 | { | ||
681 | return sprintf(page, "%s\n", dump_type_str(dump_type)); | ||
682 | } | ||
683 | |||
684 | static ssize_t dump_type_store(struct kobject *kobj, | ||
685 | struct kobj_attribute *attr, | ||
686 | const char *buf, size_t len) | ||
687 | { | ||
688 | int rc = -EINVAL; | ||
689 | |||
690 | if (strncmp(buf, DUMP_NONE_STR, strlen(DUMP_NONE_STR)) == 0) | ||
691 | rc = dump_set_type(DUMP_TYPE_NONE); | ||
692 | else if (strncmp(buf, DUMP_CCW_STR, strlen(DUMP_CCW_STR)) == 0) | ||
693 | rc = dump_set_type(DUMP_TYPE_CCW); | ||
694 | else if (strncmp(buf, DUMP_FCP_STR, strlen(DUMP_FCP_STR)) == 0) | ||
695 | rc = dump_set_type(DUMP_TYPE_FCP); | ||
696 | return (rc != 0) ? rc : len; | ||
697 | } | ||
698 | |||
699 | static struct kobj_attribute dump_type_attr = | ||
700 | __ATTR(dump_type, 0644, dump_type_show, dump_type_store); | ||
701 | |||
702 | static struct kset *dump_kset; | ||
703 | |||
704 | /* | ||
705 | * Shutdown actions section | ||
706 | */ | ||
707 | |||
708 | static struct kset *shutdown_actions_kset; | ||
709 | |||
710 | /* on panic */ | ||
711 | |||
712 | static ssize_t on_panic_show(struct kobject *kobj, | ||
713 | struct kobj_attribute *attr, char *page) | ||
714 | { | ||
715 | return sprintf(page, "%s\n", shutdown_action_str(on_panic_action)); | ||
716 | } | ||
717 | |||
718 | static ssize_t on_panic_store(struct kobject *kobj, | ||
719 | struct kobj_attribute *attr, | ||
720 | const char *buf, size_t len) | ||
721 | { | ||
722 | if (strncmp(buf, SHUTDOWN_REIPL_STR, strlen(SHUTDOWN_REIPL_STR)) == 0) | ||
723 | on_panic_action = SHUTDOWN_REIPL; | ||
724 | else if (strncmp(buf, SHUTDOWN_DUMP_STR, | ||
725 | strlen(SHUTDOWN_DUMP_STR)) == 0) | ||
726 | on_panic_action = SHUTDOWN_DUMP; | ||
727 | else if (strncmp(buf, SHUTDOWN_STOP_STR, | ||
728 | strlen(SHUTDOWN_STOP_STR)) == 0) | ||
729 | on_panic_action = SHUTDOWN_STOP; | ||
730 | else | ||
731 | return -EINVAL; | ||
732 | |||
733 | return len; | ||
734 | } | ||
735 | |||
736 | static struct kobj_attribute on_panic_attr = | ||
737 | __ATTR(on_panic, 0644, on_panic_show, on_panic_store); | ||
738 | |||
739 | void do_reipl(void) | ||
740 | { | 656 | { |
741 | struct ccw_dev_id devid; | 657 | struct ccw_dev_id devid; |
742 | static char buf[100]; | 658 | static char buf[100]; |
@@ -745,8 +661,6 @@ void do_reipl(void) | |||
745 | switch (reipl_method) { | 661 | switch (reipl_method) { |
746 | case REIPL_METHOD_CCW_CIO: | 662 | case REIPL_METHOD_CCW_CIO: |
747 | devid.devno = reipl_block_ccw->ipl_info.ccw.devno; | 663 | devid.devno = reipl_block_ccw->ipl_info.ccw.devno; |
748 | if (ipl_info.type == IPL_TYPE_CCW && devid.devno == ipl_devno) | ||
749 | diag308(DIAG308_IPL, NULL); | ||
750 | devid.ssid = 0; | 664 | devid.ssid = 0; |
751 | reipl_ccw_dev(&devid); | 665 | reipl_ccw_dev(&devid); |
752 | break; | 666 | break; |
@@ -787,98 +701,6 @@ void do_reipl(void) | |||
787 | default: | 701 | default: |
788 | break; | 702 | break; |
789 | } | 703 | } |
790 | signal_processor(smp_processor_id(), sigp_stop_and_store_status); | ||
791 | } | ||
792 | |||
793 | static void do_dump(void) | ||
794 | { | ||
795 | struct ccw_dev_id devid; | ||
796 | static char buf[100]; | ||
797 | |||
798 | switch (dump_method) { | ||
799 | case DUMP_METHOD_CCW_CIO: | ||
800 | smp_send_stop(); | ||
801 | devid.devno = dump_block_ccw->ipl_info.ccw.devno; | ||
802 | devid.ssid = 0; | ||
803 | reipl_ccw_dev(&devid); | ||
804 | break; | ||
805 | case DUMP_METHOD_CCW_VM: | ||
806 | smp_send_stop(); | ||
807 | sprintf(buf, "STORE STATUS"); | ||
808 | __cpcmd(buf, NULL, 0, NULL); | ||
809 | sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno); | ||
810 | __cpcmd(buf, NULL, 0, NULL); | ||
811 | break; | ||
812 | case DUMP_METHOD_CCW_DIAG: | ||
813 | diag308(DIAG308_SET, dump_block_ccw); | ||
814 | diag308(DIAG308_DUMP, NULL); | ||
815 | break; | ||
816 | case DUMP_METHOD_FCP_DIAG: | ||
817 | diag308(DIAG308_SET, dump_block_fcp); | ||
818 | diag308(DIAG308_DUMP, NULL); | ||
819 | break; | ||
820 | case DUMP_METHOD_NONE: | ||
821 | default: | ||
822 | return; | ||
823 | } | ||
824 | printk(KERN_EMERG "Dump failed!\n"); | ||
825 | } | ||
826 | |||
827 | /* init functions */ | ||
828 | |||
829 | static int __init ipl_register_fcp_files(void) | ||
830 | { | ||
831 | int rc; | ||
832 | |||
833 | rc = sysfs_create_group(&ipl_kset->kobj, | ||
834 | &ipl_fcp_attr_group); | ||
835 | if (rc) | ||
836 | goto out; | ||
837 | rc = sysfs_create_bin_file(&ipl_kset->kobj, | ||
838 | &ipl_parameter_attr); | ||
839 | if (rc) | ||
840 | goto out_ipl_parm; | ||
841 | rc = sysfs_create_bin_file(&ipl_kset->kobj, | ||
842 | &ipl_scp_data_attr); | ||
843 | if (!rc) | ||
844 | goto out; | ||
845 | |||
846 | sysfs_remove_bin_file(&ipl_kset->kobj, &ipl_parameter_attr); | ||
847 | |||
848 | out_ipl_parm: | ||
849 | sysfs_remove_group(&ipl_kset->kobj, &ipl_fcp_attr_group); | ||
850 | out: | ||
851 | return rc; | ||
852 | } | ||
853 | |||
854 | static int __init ipl_init(void) | ||
855 | { | ||
856 | int rc; | ||
857 | |||
858 | ipl_kset = kset_create_and_add("ipl", NULL, firmware_kobj); | ||
859 | if (!ipl_kset) | ||
860 | return -ENOMEM; | ||
861 | switch (ipl_info.type) { | ||
862 | case IPL_TYPE_CCW: | ||
863 | rc = sysfs_create_group(&ipl_kset->kobj, | ||
864 | &ipl_ccw_attr_group); | ||
865 | break; | ||
866 | case IPL_TYPE_FCP: | ||
867 | case IPL_TYPE_FCP_DUMP: | ||
868 | rc = ipl_register_fcp_files(); | ||
869 | break; | ||
870 | case IPL_TYPE_NSS: | ||
871 | rc = sysfs_create_group(&ipl_kset->kobj, | ||
872 | &ipl_nss_attr_group); | ||
873 | break; | ||
874 | default: | ||
875 | rc = sysfs_create_group(&ipl_kset->kobj, | ||
876 | &ipl_unknown_attr_group); | ||
877 | break; | ||
878 | } | ||
879 | if (rc) | ||
880 | kset_unregister(ipl_kset); | ||
881 | return rc; | ||
882 | } | 704 | } |
883 | 705 | ||
884 | static void __init reipl_probe(void) | 706 | static void __init reipl_probe(void) |
@@ -923,6 +745,7 @@ static int __init reipl_ccw_init(void) | |||
923 | reipl_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION; | 745 | reipl_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION; |
924 | reipl_block_ccw->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN; | 746 | reipl_block_ccw->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN; |
925 | 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; | ||
926 | /* check if read scp info worked and set loadparm */ | 749 | /* check if read scp info worked and set loadparm */ |
927 | if (sclp_ipl_info.is_valid) | 750 | if (sclp_ipl_info.is_valid) |
928 | memcpy(reipl_block_ccw->ipl_info.ccw.load_param, | 751 | memcpy(reipl_block_ccw->ipl_info.ccw.load_param, |
@@ -931,8 +754,7 @@ static int __init reipl_ccw_init(void) | |||
931 | /* read scp info failed: set empty loadparm (EBCDIC blanks) */ | 754 | /* read scp info failed: set empty loadparm (EBCDIC blanks) */ |
932 | memset(reipl_block_ccw->ipl_info.ccw.load_param, 0x40, | 755 | memset(reipl_block_ccw->ipl_info.ccw.load_param, 0x40, |
933 | LOADPARM_LEN); | 756 | LOADPARM_LEN); |
934 | /* FIXME: check for diag308_set_works when enabling diag ccw reipl */ | 757 | if (!MACHINE_IS_VM && !diag308_set_works) |
935 | if (!MACHINE_IS_VM) | ||
936 | sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO; | 758 | sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO; |
937 | if (ipl_info.type == IPL_TYPE_CCW) | 759 | if (ipl_info.type == IPL_TYPE_CCW) |
938 | reipl_block_ccw->ipl_info.ccw.devno = ipl_devno; | 760 | reipl_block_ccw->ipl_info.ccw.devno = ipl_devno; |
@@ -970,7 +792,7 @@ static int __init reipl_fcp_init(void) | |||
970 | return 0; | 792 | return 0; |
971 | } | 793 | } |
972 | 794 | ||
973 | static int __init reipl_init(void) | 795 | static int reipl_init(void) |
974 | { | 796 | { |
975 | int rc; | 797 | int rc; |
976 | 798 | ||
@@ -997,6 +819,140 @@ static int __init reipl_init(void) | |||
997 | return 0; | 819 | return 0; |
998 | } | 820 | } |
999 | 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 | |||
1000 | static int __init dump_ccw_init(void) | 956 | static int __init dump_ccw_init(void) |
1001 | { | 957 | { |
1002 | int rc; | 958 | int rc; |
@@ -1042,31 +998,14 @@ static int __init dump_fcp_init(void) | |||
1042 | return 0; | 998 | return 0; |
1043 | } | 999 | } |
1044 | 1000 | ||
1045 | #define SHUTDOWN_ON_PANIC_PRIO 0 | 1001 | static int dump_init(void) |
1046 | |||
1047 | static int shutdown_on_panic_notify(struct notifier_block *self, | ||
1048 | unsigned long event, void *data) | ||
1049 | { | ||
1050 | if (on_panic_action == SHUTDOWN_DUMP) | ||
1051 | do_dump(); | ||
1052 | else if (on_panic_action == SHUTDOWN_REIPL) | ||
1053 | do_reipl(); | ||
1054 | return NOTIFY_OK; | ||
1055 | } | ||
1056 | |||
1057 | static struct notifier_block shutdown_on_panic_nb = { | ||
1058 | .notifier_call = shutdown_on_panic_notify, | ||
1059 | .priority = SHUTDOWN_ON_PANIC_PRIO | ||
1060 | }; | ||
1061 | |||
1062 | static int __init dump_init(void) | ||
1063 | { | 1002 | { |
1064 | int rc; | 1003 | int rc; |
1065 | 1004 | ||
1066 | dump_kset = kset_create_and_add("dump", NULL, firmware_kobj); | 1005 | dump_kset = kset_create_and_add("dump", NULL, firmware_kobj); |
1067 | if (!dump_kset) | 1006 | if (!dump_kset) |
1068 | return -ENOMEM; | 1007 | return -ENOMEM; |
1069 | rc = sysfs_create_file(&dump_kset->kobj, &dump_type_attr); | 1008 | rc = sysfs_create_file(&dump_kset->kobj, &dump_type_attr.attr); |
1070 | if (rc) { | 1009 | if (rc) { |
1071 | kset_unregister(dump_kset); | 1010 | kset_unregister(dump_kset); |
1072 | return rc; | 1011 | return rc; |
@@ -1081,47 +1020,381 @@ static int __init dump_init(void) | |||
1081 | return 0; | 1020 | return 0; |
1082 | } | 1021 | } |
1083 | 1022 | ||
1084 | 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) | ||
1085 | { | 1083 | { |
1086 | 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 | } | ||
1091 | |||
1092 | static struct shutdown_action vmcmd_action = {SHUTDOWN_ACTION_VMCMD_STR, | ||
1093 | vmcmd_run, vmcmd_init}; | ||
1094 | |||
1095 | /* | ||
1096 | * stop shutdown action: Stop Linux on shutdown. | ||
1097 | */ | ||
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 (;;); | ||
1106 | } | ||
1107 | } | ||
1108 | |||
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) | ||
1126 | { | ||
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 | } | ||
1139 | |||
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 | } | ||
1087 | 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 | { | ||
1088 | shutdown_actions_kset = kset_create_and_add("shutdown_actions", NULL, | 1255 | shutdown_actions_kset = kset_create_and_add("shutdown_actions", NULL, |
1089 | firmware_kobj); | 1256 | firmware_kobj); |
1090 | if (!shutdown_actions_kset) | 1257 | if (!shutdown_actions_kset) |
1091 | return -ENOMEM; | 1258 | goto fail; |
1092 | rc = sysfs_create_file(&shutdown_actions_kset->kobj, &on_panic_attr); | 1259 | if (sysfs_create_file(&shutdown_actions_kset->kobj, |
1093 | if (rc) { | 1260 | &on_reboot_attr.attr)) |
1094 | kset_unregister(shutdown_actions_kset); | 1261 | goto fail; |
1095 | return rc; | 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; | ||
1096 | } | 1286 | } |
1097 | atomic_notifier_chain_register(&panic_notifier_list, | ||
1098 | &shutdown_on_panic_nb); | ||
1099 | return 0; | ||
1100 | } | 1287 | } |
1101 | 1288 | ||
1102 | static int __init s390_ipl_init(void) | 1289 | static int __init s390_ipl_init(void) |
1103 | { | 1290 | { |
1104 | int rc; | ||
1105 | |||
1106 | sclp_get_ipl_info(&sclp_ipl_info); | ||
1107 | reipl_probe(); | 1291 | reipl_probe(); |
1108 | rc = ipl_init(); | 1292 | sclp_get_ipl_info(&sclp_ipl_info); |
1109 | if (rc) | 1293 | shutdown_actions_init(); |
1110 | return rc; | 1294 | shutdown_triggers_init(); |
1111 | rc = reipl_init(); | ||
1112 | if (rc) | ||
1113 | return rc; | ||
1114 | rc = dump_init(); | ||
1115 | if (rc) | ||
1116 | return rc; | ||
1117 | rc = shutdown_actions_init(); | ||
1118 | if (rc) | ||
1119 | return rc; | ||
1120 | return 0; | 1295 | return 0; |
1121 | } | 1296 | } |
1122 | 1297 | ||
1123 | __initcall(s390_ipl_init); | 1298 | __initcall(s390_ipl_init); |
1124 | 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 | |||
1125 | void __init ipl_save_parameters(void) | 1398 | void __init ipl_save_parameters(void) |
1126 | { | 1399 | { |
1127 | struct cio_iplinfo iplinfo; | 1400 | struct cio_iplinfo iplinfo; |
@@ -1202,3 +1475,4 @@ void s390_reset_system(void) | |||
1202 | 1475 | ||
1203 | do_reset_calls(); | 1476 | do_reset_calls(); |
1204 | } | 1477 | } |
1478 | |||