diff options
Diffstat (limited to 'arch/s390/kernel/ipl.c')
-rw-r--r-- | arch/s390/kernel/ipl.c | 236 |
1 files changed, 166 insertions, 70 deletions
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index 1f5e782b3d05..9e9972e8a52b 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 | */ |
@@ -552,48 +609,29 @@ static ssize_t on_panic_store(struct subsystem *subsys, const char *buf, | |||
552 | static struct subsys_attribute on_panic_attr = | 609 | static struct subsys_attribute on_panic_attr = |
553 | __ATTR(on_panic, 0644, on_panic_show, on_panic_store); | 610 | __ATTR(on_panic, 0644, on_panic_show, on_panic_store); |
554 | 611 | ||
555 | static void print_fcp_block(struct ipl_parameter_block *fcp_block) | ||
556 | { | ||
557 | printk(KERN_EMERG "wwpn: %016llx\n", | ||
558 | (unsigned long long)fcp_block->ipl_info.fcp.wwpn); | ||
559 | printk(KERN_EMERG "lun: %016llx\n", | ||
560 | (unsigned long long)fcp_block->ipl_info.fcp.lun); | ||
561 | printk(KERN_EMERG "bootprog: %lld\n", | ||
562 | (unsigned long long)fcp_block->ipl_info.fcp.bootprog); | ||
563 | printk(KERN_EMERG "br_lba: %lld\n", | ||
564 | (unsigned long long)fcp_block->ipl_info.fcp.br_lba); | ||
565 | printk(KERN_EMERG "device: %llx\n", | ||
566 | (unsigned long long)fcp_block->ipl_info.fcp.devno); | ||
567 | printk(KERN_EMERG "opt: %x\n", fcp_block->ipl_info.fcp.opt); | ||
568 | } | ||
569 | |||
570 | void do_reipl(void) | 612 | void do_reipl(void) |
571 | { | 613 | { |
572 | struct ccw_dev_id devid; | 614 | struct ccw_dev_id devid; |
573 | static char buf[100]; | 615 | static char buf[100]; |
574 | 616 | char loadparm[LOADPARM_LEN + 1]; | |
575 | switch (reipl_type) { | ||
576 | case IPL_TYPE_CCW: | ||
577 | printk(KERN_EMERG "reboot on ccw device: 0.0.%04x\n", | ||
578 | reipl_block_ccw->ipl_info.ccw.devno); | ||
579 | break; | ||
580 | case IPL_TYPE_FCP: | ||
581 | printk(KERN_EMERG "reboot on fcp device:\n"); | ||
582 | print_fcp_block(reipl_block_fcp); | ||
583 | break; | ||
584 | default: | ||
585 | break; | ||
586 | } | ||
587 | 617 | ||
588 | switch (reipl_method) { | 618 | switch (reipl_method) { |
589 | case IPL_METHOD_CCW_CIO: | 619 | case IPL_METHOD_CCW_CIO: |
590 | devid.devno = reipl_block_ccw->ipl_info.ccw.devno; | 620 | devid.devno = reipl_block_ccw->ipl_info.ccw.devno; |
621 | if (ipl_get_type() == IPL_TYPE_CCW && devid.devno == ipl_devno) | ||
622 | diag308(DIAG308_IPL, NULL); | ||
591 | devid.ssid = 0; | 623 | devid.ssid = 0; |
592 | reipl_ccw_dev(&devid); | 624 | reipl_ccw_dev(&devid); |
593 | break; | 625 | break; |
594 | case IPL_METHOD_CCW_VM: | 626 | case IPL_METHOD_CCW_VM: |
595 | sprintf(buf, "IPL %X", reipl_block_ccw->ipl_info.ccw.devno); | 627 | reipl_get_ascii_loadparm(loadparm); |
596 | cpcmd(buf, NULL, 0, NULL); | 628 | if (strlen(loadparm) == 0) |
629 | sprintf(buf, "IPL %X", | ||
630 | reipl_block_ccw->ipl_info.ccw.devno); | ||
631 | else | ||
632 | sprintf(buf, "IPL %X LOADPARM '%s'", | ||
633 | reipl_block_ccw->ipl_info.ccw.devno, loadparm); | ||
634 | __cpcmd(buf, NULL, 0, NULL); | ||
597 | break; | 635 | break; |
598 | case IPL_METHOD_CCW_DIAG: | 636 | case IPL_METHOD_CCW_DIAG: |
599 | diag308(DIAG308_SET, reipl_block_ccw); | 637 | diag308(DIAG308_SET, reipl_block_ccw); |
@@ -607,16 +645,16 @@ void do_reipl(void) | |||
607 | diag308(DIAG308_IPL, NULL); | 645 | diag308(DIAG308_IPL, NULL); |
608 | break; | 646 | break; |
609 | case IPL_METHOD_FCP_RO_VM: | 647 | case IPL_METHOD_FCP_RO_VM: |
610 | cpcmd("IPL", NULL, 0, NULL); | 648 | __cpcmd("IPL", NULL, 0, NULL); |
611 | break; | 649 | break; |
612 | case IPL_METHOD_NONE: | 650 | case IPL_METHOD_NONE: |
613 | default: | 651 | default: |
614 | if (MACHINE_IS_VM) | 652 | if (MACHINE_IS_VM) |
615 | cpcmd("IPL", NULL, 0, NULL); | 653 | __cpcmd("IPL", NULL, 0, NULL); |
616 | diag308(DIAG308_IPL, NULL); | 654 | diag308(DIAG308_IPL, NULL); |
617 | break; | 655 | break; |
618 | } | 656 | } |
619 | panic("reipl failed!\n"); | 657 | signal_processor(smp_processor_id(), sigp_stop_and_store_status); |
620 | } | 658 | } |
621 | 659 | ||
622 | static void do_dump(void) | 660 | static void do_dump(void) |
@@ -624,32 +662,19 @@ static void do_dump(void) | |||
624 | struct ccw_dev_id devid; | 662 | struct ccw_dev_id devid; |
625 | static char buf[100]; | 663 | static char buf[100]; |
626 | 664 | ||
627 | switch (dump_type) { | ||
628 | case IPL_TYPE_CCW: | ||
629 | printk(KERN_EMERG "Automatic dump on ccw device: 0.0.%04x\n", | ||
630 | dump_block_ccw->ipl_info.ccw.devno); | ||
631 | break; | ||
632 | case IPL_TYPE_FCP: | ||
633 | printk(KERN_EMERG "Automatic dump on fcp device:\n"); | ||
634 | print_fcp_block(dump_block_fcp); | ||
635 | break; | ||
636 | default: | ||
637 | return; | ||
638 | } | ||
639 | |||
640 | switch (dump_method) { | 665 | switch (dump_method) { |
641 | case IPL_METHOD_CCW_CIO: | 666 | case IPL_METHOD_CCW_CIO: |
642 | dump_smp_stop_all(); | 667 | smp_send_stop(); |
643 | devid.devno = dump_block_ccw->ipl_info.ccw.devno; | 668 | devid.devno = dump_block_ccw->ipl_info.ccw.devno; |
644 | devid.ssid = 0; | 669 | devid.ssid = 0; |
645 | reipl_ccw_dev(&devid); | 670 | reipl_ccw_dev(&devid); |
646 | break; | 671 | break; |
647 | case IPL_METHOD_CCW_VM: | 672 | case IPL_METHOD_CCW_VM: |
648 | dump_smp_stop_all(); | 673 | smp_send_stop(); |
649 | sprintf(buf, "STORE STATUS"); | 674 | sprintf(buf, "STORE STATUS"); |
650 | cpcmd(buf, NULL, 0, NULL); | 675 | __cpcmd(buf, NULL, 0, NULL); |
651 | sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno); | 676 | sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno); |
652 | cpcmd(buf, NULL, 0, NULL); | 677 | __cpcmd(buf, NULL, 0, NULL); |
653 | break; | 678 | break; |
654 | case IPL_METHOD_CCW_DIAG: | 679 | case IPL_METHOD_CCW_DIAG: |
655 | diag308(DIAG308_SET, dump_block_ccw); | 680 | diag308(DIAG308_SET, dump_block_ccw); |
@@ -746,6 +771,17 @@ static int __init reipl_ccw_init(void) | |||
746 | reipl_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION; | 771 | reipl_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION; |
747 | reipl_block_ccw->hdr.blk0_len = sizeof(reipl_block_ccw->ipl_info.ccw); | 772 | reipl_block_ccw->hdr.blk0_len = sizeof(reipl_block_ccw->ipl_info.ccw); |
748 | reipl_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW; | 773 | reipl_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW; |
774 | /* check if read scp info worked and set loadparm */ | ||
775 | if (SCCB_VALID) | ||
776 | memcpy(reipl_block_ccw->ipl_info.ccw.load_param, | ||
777 | SCCB_LOADPARM, LOADPARM_LEN); | ||
778 | else | ||
779 | /* read scp info failed: set empty loadparm (EBCDIC blanks) */ | ||
780 | memset(reipl_block_ccw->ipl_info.ccw.load_param, 0x40, | ||
781 | LOADPARM_LEN); | ||
782 | /* FIXME: check for diag308_set_works when enabling diag ccw reipl */ | ||
783 | if (!MACHINE_IS_VM) | ||
784 | sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO; | ||
749 | if (ipl_get_type() == IPL_TYPE_CCW) | 785 | if (ipl_get_type() == IPL_TYPE_CCW) |
750 | reipl_block_ccw->ipl_info.ccw.devno = ipl_devno; | 786 | reipl_block_ccw->ipl_info.ccw.devno = ipl_devno; |
751 | reipl_capabilities |= IPL_TYPE_CCW; | 787 | reipl_capabilities |= IPL_TYPE_CCW; |
@@ -827,13 +863,11 @@ static int __init dump_ccw_init(void) | |||
827 | return 0; | 863 | return 0; |
828 | } | 864 | } |
829 | 865 | ||
830 | extern char s390_readinfo_sccb[]; | ||
831 | |||
832 | static int __init dump_fcp_init(void) | 866 | static int __init dump_fcp_init(void) |
833 | { | 867 | { |
834 | int rc; | 868 | int rc; |
835 | 869 | ||
836 | if(!(s390_readinfo_sccb[91] & 0x2)) | 870 | if(!(SCCB_FLAG & 0x2) || !SCCB_VALID) |
837 | return 0; /* LDIPL DUMP is not installed */ | 871 | return 0; /* LDIPL DUMP is not installed */ |
838 | if (!diag308_set_works) | 872 | if (!diag308_set_works) |
839 | return 0; | 873 | return 0; |
@@ -931,3 +965,65 @@ static int __init s390_ipl_init(void) | |||
931 | } | 965 | } |
932 | 966 | ||
933 | __initcall(s390_ipl_init); | 967 | __initcall(s390_ipl_init); |
968 | |||
969 | static LIST_HEAD(rcall); | ||
970 | static DEFINE_MUTEX(rcall_mutex); | ||
971 | |||
972 | void register_reset_call(struct reset_call *reset) | ||
973 | { | ||
974 | mutex_lock(&rcall_mutex); | ||
975 | list_add(&reset->list, &rcall); | ||
976 | mutex_unlock(&rcall_mutex); | ||
977 | } | ||
978 | EXPORT_SYMBOL_GPL(register_reset_call); | ||
979 | |||
980 | void unregister_reset_call(struct reset_call *reset) | ||
981 | { | ||
982 | mutex_lock(&rcall_mutex); | ||
983 | list_del(&reset->list); | ||
984 | mutex_unlock(&rcall_mutex); | ||
985 | } | ||
986 | EXPORT_SYMBOL_GPL(unregister_reset_call); | ||
987 | |||
988 | static void do_reset_calls(void) | ||
989 | { | ||
990 | struct reset_call *reset; | ||
991 | |||
992 | list_for_each_entry(reset, &rcall, list) | ||
993 | reset->fn(); | ||
994 | } | ||
995 | |||
996 | extern void reset_mcck_handler(void); | ||
997 | extern void reset_pgm_handler(void); | ||
998 | extern __u32 dump_prefix_page; | ||
999 | |||
1000 | void s390_reset_system(void) | ||
1001 | { | ||
1002 | struct _lowcore *lc; | ||
1003 | |||
1004 | lc = (struct _lowcore *)(unsigned long) store_prefix(); | ||
1005 | |||
1006 | /* Stack for interrupt/machine check handler */ | ||
1007 | lc->panic_stack = S390_lowcore.panic_stack; | ||
1008 | |||
1009 | /* Save prefix page address for dump case */ | ||
1010 | dump_prefix_page = (unsigned long) lc; | ||
1011 | |||
1012 | /* Disable prefixing */ | ||
1013 | set_prefix(0); | ||
1014 | |||
1015 | /* Disable lowcore protection */ | ||
1016 | __ctl_clear_bit(0,28); | ||
1017 | |||
1018 | /* Set new machine check handler */ | ||
1019 | S390_lowcore.mcck_new_psw.mask = PSW_KERNEL_BITS & ~PSW_MASK_MCHECK; | ||
1020 | S390_lowcore.mcck_new_psw.addr = | ||
1021 | PSW_ADDR_AMODE | (unsigned long) &reset_mcck_handler; | ||
1022 | |||
1023 | /* Set new program check handler */ | ||
1024 | S390_lowcore.program_new_psw.mask = PSW_KERNEL_BITS & ~PSW_MASK_MCHECK; | ||
1025 | S390_lowcore.program_new_psw.addr = | ||
1026 | PSW_ADDR_AMODE | (unsigned long) &reset_pgm_handler; | ||
1027 | |||
1028 | do_reset_calls(); | ||
1029 | } | ||