aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/kernel/ipl.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2006-12-05 12:01:28 -0500
committerDavid Howells <dhowells@warthog.cambridge.redhat.com>2006-12-05 12:01:28 -0500
commit9db73724453a9350e1c22dbe732d427e2939a5c9 (patch)
tree15e3ead6413ae97398a54292acc199bee0864d42 /arch/s390/kernel/ipl.c
parent4c1ac1b49122b805adfa4efc620592f68dccf5db (diff)
parente62438630ca37539c8cc1553710bbfaa3cf960a7 (diff)
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Conflicts: drivers/ata/libata-scsi.c include/linux/libata.h Futher merge of Linus's head and compilation fixups. Signed-Off-By: David Howells <dhowells@redhat.com>
Diffstat (limited to 'arch/s390/kernel/ipl.c')
-rw-r--r--arch/s390/kernel/ipl.c185
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
27extern 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
23enum ipl_type { 32enum 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
301static 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
313static struct subsys_attribute sys_ipl_ccw_loadparm_attr =
314 __ATTR(loadparm, 0444, ipl_ccw_loadparm_show, NULL);
315
292static struct attribute *ipl_ccw_attrs[] = { 316static 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 = {
348DEFINE_IPL_ATTR_RW(reipl_ccw, device, "0.0.%04llx\n", "0.0.%llx\n", 373DEFINE_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
376static 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
385static 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
393static 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
420static struct subsys_attribute sys_reipl_ccw_loadparm_attr =
421 __ATTR(loadparm, 0644, reipl_ccw_loadparm_show,
422 reipl_ccw_loadparm_store);
423
351static struct attribute *reipl_ccw_attrs[] = { 424static 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
503static decl_subsys(dump, NULL, NULL); 577static decl_subsys(dump, NULL, NULL);
504 578
505#ifdef CONFIG_SMP
506static 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
622static void do_dump(void) 690static 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
830extern char s390_readinfo_sccb[];
831
832static int __init dump_fcp_init(void) 909static 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
1012static LIST_HEAD(rcall);
1013static DEFINE_MUTEX(rcall_mutex);
1014
1015void 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}
1021EXPORT_SYMBOL_GPL(register_reset_call);
1022
1023void unregister_reset_call(struct reset_call *reset)
1024{
1025 mutex_lock(&rcall_mutex);
1026 list_del(&reset->list);
1027 mutex_unlock(&rcall_mutex);
1028}
1029EXPORT_SYMBOL_GPL(unregister_reset_call);
1030
1031static 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
1039extern void reset_mcck_handler(void);
1040
1041void 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}