You've already forked linux-rockchip
mirror of
https://github.com/armbian/linux-rockchip.git
synced 2026-01-06 11:08:10 -08:00
[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>
This commit is contained in:
committed by
Martin Schwidefsky
parent
7039d3a11c
commit
411ed32257
87
Documentation/s390/zfcpdump.txt
Normal file
87
Documentation/s390/zfcpdump.txt
Normal file
@@ -0,0 +1,87 @@
|
||||
s390 SCSI dump tool (zfcpdump)
|
||||
|
||||
System z machines (z900 or higher) provide hardware support for creating system
|
||||
dumps on SCSI disks. The dump process is initiated by booting a dump tool, which
|
||||
has to create a dump of the current (probably crashed) Linux image. In order to
|
||||
not overwrite memory of the crashed Linux with data of the dump tool, the
|
||||
hardware saves some memory plus the register sets of the boot cpu before the
|
||||
dump tool is loaded. There exists an SCLP hardware interface to obtain the saved
|
||||
memory afterwards. Currently 32 MB are saved.
|
||||
|
||||
This zfcpdump implementation consists of a Linux dump kernel together with
|
||||
a userspace dump tool, which are loaded together into the saved memory region
|
||||
below 32 MB. zfcpdump is installed on a SCSI disk using zipl (as contained in
|
||||
the s390-tools package) to make the device bootable. The operator of a Linux
|
||||
system can then trigger a SCSI dump by booting the SCSI disk, where zfcpdump
|
||||
resides on.
|
||||
|
||||
The kernel part of zfcpdump is implemented as a debugfs file under "zcore/mem",
|
||||
which exports memory and registers of the crashed Linux in an s390
|
||||
standalone dump format. It can be used in the same way as e.g. /dev/mem. The
|
||||
dump format defines a 4K header followed by plain uncompressed memory. The
|
||||
register sets are stored in the prefix pages of the respective cpus. To build a
|
||||
dump enabled kernel with the zcore driver, the kernel config option
|
||||
CONFIG_ZFCPDUMP has to be set. When reading from "zcore/mem", the part of
|
||||
memory, which has been saved by hardware is read by the driver via the SCLP
|
||||
hardware interface. The second part is just copied from the non overwritten real
|
||||
memory.
|
||||
|
||||
The userspace application of zfcpdump can reside e.g. in an intitramfs or an
|
||||
initrd. It reads from zcore/mem and writes the system dump to a file on a
|
||||
SCSI disk.
|
||||
|
||||
To build a zfcpdump kernel use the following settings in your kernel
|
||||
configuration:
|
||||
* CONFIG_ZFCPDUMP=y
|
||||
* Enable ZFCP driver
|
||||
* Enable SCSI driver
|
||||
* Enable ext2 and ext3 filesystems
|
||||
* Disable as many features as possible to keep the kernel small.
|
||||
E.g. network support is not needed at all.
|
||||
|
||||
To use the zfcpdump userspace application in an initramfs you have to do the
|
||||
following:
|
||||
|
||||
* Copy the zfcpdump executable somewhere into your Linux tree.
|
||||
E.g. to "arch/s390/boot/zfcpdump. If you do not want to include
|
||||
shared libraries, compile the tool with the "-static" gcc option.
|
||||
* If you want to include e2fsck, add it to your source tree, too. The zfcpdump
|
||||
application attempts to start /sbin/e2fsck from the ramdisk.
|
||||
* Use an initramfs config file like the following:
|
||||
|
||||
dir /dev 755 0 0
|
||||
nod /dev/console 644 0 0 c 5 1
|
||||
nod /dev/null 644 0 0 c 1 3
|
||||
nod /dev/sda1 644 0 0 b 8 1
|
||||
nod /dev/sda2 644 0 0 b 8 2
|
||||
nod /dev/sda3 644 0 0 b 8 3
|
||||
nod /dev/sda4 644 0 0 b 8 4
|
||||
nod /dev/sda5 644 0 0 b 8 5
|
||||
nod /dev/sda6 644 0 0 b 8 6
|
||||
nod /dev/sda7 644 0 0 b 8 7
|
||||
nod /dev/sda8 644 0 0 b 8 8
|
||||
nod /dev/sda9 644 0 0 b 8 9
|
||||
nod /dev/sda10 644 0 0 b 8 10
|
||||
nod /dev/sda11 644 0 0 b 8 11
|
||||
nod /dev/sda12 644 0 0 b 8 12
|
||||
nod /dev/sda13 644 0 0 b 8 13
|
||||
nod /dev/sda14 644 0 0 b 8 14
|
||||
nod /dev/sda15 644 0 0 b 8 15
|
||||
file /init arch/s390/boot/zfcpdump 755 0 0
|
||||
file /sbin/e2fsck arch/s390/boot/e2fsck 755 0 0
|
||||
dir /proc 755 0 0
|
||||
dir /sys 755 0 0
|
||||
dir /mnt 755 0 0
|
||||
dir /sbin 755 0 0
|
||||
|
||||
* Issue "make image" to build the zfcpdump image with initramfs.
|
||||
|
||||
In a Linux distribution the zfcpdump enabled kernel image must be copied to
|
||||
/usr/share/zfcpdump/zfcpdump.image, where the s390 zipl tool is looking for the
|
||||
dump kernel when preparing a SCSI dump disk.
|
||||
|
||||
If you use a ramdisk copy it to "/usr/share/zfcpdump/zfcpdump.rd".
|
||||
|
||||
For more information on how to use zfcpdump refer to the s390 'Using the Dump
|
||||
Tools book', which is available from
|
||||
http://www.ibm.com/developerworks/linux/linux390.
|
||||
@@ -519,6 +519,14 @@ config KEXEC
|
||||
current kernel, and to start another kernel. It is like a reboot
|
||||
but is independent of hardware/microcode support.
|
||||
|
||||
config ZFCPDUMP
|
||||
tristate "zfcpdump support"
|
||||
select SMP
|
||||
default n
|
||||
help
|
||||
Select this option if you want to build an zfcpdump enabled kernel.
|
||||
Refer to "Documentation/s390/zfcpdump.txt" for more details on this.
|
||||
|
||||
endmenu
|
||||
|
||||
source "net/Kconfig"
|
||||
|
||||
@@ -105,6 +105,9 @@ install: vmlinux
|
||||
image: vmlinux
|
||||
$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
|
||||
|
||||
zfcpdump:
|
||||
$(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
|
||||
|
||||
archclean:
|
||||
$(Q)$(MAKE) $(clean)=$(boot)
|
||||
|
||||
|
||||
@@ -167,6 +167,7 @@ CONFIG_NO_IDLE_HZ=y
|
||||
CONFIG_NO_IDLE_HZ_INIT=y
|
||||
CONFIG_S390_HYPFS_FS=y
|
||||
CONFIG_KEXEC=y
|
||||
# CONFIG_ZFCPDUMP is not set
|
||||
|
||||
#
|
||||
# Networking
|
||||
|
||||
@@ -39,7 +39,69 @@ startup_continue:
|
||||
basr %r13,0 # get base
|
||||
.LPG1: sll %r13,1 # remove high order bit
|
||||
srl %r13,1
|
||||
lhi %r1,1 # mode 1 = esame
|
||||
|
||||
#ifdef CONFIG_ZFCPDUMP
|
||||
|
||||
# check if we have been ipled using zfcp dump:
|
||||
|
||||
tm 0xb9,0x01 # test if subchannel is enabled
|
||||
jno .nodump # subchannel disabled
|
||||
l %r1,0xb8
|
||||
la %r5,.Lipl_schib-.LPG1(%r13)
|
||||
stsch 0(%r5) # get schib of subchannel
|
||||
jne .nodump # schib not available
|
||||
tm 5(%r5),0x01 # devno valid?
|
||||
jno .nodump
|
||||
tm 4(%r5),0x80 # qdio capable device?
|
||||
jno .nodump
|
||||
l %r2,20(%r0) # address of ipl parameter block
|
||||
lhi %r3,0
|
||||
ic %r3,0x148(%r2) # get opt field
|
||||
chi %r3,0x20 # load with dump?
|
||||
jne .nodump
|
||||
|
||||
# store all prefix registers in case of load with dump:
|
||||
|
||||
la %r7,0 # base register for 0 page
|
||||
la %r8,0 # first cpu
|
||||
l %r11,.Lpref_arr_ptr-.LPG1(%r13) # address of prefix array
|
||||
ahi %r11,4 # skip boot cpu
|
||||
lr %r12,%r11
|
||||
ahi %r12,(CONFIG_NR_CPUS*4) # end of prefix array
|
||||
stap .Lcurrent_cpu+2-.LPG1(%r13) # store current cpu addr
|
||||
1:
|
||||
cl %r8,.Lcurrent_cpu-.LPG1(%r13) # is ipl cpu ?
|
||||
je 4f # if yes get next cpu
|
||||
2:
|
||||
lr %r9,%r7
|
||||
sigp %r9,%r8,0x9 # stop & store status of cpu
|
||||
brc 8,3f # accepted
|
||||
brc 4,4f # status stored: next cpu
|
||||
brc 2,2b # busy: try again
|
||||
brc 1,4f # not op: next cpu
|
||||
3:
|
||||
mvc 0(4,%r11),264(%r7) # copy prefix register to prefix array
|
||||
ahi %r11,4 # next element in prefix array
|
||||
clr %r11,%r12
|
||||
je 5f # no more space in prefix array
|
||||
4:
|
||||
ahi %r8,1 # next cpu (r8 += 1)
|
||||
cl %r8,.Llast_cpu-.LPG1(%r13) # is last possible cpu ?
|
||||
jl 1b # jump if not last cpu
|
||||
5:
|
||||
lhi %r1,2 # mode 2 = esame (dump)
|
||||
j 6f
|
||||
.align 4
|
||||
.Lipl_schib:
|
||||
.rept 13
|
||||
.long 0
|
||||
.endr
|
||||
.nodump:
|
||||
lhi %r1,1 # mode 1 = esame (normal ipl)
|
||||
6:
|
||||
#else
|
||||
lhi %r1,1 # mode 1 = esame (normal ipl)
|
||||
#endif /* CONFIG_ZFCPDUMP */
|
||||
mvi __LC_AR_MODE_ID,1 # set esame flag
|
||||
slr %r0,%r0 # set cpuid to zero
|
||||
sigp %r1,%r0,0x12 # switch to esame mode
|
||||
@@ -149,6 +211,14 @@ startup_continue:
|
||||
.L4malign:.quad 0xffffffffffc00000
|
||||
.Lscan2g:.quad 0x80000000 + 0x20000 - 8 # 2GB + 128K - 8
|
||||
.Lnop: .long 0x07000700
|
||||
#ifdef CONFIG_ZFCPDUMP
|
||||
.Lcurrent_cpu:
|
||||
.long 0x0
|
||||
.Llast_cpu:
|
||||
.long 0x0000ffff
|
||||
.Lpref_arr_ptr:
|
||||
.long zfcpdump_prefix_array
|
||||
#endif /* CONFIG_ZFCPDUMP */
|
||||
.Lparmaddr:
|
||||
.quad PARMAREA
|
||||
.align 64
|
||||
|
||||
@@ -29,36 +29,21 @@
|
||||
#define SCCB_LOADPARM (&s390_readinfo_sccb.loadparm)
|
||||
#define SCCB_FLAG (s390_readinfo_sccb.flags)
|
||||
|
||||
enum ipl_type {
|
||||
IPL_TYPE_NONE = 1,
|
||||
IPL_TYPE_UNKNOWN = 2,
|
||||
IPL_TYPE_CCW = 4,
|
||||
IPL_TYPE_FCP = 8,
|
||||
IPL_TYPE_NSS = 16,
|
||||
};
|
||||
|
||||
#define IPL_NONE_STR "none"
|
||||
#define IPL_UNKNOWN_STR "unknown"
|
||||
#define IPL_CCW_STR "ccw"
|
||||
#define IPL_FCP_STR "fcp"
|
||||
#define IPL_NSS_STR "nss"
|
||||
|
||||
/*
|
||||
* Must be in data section since the bss section
|
||||
* is not cleared when these are accessed.
|
||||
*/
|
||||
static u16 ipl_devno __attribute__((__section__(".data"))) = 0;
|
||||
u32 ipl_flags __attribute__((__section__(".data"))) = 0;
|
||||
#define IPL_UNKNOWN_STR "unknown"
|
||||
#define IPL_CCW_STR "ccw"
|
||||
#define IPL_FCP_STR "fcp"
|
||||
#define IPL_FCP_DUMP_STR "fcp_dump"
|
||||
#define IPL_NSS_STR "nss"
|
||||
|
||||
static char *ipl_type_str(enum ipl_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case IPL_TYPE_NONE:
|
||||
return IPL_NONE_STR;
|
||||
case IPL_TYPE_CCW:
|
||||
return IPL_CCW_STR;
|
||||
case IPL_TYPE_FCP:
|
||||
return IPL_FCP_STR;
|
||||
case IPL_TYPE_FCP_DUMP:
|
||||
return IPL_FCP_DUMP_STR;
|
||||
case IPL_TYPE_NSS:
|
||||
return IPL_NSS_STR;
|
||||
case IPL_TYPE_UNKNOWN:
|
||||
@@ -67,15 +52,55 @@ static char *ipl_type_str(enum ipl_type type)
|
||||
}
|
||||
}
|
||||
|
||||
enum dump_type {
|
||||
DUMP_TYPE_NONE = 1,
|
||||
DUMP_TYPE_CCW = 2,
|
||||
DUMP_TYPE_FCP = 4,
|
||||
};
|
||||
|
||||
#define DUMP_NONE_STR "none"
|
||||
#define DUMP_CCW_STR "ccw"
|
||||
#define DUMP_FCP_STR "fcp"
|
||||
|
||||
static char *dump_type_str(enum dump_type type)
|
||||
{
|
||||
switch (type) {
|
||||
case DUMP_TYPE_NONE:
|
||||
return DUMP_NONE_STR;
|
||||
case DUMP_TYPE_CCW:
|
||||
return DUMP_CCW_STR;
|
||||
case DUMP_TYPE_FCP:
|
||||
return DUMP_FCP_STR;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Must be in data section since the bss section
|
||||
* is not cleared when these are accessed.
|
||||
*/
|
||||
static u16 ipl_devno __attribute__((__section__(".data"))) = 0;
|
||||
u32 ipl_flags __attribute__((__section__(".data"))) = 0;
|
||||
|
||||
enum ipl_method {
|
||||
IPL_METHOD_NONE,
|
||||
IPL_METHOD_CCW_CIO,
|
||||
IPL_METHOD_CCW_DIAG,
|
||||
IPL_METHOD_CCW_VM,
|
||||
IPL_METHOD_FCP_RO_DIAG,
|
||||
IPL_METHOD_FCP_RW_DIAG,
|
||||
IPL_METHOD_FCP_RO_VM,
|
||||
IPL_METHOD_NSS,
|
||||
REIPL_METHOD_CCW_CIO,
|
||||
REIPL_METHOD_CCW_DIAG,
|
||||
REIPL_METHOD_CCW_VM,
|
||||
REIPL_METHOD_FCP_RO_DIAG,
|
||||
REIPL_METHOD_FCP_RW_DIAG,
|
||||
REIPL_METHOD_FCP_RO_VM,
|
||||
REIPL_METHOD_FCP_DUMP,
|
||||
REIPL_METHOD_NSS,
|
||||
REIPL_METHOD_DEFAULT,
|
||||
};
|
||||
|
||||
enum dump_method {
|
||||
DUMP_METHOD_NONE,
|
||||
DUMP_METHOD_CCW_CIO,
|
||||
DUMP_METHOD_CCW_DIAG,
|
||||
DUMP_METHOD_CCW_VM,
|
||||
DUMP_METHOD_FCP_DIAG,
|
||||
};
|
||||
|
||||
enum shutdown_action {
|
||||
@@ -107,15 +132,15 @@ static int diag308_set_works = 0;
|
||||
static int reipl_capabilities = IPL_TYPE_UNKNOWN;
|
||||
|
||||
static enum ipl_type reipl_type = IPL_TYPE_UNKNOWN;
|
||||
static enum ipl_method reipl_method = IPL_METHOD_NONE;
|
||||
static enum ipl_method reipl_method = REIPL_METHOD_DEFAULT;
|
||||
static struct ipl_parameter_block *reipl_block_fcp;
|
||||
static struct ipl_parameter_block *reipl_block_ccw;
|
||||
|
||||
static char reipl_nss_name[NSS_NAME_SIZE + 1];
|
||||
|
||||
static int dump_capabilities = IPL_TYPE_NONE;
|
||||
static enum ipl_type dump_type = IPL_TYPE_NONE;
|
||||
static enum ipl_method dump_method = IPL_METHOD_NONE;
|
||||
static int dump_capabilities = DUMP_TYPE_NONE;
|
||||
static enum dump_type dump_type = DUMP_TYPE_NONE;
|
||||
static enum dump_method dump_method = DUMP_METHOD_NONE;
|
||||
static struct ipl_parameter_block *dump_block_fcp;
|
||||
static struct ipl_parameter_block *dump_block_ccw;
|
||||
|
||||
@@ -134,6 +159,7 @@ int diag308(unsigned long subcode, void *addr)
|
||||
: "d" (subcode) : "cc", "memory");
|
||||
return _rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(diag308);
|
||||
|
||||
/* SYSFS */
|
||||
|
||||
@@ -197,7 +223,7 @@ static void make_attrs_ro(struct attribute **attrs)
|
||||
* ipl section
|
||||
*/
|
||||
|
||||
static enum ipl_type ipl_get_type(void)
|
||||
static __init enum ipl_type get_ipl_type(void)
|
||||
{
|
||||
struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START;
|
||||
|
||||
@@ -211,12 +237,44 @@ static enum ipl_type ipl_get_type(void)
|
||||
return IPL_TYPE_UNKNOWN;
|
||||
if (ipl->hdr.pbt != DIAG308_IPL_TYPE_FCP)
|
||||
return IPL_TYPE_UNKNOWN;
|
||||
if (ipl->ipl_info.fcp.opt == DIAG308_IPL_OPT_DUMP)
|
||||
return IPL_TYPE_FCP_DUMP;
|
||||
return IPL_TYPE_FCP;
|
||||
}
|
||||
|
||||
void __init setup_ipl_info(void)
|
||||
{
|
||||
ipl_info.type = get_ipl_type();
|
||||
switch (ipl_info.type) {
|
||||
case IPL_TYPE_CCW:
|
||||
ipl_info.data.ccw.dev_id.devno = ipl_devno;
|
||||
ipl_info.data.ccw.dev_id.ssid = 0;
|
||||
break;
|
||||
case IPL_TYPE_FCP:
|
||||
case IPL_TYPE_FCP_DUMP:
|
||||
ipl_info.data.fcp.dev_id.devno =
|
||||
IPL_PARMBLOCK_START->ipl_info.fcp.devno;
|
||||
ipl_info.data.fcp.dev_id.ssid = 0;
|
||||
ipl_info.data.fcp.wwpn = IPL_PARMBLOCK_START->ipl_info.fcp.wwpn;
|
||||
ipl_info.data.fcp.lun = IPL_PARMBLOCK_START->ipl_info.fcp.lun;
|
||||
break;
|
||||
case IPL_TYPE_NSS:
|
||||
strncpy(ipl_info.data.nss.name, kernel_nss_name,
|
||||
sizeof(ipl_info.data.nss.name));
|
||||
break;
|
||||
case IPL_TYPE_UNKNOWN:
|
||||
default:
|
||||
/* We have no info to copy */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
struct ipl_info ipl_info;
|
||||
EXPORT_SYMBOL_GPL(ipl_info);
|
||||
|
||||
static ssize_t ipl_type_show(struct subsystem *subsys, char *page)
|
||||
{
|
||||
return sprintf(page, "%s\n", ipl_type_str(ipl_get_type()));
|
||||
return sprintf(page, "%s\n", ipl_type_str(ipl_info.type));
|
||||
}
|
||||
|
||||
static struct subsys_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type);
|
||||
@@ -225,10 +283,11 @@ static ssize_t sys_ipl_device_show(struct subsystem *subsys, char *page)
|
||||
{
|
||||
struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START;
|
||||
|
||||
switch (ipl_get_type()) {
|
||||
switch (ipl_info.type) {
|
||||
case IPL_TYPE_CCW:
|
||||
return sprintf(page, "0.0.%04x\n", ipl_devno);
|
||||
case IPL_TYPE_FCP:
|
||||
case IPL_TYPE_FCP_DUMP:
|
||||
return sprintf(page, "0.0.%04x\n", ipl->ipl_info.fcp.devno);
|
||||
default:
|
||||
return 0;
|
||||
@@ -485,23 +544,29 @@ static int reipl_set_type(enum ipl_type type)
|
||||
switch(type) {
|
||||
case IPL_TYPE_CCW:
|
||||
if (MACHINE_IS_VM)
|
||||
reipl_method = IPL_METHOD_CCW_VM;
|
||||
reipl_method = REIPL_METHOD_CCW_VM;
|
||||
else
|
||||
reipl_method = IPL_METHOD_CCW_CIO;
|
||||
reipl_method = REIPL_METHOD_CCW_CIO;
|
||||
break;
|
||||
case IPL_TYPE_FCP:
|
||||
if (diag308_set_works)
|
||||
reipl_method = IPL_METHOD_FCP_RW_DIAG;
|
||||
reipl_method = REIPL_METHOD_FCP_RW_DIAG;
|
||||
else if (MACHINE_IS_VM)
|
||||
reipl_method = IPL_METHOD_FCP_RO_VM;
|
||||
reipl_method = REIPL_METHOD_FCP_RO_VM;
|
||||
else
|
||||
reipl_method = IPL_METHOD_FCP_RO_DIAG;
|
||||
reipl_method = REIPL_METHOD_FCP_RO_DIAG;
|
||||
break;
|
||||
case IPL_TYPE_FCP_DUMP:
|
||||
reipl_method = REIPL_METHOD_FCP_DUMP;
|
||||
break;
|
||||
case IPL_TYPE_NSS:
|
||||
reipl_method = IPL_METHOD_NSS;
|
||||
reipl_method = REIPL_METHOD_NSS;
|
||||
break;
|
||||
case IPL_TYPE_UNKNOWN:
|
||||
reipl_method = REIPL_METHOD_DEFAULT;
|
||||
break;
|
||||
default:
|
||||
reipl_method = IPL_METHOD_NONE;
|
||||
BUG();
|
||||
}
|
||||
reipl_type = type;
|
||||
return 0;
|
||||
@@ -579,22 +644,22 @@ static struct attribute_group dump_ccw_attr_group = {
|
||||
|
||||
/* dump type */
|
||||
|
||||
static int dump_set_type(enum ipl_type type)
|
||||
static int dump_set_type(enum dump_type type)
|
||||
{
|
||||
if (!(dump_capabilities & type))
|
||||
return -EINVAL;
|
||||
switch(type) {
|
||||
case IPL_TYPE_CCW:
|
||||
case DUMP_TYPE_CCW:
|
||||
if (MACHINE_IS_VM)
|
||||
dump_method = IPL_METHOD_CCW_VM;
|
||||
dump_method = DUMP_METHOD_CCW_VM;
|
||||
else
|
||||
dump_method = IPL_METHOD_CCW_CIO;
|
||||
dump_method = DUMP_METHOD_CCW_CIO;
|
||||
break;
|
||||
case IPL_TYPE_FCP:
|
||||
dump_method = IPL_METHOD_FCP_RW_DIAG;
|
||||
case DUMP_TYPE_FCP:
|
||||
dump_method = DUMP_METHOD_FCP_DIAG;
|
||||
break;
|
||||
default:
|
||||
dump_method = IPL_METHOD_NONE;
|
||||
dump_method = DUMP_METHOD_NONE;
|
||||
}
|
||||
dump_type = type;
|
||||
return 0;
|
||||
@@ -602,7 +667,7 @@ static int dump_set_type(enum ipl_type type)
|
||||
|
||||
static ssize_t dump_type_show(struct subsystem *subsys, char *page)
|
||||
{
|
||||
return sprintf(page, "%s\n", ipl_type_str(dump_type));
|
||||
return sprintf(page, "%s\n", dump_type_str(dump_type));
|
||||
}
|
||||
|
||||
static ssize_t dump_type_store(struct subsystem *subsys, const char *buf,
|
||||
@@ -610,12 +675,12 @@ static ssize_t dump_type_store(struct subsystem *subsys, const char *buf,
|
||||
{
|
||||
int rc = -EINVAL;
|
||||
|
||||
if (strncmp(buf, IPL_NONE_STR, strlen(IPL_NONE_STR)) == 0)
|
||||
rc = dump_set_type(IPL_TYPE_NONE);
|
||||
else if (strncmp(buf, IPL_CCW_STR, strlen(IPL_CCW_STR)) == 0)
|
||||
rc = dump_set_type(IPL_TYPE_CCW);
|
||||
else if (strncmp(buf, IPL_FCP_STR, strlen(IPL_FCP_STR)) == 0)
|
||||
rc = dump_set_type(IPL_TYPE_FCP);
|
||||
if (strncmp(buf, DUMP_NONE_STR, strlen(DUMP_NONE_STR)) == 0)
|
||||
rc = dump_set_type(DUMP_TYPE_NONE);
|
||||
else if (strncmp(buf, DUMP_CCW_STR, strlen(DUMP_CCW_STR)) == 0)
|
||||
rc = dump_set_type(DUMP_TYPE_CCW);
|
||||
else if (strncmp(buf, DUMP_FCP_STR, strlen(DUMP_FCP_STR)) == 0)
|
||||
rc = dump_set_type(DUMP_TYPE_FCP);
|
||||
return (rc != 0) ? rc : len;
|
||||
}
|
||||
|
||||
@@ -664,14 +729,14 @@ void do_reipl(void)
|
||||
char loadparm[LOADPARM_LEN + 1];
|
||||
|
||||
switch (reipl_method) {
|
||||
case IPL_METHOD_CCW_CIO:
|
||||
case REIPL_METHOD_CCW_CIO:
|
||||
devid.devno = reipl_block_ccw->ipl_info.ccw.devno;
|
||||
if (ipl_get_type() == IPL_TYPE_CCW && devid.devno == ipl_devno)
|
||||
if (ipl_info.type == IPL_TYPE_CCW && devid.devno == ipl_devno)
|
||||
diag308(DIAG308_IPL, NULL);
|
||||
devid.ssid = 0;
|
||||
reipl_ccw_dev(&devid);
|
||||
break;
|
||||
case IPL_METHOD_CCW_VM:
|
||||
case REIPL_METHOD_CCW_VM:
|
||||
reipl_get_ascii_loadparm(loadparm);
|
||||
if (strlen(loadparm) == 0)
|
||||
sprintf(buf, "IPL %X",
|
||||
@@ -681,30 +746,32 @@ void do_reipl(void)
|
||||
reipl_block_ccw->ipl_info.ccw.devno, loadparm);
|
||||
__cpcmd(buf, NULL, 0, NULL);
|
||||
break;
|
||||
case IPL_METHOD_CCW_DIAG:
|
||||
case REIPL_METHOD_CCW_DIAG:
|
||||
diag308(DIAG308_SET, reipl_block_ccw);
|
||||
diag308(DIAG308_IPL, NULL);
|
||||
break;
|
||||
case IPL_METHOD_FCP_RW_DIAG:
|
||||
case REIPL_METHOD_FCP_RW_DIAG:
|
||||
diag308(DIAG308_SET, reipl_block_fcp);
|
||||
diag308(DIAG308_IPL, NULL);
|
||||
break;
|
||||
case IPL_METHOD_FCP_RO_DIAG:
|
||||
case REIPL_METHOD_FCP_RO_DIAG:
|
||||
diag308(DIAG308_IPL, NULL);
|
||||
break;
|
||||
case IPL_METHOD_FCP_RO_VM:
|
||||
case REIPL_METHOD_FCP_RO_VM:
|
||||
__cpcmd("IPL", NULL, 0, NULL);
|
||||
break;
|
||||
case IPL_METHOD_NSS:
|
||||
case REIPL_METHOD_NSS:
|
||||
sprintf(buf, "IPL %s", reipl_nss_name);
|
||||
__cpcmd(buf, NULL, 0, NULL);
|
||||
break;
|
||||
case IPL_METHOD_NONE:
|
||||
default:
|
||||
case REIPL_METHOD_DEFAULT:
|
||||
if (MACHINE_IS_VM)
|
||||
__cpcmd("IPL", NULL, 0, NULL);
|
||||
diag308(DIAG308_IPL, NULL);
|
||||
break;
|
||||
case REIPL_METHOD_FCP_DUMP:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
signal_processor(smp_processor_id(), sigp_stop_and_store_status);
|
||||
}
|
||||
@@ -715,28 +782,28 @@ static void do_dump(void)
|
||||
static char buf[100];
|
||||
|
||||
switch (dump_method) {
|
||||
case IPL_METHOD_CCW_CIO:
|
||||
case DUMP_METHOD_CCW_CIO:
|
||||
smp_send_stop();
|
||||
devid.devno = dump_block_ccw->ipl_info.ccw.devno;
|
||||
devid.ssid = 0;
|
||||
reipl_ccw_dev(&devid);
|
||||
break;
|
||||
case IPL_METHOD_CCW_VM:
|
||||
case DUMP_METHOD_CCW_VM:
|
||||
smp_send_stop();
|
||||
sprintf(buf, "STORE STATUS");
|
||||
__cpcmd(buf, NULL, 0, NULL);
|
||||
sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno);
|
||||
__cpcmd(buf, NULL, 0, NULL);
|
||||
break;
|
||||
case IPL_METHOD_CCW_DIAG:
|
||||
case DUMP_METHOD_CCW_DIAG:
|
||||
diag308(DIAG308_SET, dump_block_ccw);
|
||||
diag308(DIAG308_DUMP, NULL);
|
||||
break;
|
||||
case IPL_METHOD_FCP_RW_DIAG:
|
||||
case DUMP_METHOD_FCP_DIAG:
|
||||
diag308(DIAG308_SET, dump_block_fcp);
|
||||
diag308(DIAG308_DUMP, NULL);
|
||||
break;
|
||||
case IPL_METHOD_NONE:
|
||||
case DUMP_METHOD_NONE:
|
||||
default:
|
||||
return;
|
||||
}
|
||||
@@ -777,12 +844,13 @@ static int __init ipl_init(void)
|
||||
rc = firmware_register(&ipl_subsys);
|
||||
if (rc)
|
||||
return rc;
|
||||
switch (ipl_get_type()) {
|
||||
switch (ipl_info.type) {
|
||||
case IPL_TYPE_CCW:
|
||||
rc = sysfs_create_group(&ipl_subsys.kset.kobj,
|
||||
&ipl_ccw_attr_group);
|
||||
break;
|
||||
case IPL_TYPE_FCP:
|
||||
case IPL_TYPE_FCP_DUMP:
|
||||
rc = ipl_register_fcp_files();
|
||||
break;
|
||||
case IPL_TYPE_NSS:
|
||||
@@ -852,7 +920,7 @@ static int __init reipl_ccw_init(void)
|
||||
/* FIXME: check for diag308_set_works when enabling diag ccw reipl */
|
||||
if (!MACHINE_IS_VM)
|
||||
sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO;
|
||||
if (ipl_get_type() == IPL_TYPE_CCW)
|
||||
if (ipl_info.type == IPL_TYPE_CCW)
|
||||
reipl_block_ccw->ipl_info.ccw.devno = ipl_devno;
|
||||
reipl_capabilities |= IPL_TYPE_CCW;
|
||||
return 0;
|
||||
@@ -862,9 +930,9 @@ static int __init reipl_fcp_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if ((!diag308_set_works) && (ipl_get_type() != IPL_TYPE_FCP))
|
||||
if ((!diag308_set_works) && (ipl_info.type != IPL_TYPE_FCP))
|
||||
return 0;
|
||||
if ((!diag308_set_works) && (ipl_get_type() == IPL_TYPE_FCP))
|
||||
if ((!diag308_set_works) && (ipl_info.type == IPL_TYPE_FCP))
|
||||
make_attrs_ro(reipl_fcp_attrs);
|
||||
|
||||
reipl_block_fcp = (void *) get_zeroed_page(GFP_KERNEL);
|
||||
@@ -875,7 +943,7 @@ static int __init reipl_fcp_init(void)
|
||||
free_page((unsigned long)reipl_block_fcp);
|
||||
return rc;
|
||||
}
|
||||
if (ipl_get_type() == IPL_TYPE_FCP) {
|
||||
if (ipl_info.type == IPL_TYPE_FCP) {
|
||||
memcpy(reipl_block_fcp, IPL_PARMBLOCK_START, PAGE_SIZE);
|
||||
} else {
|
||||
reipl_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN;
|
||||
@@ -909,7 +977,7 @@ static int __init reipl_init(void)
|
||||
rc = reipl_nss_init();
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = reipl_set_type(ipl_get_type());
|
||||
rc = reipl_set_type(ipl_info.type);
|
||||
if (rc)
|
||||
return rc;
|
||||
return 0;
|
||||
@@ -931,7 +999,7 @@ static int __init dump_ccw_init(void)
|
||||
dump_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION;
|
||||
dump_block_ccw->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN;
|
||||
dump_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW;
|
||||
dump_capabilities |= IPL_TYPE_CCW;
|
||||
dump_capabilities |= DUMP_TYPE_CCW;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -956,7 +1024,7 @@ static int __init dump_fcp_init(void)
|
||||
dump_block_fcp->hdr.blk0_len = IPL_PARM_BLK0_FCP_LEN;
|
||||
dump_block_fcp->hdr.pbt = DIAG308_IPL_TYPE_FCP;
|
||||
dump_block_fcp->ipl_info.fcp.opt = DIAG308_IPL_OPT_DUMP;
|
||||
dump_capabilities |= IPL_TYPE_FCP;
|
||||
dump_capabilities |= DUMP_TYPE_FCP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -995,7 +1063,7 @@ static int __init dump_init(void)
|
||||
rc = dump_fcp_init();
|
||||
if (rc)
|
||||
return rc;
|
||||
dump_set_type(IPL_TYPE_NONE);
|
||||
dump_set_type(DUMP_TYPE_NONE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -285,6 +285,26 @@ static void __init conmode_default(void)
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE)
|
||||
static void __init setup_zfcpdump(unsigned int console_devno)
|
||||
{
|
||||
static char str[64];
|
||||
|
||||
if (ipl_info.type != IPL_TYPE_FCP_DUMP)
|
||||
return;
|
||||
if (console_devno != -1)
|
||||
sprintf(str, "cio_ignore=all,!0.0.%04x,!0.0.%04x",
|
||||
ipl_info.data.fcp.dev_id.devno, console_devno);
|
||||
else
|
||||
sprintf(str, "cio_ignore=all,!0.0.%04x",
|
||||
ipl_info.data.fcp.dev_id.devno);
|
||||
strcat(COMMAND_LINE, str);
|
||||
console_loglevel = 2;
|
||||
}
|
||||
#else
|
||||
static inline void setup_zfcpdump(unsigned int console_devno) {}
|
||||
#endif /* CONFIG_ZFCPDUMP */
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
void (*_machine_restart)(char *command) = machine_restart_smp;
|
||||
void (*_machine_halt)(void) = machine_halt_smp;
|
||||
@@ -586,13 +606,20 @@ setup_resources(void)
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long real_memory_size;
|
||||
EXPORT_SYMBOL_GPL(real_memory_size);
|
||||
|
||||
static void __init setup_memory_end(void)
|
||||
{
|
||||
unsigned long real_size, memory_size;
|
||||
unsigned long memory_size;
|
||||
unsigned long max_mem, max_phys;
|
||||
int i;
|
||||
|
||||
memory_size = real_size = 0;
|
||||
#if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE)
|
||||
if (ipl_info.type == IPL_TYPE_FCP_DUMP)
|
||||
memory_end = ZFCPDUMP_HSA_SIZE;
|
||||
#endif
|
||||
memory_size = 0;
|
||||
max_phys = VMALLOC_END_INIT - VMALLOC_MIN_SIZE;
|
||||
memory_end &= PAGE_MASK;
|
||||
|
||||
@@ -601,7 +628,8 @@ static void __init setup_memory_end(void)
|
||||
for (i = 0; i < MEMORY_CHUNKS; i++) {
|
||||
struct mem_chunk *chunk = &memory_chunk[i];
|
||||
|
||||
real_size = max(real_size, chunk->addr + chunk->size);
|
||||
real_memory_size = max(real_memory_size,
|
||||
chunk->addr + chunk->size);
|
||||
if (chunk->addr >= max_mem) {
|
||||
memset(chunk, 0, sizeof(*chunk));
|
||||
continue;
|
||||
@@ -765,6 +793,7 @@ setup_arch(char **cmdline_p)
|
||||
|
||||
parse_early_param();
|
||||
|
||||
setup_ipl_info();
|
||||
setup_memory_end();
|
||||
setup_addressing_mode();
|
||||
setup_memory();
|
||||
@@ -782,6 +811,9 @@ setup_arch(char **cmdline_p)
|
||||
|
||||
/* Setup default console */
|
||||
conmode_default();
|
||||
|
||||
/* Setup zfcpdump support */
|
||||
setup_zfcpdump(console_devno);
|
||||
}
|
||||
|
||||
void print_cpu_info(struct cpuinfo_S390 *cpuinfo)
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/timex.h>
|
||||
#include <linux/bootmem.h>
|
||||
#include <asm/ipl.h>
|
||||
#include <asm/setup.h>
|
||||
#include <asm/sigp.h>
|
||||
@@ -40,6 +41,7 @@
|
||||
#include <asm/cpcmd.h>
|
||||
#include <asm/tlbflush.h>
|
||||
#include <asm/timer.h>
|
||||
#include <asm/lowcore.h>
|
||||
|
||||
extern volatile int __cpu_logical_map[];
|
||||
|
||||
@@ -395,6 +397,65 @@ void smp_ctl_clear_bit(int cr, int bit)
|
||||
on_each_cpu(smp_ctl_bit_callback, &parms, 0, 1);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE)
|
||||
|
||||
/*
|
||||
* zfcpdump_prefix_array holds prefix registers for the following scenario:
|
||||
* 64 bit zfcpdump kernel and 31 bit kernel which is to be dumped. We have to
|
||||
* save its prefix registers, since they get lost, when switching from 31 bit
|
||||
* to 64 bit.
|
||||
*/
|
||||
unsigned int zfcpdump_prefix_array[NR_CPUS + 1] \
|
||||
__attribute__((__section__(".data")));
|
||||
|
||||
static void __init smp_get_save_areas(void)
|
||||
{
|
||||
unsigned int cpu, cpu_num, rc;
|
||||
__u16 boot_cpu_addr;
|
||||
|
||||
if (ipl_info.type != IPL_TYPE_FCP_DUMP)
|
||||
return;
|
||||
boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr;
|
||||
cpu_num = 1;
|
||||
for (cpu = 0; cpu <= 65535; cpu++) {
|
||||
if ((u16) cpu == boot_cpu_addr)
|
||||
continue;
|
||||
__cpu_logical_map[1] = (__u16) cpu;
|
||||
if (signal_processor(1, sigp_sense) == sigp_not_operational)
|
||||
continue;
|
||||
if (cpu_num >= NR_CPUS) {
|
||||
printk("WARNING: Registers for cpu %i are not "
|
||||
"saved, since dump kernel was compiled with"
|
||||
"NR_CPUS=%i!\n", cpu_num, NR_CPUS);
|
||||
continue;
|
||||
}
|
||||
zfcpdump_save_areas[cpu_num] =
|
||||
alloc_bootmem(sizeof(union save_area));
|
||||
while (1) {
|
||||
rc = signal_processor(1, sigp_stop_and_store_status);
|
||||
if (rc != sigp_busy)
|
||||
break;
|
||||
cpu_relax();
|
||||
}
|
||||
memcpy(zfcpdump_save_areas[cpu_num],
|
||||
(void *)(unsigned long) store_prefix() +
|
||||
SAVE_AREA_BASE, SAVE_AREA_SIZE);
|
||||
#ifdef __s390x__
|
||||
/* copy original prefix register */
|
||||
zfcpdump_save_areas[cpu_num]->s390x.pref_reg =
|
||||
zfcpdump_prefix_array[cpu_num];
|
||||
#endif
|
||||
cpu_num++;
|
||||
}
|
||||
}
|
||||
|
||||
union save_area *zfcpdump_save_areas[NR_CPUS + 1];
|
||||
EXPORT_SYMBOL_GPL(zfcpdump_save_areas);
|
||||
|
||||
#else
|
||||
#define smp_get_save_areas() do { } while (0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Lets check how many CPUs we have.
|
||||
*/
|
||||
@@ -589,6 +650,7 @@ void __init smp_setup_cpu_possible_map(void)
|
||||
{
|
||||
unsigned int phy_cpus, pos_cpus, cpu;
|
||||
|
||||
smp_get_save_areas();
|
||||
phy_cpus = smp_count_cpus();
|
||||
pos_cpus = min(phy_cpus + additional_cpus, (unsigned int) NR_CPUS);
|
||||
|
||||
|
||||
@@ -29,3 +29,6 @@ obj-$(CONFIG_S390_TAPE_34XX) += tape_34xx.o
|
||||
obj-$(CONFIG_S390_TAPE_3590) += tape_3590.o
|
||||
obj-$(CONFIG_MONREADER) += monreader.o
|
||||
obj-$(CONFIG_MONWRITER) += monwriter.o
|
||||
|
||||
zcore_mod-objs := sclp_sdias.o zcore.o
|
||||
obj-$(CONFIG_ZFCPDUMP) += zcore_mod.o
|
||||
|
||||
@@ -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
|
||||
|
||||
255
drivers/s390/char/sclp_sdias.c
Normal file
255
drivers/s390/char/sclp_sdias.c
Normal file
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Sclp "store data in absolut storage"
|
||||
*
|
||||
* Copyright IBM Corp. 2003,2007
|
||||
* Author(s): Michael Holzheu
|
||||
*/
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <asm/sclp.h>
|
||||
#include <asm/debug.h>
|
||||
#include <asm/ipl.h>
|
||||
#include "sclp.h"
|
||||
#include "sclp_rw.h"
|
||||
|
||||
#define TRACE(x...) debug_sprintf_event(sdias_dbf, 1, x)
|
||||
#define ERROR_MSG(x...) printk ( KERN_ALERT "SDIAS: " x )
|
||||
|
||||
#define SDIAS_RETRIES 300
|
||||
#define SDIAS_SLEEP_TICKS 50
|
||||
|
||||
#define EQ_STORE_DATA 0x0
|
||||
#define EQ_SIZE 0x1
|
||||
#define DI_FCP_DUMP 0x0
|
||||
#define ASA_SIZE_32 0x0
|
||||
#define ASA_SIZE_64 0x1
|
||||
#define EVSTATE_ALL_STORED 0x0
|
||||
#define EVSTATE_NO_DATA 0x3
|
||||
#define EVSTATE_PART_STORED 0x10
|
||||
|
||||
static struct debug_info *sdias_dbf;
|
||||
|
||||
static struct sclp_register sclp_sdias_register = {
|
||||
.send_mask = EvTyp_SDIAS_Mask,
|
||||
};
|
||||
|
||||
struct sdias_evbuf {
|
||||
struct evbuf_header hdr;
|
||||
u8 event_qual;
|
||||
u8 data_id;
|
||||
u64 reserved2;
|
||||
u32 event_id;
|
||||
u16 reserved3;
|
||||
u8 asa_size;
|
||||
u8 event_status;
|
||||
u32 reserved4;
|
||||
u32 blk_cnt;
|
||||
u64 asa;
|
||||
u32 reserved5;
|
||||
u32 fbn;
|
||||
u32 reserved6;
|
||||
u32 lbn;
|
||||
u16 reserved7;
|
||||
u16 dbs;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct sdias_sccb {
|
||||
struct sccb_header hdr;
|
||||
struct sdias_evbuf evbuf;
|
||||
} __attribute__((packed));
|
||||
|
||||
static struct sdias_sccb sccb __attribute__((aligned(4096)));
|
||||
|
||||
static int sclp_req_done;
|
||||
static wait_queue_head_t sdias_wq;
|
||||
static DEFINE_MUTEX(sdias_mutex);
|
||||
|
||||
static void sdias_callback(struct sclp_req *request, void *data)
|
||||
{
|
||||
struct sdias_sccb *sccb;
|
||||
|
||||
sccb = (struct sdias_sccb *) request->sccb;
|
||||
sclp_req_done = 1;
|
||||
wake_up(&sdias_wq); /* Inform caller, that request is complete */
|
||||
TRACE("callback done\n");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
651
drivers/s390/char/zcore.c
Normal file
651
drivers/s390/char/zcore.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,8 @@
|
||||
#define _ASM_S390_IPL_H
|
||||
|
||||
#include <asm/types.h>
|
||||
#include <asm/cio.h>
|
||||
#include <asm/setup.h>
|
||||
|
||||
#define IPL_PARMBLOCK_ORIGIN 0x2000
|
||||
|
||||
@@ -79,6 +81,7 @@ struct ipl_parameter_block {
|
||||
extern u32 ipl_flags;
|
||||
|
||||
extern u32 dump_prefix_page;
|
||||
|
||||
extern void do_reipl(void);
|
||||
extern void ipl_save_parameters(void);
|
||||
|
||||
@@ -88,6 +91,35 @@ enum {
|
||||
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
|
||||
*/
|
||||
|
||||
@@ -147,6 +147,52 @@ void pgm_check_handler(void);
|
||||
void mcck_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
|
||||
{
|
||||
#ifndef __s390x__
|
||||
|
||||
@@ -44,6 +44,8 @@ struct sclp_chp_info {
|
||||
|
||||
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);
|
||||
extern int sclp_chp_configure(struct chp_id chpid);
|
||||
extern int sclp_chp_deconfigure(struct chp_id chpid);
|
||||
extern int sclp_chp_read_info(struct sclp_chp_info *info);
|
||||
|
||||
@@ -40,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;
|
||||
@@ -77,6 +78,7 @@ extern unsigned long machine_flags;
|
||||
#endif /* __s390x__ */
|
||||
|
||||
#define MACHINE_HAS_SCLP (!MACHINE_IS_P390)
|
||||
#define ZFCPDUMP_HSA_SIZE (32UL<<20)
|
||||
|
||||
/*
|
||||
* Console mode. Override with conmode=
|
||||
|
||||
@@ -119,4 +119,5 @@ static inline void smp_send_stop(void)
|
||||
#define smp_setup_cpu_possible_map() do { } while (0)
|
||||
#endif
|
||||
|
||||
extern union save_area *zfcpdump_save_areas[NR_CPUS + 1];
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user