summaryrefslogtreecommitdiffstats
path: root/arch/s390
diff options
context:
space:
mode:
authorMichael Holzheu <holzheu@de.ibm.com>2007-04-27 16:01:49 +0200
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2007-04-27 16:01:44 +0200
commit411ed3225733dbd83b4cbaaa992ef80d6ec1534e (patch)
tree388aeac39e9fad5f7cadcc8fcbf0838811f5829d /arch/s390
parent7039d3a11c4b4b59f9ef933b4b0a28304bdd07d1 (diff)
[S390] zfcpdump support.
s390 machines provide hardware support for creating Linux dumps on SCSI disks. For creating a dump a special purpose dump Linux is used. The first 32 MB of memory are saved by the hardware before the dump Linux is booted. Via an SCLP interface, the saved memory can be accessed from Linux. This patch exports memory and registers of the crashed Linux to userspace via a debugfs file. For more information refer to Documentation/s390/zfcpdump.txt, which is included in this patch. Signed-off-by: Michael Holzheu <holzheu@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com> Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Diffstat (limited to 'arch/s390')
-rw-r--r--arch/s390/Kconfig8
-rw-r--r--arch/s390/Makefile3
-rw-r--r--arch/s390/defconfig1
-rw-r--r--arch/s390/kernel/head64.S72
-rw-r--r--arch/s390/kernel/ipl.c232
-rw-r--r--arch/s390/kernel/setup.c38
-rw-r--r--arch/s390/kernel/smp.c62
7 files changed, 330 insertions, 86 deletions
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 0f9517bc8e7..e6ec418093e 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -519,6 +519,14 @@ config KEXEC
current kernel, and to start another kernel. It is like a reboot
but is independent of hardware/microcode support.
+config ZFCPDUMP
+ tristate "zfcpdump support"
+ select SMP
+ default n
+ help
+ Select this option if you want to build an zfcpdump enabled kernel.
+ Refer to "Documentation/s390/zfcpdump.txt" for more details on this.
+
endmenu
source "net/Kconfig"
diff --git a/arch/s390/Makefile b/arch/s390/Makefile
index ece5adc0560..68441e0e74b 100644
--- a/arch/s390/Makefile
+++ b/arch/s390/Makefile
@@ -105,6 +105,9 @@ install: vmlinux
image: vmlinux
$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
+zfcpdump:
+ $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
+
archclean:
$(Q)$(MAKE) $(clean)=$(boot)
diff --git a/arch/s390/defconfig b/arch/s390/defconfig
index 80046d9e2a3..0e4da8a7d82 100644
--- a/arch/s390/defconfig
+++ b/arch/s390/defconfig
@@ -167,6 +167,7 @@ CONFIG_NO_IDLE_HZ=y
CONFIG_NO_IDLE_HZ_INIT=y
CONFIG_S390_HYPFS_FS=y
CONFIG_KEXEC=y
+# CONFIG_ZFCPDUMP is not set
#
# Networking
diff --git a/arch/s390/kernel/head64.S b/arch/s390/kernel/head64.S
index 37010709fe6..a87b1976d40 100644
--- a/arch/s390/kernel/head64.S
+++ b/arch/s390/kernel/head64.S
@@ -39,7 +39,69 @@ startup_continue:
basr %r13,0 # get base
.LPG1: sll %r13,1 # remove high order bit
srl %r13,1
- lhi %r1,1 # mode 1 = esame
+
+#ifdef CONFIG_ZFCPDUMP
+
+ # check if we have been ipled using zfcp dump:
+
+ tm 0xb9,0x01 # test if subchannel is enabled
+ jno .nodump # subchannel disabled
+ l %r1,0xb8
+ la %r5,.Lipl_schib-.LPG1(%r13)
+ stsch 0(%r5) # get schib of subchannel
+ jne .nodump # schib not available
+ tm 5(%r5),0x01 # devno valid?
+ jno .nodump
+ tm 4(%r5),0x80 # qdio capable device?
+ jno .nodump
+ l %r2,20(%r0) # address of ipl parameter block
+ lhi %r3,0
+ ic %r3,0x148(%r2) # get opt field
+ chi %r3,0x20 # load with dump?
+ jne .nodump
+
+ # store all prefix registers in case of load with dump:
+
+ la %r7,0 # base register for 0 page
+ la %r8,0 # first cpu
+ l %r11,.Lpref_arr_ptr-.LPG1(%r13) # address of prefix array
+ ahi %r11,4 # skip boot cpu
+ lr %r12,%r11
+ ahi %r12,(CONFIG_NR_CPUS*4) # end of prefix array
+ stap .Lcurrent_cpu+2-.LPG1(%r13) # store current cpu addr
+1:
+ cl %r8,.Lcurrent_cpu-.LPG1(%r13) # is ipl cpu ?
+ je 4f # if yes get next cpu
+2:
+ lr %r9,%r7
+ sigp %r9,%r8,0x9 # stop & store status of cpu
+ brc 8,3f # accepted
+ brc 4,4f # status stored: next cpu
+ brc 2,2b # busy: try again
+ brc 1,4f # not op: next cpu
+3:
+ mvc 0(4,%r11),264(%r7) # copy prefix register to prefix array
+ ahi %r11,4 # next element in prefix array
+ clr %r11,%r12
+ je 5f # no more space in prefix array
+4:
+ ahi %r8,1 # next cpu (r8 += 1)
+ cl %r8,.Llast_cpu-.LPG1(%r13) # is last possible cpu ?
+ jl 1b # jump if not last cpu
+5:
+ lhi %r1,2 # mode 2 = esame (dump)
+ j 6f
+ .align 4
+.Lipl_schib:
+ .rept 13
+ .long 0
+ .endr
+.nodump:
+ lhi %r1,1 # mode 1 = esame (normal ipl)
+6:
+#else
+ lhi %r1,1 # mode 1 = esame (normal ipl)
+#endif /* CONFIG_ZFCPDUMP */
mvi __LC_AR_MODE_ID,1 # set esame flag
slr %r0,%r0 # set cpuid to zero
sigp %r1,%r0,0x12 # switch to esame mode
@@ -149,6 +211,14 @@ startup_continue:
.L4malign:.quad 0xffffffffffc00000
.Lscan2g:.quad 0x80000000 + 0x20000 - 8 # 2GB + 128K - 8
.Lnop: .long 0x07000700
+#ifdef CONFIG_ZFCPDUMP
+.Lcurrent_cpu:
+ .long 0x0
+.Llast_cpu:
+ .long 0x0000ffff
+.Lpref_arr_ptr:
+ .long zfcpdump_prefix_array
+#endif /* CONFIG_ZFCPDUMP */
.Lparmaddr:
.quad PARMAREA
.align 64
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c
index a83cf1fdd8f..06833ac2b11 100644
--- a/arch/s390/kernel/ipl.c
+++ b/arch/s390/kernel/ipl.c
@@ -29,36 +29,21 @@
#define SCCB_LOADPARM (&s390_readinfo_sccb.loadparm)
#define SCCB_FLAG (s390_readinfo_sccb.flags)
-enum ipl_type {
- IPL_TYPE_NONE = 1,
- IPL_TYPE_UNKNOWN = 2,
- IPL_TYPE_CCW = 4,
- IPL_TYPE_FCP = 8,
- IPL_TYPE_NSS = 16,
-};
-
-#define IPL_NONE_STR "none"
-#define IPL_UNKNOWN_STR "unknown"
-#define IPL_CCW_STR "ccw"
-#define IPL_FCP_STR "fcp"
-#define IPL_NSS_STR "nss"
-
-/*
- * Must be in data section since the bss section
- * is not cleared when these are accessed.
- */
-static u16 ipl_devno __attribute__((__section__(".data"))) = 0;
-u32 ipl_flags __attribute__((__section__(".data"))) = 0;
+#define IPL_UNKNOWN_STR "unknown"
+#define IPL_CCW_STR "ccw"
+#define IPL_FCP_STR "fcp"
+#define IPL_FCP_DUMP_STR "fcp_dump"
+#define IPL_NSS_STR "nss"
static char *ipl_type_str(enum ipl_type type)
{
switch (type) {
- case IPL_TYPE_NONE:
- return IPL_NONE_STR;
case IPL_TYPE_CCW:
return IPL_CCW_STR;
case IPL_TYPE_FCP:
return IPL_FCP_STR;
+ case IPL_TYPE_FCP_DUMP:
+ return IPL_FCP_DUMP_STR;
case IPL_TYPE_NSS:
return IPL_NSS_STR;
case IPL_TYPE_UNKNOWN:
@@ -67,15 +52,55 @@ static char *ipl_type_str(enum ipl_type type)
}
}
+enum dump_type {
+ DUMP_TYPE_NONE = 1,
+ DUMP_TYPE_CCW = 2,
+ DUMP_TYPE_FCP = 4,
+};
+
+#define DUMP_NONE_STR "none"
+#define DUMP_CCW_STR "ccw"
+#define DUMP_FCP_STR "fcp"
+
+static char *dump_type_str(enum dump_type type)
+{
+ switch (type) {
+ case DUMP_TYPE_NONE:
+ return DUMP_NONE_STR;
+ case DUMP_TYPE_CCW:
+ return DUMP_CCW_STR;
+ case DUMP_TYPE_FCP:
+ return DUMP_FCP_STR;
+ default:
+ return NULL;
+ }
+}
+
+/*
+ * Must be in data section since the bss section
+ * is not cleared when these are accessed.
+ */
+static u16 ipl_devno __attribute__((__section__(".data"))) = 0;
+u32 ipl_flags __attribute__((__section__(".data"))) = 0;
+
enum ipl_method {
- IPL_METHOD_NONE,
- IPL_METHOD_CCW_CIO,
- IPL_METHOD_CCW_DIAG,
- IPL_METHOD_CCW_VM,
- IPL_METHOD_FCP_RO_DIAG,
- IPL_METHOD_FCP_RW_DIAG,
- IPL_METHOD_FCP_RO_VM,
- IPL_METHOD_NSS,
+ REIPL_METHOD_CCW_CIO,
+ REIPL_METHOD_CCW_DIAG,
+ REIPL_METHOD_CCW_VM,
+ REIPL_METHOD_FCP_RO_DIAG,
+ REIPL_METHOD_FCP_RW_DIAG,
+ REIPL_METHOD_FCP_RO_VM,
+ REIPL_METHOD_FCP_DUMP,
+ REIPL_METHOD_NSS,
+ REIPL_METHOD_DEFAULT,
+};
+
+enum dump_method {
+ DUMP_METHOD_NONE,
+ DUMP_METHOD_CCW_CIO,
+ DUMP_METHOD_CCW_DIAG,
+ DUMP_METHOD_CCW_VM,
+ DUMP_METHOD_FCP_DIAG,
};
enum shutdown_action {
@@ -107,15 +132,15 @@ static int diag308_set_works = 0;
static int reipl_capabilities = IPL_TYPE_UNKNOWN;
static enum ipl_type reipl_type = IPL_TYPE_UNKNOWN;
-static enum ipl_method reipl_method = IPL_METHOD_NONE;
+static enum ipl_method reipl_method = REIPL_METHOD_DEFAULT;
static struct ipl_parameter_block *reipl_block_fcp;
static struct ipl_parameter_block *reipl_block_ccw;
static char reipl_nss_name[NSS_NAME_SIZE + 1];
-static int dump_capabilities = IPL_TYPE_NONE;
-static enum ipl_type dump_type = IPL_TYPE_NONE;
-static enum ipl_method dump_method = IPL_METHOD_NONE;
+static int dump_capabilities = DUMP_TYPE_NONE;
+static enum dump_type dump_type = DUMP_TYPE_NONE;
+static enum dump_method dump_method = DUMP_METHOD_NONE;
static struct ipl_parameter_block *dump_block_fcp;
static struct ipl_parameter_block *dump_block_ccw;
@@ -134,6 +159,7 @@ int diag308(unsigned long subcode, void *addr)
: "d" (subcode) : "cc", "memory");
return _rc;
}
+EXPORT_SYMBOL_GPL(diag308);
/* SYSFS */
@@ -197,7 +223,7 @@ static void make_attrs_ro(struct attribute **attrs)
* ipl section
*/
-static enum ipl_type ipl_get_type(void)
+static __init enum ipl_type get_ipl_type(void)
{
struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START;
@@ -211,12 +237,44 @@ static enum ipl_type ipl_get_type(void)
return IPL_TYPE_UNKNOWN;
if (ipl->hdr.pbt != DIAG308_IPL_TYPE_FCP)
return IPL_TYPE_UNKNOWN;
+ if (ipl->ipl_info.fcp.opt == DIAG308_IPL_OPT_DUMP)
+ return IPL_TYPE_FCP_DUMP;
return IPL_TYPE_FCP;
}
+void __init setup_ipl_info(void)
+{
+ ipl_info.type = get_ipl_type();
+ switch (ipl_info.type) {
+ case IPL_TYPE_CCW:
+ ipl_info.data.ccw.dev_id.devno = ipl_devno;
+ ipl_info.data.ccw.dev_id.ssid = 0;
+ break;
+ case IPL_TYPE_FCP:
+ case IPL_TYPE_FCP_DUMP:
+ ipl_info.data.fcp.dev_id.devno =
+ IPL_PARMBLOCK_START->ipl_info.fcp.devno;
+ ipl_info.data.fcp.dev_id.ssid = 0;
+ ipl_info.data.fcp.wwpn = IPL_PARMBLOCK_START->ipl_info.fcp.wwpn;
+ ipl_info.data.fcp.lun = IPL_PARMBLOCK_START->ipl_info.fcp.lun;
+ break;
+ case IPL_TYPE_NSS:
+ strncpy(ipl_info.data.nss.name, kernel_nss_name,
+ sizeof(ipl_info.data.nss.name));
+ break;
+ case IPL_TYPE_UNKNOWN:
+ default:
+ /* We have no info to copy */
+ break;
+ }
+}
+
+struct ipl_info ipl_info;
+EXPORT_SYMBOL_GPL(ipl_info);
+
static ssize_t ipl_type_show(struct subsystem *subsys, char *page)
{
- return sprintf(page, "%s\n", ipl_type_str(ipl_get_type()));
+ return sprintf(page, "%s\n", ipl_type_str(ipl_info.type));
}
static struct subsys_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type);
@@ -225,10 +283,11 @@ static ssize_t sys_ipl_device_show(struct subsystem *subsys, char *page)
{
struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START;
- switch (ipl_get_type()) {
+ switch (ipl_info.type) {
case IPL_TYPE_CCW:
return sprintf(page, "0.0.%04x\n", ipl_devno);
case IPL_TYPE_FCP:
+ case IPL_TYPE_FCP_DUMP:
return sprintf(page, "0.0.%04x\n", ipl->ipl_info.fcp.devno);
default:
return 0;
@@ -485,23 +544,29 @@ static int reipl_set_type(enum ipl_type type)
switch(type) {
case IPL_TYPE_CCW:
if (MACHINE_IS_VM)
- reipl_method = IPL_METHOD_CCW_VM;
+ reipl_method = REIPL_METHOD_CCW_VM;
else
- reipl_method = IPL_METHOD_CCW_CIO;
+ reipl_method = REIPL_METHOD_CCW_CIO;
break;
case IPL_TYPE_FCP:
if (diag308_set_works)
- reipl_method = IPL_METHOD_FCP_RW_DIAG;
+ reipl_method = REIPL_METHOD_FCP_RW_DIAG;
else if (MACHINE_IS_VM)
- reipl_method = IPL_METHOD_FCP_RO_VM;
+ reipl_method = REIPL_METHOD_FCP_RO_VM;
else
- reipl_method = IPL_METHOD_FCP_RO_DIAG;
+ reipl_method = REIPL_METHOD_FCP_RO_DIAG;
+ break;
+ case IPL_TYPE_FCP_DUMP:
+ reipl_method = REIPL_METHOD_FCP_DUMP;
break;
case IPL_TYPE_NSS:
- reipl_method = IPL_METHOD_NSS;
+ reipl_method = REIPL_METHOD_NSS;
+ break;
+ case IPL_TYPE_UNKNOWN:
+ reipl_method = REIPL_METHOD_DEFAULT;
break;
default:
- reipl_method = IPL_METHOD_NONE;
+ BUG();
}
reipl_type = type;
return 0;
@@ -579,22 +644,22 @@ static struct attribute_group dump_ccw_attr_group = {
/* dump type */
-static int dump_set_type(enum ipl_type type)
+static int dump_set_type(enum dump_type type)
{
if (!(dump_capabilities & type))
return -EINVAL;
switch(type) {
- case IPL_TYPE_CCW:
+ case DUMP_TYPE_CCW:
if (MACHINE_IS_VM)
- dump_method = IPL_METHOD_CCW_VM;
+ dump_method = DUMP_METHOD_CCW_VM;
else
- dump_method = IPL_METHOD_CCW_CIO;
+ dump_method = DUMP_METHOD_CCW_CIO;
break;
- case IPL_TYPE_FCP:
- dump_method = IPL_METHOD_FCP_RW_DIAG;
+ case DUMP_TYPE_FCP:
+ dump_method = DUMP_METHOD_FCP_DIAG;
break;
default:
- dump_method = IPL_METHOD_NONE;
+ dump_method = DUMP_METHOD_NONE;
}
dump_type = type;
return 0;
@@ -602,7 +667,7 @@ static int dump_set_type(enum ipl_type type)
static ssize_t dump_type_show(struct subsystem *subsys, char *page)
{
- return sprintf(page, "%s\n", ipl_type_str(dump_type));
+ return sprintf(page, "%s\n", dump_type_str(dump_type));
}
static ssize_t dump_type_store(struct subsystem *subsys, const char *buf,
@@ -610,12 +675,12 @@ static ssize_t dump_type_store(struct subsystem *subsys, const char *buf,
{
int rc = -EINVAL;
- if (strncmp(buf, IPL_NONE_STR, strlen(IPL_NONE_STR)) == 0)
- rc = dump_set_type(IPL_TYPE_NONE);
- else if (strncmp(buf, IPL_CCW_STR, strlen(IPL_CCW_STR)) == 0)
- rc = dump_set_type(IPL_TYPE_CCW);
- else if (strncmp(buf, IPL_FCP_STR, strlen(IPL_FCP_STR)) == 0)
- rc = dump_set_type(IPL_TYPE_FCP);
+ if (strncmp(buf, DUMP_NONE_STR, strlen(DUMP_NONE_STR)) == 0)
+ rc = dump_set_type(DUMP_TYPE_NONE);
+ else if (strncmp(buf, DUMP_CCW_STR, strlen(DUMP_CCW_STR)) == 0)
+ rc = dump_set_type(DUMP_TYPE_CCW);
+ else if (strncmp(buf, DUMP_FCP_STR, strlen(DUMP_FCP_STR)) == 0)
+ rc = dump_set_type(DUMP_TYPE_FCP);
return (rc != 0) ? rc : len;
}
@@ -664,14 +729,14 @@ void do_reipl(void)
char loadparm[LOADPARM_LEN + 1];
switch (reipl_method) {
- case IPL_METHOD_CCW_CIO:
+ case REIPL_METHOD_CCW_CIO:
devid.devno = reipl_block_ccw->ipl_info.ccw.devno;
- if (ipl_get_type() == IPL_TYPE_CCW && devid.devno == ipl_devno)
+ if (ipl_info.type == IPL_TYPE_CCW && devid.devno == ipl_devno)
diag308(DIAG308_IPL, NULL);
devid.ssid = 0;
reipl_ccw_dev(&devid);
break;
- case IPL_METHOD_CCW_VM:
+ case REIPL_METHOD_CCW_VM:
reipl_get_ascii_loadparm(loadparm);
if (strlen(loadparm) == 0)
sprintf(buf, "IPL %X",
@@ -681,30 +746,32 @@ void do_reipl(void)
reipl_block_ccw->ipl_info.ccw.devno, loadparm);
__cpcmd(buf, NULL, 0, NULL);
break;
- case IPL_METHOD_CCW_DIAG:
+ case REIPL_METHOD_CCW_DIAG:
diag308(DIAG308_SET, reipl_block_ccw);
diag308(DIAG308_IPL, NULL);
break;
- case IPL_METHOD_FCP_RW_DIAG:
+ case REIPL_METHOD_FCP_RW_DIAG:
diag308(DIAG308_SET, reipl_block_fcp);
diag308(DIAG308_IPL, NULL);
break;
- case IPL_METHOD_FCP_RO_DIAG:
+ case REIPL_METHOD_FCP_RO_DIAG:
diag308(DIAG308_IPL, NULL);
break;
- case IPL_METHOD_FCP_RO_VM:
+ case REIPL_METHOD_FCP_RO_VM:
__cpcmd("IPL", NULL, 0, NULL);
break;
- case IPL_METHOD_NSS:
+ case REIPL_METHOD_NSS:
sprintf(buf, "IPL %s", reipl_nss_name);
__cpcmd(buf, NULL, 0, NULL);
break;
- case IPL_METHOD_NONE:
- default:
+ case REIPL_METHOD_DEFAULT:
if (MACHINE_IS_VM)
__cpcmd("IPL", NULL, 0, NULL);
diag308(DIAG308_IPL, NULL);
break;
+ case REIPL_METHOD_FCP_DUMP:
+ default:
+ break;
}
signal_processor(smp_processor_id(), sigp_stop_and_store_status);
}
@@ -715,28 +782,28 @@ static void do_dump(void)
static char buf[100];
switch (dump_method) {
- case IPL_METHOD_CCW_CIO:
+ case DUMP_METHOD_CCW_CIO:
smp_send_stop();
devid.devno = dump_block_ccw->ipl_info.ccw.devno;
devid.ssid = 0;
reipl_ccw_dev(&devid);
break;
- case IPL_METHOD_CCW_VM:
+ case DUMP_METHOD_CCW_VM:
smp_send_stop();
sprintf(buf, "STORE STATUS");
__cpcmd(buf, NULL, 0, NULL);
sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno);
__cpcmd(buf, NULL, 0, NULL);
break;
- case IPL_METHOD_CCW_DIAG:
+ case DUMP_METHOD_CCW_DIAG:
diag308(DIAG308_SET, dump_block_ccw);
diag308(DIAG308_DUMP, NULL);
break;
- case IPL_METHOD_FCP_RW_DIAG:
+ case DUMP_METHOD_FCP_DIAG:
diag308(DIAG308_SET, dump_block_fcp);
diag308(DIAG308_DUMP, NULL);
break;
- case IPL_METHOD_NONE:
+ case DUMP_METHOD_NONE:
default:
return;
}
@@ -777,12 +844,13 @@ static int __init ipl_init(void)
rc = firmware_register(&ipl_subsys);
if (rc)
return rc;
- switch (ipl_get_type()) {
+ switch (ipl_info.type) {
case IPL_TYPE_CCW:
rc = sysfs_create_group(&ipl_subsys.kset.kobj,
&ipl_ccw_attr_group);
break;
case IPL_TYPE_FCP:
+ case IPL_TYPE_FCP_DUMP:
rc = ipl_register_fcp_files();
break;
case IPL_TYPE_NSS:
@@ -852,7 +920,7 @@ static int __init reipl_ccw_init(void)
/* FIXME: check for diag308_set_works when enabling diag ccw reipl */
if (!MACHINE_IS_VM)
sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO;
- if (ipl_get_type() == IPL_TYPE_CCW)
+ if (ipl_info.type == IPL_TYPE_CCW)
reipl_block_ccw->ipl_info.ccw.devno = ipl_devno;
reipl_capabilities |= IPL_TYPE_CCW;
return 0;
@@ -862,9 +930,9 @@ static int __init reipl_fcp_init(void)
{
int rc;
- if ((!diag308_set_works) && (ipl_get_type() != IPL_TYPE_FCP))
+ if ((!diag308_set_works) && (ipl_info.type != IPL_TYPE_FCP))
return 0;
- if ((!diag308_set_works) && (ipl_get_type() == IPL_TYPE_FCP))
+ if ((!diag308_set_works) && (ipl_info.type == IPL_TYPE_FCP))
make_attrs_ro(reipl_fcp_attrs);
reipl_block_fcp = (void *) get_zeroed_page(GFP_KERNEL);
@@ -875,7 +943,7 @@ static int __init reipl_fcp_init(void)
free_page((unsigned long)reipl_block_fcp);
return rc;
}
- if (ipl_get_type() == IPL_TYPE_FCP) {
+ if (ipl_info.type == IPL_TYPE_FCP) {
memcpy(reipl_block_fcp, IPL_PARMBLOCK_START, PAGE_SIZE);
} else {
reipl_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN;
@@ -909,7 +977,7 @@ static int __init reipl_init(void)
rc = reipl_nss_init();
if (rc)
return rc;
- rc = reipl_set_type(ipl_get_type());
+ rc = reipl_set_type(ipl_info.type);
if (rc)
return rc;
return 0;
@@ -931,7 +999,7 @@ static int __init dump_ccw_init(void)
dump_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION;
dump_block_ccw->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN;
dump_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW;
- dump_capabilities |= IPL_TYPE_CCW;
+ dump_capabilities |= DUMP_TYPE_CCW;
return 0;
}
@@ -956,7 +1024,7 @@ static int __init dump_fcp_init(void)
dump_block_fcp->hdr.blk0_len = IPL_PARM_BLK0_FCP_LEN;
dump_block_fcp->hdr.pbt = DIAG308_IPL_TYPE_FCP;
dump_block_fcp->ipl_info.fcp.opt = DIAG308_IPL_OPT_DUMP;
- dump_capabilities |= IPL_TYPE_FCP;
+ dump_capabilities |= DUMP_TYPE_FCP;
return 0;
}
@@ -995,7 +1063,7 @@ static int __init dump_init(void)
rc = dump_fcp_init();
if (rc)
return rc;
- dump_set_type(IPL_TYPE_NONE);
+ dump_set_type(DUMP_TYPE_NONE);
return 0;
}
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index 863c8d08c02..3dfd0985861 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -285,6 +285,26 @@ static void __init conmode_default(void)
}
}
+#if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE)
+static void __init setup_zfcpdump(unsigned int console_devno)
+{
+ static char str[64];
+
+ if (ipl_info.type != IPL_TYPE_FCP_DUMP)
+ return;
+ if (console_devno != -1)
+ sprintf(str, "cio_ignore=all,!0.0.%04x,!0.0.%04x",
+ ipl_info.data.fcp.dev_id.devno, console_devno);
+ else
+ sprintf(str, "cio_ignore=all,!0.0.%04x",
+ ipl_info.data.fcp.dev_id.devno);
+ strcat(COMMAND_LINE, str);
+ console_loglevel = 2;
+}
+#else
+static inline void setup_zfcpdump(unsigned int console_devno) {}
+#endif /* CONFIG_ZFCPDUMP */
+
#ifdef CONFIG_SMP
void (*_machine_restart)(char *command) = machine_restart_smp;
void (*_machine_halt)(void) = machine_halt_smp;
@@ -586,13 +606,20 @@ setup_resources(void)
}
}
+unsigned long real_memory_size;
+EXPORT_SYMBOL_GPL(real_memory_size);
+
static void __init setup_memory_end(void)
{
- unsigned long real_size, memory_size;
+ unsigned long memory_size;
unsigned long max_mem, max_phys;
int i;
- memory_size = real_size = 0;
+#if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE)
+ if (ipl_info.type == IPL_TYPE_FCP_DUMP)
+ memory_end = ZFCPDUMP_HSA_SIZE;
+#endif
+ memory_size = 0;
max_phys = VMALLOC_END_INIT - VMALLOC_MIN_SIZE;
memory_end &= PAGE_MASK;
@@ -601,7 +628,8 @@ static void __init setup_memory_end(void)
for (i = 0; i < MEMORY_CHUNKS; i++) {
struct mem_chunk *chunk = &memory_chunk[i];
- real_size = max(real_size, chunk->addr + chunk->size);
+ real_memory_size = max(real_memory_size,
+ chunk->addr + chunk->size);
if (chunk->addr >= max_mem) {
memset(chunk, 0, sizeof(*chunk));
continue;
@@ -765,6 +793,7 @@ setup_arch(char **cmdline_p)
parse_early_param();
+ setup_ipl_info();
setup_memory_end();
setup_addressing_mode();
setup_memory();
@@ -782,6 +811,9 @@ setup_arch(char **cmdline_p)
/* Setup default console */
conmode_default();
+
+ /* Setup zfcpdump support */
+ setup_zfcpdump(console_devno);
}
void print_cpu_info(struct cpuinfo_S390 *cpuinfo)
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
index 97764f710bb..7c0143fdf71 100644
--- a/arch/s390/kernel/smp.c
+++ b/arch/s390/kernel/smp.c
@@ -31,6 +31,7 @@
#include <linux/interrupt.h>
#include <linux/cpu.h>
#include <linux/timex.h>
+#include <linux/bootmem.h>
#include <asm/ipl.h>
#include <asm/setup.h>
#include <asm/sigp.h>
@@ -40,6 +41,7 @@
#include <asm/cpcmd.h>
#include <asm/tlbflush.h>
#include <asm/timer.h>
+#include <asm/lowcore.h>
extern volatile int __cpu_logical_map[];
@@ -395,6 +397,65 @@ void smp_ctl_clear_bit(int cr, int bit)
on_each_cpu(smp_ctl_bit_callback, &parms, 0, 1);
}
+#if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE)
+
+/*
+ * zfcpdump_prefix_array holds prefix registers for the following scenario:
+ * 64 bit zfcpdump kernel and 31 bit kernel which is to be dumped. We have to
+ * save its prefix registers, since they get lost, when switching from 31 bit
+ * to 64 bit.
+ */
+unsigned int zfcpdump_prefix_array[NR_CPUS + 1] \
+ __attribute__((__section__(".data")));
+
+static void __init smp_get_save_areas(void)
+{
+ unsigned int cpu, cpu_num, rc;
+ __u16 boot_cpu_addr;
+
+ if (ipl_info.type != IPL_TYPE_FCP_DUMP)
+ return;
+ boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr;
+ cpu_num = 1;
+ for (cpu = 0; cpu <= 65535; cpu++) {
+ if ((u16) cpu == boot_cpu_addr)
+ continue;
+ __cpu_logical_map[1] = (__u16) cpu;
+ if (signal_processor(1, sigp_sense) == sigp_not_operational)
+ continue;
+ if (cpu_num >= NR_CPUS) {
+ printk("WARNING: Registers for cpu %i are not "
+ "saved, since dump kernel was compiled with"
+ "NR_CPUS=%i!\n", cpu_num, NR_CPUS);
+ continue;
+ }
+ zfcpdump_save_areas[cpu_num] =
+ alloc_bootmem(sizeof(union save_area));
+ while (1) {
+ rc = signal_processor(1, sigp_stop_and_store_status);
+ if (rc != sigp_busy)
+ break;
+ cpu_relax();
+ }
+ memcpy(zfcpdump_save_areas[cpu_num],
+ (void *)(unsigned long) store_prefix() +
+ SAVE_AREA_BASE, SAVE_AREA_SIZE);
+#ifdef __s390x__
+ /* copy original prefix register */
+ zfcpdump_save_areas[cpu_num]->s390x.pref_reg =
+ zfcpdump_prefix_array[cpu_num];
+#endif
+ cpu_num++;
+ }
+}
+
+union save_area *zfcpdump_save_areas[NR_CPUS + 1];
+EXPORT_SYMBOL_GPL(zfcpdump_save_areas);
+
+#else
+#define smp_get_save_areas() do { } while (0)
+#endif
+
/*
* Lets check how many CPUs we have.
*/
@@ -589,6 +650,7 @@ void __init smp_setup_cpu_possible_map(void)
{
unsigned int phy_cpus, pos_cpus, cpu;
+ smp_get_save_areas();
phy_cpus = smp_count_cpus();
pos_cpus = min(phy_cpus + additional_cpus, (unsigned int) NR_CPUS);