diff options
Diffstat (limited to 'arch/s390/kernel/ipl.c')
-rw-r--r-- | arch/s390/kernel/ipl.c | 185 |
1 files changed, 156 insertions, 29 deletions
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index 1f5e782b3d05..a36bea1188d9 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c | |||
@@ -13,12 +13,21 @@ | |||
13 | #include <linux/device.h> | 13 | #include <linux/device.h> |
14 | #include <linux/delay.h> | 14 | #include <linux/delay.h> |
15 | #include <linux/reboot.h> | 15 | #include <linux/reboot.h> |
16 | #include <linux/ctype.h> | ||
16 | #include <asm/smp.h> | 17 | #include <asm/smp.h> |
17 | #include <asm/setup.h> | 18 | #include <asm/setup.h> |
18 | #include <asm/cpcmd.h> | 19 | #include <asm/cpcmd.h> |
19 | #include <asm/cio.h> | 20 | #include <asm/cio.h> |
21 | #include <asm/ebcdic.h> | ||
22 | #include <asm/reset.h> | ||
20 | 23 | ||
21 | #define IPL_PARM_BLOCK_VERSION 0 | 24 | #define IPL_PARM_BLOCK_VERSION 0 |
25 | #define LOADPARM_LEN 8 | ||
26 | |||
27 | extern char s390_readinfo_sccb[]; | ||
28 | #define SCCB_VALID (*((__u16*)&s390_readinfo_sccb[6]) == 0x0010) | ||
29 | #define SCCB_LOADPARM (&s390_readinfo_sccb[24]) | ||
30 | #define SCCB_FLAG (s390_readinfo_sccb[91]) | ||
22 | 31 | ||
23 | enum ipl_type { | 32 | enum ipl_type { |
24 | IPL_TYPE_NONE = 1, | 33 | IPL_TYPE_NONE = 1, |
@@ -289,9 +298,25 @@ static struct attribute_group ipl_fcp_attr_group = { | |||
289 | 298 | ||
290 | /* CCW ipl device attributes */ | 299 | /* CCW ipl device attributes */ |
291 | 300 | ||
301 | static ssize_t ipl_ccw_loadparm_show(struct subsystem *subsys, char *page) | ||
302 | { | ||
303 | char loadparm[LOADPARM_LEN + 1] = {}; | ||
304 | |||
305 | if (!SCCB_VALID) | ||
306 | return sprintf(page, "#unknown#\n"); | ||
307 | memcpy(loadparm, SCCB_LOADPARM, LOADPARM_LEN); | ||
308 | EBCASC(loadparm, LOADPARM_LEN); | ||
309 | strstrip(loadparm); | ||
310 | return sprintf(page, "%s\n", loadparm); | ||
311 | } | ||
312 | |||
313 | static struct subsys_attribute sys_ipl_ccw_loadparm_attr = | ||
314 | __ATTR(loadparm, 0444, ipl_ccw_loadparm_show, NULL); | ||
315 | |||
292 | static struct attribute *ipl_ccw_attrs[] = { | 316 | static struct attribute *ipl_ccw_attrs[] = { |
293 | &sys_ipl_type_attr.attr, | 317 | &sys_ipl_type_attr.attr, |
294 | &sys_ipl_device_attr.attr, | 318 | &sys_ipl_device_attr.attr, |
319 | &sys_ipl_ccw_loadparm_attr.attr, | ||
295 | NULL, | 320 | NULL, |
296 | }; | 321 | }; |
297 | 322 | ||
@@ -348,8 +373,57 @@ static struct attribute_group reipl_fcp_attr_group = { | |||
348 | DEFINE_IPL_ATTR_RW(reipl_ccw, device, "0.0.%04llx\n", "0.0.%llx\n", | 373 | DEFINE_IPL_ATTR_RW(reipl_ccw, device, "0.0.%04llx\n", "0.0.%llx\n", |
349 | reipl_block_ccw->ipl_info.ccw.devno); | 374 | reipl_block_ccw->ipl_info.ccw.devno); |
350 | 375 | ||
376 | static void reipl_get_ascii_loadparm(char *loadparm) | ||
377 | { | ||
378 | memcpy(loadparm, &reipl_block_ccw->ipl_info.ccw.load_param, | ||
379 | LOADPARM_LEN); | ||
380 | EBCASC(loadparm, LOADPARM_LEN); | ||
381 | loadparm[LOADPARM_LEN] = 0; | ||
382 | strstrip(loadparm); | ||
383 | } | ||
384 | |||
385 | static ssize_t reipl_ccw_loadparm_show(struct subsystem *subsys, char *page) | ||
386 | { | ||
387 | char buf[LOADPARM_LEN + 1]; | ||
388 | |||
389 | reipl_get_ascii_loadparm(buf); | ||
390 | return sprintf(page, "%s\n", buf); | ||
391 | } | ||
392 | |||
393 | static ssize_t reipl_ccw_loadparm_store(struct subsystem *subsys, | ||
394 | const char *buf, size_t len) | ||
395 | { | ||
396 | int i, lp_len; | ||
397 | |||
398 | /* ignore trailing newline */ | ||
399 | lp_len = len; | ||
400 | if ((len > 0) && (buf[len - 1] == '\n')) | ||
401 | lp_len--; | ||
402 | /* loadparm can have max 8 characters and must not start with a blank */ | ||
403 | if ((lp_len > LOADPARM_LEN) || ((lp_len > 0) && (buf[0] == ' '))) | ||
404 | return -EINVAL; | ||
405 | /* loadparm can only contain "a-z,A-Z,0-9,SP,." */ | ||
406 | for (i = 0; i < lp_len; i++) { | ||
407 | if (isalpha(buf[i]) || isdigit(buf[i]) || (buf[i] == ' ') || | ||
408 | (buf[i] == '.')) | ||
409 | continue; | ||
410 | return -EINVAL; | ||
411 | } | ||
412 | /* initialize loadparm with blanks */ | ||
413 | memset(&reipl_block_ccw->ipl_info.ccw.load_param, ' ', LOADPARM_LEN); | ||
414 | /* copy and convert to ebcdic */ | ||
415 | memcpy(&reipl_block_ccw->ipl_info.ccw.load_param, buf, lp_len); | ||
416 | ASCEBC(reipl_block_ccw->ipl_info.ccw.load_param, LOADPARM_LEN); | ||
417 | return len; | ||
418 | } | ||
419 | |||
420 | static struct subsys_attribute sys_reipl_ccw_loadparm_attr = | ||
421 | __ATTR(loadparm, 0644, reipl_ccw_loadparm_show, | ||
422 | reipl_ccw_loadparm_store); | ||
423 | |||
351 | static struct attribute *reipl_ccw_attrs[] = { | 424 | static struct attribute *reipl_ccw_attrs[] = { |
352 | &sys_reipl_ccw_device_attr.attr, | 425 | &sys_reipl_ccw_device_attr.attr, |
426 | &sys_reipl_ccw_loadparm_attr.attr, | ||
353 | NULL, | 427 | NULL, |
354 | }; | 428 | }; |
355 | 429 | ||
@@ -502,23 +576,6 @@ static struct subsys_attribute dump_type_attr = | |||
502 | 576 | ||
503 | static decl_subsys(dump, NULL, NULL); | 577 | static decl_subsys(dump, NULL, NULL); |
504 | 578 | ||
505 | #ifdef CONFIG_SMP | ||
506 | static void dump_smp_stop_all(void) | ||
507 | { | ||
508 | int cpu; | ||
509 | preempt_disable(); | ||
510 | for_each_online_cpu(cpu) { | ||
511 | if (cpu == smp_processor_id()) | ||
512 | continue; | ||
513 | while (signal_processor(cpu, sigp_stop) == sigp_busy) | ||
514 | udelay(10); | ||
515 | } | ||
516 | preempt_enable(); | ||
517 | } | ||
518 | #else | ||
519 | #define dump_smp_stop_all() do { } while (0) | ||
520 | #endif | ||
521 | |||
522 | /* | 579 | /* |
523 | * Shutdown actions section | 580 | * Shutdown actions section |
524 | */ | 581 | */ |
@@ -571,11 +628,14 @@ void do_reipl(void) | |||
571 | { | 628 | { |
572 | struct ccw_dev_id devid; | 629 | struct ccw_dev_id devid; |
573 | static char buf[100]; | 630 | static char buf[100]; |
631 | char loadparm[LOADPARM_LEN + 1]; | ||
574 | 632 | ||
575 | switch (reipl_type) { | 633 | switch (reipl_type) { |
576 | case IPL_TYPE_CCW: | 634 | case IPL_TYPE_CCW: |
635 | reipl_get_ascii_loadparm(loadparm); | ||
577 | printk(KERN_EMERG "reboot on ccw device: 0.0.%04x\n", | 636 | printk(KERN_EMERG "reboot on ccw device: 0.0.%04x\n", |
578 | reipl_block_ccw->ipl_info.ccw.devno); | 637 | reipl_block_ccw->ipl_info.ccw.devno); |
638 | printk(KERN_EMERG "loadparm = '%s'\n", loadparm); | ||
579 | break; | 639 | break; |
580 | case IPL_TYPE_FCP: | 640 | case IPL_TYPE_FCP: |
581 | printk(KERN_EMERG "reboot on fcp device:\n"); | 641 | printk(KERN_EMERG "reboot on fcp device:\n"); |
@@ -588,12 +648,19 @@ void do_reipl(void) | |||
588 | switch (reipl_method) { | 648 | switch (reipl_method) { |
589 | case IPL_METHOD_CCW_CIO: | 649 | case IPL_METHOD_CCW_CIO: |
590 | devid.devno = reipl_block_ccw->ipl_info.ccw.devno; | 650 | devid.devno = reipl_block_ccw->ipl_info.ccw.devno; |
651 | if (ipl_get_type() == IPL_TYPE_CCW && devid.devno == ipl_devno) | ||
652 | diag308(DIAG308_IPL, NULL); | ||
591 | devid.ssid = 0; | 653 | devid.ssid = 0; |
592 | reipl_ccw_dev(&devid); | 654 | reipl_ccw_dev(&devid); |
593 | break; | 655 | break; |
594 | case IPL_METHOD_CCW_VM: | 656 | case IPL_METHOD_CCW_VM: |
595 | sprintf(buf, "IPL %X", reipl_block_ccw->ipl_info.ccw.devno); | 657 | if (strlen(loadparm) == 0) |
596 | cpcmd(buf, NULL, 0, NULL); | 658 | sprintf(buf, "IPL %X", |
659 | reipl_block_ccw->ipl_info.ccw.devno); | ||
660 | else | ||
661 | sprintf(buf, "IPL %X LOADPARM '%s'", | ||
662 | reipl_block_ccw->ipl_info.ccw.devno, loadparm); | ||
663 | __cpcmd(buf, NULL, 0, NULL); | ||
597 | break; | 664 | break; |
598 | case IPL_METHOD_CCW_DIAG: | 665 | case IPL_METHOD_CCW_DIAG: |
599 | diag308(DIAG308_SET, reipl_block_ccw); | 666 | diag308(DIAG308_SET, reipl_block_ccw); |
@@ -607,16 +674,17 @@ void do_reipl(void) | |||
607 | diag308(DIAG308_IPL, NULL); | 674 | diag308(DIAG308_IPL, NULL); |
608 | break; | 675 | break; |
609 | case IPL_METHOD_FCP_RO_VM: | 676 | case IPL_METHOD_FCP_RO_VM: |
610 | cpcmd("IPL", NULL, 0, NULL); | 677 | __cpcmd("IPL", NULL, 0, NULL); |
611 | break; | 678 | break; |
612 | case IPL_METHOD_NONE: | 679 | case IPL_METHOD_NONE: |
613 | default: | 680 | default: |
614 | if (MACHINE_IS_VM) | 681 | if (MACHINE_IS_VM) |
615 | cpcmd("IPL", NULL, 0, NULL); | 682 | __cpcmd("IPL", NULL, 0, NULL); |
616 | diag308(DIAG308_IPL, NULL); | 683 | diag308(DIAG308_IPL, NULL); |
617 | break; | 684 | break; |
618 | } | 685 | } |
619 | panic("reipl failed!\n"); | 686 | printk(KERN_EMERG "reboot failed!\n"); |
687 | signal_processor(smp_processor_id(), sigp_stop_and_store_status); | ||
620 | } | 688 | } |
621 | 689 | ||
622 | static void do_dump(void) | 690 | static void do_dump(void) |
@@ -639,17 +707,17 @@ static void do_dump(void) | |||
639 | 707 | ||
640 | switch (dump_method) { | 708 | switch (dump_method) { |
641 | case IPL_METHOD_CCW_CIO: | 709 | case IPL_METHOD_CCW_CIO: |
642 | dump_smp_stop_all(); | 710 | smp_send_stop(); |
643 | devid.devno = dump_block_ccw->ipl_info.ccw.devno; | 711 | devid.devno = dump_block_ccw->ipl_info.ccw.devno; |
644 | devid.ssid = 0; | 712 | devid.ssid = 0; |
645 | reipl_ccw_dev(&devid); | 713 | reipl_ccw_dev(&devid); |
646 | break; | 714 | break; |
647 | case IPL_METHOD_CCW_VM: | 715 | case IPL_METHOD_CCW_VM: |
648 | dump_smp_stop_all(); | 716 | smp_send_stop(); |
649 | sprintf(buf, "STORE STATUS"); | 717 | sprintf(buf, "STORE STATUS"); |
650 | cpcmd(buf, NULL, 0, NULL); | 718 | __cpcmd(buf, NULL, 0, NULL); |
651 | sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno); | 719 | sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno); |
652 | cpcmd(buf, NULL, 0, NULL); | 720 | __cpcmd(buf, NULL, 0, NULL); |
653 | break; | 721 | break; |
654 | case IPL_METHOD_CCW_DIAG: | 722 | case IPL_METHOD_CCW_DIAG: |
655 | diag308(DIAG308_SET, dump_block_ccw); | 723 | diag308(DIAG308_SET, dump_block_ccw); |
@@ -746,6 +814,17 @@ static int __init reipl_ccw_init(void) | |||
746 | reipl_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION; | 814 | reipl_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION; |
747 | reipl_block_ccw->hdr.blk0_len = sizeof(reipl_block_ccw->ipl_info.ccw); | 815 | reipl_block_ccw->hdr.blk0_len = sizeof(reipl_block_ccw->ipl_info.ccw); |
748 | reipl_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW; | 816 | reipl_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW; |
817 | /* check if read scp info worked and set loadparm */ | ||
818 | if (SCCB_VALID) | ||
819 | memcpy(reipl_block_ccw->ipl_info.ccw.load_param, | ||
820 | SCCB_LOADPARM, LOADPARM_LEN); | ||
821 | else | ||
822 | /* read scp info failed: set empty loadparm (EBCDIC blanks) */ | ||
823 | memset(reipl_block_ccw->ipl_info.ccw.load_param, 0x40, | ||
824 | LOADPARM_LEN); | ||
825 | /* FIXME: check for diag308_set_works when enabling diag ccw reipl */ | ||
826 | if (!MACHINE_IS_VM) | ||
827 | sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO; | ||
749 | if (ipl_get_type() == IPL_TYPE_CCW) | 828 | if (ipl_get_type() == IPL_TYPE_CCW) |
750 | reipl_block_ccw->ipl_info.ccw.devno = ipl_devno; | 829 | reipl_block_ccw->ipl_info.ccw.devno = ipl_devno; |
751 | reipl_capabilities |= IPL_TYPE_CCW; | 830 | reipl_capabilities |= IPL_TYPE_CCW; |
@@ -827,13 +906,11 @@ static int __init dump_ccw_init(void) | |||
827 | return 0; | 906 | return 0; |
828 | } | 907 | } |
829 | 908 | ||
830 | extern char s390_readinfo_sccb[]; | ||
831 | |||
832 | static int __init dump_fcp_init(void) | 909 | static int __init dump_fcp_init(void) |
833 | { | 910 | { |
834 | int rc; | 911 | int rc; |
835 | 912 | ||
836 | if(!(s390_readinfo_sccb[91] & 0x2)) | 913 | if(!(SCCB_FLAG & 0x2) || !SCCB_VALID) |
837 | return 0; /* LDIPL DUMP is not installed */ | 914 | return 0; /* LDIPL DUMP is not installed */ |
838 | if (!diag308_set_works) | 915 | if (!diag308_set_works) |
839 | return 0; | 916 | return 0; |
@@ -931,3 +1008,53 @@ static int __init s390_ipl_init(void) | |||
931 | } | 1008 | } |
932 | 1009 | ||
933 | __initcall(s390_ipl_init); | 1010 | __initcall(s390_ipl_init); |
1011 | |||
1012 | static LIST_HEAD(rcall); | ||
1013 | static DEFINE_MUTEX(rcall_mutex); | ||
1014 | |||
1015 | void register_reset_call(struct reset_call *reset) | ||
1016 | { | ||
1017 | mutex_lock(&rcall_mutex); | ||
1018 | list_add(&reset->list, &rcall); | ||
1019 | mutex_unlock(&rcall_mutex); | ||
1020 | } | ||
1021 | EXPORT_SYMBOL_GPL(register_reset_call); | ||
1022 | |||
1023 | void unregister_reset_call(struct reset_call *reset) | ||
1024 | { | ||
1025 | mutex_lock(&rcall_mutex); | ||
1026 | list_del(&reset->list); | ||
1027 | mutex_unlock(&rcall_mutex); | ||
1028 | } | ||
1029 | EXPORT_SYMBOL_GPL(unregister_reset_call); | ||
1030 | |||
1031 | static void do_reset_calls(void) | ||
1032 | { | ||
1033 | struct reset_call *reset; | ||
1034 | |||
1035 | list_for_each_entry(reset, &rcall, list) | ||
1036 | reset->fn(); | ||
1037 | } | ||
1038 | |||
1039 | extern void reset_mcck_handler(void); | ||
1040 | |||
1041 | void s390_reset_system(void) | ||
1042 | { | ||
1043 | struct _lowcore *lc; | ||
1044 | |||
1045 | /* Stack for interrupt/machine check handler */ | ||
1046 | lc = (struct _lowcore *)(unsigned long) store_prefix(); | ||
1047 | lc->panic_stack = S390_lowcore.panic_stack; | ||
1048 | |||
1049 | /* Disable prefixing */ | ||
1050 | set_prefix(0); | ||
1051 | |||
1052 | /* Disable lowcore protection */ | ||
1053 | __ctl_clear_bit(0,28); | ||
1054 | |||
1055 | /* Set new machine check handler */ | ||
1056 | S390_lowcore.mcck_new_psw.mask = PSW_KERNEL_BITS & ~PSW_MASK_MCHECK; | ||
1057 | S390_lowcore.mcck_new_psw.addr = | ||
1058 | PSW_ADDR_AMODE | (unsigned long) &reset_mcck_handler; | ||
1059 | do_reset_calls(); | ||
1060 | } | ||