diff options
Diffstat (limited to 'arch/s390/kernel/ipl.c')
-rw-r--r-- | arch/s390/kernel/ipl.c | 931 |
1 files changed, 599 insertions, 332 deletions
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index b97694fa62ec..d73aff63725f 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 */ |
@@ -600,143 +646,11 @@ static ssize_t reipl_type_store(struct kobject *kobj, | |||
600 | } | 646 | } |
601 | 647 | ||
602 | static struct kobj_attribute reipl_type_attr = | 648 | static struct kobj_attribute reipl_type_attr = |
603 | __ATTR(reipl_type, 0644, reipl_type_show, reipl_type_store); | 649 | __ATTR(reipl_type, 0644, reipl_type_show, reipl_type_store); |
604 | 650 | ||
605 | static struct kset *reipl_kset; | 651 | static struct kset *reipl_kset; |
606 | 652 | ||
607 | /* | 653 | 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 | { | 654 | { |
741 | struct ccw_dev_id devid; | 655 | struct ccw_dev_id devid; |
742 | static char buf[100]; | 656 | static char buf[100]; |
@@ -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) |
@@ -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,138 @@ 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 (MACHINE_IS_VM) | ||
880 | dump_method = DUMP_METHOD_CCW_VM; | ||
881 | else | ||
882 | dump_method = DUMP_METHOD_CCW_CIO; | ||
883 | break; | ||
884 | case DUMP_TYPE_FCP: | ||
885 | dump_method = DUMP_METHOD_FCP_DIAG; | ||
886 | break; | ||
887 | default: | ||
888 | dump_method = DUMP_METHOD_NONE; | ||
889 | } | ||
890 | dump_type = type; | ||
891 | return 0; | ||
892 | } | ||
893 | |||
894 | static ssize_t dump_type_show(struct kobject *kobj, | ||
895 | struct kobj_attribute *attr, char *page) | ||
896 | { | ||
897 | return sprintf(page, "%s\n", dump_type_str(dump_type)); | ||
898 | } | ||
899 | |||
900 | static ssize_t dump_type_store(struct kobject *kobj, | ||
901 | struct kobj_attribute *attr, | ||
902 | const char *buf, size_t len) | ||
903 | { | ||
904 | int rc = -EINVAL; | ||
905 | |||
906 | if (strncmp(buf, DUMP_NONE_STR, strlen(DUMP_NONE_STR)) == 0) | ||
907 | rc = dump_set_type(DUMP_TYPE_NONE); | ||
908 | else if (strncmp(buf, DUMP_CCW_STR, strlen(DUMP_CCW_STR)) == 0) | ||
909 | rc = dump_set_type(DUMP_TYPE_CCW); | ||
910 | else if (strncmp(buf, DUMP_FCP_STR, strlen(DUMP_FCP_STR)) == 0) | ||
911 | rc = dump_set_type(DUMP_TYPE_FCP); | ||
912 | return (rc != 0) ? rc : len; | ||
913 | } | ||
914 | |||
915 | static struct kobj_attribute dump_type_attr = | ||
916 | __ATTR(dump_type, 0644, dump_type_show, dump_type_store); | ||
917 | |||
918 | static struct kset *dump_kset; | ||
919 | |||
920 | static void dump_run(struct shutdown_trigger *trigger) | ||
921 | { | ||
922 | struct ccw_dev_id devid; | ||
923 | static char buf[100]; | ||
924 | |||
925 | switch (dump_method) { | ||
926 | case DUMP_METHOD_CCW_CIO: | ||
927 | smp_send_stop(); | ||
928 | devid.devno = dump_block_ccw->ipl_info.ccw.devno; | ||
929 | devid.ssid = 0; | ||
930 | reipl_ccw_dev(&devid); | ||
931 | break; | ||
932 | case DUMP_METHOD_CCW_VM: | ||
933 | smp_send_stop(); | ||
934 | sprintf(buf, "STORE STATUS"); | ||
935 | __cpcmd(buf, NULL, 0, NULL); | ||
936 | sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno); | ||
937 | __cpcmd(buf, NULL, 0, NULL); | ||
938 | break; | ||
939 | case DUMP_METHOD_CCW_DIAG: | ||
940 | diag308(DIAG308_SET, dump_block_ccw); | ||
941 | diag308(DIAG308_DUMP, NULL); | ||
942 | break; | ||
943 | case DUMP_METHOD_FCP_DIAG: | ||
944 | diag308(DIAG308_SET, dump_block_fcp); | ||
945 | diag308(DIAG308_DUMP, NULL); | ||
946 | break; | ||
947 | case DUMP_METHOD_NONE: | ||
948 | default: | ||
949 | return; | ||
950 | } | ||
951 | printk(KERN_EMERG "Dump failed!\n"); | ||
952 | } | ||
953 | |||
1000 | static int __init dump_ccw_init(void) | 954 | static int __init dump_ccw_init(void) |
1001 | { | 955 | { |
1002 | int rc; | 956 | int rc; |
@@ -1042,31 +996,14 @@ static int __init dump_fcp_init(void) | |||
1042 | return 0; | 996 | return 0; |
1043 | } | 997 | } |
1044 | 998 | ||
1045 | #define SHUTDOWN_ON_PANIC_PRIO 0 | 999 | 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 | { | 1000 | { |
1064 | int rc; | 1001 | int rc; |
1065 | 1002 | ||
1066 | dump_kset = kset_create_and_add("dump", NULL, firmware_kobj); | 1003 | dump_kset = kset_create_and_add("dump", NULL, firmware_kobj); |
1067 | if (!dump_kset) | 1004 | if (!dump_kset) |
1068 | return -ENOMEM; | 1005 | return -ENOMEM; |
1069 | rc = sysfs_create_file(&dump_kset->kobj, &dump_type_attr); | 1006 | rc = sysfs_create_file(&dump_kset->kobj, &dump_type_attr.attr); |
1070 | if (rc) { | 1007 | if (rc) { |
1071 | kset_unregister(dump_kset); | 1008 | kset_unregister(dump_kset); |
1072 | return rc; | 1009 | return rc; |
@@ -1081,47 +1018,376 @@ static int __init dump_init(void) | |||
1081 | return 0; | 1018 | return 0; |
1082 | } | 1019 | } |
1083 | 1020 | ||
1084 | static int __init shutdown_actions_init(void) | 1021 | static struct shutdown_action dump_action = {SHUTDOWN_ACTION_DUMP_STR, |
1022 | dump_run, dump_init}; | ||
1023 | |||
1024 | /* | ||
1025 | * vmcmd shutdown action: Trigger vm command on shutdown. | ||
1026 | */ | ||
1027 | |||
1028 | static char vmcmd_on_reboot[128]; | ||
1029 | static char vmcmd_on_panic[128]; | ||
1030 | static char vmcmd_on_halt[128]; | ||
1031 | static char vmcmd_on_poff[128]; | ||
1032 | |||
1033 | DEFINE_IPL_ATTR_STR_RW(vmcmd, on_reboot, "%s\n", "%s\n", vmcmd_on_reboot); | ||
1034 | DEFINE_IPL_ATTR_STR_RW(vmcmd, on_panic, "%s\n", "%s\n", vmcmd_on_panic); | ||
1035 | DEFINE_IPL_ATTR_STR_RW(vmcmd, on_halt, "%s\n", "%s\n", vmcmd_on_halt); | ||
1036 | DEFINE_IPL_ATTR_STR_RW(vmcmd, on_poff, "%s\n", "%s\n", vmcmd_on_poff); | ||
1037 | |||
1038 | static struct attribute *vmcmd_attrs[] = { | ||
1039 | &sys_vmcmd_on_reboot_attr.attr, | ||
1040 | &sys_vmcmd_on_panic_attr.attr, | ||
1041 | &sys_vmcmd_on_halt_attr.attr, | ||
1042 | &sys_vmcmd_on_poff_attr.attr, | ||
1043 | NULL, | ||
1044 | }; | ||
1045 | |||
1046 | static struct attribute_group vmcmd_attr_group = { | ||
1047 | .attrs = vmcmd_attrs, | ||
1048 | }; | ||
1049 | |||
1050 | static struct kset *vmcmd_kset; | ||
1051 | |||
1052 | static void vmcmd_run(struct shutdown_trigger *trigger) | ||
1085 | { | 1053 | { |
1086 | int rc; | 1054 | char *cmd, *next_cmd; |
1055 | |||
1056 | if (strcmp(trigger->name, ON_REIPL_STR) == 0) | ||
1057 | cmd = vmcmd_on_reboot; | ||
1058 | else if (strcmp(trigger->name, ON_PANIC_STR) == 0) | ||
1059 | cmd = vmcmd_on_panic; | ||
1060 | else if (strcmp(trigger->name, ON_HALT_STR) == 0) | ||
1061 | cmd = vmcmd_on_halt; | ||
1062 | else if (strcmp(trigger->name, ON_POFF_STR) == 0) | ||
1063 | cmd = vmcmd_on_poff; | ||
1064 | else | ||
1065 | return; | ||
1066 | |||
1067 | if (strlen(cmd) == 0) | ||
1068 | return; | ||
1069 | do { | ||
1070 | next_cmd = strchr(cmd, '\n'); | ||
1071 | if (next_cmd) { | ||
1072 | next_cmd[0] = 0; | ||
1073 | next_cmd += 1; | ||
1074 | } | ||
1075 | __cpcmd(cmd, NULL, 0, NULL); | ||
1076 | cmd = next_cmd; | ||
1077 | } while (cmd != NULL); | ||
1078 | } | ||
1079 | |||
1080 | static int vmcmd_init(void) | ||
1081 | { | ||
1082 | if (!MACHINE_IS_VM) | ||
1083 | return -ENOTSUPP; | ||
1084 | vmcmd_kset = kset_create_and_add("vmcmd", NULL, firmware_kobj); | ||
1085 | if (!vmcmd_kset) | ||
1086 | return -ENOMEM; | ||
1087 | return sysfs_create_group(&vmcmd_kset->kobj, &vmcmd_attr_group); | ||
1088 | } | ||
1089 | |||
1090 | static struct shutdown_action vmcmd_action = {SHUTDOWN_ACTION_VMCMD_STR, | ||
1091 | vmcmd_run, vmcmd_init}; | ||
1092 | |||
1093 | /* | ||
1094 | * stop shutdown action: Stop Linux on shutdown. | ||
1095 | */ | ||
1096 | |||
1097 | static void stop_run(struct shutdown_trigger *trigger) | ||
1098 | { | ||
1099 | signal_processor(smp_processor_id(), sigp_stop_and_store_status); | ||
1100 | for (;;); | ||
1101 | } | ||
1102 | |||
1103 | static struct shutdown_action stop_action = {SHUTDOWN_ACTION_STOP_STR, | ||
1104 | stop_run, NULL}; | ||
1105 | |||
1106 | /* action list */ | ||
1107 | |||
1108 | static struct shutdown_action *shutdown_actions_list[] = { | ||
1109 | &ipl_action, &reipl_action, &dump_action, &vmcmd_action, &stop_action}; | ||
1110 | #define SHUTDOWN_ACTIONS_COUNT (sizeof(shutdown_actions_list) / sizeof(void *)) | ||
1111 | |||
1112 | /* | ||
1113 | * Trigger section | ||
1114 | */ | ||
1115 | |||
1116 | static struct kset *shutdown_actions_kset; | ||
1117 | |||
1118 | static int set_trigger(const char *buf, struct shutdown_trigger *trigger, | ||
1119 | size_t len) | ||
1120 | { | ||
1121 | int i; | ||
1122 | for (i = 0; i < SHUTDOWN_ACTIONS_COUNT; i++) { | ||
1123 | if (!shutdown_actions_list[i]) | ||
1124 | continue; | ||
1125 | if (strncmp(buf, shutdown_actions_list[i]->name, | ||
1126 | strlen(shutdown_actions_list[i]->name)) == 0) { | ||
1127 | trigger->action = shutdown_actions_list[i]; | ||
1128 | return len; | ||
1129 | } | ||
1130 | } | ||
1131 | return -EINVAL; | ||
1132 | } | ||
1133 | |||
1134 | /* on reipl */ | ||
1135 | |||
1136 | static struct shutdown_trigger on_reboot_trigger = {ON_REIPL_STR, | ||
1137 | &reipl_action}; | ||
1138 | |||
1139 | static ssize_t on_reboot_show(struct kobject *kobj, | ||
1140 | struct kobj_attribute *attr, char *page) | ||
1141 | { | ||
1142 | return sprintf(page, "%s\n", on_reboot_trigger.action->name); | ||
1143 | } | ||
1144 | |||
1145 | static ssize_t on_reboot_store(struct kobject *kobj, | ||
1146 | struct kobj_attribute *attr, | ||
1147 | const char *buf, size_t len) | ||
1148 | { | ||
1149 | return set_trigger(buf, &on_reboot_trigger, len); | ||
1150 | } | ||
1151 | |||
1152 | static struct kobj_attribute on_reboot_attr = | ||
1153 | __ATTR(on_reboot, 0644, on_reboot_show, on_reboot_store); | ||
1154 | |||
1155 | static void do_machine_restart(char *__unused) | ||
1156 | { | ||
1157 | smp_send_stop(); | ||
1158 | on_reboot_trigger.action->fn(&on_reboot_trigger); | ||
1159 | reipl_run(NULL); | ||
1160 | } | ||
1161 | void (*_machine_restart)(char *command) = do_machine_restart; | ||
1162 | |||
1163 | /* on panic */ | ||
1164 | |||
1165 | static struct shutdown_trigger on_panic_trigger = {ON_PANIC_STR, &stop_action}; | ||
1166 | |||
1167 | static ssize_t on_panic_show(struct kobject *kobj, | ||
1168 | struct kobj_attribute *attr, char *page) | ||
1169 | { | ||
1170 | return sprintf(page, "%s\n", on_panic_trigger.action->name); | ||
1171 | } | ||
1172 | |||
1173 | static ssize_t on_panic_store(struct kobject *kobj, | ||
1174 | struct kobj_attribute *attr, | ||
1175 | const char *buf, size_t len) | ||
1176 | { | ||
1177 | return set_trigger(buf, &on_panic_trigger, len); | ||
1178 | } | ||
1179 | |||
1180 | static struct kobj_attribute on_panic_attr = | ||
1181 | __ATTR(on_panic, 0644, on_panic_show, on_panic_store); | ||
1182 | |||
1183 | static void do_panic(void) | ||
1184 | { | ||
1185 | on_panic_trigger.action->fn(&on_panic_trigger); | ||
1186 | stop_run(&on_panic_trigger); | ||
1187 | } | ||
1188 | |||
1189 | /* on halt */ | ||
1190 | |||
1191 | static struct shutdown_trigger on_halt_trigger = {ON_HALT_STR, &stop_action}; | ||
1192 | |||
1193 | static ssize_t on_halt_show(struct kobject *kobj, | ||
1194 | struct kobj_attribute *attr, char *page) | ||
1195 | { | ||
1196 | return sprintf(page, "%s\n", on_halt_trigger.action->name); | ||
1197 | } | ||
1198 | |||
1199 | static ssize_t on_halt_store(struct kobject *kobj, | ||
1200 | struct kobj_attribute *attr, | ||
1201 | const char *buf, size_t len) | ||
1202 | { | ||
1203 | return set_trigger(buf, &on_halt_trigger, len); | ||
1204 | } | ||
1205 | |||
1206 | static struct kobj_attribute on_halt_attr = | ||
1207 | __ATTR(on_halt, 0644, on_halt_show, on_halt_store); | ||
1208 | |||
1209 | |||
1210 | static void do_machine_halt(void) | ||
1211 | { | ||
1212 | smp_send_stop(); | ||
1213 | on_halt_trigger.action->fn(&on_halt_trigger); | ||
1214 | stop_run(&on_halt_trigger); | ||
1215 | } | ||
1216 | void (*_machine_halt)(void) = do_machine_halt; | ||
1217 | |||
1218 | /* on power off */ | ||
1219 | |||
1220 | static struct shutdown_trigger on_poff_trigger = {ON_POFF_STR, &stop_action}; | ||
1221 | |||
1222 | static ssize_t on_poff_show(struct kobject *kobj, | ||
1223 | struct kobj_attribute *attr, char *page) | ||
1224 | { | ||
1225 | return sprintf(page, "%s\n", on_poff_trigger.action->name); | ||
1226 | } | ||
1227 | |||
1228 | static ssize_t on_poff_store(struct kobject *kobj, | ||
1229 | struct kobj_attribute *attr, | ||
1230 | const char *buf, size_t len) | ||
1231 | { | ||
1232 | return set_trigger(buf, &on_poff_trigger, len); | ||
1233 | } | ||
1234 | |||
1235 | static struct kobj_attribute on_poff_attr = | ||
1236 | __ATTR(on_poff, 0644, on_poff_show, on_poff_store); | ||
1237 | |||
1238 | |||
1239 | static void do_machine_power_off(void) | ||
1240 | { | ||
1241 | smp_send_stop(); | ||
1242 | on_poff_trigger.action->fn(&on_poff_trigger); | ||
1243 | stop_run(&on_poff_trigger); | ||
1244 | } | ||
1245 | void (*_machine_power_off)(void) = do_machine_power_off; | ||
1087 | 1246 | ||
1247 | static void __init shutdown_triggers_init(void) | ||
1248 | { | ||
1088 | shutdown_actions_kset = kset_create_and_add("shutdown_actions", NULL, | 1249 | shutdown_actions_kset = kset_create_and_add("shutdown_actions", NULL, |
1089 | firmware_kobj); | 1250 | firmware_kobj); |
1090 | if (!shutdown_actions_kset) | 1251 | if (!shutdown_actions_kset) |
1091 | return -ENOMEM; | 1252 | goto fail; |
1092 | rc = sysfs_create_file(&shutdown_actions_kset->kobj, &on_panic_attr); | 1253 | if (sysfs_create_file(&shutdown_actions_kset->kobj, |
1093 | if (rc) { | 1254 | &on_reboot_attr.attr)) |
1094 | kset_unregister(shutdown_actions_kset); | 1255 | goto fail; |
1095 | return rc; | 1256 | if (sysfs_create_file(&shutdown_actions_kset->kobj, |
1257 | &on_panic_attr.attr)) | ||
1258 | goto fail; | ||
1259 | if (sysfs_create_file(&shutdown_actions_kset->kobj, | ||
1260 | &on_halt_attr.attr)) | ||
1261 | goto fail; | ||
1262 | if (sysfs_create_file(&shutdown_actions_kset->kobj, | ||
1263 | &on_poff_attr.attr)) | ||
1264 | goto fail; | ||
1265 | |||
1266 | return; | ||
1267 | fail: | ||
1268 | panic("shutdown_triggers_init failed\n"); | ||
1269 | } | ||
1270 | |||
1271 | static void __init shutdown_actions_init(void) | ||
1272 | { | ||
1273 | int i; | ||
1274 | |||
1275 | for (i = 0; i < SHUTDOWN_ACTIONS_COUNT; i++) { | ||
1276 | if (!shutdown_actions_list[i]->init) | ||
1277 | continue; | ||
1278 | if (shutdown_actions_list[i]->init()) | ||
1279 | shutdown_actions_list[i] = NULL; | ||
1096 | } | 1280 | } |
1097 | atomic_notifier_chain_register(&panic_notifier_list, | ||
1098 | &shutdown_on_panic_nb); | ||
1099 | return 0; | ||
1100 | } | 1281 | } |
1101 | 1282 | ||
1102 | static int __init s390_ipl_init(void) | 1283 | static int __init s390_ipl_init(void) |
1103 | { | 1284 | { |
1104 | int rc; | ||
1105 | |||
1106 | sclp_get_ipl_info(&sclp_ipl_info); | ||
1107 | reipl_probe(); | 1285 | reipl_probe(); |
1108 | rc = ipl_init(); | 1286 | shutdown_actions_init(); |
1109 | if (rc) | 1287 | shutdown_triggers_init(); |
1110 | return rc; | ||
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; | 1288 | return 0; |
1121 | } | 1289 | } |
1122 | 1290 | ||
1123 | __initcall(s390_ipl_init); | 1291 | __initcall(s390_ipl_init); |
1124 | 1292 | ||
1293 | static void __init strncpy_skip_quote(char *dst, char *src, int n) | ||
1294 | { | ||
1295 | int sx, dx; | ||
1296 | |||
1297 | dx = 0; | ||
1298 | for (sx = 0; src[sx] != 0; sx++) { | ||
1299 | if (src[sx] == '"') | ||
1300 | continue; | ||
1301 | dst[dx++] = src[sx]; | ||
1302 | if (dx >= n) | ||
1303 | break; | ||
1304 | } | ||
1305 | } | ||
1306 | |||
1307 | static int __init vmcmd_on_reboot_setup(char *str) | ||
1308 | { | ||
1309 | if (!MACHINE_IS_VM) | ||
1310 | return 1; | ||
1311 | strncpy_skip_quote(vmcmd_on_reboot, str, 127); | ||
1312 | vmcmd_on_reboot[127] = 0; | ||
1313 | on_reboot_trigger.action = &vmcmd_action; | ||
1314 | return 1; | ||
1315 | } | ||
1316 | __setup("vmreboot=", vmcmd_on_reboot_setup); | ||
1317 | |||
1318 | static int __init vmcmd_on_panic_setup(char *str) | ||
1319 | { | ||
1320 | if (!MACHINE_IS_VM) | ||
1321 | return 1; | ||
1322 | strncpy_skip_quote(vmcmd_on_panic, str, 127); | ||
1323 | vmcmd_on_panic[127] = 0; | ||
1324 | on_panic_trigger.action = &vmcmd_action; | ||
1325 | return 1; | ||
1326 | } | ||
1327 | __setup("vmpanic=", vmcmd_on_panic_setup); | ||
1328 | |||
1329 | static int __init vmcmd_on_halt_setup(char *str) | ||
1330 | { | ||
1331 | if (!MACHINE_IS_VM) | ||
1332 | return 1; | ||
1333 | strncpy_skip_quote(vmcmd_on_halt, str, 127); | ||
1334 | vmcmd_on_halt[127] = 0; | ||
1335 | on_halt_trigger.action = &vmcmd_action; | ||
1336 | return 1; | ||
1337 | } | ||
1338 | __setup("vmhalt=", vmcmd_on_halt_setup); | ||
1339 | |||
1340 | static int __init vmcmd_on_poff_setup(char *str) | ||
1341 | { | ||
1342 | if (!MACHINE_IS_VM) | ||
1343 | return 1; | ||
1344 | strncpy_skip_quote(vmcmd_on_poff, str, 127); | ||
1345 | vmcmd_on_poff[127] = 0; | ||
1346 | on_poff_trigger.action = &vmcmd_action; | ||
1347 | return 1; | ||
1348 | } | ||
1349 | __setup("vmpoff=", vmcmd_on_poff_setup); | ||
1350 | |||
1351 | static int on_panic_notify(struct notifier_block *self, | ||
1352 | unsigned long event, void *data) | ||
1353 | { | ||
1354 | do_panic(); | ||
1355 | return NOTIFY_OK; | ||
1356 | } | ||
1357 | |||
1358 | static struct notifier_block on_panic_nb = { | ||
1359 | .notifier_call = on_panic_notify, | ||
1360 | .priority = 0, | ||
1361 | }; | ||
1362 | |||
1363 | void __init setup_ipl(void) | ||
1364 | { | ||
1365 | ipl_info.type = get_ipl_type(); | ||
1366 | switch (ipl_info.type) { | ||
1367 | case IPL_TYPE_CCW: | ||
1368 | ipl_info.data.ccw.dev_id.devno = ipl_devno; | ||
1369 | ipl_info.data.ccw.dev_id.ssid = 0; | ||
1370 | break; | ||
1371 | case IPL_TYPE_FCP: | ||
1372 | case IPL_TYPE_FCP_DUMP: | ||
1373 | ipl_info.data.fcp.dev_id.devno = | ||
1374 | IPL_PARMBLOCK_START->ipl_info.fcp.devno; | ||
1375 | ipl_info.data.fcp.dev_id.ssid = 0; | ||
1376 | ipl_info.data.fcp.wwpn = IPL_PARMBLOCK_START->ipl_info.fcp.wwpn; | ||
1377 | ipl_info.data.fcp.lun = IPL_PARMBLOCK_START->ipl_info.fcp.lun; | ||
1378 | break; | ||
1379 | case IPL_TYPE_NSS: | ||
1380 | strncpy(ipl_info.data.nss.name, kernel_nss_name, | ||
1381 | sizeof(ipl_info.data.nss.name)); | ||
1382 | break; | ||
1383 | case IPL_TYPE_UNKNOWN: | ||
1384 | default: | ||
1385 | /* We have no info to copy */ | ||
1386 | break; | ||
1387 | } | ||
1388 | atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb); | ||
1389 | } | ||
1390 | |||
1125 | void __init ipl_save_parameters(void) | 1391 | void __init ipl_save_parameters(void) |
1126 | { | 1392 | { |
1127 | struct cio_iplinfo iplinfo; | 1393 | struct cio_iplinfo iplinfo; |
@@ -1202,3 +1468,4 @@ void s390_reset_system(void) | |||
1202 | 1468 | ||
1203 | do_reset_calls(); | 1469 | do_reset_calls(); |
1204 | } | 1470 | } |
1471 | |||