Commit 411ed322 authored by Michael Holzheu's avatar Michael Holzheu Committed by Martin Schwidefsky

[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: default avatarMichael Holzheu <holzheu@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: default avatarHeiko Carstens <heiko.carstens@de.ibm.com>
parent 7039d3a1
s390 SCSI dump tool (zfcpdump)
System z machines (z900 or higher) provide hardware support for creating system
dumps on SCSI disks. The dump process is initiated by booting a dump tool, which
has to create a dump of the current (probably crashed) Linux image. In order to
not overwrite memory of the crashed Linux with data of the dump tool, the
hardware saves some memory plus the register sets of the boot cpu before the
dump tool is loaded. There exists an SCLP hardware interface to obtain the saved
memory afterwards. Currently 32 MB are saved.
This zfcpdump implementation consists of a Linux dump kernel together with
a userspace dump tool, which are loaded together into the saved memory region
below 32 MB. zfcpdump is installed on a SCSI disk using zipl (as contained in
the s390-tools package) to make the device bootable. The operator of a Linux
system can then trigger a SCSI dump by booting the SCSI disk, where zfcpdump
resides on.
The kernel part of zfcpdump is implemented as a debugfs file under "zcore/mem",
which exports memory and registers of the crashed Linux in an s390
standalone dump format. It can be used in the same way as e.g. /dev/mem. The
dump format defines a 4K header followed by plain uncompressed memory. The
register sets are stored in the prefix pages of the respective cpus. To build a
dump enabled kernel with the zcore driver, the kernel config option
CONFIG_ZFCPDUMP has to be set. When reading from "zcore/mem", the part of
memory, which has been saved by hardware is read by the driver via the SCLP
hardware interface. The second part is just copied from the non overwritten real
memory.
The userspace application of zfcpdump can reside e.g. in an intitramfs or an
initrd. It reads from zcore/mem and writes the system dump to a file on a
SCSI disk.
To build a zfcpdump kernel use the following settings in your kernel
configuration:
* CONFIG_ZFCPDUMP=y
* Enable ZFCP driver
* Enable SCSI driver
* Enable ext2 and ext3 filesystems
* Disable as many features as possible to keep the kernel small.
E.g. network support is not needed at all.
To use the zfcpdump userspace application in an initramfs you have to do the
following:
* Copy the zfcpdump executable somewhere into your Linux tree.
E.g. to "arch/s390/boot/zfcpdump. If you do not want to include
shared libraries, compile the tool with the "-static" gcc option.
* If you want to include e2fsck, add it to your source tree, too. The zfcpdump
application attempts to start /sbin/e2fsck from the ramdisk.
* Use an initramfs config file like the following:
dir /dev 755 0 0
nod /dev/console 644 0 0 c 5 1
nod /dev/null 644 0 0 c 1 3
nod /dev/sda1 644 0 0 b 8 1
nod /dev/sda2 644 0 0 b 8 2
nod /dev/sda3 644 0 0 b 8 3
nod /dev/sda4 644 0 0 b 8 4
nod /dev/sda5 644 0 0 b 8 5
nod /dev/sda6 644 0 0 b 8 6
nod /dev/sda7 644 0 0 b 8 7
nod /dev/sda8 644 0 0 b 8 8
nod /dev/sda9 644 0 0 b 8 9
nod /dev/sda10 644 0 0 b 8 10
nod /dev/sda11 644 0 0 b 8 11
nod /dev/sda12 644 0 0 b 8 12
nod /dev/sda13 644 0 0 b 8 13
nod /dev/sda14 644 0 0 b 8 14
nod /dev/sda15 644 0 0 b 8 15
file /init arch/s390/boot/zfcpdump 755 0 0
file /sbin/e2fsck arch/s390/boot/e2fsck 755 0 0
dir /proc 755 0 0
dir /sys 755 0 0
dir /mnt 755 0 0
dir /sbin 755 0 0
* Issue "make image" to build the zfcpdump image with initramfs.
In a Linux distribution the zfcpdump enabled kernel image must be copied to
/usr/share/zfcpdump/zfcpdump.image, where the s390 zipl tool is looking for the
dump kernel when preparing a SCSI dump disk.
If you use a ramdisk copy it to "/usr/share/zfcpdump/zfcpdump.rd".
For more information on how to use zfcpdump refer to the s390 'Using the Dump
Tools book', which is available from
http://www.ibm.com/developerworks/linux/linux390.
...@@ -519,6 +519,14 @@ config KEXEC ...@@ -519,6 +519,14 @@ config KEXEC
current kernel, and to start another kernel. It is like a reboot current kernel, and to start another kernel. It is like a reboot
but is independent of hardware/microcode support. but is independent of hardware/microcode support.
config ZFCPDUMP
tristate "zfcpdump support"
select SMP
default n
help
Select this option if you want to build an zfcpdump enabled kernel.
Refer to "Documentation/s390/zfcpdump.txt" for more details on this.
endmenu endmenu
source "net/Kconfig" source "net/Kconfig"
......
...@@ -105,6 +105,9 @@ install: vmlinux ...@@ -105,6 +105,9 @@ install: vmlinux
image: vmlinux image: vmlinux
$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@ $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
zfcpdump:
$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
archclean: archclean:
$(Q)$(MAKE) $(clean)=$(boot) $(Q)$(MAKE) $(clean)=$(boot)
......
...@@ -167,6 +167,7 @@ CONFIG_NO_IDLE_HZ=y ...@@ -167,6 +167,7 @@ CONFIG_NO_IDLE_HZ=y
CONFIG_NO_IDLE_HZ_INIT=y CONFIG_NO_IDLE_HZ_INIT=y
CONFIG_S390_HYPFS_FS=y CONFIG_S390_HYPFS_FS=y
CONFIG_KEXEC=y CONFIG_KEXEC=y
# CONFIG_ZFCPDUMP is not set
# #
# Networking # Networking
......
...@@ -39,7 +39,69 @@ startup_continue: ...@@ -39,7 +39,69 @@ startup_continue:
basr %r13,0 # get base basr %r13,0 # get base
.LPG1: sll %r13,1 # remove high order bit .LPG1: sll %r13,1 # remove high order bit
srl %r13,1 srl %r13,1
lhi %r1,1 # mode 1 = esame
#ifdef CONFIG_ZFCPDUMP
# check if we have been ipled using zfcp dump:
tm 0xb9,0x01 # test if subchannel is enabled
jno .nodump # subchannel disabled
l %r1,0xb8
la %r5,.Lipl_schib-.LPG1(%r13)
stsch 0(%r5) # get schib of subchannel
jne .nodump # schib not available
tm 5(%r5),0x01 # devno valid?
jno .nodump
tm 4(%r5),0x80 # qdio capable device?
jno .nodump
l %r2,20(%r0) # address of ipl parameter block
lhi %r3,0
ic %r3,0x148(%r2) # get opt field
chi %r3,0x20 # load with dump?
jne .nodump
# store all prefix registers in case of load with dump:
la %r7,0 # base register for 0 page
la %r8,0 # first cpu
l %r11,.Lpref_arr_ptr-.LPG1(%r13) # address of prefix array
ahi %r11,4 # skip boot cpu
lr %r12,%r11
ahi %r12,(CONFIG_NR_CPUS*4) # end of prefix array
stap .Lcurrent_cpu+2-.LPG1(%r13) # store current cpu addr
1:
cl %r8,.Lcurrent_cpu-.LPG1(%r13) # is ipl cpu ?
je 4f # if yes get next cpu
2:
lr %r9,%r7
sigp %r9,%r8,0x9 # stop & store status of cpu
brc 8,3f # accepted
brc 4,4f # status stored: next cpu
brc 2,2b # busy: try again
brc 1,4f # not op: next cpu
3:
mvc 0(4,%r11),264(%r7) # copy prefix register to prefix array
ahi %r11,4 # next element in prefix array
clr %r11,%r12
je 5f # no more space in prefix array
4:
ahi %r8,1 # next cpu (r8 += 1)
cl %r8,.Llast_cpu-.LPG1(%r13) # is last possible cpu ?
jl 1b # jump if not last cpu
5:
lhi %r1,2 # mode 2 = esame (dump)
j 6f
.align 4
.Lipl_schib:
.rept 13
.long 0
.endr
.nodump:
lhi %r1,1 # mode 1 = esame (normal ipl)
6:
#else
lhi %r1,1 # mode 1 = esame (normal ipl)
#endif /* CONFIG_ZFCPDUMP */
mvi __LC_AR_MODE_ID,1 # set esame flag mvi __LC_AR_MODE_ID,1 # set esame flag
slr %r0,%r0 # set cpuid to zero slr %r0,%r0 # set cpuid to zero
sigp %r1,%r0,0x12 # switch to esame mode sigp %r1,%r0,0x12 # switch to esame mode
...@@ -149,6 +211,14 @@ startup_continue: ...@@ -149,6 +211,14 @@ startup_continue:
.L4malign:.quad 0xffffffffffc00000 .L4malign:.quad 0xffffffffffc00000
.Lscan2g:.quad 0x80000000 + 0x20000 - 8 # 2GB + 128K - 8 .Lscan2g:.quad 0x80000000 + 0x20000 - 8 # 2GB + 128K - 8
.Lnop: .long 0x07000700 .Lnop: .long 0x07000700
#ifdef CONFIG_ZFCPDUMP
.Lcurrent_cpu:
.long 0x0
.Llast_cpu:
.long 0x0000ffff
.Lpref_arr_ptr:
.long zfcpdump_prefix_array
#endif /* CONFIG_ZFCPDUMP */
.Lparmaddr: .Lparmaddr:
.quad PARMAREA .quad PARMAREA
.align 64 .align 64
......
This diff is collapsed.
...@@ -285,6 +285,26 @@ static void __init conmode_default(void) ...@@ -285,6 +285,26 @@ static void __init conmode_default(void)
} }
} }
#if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE)
static void __init setup_zfcpdump(unsigned int console_devno)
{
static char str[64];
if (ipl_info.type != IPL_TYPE_FCP_DUMP)
return;
if (console_devno != -1)
sprintf(str, "cio_ignore=all,!0.0.%04x,!0.0.%04x",
ipl_info.data.fcp.dev_id.devno, console_devno);
else
sprintf(str, "cio_ignore=all,!0.0.%04x",
ipl_info.data.fcp.dev_id.devno);
strcat(COMMAND_LINE, str);
console_loglevel = 2;
}
#else
static inline void setup_zfcpdump(unsigned int console_devno) {}
#endif /* CONFIG_ZFCPDUMP */
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
void (*_machine_restart)(char *command) = machine_restart_smp; void (*_machine_restart)(char *command) = machine_restart_smp;
void (*_machine_halt)(void) = machine_halt_smp; void (*_machine_halt)(void) = machine_halt_smp;
...@@ -586,13 +606,20 @@ setup_resources(void) ...@@ -586,13 +606,20 @@ setup_resources(void)
} }
} }
unsigned long real_memory_size;
EXPORT_SYMBOL_GPL(real_memory_size);
static void __init setup_memory_end(void) static void __init setup_memory_end(void)
{ {
unsigned long real_size, memory_size; unsigned long memory_size;
unsigned long max_mem, max_phys; unsigned long max_mem, max_phys;
int i; int i;
memory_size = real_size = 0; #if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE)
if (ipl_info.type == IPL_TYPE_FCP_DUMP)
memory_end = ZFCPDUMP_HSA_SIZE;
#endif
memory_size = 0;
max_phys = VMALLOC_END_INIT - VMALLOC_MIN_SIZE; max_phys = VMALLOC_END_INIT - VMALLOC_MIN_SIZE;
memory_end &= PAGE_MASK; memory_end &= PAGE_MASK;
...@@ -601,7 +628,8 @@ static void __init setup_memory_end(void) ...@@ -601,7 +628,8 @@ static void __init setup_memory_end(void)
for (i = 0; i < MEMORY_CHUNKS; i++) { for (i = 0; i < MEMORY_CHUNKS; i++) {
struct mem_chunk *chunk = &memory_chunk[i]; struct mem_chunk *chunk = &memory_chunk[i];
real_size = max(real_size, chunk->addr + chunk->size); real_memory_size = max(real_memory_size,
chunk->addr + chunk->size);
if (chunk->addr >= max_mem) { if (chunk->addr >= max_mem) {
memset(chunk, 0, sizeof(*chunk)); memset(chunk, 0, sizeof(*chunk));
continue; continue;
...@@ -765,6 +793,7 @@ setup_arch(char **cmdline_p) ...@@ -765,6 +793,7 @@ setup_arch(char **cmdline_p)
parse_early_param(); parse_early_param();
setup_ipl_info();
setup_memory_end(); setup_memory_end();
setup_addressing_mode(); setup_addressing_mode();
setup_memory(); setup_memory();
...@@ -782,6 +811,9 @@ setup_arch(char **cmdline_p) ...@@ -782,6 +811,9 @@ setup_arch(char **cmdline_p)
/* Setup default console */ /* Setup default console */
conmode_default(); conmode_default();
/* Setup zfcpdump support */
setup_zfcpdump(console_devno);
} }
void print_cpu_info(struct cpuinfo_S390 *cpuinfo) void print_cpu_info(struct cpuinfo_S390 *cpuinfo)
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/timex.h> #include <linux/timex.h>
#include <linux/bootmem.h>
#include <asm/ipl.h> #include <asm/ipl.h>
#include <asm/setup.h> #include <asm/setup.h>
#include <asm/sigp.h> #include <asm/sigp.h>
...@@ -40,6 +41,7 @@ ...@@ -40,6 +41,7 @@
#include <asm/cpcmd.h> #include <asm/cpcmd.h>
#include <asm/tlbflush.h> #include <asm/tlbflush.h>
#include <asm/timer.h> #include <asm/timer.h>
#include <asm/lowcore.h>
extern volatile int __cpu_logical_map[]; extern volatile int __cpu_logical_map[];
...@@ -395,6 +397,65 @@ void smp_ctl_clear_bit(int cr, int bit) ...@@ -395,6 +397,65 @@ void smp_ctl_clear_bit(int cr, int bit)
on_each_cpu(smp_ctl_bit_callback, &parms, 0, 1); on_each_cpu(smp_ctl_bit_callback, &parms, 0, 1);
} }
#if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE)
/*
* zfcpdump_prefix_array holds prefix registers for the following scenario:
* 64 bit zfcpdump kernel and 31 bit kernel which is to be dumped. We have to
* save its prefix registers, since they get lost, when switching from 31 bit
* to 64 bit.
*/
unsigned int zfcpdump_prefix_array[NR_CPUS + 1] \
__attribute__((__section__(".data")));
static void __init smp_get_save_areas(void)
{
unsigned int cpu, cpu_num, rc;
__u16 boot_cpu_addr;
if (ipl_info.type != IPL_TYPE_FCP_DUMP)
return;
boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr;
cpu_num = 1;
for (cpu = 0; cpu <= 65535; cpu++) {
if ((u16) cpu == boot_cpu_addr)
continue;
__cpu_logical_map[1] = (__u16) cpu;
if (signal_processor(1, sigp_sense) == sigp_not_operational)
continue;
if (cpu_num >= NR_CPUS) {
printk("WARNING: Registers for cpu %i are not "
"saved, since dump kernel was compiled with"
"NR_CPUS=%i!\n", cpu_num, NR_CPUS);
continue;
}
zfcpdump_save_areas[cpu_num] =
alloc_bootmem(sizeof(union save_area));
while (1) {
rc = signal_processor(1, sigp_stop_and_store_status);
if (rc != sigp_busy)
break;
cpu_relax();
}
memcpy(zfcpdump_save_areas[cpu_num],
(void *)(unsigned long) store_prefix() +
SAVE_AREA_BASE, SAVE_AREA_SIZE);
#ifdef __s390x__
/* copy original prefix register */
zfcpdump_save_areas[cpu_num]->s390x.pref_reg =
zfcpdump_prefix_array[cpu_num];
#endif
cpu_num++;
}
}
union save_area *zfcpdump_save_areas[NR_CPUS + 1];
EXPORT_SYMBOL_GPL(zfcpdump_save_areas);
#else
#define smp_get_save_areas() do { } while (0)
#endif
/* /*
* Lets check how many CPUs we have. * Lets check how many CPUs we have.
*/ */
...@@ -589,6 +650,7 @@ void __init smp_setup_cpu_possible_map(void) ...@@ -589,6 +650,7 @@ void __init smp_setup_cpu_possible_map(void)
{ {
unsigned int phy_cpus, pos_cpus, cpu; unsigned int phy_cpus, pos_cpus, cpu;
smp_get_save_areas();
phy_cpus = smp_count_cpus(); phy_cpus = smp_count_cpus();
pos_cpus = min(phy_cpus + additional_cpus, (unsigned int) NR_CPUS); pos_cpus = min(phy_cpus + additional_cpus, (unsigned int) NR_CPUS);
......
...@@ -29,3 +29,6 @@ obj-$(CONFIG_S390_TAPE_34XX) += tape_34xx.o ...@@ -29,3 +29,6 @@ obj-$(CONFIG_S390_TAPE_34XX) += tape_34xx.o
obj-$(CONFIG_S390_TAPE_3590) += tape_3590.o obj-$(CONFIG_S390_TAPE_3590) += tape_3590.o
obj-$(CONFIG_MONREADER) += monreader.o obj-$(CONFIG_MONREADER) += monreader.o
obj-$(CONFIG_MONWRITER) += monwriter.o obj-$(CONFIG_MONWRITER) += monwriter.o
zcore_mod-objs := sclp_sdias.o zcore.o
obj-$(CONFIG_ZFCPDUMP) += zcore_mod.o
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#define EvTyp_CntlProgIdent 0x0B #define EvTyp_CntlProgIdent 0x0B
#define EvTyp_SigQuiesce 0x1D #define EvTyp_SigQuiesce 0x1D
#define EvTyp_VT220Msg 0x1A #define EvTyp_VT220Msg 0x1A
#define EvTyp_SDIAS 0x1C
#define EvTyp_OpCmd_Mask 0x80000000 #define EvTyp_OpCmd_Mask 0x80000000
#define EvTyp_Msg_Mask 0x40000000 #define EvTyp_Msg_Mask 0x40000000
...@@ -36,6 +37,7 @@ ...@@ -36,6 +37,7 @@
#define EvTyp_CtlProgIdent_Mask 0x00200000 #define EvTyp_CtlProgIdent_Mask 0x00200000
#define EvTyp_SigQuiesce_Mask 0x00000008 #define EvTyp_SigQuiesce_Mask 0x00000008
#define EvTyp_VT220Msg_Mask 0x00000040 #define EvTyp_VT220Msg_Mask 0x00000040
#define EvTyp_SDIAS_Mask 0x00000010
#define GnrlMsgFlgs_DOM 0x8000 #define GnrlMsgFlgs_DOM 0x8000
#define GnrlMsgFlgs_SndAlrm 0x4000 #define GnrlMsgFlgs_SndAlrm 0x4000
......
/*
* Sclp "store data in absolut storage"
*
* Copyright IBM Corp. 2003,2007
* Author(s): Michael Holzheu
*/
#include <linux/sched.h>
#include <asm/sclp.h>
#include <asm/debug.h>
#include <asm/ipl.h>
#include "sclp.h"
#include "sclp_rw.h"
#define TRACE(x...) debug_sprintf_event(sdias_dbf, 1, x)
#define ERROR_MSG(x...) printk ( KERN_ALERT "SDIAS: " x )
#define SDIAS_RETRIES 300
#define SDIAS_SLEEP_TICKS 50
#define EQ_STORE_DATA 0x0
#define EQ_SIZE 0x1
#define DI_FCP_DUMP 0x0
#define ASA_SIZE_32 0x0
#define ASA_SIZE_64 0x1
#define EVSTATE_ALL_STORED 0x0
#define EVSTATE_NO_DATA 0x3
#define EVSTATE_PART_STORED 0x10
static struct debug_info *sdias_dbf;
static struct sclp_register sclp_sdias_register = {
.send_mask = EvTyp_SDIAS_Mask,
};
struct sdias_evbuf {
struct evbuf_header hdr;
u8 event_qual;
u8 data_id;
u64 reserved2;
u32 event_id;
u16 reserved3;
u8 asa_size;
u8 event_status;
u32 reserved4;
u32 blk_cnt;
u64 asa;
u32 reserved5;
u32 fbn;
u32 reserved6;
u32 lbn;
u16 reserved7;
u16 dbs;
} __attribute__((packed));
struct sdias_sccb {
struct sccb_header hdr;
struct sdias_evbuf evbuf;
} __attribute__((packed));
static struct sdias_sccb sccb __attribute__((aligned(4096)));
static int sclp_req_done;
static wait_queue_head_t sdias_wq;
static DEFINE_MUTEX(sdias_mutex);
static void sdias_callback(struct sclp_req *request, void *data)
{
struct sdias_sccb *sccb;
sccb = (struct sdias_sccb *) request->sccb;
sclp_req_done = 1;
wake_up(&sdias_wq); /* Inform caller, that request is complete */
TRACE("callback done\n");
}
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;
}
/*
* 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;
}
int __init sdias_init(void)
{
int rc;
if (ipl_info.type != IPL_TYPE_FCP_DUMP)
return 0;
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;
}
void __exit sdias_exit(void)
{
debug_unregister(sdias_dbf);
sclp_unregister(&sclp_sdias_register);
}
This diff is collapsed.
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#define _ASM_S390_IPL_H #define _ASM_S390_IPL_H
#include <asm/types.h> #include <asm/types.h>
#include <asm/cio.h>
#include <asm/setup.h>
#define IPL_PARMBLOCK_ORIGIN 0x2000 #define IPL_PARMBLOCK_ORIGIN 0x2000
...@@ -79,6 +81,7 @@ struct ipl_parameter_block { ...@@ -79,6 +81,7 @@ struct ipl_parameter_block {
extern u32 ipl_flags; extern u32 ipl_flags;
extern u32 dump_prefix_page; extern u32 dump_prefix_page;
extern void do_reipl(void); extern void do_reipl(void);
extern void ipl_save_parameters(void); extern void ipl_save_parameters(void);
...@@ -88,6 +91,35 @@ enum { ...@@ -88,6 +91,35 @@ enum {
IPL_NSS_VALID = 4, IPL_NSS_VALID = 4,
}; };
enum ipl_type {
IPL_TYPE_UNKNOWN = 1,
IPL_TYPE_CCW = 2,
IPL_TYPE_FCP = 4,
IPL_TYPE_FCP_DUMP = 8,
IPL_TYPE_NSS = 16,
};
struct ipl_info
{
enum ipl_type type;
union {
struct {
struct ccw_dev_id dev_id;
} ccw;
struct {
struct ccw_dev_id dev_id;
u64 wwpn;
u64 lun;
} fcp;
struct {
char name[NSS_NAME_SIZE + 1];
} nss;
} data;
};
extern struct ipl_info ipl_info;
extern void setup_ipl_info(void);
/* /*
* DIAG 308 support * DIAG 308 support
*/ */
......
...@@ -147,6 +147,52 @@ void pgm_check_handler(void); ...@@ -147,6 +147,52 @@ void pgm_check_handler(void);
void mcck_int_handler(void); void mcck_int_handler(void);
void io_int_handler(void); void io_int_handler(void);
struct save_area_s390 {
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));
struct save_area_s390x {
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));
union save_area {
struct save_area_s390 s390;
struct save_area_s390x s390x;
};
#define SAVE_AREA_BASE_S390 0xd4
#define SAVE_AREA_BASE_S390X 0x1200
#ifndef __s390x__
#define SAVE_AREA_SIZE sizeof(struct save_area_s390)
#define SAVE_AREA_BASE SAVE_AREA_BASE_S390
#else
#define SAVE_AREA_SIZE sizeof(struct save_area_s390x)
#define SAVE_AREA_BASE SAVE_AREA_BASE_S390X
#endif
struct _lowcore struct _lowcore
{ {
#ifndef __s390x__ #ifndef __s390x__
......
...@@ -44,6 +44,8 @@ struct sclp_chp_info { ...@@ -44,6 +44,8 @@ struct sclp_chp_info {
extern struct sclp_readinfo_sccb s390_readinfo_sccb; extern struct sclp_readinfo_sccb s390_readinfo_sccb;
extern void sclp_readinfo_early(void); 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);
extern int sclp_chp_configure(struct chp_id chpid); extern int sclp_chp_configure(struct chp_id chpid);
extern int sclp_chp_deconfigure(struct chp_id chpid); extern int sclp_chp_deconfigure(struct chp_id chpid);
extern int sclp_chp_read_info(struct sclp_chp_info *info); extern int sclp_chp_read_info(struct sclp_chp_info *info);
......
...@@ -40,6 +40,7 @@ struct mem_chunk { ...@@ -40,6 +40,7 @@ struct mem_chunk {
}; };
extern struct mem_chunk memory_chunk[]; extern struct mem_chunk memory_chunk[];
extern unsigned long real_memory_size;
#ifdef CONFIG_S390_SWITCH_AMODE #ifdef CONFIG_S390_SWITCH_AMODE
extern unsigned int switch_amode; extern unsigned int switch_amode;
...@@ -77,6 +78,7 @@ extern unsigned long machine_flags; ...@@ -77,6 +78,7 @@ extern unsigned long machine_flags;
#endif /* __s390x__ */ #endif /* __s390x__ */
#define MACHINE_HAS_SCLP (!MACHINE_IS_P390) #define MACHINE_HAS_SCLP (!MACHINE_IS_P390)
#define ZFCPDUMP_HSA_SIZE (32UL<<20)
/* /*
* Console mode. Override with conmode= * Console mode. Override with conmode=
......
...@@ -119,4 +119,5 @@ static inline void smp_send_stop(void) ...@@ -119,4 +119,5 @@ static inline void smp_send_stop(void)
#define smp_setup_cpu_possible_map() do { } while (0) #define smp_setup_cpu_possible_map() do { } while (0)
#endif #endif
extern union save_area *zfcpdump_save_areas[NR_CPUS + 1];
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment