Kernel part of the s390 SCSI dumper: zcore character device driver.

Acked-by: Martin Schwidefsky <[EMAIL PROTECTED]>
Signed-off-by: Michael Holzheu <[EMAIL PROTECTED]>

---
 Documentation/s390/zfcpdump.txt |   41 +
 arch/s390/Kconfig               |    7 
 arch/s390/kernel/early.c        |    3 
 arch/s390/kernel/head64.S       |   43 +
 arch/s390/kernel/ipl.c          |   24 -
 arch/s390/kernel/setup.c        |    3 
 arch/s390/kernel/smp.c          |    1 
 drivers/s390/char/Makefile      |    2 
 drivers/s390/char/sclp.h        |    2 
 drivers/s390/char/sclp_sdias.c  |  248 +++++++++++
 drivers/s390/char/zcore.c       |  885 ++++++++++++++++++++++++++++++++++++++++
 drivers/s390/cio/cio.c          |    1 
 include/asm-s390/ipl.h          |  116 +++++
 include/asm-s390/processor.h    |    5 
 include/asm-s390/sclp.h         |    2 
 include/asm-s390/setup.h        |   75 ---
 16 files changed, 1360 insertions(+), 98 deletions(-)

Index: git-linux-2.6/Documentation/s390/zfcpdump.txt
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ git-linux-2.6/Documentation/s390/zfcpdump.txt       2007-02-21 
11:00:15.000000000 +0100
@@ -0,0 +1,41 @@
+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 small 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 character device driver named
+"zcore", 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 different cpus. To build an
+dump enabled kernel with the zcore driver, the kernel config option
+"S390_ZFCPDUMP" has to be set. When reading from /dev/zcore, the first part of
+memory, which has been saved by the 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 resides in an intitramfs. It reads from
+/dev/zcore and writes the system dump to a file on a SCSI disk.
+
+To build zfcpdump you have to do the following:
+- Use /arch/s390/zfcpdump/defconfig.zfcpdump as kernel configuration file, 
which
+  enables the S390_ZFCPDUMP option and sets all other required kernel options.
+- Issue "make zfcpdump" from the toplevel directory of the linux tree to
+  build the userspace application. Note, that the zfcpdump application has a
+  dependency on glibc and libz.
+- Issue "make image" to build the zfcpdump image with initramfs.
+
+The zfcpdump enabled kernel image must be copied to
+/usr/share/zfcpdump/zfcpdump.image, where the zipl tool is looking for the dump
+kernel, when preparing a SCSI dump disk.
Index: git-linux-2.6/arch/s390/Kconfig
===================================================================
--- git-linux-2.6.orig/arch/s390/Kconfig        2007-02-21 10:22:03.000000000 
+0100
+++ git-linux-2.6/arch/s390/Kconfig     2007-02-21 11:00:15.000000000 +0100
@@ -512,6 +512,13 @@ config KEXEC
          current kernel, and to start another kernel.  It is like a reboot
          but is independent of hardware/microcode support.
 
+config S390_ZFCPDUMP
+       bool "zfcp dump kernel"
+       default n
+       help
+         Select this option if you want to build an zfcp dump enabled kernel.
+         Do NOT select this option for normal kernels!
+
 endmenu
 
 source "net/Kconfig"
Index: git-linux-2.6/arch/s390/kernel/early.c
===================================================================
--- git-linux-2.6.orig/arch/s390/kernel/early.c 2007-02-21 10:22:04.000000000 
+0100
+++ git-linux-2.6/arch/s390/kernel/early.c      2007-02-21 10:56:03.000000000 
+0100
@@ -14,6 +14,7 @@
 #include <linux/module.h>
 #include <linux/pfn.h>
 #include <linux/uaccess.h>
+#include <asm/ipl.h>
 #include <asm/lowcore.h>
 #include <asm/processor.h>
 #include <asm/sections.h>
@@ -129,7 +130,7 @@ static noinline __init void detect_machi
 {
        struct cpuinfo_S390 *cpuinfo = &S390_lowcore.cpu_data;
 
-       asm volatile("stidp %0" : "=m" (S390_lowcore.cpu_data.cpu_id));
+       get_cpu_id(&S390_lowcore.cpu_data.cpu_id);
 
        /* Running under z/VM ? */
        if (cpuinfo->cpu_id.version == 0xff)
Index: git-linux-2.6/arch/s390/kernel/head64.S
===================================================================
--- git-linux-2.6.orig/arch/s390/kernel/head64.S        2007-02-21 
10:22:04.000000000 +0100
+++ git-linux-2.6/arch/s390/kernel/head64.S     2007-02-21 11:00:15.000000000 
+0100
@@ -39,7 +39,42 @@ startup_continue:
        basr    %r13,0                  # get base
 .LPG1: sll     %r13,1                  # remove high order bit
        srl     %r13,1
+
+#ifdef CONFIG_S390_ZFCPDUMP
+
+       # store all prefix registers:
+
+       la      %r7,0                   # base register for 0 page
+       la      %r8,0                   # first cpu
+       l       %r11,.Lpref_arr_ptr-.LPG1(%r13) # address of prefix array
+       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 for dump
+#else
        lhi     %r1,1                   # mode 1 = esame
+#endif /* CONFIG_S390_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
@@ -151,6 +186,14 @@ startup_continue:
 .L4malign:.quad 0xffffffffffc00000
 .Lscan2g:.quad 0x80000000 + 0x20000 - 8        # 2GB + 128K - 8
 .Lnop: .long   0x07000700
+#ifdef CONFIG_S390_ZFCPDUMP
+.Lcurrent_cpu:
+       .long 0x0
+.Llast_cpu:
+       .long 0x0000ffff
+.Lpref_arr_ptr:
+       .long dump_prefix_array
+#endif /* CONFIG_S390_ZFCPDUMP */
 .Lparmaddr:
        .quad   PARMAREA
 
Index: git-linux-2.6/arch/s390/kernel/ipl.c
===================================================================
--- git-linux-2.6.orig/arch/s390/kernel/ipl.c   2007-02-21 10:22:04.000000000 
+0100
+++ git-linux-2.6/arch/s390/kernel/ipl.c        2007-02-21 10:54:51.000000000 
+0100
@@ -14,6 +14,7 @@
 #include <linux/delay.h>
 #include <linux/reboot.h>
 #include <linux/ctype.h>
+#include <asm/ipl.h>
 #include <asm/smp.h>
 #include <asm/setup.h>
 #include <asm/cpcmd.h>
@@ -94,27 +95,6 @@ static char *shutdown_action_str(enum sh
        }
 }
 
-enum diag308_subcode  {
-       DIAG308_IPL   = 3,
-       DIAG308_DUMP  = 4,
-       DIAG308_SET   = 5,
-       DIAG308_STORE = 6,
-};
-
-enum diag308_ipl_type {
-       DIAG308_IPL_TYPE_FCP = 0,
-       DIAG308_IPL_TYPE_CCW = 2,
-};
-
-enum diag308_opt {
-       DIAG308_IPL_OPT_IPL  = 0x10,
-       DIAG308_IPL_OPT_DUMP = 0x20,
-};
-
-enum diag308_rc {
-       DIAG308_RC_OK = 1,
-};
-
 static int diag308_set_works = 0;
 
 static int reipl_capabilities = IPL_TYPE_UNKNOWN;
