[RFC] [PATCH 1/2] s390: SCSI dump kernel and userspace application

From: Michael Holzheu
Date: Wed Feb 21 2007 - 08:39:14 EST


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

Acked-by: Martin Schwidefsky <schwidefsky@xxxxxxxxxx>
Signed-off-by: Michael Holzheu <holzheu@xxxxxxxxxx>

---
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 majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/