summaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--Documentation/s390/zfcpdump.txt87
-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
-rw-r--r--drivers/s390/char/Makefile3
-rw-r--r--drivers/s390/char/sclp.h2
-rw-r--r--drivers/s390/char/sclp_sdias.c255
-rw-r--r--drivers/s390/char/zcore.c651
-rw-r--r--include/asm-s390/ipl.h32
-rw-r--r--include/asm-s390/lowcore.h46
-rw-r--r--include/asm-s390/sclp.h2
-rw-r--r--include/asm-s390/setup.h2
-rw-r--r--include/asm-s390/smp.h1
17 files changed, 1411 insertions, 86 deletions
diff --git a/Documentation/s390/zfcpdump.txt b/Documentation/s390/zfcpdump.txt
new file mode 100644
index 000000000000..cf45d27c4608
--- /dev/null
+++ b/Documentation/s390/zfcpdump.txt
@@ -0,0 +1,87 @@
+s390 SCSI dump tool (zfcpdump)
+
+System z machines (z900 or higher) provide hardware support for creating system
+dumps on SCSI disks. The dump process is initiated by booting a dump tool, which
+has to create a dump of the current (probably crashed) Linux image. In order to
+not overwrite memory of the crashed Linux with data of the dump tool, the
+hardware saves some memory plus the register sets of the boot cpu before the
+dump tool is loaded. There exists an SCLP hardware interface to obtain the saved
+memory afterwards. Currently 32 MB are saved.
+
+This zfcpdump implementation consists of a Linux dump kernel together with
+a userspace dump tool, which are loaded together into the saved memory region
+below 32 MB. zfcpdump is installed on a SCSI disk using zipl (as contained in
+the s390-tools package) to make the device bootable. The operator of a Linux
+system can then trigger a SCSI dump by booting the SCSI disk, where zfcpdump
+resides on.
+
+The kernel part of zfcpdump is implemented as a debugfs file under "zcore/mem",
+which exports memory and registers of the crashed Linux in an s390
+standalone dump format. It can be used in the same way as e.g. /dev/mem. The
+dump format defines a 4K header followed by plain uncompressed memory. The
+register sets are stored in the prefix pages of the respective cpus. To build a
+dump enabled kernel with the zcore driver, the kernel config option
+CONFIG_ZFCPDUMP has to be set. When reading from "zcore/mem", the part of
+memory, which has been saved by hardware is read by the driver via the SCLP
+hardware interface. The second part is just copied from the non overwritten real
+memory.
+
+The userspace application of zfcpdump can reside e.g. in an intitramfs or an
+initrd. It reads from zcore/mem and writes the system dump to a file on a
+SCSI disk.
+
+To build a zfcpdump kernel use the following settings in your kernel
+configuration:
+ * CONFIG_ZFCPDUMP=y
+ * Enable ZFCP driver
+ * Enable SCSI driver
+ * Enable ext2 and ext3 filesystems
+ * Disable as many features as possible to keep the kernel small.
+ E.g. network support is not needed at all.
+
+To use the zfcpdump userspace application in an initramfs you have to do the
+following:
+
+ * Copy the zfcpdump executable somewhere into your Linux tree.
+ E.g. to "arch/s390/boot/zfcpdump. If you do not want to include
+ shared libraries, compile the tool with the "-static" gcc option.
+ * If you want to include e2fsck, add it to your source tree, too. The zfcpdump
+ application attempts to start /sbin/e2fsck from the ramdisk.
+ * Use an initramfs config file like the following:
+
+ dir /dev 755 0 0
+ nod /dev/console 644 0 0 c 5 1
+ nod /dev/null 644 0 0 c 1 3
+ nod /dev/sda1 644 0 0 b 8 1
+ nod /dev/sda2 644 0 0 b 8 2
+ nod /dev/sda3 644 0 0 b 8 3
+ nod /dev/sda4 644 0 0 b 8 4
+ nod /dev/sda5 644 0 0 b 8 5
+ nod /dev/sda6 644 0 0 b 8 6
+ nod /dev/sda7 644 0 0 b 8 7
+ nod /dev/sda8 644 0 0 b 8 8
+ nod /dev/sda9 644 0 0 b 8 9
+ nod /dev/sda10 644 0 0 b 8 10
+ nod /dev/sda11 644 0 0 b 8 11
+ nod /dev/sda12 644 0 0 b 8 12
+ nod /dev/sda13 644 0 0 b 8 13
+ nod /dev/sda14 644 0 0 b 8 14
+ nod /dev/sda15 644 0 0 b 8 15
+ file /init arch/s390/boot/zfcpdump 755 0 0
+ file /sbin/e2fsck arch/s390/boot/e2fsck 755 0 0
+ dir /proc 755 0 0
+ dir /sys 755 0 0
+ dir /mnt 755 0 0
+ dir /sbin 755 0 0
+
+ * Issue "make image" to build the zfcpdump image with initramfs.
+
+In a Linux distribution the zfcpdump enabled kernel image must be copied to
+/usr/share/zfcpdump/zfcpdump.image, where the s390 zipl tool is looking for the
+dump kernel when preparing a SCSI dump disk.
+
+If you use a ramdisk copy it to "/usr/share/zfcpdump/zfcpdump.rd".
+
+For more information on how to use zfcpdump refer to the s390 'Using the Dump
+Tools book', which is available from
+http://www.ibm.com/developerworks/linux/linux390.
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 0f9517bc8e70..e6ec418093e5 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 ece5adc05606..68441e0e74b6 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 80046d9e2a3b..0e4da8a7d826 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 37010709fe68..a87b1976d409 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 a83cf1fdd8f5..06833ac2b115 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 863c8d08c026..3dfd0985861c 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 97764f710bb7..7c0143fdf710 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);
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile
index 5fd581c22db3..a0f6db21855a 100644
--- a/drivers/s390/char/Makefile
+++ b/drivers/s390/char/Makefile
@@ -29,3 +29,6 @@ obj-$(CONFIG_S390_TAPE_34XX) += tape_34xx.o
obj-$(CONFIG_S390_TAPE_3590) += tape_3590.o
obj-$(CONFIG_MONREADER) += monreader.o
obj-$(CONFIG_MONWRITER) += monwriter.o
+
+zcore_mod-objs := sclp_sdias.o zcore.o
+obj-$(CONFIG_ZFCPDUMP) += zcore_mod.o
diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h
index 7d29ab45a6ed..6402e943436f 100644
--- a/drivers/s390/char/sclp.h
+++ b/drivers/s390/char/sclp.h
@@ -27,6 +27,7 @@
#define EvTyp_CntlProgIdent 0x0B
#define EvTyp_SigQuiesce 0x1D
#define EvTyp_VT220Msg 0x1A
+#define EvTyp_SDIAS 0x1C
#define EvTyp_OpCmd_Mask 0x80000000
#define EvTyp_Msg_Mask 0x40000000
@@ -36,6 +37,7 @@
#define EvTyp_CtlProgIdent_Mask 0x00200000
#define EvTyp_SigQuiesce_Mask 0x00000008
#define EvTyp_VT220Msg_Mask 0x00000040
+#define EvTyp_SDIAS_Mask 0x00000010
#define GnrlMsgFlgs_DOM 0x8000
#define GnrlMsgFlgs_SndAlrm 0x4000
diff --git a/drivers/s390/char/sclp_sdias.c b/drivers/s390/char/sclp_sdias.c
new file mode 100644
index 000000000000..06a4d0897232
--- /dev/null
+++ b/drivers/s390/char/sclp_sdias.c
@@ -0,0 +1,255 @@
+/*
+ * Sclp "store data in absolut storage"
+ *
+ * Copyright IBM Corp. 2003,2007
+ * Author(s): Michael Holzheu
+ */
+
+#include <linux/sched.h>
+#include <asm/sclp.h>
+#include <asm/debug.h>
+#include <asm/ipl.h>
+#include "sclp.h"
+#include "sclp_rw.h"
+
+#define TRACE(x...) debug_sprintf_event(sdias_dbf, 1, x)
+#define ERROR_MSG(x...) printk ( KERN_ALERT "SDIAS: " x )
+
+#define SDIAS_RETRIES 300
+#define SDIAS_SLEEP_TICKS 50
+
+#define EQ_STORE_DATA 0x0
+#define EQ_SIZE 0x1
+#define DI_FCP_DUMP 0x0
+#define ASA_SIZE_32 0x0
+#define ASA_SIZE_64 0x1
+#define EVSTATE_ALL_STORED 0x0
+#define EVSTATE_NO_DATA 0x3
+#define EVSTATE_PART_STORED 0x10
+
+static struct debug_info *sdias_dbf;
+
+static struct sclp_register sclp_sdias_register = {
+ .send_mask = EvTyp_SDIAS_Mask,
+};
+
+struct sdias_evbuf {
+ struct evbuf_header hdr;
+ u8 event_qual;
+ u8 data_id;
+ u64 reserved2;
+ u32 event_id;
+ u16 reserved3;
+ u8 asa_size;
+ u8 event_status;
+ u32 reserved4;
+ u32 blk_cnt;
+ u64 asa;
+ u32 reserved5;
+ u32 fbn;
+ u32 reserved6;
+ u32 lbn;
+ u16 reserved7;
+ u16 dbs;
+} __attribute__((packed));
+
+struct sdias_sccb {
+ struct sccb_header hdr;
+ struct sdias_evbuf evbuf;
+} __attribute__((packed));
+
+static struct sdias_sccb sccb __attribute__((aligned(4096)));
+
+static int sclp_req_done;
+static wait_queue_head_t sdias_wq;
+static DEFINE_MUTEX(sdias_mutex);
+
+static void sdias_callback(struct sclp_req *request, void *data)
+{
+ struct sdias_sccb *sccb;
+
+ sccb = (struct sdias_sccb *) request->sccb;
+ sclp_req_done = 1;
+ wake_up(&sdias_wq); /* Inform caller, that request is complete */
+ TRACE("callback done\n");