@@ -134,7 +114,7 @@ static struct ipl_parameter_block *dump_
 
 static enum shutdown_action on_panic_action = SHUTDOWN_STOP;
 
-static int diag308(unsigned long subcode, void *addr)
+int diag308(unsigned long subcode, void *addr)
 {
        register unsigned long _addr asm("0") = (unsigned long) addr;
        register unsigned long _rc asm("1") = 0;
Index: git-linux-2.6/arch/s390/kernel/setup.c
===================================================================
--- git-linux-2.6.orig/arch/s390/kernel/setup.c 2007-02-21 10:22:04.000000000 
+0100
+++ git-linux-2.6/arch/s390/kernel/setup.c      2007-02-21 10:56:03.000000000 
+0100
@@ -41,6 +41,7 @@
 #include <linux/ctype.h>
 #include <linux/reboot.h>
 
+#include <asm/ipl.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
 #include <asm/smp.h>
@@ -106,7 +107,7 @@ void __devinit cpu_init (void)
         /*
          * Store processor id in lowcore (used e.g. in timer_interrupt)
          */
-       asm volatile("stidp %0": "=m" (S390_lowcore.cpu_data.cpu_id));
+       get_cpu_id(&S390_lowcore.cpu_data.cpu_id);
         S390_lowcore.cpu_data.cpu_addr = addr;
 
         /*
Index: git-linux-2.6/arch/s390/kernel/smp.c
===================================================================
--- git-linux-2.6.orig/arch/s390/kernel/smp.c   2007-02-21 10:22:04.000000000 
+0100
+++ git-linux-2.6/arch/s390/kernel/smp.c        2007-02-21 10:54:51.000000000 
+0100
@@ -31,6 +31,7 @@
 #include <linux/interrupt.h>
 #include <linux/cpu.h>
 #include <linux/timex.h>
+#include <asm/ipl.h>
 #include <asm/setup.h>
 #include <asm/sigp.h>
 #include <asm/pgalloc.h>
Index: git-linux-2.6/drivers/s390/char/Makefile
===================================================================
--- git-linux-2.6.orig/drivers/s390/char/Makefile       2007-02-21 
10:22:40.000000000 +0100
+++ git-linux-2.6/drivers/s390/char/Makefile    2007-02-21 11:00:15.000000000 
+0100
@@ -29,3 +29,5 @@ obj-$(CONFIG_S390_TAPE_34XX) += tape_34x
 obj-$(CONFIG_S390_TAPE_3590) += tape_3590.o
 obj-$(CONFIG_MONREADER) += monreader.o
 obj-$(CONFIG_MONWRITER) += monwriter.o
+
+obj-$(CONFIG_S390_ZFCPDUMP) += zcore.o sclp_sdias.o
Index: git-linux-2.6/drivers/s390/char/sclp.h
===================================================================
--- git-linux-2.6.orig/drivers/s390/char/sclp.h 2007-02-21 10:22:40.000000000 
+0100
+++ git-linux-2.6/drivers/s390/char/sclp.h      2007-02-21 11:01:21.000000000 
+0100
@@ -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
Index: git-linux-2.6/drivers/s390/char/sclp_sdias.c
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ git-linux-2.6/drivers/s390/char/sclp_sdias.c        2007-02-21 
11:02:29.000000000 +0100
@@ -0,0 +1,248 @@
+/*
+ * 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 "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");
+}
+
+static int sdias_sclp_send(struct sclp_req *req)
+{
+       int retries;
+       int rc;
+
+       for (retries = SDIAS_RETRIES; retries; retries--) {
+               sclp_req_done = 0;
+               TRACE("add request\n");
+               rc = sclp_add_request(req);
+               if (rc) {
+                       /* not initiated, wait some time and retry */
+                       set_current_state(TASK_INTERRUPTIBLE);
+                       TRACE("add request failed: rc = %i\n",rc);
+                       schedule_timeout(SDIAS_SLEEP_TICKS);
+                       continue;
+               }
+               /* initiated, wait for completion of service call */
+               wait_event(sdias_wq, (sclp_req_done == 1));
+               if (req->status == SCLP_REQ_FAILED) {
+                       TRACE("sclp request failed\n");
+                       rc = -EIO;
+                       continue;
+               }
+               TRACE("request done\n");
+               break;
+       }
+       return rc;
+}
+
+static int sdias_init(void)
+{
+       int rc;
+
+       sdias_dbf = debug_register("dump_sdias", 4, 1, 4 * sizeof(long));
+       debug_register_view(sdias_dbf, &debug_sprintf_view);
+       debug_set_level(sdias_dbf, 6);
+       rc = sclp_register(&sclp_sdias_register);
+       if (rc) {
+               ERROR_MSG("sclp register failed\n");
+               return rc;
+       }
+       init_waitqueue_head(&sdias_wq);
+       TRACE("init done\n");
+       return 0;
+}
+
+/*
+ * Get number of blocks (4K) available in the HSA
+ */
+int sclp_sdias_blk_count(void)
+{
+       struct sclp_req request;
+       int rc;
+
+       mutex_lock(&sdias_mutex);
+
+       memset(&sccb, 0, sizeof(sccb));
+       memset(&request, 0, sizeof(request));
+
+       sccb.hdr.length = sizeof(sccb);
+       sccb.evbuf.hdr.length = sizeof(struct sdias_evbuf);
+       sccb.evbuf.hdr.type = EvTyp_SDIAS;
+       sccb.evbuf.event_qual = EQ_SIZE;
+       sccb.evbuf.data_id = DI_FCP_DUMP;
+       sccb.evbuf.event_id = 4712;
+       sccb.evbuf.dbs = 1;
+
+       request.sccb = &sccb;
+       request.command = SCLP_CMDW_WRITE_EVENT_DATA;
+       request.status = SCLP_REQ_FILLED;
+       request.callback = sdias_callback;
+
+       rc = sdias_sclp_send(&request);
+       if (rc) {
+               ERROR_MSG("sclp_send failed for get_nr_blocks\n");
+               goto out;
+       }
+       if (sccb.hdr.response_code != 0x0020) {
+               TRACE("send failed: %x\n", sccb.hdr.response_code);
+               rc = -EIO;
+               goto out;
+       }
+
+       switch (sccb.evbuf.event_status) {
+               case 0:
+                       rc = sccb.evbuf.blk_cnt;
+                       break;
+               default:
+                       ERROR_MSG("SCLP error: %x\n", sccb.evbuf.event_status);
+                       rc = -EIO;
+                       goto out;
+       }
+       TRACE("%i blocks\n", rc);
+out:
+       mutex_unlock(&sdias_mutex);
+       return rc;
+}
+
+/*
+ * Copy from HSA to absolute storage (not reentrant):
+ *
+ * @dest     : Address of buffer where data should be copied
+ * @start_blk: Start Block (beginning with 1)
+ * @nr_blks  : Number of 4K blocks to copy
+ *
+ * Return Value: 0 : Requested 'number' of blocks of data copied
+ *              <0: ERROR - negative event status
+ */
+int sclp_sdias_copy(void *dest, int start_blk, int nr_blks)
+{
+       struct sclp_req request;
+       int rc;
+
+       mutex_lock(&sdias_mutex);
+
+       memset(&sccb, 0, sizeof(sccb));
+       memset(&request, 0, sizeof(request));
+
+       sccb.hdr.length = sizeof(sccb);
+       sccb.evbuf.hdr.length = sizeof(struct sdias_evbuf);
+       sccb.evbuf.hdr.type = EvTyp_SDIAS;
+       sccb.evbuf.hdr.flags = 0;
+       sccb.evbuf.event_qual = EQ_STORE_DATA;
+       sccb.evbuf.data_id = DI_FCP_DUMP;
+       sccb.evbuf.event_id = 4712;
+#ifdef __s390x__
+       sccb.evbuf.asa_size = ASA_SIZE_64;
+#else
+       sccb.evbuf.asa_size = ASA_SIZE_32;
+#endif
+       sccb.evbuf.event_status = 0;
+       sccb.evbuf.blk_cnt = nr_blks;
+       sccb.evbuf.asa = (unsigned long)dest;
+       sccb.evbuf.fbn = start_blk;
+       sccb.evbuf.lbn = 0;
+       sccb.evbuf.dbs = 1;
+
+       request.sccb     = &sccb;
+       request.command  = SCLP_CMDW_WRITE_EVENT_DATA;
+       request.status   = SCLP_REQ_FILLED;
+       request.callback = sdias_callback;
+
+       rc = sdias_sclp_send(&request);
+       if (rc) {
+               ERROR_MSG("sclp_send failed: %x\n", rc);
+               goto out;
+       }
+       if (sccb.hdr.response_code != 0x0020) {
+               TRACE("copy failed: %x\n", sccb.hdr.response_code);
+               rc = -EIO;
+               goto out;
+       }
+
+       switch (sccb.evbuf.event_status) {
+               case EVSTATE_ALL_STORED:
+                       TRACE("all stored\n");
+               case EVSTATE_PART_STORED:
+                       TRACE("part stored: %i\n", sccb.evbuf.blk_cnt);
+                       break;
+               case EVSTATE_NO_DATA:
+                       TRACE("no data\n");
+               default:
+                       ERROR_MSG("Error from SCLP while copying hsa. "
+                                 "Event status = %x\n",
+                               sccb.evbuf.event_status);
+                       rc = -EIO;
+       }
+out:
+       mutex_unlock(&sdias_mutex);
+       return rc;
+}
+
+device_initcall(sdias_init);
Index: git-linux-2.6/drivers/s390/char/zcore.c
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ git-linux-2.6/drivers/s390/char/zcore.c     2007-02-21 11:02:16.000000000 
+0100
@@ -0,0 +1,885 @@
+/*
+ * zcore character device to export memory content and register sets for
+ * creating system dumps on SCSI disks. /dev/zcore shows the same dump format
+ * as s390 standalone dumps.
+ *
+ * Copyright IBM Corp. 2003,2007
+ * Author(s): Michael Holzheu
+ */
+
+#include <linux/init.h>
+#include <linux/compile.h>
+#include <linux/miscdevice.h>
+#include <asm/ipl.h>
+#include <asm/sclp.h>
+#include <asm/setup.h>
+#include <asm/sigp.h>
+#include <asm/uaccess.h>
+#include <asm/debug.h>
+#include <asm/processor.h>
+#include <asm/irqflags.h>
+
+#define HSA_SIZE       (32<<20) /* 32MB */
+
+#define SA_BASE_S390X  4608
+#define SA_BASE_S390   212
+
+#define DUMP_LOG_LEVEL 2
+
+#define TRACE(x...) debug_sprintf_event(zcore_dbf, 1, x)
+#define MSG(x...) printk( KERN_ALERT x )
+#define ERROR_MSG(x...) printk ( KERN_ALERT "DUMP: " x )
+
+#define FCP_DATA (&IPL_PARMBLOCK_START->ipl_info.fcp)
+
+#define TO_USER                0
+#define TO_KERNEL      1
+
+enum arch_id {
+       ARCH_S390       = 0,
+       ARCH_S390X      = 1,
+};
+
+/* lowcore data */
+
+union cpu_info {
+       struct {
+               u32     ext_save;
+               u64     timer;
+               u64     clk_cmp;
+               u8      pad1[24];
+               u8      psw[8];
+               u32     pref_reg;
+               u8      pad2[20];
+               u32     acc_regs[16];
+               u64     fp_regs[4];
+               u32     gp_regs[16];
+               u32     ctrl_regs[16];
+       }  __attribute__((packed)) s390;
+
+       struct {
+               u64     fp_regs[16];
+               u64     gp_regs[16];
+               u8      psw[16];
+               u8      pad1[8];
+               u32     pref_reg;
+               u32     fp_ctrl_reg;
+               u8      pad2[4];
+               u32     tod_reg;
+               u64     timer;
+               u64     clk_cmp;
+               u8      pad3[8];
+               u32     acc_regs[16];
+               u64     ctrl_regs[16];
+       }  __attribute__((packed)) s390x;
+};
+
+/* dump system info */
+
+struct sys_info {
+       enum arch_id    arch;
+       unsigned long   sa_base;
+       u32             sa_size;
+       int             cpu_count;
+       union cpu_info  *cpu_info;
+       int             cpu_map[NR_CPUS];
+       unsigned long   mem_size;
+       union cpu_info  lc_mask;
+};
+
+/*
+ * dump_prefix_array holds prefix registers for the following scenario:
+ * 64 bit system dumper and 32 bit kernel which is dumped. The array is filled
+ * in s390x/kernel/head*.S and is 0 terminated. The boot cpu is not
+ * contained in the array. The prefix register for a logical cpu x (according
+ * to cpu_logical_map can be found with dump_prefix_array[x-1]
+ */
+unsigned int dump_prefix_array[NR_CPUS] __attribute__((__section__(".data")));
+static struct sys_info sys_info;
+static struct debug_info *zcore_dbf;
+static int hsa_available;
+
+/*
+ * Copy memory from HSA to kernel or user memory (not reentrant):
+ *
+ * @dest:  Kernel or user buffer where memory should be copied to
+ * @src:   Start address within HSA where data should be copied
+ * @count: Size of buffer, which should be copied
+ * @mode:  Either TO_KERNEL or TO_USER
+ */
+static int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode)
+{
+       int offs, blk_num;
+       static char buf[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
+
+       if (count == 0)
+               return 0;
+
+       /* copy first block */
+       offs = 0;
+       if ((src % PAGE_SIZE) != 0) {
+               blk_num = src / PAGE_SIZE + 2;
+               if (sclp_sdias_copy(buf, blk_num, 1)) {
+                       TRACE("sclp_sdias_copy() failed\n");
+                       return -EIO;
+               }
+               offs = min((PAGE_SIZE - (src % PAGE_SIZE)), count);
+               if (mode == TO_USER) {
+                       if (copy_to_user((__force __user void*) dest,
+                                        buf + (src % PAGE_SIZE), offs))
+                               return -EFAULT;
+               } else
+                       memcpy(dest, buf + (src % PAGE_SIZE), offs);
+       }
+       if (offs == count)
+               goto out;
+
+       /* copy middle */
+       for (; (offs + PAGE_SIZE) <= count; offs += PAGE_SIZE) {
+               blk_num = (src + offs) / PAGE_SIZE + 2;
+               if (sclp_sdias_copy(buf, blk_num, 1)) {
+                       TRACE("sclp_sdias_copy() failed\n");
+                       return -EIO;
+               }
+               if (mode == TO_USER) {
+                       if (copy_to_user((__force __user void*) dest + offs,
+                                        buf, PAGE_SIZE))
+                               return -EFAULT;
+               } else
+                       memcpy(dest + offs, buf, PAGE_SIZE);
+       }
+       if (offs == count)
+               goto out;
+
+       /* copy last block */
+       blk_num = (src + offs) / PAGE_SIZE + 2;
+       if (sclp_sdias_copy(buf, blk_num, 1)) {
+               TRACE("sclp_sdias_copy() failed\n");
+               return -EIO;
+       }
+       if (mode == TO_USER) {
+               if (copy_to_user((__force __user void*) dest + offs, buf,
+                                PAGE_SIZE))
+                       return -EFAULT;
+       } else
+               memcpy(dest + offs, buf, count - offs);
+out:
+       return 0;
+}
+
+static int memcpy_hsa_user(void __user *dest, unsigned long src, size_t count)
+{
+       return memcpy_hsa((void __force *) dest, src, count, TO_USER);
+}
+
+static int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count)
+{
+       return memcpy_hsa(dest, src, count, TO_KERNEL);
+}
+
+static int memcpy_real(void *dest, unsigned long src, size_t count)
+{
+       unsigned long flags;
+       int rc = -EFAULT;
+       register unsigned long _dest asm("2") = (unsigned long) dest;
+       register unsigned long _len1 asm("3") = (unsigned long) count;
+       register unsigned long _src  asm("4") = src;
+       register unsigned long _len2 asm("5") = (unsigned long) count;
+
+       if (count == 0)
+               return 0;
+       flags = __raw_local_irq_stnsm(0xf8); /* switch to real mode */
+       asm volatile (
+               "0:     mvcle   %1,%2,0x0\n"
+               "1:     jo      0b\n"
+               "       lhi     %0,0x0\n"
+               "2:\n"
+               EX_TABLE(1b,2b)
+               : "+d" (rc)
+               : "d" (_dest), "d" (_src), "d" (_len1), "d" (_len2)
+               : "cc", "memory");
+       __raw_local_irq_ssm(flags);
+
+       return rc;
+}
+
+static int memcpy_real_user(__user void *dest, unsigned long src, size_t count)
+{
+       static char buf[4096];
+       int offs = 0, size;
+
+       while (offs < count) {
+               size = min(sizeof(buf), count - offs);
+               if (memcpy_real(buf, src + offs, size))
+                       return -EFAULT;
+               if (copy_to_user(dest + offs, buf, size))
+                       return -EFAULT;
+               offs += size;
+       }
+       return 0;
+}
+
+/*
+ * Send a signal to a physical cpu number
+ */
+static sigp_ccode sigp(u16 cpu_addr, sigp_order_code order_code)
+{
+       register unsigned long reg1 asm ("1") = 0;
+       sigp_ccode ccode;
+
+       asm volatile(
+               "       sigp    %1,%2,0(%3)\n"
+               "       ipm     %0\n"
+               "       srl     %0,28\n"
+               : "=d" (ccode)
+               : "d" (reg1), "a" (cpu_addr), "a" (order_code)
+               : "cc" , "memory");
+       return ccode;
+}
+
+/*
+ * Count CPUs
+ */
+static int count_and_map_cpus(void)
+{
+       int cpu_count, rc, cpu;
+       u16 boot_cpu;
+
+       boot_cpu = __cpu_logical_map[0]; /* physical number of cpu */
+       sys_info.cpu_map[0] = boot_cpu;
+       cpu_count = 1;
+       TRACE("boot cpu : %x\n", boot_cpu);
+       for (cpu = 0; cpu <= 65535; cpu++) {
+               if (cpu == boot_cpu)
+                       continue;
+               rc = sigp(cpu, sigp_sense);
+               if (rc == sigp_not_operational)
+                       continue;
+               else {
+                       TRACE("detected cpu: %i (%x)\n", cpu, rc);
+                       sys_info.cpu_map[cpu_count] = cpu;
+                       cpu_count++;
+               }
+       }
+       return cpu_count;
+}
+
+/*
+ * Print 31 bit CPU info (Registers/PSWs) for tracing
+ */
+static void print_cpu_info_s390(union cpu_info *cpu_info)
+{
+       int i;
+
+       TRACE("psw   : %08x %08x\n",
+               *((int*) cpu_info->s390.psw),
+               *((int*) &(cpu_info->s390.psw[4])));
+       TRACE("prefix: %08x\n",cpu_info->s390.pref_reg);
+       TRACE("clk   : %016Lx\n", (long long) cpu_info->s390.timer);
+       TRACE("clkcmp: %016Lx\n", (long long) cpu_info->s390.clk_cmp);
+       TRACE("gpregs:\n");
+       for (i = 0; i < 16; i += 4) {
+               TRACE("%08x %08x %08x %08x\n",
+                       cpu_info->s390.gp_regs[i],
+                       cpu_info->s390.gp_regs[i+1],
+                       cpu_info->s390.gp_regs[i+2],
+                       cpu_info->s390.gp_regs[i+3]);
+       }
+       TRACE("accregs:\n");
+       for (i = 0; i < 16; i += 4) {
+               TRACE("%08x %08x %08x %08x\n",
+                       cpu_info->s390.acc_regs[i],
+                       cpu_info->s390.acc_regs[i+1],
+                       cpu_info->s390.acc_regs[i+2],
+                       cpu_info->s390.acc_regs[i+3]);
+       }
+       TRACE("ctrl_regs:\n");
+       for (i = 0; i < 16; i += 4) {
+               TRACE("%08x %08x %08x %08x\n",
+                       cpu_info->s390.ctrl_regs[i],
+                       cpu_info->s390.ctrl_regs[i+1],
+                       cpu_info->s390.ctrl_regs[i+2],
+                       cpu_info->s390.ctrl_regs[i+3]);
+       }
+       TRACE("fp_regs:\n");
+       for (i = 0; i < 4; i += 2) {
+               TRACE("%016Lx %016Lx\n",
+                       (long long) cpu_info->s390.fp_regs[i],
+                       (long long) cpu_info->s390.fp_regs[i+1]);
+       }
+}
+
+/*
+ * Print 64 bit CPU info (Registers/PSWs) for tracing
+ */
+static void print_cpu_info_s390x(union cpu_info *cpu_info)
+{
+       int i;
+
+       TRACE("psw: %016lx %016lx\n",
+               *((unsigned long*) cpu_info->s390x.psw),
+               *((unsigned long*) &(cpu_info->s390x.psw[8])));
+       TRACE("prefix: %08x\n", cpu_info->s390x.pref_reg);
+       TRACE("clk   : %016Lx\n", (long long) cpu_info->s390x.timer);
+       TRACE("clkcmp: %016Lx\n", (long long) cpu_info->s390x.clk_cmp);
+       TRACE("fpctrl: %04x\n",cpu_info->s390x.fp_ctrl_reg);
+       TRACE("todreg: %04x\n",cpu_info->s390x.tod_reg);
+       TRACE("gpregs:\n");
+       for (i = 0; i < 16; i += 2) {
+               TRACE("%016Lx %016Lx\n",
+                       (long long) cpu_info->s390x.gp_regs[i],
+                       (long long) cpu_info->s390x.gp_regs[i+1]);
+       }
+       TRACE("accregs:\n");
+       for (i = 0; i < 16; i += 4) {
+               TRACE("%08x %08x %08x %08x\n",
+                       cpu_info->s390x.acc_regs[i],
+                       cpu_info->s390x.acc_regs[i+1],
+                       cpu_info->s390x.acc_regs[i+2],
+                       cpu_info->s390x.acc_regs[i+3]);
+       }
+       TRACE("ctrl_regs:\n");
+       for (i = 0; i < 16; i+=2) {
+               TRACE("%016Lx %016Lx\n",
+                       (long long) cpu_info->s390x.ctrl_regs[i],
+                       (long long) cpu_info->s390x.ctrl_regs[i+1]);
+       }
+       TRACE("fp_regs:\n");
+       for (i = 0; i < 16; i+=2) {
+               TRACE("%016Lx %016Lx\n",
+                       (long long) cpu_info->s390x.fp_regs[i],
+                       (long long) cpu_info->s390x.fp_regs[i+1]);
+       }
+}
+
+/*
+ * Print global data
+ */
+static void print_glob_info(void)
+{
+       TRACE("Architecture  : %i\n", sys_info.arch);
+       TRACE("Number of cpus: %i\n", sys_info.cpu_count);
+       TRACE("Memory size   : %li\n", sys_info.mem_size);
+}
+
+#ifdef __s390x__
+/*
+ * Convert s390x (64 bit) cpu info to s390 (32 bit) cpu info
+ */
+static void s390x_to_s390_regs(union cpu_info *out, union cpu_info *in, int 
cpu)
+{
+       int i;
+
+       for (i = 0; i < 16; i++) {
+               out->s390.gp_regs[i] = in->s390x.gp_regs[i] & 
0x00000000ffffffff;
+               out->s390.acc_regs[i] = in->s390x.acc_regs[i];
+               out->s390.ctrl_regs[i] =
+                       in->s390x.ctrl_regs[i] & 0x00000000ffffffff;
+       }
+       /* locore for 31 bit has only space for fpregs 0,2,4,6 */
+       out->s390.fp_regs[0] = in->s390x.fp_regs[0];
+       out->s390.fp_regs[1] = in->s390x.fp_regs[2];
+       out->s390.fp_regs[2] = in->s390x.fp_regs[4];
+       out->s390.fp_regs[3] = in->s390x.fp_regs[6];
+       memcpy(&(out->s390.psw[0]), &(in->s390x.psw[0]), 4);
+       out->s390.psw[1] |= 0x8; /* set bit 12 */
+       memcpy(&(out->s390.psw[4]),&(in->s390x.psw[12]), 4);
+       out->s390.psw[4] |= 0x80; /* set (31bit) addressing bit */
+       out->s390.pref_reg = dump_prefix_array[cpu-1];
+       out->s390.timer = in->s390x.timer;
+       out->s390.clk_cmp = in->s390x.clk_cmp;
+}
+
+#endif
+
+/*
+ * stop a cpu and store status of the cpu
+ */
+static void stop_and_store_status(int cpu)
+{
+       int ccode;
+
+       do {
+               ccode = sigp(sys_info.cpu_map[cpu], sigp_stop_and_store_status);
+       } while (ccode == sigp_busy);
+}
+
+/*
+ * Collect and return cpu info (registers/PSWs) for all existing CPUs
+ */
+static union cpu_info *get_cpu_info(int *count, enum arch_id arch)
+{
+       union cpu_info *info;
+       int i, cpu_count, sa_size;
+       unsigned long sa_base;
+
+       sa_base = sys_info.sa_base;
+       sa_size = sys_info.sa_size;
+
+       cpu_count = count_and_map_cpus();
+
+       info = kmalloc(cpu_count * sizeof(*info), GFP_KERNEL);
+       if (!info) {
+               ERROR_MSG("kmalloc failed: %s: %i\n",__FUNCTION__, __LINE__);
+               return NULL;
+       }
+
+       /* get first lowcore from hsa */
+
+       if (memcpy_hsa_kernel(info, sa_base, sa_size) < 0) {
+               ERROR_MSG("could not copy from HSA\n");
+               goto fail;
+       }
+       if (arch == ARCH_S390)
+               print_cpu_info_s390(&(info[0]));
+       else
+               print_cpu_info_s390x(&(info[0]));
+
+       /* now get the other lowcores with sigp store status */
+
+       for (i = 1; i < cpu_count; i++) {
+               TRACE("get cpu info for cpu: %i\n", i);
+               stop_and_store_status(i);
+               switch (arch) {
+#ifdef __s390x__
+               union cpu_info tmp_info;
+               case ARCH_S390:
+                       memcpy_real(&tmp_info, SA_BASE_S390X,
+                                       sizeof(tmp_info.s390x));
+                       s390x_to_s390_regs(&info[i], &tmp_info, i);
+#else
+               case ARCH_S390:
+                       memcpy_real(&info[i], sa_base, sa_size);
+#endif
+                       print_cpu_info_s390(&(info[i]));
+                       break;
+               case ARCH_S390X:
+                       memcpy_real(&info[i], sa_base, sa_size);
+                       print_cpu_info_s390x(&(info[i]));
+                       break;
+               default:
+                       ERROR_MSG("dump: unknown arch %x\n", sys_info.arch);
+                       goto fail;
+               }
+       }
+       *count = cpu_count;
+       return info;
+fail:
+       kfree(info);
+       return NULL;
+}
+
+void setup_dump(void)
+{
+       static char str[100];
+
+       printk("System Dumper (%s) starting...\n", UTS_MACHINE);
+       sprintf(str,
+               " root=/dev/ram0 rw mem=%iM maxcpus=1 cio_ignore=all,!0.0.%04x",
+               HSA_SIZE>>20, IPL_PARMBLOCK_START->ipl_info.fcp.devno);
+       strcat(COMMAND_LINE, str);
+       console_loglevel = DUMP_LOG_LEVEL;
+}
+
+/*
+ * zcore character device driver
+ */
+
+static DEFINE_MUTEX(zcore_mutex);
+
+#define DUMP_VERSION   0x3
+#define DUMP_MAGIC     0xa8190173618f23fdULL
+#define DUMP_ARCH_S390X        2
+#define DUMP_ARCH_S390 1
+#define HEADER_SIZE    4096
+
+/* dump header dumped according to s390 crash dump format */
+
+struct zcore_header {
+       u64 magic;
+       u32 version;
+       u32 header_size;
+       u32 dump_level;
+       u32 page_size;
+       u64 mem_size;
+       u64 mem_start;
+       u64 mem_end;
+       u32 num_pages;
+       u32 pad1;
+       u64 tod;
+       cpuid_t cpu_id;
+       u32 arch_id;
+       u32 build_arch;
+       char pad2[4016];
+} __attribute__((packed,__aligned__(16)));
+
+static struct zcore_header zcore_header = {
+       .magic          = DUMP_MAGIC,
+       .version        = DUMP_VERSION,
+       .header_size    = 4096,
+       .dump_level     = 0,
+       .page_size      = PAGE_SIZE,
+       .mem_start      = 0,
+#ifdef __s390x__
+       .build_arch     = DUMP_ARCH_S390X,
+#else
+       .build_arch     = DUMP_ARCH_S390,
+#endif
+};
+
+/*
+ * Copy lowcore info to buffer. Use map in order to copy only register parts.
+ *
+ * @buf:    User buffer
+ * @sa:     Pointer to save area
+ * @sa_off: Offset in save area to copy
+ * @len:    Number of bytes to copy
+ */
+static int copy_lc(void __user *buf, void *sa, int sa_off, int len)
+{
+       int i;
+       char *lc_mask = (char*)&sys_info.lc_mask;
+
+       for (i = 0; i < len; i++) {
+               if (!lc_mask[i + sa_off])
+                       continue;
+               if (copy_to_user(buf + i, sa + sa_off + i, 1))
+                       return -EFAULT;
+       }
+       return 0;
+}
+
+/*
+ * Copy lowcores info to memory, if necessary
+ *
+ * @buf:   User buffer
+ * @addr:  Start address of buffer in dump memory
+ * @count: Size of buffer
+ */
+static int zcore_add_lc(char __user *buf, unsigned long start, size_t count)
+{
+       unsigned long end;
+       int i;
+
+       if (count == 0)
+               return 0;
+
+       end = start + count;
+       for (i = 0; i < sys_info.cpu_count; i++) {
+               unsigned long cp_start, cp_end; /* copy range */
+               unsigned long sa_start, sa_end; /* save area range */
+               unsigned long prefix;
+               unsigned long sa_off, len, buf_off;
+
+               if (sys_info.arch == ARCH_S390)
+                       prefix = sys_info.cpu_info[i].s390.pref_reg;
+               else
+                       prefix = sys_info.cpu_info[i].s390x.pref_reg;
+
+               sa_start = prefix + sys_info.sa_base;
+               sa_end = prefix + sys_info.sa_base + sys_info.sa_size;
+
+               if (end < sa_start)
+                       continue;
+               if (start > sa_end)
+                       continue;
+               cp_start = max(start, sa_start);
+               cp_end = min(end, sa_end);
+
+               buf_off = cp_start - start;
+               sa_off = cp_start - sa_start;
+               len = cp_end - cp_start;
+
+               TRACE("copy_lc for: %lx\n", start);
+               if (copy_lc(buf + buf_off, &sys_info.cpu_info[i], sa_off, len))
+                       return -EFAULT;
+       }
+       return 0;
+}
+
+/*
+ * Read routine for zcore character device
+ * First 4K are dump header
+ * Next 32MB are HSA Memory
+ * Rest is read from absolute Memory
+ */
+static ssize_t zcore_read(struct file *file, char __user *buf, size_t count,
+                         loff_t *ppos)
+{
+       unsigned long mem_start; /* Start address in memory */
+       size_t mem_offs;          /* Offset in dump memory */
+       size_t hdr_count;         /* Size of header part of output buffer */
+       size_t size;
+       int rc;
+
+       mutex_lock(&zcore_mutex);
+
+       if (*ppos > (sys_info.mem_size + HEADER_SIZE)) {
+               rc = -EINVAL;
+               goto fail;
+       }
+
+       count = min(count, (size_t) (sys_info.mem_size + HEADER_SIZE - *ppos));
+
+       /* Copy dump header */
+       if (*ppos < HEADER_SIZE) {
+               size = min(count, (size_t) (HEADER_SIZE - *ppos));
+               if (copy_to_user(buf, &zcore_header + *ppos, size)) {
+                       rc = -EFAULT;
+                       goto fail;
+               }
+               hdr_count = size;
+               mem_start = 0;
+       } else {
+               hdr_count = 0;
+               mem_start = *ppos - HEADER_SIZE;
+       }
+
+       mem_offs = 0;
+
+       /* Copy from HSA data */
+       if (*ppos < (HSA_SIZE + HEADER_SIZE)) {
+               size = min((count - hdr_count), (size_t) (HSA_SIZE - 
mem_start));
+               rc = memcpy_hsa_user(buf + hdr_count, mem_start, size);
+               if (rc)
+                       goto fail;
+
+               mem_offs += size;
+       }
+
+       /* Copy from real mem */
+       size = count - mem_offs - hdr_count;
+       rc = memcpy_real_user(buf + hdr_count + mem_offs, mem_start + mem_offs,
+                             size);
+       if (rc)
+               goto fail;
+
+       /*
+        * Since s390 dump analysis tools like lcrash or crash
+        * expect register sets in the prefix pages of the cpus,
+        * we copy them into the read buffer, if necessary.
+        * buf + hdr_count: Start of memory part of output buffer
+        * mem_start: Start memory address to copy from
+        * count - hdr_count: Size of memory area to copy
+        */
+       if (zcore_add_lc(buf + hdr_count, mem_start, count - hdr_count)) {
+               rc = -EFAULT;
+               goto fail;
+       }
+       *ppos += count;
+fail:
+       mutex_unlock(&zcore_mutex);
+       return (rc < 0) ? rc : count;
+}
+
+static int zcore_open(struct inode *inode, struct file *filp)
+{
+       if (!hsa_available)
+               return -ENOSPC;
+       else
+               return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
+}
+
+static int zcore_release(struct inode *inode, struct file *filep)
+{
+       diag308(DIAG308_REL_HSA, NULL);
+       hsa_available = 0;
+       return 0;
+}
+
+static loff_t zcore_lseek(struct file *file, loff_t offset, int orig)
+{
+       loff_t rc;
+
+       mutex_lock(&zcore_mutex);
+       switch (orig) {
+       case 0:
+               file->f_pos = offset;
+               rc = file->f_pos;
+               break;
+       case 1:
+               file->f_pos += offset;
+               rc = file->f_pos;
+               break;
+       default:
+               rc = -EINVAL;
+       }
+       mutex_unlock(&zcore_mutex);
+       return rc;
+}
+
+static struct file_operations zcore_fops = {
+       .owner          = THIS_MODULE,
+       .llseek         = zcore_lseek,
+       .read           = zcore_read,
+       .open           = zcore_open,
+       .release        = zcore_release,
+};
+
+static struct miscdevice zcore_dev = {
+       .name   = "zcore",
+       .minor  = MISC_DYNAMIC_MINOR,
+       .fops   = &zcore_fops,
+};
+
+/*
+ * Init character device
+ */
+static int __init zcore_init(void)
+{
+       int rc;
+
+       if (sys_info.arch == ARCH_S390X)
+               zcore_header.arch_id = DUMP_ARCH_S390X;
+       else
+               zcore_header.arch_id = DUMP_ARCH_S390;
+       zcore_header.mem_size = sys_info.mem_size;
+       zcore_header.mem_end = sys_info.mem_size;
+       zcore_header.num_pages = sys_info.mem_size / PAGE_SIZE;
+       zcore_header.tod = get_clock();
+       get_cpu_id(&zcore_header.cpu_id);
+       rc = misc_register(&zcore_dev);
+       if (rc)
+               ERROR_MSG("Unable to register zcore device\n");
+       return rc;
+}
+
+static void __init set_s390_lc_mask(union cpu_info *map)
+{
+       memset(&map->s390.ext_save, 0xff, sizeof(map->s390.ext_save));
+       memset(&map->s390.timer, 0xff, sizeof(map->s390.timer));
+       memset(&map->s390.clk_cmp, 0xff, sizeof(map->s390.clk_cmp));
+       memset(&map->s390.psw, 0xff, sizeof(map->s390.psw));
+       memset(&map->s390.pref_reg, 0xff, sizeof(map->s390.pref_reg));
+       memset(&map->s390.acc_regs, 0xff, sizeof(map->s390.acc_regs));
+       memset(&map->s390.fp_regs, 0xff, sizeof(map->s390.fp_regs));
+       memset(&map->s390.gp_regs, 0xff, sizeof(map->s390.gp_regs));
+       memset(&map->s390.ctrl_regs, 0xff, sizeof(map->s390.ctrl_regs));
+}
+
+static void __init set_s390x_lc_mask(union cpu_info *map)
+{
+       memset(&map->s390x.fp_regs, 0xff, sizeof(map->s390x.fp_regs));
+       memset(&map->s390x.gp_regs, 0xff, sizeof(map->s390x.gp_regs));
+       memset(&map->s390x.psw, 0xff, sizeof(map->s390x.psw));
+       memset(&map->s390x.pref_reg, 0xff, sizeof(map->s390x.pref_reg));
+       memset(&map->s390x.fp_ctrl_reg, 0xff, sizeof(map->s390x.fp_ctrl_reg));
+       memset(&map->s390x.tod_reg, 0xff, sizeof(map->s390x.tod_reg));
+       memset(&map->s390x.timer, 0xff, sizeof(map->s390x.timer));
+       memset(&map->s390x.clk_cmp, 0xff, sizeof(map->s390x.clk_cmp));
+       memset(&map->s390x.acc_regs, 0xff, sizeof(map->s390x.acc_regs));
+       memset(&map->s390x.ctrl_regs, 0xff, sizeof(map->s390x.ctrl_regs));
+}
+
+/*
+ * Initialize dump globals for a given architecture
+ */
+
+static int __init sys_info_init(enum arch_id arch)
+{
+       switch (arch) {
+       case ARCH_S390X:
+               MSG("DETECTED 'S390X (64 bit) OS'\n");
+               sys_info.sa_base = SA_BASE_S390X;
+               sys_info.sa_size = sizeof(sys_info.cpu_info[0].s390x);
+               set_s390x_lc_mask(&sys_info.lc_mask);
+               break;
+       case ARCH_S390:
+               MSG("DETECTED 'S390 (32 bit) OS'\n");
+               sys_info.sa_base = SA_BASE_S390;
+               sys_info.sa_size = sizeof(sys_info.cpu_info[0].s390);
+               set_s390_lc_mask(&sys_info.lc_mask);
+               break;
+       default:
+               ERROR_MSG("unknown architecture 0x%x.\n",arch);
+               return -EINVAL;
+       }
+       sys_info.arch = arch;
+       sys_info.cpu_info = get_cpu_info(&sys_info.cpu_count, arch);
+       if (!sys_info.cpu_info) {
+               ERROR_MSG("get cpu info failed\n");
+               kfree(sys_info.cpu_info);
+               return -ENOMEM;
+       }
+       sys_info.mem_size = real_memory_size;
+       print_glob_info();
+
+       return 0;
+}
+
+static int __init check_sdias(void)
+{
+       int rc, act_hsa_size;
+
+       rc = sclp_sdias_blk_count();
+       if (rc < 0) {
+               ERROR_MSG("Could not determine HSA size\n");
+               return rc;
+       }
+       act_hsa_size = (rc - 1) * PAGE_SIZE;
+       if (act_hsa_size < HSA_SIZE) {
+               ERROR_MSG("HSA size too small: %i\n", act_hsa_size);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int __init dump_init(void)
+{
+       unsigned char arch;
+       int rc;
+
+       zcore_dbf = debug_register("dump", 4, 1, 4 * sizeof(long));
+       debug_register_view(zcore_dbf, &debug_sprintf_view);
+       debug_set_level(zcore_dbf, 6);
+
+       TRACE("devno:  %x\n", FCP_DATA->devno);
+       TRACE("wwpn:   %llx\n", (unsigned long long) FCP_DATA->wwpn);
+       TRACE("lun:    %llx\n", (unsigned long long) FCP_DATA->lun);
+       TRACE("prefix: %p\n", lowcore_ptr[0]);
+
+       rc = check_sdias();
+       if (rc) {
+               ERROR_MSG("Dump initialization failed\n");
+               goto failed;
+       }
+
+       rc = memcpy_hsa_kernel(&arch, __LC_AR_MODE_ID, 1);
+       if (rc) {
+               ERROR_MSG("sdial memcpy for arch id failed\n");
+               goto failed;
+       }
+
+#ifndef __s390x__
+       if (arch == ARCH_S390X) {
+               ERROR_MSG("32 bit dumper can't dump 64 bit system!\n");
+               rc = -EINVAL;
+               goto failed;
+       }
+#endif
+
+       /* set lowcore page to absolute 0 */
+       *(lowcore_ptr[0]) = S390_lowcore;
+       set_prefix(0);
+       lowcore_ptr[0] = NULL;
+
+       rc = sys_info_init(arch);
+       if (rc) {
+               ERROR_MSG("arch init failed\n");
+               goto failed;
+       }
+
+       rc = zcore_init();
+       if (rc) {
+               ERROR_MSG("zcore init failed\n");
+               goto failed;
+       }
+       hsa_available = 1;
+       return 0;
+
+failed:
+       diag308(DIAG308_REL_HSA, NULL);
+       return rc;
+}
+
+late_initcall(dump_init);
Index: git-linux-2.6/drivers/s390/cio/cio.c
===================================================================
--- git-linux-2.6.orig/drivers/s390/cio/cio.c   2007-02-21 10:22:40.000000000 
+0100
+++ git-linux-2.6/drivers/s390/cio/cio.c        2007-02-21 10:54:51.000000000 
+0100
@@ -21,6 +21,7 @@
 #include <asm/irq_regs.h>
 #include <asm/setup.h>
 #include <asm/reset.h>
+#include <asm/ipl.h>
 #include "airq.h"
 #include "cio.h"
 #include "css.h"
Index: git-linux-2.6/include/asm-s390/ipl.h
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ git-linux-2.6/include/asm-s390/ipl.h        2007-02-21 11:00:15.000000000 
+0100
@@ -0,0 +1,116 @@
+/*
+ * s390 (re)ipl support
+ *
+ * Copyright IBM Corp. 2007
+ */
+
+#ifndef _ASM_S390_IPL_H
+#define _ASM_S390_IPL_H
+
+#include <asm/types.h>
+
+#define IPL_PARMBLOCK_ORIGIN   0x2000
+
+#define IPL_PARM_BLK_FCP_LEN (sizeof(struct ipl_list_hdr) + \
+                             sizeof(struct ipl_block_fcp))
+
+#define IPL_PARM_BLK_CCW_LEN (sizeof(struct ipl_list_hdr) + \
+                             sizeof(struct ipl_block_ccw))
+
+#define IPL_MAX_SUPPORTED_VERSION (0)
+
+#define IPL_PARMBLOCK_START    ((struct ipl_parameter_block *) \
+                                IPL_PARMBLOCK_ORIGIN)
+#define IPL_PARMBLOCK_SIZE     (IPL_PARMBLOCK_START->hdr.len)
+
+struct ipl_list_hdr {
+       u32 len;
+       u8  reserved1[3];
+       u8  version;
+       u32 blk0_len;
+       u8  pbt;
+       u8  flags;
+       u16 reserved2;
+} __attribute__((packed));
+
+struct ipl_block_fcp {
+       u8  reserved1[313-1];
+       u8  opt;
+       u8  reserved2[3];
+       u16 reserved3;
+       u16 devno;
+       u8  reserved4[4];
+       u64 wwpn;
+       u64 lun;
+       u32 bootprog;
+       u8  reserved5[12];
+       u64 br_lba;
+       u32 scp_data_len;
+       u8  reserved6[260];
+       u8  scp_data[];
+} __attribute__((packed));
+
+struct ipl_block_ccw {
+       u8  load_param[8];
+       u8  reserved1[84];
+       u8  reserved2[2];
+       u16 devno;
+       u8  vm_flags;
+       u8  reserved3[3];
+       u32 vm_parm_len;
+} __attribute__((packed));
+
+struct ipl_parameter_block {
+       struct ipl_list_hdr hdr;
+       union {
+               struct ipl_block_fcp fcp;
+               struct ipl_block_ccw ccw;
+       } ipl_info;
+} __attribute__((packed));
+
+/*
+ * IPL validity flags and parameters as detected in head.S
+ */
+extern u32 ipl_flags;
+extern u16 ipl_devno;
+
+extern unsigned int dump_prefix_array[];
+
+extern void do_reipl(void);
+extern void ipl_save_parameters(void);
+extern void setup_dump(void);
+
+enum {
+       IPL_DEVNO_VALID         = 1,
+       IPL_PARMBLOCK_VALID     = 2,
+       IPL_NSS_VALID           = 4,
+};
+
+/*
+ * DIAG 308 support
+ */
+enum diag308_subcode  {
+       DIAG308_REL_HSA = 2,
+       DIAG308_IPL     = 3,
+       DIAG308_DUMP    = 4,
+       DIAG308_SET     = 5,
+       DIAG308_STORE   = 6,
+};
+
+enum diag308_ipl_type {
+       DIAG308_IPL_TYPE_FCP    = 0,
+       DIAG308_IPL_TYPE_CCW    = 2,
+};
+
+enum diag308_opt {
+       DIAG308_IPL_OPT_IPL     = 0x10,
+       DIAG308_IPL_OPT_DUMP    = 0x20,
+};
+
+enum diag308_rc {
+       DIAG308_RC_OK   = 1,
+};
+
+extern int diag308(unsigned long subcode, void *addr);
+
+#endif /* _ASM_S390_IPL_H */
Index: git-linux-2.6/include/asm-s390/processor.h
===================================================================
--- git-linux-2.6.orig/include/asm-s390/processor.h     2007-02-21 
10:23:12.000000000 +0100
+++ git-linux-2.6/include/asm-s390/processor.h  2007-02-21 10:56:03.000000000 
+0100
@@ -36,6 +36,11 @@ typedef struct
         unsigned int unused  : 16;
 } __attribute__ ((packed)) cpuid_t;
 
