aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Holzheu <holzheu@de.ibm.com>2007-04-27 10:01:49 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2007-04-27 10:01:44 -0400
commit411ed3225733dbd83b4cbaaa992ef80d6ec1534e (patch)
tree388aeac39e9fad5f7cadcc8fcbf0838811f5829d
parent7039d3a11c4b4b59f9ef933b4b0a28304bdd07d1 (diff)
[S390] zfcpdump support.
s390 machines provide hardware support for creating Linux dumps on SCSI disks. For creating a dump a special purpose dump Linux is used. The first 32 MB of memory are saved by the hardware before the dump Linux is booted. Via an SCLP interface, the saved memory can be accessed from Linux. This patch exports memory and registers of the crashed Linux to userspace via a debugfs file. For more information refer to Documentation/s390/zfcpdump.txt, which is included in this patch. Signed-off-by: Michael Holzheu <holzheu@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com> Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
-rw-r--r--Documentation/s390/zfcpdump.txt87
-rw-r--r--arch/s390/Kconfig8
-rw-r--r--arch/s390/Makefile3
-rw-r--r--arch/s390/defconfig1
-rw-r--r--arch/s390/kernel/head64.S72
-rw-r--r--arch/s390/kernel/ipl.c232
-rw-r--r--arch/s390/kernel/setup.c38
-rw-r--r--arch/s390/kernel/smp.c62
-rw-r--r--drivers/s390/char/Makefile3
-rw-r--r--drivers/s390/char/sclp.h2
-rw-r--r--drivers/s390/char/sclp_sdias.c255
-rw-r--r--drivers/s390/char/zcore.c651
-rw-r--r--include/asm-s390/ipl.h32
-rw-r--r--include/asm-s390/lowcore.h46
-rw-r--r--include/asm-s390/sclp.h2
-rw-r--r--include/asm-s390/setup.h2
-rw-r--r--include/asm-s390/smp.h1
17 files changed, 1411 insertions, 86 deletions
diff --git a/Documentation/s390/zfcpdump.txt b/Documentation/s390/zfcpdump.txt
new file mode 100644
index 000000000000..cf45d27c4608
--- /dev/null
+++ b/Documentation/s390/zfcpdump.txt
@@ -0,0 +1,87 @@
1s390 SCSI dump tool (zfcpdump)
2
3System z machines (z900 or higher) provide hardware support for creating system
4dumps on SCSI disks. The dump process is initiated by booting a dump tool, which
5has to create a dump of the current (probably crashed) Linux image. In order to
6not overwrite memory of the crashed Linux with data of the dump tool, the
7hardware saves some memory plus the register sets of the boot cpu before the
8dump tool is loaded. There exists an SCLP hardware interface to obtain the saved
9memory afterwards. Currently 32 MB are saved.
10
11This zfcpdump implementation consists of a Linux dump kernel together with
12a userspace dump tool, which are loaded together into the saved memory region
13below 32 MB. zfcpdump is installed on a SCSI disk using zipl (as contained in
14the s390-tools package) to make the device bootable. The operator of a Linux
15system can then trigger a SCSI dump by booting the SCSI disk, where zfcpdump
16resides on.
17
18The kernel part of zfcpdump is implemented as a debugfs file under "zcore/mem",
19which exports memory and registers of the crashed Linux in an s390
20standalone dump format. It can be used in the same way as e.g. /dev/mem. The
21dump format defines a 4K header followed by plain uncompressed memory. The
22register sets are stored in the prefix pages of the respective cpus. To build a
23dump enabled kernel with the zcore driver, the kernel config option
24CONFIG_ZFCPDUMP has to be set. When reading from "zcore/mem", the part of
25memory, which has been saved by hardware is read by the driver via the SCLP
26hardware interface. The second part is just copied from the non overwritten real
27memory.
28
29The userspace application of zfcpdump can reside e.g. in an intitramfs or an
30initrd. It reads from zcore/mem and writes the system dump to a file on a
31SCSI disk.
32
33To build a zfcpdump kernel use the following settings in your kernel
34configuration:
35 * CONFIG_ZFCPDUMP=y
36 * Enable ZFCP driver
37 * Enable SCSI driver
38 * Enable ext2 and ext3 filesystems
39 * Disable as many features as possible to keep the kernel small.
40 E.g. network support is not needed at all.
41
42To use the zfcpdump userspace application in an initramfs you have to do the
43following:
44
45 * Copy the zfcpdump executable somewhere into your Linux tree.
46 E.g. to "arch/s390/boot/zfcpdump. If you do not want to include
47 shared libraries, compile the tool with the "-static" gcc option.
48 * If you want to include e2fsck, add it to your source tree, too. The zfcpdump
49 application attempts to start /sbin/e2fsck from the ramdisk.
50 * Use an initramfs config file like the following:
51
52 dir /dev 755 0 0
53 nod /dev/console 644 0 0 c 5 1
54 nod /dev/null 644 0 0 c 1 3
55 nod /dev/sda1 644 0 0 b 8 1
56 nod /dev/sda2 644 0 0 b 8 2
57 nod /dev/sda3 644 0 0 b 8 3
58 nod /dev/sda4 644 0 0 b 8 4
59 nod /dev/sda5 644 0 0 b 8 5
60 nod /dev/sda6 644 0 0 b 8 6
61 nod /dev/sda7 644 0 0 b 8 7
62 nod /dev/sda8 644 0 0 b 8 8
63 nod /dev/sda9 644 0 0 b 8 9
64 nod /dev/sda10 644 0 0 b 8 10
65 nod /dev/sda11 644 0 0 b 8 11
66 nod /dev/sda12 644 0 0 b 8 12
67 nod /dev/sda13 644 0 0 b 8 13
68 nod /dev/sda14 644 0 0 b 8 14
69 nod /dev/sda15 644 0 0 b 8 15
70 file /init arch/s390/boot/zfcpdump 755 0 0
71 file /sbin/e2fsck arch/s390/boot/e2fsck 755 0 0
72 dir /proc 755 0 0
73 dir /sys 755 0 0
74 dir /mnt 755 0 0
75 dir /sbin 755 0 0
76
77 * Issue "make image" to build the zfcpdump image with initramfs.
78
79In a Linux distribution the zfcpdump enabled kernel image must be copied to
80/usr/share/zfcpdump/zfcpdump.image, where the s390 zipl tool is looking for the
81dump kernel when preparing a SCSI dump disk.
82
83If you use a ramdisk copy it to "/usr/share/zfcpdump/zfcpdump.rd".
84
85For more information on how to use zfcpdump refer to the s390 'Using the Dump
86Tools book', which is available from
87http://www.ibm.com/developerworks/linux/linux390.
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 0f9517bc8e70..e6ec418093e5 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -519,6 +519,14 @@ config KEXEC
519 current kernel, and to start another kernel. It is like a reboot 519 current kernel, and to start another kernel. It is like a reboot
520 but is independent of hardware/microcode support. 520 but is independent of hardware/microcode support.
521 521
522config ZFCPDUMP
523 tristate "zfcpdump support"
524 select SMP
525 default n
526 help
527 Select this option if you want to build an zfcpdump enabled kernel.
528 Refer to "Documentation/s390/zfcpdump.txt" for more details on this.
529
522endmenu 530endmenu
523 531
524source "net/Kconfig" 532source "net/Kconfig"
diff --git a/arch/s390/Makefile b/arch/s390/Makefile
index ece5adc05606..68441e0e74b6 100644
--- a/arch/s390/Makefile
+++ b/arch/s390/Makefile
@@ -105,6 +105,9 @@ install: vmlinux
105image: vmlinux 105image: vmlinux
106 $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ 106 $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
107 107
108zfcpdump:
109 $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
110
108archclean: 111archclean:
109 $(Q)$(MAKE) $(clean)=$(boot) 112 $(Q)$(MAKE) $(clean)=$(boot)
110 113
diff --git a/arch/s390/defconfig b/arch/s390/defconfig
index 80046d9e2a3b..0e4da8a7d826 100644
--- a/arch/s390/defconfig
+++ b/arch/s390/defconfig
@@ -167,6 +167,7 @@ CONFIG_NO_IDLE_HZ=y
167CONFIG_NO_IDLE_HZ_INIT=y 167CONFIG_NO_IDLE_HZ_INIT=y
168CONFIG_S390_HYPFS_FS=y 168CONFIG_S390_HYPFS_FS=y
169CONFIG_KEXEC=y 169CONFIG_KEXEC=y
170# CONFIG_ZFCPDUMP is not set
170 171
171# 172#
172# Networking 173# Networking
diff --git a/arch/s390/kernel/head64.S b/arch/s390/kernel/head64.S
index 37010709fe68..a87b1976d409 100644
--- a/arch/s390/kernel/head64.S
+++ b/arch/s390/kernel/head64.S
@@ -39,7 +39,69 @@ startup_continue:
39 basr %r13,0 # get base 39 basr %r13,0 # get base
40.LPG1: sll %r13,1 # remove high order bit 40.LPG1: sll %r13,1 # remove high order bit
41 srl %r13,1 41 srl %r13,1
42 lhi %r1,1 # mode 1 = esame 42
43#ifdef CONFIG_ZFCPDUMP
44
45 # check if we have been ipled using zfcp dump:
46
47 tm 0xb9,0x01 # test if subchannel is enabled
48 jno .nodump # subchannel disabled
49 l %r1,0xb8
50 la %r5,.Lipl_schib-.LPG1(%r13)
51 stsch 0(%r5) # get schib of subchannel
52 jne .nodump # schib not available
53 tm 5(%r5),0x01 # devno valid?
54 jno .nodump
55 tm 4(%r5),0x80 # qdio capable device?
56 jno .nodump
57 l %r2,20(%r0) # address of ipl parameter block
58 lhi %r3,0
59 ic %r3,0x148(%r2) # get opt field
60 chi %r3,0x20 # load with dump?
61 jne .nodump
62
63 # store all prefix registers in case of load with dump:
64
65 la %r7,0 # base register for 0 page
66 la %r8,0 # first cpu
67 l %r11,.Lpref_arr_ptr-.LPG1(%r13) # address of prefix array
68 ahi %r11,4 # skip boot cpu
69 lr %r12,%r11
70 ahi %r12,(CONFIG_NR_CPUS*4) # end of prefix array
71 stap .Lcurrent_cpu+2-.LPG1(%r13) # store current cpu addr
721:
73 cl %r8,.Lcurrent_cpu-.LPG1(%r13) # is ipl cpu ?
74 je 4f # if yes get next cpu
752:
76 lr %r9,%r7
77 sigp %r9,%r8,0x9 # stop & store status of cpu
78 brc 8,3f # accepted
79 brc 4,4f # status stored: next cpu
80 brc 2,2b # busy: try again
81 brc 1,4f # not op: next cpu
823:
83 mvc 0(4,%r11),264(%r7) # copy prefix register to prefix array
84 ahi %r11,4 # next element in prefix array
85 clr %r11,%r12
86 je 5f # no more space in prefix array
874:
88 ahi %r8,1 # next cpu (r8 += 1)
89 cl %r8,.Llast_cpu-.LPG1(%r13) # is last possible cpu ?
90 jl 1b # jump if not last cpu
915:
92 lhi %r1,2 # mode 2 = esame (dump)
93 j 6f
94 .align 4
95.Lipl_schib:
96 .rept 13
97 .long 0
98 .endr
99.nodump:
100 lhi %r1,1 # mode 1 = esame (normal ipl)
1016:
102#else
103 lhi %r1,1 # mode 1 = esame (normal ipl)
104#endif /* CONFIG_ZFCPDUMP */
43 mvi __LC_AR_MODE_ID,1 # set esame flag 105 mvi __LC_AR_MODE_ID,1 # set esame flag
44 slr %r0,%r0 # set cpuid to zero 106 slr %r0,%r0 # set cpuid to zero
45 sigp %r1,%r0,0x12 # switch to esame mode 107 sigp %r1,%r0,0x12 # switch to esame mode
@@ -149,6 +211,14 @@ startup_continue:
149.L4malign:.quad 0xffffffffffc00000 211.L4malign:.quad 0xffffffffffc00000
150.Lscan2g:.quad 0x80000000 + 0x20000 - 8 # 2GB + 128K - 8 212.Lscan2g:.quad 0x80000000 + 0x20000 - 8 # 2GB + 128K - 8
151.Lnop: .long 0x07000700 213.Lnop: .long 0x07000700
214#ifdef CONFIG_ZFCPDUMP
215.Lcurrent_cpu:
216 .long 0x0
217.Llast_cpu:
218 .long 0x0000ffff
219.Lpref_arr_ptr:
220 .long zfcpdump_prefix_array
221#endif /* CONFIG_ZFCPDUMP */
152.Lparmaddr: 222.Lparmaddr:
153 .quad PARMAREA 223 .quad PARMAREA
154 .align 64 224 .align 64
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c
index a83cf1fdd8f5..06833ac2b115 100644
--- a/arch/s390/kernel/ipl.c
+++ b/arch/s390/kernel/ipl.c
@@ -29,36 +29,21 @@
29#define SCCB_LOADPARM (&s390_readinfo_sccb.loadparm) 29#define SCCB_LOADPARM (&s390_readinfo_sccb.loadparm)
30#define SCCB_FLAG (s390_readinfo_sccb.flags) 30#define SCCB_FLAG (s390_readinfo_sccb.flags)
31 31
32enum ipl_type { 32#define IPL_UNKNOWN_STR "unknown"
33 IPL_TYPE_NONE = 1, 33#define IPL_CCW_STR "ccw"
34 IPL_TYPE_UNKNOWN = 2, 34#define IPL_FCP_STR "fcp"
35 IPL_TYPE_CCW = 4, 35#define IPL_FCP_DUMP_STR "fcp_dump"
36 IPL_TYPE_FCP = 8, 36#define IPL_NSS_STR "nss"
37 IPL_TYPE_NSS = 16,
38};
39
40#define IPL_NONE_STR "none"
41#define IPL_UNKNOWN_STR "unknown"
42#define IPL_CCW_STR "ccw"
43#define IPL_FCP_STR "fcp"
44#define IPL_NSS_STR "nss"
45
46/*
47 * Must be in data section since the bss section
48 * is not cleared when these are accessed.
49 */
50static u16 ipl_devno __attribute__((__section__(".data"))) = 0;
51u32 ipl_flags __attribute__((__section__(".data"))) = 0;
52 37
53static char *ipl_type_str(enum ipl_type type) 38static char *ipl_type_str(enum ipl_type type)
54{ 39{
55 switch (type) { 40 switch (type) {
56 case IPL_TYPE_NONE:
57 return IPL_NONE_STR;
58 case IPL_TYPE_CCW: 41 case IPL_TYPE_CCW:
59 return IPL_CCW_STR; 42 return IPL_CCW_STR;
60 case IPL_TYPE_FCP: 43 case IPL_TYPE_FCP:
61 return IPL_FCP_STR; 44 return IPL_FCP_STR;
45 case IPL_TYPE_FCP_DUMP:
46 return IPL_FCP_DUMP_STR;
62 case IPL_TYPE_NSS: 47 case IPL_TYPE_NSS:
63 return IPL_NSS_STR; 48 return IPL_NSS_STR;
64 case IPL_TYPE_UNKNOWN: 49 case IPL_TYPE_UNKNOWN:
@@ -67,15 +52,55 @@ static char *ipl_type_str(enum ipl_type type)
67 } 52 }
68} 53}
69 54
55enum dump_type {
56 DUMP_TYPE_NONE = 1,
57 DUMP_TYPE_CCW = 2,
58 DUMP_TYPE_FCP = 4,
59};
60
61#define DUMP_NONE_STR "none"
62#define DUMP_CCW_STR "ccw"
63#define DUMP_FCP_STR "fcp"
64
65static char *dump_type_str(enum dump_type type)
66{
67 switch (type) {
68 case DUMP_TYPE_NONE:
69 return DUMP_NONE_STR;
70 case DUMP_TYPE_CCW:
71 return DUMP_CCW_STR;
72 case DUMP_TYPE_FCP:
73 return DUMP_FCP_STR;
74 default:
75 return NULL;
76 }
77}
78
79/*
80 * Must be in data section since the bss section
81 * is not cleared when these are accessed.
82 */
83static u16 ipl_devno __attribute__((__section__(".data"))) = 0;
84u32 ipl_flags __attribute__((__section__(".data"))) = 0;
85
70enum ipl_method { 86enum ipl_method {
71 IPL_METHOD_NONE, 87 REIPL_METHOD_CCW_CIO,
72 IPL_METHOD_CCW_CIO, 88 REIPL_METHOD_CCW_DIAG,
73 IPL_METHOD_CCW_DIAG, 89 REIPL_METHOD_CCW_VM,
74 IPL_METHOD_CCW_VM, 90 REIPL_METHOD_FCP_RO_DIAG,
75 IPL_METHOD_FCP_RO_DIAG, 91 REIPL_METHOD_FCP_RW_DIAG,
76 IPL_METHOD_FCP_RW_DIAG, 92 REIPL_METHOD_FCP_RO_VM,
77 IPL_METHOD_FCP_RO_VM, 93 REIPL_METHOD_FCP_DUMP,
78 IPL_METHOD_NSS, 94 REIPL_METHOD_NSS,
95 REIPL_METHOD_DEFAULT,
96};
97
98enum dump_method {
99 DUMP_METHOD_NONE,
100 DUMP_METHOD_CCW_CIO,
101 DUMP_METHOD_CCW_DIAG,
102 DUMP_METHOD_CCW_VM,
103 DUMP_METHOD_FCP_DIAG,
79}; 104};
80 105
81enum shutdown_action { 106enum shutdown_action {
@@ -107,15 +132,15 @@ static int diag308_set_works = 0;
107static int reipl_capabilities = IPL_TYPE_UNKNOWN; 132static int reipl_capabilities = IPL_TYPE_UNKNOWN;
108 133
109static enum ipl_type reipl_type = IPL_TYPE_UNKNOWN; 134static enum ipl_type reipl_type = IPL_TYPE_UNKNOWN;
110static enum ipl_method reipl_method = IPL_METHOD_NONE; 135static enum ipl_method reipl_method = REIPL_METHOD_DEFAULT;
111static struct ipl_parameter_block *reipl_block_fcp; 136static struct ipl_parameter_block *reipl_block_fcp;
112static struct ipl_parameter_block *reipl_block_ccw; 137static struct ipl_parameter_block *reipl_block_ccw;
113 138
114static char reipl_nss_name[NSS_NAME_SIZE + 1]; 139static char reipl_nss_name[NSS_NAME_SIZE + 1];
115 140
116static int dump_capabilities = IPL_TYPE_NONE; 141static int dump_capabilities = DUMP_TYPE_NONE;
117static enum ipl_type dump_type = IPL_TYPE_NONE; 142static enum dump_type dump_type = DUMP_TYPE_NONE;
118static enum ipl_method dump_method = IPL_METHOD_NONE; 143static enum dump_method dump_method = DUMP_METHOD_NONE;
119static struct ipl_parameter_block *dump_block_fcp; 144static struct ipl_parameter_block *dump_block_fcp;
120static struct ipl_parameter_block *dump_block_ccw; 145static struct ipl_parameter_block *dump_block_ccw;
121 146
@@ -134,6 +159,7 @@ int diag308(unsigned long subcode, void *addr)
134 : "d" (subcode) : "cc", "memory"); 159 : "d" (subcode) : "cc", "memory");
135 return _rc; 160 return _rc;
136} 161}
162EXPORT_SYMBOL_GPL(diag308);
137 163
138/* SYSFS */ 164/* SYSFS */
139 165
@@ -197,7 +223,7 @@ static void make_attrs_ro(struct attribute **attrs)
197 * ipl section 223 * ipl section
198 */ 224 */
199 225
200static enum ipl_type ipl_get_type(void) 226static __init enum ipl_type get_ipl_type(void)
201{ 227{
202 struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START; 228 struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START;
203 229
@@ -211,12 +237,44 @@ static enum ipl_type ipl_get_type(void)
211 return IPL_TYPE_UNKNOWN; 237 return IPL_TYPE_UNKNOWN;
212 if (ipl->hdr.pbt != DIAG308_IPL_TYPE_FCP) 238 if (ipl->hdr.pbt != DIAG308_IPL_TYPE_FCP)
213 return IPL_TYPE_UNKNOWN; 239 return IPL_TYPE_UNKNOWN;
240 if (ipl->ipl_info.fcp.opt == DIAG308_IPL_OPT_DUMP)
241 return IPL_TYPE_FCP_DUMP;
214 return IPL_TYPE_FCP; 242 return IPL_TYPE_FCP;
215} 243}
216 244
245void __init setup_ipl_info(void)
246{
247 ipl_info.type = get_ipl_type();
248 switch (ipl_info.type) {
249 case IPL_TYPE_CCW:
250 ipl_info.data.ccw.dev_id.devno = ipl_devno;
251 ipl_info.data.ccw.dev_id.ssid = 0;
252 break;
253 case IPL_TYPE_FCP:
254 case IPL_TYPE_FCP_DUMP:
255 ipl_info.data.fcp.dev_id.devno =
256 IPL_PARMBLOCK_START->ipl_info.fcp.devno;
257 ipl_info.data.fcp.dev_id.ssid = 0;
258 ipl_info.data.fcp.wwpn = IPL_PARMBLOCK_START->ipl_info.fcp.wwpn;
259 ipl_info.data.fcp.lun = IPL_PARMBLOCK_START->ipl_info.fcp.lun;
260 break;
261 case IPL_TYPE_NSS:
262 strncpy(ipl_info.data.nss.name, kernel_nss_name,
263 sizeof(ipl_info.data.nss.name));
264 break;
265 case IPL_TYPE_UNKNOWN:
266 default:
267 /* We have no info to copy */
268 break;
269 }
270}
271
272struct ipl_info ipl_info;
273EXPORT_SYMBOL_GPL(ipl_info);
274
217static ssize_t ipl_type_show(struct subsystem *subsys, char *page) 275static ssize_t ipl_type_show(struct subsystem *subsys, char *page)
218{ 276{
219 return sprintf(page, "%s\n", ipl_type_str(ipl_get_type())); 277 return sprintf(page, "%s\n", ipl_type_str(ipl_info.type));
220} 278}
221 279
222static struct subsys_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type); 280static struct subsys_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type);
@@ -225,10 +283,11 @@ static ssize_t sys_ipl_device_show(struct subsystem *subsys, char *page)
225{ 283{
226 struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START; 284 struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START;
227 285
228 switch (ipl_get_type()) { 286 switch (ipl_info.type) {
229 case IPL_TYPE_CCW: 287 case IPL_TYPE_CCW:
230 return sprintf(page, "0.0.%04x\n", ipl_devno); 288 return sprintf(page, "0.0.%04x\n", ipl_devno);
231 case IPL_TYPE_FCP: 289 case IPL_TYPE_FCP:
290 case IPL_TYPE_FCP_DUMP:
232 return sprintf(page, "0.0.%04x\n", ipl->ipl_info.fcp.devno); 291 return sprintf(page, "0.0.%04x\n", ipl->ipl_info.fcp.devno);
233 default: 292 default:
234 return 0; 293 return 0;
@@ -485,23 +544,29 @@ static int reipl_set_type(enum ipl_type type)
485 switch(type) { 544 switch(type) {
486 case IPL_TYPE_CCW: 545 case IPL_TYPE_CCW:
487 if (MACHINE_IS_VM) 546 if (MACHINE_IS_VM)
488 reipl_method = IPL_METHOD_CCW_VM; 547 reipl_method = REIPL_METHOD_CCW_VM;
489 else 548 else
490 reipl_method = IPL_METHOD_CCW_CIO; 549 reipl_method = REIPL_METHOD_CCW_CIO;
491 break; 550 break;
492 case IPL_TYPE_FCP: 551 case IPL_TYPE_FCP:
493 if (diag308_set_works) 552 if (diag308_set_works)
494 reipl_method = IPL_METHOD_FCP_RW_DIAG; 553 reipl_method = REIPL_METHOD_FCP_RW_DIAG;
495 else if (MACHINE_IS_VM) 554 else if (MACHINE_IS_VM)
496 reipl_method = IPL_METHOD_FCP_RO_VM; 555 reipl_method = REIPL_METHOD_FCP_RO_VM;
497 else 556 else
498 reipl_method = IPL_METHOD_FCP_RO_DIAG; 557 reipl_method = REIPL_METHOD_FCP_RO_DIAG;
558 break;
559 case IPL_TYPE_FCP_DUMP:
560 reipl_method = REIPL_METHOD_FCP_DUMP;
499 break; 561 break;
500 case IPL_TYPE_NSS: 562 case IPL_TYPE_NSS:
501 reipl_method = IPL_METHOD_NSS; 563 reipl_method = REIPL_METHOD_NSS;
564 break;
565 case IPL_TYPE_UNKNOWN:
566 reipl_method = REIPL_METHOD_DEFAULT;
502 break; 567 break;
503 default: 568 default:
504 reipl_method = IPL_METHOD_NONE; 569 BUG();
505 } 570 }
506 reipl_type = type; 571 reipl_type = type;
507 return 0; 572 return 0;
@@ -579,22 +644,22 @@ static struct attribute_group dump_ccw_attr_group = {
579 644
580/* dump type */ 645/* dump type */
581 646
582static int dump_set_type(enum ipl_type type) 647static int dump_set_type(enum dump_type type)
583{ 648{
584 if (!(dump_capabilities & type)) 649 if (!(dump_capabilities & type))
585 return -EINVAL; 650 return -EINVAL;
586 switch(type) { 651 switch(type) {
587 case IPL_TYPE_CCW: 652 case DUMP_TYPE_CCW:
588 if (MACHINE_IS_VM) 653 if (MACHINE_IS_VM)
589 dump_method = IPL_METHOD_CCW_VM; 654 dump_method = DUMP_METHOD_CCW_VM;
590 else 655 else
591 dump_method = IPL_METHOD_CCW_CIO; 656 dump_method = DUMP_METHOD_CCW_CIO;
592 break; 657 break;
593 case IPL_TYPE_FCP: 658 case DUMP_TYPE_FCP:
594 dump_method = IPL_METHOD_FCP_RW_DIAG; 659 dump_method = DUMP_METHOD_FCP_DIAG;
595 break; 660 break;
596 default: 661 default:
597 dump_method = IPL_METHOD_NONE; 662 dump_method = DUMP_METHOD_NONE;
598 } 663 }
599 dump_type = type; 664 dump_type = type;
600 return 0; 665 return 0;
@@ -602,7 +667,7 @@ static int dump_set_type(enum ipl_type type)
602 667
603static ssize_t dump_type_show(struct subsystem *subsys, char *page) 668static ssize_t dump_type_show(struct subsystem *subsys, char *page)
604{ 669{
605 return sprintf(page, "%s\n", ipl_type_str(dump_type)); 670 return sprintf(page, "%s\n", dump_type_str(dump_type));
606} 671}
607 672
608static ssize_t dump_type_store(struct subsystem *subsys, const char *buf, 673static ssize_t dump_type_store(struct subsystem *subsys, const char *buf,
@@ -610,12 +675,12 @@ static ssize_t dump_type_store(struct subsystem *subsys, const char *buf,
610{ 675{
611 int rc = -EINVAL; 676 int rc = -EINVAL;
612 677
613 if (strncmp(buf, IPL_NONE_STR, strlen(IPL_NONE_STR)) == 0) 678 if (strncmp(buf, DUMP_NONE_STR, strlen(DUMP_NONE_STR)) == 0)
614 rc = dump_set_type(IPL_TYPE_NONE); 679 rc = dump_set_type(DUMP_TYPE_NONE);
615 else if (strncmp(buf, IPL_CCW_STR, strlen(IPL_CCW_STR)) == 0) 680 else if (strncmp(buf, DUMP_CCW_STR, strlen(DUMP_CCW_STR)) == 0)
616 rc = dump_set_type(IPL_TYPE_CCW); 681 rc = dump_set_type(DUMP_TYPE_CCW);
617 else if (strncmp(buf, IPL_FCP_STR, strlen(IPL_FCP_STR)) == 0) 682 else if (strncmp(buf, DUMP_FCP_STR, strlen(DUMP_FCP_STR)) == 0)
618 rc = dump_set_type(IPL_TYPE_FCP); 683 rc = dump_set_type(DUMP_TYPE_FCP);
619 return (rc != 0) ? rc : len; 684 return (rc != 0) ? rc : len;
620} 685}
621 686
@@ -664,14 +729,14 @@ void do_reipl(void)
664 char loadparm[LOADPARM_LEN + 1]; 729 char loadparm[LOADPARM_LEN + 1];
665 730
666 switch (reipl_method) { 731 switch (reipl_method) {
667 case IPL_METHOD_CCW_CIO: 732 case REIPL_METHOD_CCW_CIO:
668 devid.devno = reipl_block_ccw->ipl_info.ccw.devno; 733 devid.devno = reipl_block_ccw->ipl_info.ccw.devno;
669 if (ipl_get_type() == IPL_TYPE_CCW && devid.devno == ipl_devno) 734 if (ipl_info.type == IPL_TYPE_CCW && devid.devno == ipl_devno)
670 diag308(DIAG308_IPL, NULL); 735 diag308(DIAG308_IPL, NULL);
671 devid.ssid = 0; 736 devid.ssid = 0;
672 reipl_ccw_dev(&devid); 737 reipl_ccw_dev(&devid);
673 break; 738 break;
674 case IPL_METHOD_CCW_VM: 739 case REIPL_METHOD_CCW_VM:
675 reipl_get_ascii_loadparm(loadparm); 740 reipl_get_ascii_loadparm(loadparm);
676 if (strlen(loadparm) == 0) 741 if (strlen(loadparm) == 0)
677 sprintf(buf, "IPL %X", 742 sprintf(buf, "IPL %X",
@@ -681,30 +746,32 @@ void do_reipl(void)
681 reipl_block_ccw->ipl_info.ccw.devno, loadparm); 746 reipl_block_ccw->ipl_info.ccw.devno, loadparm);
682 __cpcmd(buf, NULL, 0, NULL); 747 __cpcmd(buf, NULL, 0, NULL);
683 break; 748 break;
684 case IPL_METHOD_CCW_DIAG: 749 case REIPL_METHOD_CCW_DIAG:
685 diag308(DIAG308_SET, reipl_block_ccw); 750 diag308(DIAG308_SET, reipl_block_ccw);
686 diag308(DIAG308_IPL, NULL); 751 diag308(DIAG308_IPL, NULL);
687 break; 752 break;
688 case IPL_METHOD_FCP_RW_DIAG: 753 case REIPL_METHOD_FCP_RW_DIAG:
689 diag308(DIAG308_SET, reipl_block_fcp); 754 diag308(DIAG308_SET, reipl_block_fcp);
690 diag308(DIAG308_IPL, NULL); 755 diag308(DIAG308_IPL, NULL);
691 break; 756 break;
692 case IPL_METHOD_FCP_RO_DIAG: 757 case REIPL_METHOD_FCP_RO_DIAG:
693 diag308(DIAG308_IPL, NULL); 758 diag308(DIAG308_IPL, NULL);
694 break; 759 break;
695 case IPL_METHOD_FCP_RO_VM: 760 case REIPL_METHOD_FCP_RO_VM:
696 __cpcmd("IPL", NULL, 0, NULL); 761 __cpcmd("IPL", NULL, 0, NULL);
697 break; 762 break;
698 case IPL_METHOD_NSS: 763 case REIPL_METHOD_NSS:
699 sprintf(buf, "IPL %s", reipl_nss_name); 764 sprintf(buf, "IPL %s", reipl_nss_name);
700 __cpcmd(buf, NULL, 0, NULL); 765 __cpcmd(buf, NULL, 0, NULL);
701 break; 766 break;
702 case IPL_METHOD_NONE: 767 case REIPL_METHOD_DEFAULT:
703 default:
704 if (MACHINE_IS_VM) 768 if (MACHINE_IS_VM)
705 __cpcmd("IPL", NULL, 0, NULL); 769 __cpcmd("IPL", NULL, 0, NULL);
706 diag308(DIAG308_IPL, NULL); 770 diag308(DIAG308_IPL, NULL);
707 break; 771 break;
772 case REIPL_METHOD_FCP_DUMP:
773 default:
774 break;
708 } 775 }
709 signal_processor(smp_processor_id(), sigp_stop_and_store_status); 776 signal_processor(smp_processor_id(), sigp_stop_and_store_status);
710} 777}
@@ -715,28 +782,28 @@ static void do_dump(void)
715 static char buf[100]; 782 static char buf[100];
716 783
717 switch (dump_method) { 784 switch (dump_method) {
718 case IPL_METHOD_CCW_CIO: 785 case DUMP_METHOD_CCW_CIO:
719 smp_send_stop(); 786 smp_send_stop();
720 devid.devno = dump_block_ccw->ipl_info.ccw.devno; 787 devid.devno = dump_block_ccw->ipl_info.ccw.devno;
721 devid.ssid = 0; 788 devid.ssid = 0;
722 reipl_ccw_dev(&devid); 789 reipl_ccw_dev(&devid);
723 break; 790 break;
724 case IPL_METHOD_CCW_VM: 791 case DUMP_METHOD_CCW_VM:
725 smp_send_stop(); 792 smp_send_stop();
726 sprintf(buf, "STORE STATUS"); 793 sprintf(buf, "STORE STATUS");
727 __cpcmd(buf, NULL, 0, NULL); 794 __cpcmd(buf, NULL, 0, NULL);
728 sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno); 795 sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno);
729 __cpcmd(buf, NULL, 0, NULL); 796 __cpcmd(buf, NULL, 0, NULL);
730 break; 797 break;
731 case IPL_METHOD_CCW_DIAG: 798 case DUMP_METHOD_CCW_DIAG:
732 diag308(DIAG308_SET, dump_block_ccw); 799 diag308(DIAG308_SET, dump_block_ccw);
733 diag308(DIAG308_DUMP, NULL); 800 diag308(DIAG308_DUMP, NULL);
734 break; 801 break;
735 case IPL_METHOD_FCP_RW_DIAG: 802 case DUMP_METHOD_FCP_DIAG:
736 diag308(DIAG308_SET, dump_block_fcp); 803 diag308(DIAG308_SET, dump_block_fcp);
737 diag308(DIAG308_DUMP, NULL); 804 diag308(DIAG308_DUMP, NULL);
738 break; 805 break;
739 case IPL_METHOD_NONE: 806 case DUMP_METHOD_NONE:
740 default: 807 default:
741 return; 808 return;
742 } 809 }
@@ -777,12 +844,13 @@ static int __init ipl_init(void)
777 rc = firmware_register(&ipl_subsys); 844 rc = firmware_register(&ipl_subsys);
778 if (rc) 845 if (rc)
779 return rc; 846 return rc;
780 switch (ipl_get_type()) { 847 switch (ipl_info.type) {
781 case IPL_TYPE_CCW: 848 case IPL_TYPE_CCW:
782 rc = sysfs_create_group(&ipl_subsys.kset.kobj, 849 rc = sysfs_create_group(&ipl_subsys.kset.kobj,
783 &ipl_ccw_attr_group); 850 &ipl_ccw_attr_group);
784 break; 851 break;
785 case IPL_TYPE_FCP: 852 case IPL_TYPE_FCP:
853 case IPL_TYPE_FCP_DUMP:
786 rc = ipl_register_fcp_files(); 854 rc = ipl_register_fcp_files();
787 break; 855 break;
788 case IPL_TYPE_NSS: 856 case IPL_TYPE_NSS:
@@ -852,7 +920,7 @@ static int __init reipl_ccw_init(void)
852 /* FIXME: check for diag308_set_works when enabling diag ccw reipl */ 920 /* FIXME: check for diag308_set_works when enabling diag ccw reipl */
853 if (!MACHINE_IS_VM) 921 if (!MACHINE_IS_VM)
854 sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO; 922 sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO;
855 if (ipl_get_type() == IPL_TYPE_CCW) 923 if (ipl_info.type == IPL_TYPE_CCW)
856 reipl_block_ccw->ipl_info.ccw.devno = ipl_devno; 924 reipl_block_ccw->ipl_info.ccw.devno = ipl_devno;
857 reipl_capabilities |= IPL_TYPE_CCW; 925 reipl_capabilities |= IPL_TYPE_CCW;
858 return 0; 926 return 0;
@@ -862,9 +930,9 @@ static int __init reipl_fcp_init(void)
862{ 930{
863 int rc; 931 int rc;
864 932
865 if ((!diag308_set_works) && (ipl_get_type() != IPL_TYPE_FCP)) 933 if ((!diag308_set_works) && (ipl_info.type != IPL_TYPE_FCP))
866 return 0; 934 return 0;
867 if ((!diag308_set_works) && (ipl_get_type() == IPL_TYPE_FCP)) 935 if ((!diag308_set_works) && (ipl_info.type == IPL_TYPE_FCP))
868 make_attrs_ro(reipl_fcp_attrs); 936 make_attrs_ro(reipl_fcp_attrs);
869 937
870 reipl_block_fcp = (void *) get_zeroed_page(GFP_KERNEL); 938 reipl_block_fcp = (void *) get_zeroed_page(GFP_KERNEL);
@@ -875,7 +943,7 @@ static int __init reipl_fcp_init(void)
875 free_page((unsigned long)reipl_block_fcp); 943 free_page((unsigned long)reipl_block_fcp);
876 return rc; 944 return rc;
877 } 945 }
878 if (ipl_get_type() == IPL_TYPE_FCP) { 946 if (ipl_info.type == IPL_TYPE_FCP) {
879 memcpy(reipl_block_fcp, IPL_PARMBLOCK_START, PAGE_SIZE); 947 memcpy(reipl_block_fcp, IPL_PARMBLOCK_START, PAGE_SIZE);
880 } else { 948 } else {
881 reipl_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN; 949 reipl_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN;
@@ -909,7 +977,7 @@ static int __init reipl_init(void)
909 rc = reipl_nss_init(); 977 rc = reipl_nss_init();
910 if (rc) 978 if (rc)
911 return rc; 979 return rc;
912 rc = reipl_set_type(ipl_get_type()); 980 rc = reipl_set_type(ipl_info.type);
913 if (rc) 981 if (rc)
914 return rc; 982 return rc;
915 return 0; 983 return 0;
@@ -931,7 +999,7 @@ static int __init dump_ccw_init(void)
931 dump_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION; 999 dump_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION;
932 dump_block_ccw->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN; 1000 dump_block_ccw->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN;
933 dump_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW; 1001 dump_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW;
934 dump_capabilities |= IPL_TYPE_CCW; 1002 dump_capabilities |= DUMP_TYPE_CCW;
935 return 0; 1003 return 0;
936} 1004}
937 1005
@@ -956,7 +1024,7 @@ static int __init dump_fcp_init(void)
956 dump_block_fcp->hdr.blk0_len = IPL_PARM_BLK0_FCP_LEN; 1024 dump_block_fcp->hdr.blk0_len = IPL_PARM_BLK0_FCP_LEN;
957 dump_block_fcp->hdr.pbt = DIAG308_IPL_TYPE_FCP; 1025 dump_block_fcp->hdr.pbt = DIAG308_IPL_TYPE_FCP;
958 dump_block_fcp->ipl_info.fcp.opt = DIAG308_IPL_OPT_DUMP; 1026 dump_block_fcp->ipl_info.fcp.opt = DIAG308_IPL_OPT_DUMP;
959 dump_capabilities |= IPL_TYPE_FCP; 1027 dump_capabilities |= DUMP_TYPE_FCP;
960 return 0; 1028 return 0;
961} 1029}
962 1030
@@ -995,7 +1063,7 @@ static int __init dump_init(void)
995 rc = dump_fcp_init(); 1063 rc = dump_fcp_init();
996 if (rc) 1064 if (rc)
997 return rc; 1065 return rc;
998 dump_set_type(IPL_TYPE_NONE); 1066 dump_set_type(DUMP_TYPE_NONE);
999 return 0; 1067 return 0;
1000} 1068}
1001 1069
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index 863c8d08c026..3dfd0985861c 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -285,6 +285,26 @@ static void __init conmode_default(void)
285 } 285 }
286} 286}
287 287
288#if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE)
289static void __init setup_zfcpdump(unsigned int console_devno)
290{
291 static char str[64];
292
293 if (ipl_info.type != IPL_TYPE_FCP_DUMP)
294 return;
295 if (console_devno != -1)
296 sprintf(str, "cio_ignore=all,!0.0.%04x,!0.0.%04x",
297 ipl_info.data.fcp.dev_id.devno, console_devno);
298 else
299 sprintf(str, "cio_ignore=all,!0.0.%04x",
300 ipl_info.data.fcp.dev_id.devno);
301 strcat(COMMAND_LINE, str);
302 console_loglevel = 2;
303}
304#else
305static inline void setup_zfcpdump(unsigned int console_devno) {}
306#endif /* CONFIG_ZFCPDUMP */
307
288#ifdef CONFIG_SMP 308#ifdef CONFIG_SMP
289void (*_machine_restart)(char *command) = machine_restart_smp; 309void (*_machine_restart)(char *command) = machine_restart_smp;
290void (*_machine_halt)(void) = machine_halt_smp; 310void (*_machine_halt)(void) = machine_halt_smp;
@@ -586,13 +606,20 @@ setup_resources(void)
586 } 606 }
587} 607}
588 608
609unsigned long real_memory_size;
610EXPORT_SYMBOL_GPL(real_memory_size);
611
589static void __init setup_memory_end(void) 612static void __init setup_memory_end(void)
590{ 613{
591 unsigned long real_size, memory_size; 614 unsigned long memory_size;
592 unsigned long max_mem, max_phys; 615 unsigned long max_mem, max_phys;
593 int i; 616 int i;
594 617
595 memory_size = real_size = 0; 618#if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE)
619 if (ipl_info.type == IPL_TYPE_FCP_DUMP)
620 memory_end = ZFCPDUMP_HSA_SIZE;
621#endif
622 memory_size = 0;
596 max_phys = VMALLOC_END_INIT - VMALLOC_MIN_SIZE; 623 max_phys = VMALLOC_END_INIT - VMALLOC_MIN_SIZE;
597 memory_end &= PAGE_MASK; 624 memory_end &= PAGE_MASK;
598 625
@@ -601,7 +628,8 @@ static void __init setup_memory_end(void)
601 for (i = 0; i < MEMORY_CHUNKS; i++) { 628 for (i = 0; i < MEMORY_CHUNKS; i++) {
602 struct mem_chunk *chunk = &memory_chunk[i]; 629 struct mem_chunk *chunk = &memory_chunk[i];
603 630
604 real_size = max(real_size, chunk->addr + chunk->size); 631 real_memory_size = max(real_memory_size,
632 chunk->addr + chunk->size);
605 if (chunk->addr >= max_mem) { 633 if (chunk->addr >= max_mem) {
606 memset(chunk, 0, sizeof(*chunk)); 634 memset(chunk, 0, sizeof(*chunk));
607 continue; 635 continue;
@@ -765,6 +793,7 @@ setup_arch(char **cmdline_p)
765 793
766 parse_early_param(); 794 parse_early_param();
767 795
796 setup_ipl_info();
768 setup_memory_end(); 797 setup_memory_end();
769 setup_addressing_mode(); 798 setup_addressing_mode();
770 setup_memory(); 799 setup_memory();
@@ -782,6 +811,9 @@ setup_arch(char **cmdline_p)
782 811
783 /* Setup default console */ 812 /* Setup default console */
784 conmode_default(); 813 conmode_default();
814
815 /* Setup zfcpdump support */
816 setup_zfcpdump(console_devno);
785} 817}
786 818
787void print_cpu_info(struct cpuinfo_S390 *cpuinfo) 819void print_cpu_info(struct cpuinfo_S390 *cpuinfo)
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
index 97764f710bb7..7c0143fdf710 100644
--- a/arch/s390/kernel/smp.c
+++ b/arch/s390/kernel/smp.c
@@ -31,6 +31,7 @@
31#include <linux/interrupt.h> 31#include <linux/interrupt.h>
32#include <linux/cpu.h> 32#include <linux/cpu.h>
33#include <linux/timex.h> 33#include <linux/timex.h>
34#include <linux/bootmem.h>
34#include <asm/ipl.h> 35#include <asm/ipl.h>
35#include <asm/setup.h> 36#include <asm/setup.h>
36#include <asm/sigp.h> 37#include <asm/sigp.h>
@@ -40,6 +41,7 @@
40#include <asm/cpcmd.h> 41#include <asm/cpcmd.h>
41#include <asm/tlbflush.h> 42#include <asm/tlbflush.h>
42#include <asm/timer.h> 43#include <asm/timer.h>
44#include <asm/lowcore.h>
43 45
44extern volatile int __cpu_logical_map[]; 46extern volatile int __cpu_logical_map[];
45 47
@@ -395,6 +397,65 @@ void smp_ctl_clear_bit(int cr, int bit)
395 on_each_cpu(smp_ctl_bit_callback, &parms, 0, 1); 397 on_each_cpu(smp_ctl_bit_callback, &parms, 0, 1);
396} 398}
397 399
400#if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE)
401
402/*
403 * zfcpdump_prefix_array holds prefix registers for the following scenario:
404 * 64 bit zfcpdump kernel and 31 bit kernel which is to be dumped. We have to
405 * save its prefix registers, since they get lost, when switching from 31 bit
406 * to 64 bit.
407 */
408unsigned int zfcpdump_prefix_array[NR_CPUS + 1] \
409 __attribute__((__section__(".data")));
410
411static void __init smp_get_save_areas(void)
412{
413 unsigned int cpu, cpu_num, rc;
414 __u16 boot_cpu_addr;
415
416 if (ipl_info.type != IPL_TYPE_FCP_DUMP)
417 return;
418 boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr;
419 cpu_num = 1;
420 for (cpu = 0; cpu <= 65535; cpu++) {
421 if ((u16) cpu == boot_cpu_addr)
422 continue;
423 __cpu_logical_map[1] = (__u16) cpu;
424 if (signal_processor(1, sigp_sense) == sigp_not_operational)
425 continue;
426 if (cpu_num >= NR_CPUS) {
427 printk("WARNING: Registers for cpu %i are not "
428 "saved, since dump kernel was compiled with"
429 "NR_CPUS=%i!\n", cpu_num, NR_CPUS);
430 continue;
431 }
432 zfcpdump_save_areas[cpu_num] =
433 alloc_bootmem(sizeof(union save_area));
434 while (1) {
435 rc = signal_processor(1, sigp_stop_and_store_status);
436 if (rc != sigp_busy)
437 break;
438 cpu_relax();
439 }
440 memcpy(zfcpdump_save_areas[cpu_num],
441 (void *)(unsigned long) store_prefix() +
442 SAVE_AREA_BASE, SAVE_AREA_SIZE);
443#ifdef __s390x__
444 /* copy original prefix register */
445 zfcpdump_save_areas[cpu_num]->s390x.pref_reg =
446 zfcpdump_prefix_array[cpu_num];
447#endif
448 cpu_num++;
449 }
450}
451
452union save_area *zfcpdump_save_areas[NR_CPUS + 1];
453EXPORT_SYMBOL_GPL(zfcpdump_save_areas);
454
455#else
456#define smp_get_save_areas() do { } while (0)
457#endif
458
398/* 459/*
399 * Lets check how many CPUs we have. 460 * Lets check how many CPUs we have.
400 */ 461 */
@@ -589,6 +650,7 @@ void __init smp_setup_cpu_possible_map(void)
589{ 650{
590 unsigned int phy_cpus, pos_cpus, cpu; 651 unsigned int phy_cpus, pos_cpus, cpu;
591 652
653 smp_get_save_areas();
592 phy_cpus = smp_count_cpus(); 654 phy_cpus = smp_count_cpus();
593 pos_cpus = min(phy_cpus + additional_cpus, (unsigned int) NR_CPUS); 655 pos_cpus = min(phy_cpus + additional_cpus, (unsigned int) NR_CPUS);
594 656
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile
index 5fd581c22db3..a0f6db21855a 100644
--- a/drivers/s390/char/Makefile
+++ b/drivers/s390/char/Makefile
@@ -29,3 +29,6 @@ obj-$(CONFIG_S390_TAPE_34XX) += tape_34xx.o
29obj-$(CONFIG_S390_TAPE_3590) += tape_3590.o 29obj-$(CONFIG_S390_TAPE_3590) += tape_3590.o
30obj-$(CONFIG_MONREADER) += monreader.o 30obj-$(CONFIG_MONREADER) += monreader.o
31obj-$(CONFIG_MONWRITER) += monwriter.o 31obj-$(CONFIG_MONWRITER) += monwriter.o
32
33zcore_mod-objs := sclp_sdias.o zcore.o
34obj-$(CONFIG_ZFCPDUMP) += zcore_mod.o
diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h
index 7d29ab45a6ed..6402e943436f 100644
--- a/drivers/s390/char/sclp.h
+++ b/drivers/s390/char/sclp.h
@@ -27,6 +27,7 @@
27#define EvTyp_CntlProgIdent 0x0B 27#define EvTyp_CntlProgIdent 0x0B
28#define EvTyp_SigQuiesce 0x1D 28#define EvTyp_SigQuiesce 0x1D
29#define EvTyp_VT220Msg 0x1A 29#define EvTyp_VT220Msg 0x1A
30#define EvTyp_SDIAS 0x1C
30 31
31#define EvTyp_OpCmd_Mask 0x80000000 32#define EvTyp_OpCmd_Mask 0x80000000
32#define EvTyp_Msg_Mask 0x40000000 33#define EvTyp_Msg_Mask 0x40000000
@@ -36,6 +37,7 @@
36#define EvTyp_CtlProgIdent_Mask 0x00200000 37#define EvTyp_CtlProgIdent_Mask 0x00200000
37#define EvTyp_SigQuiesce_Mask 0x00000008 38#define EvTyp_SigQuiesce_Mask 0x00000008
38#define EvTyp_VT220Msg_Mask 0x00000040 39#define EvTyp_VT220Msg_Mask 0x00000040
40#define EvTyp_SDIAS_Mask 0x00000010
39 41
40#define GnrlMsgFlgs_DOM 0x8000 42#define GnrlMsgFlgs_DOM 0x8000
41#define GnrlMsgFlgs_SndAlrm 0x4000 43#define GnrlMsgFlgs_SndAlrm 0x4000
diff --git a/drivers/s390/char/sclp_sdias.c b/drivers/s390/char/sclp_sdias.c
new file mode 100644
index 000000000000..06a4d0897232
--- /dev/null
+++ b/drivers/s390/char/sclp_sdias.c
@@ -0,0 +1,255 @@
1/*
2 * Sclp "store data in absolut storage"
3 *
4 * Copyright IBM Corp. 2003,2007
5 * Author(s): Michael Holzheu
6 */
7
8#include <linux/sched.h>
9#include <asm/sclp.h>
10#include <asm/debug.h>
11#include <asm/ipl.h>
12#include "sclp.h"
13#include "sclp_rw.h"
14
15#define TRACE(x...) debug_sprintf_event(sdias_dbf, 1, x)
16#define ERROR_MSG(x...) printk ( KERN_ALERT "SDIAS: " x )
17
18#define SDIAS_RETRIES 300
19#define SDIAS_SLEEP_TICKS 50
20
21#define EQ_STORE_DATA 0x0
22#define EQ_SIZE 0x1
23#define DI_FCP_DUMP 0x0
24#define ASA_SIZE_32 0x0
25#define ASA_SIZE_64 0x1
26#define EVSTATE_ALL_STORED 0x0
27#define EVSTATE_NO_DATA 0x3
28#define EVSTATE_PART_STORED 0x10
29
30static struct debug_info *sdias_dbf;
31
32static struct sclp_register sclp_sdias_register = {
33 .send_mask = EvTyp_SDIAS_Mask,
34};
35
36struct sdias_evbuf {
37 struct evbuf_header hdr;
38 u8 event_qual;
39 u8 data_id;
40 u64 reserved2;
41 u32 event_id;
42 u16 reserved3;
43 u8 asa_size;
44 u8 event_status;
45 u32 reserved4;
46 u32 blk_cnt;
47 u64 asa;
48 u32 reserved5;
49 u32 fbn;
50 u32 reserved6;
51 u32 lbn;
52 u16 reserved7;
53 u16 dbs;
54} __attribute__((packed));
55
56struct sdias_sccb {
57 struct sccb_header hdr;
58 struct sdias_evbuf evbuf;
59} __attribute__((packed));
60
61static struct sdias_sccb sccb __attribute__((aligned(4096)));
62
63static int sclp_req_done;
64static wait_queue_head_t sdias_wq;
65static DEFINE_MUTEX(sdias_mutex);
66
67static void sdias_callback(struct sclp_req *request, void *data)
68{
69 struct sdias_sccb *sccb;
70
71 sccb = (struct sdias_sccb *) request->sccb;
72 sclp_req_done = 1;
73 wake_up(&sdias_wq); /* Inform caller, that request is complete */
74 TRACE("callback done\n");
75}
76
77static int sdias_sclp_send(struct sclp_req *req)
78{
79 int retries;
80 int rc;
81
82 for (retries = SDIAS_RETRIES; retries; retries--) {
83 sclp_req_done = 0;
84 TRACE("add request\n");
85 rc = sclp_add_request(req);
86 if (rc) {
87 /* not initiated, wait some time and retry */
88 set_current_state(TASK_INTERRUPTIBLE);
89 TRACE("add request failed: rc = %i\n",rc);
90 schedule_timeout(SDIAS_SLEEP_TICKS);
91 continue;
92 }
93 /* initiated, wait for completion of service call */
94 wait_event(sdias_wq, (sclp_req_done == 1));
95 if (req->status == SCLP_REQ_FAILED) {
96 TRACE("sclp request failed\n");
97 rc = -EIO;
98 continue;
99 }
100 TRACE("request done\n");
101 break;
102 }
103 return rc;
104}
105
106/*
107 * Get number of blocks (4K) available in the HSA
108 */
109int sclp_sdias_blk_count(void)
110{
111 struct sclp_req request;
112 int rc;
113
114 mutex_lock(&sdias_mutex);
115
116 memset(&sccb, 0, sizeof(sccb));
117 memset(&request, 0, sizeof(request));
118
119 sccb.hdr.length = sizeof(sccb);
120 sccb.evbuf.hdr.length = sizeof(struct sdias_evbuf);
121 sccb.evbuf.hdr.type = EvTyp_SDIAS;
122 sccb.evbuf.event_qual = EQ_SIZE;
123 sccb.evbuf.data_id = DI_FCP_DUMP;
124 sccb.evbuf.event_id = 4712;
125 sccb.evbuf.dbs = 1;
126
127 request.sccb = &sccb;
128 request.command = SCLP_CMDW_WRITE_EVENT_DATA;
129 request.status = SCLP_REQ_FILLED;
130 request.callback = sdias_callback;
131
132 rc = sdias_sclp_send(&request);
133 if (rc) {
134 ERROR_MSG("sclp_send failed for get_nr_blocks\n");
135 goto out;
136 }
137 if (sccb.hdr.response_code != 0x0020) {
138 TRACE("send failed: %x\n", sccb.hdr.response_code);
139 rc = -EIO;
140 goto out;
141 }
142
143 switch (sccb.evbuf.event_status) {
144 case 0:
145 rc = sccb.evbuf.blk_cnt;
146 break;
147 default:
148 ERROR_MSG("SCLP error: %x\n", sccb.evbuf.event_status);
149 rc = -EIO;
150 goto out;
151 }
152 TRACE("%i blocks\n", rc);
153out:
154 mutex_unlock(&sdias_mutex);
155 return rc;
156}
157
158/*
159 * Copy from HSA to absolute storage (not reentrant):
160 *
161 * @dest : Address of buffer where data should be copied
162 * @start_blk: Start Block (beginning with 1)
163 * @nr_blks : Number of 4K blocks to copy
164 *
165 * Return Value: 0 : Requested 'number' of blocks of data copied
166 * <0: ERROR - negative event status
167 */
168int sclp_sdias_copy(void *dest, int start_blk, int nr_blks)
169{
170 struct sclp_req request;
171 int rc;
172
173 mutex_lock(&sdias_mutex);
174
175 memset(&sccb, 0, sizeof(sccb));
176 memset(&request, 0, sizeof(request));
177
178 sccb.hdr.length = sizeof(sccb);
179 sccb.evbuf.hdr.length = sizeof(struct sdias_evbuf);
180 sccb.evbuf.hdr.type = EvTyp_SDIAS;
181 sccb.evbuf.hdr.flags = 0;
182 sccb.evbuf.event_qual = EQ_STORE_DATA;
183 sccb.evbuf.data_id = DI_FCP_DUMP;
184 sccb.evbuf.event_id = 4712;
185#ifdef __s390x__
186 sccb.evbuf.asa_size = ASA_SIZE_64;
187#else
188 sccb.evbuf.asa_size = ASA_SIZE_32;
189#endif
190 sccb.evbuf.event_status = 0;
191 sccb.evbuf.blk_cnt = nr_blks;
192 sccb.evbuf.asa = (unsigned long)dest;
193 sccb.evbuf.fbn = start_blk;
194 sccb.evbuf.lbn = 0;
195 sccb.evbuf.dbs = 1;
196
197 request.sccb = &sccb;
198 request.command = SCLP_CMDW_WRITE_EVENT_DATA;
199 request.status = SCLP_REQ_FILLED;
200 request.callback = sdias_callback;
201
202 rc = sdias_sclp_send(&request);
203 if (rc) {
204 ERROR_MSG("sclp_send failed: %x\n", rc);
205 goto out;
206 }
207 if (sccb.hdr.response_code != 0x0020) {
208 TRACE("copy failed: %x\n", sccb.hdr.response_code);
209 rc = -EIO;
210 goto out;
211 }
212
213 switch (sccb.evbuf.event_status) {
214 case EVSTATE_ALL_STORED:
215 TRACE("all stored\n");
216 case EVSTATE_PART_STORED:
217 TRACE("part stored: %i\n", sccb.evbuf.blk_cnt);
218 break;
219 case EVSTATE_NO_DATA:
220 TRACE("no data\n");
221 default:
222 ERROR_MSG("Error from SCLP while copying hsa. "
223 "Event status = %x\n",
224 sccb.evbuf.event_status);
225 rc = -EIO;
226 }
227out:
228 mutex_unlock(&sdias_mutex);
229 return rc;
230}
231
232int __init sdias_init(void)
233{
234 int rc;
235
236 if (ipl_info.type != IPL_TYPE_FCP_DUMP)
237 return 0;
238 sdias_dbf = debug_register("dump_sdias", 4, 1, 4 * sizeof(long));
239 debug_register_view(sdias_dbf, &debug_sprintf_view);
240 debug_set_level(sdias_dbf, 6);
241 rc = sclp_register(&sclp_sdias_register);
242 if (rc) {
243 ERROR_MSG("sclp register failed\n");
244 return rc;
245 }
246 init_waitqueue_head(&sdias_wq);
247 TRACE("init done\n");
248 return 0;
249}
250
251void __exit sdias_exit(void)
252{
253 debug_unregister(sdias_dbf);
254 sclp_unregister(&sclp_sdias_register);
255}
diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c
new file mode 100644
index 000000000000..89d439316a53
--- /dev/null
+++ b/drivers/s390/char/zcore.c
@@ -0,0 +1,651 @@
1/*
2 * zcore module to export memory content and register sets for creating system
3 * dumps on SCSI disks (zfcpdump). The "zcore/mem" debugfs file shows the same
4 * dump format as s390 standalone dumps.
5 *
6 * For more information please refer to Documentation/s390/zfcpdump.txt
7 *
8 * Copyright IBM Corp. 2003,2007
9 * Author(s): Michael Holzheu
10 */
11
12#include <linux/init.h>
13#include <linux/miscdevice.h>
14#include <linux/utsname.h>
15#include <linux/debugfs.h>
16#include <asm/ipl.h>
17#include <asm/sclp.h>
18#include <asm/setup.h>
19#include <asm/sigp.h>
20#include <asm/uaccess.h>
21#include <asm/debug.h>
22#include <asm/processor.h>
23#include <asm/irqflags.h>
24
25#define TRACE(x...) debug_sprintf_event(zcore_dbf, 1, x)
26#define MSG(x...) printk( KERN_ALERT x )
27#define ERROR_MSG(x...) printk ( KERN_ALERT "DUMP: " x )
28
29#define TO_USER 0
30#define TO_KERNEL 1
31
32enum arch_id {
33 ARCH_S390 = 0,
34 ARCH_S390X = 1,
35};
36
37/* dump system info */
38
39struct sys_info {
40 enum arch_id arch;
41 unsigned long sa_base;
42 u32 sa_size;
43 int cpu_map[NR_CPUS];
44 unsigned long mem_size;
45 union save_area lc_mask;
46};
47
48static struct sys_info sys_info;
49static struct debug_info *zcore_dbf;
50static int hsa_available;
51static struct dentry *zcore_dir;
52static struct dentry *zcore_file;
53
54/*
55 * Copy memory from HSA to kernel or user memory (not reentrant):
56 *
57 * @dest: Kernel or user buffer where memory should be copied to
58 * @src: Start address within HSA where data should be copied
59 * @count: Size of buffer, which should be copied
60 * @mode: Either TO_KERNEL or TO_USER
61 */
62static int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode)
63{
64 int offs, blk_num;
65 static char buf[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
66
67 if (count == 0)
68 return 0;
69
70 /* copy first block */
71 offs = 0;
72 if ((src % PAGE_SIZE) != 0) {
73 blk_num = src / PAGE_SIZE + 2;
74 if (sclp_sdias_copy(buf, blk_num, 1)) {
75 TRACE("sclp_sdias_copy() failed\n");
76 return -EIO;
77 }
78 offs = min((PAGE_SIZE - (src % PAGE_SIZE)), count);
79 if (mode == TO_USER) {
80 if (copy_to_user((__force __user void*) dest,
81 buf + (src % PAGE_SIZE), offs))
82 return -EFAULT;
83 } else
84 memcpy(dest, buf + (src % PAGE_SIZE), offs);
85 }
86 if (offs == count)
87 goto out;
88
89 /* copy middle */
90 for (; (offs + PAGE_SIZE) <= count; offs += PAGE_SIZE) {
91 blk_num = (src + offs) / PAGE_SIZE + 2;
92 if (sclp_sdias_copy(buf, blk_num, 1)) {
93 TRACE("sclp_sdias_copy() failed\n");
94 return -EIO;
95 }
96 if (mode == TO_USER) {
97 if (copy_to_user((__force __user void*) dest + offs,
98 buf, PAGE_SIZE))
99 return -EFAULT;
100 } else
101 memcpy(dest + offs, buf, PAGE_SIZE);
102 }
103 if (offs == count)
104 goto out;
105
106 /* copy last block */
107 blk_num = (src + offs) / PAGE_SIZE + 2;
108 if (sclp_sdias_copy(buf, blk_num, 1)) {
109 TRACE("sclp_sdias_copy() failed\n");
110 return -EIO;
111 }
112 if (mode == TO_USER) {
113 if (copy_to_user((__force __user void*) dest + offs, buf,
114 PAGE_SIZE))
115 return -EFAULT;
116 } else
117 memcpy(dest + offs, buf, count - offs);
118out:
119 return 0;
120}
121
122static int memcpy_hsa_user(void __user *dest, unsigned long src, size_t count)
123{
124 return memcpy_hsa((void __force *) dest, src, count, TO_USER);
125}
126
127static int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count)
128{
129 return memcpy_hsa(dest, src, count, TO_KERNEL);
130}
131
132static int memcpy_real(void *dest, unsigned long src, size_t count)
133{
134 unsigned long flags;
135 int rc = -EFAULT;
136 register unsigned long _dest asm("2") = (unsigned long) dest;
137 register unsigned long _len1 asm("3") = (unsigned long) count;
138 register unsigned long _src asm("4") = src;
139 register unsigned long _len2 asm("5") = (unsigned long) count;
140
141 if (count == 0)
142 return 0;
143 flags = __raw_local_irq_stnsm(0xf8); /* switch to real mode */
144 asm volatile (
145 "0: mvcle %1,%2,0x0\n"
146 "1: jo 0b\n"
147 " lhi %0,0x0\n"
148 "2:\n"
149 EX_TABLE(1b,2b)
150 : "+d" (rc)
151 : "d" (_dest), "d" (_src), "d" (_len1), "d" (_len2)
152 : "cc", "memory");
153 __raw_local_irq_ssm(flags);
154
155 return rc;
156}
157
158static int memcpy_real_user(__user void *dest, unsigned long src, size_t count)
159{
160 static char buf[4096];
161 int offs = 0, size;
162
163 while (offs < count) {
164 size = min(sizeof(buf), count - offs);
165 if (memcpy_real(buf, src + offs, size))
166 return -EFAULT;
167 if (copy_to_user(dest + offs, buf, size))
168 return -EFAULT;
169 offs += size;
170 }
171 return 0;
172}
173
174#ifdef __s390x__
175/*
176 * Convert s390x (64 bit) cpu info to s390 (32 bit) cpu info
177 */
178static void __init s390x_to_s390_regs(union save_area *out, union save_area *in,
179 int cpu)
180{
181 int i;
182
183 for (i = 0; i < 16; i++) {
184 out->s390.gp_regs[i] = in->s390x.gp_regs[i] & 0x00000000ffffffff;
185 out->s390.acc_regs[i] = in->s390x.acc_regs[i];
186 out->s390.ctrl_regs[i] =
187 in->s390x.ctrl_regs[i] & 0x00000000ffffffff;
188 }
189 /* locore for 31 bit has only space for fpregs 0,2,4,6 */
190 out->s390.fp_regs[0] = in->s390x.fp_regs[0];
191 out->s390.fp_regs[1] = in->s390x.fp_regs[2];
192 out->s390.fp_regs[2] = in->s390x.fp_regs[4];
193 out->s390.fp_regs[3] = in->s390x.fp_regs[6];
194 memcpy(&(out->s390.psw[0]), &(in->s390x.psw[0]), 4);
195 out->s390.psw[1] |= 0x8; /* set bit 12 */
196 memcpy(&(out->s390.psw[4]),&(in->s390x.psw[12]), 4);
197 out->s390.psw[4] |= 0x80; /* set (31bit) addressing bit */
198 out->s390.pref_reg = in->s390x.pref_reg;
199 out->s390.timer = in->s390x.timer;
200 out->s390.clk_cmp = in->s390x.clk_cmp;
201}
202
203static void __init s390x_to_s390_save_areas(void)
204{
205 int i = 1;
206 static union save_area tmp;
207
208 while (zfcpdump_save_areas[i]) {
209 s390x_to_s390_regs(&tmp, zfcpdump_save_areas[i], i);
210 memcpy(zfcpdump_save_areas[i], &tmp, sizeof(tmp));
211 i++;
212 }
213}
214
215#endif /* __s390x__ */
216
217static int __init init_cpu_info(enum arch_id arch)
218{
219 union save_area *sa;
220
221 /* get info for boot cpu from lowcore, stored in the HSA */
222
223 sa = kmalloc(sizeof(*sa), GFP_KERNEL);
224 if (!sa) {
225 ERROR_MSG("kmalloc failed: %s: %i\n",__FUNCTION__, __LINE__);
226 return -ENOMEM;
227 }
228 if (memcpy_hsa_kernel(sa, sys_info.sa_base, sys_info.sa_size) < 0) {
229 ERROR_MSG("could not copy from HSA\n");
230 kfree(sa);
231 return -EIO;
232 }
233 zfcpdump_save_areas[0] = sa;
234
235#ifdef __s390x__
236 /* convert s390x regs to s390, if we are dumping an s390 Linux */
237
238 if (arch == ARCH_S390)
239 s390x_to_s390_save_areas();
240#endif
241
242 return 0;
243}
244
245static DEFINE_MUTEX(zcore_mutex);
246
247#define DUMP_VERSION 0x3
248#define DUMP_MAGIC 0xa8190173618f23fdULL
249#define DUMP_ARCH_S390X 2
250#define DUMP_ARCH_S390 1
251#define HEADER_SIZE 4096
252
253/* dump header dumped according to s390 crash dump format */
254
255struct zcore_header {
256 u64 magic;
257 u32 version;
258 u32 header_size;
259 u32 dump_level;
260 u32 page_size;
261 u64 mem_size;
262 u64 mem_start;
263 u64 mem_end;
264 u32 num_pages;
265 u32 pad1;
266 u64 tod;
267 cpuid_t cpu_id;
268 u32 arch_id;
269 u32 build_arch;
270 char pad2[4016];
271} __attribute__((packed,__aligned__(16)));
272
273static struct zcore_header zcore_header = {
274 .magic = DUMP_MAGIC,
275 .version = DUMP_VERSION,
276 .header_size = 4096,
277 .dump_level = 0,
278 .page_size = PAGE_SIZE,
279 .mem_start = 0,
280#ifdef __s390x__
281 .build_arch = DUMP_ARCH_S390X,
282#else
283 .build_arch = DUMP_ARCH_S390,
284#endif
285};
286
287/*
288 * Copy lowcore info to buffer. Use map in order to copy only register parts.
289 *
290 * @buf: User buffer
291 * @sa: Pointer to save area
292 * @sa_off: Offset in save area to copy
293 * @len: Number of bytes to copy
294 */
295static int copy_lc(void __user *buf, void *sa, int sa_off, int len)
296{
297 int i;
298 char *lc_mask = (char*)&sys_info.lc_mask;
299
300 for (i = 0; i < len; i++) {
301 if (!lc_mask[i + sa_off])
302 continue;
303 if (copy_to_user(buf + i, sa + sa_off + i, 1))
304 return -EFAULT;
305 }
306 return 0;
307}
308
309/*
310 * Copy lowcores info to memory, if necessary
311 *
312 * @buf: User buffer
313 * @addr: Start address of buffer in dump memory
314 * @count: Size of buffer
315 */
316static int zcore_add_lc(char __user *buf, unsigned long start, size_t count)
317{
318 unsigned long end;
319 int i = 0;
320
321 if (count == 0)
322 return 0;
323
324 end = start + count;
325 while (zfcpdump_save_areas[i]) {
326 unsigned long cp_start, cp_end; /* copy range */
327 unsigned long sa_start, sa_end; /* save area range */
328 unsigned long prefix;
329 unsigned long sa_off, len, buf_off;
330
331 if (sys_info.arch == ARCH_S390)
332 prefix = zfcpdump_save_areas[i]->s390.pref_reg;
333 else
334 prefix = zfcpdump_save_areas[i]->s390x.pref_reg;
335
336 sa_start = prefix + sys_info.sa_base;
337 sa_end = prefix + sys_info.sa_base + sys_info.sa_size;
338
339 if ((end < sa_start) || (start > sa_end))
340 goto next;
341 cp_start = max(start, sa_start);
342 cp_end = min(end, sa_end);
343
344 buf_off = cp_start - start;
345 sa_off = cp_start - sa_start;
346 len = cp_end - cp_start;
347
348 TRACE("copy_lc for: %lx\n", start);
349 if (copy_lc(buf + buf_off, zfcpdump_save_areas[i], sa_off, len))
350 return -EFAULT;
351next:
352 i++;
353 }
354 return 0;
355}
356
357/*
358 * Read routine for zcore character device
359 * First 4K are dump header
360 * Next 32MB are HSA Memory
361 * Rest is read from absolute Memory
362 */
363static ssize_t zcore_read(struct file *file, char __user *buf, size_t count,
364 loff_t *ppos)
365{
366 unsigned long mem_start; /* Start address in memory */
367 size_t mem_offs; /* Offset in dump memory */
368 size_t hdr_count; /* Size of header part of output buffer */
369 size_t size;
370 int rc;
371
372 mutex_lock(&zcore_mutex);
373
374 if (*ppos > (sys_info.mem_size + HEADER_SIZE)) {
375 rc = -EINVAL;
376 goto fail;
377 }
378
379 count = min(count, (size_t) (sys_info.mem_size + HEADER_SIZE - *ppos));
380
381 /* Copy dump header */
382 if (*ppos < HEADER_SIZE) {
383 size = min(count, (size_t) (HEADER_SIZE - *ppos));
384 if (copy_to_user(buf, &zcore_header + *ppos, size)) {
385 rc = -EFAULT;
386 goto fail;
387 }
388 hdr_count = size;
389 mem_start = 0;
390 } else {
391 hdr_count = 0;
392 mem_start = *ppos - HEADER_SIZE;
393 }
394
395 mem_offs = 0;
396
397 /* Copy from HSA data */
398 if (*ppos < (ZFCPDUMP_HSA_SIZE + HEADER_SIZE)) {
399 size = min((count - hdr_count), (size_t) (ZFCPDUMP_HSA_SIZE
400 - mem_start));
401 rc = memcpy_hsa_user(buf + hdr_count, mem_start, size);
402 if (rc)
403 goto fail;
404
405 mem_offs += size;
406 }
407
408 /* Copy from real mem */
409 size = count - mem_offs - hdr_count;
410 rc = memcpy_real_user(buf + hdr_count + mem_offs, mem_start + mem_offs,
411 size);
412 if (rc)
413 goto fail;
414
415 /*
416 * Since s390 dump analysis tools like lcrash or crash
417 * expect register sets in the prefix pages of the cpus,
418 * we copy them into the read buffer, if necessary.
419 * buf + hdr_count: Start of memory part of output buffer
420 * mem_start: Start memory address to copy from
421 * count - hdr_count: Size of memory area to copy
422 */
423 if (zcore_add_lc(buf + hdr_count, mem_start, count - hdr_count)) {
424 rc = -EFAULT;
425 goto fail;
426 }
427 *ppos += count;
428fail:
429 mutex_unlock(&zcore_mutex);
430 return (rc < 0) ? rc : count;
431}
432
433static int zcore_open(struct inode *inode, struct file *filp)
434{
435 if (!hsa_available)
436 return -ENODATA;
437 else
438 return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
439}
440
441static int zcore_release(struct inode *inode, struct file *filep)
442{
443 diag308(DIAG308_REL_HSA, NULL);
444 hsa_available = 0;
445 return 0;
446}
447
448static loff_t zcore_lseek(struct file *file, loff_t offset, int orig)
449{
450 loff_t rc;
451
452 mutex_lock(&zcore_mutex);
453 switch (orig) {
454 case 0:
455 file->f_pos = offset;
456 rc = file->f_pos;
457 break;
458 case 1:
459 file->f_pos += offset;
460 rc = file->f_pos;
461 break;
462 default:
463 rc = -EINVAL;
464 }
465 mutex_unlock(&zcore_mutex);
466 return rc;
467}
468
469static struct file_operations zcore_fops = {
470 .owner = THIS_MODULE,
471 .llseek = zcore_lseek,
472 .read = zcore_read,
473 .open = zcore_open,
474 .release = zcore_release,
475};
476
477
478static void __init set_s390_lc_mask(union save_area *map)
479{
480 memset(&map->s390.ext_save, 0xff, sizeof(map->s390.ext_save));
481 memset(&map->s390.timer, 0xff, sizeof(map->s390.timer));
482 memset(&map->s390.clk_cmp, 0xff, sizeof(map->s390.clk_cmp));
483 memset(&map->s390.psw, 0xff, sizeof(map->s390.psw));
484 memset(&map->s390.pref_reg, 0xff, sizeof(map->s390.pref_reg));
485 memset(&map->s390.acc_regs, 0xff, sizeof(map->s390.acc_regs));
486 memset(&map->s390.fp_regs, 0xff, sizeof(map->s390.fp_regs));
487 memset(&map->s390.gp_regs, 0xff, sizeof(map->s390.gp_regs));
488 memset(&map->s390.ctrl_regs, 0xff, sizeof(map->s390.ctrl_regs));
489}
490
491static void __init set_s390x_lc_mask(union save_area *map)
492{
493 memset(&map->s390x.fp_regs, 0xff, sizeof(map->s390x.fp_regs));
494 memset(&map->s390x.gp_regs, 0xff, sizeof(map->s390x.gp_regs));
495 memset(&map->s390x.psw, 0xff, sizeof(map->s390x.psw));
496 memset(&map->s390x.pref_reg, 0xff, sizeof(map->s390x.pref_reg));
497 memset(&map->s390x.fp_ctrl_reg, 0xff, sizeof(map->s390x.fp_ctrl_reg));
498 memset(&map->s390x.tod_reg, 0xff, sizeof(map->s390x.tod_reg));
499 memset(&map->s390x.timer, 0xff, sizeof(map->s390x.timer));
500 memset(&map->s390x.clk_cmp, 0xff, sizeof(map->s390x.clk_cmp));
501 memset(&map->s390x.acc_regs, 0xff, sizeof(map->s390x.acc_regs));
502 memset(&map->s390x.ctrl_regs, 0xff, sizeof(map->s390x.ctrl_regs));
503}
504
505/*
506 * Initialize dump globals for a given architecture
507 */
508static int __init sys_info_init(enum arch_id arch)
509{
510 switch (arch) {
511 case ARCH_S390X:
512 MSG("DETECTED 'S390X (64 bit) OS'\n");
513 sys_info.sa_base = SAVE_AREA_BASE_S390X;
514 sys_info.sa_size = sizeof(struct save_area_s390x);
515 set_s390x_lc_mask(&sys_info.lc_mask);
516 break;
517 case ARCH_S390:
518 MSG("DETECTED 'S390 (32 bit) OS'\n");
519 sys_info.sa_base = SAVE_AREA_BASE_S390;
520 sys_info.sa_size = sizeof(struct save_area_s390);
521 set_s390_lc_mask(&sys_info.lc_mask);
522 break;
523 default:
524 ERROR_MSG("unknown architecture 0x%x.\n",arch);
525 return -EINVAL;
526 }
527 sys_info.arch = arch;
528 if (init_cpu_info(arch)) {
529 ERROR_MSG("get cpu info failed\n");
530 return -ENOMEM;
531 }
532 sys_info.mem_size = real_memory_size;
533
534 return 0;
535}
536
537static int __init check_sdias(void)
538{
539 int rc, act_hsa_size;
540
541 rc = sclp_sdias_blk_count();
542 if (rc < 0) {
543 ERROR_MSG("Could not determine HSA size\n");
544 return rc;
545 }
546 act_hsa_size = (rc - 1) * PAGE_SIZE;
547 if (act_hsa_size < ZFCPDUMP_HSA_SIZE) {
548 ERROR_MSG("HSA size too small: %i\n", act_hsa_size);
549 return -EINVAL;
550 }
551 return 0;
552}
553
554static void __init zcore_header_init(int arch, struct zcore_header *hdr)
555{
556 if (arch == ARCH_S390X)
557 hdr->arch_id = DUMP_ARCH_S390X;
558 else
559 hdr->arch_id = DUMP_ARCH_S390;
560 hdr->mem_size = sys_info.mem_size;
561 hdr->mem_end = sys_info.mem_size;
562 hdr->num_pages = sys_info.mem_size / PAGE_SIZE;
563 hdr->tod = get_clock();
564 get_cpu_id(&hdr->cpu_id);
565}
566
567extern int sdias_init(void);
568
569static int __init zcore_init(void)
570{
571 unsigned char arch;
572 int rc;
573
574 if (ipl_info.type != IPL_TYPE_FCP_DUMP)
575 return -ENODATA;
576
577 zcore_dbf = debug_register("zcore", 4, 1, 4 * sizeof(long));
578 debug_register_view(zcore_dbf, &debug_sprintf_view);
579 debug_set_level(zcore_dbf, 6);
580
581 TRACE("devno: %x\n", ipl_info.data.fcp.dev_id.devno);
582 TRACE("wwpn: %llx\n", (unsigned long long) ipl_info.data.fcp.wwpn);
583 TRACE("lun: %llx\n", (unsigned long long) ipl_info.data.fcp.lun);
584
585 rc = sdias_init();
586 if (rc)
587 goto fail;
588
589 rc = check_sdias();
590 if (rc) {
591 ERROR_MSG("Dump initialization failed\n");
592 goto fail;
593 }
594
595 rc = memcpy_hsa_kernel(&arch, __LC_AR_MODE_ID, 1);
596 if (rc) {
597 ERROR_MSG("sdial memcpy for arch id failed\n");
598 goto fail;
599 }
600
601#ifndef __s390x__
602 if (arch == ARCH_S390X) {
603 ERROR_MSG("32 bit dumper can't dump 64 bit system!\n");
604 rc = -EINVAL;
605 goto fail;
606 }
607#endif
608
609 rc = sys_info_init(arch);
610 if (rc) {
611 ERROR_MSG("arch init failed\n");
612 goto fail;
613 }
614
615 zcore_header_init(arch, &zcore_header);
616
617 zcore_dir = debugfs_create_dir("zcore" , NULL);
618 if (!zcore_dir) {
619 rc = -ENOMEM;
620 goto fail;
621 }
622 zcore_file = debugfs_create_file("mem", S_IRUSR, zcore_dir, NULL,
623 &zcore_fops);
624 if (!zcore_file) {
625 debugfs_remove(zcore_dir);
626 rc = -ENOMEM;
627 goto fail;
628 }
629 hsa_available = 1;
630 return 0;
631
632fail:
633 diag308(DIAG308_REL_HSA, NULL);
634 return rc;
635}
636
637extern void sdias_exit(void);
638
639static void __exit zcore_exit(void)
640{
641 debug_unregister(zcore_dbf);
642 sdias_exit();
643 diag308(DIAG308_REL_HSA, NULL);
644}
645
646MODULE_AUTHOR("Copyright IBM Corp. 2003,2007");
647MODULE_DESCRIPTION("zcore module for zfcpdump support");
648MODULE_LICENSE("GPL");
649
650subsys_initcall(zcore_init);
651module_exit(zcore_exit);
diff --git a/include/asm-s390/ipl.h b/include/asm-s390/ipl.h
index 15bb0b529551..bdcd448d43fb 100644
--- a/include/asm-s390/ipl.h
+++ b/include/asm-s390/ipl.h
@@ -8,6 +8,8 @@
8#define _ASM_S390_IPL_H 8#define _ASM_S390_IPL_H
9 9
10#include <asm/types.h> 10#include <asm/types.h>
11#include <asm/cio.h>
12#include <asm/setup.h>
11 13
12#define IPL_PARMBLOCK_ORIGIN 0x2000 14#define IPL_PARMBLOCK_ORIGIN 0x2000
13 15
@@ -79,6 +81,7 @@ struct ipl_parameter_block {
79extern u32 ipl_flags; 81extern u32 ipl_flags;
80 82
81extern u32 dump_prefix_page; 83extern u32 dump_prefix_page;
84
82extern void do_reipl(void); 85extern void do_reipl(void);
83extern void ipl_save_parameters(void); 86extern void ipl_save_parameters(void);
84 87
@@ -88,6 +91,35 @@ enum {
88 IPL_NSS_VALID = 4, 91 IPL_NSS_VALID = 4,
89}; 92};
90 93
94enum ipl_type {
95 IPL_TYPE_UNKNOWN = 1,
96 IPL_TYPE_CCW = 2,
97 IPL_TYPE_FCP = 4,
98 IPL_TYPE_FCP_DUMP = 8,
99 IPL_TYPE_NSS = 16,
100};
101
102struct ipl_info
103{
104 enum ipl_type type;
105 union {
106 struct {
107 struct ccw_dev_id dev_id;
108 } ccw;
109 struct {
110 struct ccw_dev_id dev_id;
111 u64 wwpn;
112 u64 lun;
113 } fcp;
114 struct {
115 char name[NSS_NAME_SIZE + 1];
116 } nss;
117 } data;
118};
119
120extern struct ipl_info ipl_info;
121extern void setup_ipl_info(void);
122
91/* 123/*
92 * DIAG 308 support 124 * DIAG 308 support
93 */ 125 */
diff --git a/include/asm-s390/lowcore.h b/include/asm-s390/lowcore.h
index 4a31d0a7ee83..ffc9788a21a7 100644
--- a/include/asm-s390/lowcore.h
+++ b/include/asm-s390/lowcore.h
@@ -147,6 +147,52 @@ void pgm_check_handler(void);
147void mcck_int_handler(void); 147void mcck_int_handler(void);
148void io_int_handler(void); 148void io_int_handler(void);
149 149
150struct save_area_s390 {
151 u32 ext_save;
152 u64 timer;
153 u64 clk_cmp;
154 u8 pad1[24];
155 u8 psw[8];
156 u32 pref_reg;
157 u8 pad2[20];
158 u32 acc_regs[16];
159 u64 fp_regs[4];
160 u32 gp_regs[16];
161 u32 ctrl_regs[16];
162} __attribute__((packed));
163
164struct save_area_s390x {
165 u64 fp_regs[16];
166 u64 gp_regs[16];
167 u8 psw[16];
168 u8 pad1[8];
169 u32 pref_reg;
170 u32 fp_ctrl_reg;
171 u8 pad2[4];
172 u32 tod_reg;
173 u64 timer;
174 u64 clk_cmp;
175 u8 pad3[8];
176 u32 acc_regs[16];
177 u64 ctrl_regs[16];
178} __attribute__((packed));
179
180union save_area {
181 struct save_area_s390 s390;
182 struct save_area_s390x s390x;
183};
184
185#define SAVE_AREA_BASE_S390 0xd4
186#define SAVE_AREA_BASE_S390X 0x1200
187
188#ifndef __s390x__
189#define SAVE_AREA_SIZE sizeof(struct save_area_s390)
190#define SAVE_AREA_BASE SAVE_AREA_BASE_S390
191#else
192#define SAVE_AREA_SIZE sizeof(struct save_area_s390x)
193#define SAVE_AREA_BASE SAVE_AREA_BASE_S390X
194#endif
195
150struct _lowcore 196struct _lowcore
151{ 197{
152#ifndef __s390x__ 198#ifndef __s390x__
diff --git a/include/asm-s390/sclp.h b/include/asm-s390/sclp.h
index 3996daaa8f54..21ed64773210 100644
--- a/include/asm-s390/sclp.h
+++ b/include/asm-s390/sclp.h
@@ -44,6 +44,8 @@ struct sclp_chp_info {
44 44
45extern struct sclp_readinfo_sccb s390_readinfo_sccb; 45extern struct sclp_readinfo_sccb s390_readinfo_sccb;
46extern void sclp_readinfo_early(void); 46extern void sclp_readinfo_early(void);
47extern int sclp_sdias_blk_count(void);
48extern int sclp_sdias_copy(void *dest, int blk_num, int nr_blks);
47extern int sclp_chp_configure(struct chp_id chpid); 49extern int sclp_chp_configure(struct chp_id chpid);
48extern int sclp_chp_deconfigure(struct chp_id chpid); 50extern int sclp_chp_deconfigure(struct chp_id chpid);
49extern int sclp_chp_read_info(struct sclp_chp_info *info); 51extern int sclp_chp_read_info(struct sclp_chp_info *info);
diff --git a/include/asm-s390/setup.h b/include/asm-s390/setup.h
index 44c7aee2bd34..a76a6b8fd887 100644
--- a/include/asm-s390/setup.h
+++ b/include/asm-s390/setup.h
@@ -40,6 +40,7 @@ struct mem_chunk {
40}; 40};
41 41
42extern struct mem_chunk memory_chunk[]; 42extern struct mem_chunk memory_chunk[];
43extern unsigned long real_memory_size;
43 44
44#ifdef CONFIG_S390_SWITCH_AMODE 45#ifdef CONFIG_S390_SWITCH_AMODE
45extern unsigned int switch_amode; 46extern unsigned int switch_amode;
@@ -77,6 +78,7 @@ extern unsigned long machine_flags;
77#endif /* __s390x__ */ 78#endif /* __s390x__ */
78 79
79#define MACHINE_HAS_SCLP (!MACHINE_IS_P390) 80#define MACHINE_HAS_SCLP (!MACHINE_IS_P390)
81#define ZFCPDUMP_HSA_SIZE (32UL<<20)
80 82
81/* 83/*
82 * Console mode. Override with conmode= 84 * Console mode. Override with conmode=
diff --git a/include/asm-s390/smp.h b/include/asm-s390/smp.h
index b957e4cda464..676e94ee15f0 100644
--- a/include/asm-s390/smp.h
+++ b/include/asm-s390/smp.h
@@ -119,4 +119,5 @@ static inline void smp_send_stop(void)
119#define smp_setup_cpu_possible_map() do { } while (0) 119#define smp_setup_cpu_possible_map() do { } while (0)
120#endif 120#endif
121 121
122extern union save_area *zfcpdump_save_areas[NR_CPUS + 1];
122#endif 123#endif