+static inline void get_cpu_id(cpuid_t *ptr)
+{
+       asm volatile("stidp 0(%1)" : "=m" (*ptr) : "a" (ptr));
+}
+
 struct cpuinfo_S390
 {
         cpuid_t  cpu_id;
Index: git-linux-2.6/include/asm-s390/sclp.h
===================================================================
--- git-linux-2.6.orig/include/asm-s390/sclp.h  2007-02-21 10:23:12.000000000 
+0100
+++ git-linux-2.6/include/asm-s390/sclp.h       2007-02-21 11:00:15.000000000 
+0100
@@ -35,5 +35,7 @@ struct sclp_readinfo_sccb {
 
 extern struct sclp_readinfo_sccb s390_readinfo_sccb;
 extern void sclp_readinfo_early(void);
+extern int sclp_sdias_blk_count(void);
+extern int sclp_sdias_copy(void *dest, int blk_num, int nr_blks);
 
 #endif /* _ASM_S390_SCLP_H */
Index: git-linux-2.6/include/asm-s390/setup.h
===================================================================
--- git-linux-2.6.orig/include/asm-s390/setup.h 2007-02-21 10:23:12.000000000 
+0100
+++ git-linux-2.6/include/asm-s390/setup.h      2007-02-21 11:00:15.000000000 
+0100
@@ -16,7 +16,6 @@
 
 #define PARMAREA               0x10400
 #define MEMORY_CHUNKS          16      /* max 0x7fff */
-#define IPL_PARMBLOCK_ORIGIN   0x2000
 
 #ifndef __ASSEMBLY__
 
@@ -41,6 +40,7 @@ struct mem_chunk {
 };
 
 extern struct mem_chunk memory_chunk[];
+extern unsigned long real_memory_size;
 
 #ifdef CONFIG_S390_SWITCH_AMODE
 extern unsigned int switch_amode;
@@ -97,82 +97,9 @@ extern char vmpoff_cmd[];
 #define SET_CONSOLE_3215       do { console_mode = 2; } while (0)
 #define SET_CONSOLE_3270       do { console_mode = 3; } while (0)
 
-struct ipl_list_hdr {
-       u32 len;
-       u8  reserved1[3];
-       u8  version;
-       u32 blk0_len;
-       u8  pbt;
-       u8  flags;
-       u16 reserved2;
-} __attribute__((packed));
-
-struct ipl_block_fcp {
-       u8  reserved1[313-1];
-       u8  opt;
-       u8  reserved2[3];
-       u16 reserved3;
-       u16 devno;
-       u8  reserved4[4];
-       u64 wwpn;
-       u64 lun;
-       u32 bootprog;
-       u8  reserved5[12];
-       u64 br_lba;
-       u32 scp_data_len;
-       u8  reserved6[260];
-       u8  scp_data[];
-} __attribute__((packed));
-
-struct ipl_block_ccw {
-       u8  load_param[8];
-       u8  reserved1[84];
-       u8  reserved2[2];
-       u16 devno;
-       u8  vm_flags;
-       u8  reserved3[3];
-       u32 vm_parm_len;
-} __attribute__((packed));
-
-struct ipl_parameter_block {
-       struct ipl_list_hdr hdr;
-       union {
-               struct ipl_block_fcp fcp;
-               struct ipl_block_ccw ccw;
-       } ipl_info;
-} __attribute__((packed));
-
-#define IPL_PARM_BLK_FCP_LEN (sizeof(struct ipl_list_hdr) + \
-                             sizeof(struct ipl_block_fcp))
-
-#define IPL_PARM_BLK_CCW_LEN (sizeof(struct ipl_list_hdr) + \
-                             sizeof(struct ipl_block_ccw))
-
-#define IPL_MAX_SUPPORTED_VERSION (0)
-
-/*
- * IPL validity flags and parameters as detected in head.S
- */
-extern u32 ipl_flags;
-extern u16 ipl_devno;
-
-extern void do_reipl(void);
-extern void ipl_save_parameters(void);
-
-enum {
-       IPL_DEVNO_VALID = 1,
-       IPL_PARMBLOCK_VALID = 2,
-       IPL_NSS_VALID = 4,
-};
-
 #define NSS_NAME_SIZE  8
-
 extern char kernel_nss_name[];
 
-#define IPL_PARMBLOCK_START    ((struct ipl_parameter_block *) \
-                                IPL_PARMBLOCK_ORIGIN)
-#define IPL_PARMBLOCK_SIZE     (IPL_PARMBLOCK_START->hdr.len)
-
 #else /* __ASSEMBLY__ */
 
 #ifndef __s390x__
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to