8703 lines
223 KiB
Diff
8703 lines
223 KiB
Diff
From d401e50fb13e62e1d97f17e3a53e6d73bff6f587 Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Dan=20Hor=C3=A1k?= <dan@danny.cz>
|
|
Date: Fri, 28 Jan 2011 14:20:46 +0100
|
|
Subject: [PATCH 54/61] zgetdump/zipl: Add ELF dump support (needed for makedumpfile)
|
|
|
|
Summary: zgetdump/zipl: Add ELF dump support (needed for makedumpfile)
|
|
Description: The zgetdump tool can be used now for dump format conversion.
|
|
It can read ELF, s390, and LKCD and write ELF and s390 format
|
|
dumps. A mount option based on "fuse" is added to zgetdump that
|
|
allows dumps to be converted in memory on the fly without the
|
|
need of copying them. The following two options are added to
|
|
zgetdump:
|
|
* fmt: Specify output dump format (elf or s390)
|
|
* mount: Mount dump instead of copying it to standard output
|
|
|
|
The zipl dump tools now store the prefix registers in the dump
|
|
header.
|
|
|
|
With this patch also multi-volume support for tape dump is
|
|
removed, because today's tape drives have enough capacity to
|
|
store a dump on a single volume.
|
|
---
|
|
zdump/Makefile | 26 +-
|
|
zdump/df_elf.h | 100 ++++
|
|
zdump/df_lkcd.h | 77 +++
|
|
zdump/df_s390.c | 128 +++++
|
|
zdump/df_s390.h | 148 +++++
|
|
zdump/dfi.c | 613 ++++++++++++++++++++
|
|
zdump/dfi.h | 212 +++++++
|
|
zdump/dfi_elf.c | 291 ++++++++++
|
|
zdump/dfi_kdump.c | 122 ++++
|
|
zdump/dfi_lkcd.c | 333 +++++++++++
|
|
zdump/dfi_s390.c | 95 ++++
|
|
zdump/dfi_s390mv.c | 547 ++++++++++++++++++
|
|
zdump/dfi_s390tape.c | 198 +++++++
|
|
zdump/dfo.c | 204 +++++++
|
|
zdump/dfo.h | 54 ++
|
|
zdump/dfo_elf.c | 299 ++++++++++
|
|
zdump/dfo_s390.c | 200 +++++++
|
|
zdump/dt.c | 131 +++++
|
|
zdump/dt.h | 29 +
|
|
zdump/dt_s390mv.c | 19 +
|
|
zdump/dt_s390sv.c | 129 +++++
|
|
zdump/opts.c | 242 ++++++++
|
|
zdump/stdout.c | 38 ++
|
|
zdump/zfuse.c | 238 ++++++++
|
|
zdump/zg.c | 411 ++++++++++++++
|
|
zdump/zg.h | 185 ++++++
|
|
zdump/zgetdump.8 | 334 ++++++++++--
|
|
zdump/zgetdump.c | 1431 ++++-------------------------------------------
|
|
zdump/zgetdump.h | 250 +++------
|
|
zipl/boot/dumpcommon.S | 136 ++++-
|
|
zipl/boot/eckd2dump.S | 32 +-
|
|
zipl/boot/eckd2mvdump.S | 19 +-
|
|
zipl/boot/fba0.S | 30 +-
|
|
zipl/boot/fba2dump.S | 30 +-
|
|
zipl/boot/tapedump.S | 392 ++------------
|
|
zipl/include/boot.h | 4 +-
|
|
zipl/src/boot.c | 2 +-
|
|
zipl/src/install.c | 21 +-
|
|
38 files changed, 5766 insertions(+), 1984 deletions(-)
|
|
create mode 100644 zdump/df_elf.h
|
|
create mode 100644 zdump/df_lkcd.h
|
|
create mode 100644 zdump/df_s390.c
|
|
create mode 100644 zdump/df_s390.h
|
|
create mode 100644 zdump/dfi.c
|
|
create mode 100644 zdump/dfi.h
|
|
create mode 100644 zdump/dfi_elf.c
|
|
create mode 100644 zdump/dfi_kdump.c
|
|
create mode 100644 zdump/dfi_lkcd.c
|
|
create mode 100644 zdump/dfi_s390.c
|
|
create mode 100644 zdump/dfi_s390mv.c
|
|
create mode 100644 zdump/dfi_s390tape.c
|
|
create mode 100644 zdump/dfo.c
|
|
create mode 100644 zdump/dfo.h
|
|
create mode 100644 zdump/dfo_elf.c
|
|
create mode 100644 zdump/dfo_s390.c
|
|
create mode 100644 zdump/dt.c
|
|
create mode 100644 zdump/dt.h
|
|
create mode 100644 zdump/dt_s390mv.c
|
|
create mode 100644 zdump/dt_s390sv.c
|
|
create mode 100644 zdump/opts.c
|
|
create mode 100644 zdump/stdout.c
|
|
create mode 100644 zdump/zfuse.c
|
|
create mode 100644 zdump/zg.c
|
|
create mode 100644 zdump/zg.h
|
|
|
|
diff --git a/zdump/Makefile b/zdump/Makefile
|
|
index cb546de..83c54ef 100644
|
|
--- a/zdump/Makefile
|
|
+++ b/zdump/Makefile
|
|
@@ -1,18 +1,34 @@
|
|
include ../common.mak
|
|
|
|
-CPPFLAGS += -D_FILE_OFFSET_BITS=64 -I../include
|
|
+CPPFLAGS += -D_FILE_OFFSET_BITS=64 -I../include -I/usr/include/fuse
|
|
+LDLIBS += -lz
|
|
|
|
all: zgetdump
|
|
|
|
-zgetdump.o: zgetdump.h
|
|
-zgetdump: zgetdump.o
|
|
+OBJECTS = zgetdump.o opts.o zg.o \
|
|
+ dfi.o dfi_lkcd.o dfi_elf.o dfi_s390.o dfi_s390mv.o dfi_s390tape.o \
|
|
+ dfo.o dfo_elf.o dfo_s390.o \
|
|
+ df_s390.o \
|
|
+ dt.o dt_s390sv.o dt_s390mv.o \
|
|
+ stdout.o
|
|
+
|
|
+ifneq ("$(WITHOUT_FUSE)","1")
|
|
+LDLIBS += -lfuse
|
|
+OBJECTS += zfuse.o
|
|
+else
|
|
+CPPFLAGS += -DWITHOUT_FUSE
|
|
+endif
|
|
+
|
|
+$(OBJECTS): *.h Makefile
|
|
+
|
|
+zgetdump: $(OBJECTS)
|
|
|
|
install: all
|
|
$(INSTALL) -d -m 755 $(MANDIR)/man8 $(BINDIR)
|
|
$(INSTALL) -m 755 zgetdump $(BINDIR)
|
|
- $(INSTALL) -m 644 zgetdump.8 $(MANDIR)/man8
|
|
+ $(INSTALL) -m 644 zgetdump.8 $(MANDIR)/man8
|
|
|
|
clean:
|
|
- rm -f *.o *~ zgetdump core
|
|
+ rm -f *.o *~ zgetdump core.*
|
|
|
|
.PHONY: all install clean
|
|
diff --git a/zdump/df_elf.h b/zdump/df_elf.h
|
|
new file mode 100644
|
|
index 0000000..13121c3
|
|
--- /dev/null
|
|
+++ b/zdump/df_elf.h
|
|
@@ -0,0 +1,100 @@
|
|
+/*
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
+ *
|
|
+ * ELF core dump format definitions
|
|
+ *
|
|
+ * Copyright IBM Corp. 2001, 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#ifndef DF_ELF_H
|
|
+#define DF_ELF_H
|
|
+
|
|
+#include <linux/types.h>
|
|
+#include <elf.h>
|
|
+#include "zg.h"
|
|
+#include "dfo.h"
|
|
+
|
|
+/*
|
|
+ * S390 CPU timer note (u64)
|
|
+ */
|
|
+#ifndef NT_S390_TIMER
|
|
+#define NT_S390_TIMER 0x301
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * S390 TOD clock comparator note (u64)
|
|
+ */
|
|
+#ifndef NT_S390_TODCMP
|
|
+#define NT_S390_TODCMP 0x302
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * S390 TOD programmable register note (u32)
|
|
+ */
|
|
+#ifndef NT_S390_TODPREG
|
|
+#define NT_S390_TODPREG 0x303
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * S390 control registers note (16 * u32)
|
|
+ */
|
|
+#ifndef NT_S390_CTRS
|
|
+#define NT_S390_CTRS 0x304
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * S390 prefix note (u32)
|
|
+ */
|
|
+#ifndef NT_S390_PREFIX
|
|
+#define NT_S390_PREFIX 0x305
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * prstatus ELF Note
|
|
+ */
|
|
+struct nt_prstatus_64 {
|
|
+ u8 pad1[32];
|
|
+ u32 pr_pid;
|
|
+ u8 pad2[76];
|
|
+ u64 psw[2];
|
|
+ u64 gprs[16];
|
|
+ u32 acrs[16];
|
|
+ u64 orig_gpr2;
|
|
+ u32 pr_fpvalid;
|
|
+ u8 pad3[4];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * fpregset ELF Note
|
|
+ */
|
|
+struct nt_fpregset_64 {
|
|
+ u32 fpc;
|
|
+ u32 pad;
|
|
+ u64 fprs[16];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * prpsinfo ELF Note
|
|
+ */
|
|
+struct nt_prpsinfo_64 {
|
|
+ char pr_state;
|
|
+ char pr_sname;
|
|
+ char pr_zomb;
|
|
+ char pr_nice;
|
|
+ u64 pr_flag;
|
|
+ u32 pr_uid;
|
|
+ u32 pr_gid;
|
|
+ u32 pr_pid, pr_ppid, pr_pgrp, pr_sid;
|
|
+ char pr_fname[16];
|
|
+ char pr_psargs[80];
|
|
+};
|
|
+
|
|
+static inline void df_elf_ensure_s390x(void)
|
|
+{
|
|
+#ifndef __s390x__
|
|
+ ERR_EXIT("The ELF dump format is only supported on s390x (64 bit)");
|
|
+#endif
|
|
+}
|
|
+
|
|
+#endif /* DF_ELF_H */
|
|
diff --git a/zdump/df_lkcd.h b/zdump/df_lkcd.h
|
|
new file mode 100644
|
|
index 0000000..ebb9eef
|
|
--- /dev/null
|
|
+++ b/zdump/df_lkcd.h
|
|
@@ -0,0 +1,77 @@
|
|
+/*
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
+ *
|
|
+ * LKCD dump format definitions
|
|
+ *
|
|
+ * Copyright IBM Corp. 2001, 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#ifndef DF_LKCD_H
|
|
+#define DF_LKCD_H
|
|
+
|
|
+#define DF_LKCD_MAGIC 0xa8190173618f23edULL
|
|
+#define DF_LKCD_MAGIC_ASM 0x733339302d64756dULL
|
|
+#define DF_LKCD_VERSION 0x8 /* dump version number */
|
|
+#define DF_LKCD_PANIC_LEN 0x100 /* dump panic string length */
|
|
+#define DF_LKCD_HDR_SIZE 0x10000 /* Max space for the dump header */
|
|
+
|
|
+#define DF_LKCD_COMPRESS_NONE 0x0 /* don't compress this dump */
|
|
+#define DF_LKCD_COMPRESS_GZIP 0x2 /* use GZIP compression */
|
|
+
|
|
+#define DF_LKCD_DH_RAW 0x1 /* raw pg (no compression) */
|
|
+#define DF_LKCD_DH_COMPRESSED 0x2 /* pg is compressed */
|
|
+#define DF_LKCD_DH_END 0x4 /* end marker on a full dump */
|
|
+
|
|
+#define DF_LKCD_UCP_SIZE (PAGE_SIZE + sizeof(struct df_lkcd_pg_hdr))
|
|
+
|
|
+/*
|
|
+ * LKCD standard header
|
|
+ */
|
|
+struct df_lkcd_hdr {
|
|
+ u64 magic;
|
|
+ u32 version;
|
|
+ u32 hdr_size;
|
|
+ u32 dump_level;
|
|
+ u32 page_size;
|
|
+ u64 mem_size;
|
|
+ u64 mem_start;
|
|
+ u64 mem_end;
|
|
+ u32 num_dump_pgs;
|
|
+ char panic_string[0x100];
|
|
+ u64 time_tv_sec;
|
|
+ u64 time_tv_usec;
|
|
+ char utsname_sysname[65];
|
|
+ char utsname_nodename[65];
|
|
+ char utsname_release[65];
|
|
+ char utsname_version[65];
|
|
+ char utsname_machine[65];
|
|
+ char utsname_domainname[65];
|
|
+ u64 current_task;
|
|
+ u32 dump_compress;
|
|
+ u32 dump_flags;
|
|
+ u32 dump_device;
|
|
+} __attribute__((packed));
|
|
+
|
|
+/*
|
|
+ * s390 LKCD asm header
|
|
+ */
|
|
+struct df_lkcd_hdr_asm {
|
|
+ u64 magic;
|
|
+ u32 version;
|
|
+ u32 hdr_size;
|
|
+ u16 cpu_cnt;
|
|
+ u16 real_cpu_cnt;
|
|
+ u32 lc_vec[512];
|
|
+} __attribute__((packed));
|
|
+
|
|
+/*
|
|
+ * Page header
|
|
+ */
|
|
+struct df_lkcd_pg_hdr {
|
|
+ u64 addr; /* Address of dump page */
|
|
+ u32 size; /* Size of dump page */
|
|
+ u32 flags; /* flags (DF_LKCD_COMPRESSED, DF_LKCD_RAW,...) */
|
|
+} __attribute__((packed));
|
|
+
|
|
+#endif /* DF_LKCD_H */
|
|
diff --git a/zdump/df_s390.c b/zdump/df_s390.c
|
|
new file mode 100644
|
|
index 0000000..b1807bb
|
|
--- /dev/null
|
|
+++ b/zdump/df_s390.c
|
|
@@ -0,0 +1,128 @@
|
|
+/*
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
+ *
|
|
+ * S390 dump format common functions
|
|
+ *
|
|
+ * Copyright IBM Corp. 2001, 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include <string.h>
|
|
+#include <time.h>
|
|
+#include <fcntl.h>
|
|
+#include <stdio.h>
|
|
+#include <unistd.h>
|
|
+#include <sys/types.h>
|
|
+#include <sys/stat.h>
|
|
+#include "zgetdump.h"
|
|
+
|
|
+/*
|
|
+ * Check, if we can access the lowcore information in the dump
|
|
+ */
|
|
+static int check_addr_max(struct df_s390_hdr *hdr, u64 addr_max)
|
|
+{
|
|
+ unsigned int i, lc_size;
|
|
+
|
|
+ lc_size = dfi_lc_size(df_s390_to_dfi_arch(hdr->arch));
|
|
+ for (i = 0; i < hdr->cpu_cnt; i++) {
|
|
+ if (hdr->lc_vec[i] + lc_size > addr_max)
|
|
+ return -1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Convert lowcore information into internal CPU representation
|
|
+ */
|
|
+void df_s390_cpu_info_add(struct df_s390_hdr *hdr, u64 addr_max)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ if (hdr->version < 5) {
|
|
+ /* No Prefix registers in header */
|
|
+ hdr->cpu_cnt = 0;
|
|
+ dfi_cpu_info_init(DFI_CPU_CONTENT_NONE);
|
|
+ } else if (check_addr_max(hdr, addr_max) != 0) {
|
|
+ /* Only lowcore pointers available */
|
|
+ dfi_cpu_info_init(DFI_CPU_CONTENT_LC);
|
|
+ } else {
|
|
+ /* All register info available */
|
|
+ dfi_cpu_info_init(DFI_CPU_CONTENT_ALL);
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < hdr->cpu_cnt; i++)
|
|
+ dfi_cpu_add_from_lc(hdr->lc_vec[i]);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Convert s390 TOD clock into timeval structure
|
|
+ */
|
|
+static void tod2timeval(struct timeval *xtime, u64 todval)
|
|
+{
|
|
+ /* adjust todclock to 1970 */
|
|
+ todval -= 0x8126d60e46000000LL - (0x3c26700LL * 1000000 * 4096);
|
|
+
|
|
+ todval >>= 12;
|
|
+ xtime->tv_sec = todval / 1000000;
|
|
+ xtime->tv_usec = todval % 1000000;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Convert s390 header information into internal representation
|
|
+ */
|
|
+void df_s390_hdr_add(struct df_s390_hdr *hdr)
|
|
+{
|
|
+ struct timeval timeval;
|
|
+
|
|
+ if (hdr->tod) {
|
|
+ tod2timeval(&timeval, hdr->tod);
|
|
+ dfi_attr_time_set(&timeval);
|
|
+ }
|
|
+ dfi_attr_version_set(hdr->version);
|
|
+ dfi_arch_set(df_s390_to_dfi_arch(hdr->arch));
|
|
+ if (hdr->cpu_id)
|
|
+ dfi_attr_cpu_id_set(hdr->cpu_id);
|
|
+ if (hdr->version >= 3 && hdr->mem_size_real)
|
|
+ dfi_attr_mem_size_real_set(hdr->mem_size_real);
|
|
+ if (hdr->version >= 2 && hdr->build_arch)
|
|
+ dfi_attr_build_arch_set(df_s390_to_dfi_arch(hdr->build_arch));
|
|
+ if (hdr->version >= 5 && hdr->real_cpu_cnt)
|
|
+ dfi_attr_real_cpu_cnt_set(hdr->real_cpu_cnt);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Add end marker information to internal representation
|
|
+ */
|
|
+void df_s390_em_add(struct df_s390_em *em)
|
|
+{
|
|
+ struct timeval timeval;
|
|
+
|
|
+ if (em->tod) {
|
|
+ tod2timeval(&timeval, em->tod);
|
|
+ dfi_attr_time_end_set(&timeval);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Verify end marker
|
|
+ */
|
|
+int df_s390_em_verify(struct df_s390_em *em, struct df_s390_hdr *hdr)
|
|
+{
|
|
+ if (strncmp(em->str, DF_S390_EM_STR, strlen(DF_S390_EM_STR)) != 0)
|
|
+ return -EINVAL;
|
|
+ if (hdr->tod > em->tod)
|
|
+ return -EINVAL;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read s390 dump tool from DASD with given block size
|
|
+ */
|
|
+void df_s390_dumper_read(struct zg_fh *fh, int blk_size,
|
|
+ struct df_s390_dumper *dumper)
|
|
+{
|
|
+ int offset = DF_S390_MAGIC_BLK_ECKD * blk_size;
|
|
+
|
|
+ zg_seek(fh, offset, ZG_CHECK);
|
|
+ zg_read(fh, dumper, sizeof(*dumper), ZG_CHECK);
|
|
+}
|
|
diff --git a/zdump/df_s390.h b/zdump/df_s390.h
|
|
new file mode 100644
|
|
index 0000000..81b519b
|
|
--- /dev/null
|
|
+++ b/zdump/df_s390.h
|
|
@@ -0,0 +1,148 @@
|
|
+/*
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
+ *
|
|
+ * S390 dump format common functions
|
|
+ *
|
|
+ * Copyright IBM Corp. 2001, 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#ifndef DF_S390_H
|
|
+#define DF_S390_H
|
|
+
|
|
+#include "dt.h"
|
|
+#include "zg.h"
|
|
+
|
|
+#define DF_S390_MAGIC 0xa8190173618f23fdULL
|
|
+#define DF_S390_HDR_SIZE 0x1000
|
|
+#define DF_S390_EM_SIZE 16
|
|
+#define DF_S390_EM_STR "DUMP_END"
|
|
+#define DF_S390_CPU_MAX 512
|
|
+#define DF_S390_MAGIC_BLK_ECKD 3
|
|
+
|
|
+/*
|
|
+ * Architecture of dumped system
|
|
+ */
|
|
+enum df_s390_arch {
|
|
+ DF_S390_ARCH_32 = 1,
|
|
+ DF_S390_ARCH_64 = 2,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * s390 dump header format
|
|
+ */
|
|
+struct df_s390_hdr {
|
|
+ u64 magic; /* 0x000 */
|
|
+ u32 version; /* 0x008 */
|
|
+ u32 hdr_size; /* 0x00c */
|
|
+ u32 dump_level; /* 0x010 */
|
|
+ u32 page_size; /* 0x014 */
|
|
+ u64 mem_size; /* 0x018 */
|
|
+ u64 mem_start; /* 0x020 */
|
|
+ u64 mem_end; /* 0x028 */
|
|
+ u32 num_pages; /* 0x030 */
|
|
+ u32 pad; /* 0x034 */
|
|
+ u64 tod; /* 0x038 */
|
|
+ u64 cpu_id; /* 0x040 */
|
|
+ u32 arch; /* 0x048 */
|
|
+ u32 volnr; /* 0x04c */
|
|
+ u32 build_arch; /* 0x050 */
|
|
+ u64 mem_size_real; /* 0x054 */
|
|
+ u8 mvdump; /* 0x05c */
|
|
+ u16 cpu_cnt; /* 0x05d */
|
|
+ u16 real_cpu_cnt; /* 0x05f */
|
|
+ u8 end_pad1[0x200-0x061]; /* 0x061 */
|
|
+ u64 mvdump_sign; /* 0x200 */
|
|
+ u64 mvdump_zipl_time; /* 0x208 */
|
|
+ u8 end_pad2[0x800-0x210]; /* 0x210 */
|
|
+ u32 lc_vec[DF_S390_CPU_MAX]; /* 0x800 */
|
|
+} __attribute__((packed));
|
|
+
|
|
+/*
|
|
+ * End marker: Should be at the end of every valid s390 crash dump.
|
|
+ */
|
|
+struct df_s390_em {
|
|
+ char str[8];
|
|
+ u64 tod;
|
|
+} __attribute__((packed));
|
|
+
|
|
+/*
|
|
+ * Convert DFI arch to s390 arch
|
|
+ */
|
|
+static inline enum df_s390_arch df_s390_from_dfi_arch(enum dfi_arch dfi_arch)
|
|
+{
|
|
+ return dfi_arch == DFI_ARCH_64 ? DF_S390_ARCH_64 : DF_S390_ARCH_32;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Convert s390 arch to DFI arch
|
|
+ */
|
|
+static inline enum dfi_arch df_s390_to_dfi_arch(enum df_s390_arch df_s390_arch)
|
|
+{
|
|
+ return df_s390_arch == DF_S390_ARCH_64 ? DFI_ARCH_64 : DFI_ARCH_32;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Dump tool structure (version 1)
|
|
+ */
|
|
+struct df_s390_dumper_v1 {
|
|
+ char code[0xff7 - 0x8];
|
|
+ u8 force;
|
|
+ u64 mem;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+#define DF_S390_DUMPER_SIZE_V1 0x1000
|
|
+
|
|
+/*
|
|
+ * Dump tool structure (version 2)
|
|
+ */
|
|
+struct df_s390_dumper_v2 {
|
|
+ char code[0x1ff7 - 0x8];
|
|
+ u8 force;
|
|
+ u64 mem;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+#define DF_S390_DUMPER_SIZE_V2 0x2000
|
|
+
|
|
+/*
|
|
+ * Dump tool structure
|
|
+ */
|
|
+struct df_s390_dumper {
|
|
+ char magic[7];
|
|
+ u8 version;
|
|
+ union {
|
|
+ struct df_s390_dumper_v1 v1;
|
|
+ struct df_s390_dumper_v2 v2;
|
|
+ } d;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * Dumper member access helpers
|
|
+ */
|
|
+#define df_s390_dumper_magic(dumper) ((dumper).magic)
|
|
+#define df_s390_dumper_version(dumper) ((dumper).version)
|
|
+#define df_s390_dumper_mem(dumper) \
|
|
+ ((dumper).version == 1 ? dumper.d.v1.mem : dumper.d.v2.mem)
|
|
+#define df_s390_dumper_force(dumper) \
|
|
+ ((dumper).version == 1 ? dumper.d.v1.force : dumper.d.v2.force)
|
|
+#define df_s390_dumper_size(dumper) \
|
|
+ ((dumper).version == 1 ? 0x1000 : 0x2000)
|
|
+
|
|
+/*
|
|
+ * s390 dump helpers
|
|
+ */
|
|
+extern void df_s390_hdr_add(struct df_s390_hdr *hdr);
|
|
+extern void df_s390_em_add(struct df_s390_em *em);
|
|
+extern void df_s390_cpu_info_add(struct df_s390_hdr *hdr, u64 addr_max);
|
|
+extern int df_s390_em_verify(struct df_s390_em *em, struct df_s390_hdr *hdr);
|
|
+extern void df_s390_dumper_read(struct zg_fh *fh, int32_t blk_size,
|
|
+ struct df_s390_dumper *dumper);
|
|
+
|
|
+/*
|
|
+ * DASD multi-volume dumper functions
|
|
+ */
|
|
+extern int dt_s390mv_init(void);
|
|
+extern void dt_s390mv_exit(void);
|
|
+extern void dt_s390mv_info(void);
|
|
+
|
|
+#endif /* DF_S390_H */
|
|
diff --git a/zdump/dfi.c b/zdump/dfi.c
|
|
new file mode 100644
|
|
index 0000000..fc7bf12
|
|
--- /dev/null
|
|
+++ b/zdump/dfi.c
|
|
@@ -0,0 +1,613 @@
|
|
+/*
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
+ *
|
|
+ * Generic input dump format functions (DFI - Dump Format Input)
|
|
+ *
|
|
+ * Copyright IBM Corp. 2001, 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include <time.h>
|
|
+#include "zgetdump.h"
|
|
+
|
|
+#define TIME_FMT_STR "%a, %d %b %Y %H:%M:%S %z"
|
|
+#define PROGRESS_HASH_CNT 50
|
|
+
|
|
+/*
|
|
+ * DFI vector - ensure that tape is the first in the list!
|
|
+ */
|
|
+static struct dfi *dfi_vec[] = {
|
|
+ &dfi_s390tape,
|
|
+ &dfi_s390mv,
|
|
+ &dfi_s390,
|
|
+ &dfi_lkcd,
|
|
+ &dfi_elf,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * CPU information
|
|
+ */
|
|
+struct cpus {
|
|
+ struct list list;
|
|
+ enum dfi_cpu_content content;
|
|
+ unsigned int cnt;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Memory information
|
|
+ */
|
|
+struct mem {
|
|
+ struct dfi_mem_chunk *chunk_cache;
|
|
+ u64 start_addr;
|
|
+ u64 end_addr;
|
|
+ unsigned int chunk_cnt;
|
|
+ struct list chunk_list;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Dump header attribute information
|
|
+ */
|
|
+struct attr {
|
|
+ unsigned int *dfi_version;
|
|
+ struct timeval *time;
|
|
+ struct timeval *time_end;
|
|
+ u64 *cpu_id;
|
|
+ u64 *mem_size_real;
|
|
+ enum dfi_arch *build_arch;
|
|
+ unsigned int *vol_nr;
|
|
+ u32 *real_cpu_cnt;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * File local static data
|
|
+ */
|
|
+static struct {
|
|
+ enum dfi_arch arch;
|
|
+ struct attr attr;
|
|
+ struct mem mem;
|
|
+ struct cpus cpus;
|
|
+ struct dfi *dfi;
|
|
+} l;
|
|
+
|
|
+/*
|
|
+ * Print Dump date
|
|
+ */
|
|
+static void date_print(void)
|
|
+{
|
|
+ char time_str[80];
|
|
+ struct tm *tmp;
|
|
+
|
|
+ if (l.attr.time) {
|
|
+ tmp = localtime(&l.attr.time->tv_sec);
|
|
+ strftime(time_str, sizeof(time_str), TIME_FMT_STR, tmp);
|
|
+ STDERR(" Dump created.......: %s\n", time_str);
|
|
+ }
|
|
+ if (l.attr.time_end) {
|
|
+ tmp = localtime(&l.attr.time_end->tv_sec);
|
|
+ strftime(time_str, sizeof(time_str), TIME_FMT_STR, tmp);
|
|
+ STDERR(" Dump ended.........: %s\n", time_str);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print memory map
|
|
+ */
|
|
+static void mem_map_print(void)
|
|
+{
|
|
+ struct dfi_mem_chunk *mem_chunk;
|
|
+
|
|
+ STDERR("\nMemory map:\n");
|
|
+ dfi_mem_chunk_iterate(mem_chunk) {
|
|
+ STDERR(" %016llx - %016llx (%llu MB)\n", mem_chunk->start,
|
|
+ mem_chunk->end, TO_MIB(mem_chunk->size));
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print dump information (--info option)
|
|
+ */
|
|
+void dfi_info_print(void)
|
|
+{
|
|
+ STDERR("General dump info:\n");
|
|
+ STDERR(" Dump format........: %s\n", l.dfi->name);
|
|
+ if (l.attr.dfi_version)
|
|
+ STDERR(" Version............: %d\n", *l.attr.dfi_version);
|
|
+ date_print();
|
|
+ if (l.attr.cpu_id)
|
|
+ STDERR(" Dump CPU ID........: %llx\n", *l.attr.cpu_id);
|
|
+ if (l.attr.vol_nr)
|
|
+ STDERR(" Volume number......: %d\n", *l.attr.vol_nr);
|
|
+ if (l.attr.build_arch)
|
|
+ STDERR(" Build arch.........: %s\n",
|
|
+ dfi_arch_str(*l.attr.build_arch));
|
|
+ STDERR(" System arch........: %s\n", dfi_arch_str(l.arch));
|
|
+ if (l.cpus.cnt)
|
|
+ STDERR(" CPU count (online).: %d\n", l.cpus.cnt);
|
|
+ if (l.attr.real_cpu_cnt)
|
|
+ STDERR(" CPU count (real)...: %d\n", *l.attr.real_cpu_cnt);
|
|
+ STDERR(" Dump memory range..: %lld MB\n", TO_MIB(dfi_mem_range()));
|
|
+ if (l.attr.mem_size_real)
|
|
+ STDERR(" Real memory range..: %lld MB\n",
|
|
+ TO_MIB(*l.attr.mem_size_real));
|
|
+ mem_map_print();
|
|
+ if (l.dfi->info_dump) {
|
|
+ STDERR("\nDump device info:\n");
|
|
+ l.dfi->info_dump();
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Add memory chunk
|
|
+ */
|
|
+void dfi_mem_chunk_add(u64 start, u64 size, void *data,
|
|
+ dfi_mem_chunk_read_fn read_fn)
|
|
+{
|
|
+ struct dfi_mem_chunk *mem_chunk;
|
|
+
|
|
+ mem_chunk = zg_alloc(sizeof(*mem_chunk));
|
|
+ mem_chunk->start = start;
|
|
+ mem_chunk->end = start + size - 1;
|
|
+ mem_chunk->size = size;
|
|
+ mem_chunk->read_fn = read_fn;
|
|
+ mem_chunk->data = data;
|
|
+
|
|
+ list_add_end(&mem_chunk->list, &l.mem.chunk_list);
|
|
+ l.mem.start_addr = MIN(l.mem.start_addr, mem_chunk->start);
|
|
+ l.mem.end_addr = MAX(l.mem.end_addr, mem_chunk->end);
|
|
+ l.mem.chunk_cache = mem_chunk;
|
|
+ l.mem.chunk_cnt++;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return mem_chunk list head
|
|
+ */
|
|
+struct list *dfi_mem_chunk_list(void)
|
|
+{
|
|
+ return &l.mem.chunk_list;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return number of memory chunks in input dump
|
|
+ */
|
|
+unsigned int dfi_mem_chunk_cnt(void)
|
|
+{
|
|
+ return l.mem.chunk_cnt;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return maximum memory range
|
|
+ */
|
|
+u64 dfi_mem_range(void)
|
|
+{
|
|
+ return l.mem.end_addr - l.mem.start_addr + 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return first memory chunk
|
|
+ */
|
|
+struct dfi_mem_chunk *dfi_mem_chunk_first(void)
|
|
+{
|
|
+ if (list_is_empty(&l.mem.chunk_list))
|
|
+ return NULL;
|
|
+ return list_entry_first(&l.mem.chunk_list, struct dfi_mem_chunk, list);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return next memory chunk
|
|
+ */
|
|
+struct dfi_mem_chunk *dfi_mem_chunk_next(struct dfi_mem_chunk *mem_chunk)
|
|
+{
|
|
+ if (mem_chunk->list.next == &l.mem.chunk_list)
|
|
+ return NULL;
|
|
+ return list_entry_next(&mem_chunk->list, struct dfi_mem_chunk, list);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return previous memory chunk
|
|
+ */
|
|
+struct dfi_mem_chunk *dfi_mem_chunk_prev(struct dfi_mem_chunk *mem_chunk)
|
|
+{
|
|
+ if (mem_chunk->list.prev == &l.mem.chunk_list)
|
|
+ return NULL;
|
|
+ return list_entry_prev(&mem_chunk->list, struct dfi_mem_chunk, list);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Check if memory chunk contains address
|
|
+ */
|
|
+static int mem_chunk_has_addr(struct dfi_mem_chunk *mem_chunk, u64 addr)
|
|
+{
|
|
+ return (addr >= mem_chunk->start && addr <= mem_chunk->end);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Find memory chunk for given address
|
|
+ */
|
|
+struct dfi_mem_chunk *dfi_mem_chunk_find(u64 addr)
|
|
+{
|
|
+ struct dfi_mem_chunk *mem_chunk;
|
|
+
|
|
+ if (mem_chunk_has_addr(l.mem.chunk_cache, addr))
|
|
+ return l.mem.chunk_cache;
|
|
+ dfi_mem_chunk_iterate(mem_chunk) {
|
|
+ if (mem_chunk_has_addr(mem_chunk, addr)) {
|
|
+ l.mem.chunk_cache = mem_chunk;
|
|
+ return mem_chunk;
|
|
+ }
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize CPU info
|
|
+ */
|
|
+void dfi_cpu_info_init(enum dfi_cpu_content cpu_content)
|
|
+{
|
|
+ l.cpus.content = cpu_content;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Allocate new DFI CPU
|
|
+ */
|
|
+struct dfi_cpu *dfi_cpu_alloc(void)
|
|
+{
|
|
+ return zg_alloc(sizeof(struct dfi_cpu));
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Add DFI CPU
|
|
+ */
|
|
+void dfi_cpu_add(struct dfi_cpu *cpu)
|
|
+{
|
|
+ list_add_end(&cpu->list, &l.cpus.list);
|
|
+ l.cpus.cnt++;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return CPU with number cpu_nr
|
|
+ */
|
|
+struct dfi_cpu *dfi_cpu(unsigned int cpu_nr)
|
|
+{
|
|
+ struct dfi_cpu *cpu;
|
|
+ unsigned int i = 0;
|
|
+
|
|
+ dfi_cpu_iterate(cpu) {
|
|
+ if (i == cpu_nr)
|
|
+ return cpu;
|
|
+ i++;
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return CPU count
|
|
+ */
|
|
+unsigned int dfi_cpu_cnt(void)
|
|
+{
|
|
+ return l.cpus.cnt;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return CPU content
|
|
+ */
|
|
+enum dfi_cpu_content dfi_cpu_content(void)
|
|
+{
|
|
+ return l.cpus.content;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Set DFI architecture
|
|
+ */
|
|
+void dfi_arch_set(enum dfi_arch arch)
|
|
+{
|
|
+ l.arch = arch;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return DFI architecture
|
|
+ */
|
|
+enum dfi_arch dfi_arch(void)
|
|
+{
|
|
+ return l.arch;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return DFI CPU list
|
|
+ */
|
|
+struct list *dfi_cpu_list(void)
|
|
+{
|
|
+ return &l.cpus.list;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read memory at given address
|
|
+ */
|
|
+void dfi_mem_read(u64 addr, void *buf, size_t cnt)
|
|
+{
|
|
+ struct dfi_mem_chunk *mem_chunk;
|
|
+ u64 size, copied = 0;
|
|
+
|
|
+ while (copied != cnt) {
|
|
+ mem_chunk = dfi_mem_chunk_find(addr);
|
|
+ size = MIN(cnt - copied, mem_chunk->end - addr + 1);
|
|
+ mem_chunk->read_fn(mem_chunk, addr - mem_chunk->start,
|
|
+ buf + copied, size);
|
|
+ copied += size;
|
|
+ addr += size;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Get input dump format name
|
|
+ */
|
|
+const char *dfi_name(void)
|
|
+{
|
|
+ return l.dfi->name;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Can input dump format seek?
|
|
+ */
|
|
+int dfi_feat_seek(void)
|
|
+{
|
|
+ return l.dfi->feat_bits & DFI_FEAT_SEEK;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Can input dump format be used for copying?
|
|
+ */
|
|
+int dfi_feat_copy(void)
|
|
+{
|
|
+ return l.dfi->feat_bits & DFI_FEAT_COPY;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Return DFI arch string
|
|
+ */
|
|
+const char *dfi_arch_str(enum dfi_arch arch)
|
|
+{
|
|
+ switch (arch) {
|
|
+ case DFI_ARCH_32:
|
|
+ return "s390 (32 bit)";
|
|
+ case DFI_ARCH_64:
|
|
+ return "s390x (64 bit)";
|
|
+ case DFI_ARCH_UNKNOWN:
|
|
+ return "unknown";
|
|
+ }
|
|
+ ABORT("dfi_arch_str: Invalid dfi arch: %d", arch);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize input dump format.
|
|
+ */
|
|
+int dfi_init(void)
|
|
+{
|
|
+ struct dfi *dfi;
|
|
+ int i = 0, rc;
|
|
+
|
|
+ l.mem.start_addr = U64_MAX;
|
|
+ l.mem.end_addr = 0;
|
|
+ list_init(&l.mem.chunk_list);
|
|
+ list_init(&l.cpus.list);
|
|
+ while ((dfi = dfi_vec[i])) {
|
|
+ l.dfi = dfi;
|
|
+ g.fh = zg_open(g.opts.device, O_RDONLY, ZG_CHECK);
|
|
+ rc = dfi->init();
|
|
+ if (rc == 0 || rc == -EINVAL)
|
|
+ return rc;
|
|
+ zg_close(g.fh);
|
|
+ i++;
|
|
+ }
|
|
+ ERR_EXIT("No valid dump found on \"%s\"", g.opts.device);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Attribute: Dump time
|
|
+ */
|
|
+void dfi_attr_time_set(struct timeval *time)
|
|
+{
|
|
+ l.attr.time = zg_alloc(sizeof(*l.attr.time));
|
|
+ *l.attr.time = *time;
|
|
+}
|
|
+
|
|
+struct timeval *dfi_attr_time(void)
|
|
+{
|
|
+ return l.attr.time;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Attribute: Dump end time
|
|
+ */
|
|
+void dfi_attr_time_end_set(struct timeval *time_end)
|
|
+{
|
|
+ l.attr.time_end = zg_alloc(sizeof(*l.attr.time_end));
|
|
+ *l.attr.time_end = *time_end;
|
|
+}
|
|
+
|
|
+struct timeval *dfi_attr_time_end(void)
|
|
+{
|
|
+ return l.attr.time_end;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Attribute: Volume number
|
|
+ */
|
|
+void dfi_attr_vol_nr_set(unsigned int vol_nr)
|
|
+{
|
|
+ l.attr.vol_nr = zg_alloc(sizeof(*l.attr.vol_nr));
|
|
+ *l.attr.vol_nr = vol_nr;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Attribute: DFI version
|
|
+ */
|
|
+void dfi_attr_version_set(unsigned int dfi_version)
|
|
+{
|
|
+ l.attr.dfi_version = zg_alloc(sizeof(*l.attr.dfi_version));
|
|
+ *l.attr.dfi_version = dfi_version;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Attribute: CPU ID
|
|
+ */
|
|
+void dfi_attr_cpu_id_set(u64 cpu_id)
|
|
+{
|
|
+ l.attr.cpu_id = zg_alloc(sizeof(*l.attr.cpu_id));
|
|
+ *l.attr.cpu_id = cpu_id;
|
|
+}
|
|
+
|
|
+u64 *dfi_attr_cpu_id(void)
|
|
+{
|
|
+ return l.attr.cpu_id;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Attribute: Real memory size
|
|
+ */
|
|
+void dfi_attr_mem_size_real_set(u64 mem_size_real)
|
|
+{
|
|
+ l.attr.mem_size_real = zg_alloc(sizeof(*l.attr.mem_size_real));
|
|
+ *l.attr.mem_size_real = mem_size_real;
|
|
+}
|
|
+
|
|
+u64 *dfi_attr_mem_size_real(void)
|
|
+{
|
|
+ return l.attr.mem_size_real;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Attribute: Build architecture
|
|
+ */
|
|
+void dfi_attr_build_arch_set(enum dfi_arch build_arch)
|
|
+{
|
|
+ l.attr.build_arch = zg_alloc(sizeof(*l.attr.build_arch));
|
|
+ *l.attr.build_arch = build_arch;
|
|
+}
|
|
+
|
|
+enum dfi_arch *dfi_attr_build_arch(void)
|
|
+{
|
|
+ return l.attr.build_arch;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Attribute: Real CPU count
|
|
+ */
|
|
+void dfi_attr_real_cpu_cnt_set(unsigned int real_cnt_cnt)
|
|
+{
|
|
+ l.attr.real_cpu_cnt = zg_alloc(sizeof(*l.attr.real_cpu_cnt));
|
|
+ *l.attr.real_cpu_cnt = real_cnt_cnt;
|
|
+}
|
|
+
|
|
+unsigned int *dfi_attr_real_cpu_cnt(void)
|
|
+{
|
|
+ return l.attr.real_cpu_cnt;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Convert 32 bit CPU register set to 64 bit
|
|
+ */
|
|
+static void cpu_32_to_64(struct dfi_cpu *cpu_64, struct dfi_cpu_32 *cpu_32)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < 16; i++) {
|
|
+ cpu_64->gprs[i] = cpu_32->gprs[i];
|
|
+ cpu_64->ctrs[i] = cpu_32->ctrs[i];
|
|
+ cpu_64->acrs[i] = cpu_32->acrs[i];
|
|
+ if (i < 4)
|
|
+ cpu_64->fprs[i] = cpu_32->fprs[i];
|
|
+ }
|
|
+ cpu_64->psw[0] = cpu_32->psw[0];
|
|
+ cpu_64->psw[1] = cpu_32->psw[1];
|
|
+ cpu_64->prefix = cpu_32->prefix;
|
|
+ cpu_64->timer = cpu_32->timer;
|
|
+ cpu_64->todcmp = cpu_32->todcmp;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Convert 64 bit CPU register set to 32 bit
|
|
+ */
|
|
+void dfi_cpu_64_to_32(struct dfi_cpu_32 *cpu_32, struct dfi_cpu *cpu_64)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < 16; i++) {
|
|
+ cpu_32->gprs[i] = (u32) cpu_64->gprs[i];
|
|
+ cpu_32->ctrs[i] = (u32) cpu_64->ctrs[i];
|
|
+ cpu_32->acrs[i] = (u32) cpu_64->acrs[i];
|
|
+ if (i < 4)
|
|
+ cpu_32->fprs[i] = (u32) cpu_64->fprs[i];
|
|
+ }
|
|
+ cpu_32->psw[0] = (u32) cpu_64->psw[0];
|
|
+ cpu_32->psw[1] = (u32) cpu_64->psw[1];
|
|
+ cpu_32->prefix = cpu_64->prefix;
|
|
+ cpu_32->timer = cpu_64->timer;
|
|
+ cpu_32->todcmp = cpu_64->todcmp;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Copy 64 bit lowcore to internal register set
|
|
+ */
|
|
+static void lc2cpu_64(struct dfi_cpu *cpu, struct dfi_lowcore_64 *lc)
|
|
+{
|
|
+ memcpy(&cpu->gprs, lc->gpregs_save_area, sizeof(cpu->gprs));
|
|
+ memcpy(&cpu->ctrs, lc->cregs_save_area, sizeof(cpu->ctrs));
|
|
+ memcpy(&cpu->acrs, lc->access_regs_save_area, sizeof(cpu->acrs));
|
|
+ memcpy(&cpu->fprs, lc->floating_pt_save_area, sizeof(cpu->fprs));
|
|
+ memcpy(&cpu->fpc, &lc->fpt_creg_save_area, sizeof(cpu->fpc));
|
|
+ memcpy(&cpu->psw, lc->st_status_fixed_logout, sizeof(cpu->psw));
|
|
+ memcpy(&cpu->prefix, &lc->prefixreg_save_area, sizeof(cpu->prefix));
|
|
+ memcpy(&cpu->timer, lc->timer_save_area, sizeof(cpu->timer));
|
|
+ memcpy(&cpu->todpreg, &lc->tod_progreg_save_area, sizeof(cpu->todpreg));
|
|
+ memcpy(&cpu->todcmp, lc->clock_comp_save_area, sizeof(cpu->todcmp));
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Copy 32 bit lowcore to internal 32 bit cpu
|
|
+ */
|
|
+static void lc2cpu_32(struct dfi_cpu_32 *cpu, struct dfi_lowcore_32 *lc)
|
|
+{
|
|
+ memcpy(&cpu->gprs, lc->gpregs_save_area, sizeof(cpu->gprs));
|
|
+ memcpy(&cpu->ctrs, lc->cregs_save_area, sizeof(cpu->ctrs));
|
|
+ memcpy(&cpu->acrs, lc->access_regs_save_area, sizeof(cpu->acrs));
|
|
+ memcpy(&cpu->fprs, lc->floating_pt_save_area, sizeof(cpu->fprs));
|
|
+ memcpy(&cpu->psw, lc->st_status_fixed_logout, sizeof(cpu->psw));
|
|
+ memcpy(&cpu->prefix, &lc->prefixreg_save_area, sizeof(cpu->prefix));
|
|
+ memcpy(&cpu->timer, lc->timer_save_area, sizeof(cpu->timer));
|
|
+ memcpy(&cpu->todcmp, lc->clock_comp_save_area, sizeof(cpu->todcmp));
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize and add a new CPU with given lowcore pointer
|
|
+ *
|
|
+ * Note: When this function is called, the memory chunks have to be already
|
|
+ * defined by the DFI dump specific code.
|
|
+ */
|
|
+void dfi_cpu_add_from_lc(u32 lc_addr)
|
|
+{
|
|
+ struct dfi_cpu *cpu = dfi_cpu_alloc();
|
|
+
|
|
+ switch (l.cpus.content) {
|
|
+ case DFI_CPU_CONTENT_LC:
|
|
+ cpu->prefix = lc_addr;
|
|
+ break;
|
|
+ case DFI_CPU_CONTENT_ALL:
|
|
+ if (l.arch == DFI_ARCH_32) {
|
|
+ struct dfi_cpu_32 cpu_32;
|
|
+ struct dfi_lowcore_32 lc;
|
|
+ dfi_mem_read(lc_addr, &lc, sizeof(lc));
|
|
+ lc2cpu_32(&cpu_32, &lc);
|
|
+ cpu_32_to_64(cpu, &cpu_32);
|
|
+ } else {
|
|
+ struct dfi_lowcore_64 lc;
|
|
+ dfi_mem_read(lc_addr, &lc, sizeof(lc));
|
|
+ lc2cpu_64(cpu, &lc);
|
|
+ }
|
|
+ break;
|
|
+ case DFI_CPU_CONTENT_NONE:
|
|
+ ABORT("dfi_cpu_add_from_lc() called for CONTENT_NONE");
|
|
+ }
|
|
+ dfi_cpu_add(cpu);
|
|
+}
|
|
+
|
|
diff --git a/zdump/dfi.h b/zdump/dfi.h
|
|
new file mode 100644
|
|
index 0000000..0b9d849
|
|
--- /dev/null
|
|
+++ b/zdump/dfi.h
|
|
@@ -0,0 +1,212 @@
|
|
+/*
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
+ *
|
|
+ * Generic input dump format functions (DFI - Dump Format Input)
|
|
+ *
|
|
+ * Copyright IBM Corp. 2001, 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#ifndef DFI_H
|
|
+#define DFI_H
|
|
+
|
|
+#include "zg.h"
|
|
+#include "list.h"
|
|
+
|
|
+/*
|
|
+ * CPU info functions and definitions
|
|
+ */
|
|
+
|
|
+enum dfi_arch {
|
|
+ DFI_ARCH_32 = 0,
|
|
+ DFI_ARCH_64 = 1,
|
|
+ DFI_ARCH_UNKNOWN = 2,
|
|
+};
|
|
+
|
|
+struct dfi_lowcore_32 {
|
|
+ u8 pad_0x0000[0x0084 - 0x0000]; /* 0x0000 */
|
|
+ u16 cpu_addr; /* 0x0084 */
|
|
+ u8 pad_0x0086[0x00d4 - 0x0086]; /* 0x0086 */
|
|
+ u32 extended_save_area_addr; /* 0x00d4 */
|
|
+ u32 timer_save_area[2]; /* 0x00d8 */
|
|
+ u32 clock_comp_save_area[2]; /* 0x00e0 */
|
|
+ u32 mcck_interruption_code[2]; /* 0x00e8 */
|
|
+ u8 pad_0x00f0[0x00f4-0x00f0]; /* 0x00f0 */
|
|
+ u32 external_damage_code; /* 0x00f4 */
|
|
+ u32 failing_storage_address; /* 0x00f8 */
|
|
+ u8 pad_0x00fc[0x0100-0x00fc]; /* 0x00fc */
|
|
+ u32 st_status_fixed_logout[2]; /* 0x0100 */
|
|
+ u32 prefixreg_save_area; /* 0x0108 */
|
|
+ u8 pad_0x0110[0x0120-0x010c]; /* 0x010c */
|
|
+ u32 access_regs_save_area[16]; /* 0x0120 */
|
|
+ u32 floating_pt_save_area[8]; /* 0x0160 */
|
|
+ u32 gpregs_save_area[16]; /* 0x0180 */
|
|
+ u32 cregs_save_area[16]; /* 0x01c0 */
|
|
+ u8 pad_0x0200[0x1000 - 0x0200]; /* 0x0200 */
|
|
+};
|
|
+
|
|
+struct dfi_lowcore_64 {
|
|
+ u8 pad_0x0000[0x0084 - 0x0000]; /* 0x0000 */
|
|
+ u16 cpu_addr; /* 0x0084 */
|
|
+ u8 pad_0x0086[0x1200 - 0x0086]; /* 0x0086 */
|
|
+ u64 floating_pt_save_area[16]; /* 0x1200 */
|
|
+ u64 gpregs_save_area[16]; /* 0x1280 */
|
|
+ u32 st_status_fixed_logout[4]; /* 0x1300 */
|
|
+ u8 pad_0x1310[0x1318-0x1310]; /* 0x1310 */
|
|
+ u32 prefixreg_save_area; /* 0x1318 */
|
|
+ u32 fpt_creg_save_area; /* 0x131c */
|
|
+ u8 pad_0x1320[0x1324-0x1320]; /* 0x1320 */
|
|
+ u32 tod_progreg_save_area; /* 0x1324 */
|
|
+ u32 timer_save_area[2]; /* 0x1328 */
|
|
+ u32 clock_comp_save_area[2]; /* 0x1330 */
|
|
+ u8 pad_0x1338[0x1340-0x1338]; /* 0x1338 */
|
|
+ u32 access_regs_save_area[16]; /* 0x1340 */
|
|
+ u64 cregs_save_area[16]; /* 0x1380 */
|
|
+ u8 pad_0x1400[0x2000-0x1400]; /* 0x1400 */
|
|
+} __attribute__((packed));
|
|
+
|
|
+static inline u64 dfi_lc_size(enum dfi_arch arch)
|
|
+{
|
|
+ if (arch == DFI_ARCH_64)
|
|
+ return 0x2000;
|
|
+ else
|
|
+ return 0x1000;
|
|
+}
|
|
+
|
|
+struct dfi_cpu {
|
|
+ struct list list;
|
|
+ u64 gprs[16];
|
|
+ u64 ctrs[16];
|
|
+ u32 acrs[16];
|
|
+ u64 fprs[16];
|
|
+ u32 fpc;
|
|
+ u64 psw[2];
|
|
+ u32 prefix;
|
|
+ u64 timer;
|
|
+ u64 todcmp;
|
|
+ u32 todpreg;
|
|
+};
|
|
+
|
|
+struct dfi_cpu_32 {
|
|
+ u32 gprs[16];
|
|
+ u32 ctrs[16];
|
|
+ u32 acrs[16];
|
|
+ u64 fprs[4];
|
|
+ u32 psw[2];
|
|
+ u32 prefix;
|
|
+ u64 timer;
|
|
+ u64 todcmp;
|
|
+};
|
|
+
|
|
+extern void dfi_cpu_64_to_32(struct dfi_cpu_32 *cpu_32, struct dfi_cpu *cpu_64);
|
|
+
|
|
+extern enum dfi_arch dfi_arch(void);
|
|
+extern void dfi_arch_set(enum dfi_arch arch);
|
|
+extern const char *dfi_arch_str(enum dfi_arch arch);
|
|
+
|
|
+enum dfi_cpu_content {
|
|
+ DFI_CPU_CONTENT_NONE, /* No register information available */
|
|
+ DFI_CPU_CONTENT_LC, /* Only lowcore information available */
|
|
+ DFI_CPU_CONTENT_ALL, /* Complete register information available */
|
|
+};
|
|
+
|
|
+#define dfi_cpu_iterate(cpu) \
|
|
+ list_iterate(cpu, dfi_cpu_list(), list)
|
|
+
|
|
+extern struct list *dfi_cpu_list(void);
|
|
+extern void dfi_cpu_info_init(enum dfi_cpu_content content);
|
|
+extern struct dfi_cpu *dfi_cpu_alloc(void);
|
|
+extern struct dfi_cpu *dfi_cpu(unsigned int cpu_nr);
|
|
+extern void dfi_cpu_add(struct dfi_cpu *cpu);
|
|
+extern unsigned int dfi_cpu_cnt(void);
|
|
+extern enum dfi_cpu_content dfi_cpu_content(void);
|
|
+extern void dfi_cpu_add_from_lc(u32 lc_addr);
|
|
+
|
|
+/*
|
|
+ * Mem chunk functions and definitions
|
|
+ */
|
|
+struct dfi_mem_chunk;
|
|
+
|
|
+typedef void (*dfi_mem_chunk_read_fn)(struct dfi_mem_chunk *mem_chunk,
|
|
+ u64 off, void *buf, u64 cnt);
|
|
+
|
|
+struct dfi_mem_chunk {
|
|
+ struct list list; /* List */
|
|
+ u64 start; /* Start address in memory */
|
|
+ u64 end; /* End address in memory */
|
|
+ u64 size; /* Size of chunk in dump file */
|
|
+ u64 out_start; /* Start offset in dump file */
|
|
+ u64 out_end; /* End offset in dump file */
|
|
+ dfi_mem_chunk_read_fn read_fn; /* Chunk read callback */
|
|
+ void *data; /* Data for callback */
|
|
+};
|
|
+
|
|
+extern void dfi_mem_chunk_add(u64 start, u64 size, void *data,
|
|
+ dfi_mem_chunk_read_fn read_fn);
|
|
+extern u64 dfi_mem_range(void);
|
|
+extern unsigned int dfi_mem_chunk_cnt(void);
|
|
+extern struct dfi_mem_chunk *dfi_mem_chunk_first(void);
|
|
+extern struct dfi_mem_chunk *dfi_mem_chunk_next(struct dfi_mem_chunk *chunk);
|
|
+extern struct dfi_mem_chunk *dfi_mem_chunk_prev(struct dfi_mem_chunk *chunk);
|
|
+extern struct dfi_mem_chunk *dfi_mem_chunk_find(u64 addr);
|
|
+
|
|
+extern struct list *dfi_mem_chunk_list(void);
|
|
+#define dfi_mem_chunk_iterate(mem_chunk) \
|
|
+ list_iterate(mem_chunk, dfi_mem_chunk_list(), list)
|
|
+
|
|
+/*
|
|
+ * Dump header attribute set/get functions
|
|
+ */
|
|
+extern void dfi_attr_time_set(struct timeval *time);
|
|
+extern struct timeval *dfi_attr_time(void);
|
|
+
|
|
+extern void dfi_attr_time_end_set(struct timeval *time_end);
|
|
+extern struct timeval *dfi_attr_time_end(void);
|
|
+
|
|
+extern void dfi_attr_cpu_id_set(u64 cpu_id);
|
|
+extern u64 *dfi_attr_cpu_id(void);
|
|
+
|
|
+extern void dfi_attr_mem_size_real_set(u64 mem_size_real);
|
|
+extern u64 *dfi_attr_mem_size_real();
|
|
+
|
|
+extern void dfi_attr_vol_nr_set(unsigned int vol_nr);
|
|
+extern unsigned int *dfi_attr_vol_nr(void);
|
|
+
|
|
+extern void dfi_attr_version_set(unsigned int dfi_version);
|
|
+extern unsigned int *dfi_attr_dfi_version(void);
|
|
+
|
|
+extern void dfi_attr_build_arch_set(enum dfi_arch build_arch);
|
|
+extern enum dfi_arch *dfi_attr_build_arch(void);
|
|
+
|
|
+extern void dfi_attr_real_cpu_cnt_set(u32 real_cpu_cnt);
|
|
+extern u32 *dfi_attr_real_cpu_cnt(void);
|
|
+
|
|
+/*
|
|
+ * DFI external functions
|
|
+ */
|
|
+extern void dfi_mem_read(u64 addr, void *buf, size_t cnt);
|
|
+extern void dfi_info_print(void);
|
|
+
|
|
+/*
|
|
+ * DFI feature bits
|
|
+ */
|
|
+#define DFI_FEAT_SEEK 0x1 /* Necessary for fuse mount */
|
|
+#define DFI_FEAT_COPY 0x2 /* Necessary for stdout */
|
|
+
|
|
+extern int dfi_feat_seek(void);
|
|
+extern int dfi_feat_copy(void);
|
|
+
|
|
+/*
|
|
+ * DFI operations
|
|
+ */
|
|
+struct dfi {
|
|
+ const char *name;
|
|
+ int (*init)(void);
|
|
+ void (*info_dump)(void);
|
|
+ int feat_bits;
|
|
+};
|
|
+
|
|
+extern const char *dfi_name(void);
|
|
+extern int dfi_init(void);
|
|
+
|
|
+#endif /* DFI_H */
|
|
diff --git a/zdump/dfi_elf.c b/zdump/dfi_elf.c
|
|
new file mode 100644
|
|
index 0000000..866411b
|
|
--- /dev/null
|
|
+++ b/zdump/dfi_elf.c
|
|
@@ -0,0 +1,291 @@
|
|
+/*
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
+ *
|
|
+ * ELF core dump input format
|
|
+ *
|
|
+ * Copyright IBM Corp. 2001, 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include <elf.h>
|
|
+#include <stdio.h>
|
|
+#include <string.h>
|
|
+#include <stdlib.h>
|
|
+#include <unistd.h>
|
|
+#include <assert.h>
|
|
+#include "zgetdump.h"
|
|
+
|
|
+/*
|
|
+ * Read memory for given memory chunk
|
|
+ */
|
|
+static void dfi_elf_mem_chunk_read_fn(struct dfi_mem_chunk *mem_chunk, u64 off,
|
|
+ void *buf, u64 cnt)
|
|
+{
|
|
+ u64 elf_load_off = *((u64 *) mem_chunk->data);
|
|
+
|
|
+ zg_seek(g.fh, elf_load_off + off, ZG_CHECK);
|
|
+ zg_read(g.fh, buf, cnt, ZG_CHECK);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Add load (memory chunk) to DFI dump
|
|
+ */
|
|
+static int pt_load_add(Elf64_Phdr *phdr)
|
|
+{
|
|
+ u64 *off_ptr;
|
|
+
|
|
+ if (phdr->p_paddr != phdr->p_vaddr) {
|
|
+ phdr->p_paddr = phdr->p_vaddr;
|
|
+ STDERR("Dump file \"%s\" is a user space core dump\n",
|
|
+ g.opts.device);
|
|
+ }
|
|
+ if (phdr->p_filesz == 0) /* Skip null pt loads */
|
|
+ return 0;
|
|
+ off_ptr = zg_alloc(sizeof(*off_ptr));
|
|
+ *off_ptr = phdr->p_offset;
|
|
+ dfi_mem_chunk_add(phdr->p_paddr, phdr->p_memsz, off_ptr,
|
|
+ dfi_elf_mem_chunk_read_fn);
|
|
+ if (phdr->p_offset + phdr->p_memsz > zg_size(g.fh))
|
|
+ return -EINVAL;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Skip name of note
|
|
+ */
|
|
+static void nt_name_skip(Elf64_Nhdr *note)
|
|
+{
|
|
+ zg_seek_cur(g.fh, ROUNDUP(note->n_namesz, 4), ZG_CHECK);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read note
|
|
+ */
|
|
+static int nt_read(Elf64_Nhdr *note, void *buf)
|
|
+{
|
|
+ off_t buf_len = ROUNDUP(note->n_descsz, 4);
|
|
+ char tmp_buf[buf_len];
|
|
+
|
|
+ nt_name_skip(note);
|
|
+ if (zg_read(g.fh, tmp_buf, buf_len, ZG_CHECK_ERR) != buf_len)
|
|
+ return -EINVAL;
|
|
+ if (buf)
|
|
+ memcpy(buf, tmp_buf, note->n_descsz);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Skip note
|
|
+ */
|
|
+static int nt_skip(Elf64_Nhdr *note)
|
|
+{
|
|
+ return nt_read(note, NULL);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Ensure that CPU is already defined by prstatus note
|
|
+ */
|
|
+static void check_cpu(struct dfi_cpu *cpu, const char *note_str)
|
|
+{
|
|
+ if (cpu)
|
|
+ return;
|
|
+ ERR_EXIT("Invalid ELF dump (%s before prstatus found)", note_str);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read prstatus note and return new DFI CPU
|
|
+ */
|
|
+static struct dfi_cpu *nt_prstatus_read(Elf64_Nhdr *note)
|
|
+{
|
|
+ struct dfi_cpu *cpu = dfi_cpu_alloc();
|
|
+ struct nt_prstatus_64 nt_prstatus;
|
|
+
|
|
+ if (nt_read(note, &nt_prstatus))
|
|
+ return NULL;
|
|
+
|
|
+ memcpy(cpu->gprs, &nt_prstatus.gprs, sizeof(cpu->gprs));
|
|
+ memcpy(cpu->psw, &nt_prstatus.psw, sizeof(cpu->psw));
|
|
+ memcpy(cpu->acrs, &nt_prstatus.acrs, sizeof(cpu->acrs));
|
|
+
|
|
+ dfi_cpu_add(cpu);
|
|
+ return cpu;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read fpregset note
|
|
+ */
|
|
+static int nt_fpregset_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
|
+{
|
|
+ struct nt_fpregset_64 nt_fpregset;
|
|
+
|
|
+ check_cpu(cpu, "FPREGSET");
|
|
+ if (nt_read(note, &nt_fpregset))
|
|
+ return -EINVAL;
|
|
+
|
|
+ memcpy(&cpu->fpc, &nt_fpregset.fpc, sizeof(cpu->fpc));
|
|
+ memcpy(cpu->fprs, &nt_fpregset.fprs, sizeof(cpu->fprs));
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read s390 timer note
|
|
+ */
|
|
+static int nt_s390_timer_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
|
+{
|
|
+ check_cpu(cpu, "S390_TIMER");
|
|
+ return nt_read(note, &cpu->timer);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read s390 todcmp note
|
|
+ */
|
|
+static int nt_s390_todcmp_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
|
+{
|
|
+ check_cpu(cpu, "S390_TODCMP");
|
|
+ return nt_read(note, &cpu->todcmp);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read s390 todpreg note
|
|
+ */
|
|
+static int nt_s390_todpreg_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
|
+{
|
|
+ check_cpu(cpu, "S390_TODPREG");
|
|
+ return nt_read(note, &cpu->todpreg);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read s390 ctrs note
|
|
+ */
|
|
+static int nt_s390_ctrs_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
|
+{
|
|
+ check_cpu(cpu, "S390_CTRS");
|
|
+ return nt_read(note, &cpu->ctrs);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read s390 prefix note
|
|
+ */
|
|
+static int nt_s390_prefix_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
|
+{
|
|
+ check_cpu(cpu, "S390_PREFIX");
|
|
+ return nt_read(note, &cpu->prefix);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Add all notes for notes phdr
|
|
+ */
|
|
+static int pt_notes_add(Elf64_Phdr *phdr)
|
|
+{
|
|
+ u64 start_off = zg_tell(g.fh, ZG_CHECK);
|
|
+ struct dfi_cpu *cpu_current = NULL;
|
|
+ u64 notes_start_off;
|
|
+ Elf64_Nhdr note;
|
|
+ int rc;
|
|
+
|
|
+ zg_seek(g.fh, phdr->p_offset, ZG_CHECK);
|
|
+ notes_start_off = zg_tell(g.fh, ZG_CHECK);
|
|
+ while (zg_tell(g.fh, ZG_CHECK) - notes_start_off < phdr->p_filesz) {
|
|
+ rc = zg_read(g.fh, ¬e, sizeof(note), ZG_CHECK_ERR);
|
|
+ if (rc != sizeof(note))
|
|
+ return -EINVAL;
|
|
+ switch (note.n_type) {
|
|
+ case NT_PRSTATUS:
|
|
+ cpu_current = nt_prstatus_read(¬e);
|
|
+ if (!cpu_current)
|
|
+ return -EINVAL;
|
|
+ break;
|
|
+ case NT_FPREGSET:
|
|
+ if (nt_fpregset_read(cpu_current, ¬e))
|
|
+ return -EINVAL;
|
|
+ break;
|
|
+ case NT_S390_TIMER:
|
|
+ if (nt_s390_timer_read(cpu_current, ¬e))
|
|
+ return -EINVAL;
|
|
+ break;
|
|
+ case NT_S390_TODCMP:
|
|
+ if (nt_s390_todcmp_read(cpu_current, ¬e))
|
|
+ return -EINVAL;
|
|
+ break;
|
|
+ case NT_S390_TODPREG:
|
|
+ if (nt_s390_todpreg_read(cpu_current, ¬e))
|
|
+ return -EINVAL;
|
|
+ break;
|
|
+ case NT_S390_CTRS:
|
|
+ if (nt_s390_ctrs_read(cpu_current, ¬e))
|
|
+ return -EINVAL;
|
|
+ break;
|
|
+ case NT_S390_PREFIX:
|
|
+ if (nt_s390_prefix_read(cpu_current, ¬e))
|
|
+ return -EINVAL;
|
|
+ break;
|
|
+ default:
|
|
+ if (nt_skip(¬e))
|
|
+ return -EINVAL;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ zg_seek(g.fh, start_off, ZG_CHECK);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read ELF header
|
|
+ */
|
|
+static int read_elf_hdr(Elf64_Ehdr *ehdr)
|
|
+{
|
|
+ if (zg_size(g.fh) < sizeof(*ehdr))
|
|
+ return -ENODEV;
|
|
+ zg_read(g.fh, ehdr, sizeof(*ehdr), ZG_CHECK);
|
|
+ if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0)
|
|
+ return -ENODEV;
|
|
+ if (ehdr->e_type != ET_CORE)
|
|
+ return -ENODEV;
|
|
+ if (ehdr->e_machine != EM_S390 || ehdr->e_ident[EI_CLASS] != ELFCLASS64)
|
|
+ ERR_EXIT("Only s390x (64 bit) core dump files are supported");
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize ELF input dump format
|
|
+ */
|
|
+static int dfi_elf_init(void)
|
|
+{
|
|
+ Elf64_Ehdr ehdr;
|
|
+ Elf64_Phdr phdr;
|
|
+ int i;
|
|
+
|
|
+ if (read_elf_hdr(&ehdr) != 0)
|
|
+ return -ENODEV;
|
|
+
|
|
+ df_elf_ensure_s390x();
|
|
+ dfi_arch_set(DFI_ARCH_64);
|
|
+ dfi_cpu_info_init(DFI_CPU_CONTENT_ALL);
|
|
+
|
|
+ for (i = 0; i < ehdr.e_phnum; i++) {
|
|
+ zg_read(g.fh, &phdr, sizeof(phdr), ZG_CHECK);
|
|
+ switch (phdr.p_type) {
|
|
+ case PT_LOAD:
|
|
+ if (pt_load_add(&phdr))
|
|
+ return -EINVAL;
|
|
+ break;
|
|
+ case PT_NOTE:
|
|
+ if (pt_notes_add(&phdr))
|
|
+ return -EINVAL;
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ dfi_attr_version_set(ehdr.e_ident[EI_VERSION]);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * ELF DFI operations
|
|
+ */
|
|
+struct dfi dfi_elf = {
|
|
+ .name = "elf",
|
|
+ .init = dfi_elf_init,
|
|
+ .feat_bits = DFI_FEAT_COPY | DFI_FEAT_SEEK,
|
|
+};
|
|
diff --git a/zdump/dfi_kdump.c b/zdump/dfi_kdump.c
|
|
new file mode 100644
|
|
index 0000000..537ea55
|
|
--- /dev/null
|
|
+++ b/zdump/dfi_kdump.c
|
|
@@ -0,0 +1,122 @@
|
|
+/*
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
+ *
|
|
+ * kdump input format
|
|
+ *
|
|
+ * Copyright IBM Corp. 2001, 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include "zgetdump.h"
|
|
+
|
|
+struct l_new_utsname {
|
|
+ char sysname[65];
|
|
+ char nodename[65];
|
|
+ char release[65];
|
|
+ char version[65];
|
|
+ char machine[65];
|
|
+ char domainname[65];
|
|
+};
|
|
+
|
|
+struct df_kdump_hdr {
|
|
+ char signature[8];
|
|
+ int header_version;
|
|
+ struct l_new_utsname utsname;
|
|
+ struct timeval timestamp;
|
|
+ unsigned int status;
|
|
+ int block_size;
|
|
+ int sub_hdr_size;
|
|
+ unsigned int bitmap_blocks;
|
|
+ unsigned int max_mapnr;
|
|
+ unsigned int total_ram_blocks;
|
|
+ unsigned int device_blocks;
|
|
+ unsigned int written_blocks;
|
|
+ unsigned int current_cpu;
|
|
+ int nr_cpus;
|
|
+ void *tasks[0];
|
|
+};
|
|
+
|
|
+struct df_kdump_sub_hdr {
|
|
+ unsigned long phys_base;
|
|
+ int dump_level;
|
|
+ int split;
|
|
+ unsigned long start_pfn;
|
|
+ unsigned long end_pfn;
|
|
+ off_t offset_vmcoreinfo;
|
|
+ unsigned long size_vmcoreinfo;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * File local static data
|
|
+ */
|
|
+static struct {
|
|
+ struct df_kdump_hdr hdr; /* kdump (diskdump) dump header */
|
|
+ struct df_kdump_sub_hdr shdr; /* kdump subheader */
|
|
+} l;
|
|
+
|
|
+#ifdef DEBUG
|
|
+static void print_header(void)
|
|
+{
|
|
+ STDERR("diskdump main header\n");
|
|
+ STDERR(" signature : %s\n", l.hdr.signature);
|
|
+ STDERR(" header_version : %d\n", l.hdr.header_version);
|
|
+ STDERR(" status : %d\n", l.hdr.status);
|
|
+ STDERR(" block_size : %d\n", l.hdr.block_size);
|
|
+ STDERR(" sub_hdr_size : %d\n", l.hdr.sub_hdr_size);
|
|
+ STDERR(" bitmap_blocks : %d\n", l.hdr.bitmap_blocks);
|
|
+ STDERR(" max_mapnr : 0x%x\n", l.hdr.max_mapnr);
|
|
+ STDERR(" total_ram_blocks : %d\n", l.hdr.total_ram_blocks);
|
|
+ STDERR(" device_blocks : %d\n", l.hdr.device_blocks);
|
|
+ STDERR(" written_blocks : %d\n", l.hdr.written_blocks);
|
|
+ STDERR(" current_cpu : %d\n", l.hdr.current_cpu);
|
|
+ STDERR(" nr_cpus : %d\n", l.hdr.nr_cpus);
|
|
+ STDERR("kdump sub header\n");
|
|
+ STDERR(" phys_base : 0x%lx\n", l.shdr.phys_base);
|
|
+ STDERR(" dump_level : %d\n", l.shdr.dump_level);
|
|
+ STDERR(" split : %d\n", l.shdr.split);
|
|
+ STDERR(" start_pfn : 0x%lx\n", l.shdr.start_pfn);
|
|
+ STDERR(" end_pfn : 0x%lx\n", l.shdr.end_pfn);
|
|
+}
|
|
+#endif
|
|
+
|
|
+/*
|
|
+ * Read kdump dump header
|
|
+ */
|
|
+static int read_kdump_hdr(void)
|
|
+{
|
|
+ if ((zg_type(g.fh) == ZG_TYPE_FILE) && (zg_size(g.fh) < sizeof(l.hdr)))
|
|
+ return -ENODEV;
|
|
+ zg_read(g.fh, &l.hdr, sizeof(l.hdr), ZG_CHECK);
|
|
+ if (memcmp(l.hdr.signature, "KDUMP", 5) != 0)
|
|
+ return -ENODEV;
|
|
+ zg_seek(g.fh, l.hdr.block_size, ZG_CHECK);
|
|
+ zg_read(g.fh, &l.shdr, sizeof(l.shdr), ZG_CHECK);
|
|
+ dfi_attr_version_set(l.hdr.header_version);
|
|
+ dfi_attr_real_cpu_cnt_set(l.hdr.nr_cpus);
|
|
+ dfi_arch_set(DFI_ARCH_64);
|
|
+#ifdef DEBUG
|
|
+ print_header();
|
|
+#endif
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize kdump DFI
|
|
+ */
|
|
+static int dfi_kdump_init(void)
|
|
+{
|
|
+ if (read_kdump_hdr() != 0)
|
|
+ return -ENODEV;
|
|
+ dfi_mem_chunk_add(0, l.hdr.max_mapnr * PAGE_SIZE, NULL, NULL);
|
|
+ return 0;
|
|
+
|
|
+}
|
|
+
|
|
+/*
|
|
+ * S390 DFI operations
|
|
+ */
|
|
+struct dfi dfi_kdump = {
|
|
+ .name = "kdump",
|
|
+ .init = dfi_kdump_init,
|
|
+ .feat_bits = 0,
|
|
+};
|
|
diff --git a/zdump/dfi_lkcd.c b/zdump/dfi_lkcd.c
|
|
new file mode 100644
|
|
index 0000000..0c5e8a9
|
|
--- /dev/null
|
|
+++ b/zdump/dfi_lkcd.c
|
|
@@ -0,0 +1,333 @@
|
|
+/*
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
+ *
|
|
+ * LKCD dump input format
|
|
+ *
|
|
+ * Copyright IBM Corp. 2001, 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include <zlib.h>
|
|
+#include "zgetdump.h"
|
|
+
|
|
+#define MEM_HOLE_SIZE_MIN (1024 * 1024)
|
|
+#define IDX_KIB 64 /* One index entry per IDX_KIB */
|
|
+#define IDX_TO_ADDR(idx) (idx * IDX_KIB * 1024)
|
|
+#define ADDR_TO_IDX(addr) (addr / (1024 * IDX_KIB))
|
|
+
|
|
+/*
|
|
+ * File local static data
|
|
+ */
|
|
+static struct {
|
|
+ u64 *pg_hdr_idx;
|
|
+ u64 page_last;
|
|
+ struct df_lkcd_hdr hdr;
|
|
+ struct df_lkcd_hdr_asm hdr_asm;
|
|
+ int dump_full;
|
|
+} l;
|
|
+
|
|
+/*
|
|
+ * Read LKCD page buffer, either compressed or uncompressed
|
|
+ */
|
|
+static void read_page_buf(struct df_lkcd_pg_hdr *pg_hdr, void *buf)
|
|
+{
|
|
+ unsigned long size = PAGE_SIZE;
|
|
+ unsigned char cbuf[PAGE_SIZE];
|
|
+
|
|
+ switch (pg_hdr->flags) {
|
|
+ case DF_LKCD_DH_RAW:
|
|
+ zg_read(g.fh, buf, pg_hdr->size, ZG_CHECK);
|
|
+ break;
|
|
+ case DF_LKCD_DH_COMPRESSED:
|
|
+ zg_read(g.fh, cbuf, pg_hdr->size, ZG_CHECK);
|
|
+ uncompress(buf, &size, cbuf, pg_hdr->size);
|
|
+ if (size != PAGE_SIZE)
|
|
+ ABORT("Invalid page size: %ld", size);
|
|
+ break;
|
|
+ default:
|
|
+ ERR_EXIT("Unsupported page flags: %x at addr %Lx",
|
|
+ pg_hdr->flags, pg_hdr->addr);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read next LKCD page from current file position
|
|
+ *
|
|
+ * If we find the page, we copy the page content. If the page is not present
|
|
+ * we copy zeroes and skip it. If the page address is not yet reached, we just
|
|
+ * skip it.
|
|
+ */
|
|
+static int read_next_page(u64 addr, void *buf)
|
|
+{
|
|
+ struct df_lkcd_pg_hdr pg_hdr;
|
|
+
|
|
+ zg_read(g.fh, &pg_hdr, sizeof(pg_hdr), ZG_CHECK);
|
|
+ l.page_last = pg_hdr.addr / PAGE_SIZE;
|
|
+ if (pg_hdr.addr == addr) {
|
|
+ read_page_buf(&pg_hdr, buf);
|
|
+ return 0;
|
|
+ }
|
|
+ if (pg_hdr.addr > addr) {
|
|
+ memset(buf, 0, PAGE_SIZE);
|
|
+ zg_seek_cur(g.fh, pg_hdr.size, ZG_CHECK);
|
|
+ return 0;
|
|
+ }
|
|
+ zg_seek_cur(g.fh, pg_hdr.size, ZG_CHECK);
|
|
+ return -ENODEV;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read LKCD dump page for flex dump
|
|
+ *
|
|
+ * If the page after the last read page should be read, we just read
|
|
+ * the next one. Otherwise we seek to the beginning of the page cluster
|
|
+ * of the page index and search the page there.
|
|
+ */
|
|
+static void read_page_flex(u64 pg_num, void *buf)
|
|
+{
|
|
+ u64 addr = pg_num * PAGE_SIZE;
|
|
+
|
|
+ if (l.pg_hdr_idx[ADDR_TO_IDX(addr)] == 0)
|
|
+ ABORT("Dump page index broken");
|
|
+
|
|
+ if (l.page_last == pg_num - 1) {
|
|
+ read_next_page(addr, buf);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ zg_seek(g.fh, l.pg_hdr_idx[ADDR_TO_IDX(addr)], ZG_CHECK);
|
|
+ do {
|
|
+ if (read_next_page(addr, buf) == 0)
|
|
+ break;
|
|
+ } while (1);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read lkcd page for full dump
|
|
+ */
|
|
+static void read_page_full(u64 pg_num, void *buf)
|
|
+{
|
|
+ zg_seek(g.fh, DF_LKCD_HDR_SIZE + pg_num * DF_LKCD_UCP_SIZE +
|
|
+ sizeof(struct df_lkcd_pg_hdr), ZG_CHECK);
|
|
+ zg_read(g.fh, buf, PAGE_SIZE, ZG_CHECK);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read lkcd page
|
|
+ */
|
|
+static void read_page(u64 pg_num, void *buf)
|
|
+{
|
|
+ if (l.dump_full)
|
|
+ read_page_full(pg_num, buf);
|
|
+ else
|
|
+ read_page_flex(pg_num, buf);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * LKCD mem chunk read callback
|
|
+ */
|
|
+static void dfi_lkcd_mem_chunk_read_fn(struct dfi_mem_chunk *mem_chunk, u64 off,
|
|
+ void *buf, u64 cnt)
|
|
+{
|
|
+ u64 copied = 0, size, pg_nr, addr = off + mem_chunk->start;
|
|
+ char pg_buf[PAGE_SIZE];
|
|
+ unsigned int pg_off;
|
|
+
|
|
+ while (copied != cnt) {
|
|
+ pg_nr = (addr + copied) / PAGE_SIZE;
|
|
+ pg_off = (addr + copied) % PAGE_SIZE;
|
|
+ size = MIN(cnt - copied, PAGE_SIZE - pg_off);
|
|
+ read_page(pg_nr, pg_buf);
|
|
+ memcpy(buf + copied, &pg_buf[pg_off], size);
|
|
+ copied += size;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Did we find the end of the LCKD dump?
|
|
+ */
|
|
+static int dump_end(u64 addr, struct df_lkcd_pg_hdr *pg_hdr)
|
|
+{
|
|
+ if (addr == pg_hdr->addr) {
|
|
+ /*
|
|
+ * This is a workaroud for a bug in vmconvert,
|
|
+ * where instaed of the end marker the last
|
|
+ * page was written twice. Sorry for that...
|
|
+ */
|
|
+ return 1;
|
|
+ }
|
|
+ if (pg_hdr->addr == 0 && pg_hdr->size == 4 && pg_hdr->flags == 0) {
|
|
+ /*
|
|
+ * zfcpdump bug (wrong end marker)
|
|
+ */
|
|
+ return 1;
|
|
+ }
|
|
+ if (pg_hdr->flags == DF_LKCD_DH_END)
|
|
+ return 1;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Init memory chunks for full dump
|
|
+ *
|
|
+ * Full dump: It is not compressed and it does not have any memory holes.
|
|
+ */
|
|
+static int mem_init_full(void)
|
|
+{
|
|
+ dfi_mem_chunk_add(0, l.hdr.mem_end, NULL, dfi_lkcd_mem_chunk_read_fn);
|
|
+ l.dump_full = 1;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Init memory chunks for flex dump
|
|
+ *
|
|
+ * Flex dump: It is compressed and/or it has memory holes.
|
|
+ */
|
|
+static int mem_init_flex(void)
|
|
+{
|
|
+ u64 addr = U64_MAX, idx = 0, mem_chunk_start = 0, rc;
|
|
+ struct df_lkcd_pg_hdr pg_hdr;
|
|
+ int dump_incomplete = 0;
|
|
+
|
|
+ l.pg_hdr_idx = zg_alloc(sizeof(u64) * (ADDR_TO_IDX(l.hdr.mem_end) + 1));
|
|
+ zg_seek(g.fh, DF_LKCD_HDR_SIZE, ZG_CHECK_NONE);
|
|
+ zg_progress_init("Analyzing dump", l.hdr.mem_end);
|
|
+ do {
|
|
+ rc = zg_read(g.fh, &pg_hdr, sizeof(pg_hdr), ZG_CHECK_ERR);
|
|
+ if (rc != sizeof(pg_hdr)) {
|
|
+ dump_incomplete = 1;
|
|
+ break;
|
|
+ }
|
|
+ if (dump_end(addr, &pg_hdr))
|
|
+ break;
|
|
+ if (pg_hdr.addr - addr > MEM_HOLE_SIZE_MIN) {
|
|
+ dfi_mem_chunk_add(mem_chunk_start,
|
|
+ addr + PAGE_SIZE - mem_chunk_start,
|
|
+ NULL,
|
|
+ dfi_lkcd_mem_chunk_read_fn);
|
|
+ mem_chunk_start = pg_hdr.addr;
|
|
+ }
|
|
+ addr = pg_hdr.addr;
|
|
+ zg_progress(addr);
|
|
+ if (addr >= IDX_TO_ADDR(idx)) {
|
|
+ idx = ADDR_TO_IDX(addr);
|
|
+ l.pg_hdr_idx[idx] = zg_tell(g.fh, ZG_CHECK) -
|
|
+ sizeof(pg_hdr);
|
|
+ idx++;
|
|
+ }
|
|
+ zg_seek_cur(g.fh, pg_hdr.size, ZG_CHECK);
|
|
+ } while (1);
|
|
+
|
|
+ if (addr != mem_chunk_start) {
|
|
+ dfi_mem_chunk_add(mem_chunk_start,
|
|
+ l.hdr.mem_end - mem_chunk_start,
|
|
+ NULL,
|
|
+ dfi_lkcd_mem_chunk_read_fn);
|
|
+ }
|
|
+ zg_progress(l.hdr.mem_end);
|
|
+ if (g.opts.action != ZG_ACTION_MOUNT)
|
|
+ fprintf(stderr, "\n");
|
|
+ if (dump_incomplete)
|
|
+ return -EINVAL;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Do we have a full dump?
|
|
+ */
|
|
+static int is_full_dump()
|
|
+{
|
|
+ u64 full_size;
|
|
+ int pages;
|
|
+
|
|
+ if (l.hdr.dump_compress != DF_LKCD_COMPRESS_NONE)
|
|
+ return 0;
|
|
+ pages = l.hdr.mem_end / PAGE_SIZE;
|
|
+ full_size = DF_LKCD_HDR_SIZE + pages * DF_LKCD_UCP_SIZE +
|
|
+ sizeof(struct df_lkcd_pg_hdr);
|
|
+ if (zg_size(g.fh) != full_size)
|
|
+ return 0;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Init memory chunks
|
|
+ */
|
|
+static int mem_init(void)
|
|
+{
|
|
+ if (is_full_dump())
|
|
+ return mem_init_full();
|
|
+ else
|
|
+ return mem_init_flex();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize CPU information
|
|
+ */
|
|
+static void cpu_init(void)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ if (l.hdr_asm.magic != DF_LKCD_MAGIC_ASM) {
|
|
+ /* Old LKCD dump without asm header */
|
|
+ dfi_cpu_info_init(DFI_CPU_CONTENT_NONE);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ dfi_cpu_info_init(DFI_CPU_CONTENT_ALL);
|
|
+ for (i = 0; i < l.hdr_asm.cpu_cnt; i++)
|
|
+ dfi_cpu_add_from_lc(l.hdr_asm.lc_vec[i]);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read LKCD dump header and dump asm header
|
|
+ */
|
|
+static int read_lkcd_hdr(void)
|
|
+{
|
|
+ if (zg_size(g.fh) < DF_LKCD_HDR_SIZE)
|
|
+ return -ENODEV;
|
|
+
|
|
+ /* Read dump header */
|
|
+ zg_read(g.fh, &l.hdr, sizeof(l.hdr), ZG_CHECK);
|
|
+
|
|
+ if (l.hdr.magic != DF_LKCD_MAGIC)
|
|
+ return -ENODEV;
|
|
+
|
|
+ /* Read asm header */
|
|
+ zg_seek(g.fh, l.hdr.hdr_size, ZG_CHECK);
|
|
+ zg_read(g.fh, &l.hdr_asm, sizeof(l.hdr_asm), ZG_CHECK);
|
|
+ if (strncmp(l.hdr.utsname_machine, "s390x", sizeof("s390x")) == 0)
|
|
+ dfi_arch_set(DFI_ARCH_64);
|
|
+ else if (strncmp(l.hdr.utsname_machine, "s390", sizeof("s390")) == 0)
|
|
+ dfi_arch_set(DFI_ARCH_32);
|
|
+ else
|
|
+ ERR_EXIT("Dump architecture \"%s\" is not supported",
|
|
+ l.hdr.utsname_machine);
|
|
+ if (l.hdr_asm.magic == DF_LKCD_MAGIC_ASM)
|
|
+ dfi_attr_real_cpu_cnt_set(l.hdr_asm.real_cpu_cnt);
|
|
+ dfi_attr_version_set(l.hdr.version);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize LKCD DFI
|
|
+ */
|
|
+static int dfi_lkcd_init(void)
|
|
+{
|
|
+ if (read_lkcd_hdr() != 0)
|
|
+ return -ENODEV;
|
|
+ if (mem_init() != 0)
|
|
+ return -EINVAL;
|
|
+ cpu_init();
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * LKCD DFI operations
|
|
+ */
|
|
+struct dfi dfi_lkcd = {
|
|
+ .name = "lkcd",
|
|
+ .init = dfi_lkcd_init,
|
|
+ .feat_bits = DFI_FEAT_COPY | DFI_FEAT_SEEK,
|
|
+};
|
|
diff --git a/zdump/dfi_s390.c b/zdump/dfi_s390.c
|
|
new file mode 100644
|
|
index 0000000..8a69849
|
|
--- /dev/null
|
|
+++ b/zdump/dfi_s390.c
|
|
@@ -0,0 +1,95 @@
|
|
+/*
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
+ *
|
|
+ * S390 dump input format
|
|
+ *
|
|
+ * Copyright IBM Corp. 2001, 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include <string.h>
|
|
+#include <time.h>
|
|
+#include <fcntl.h>
|
|
+#include <stdio.h>
|
|
+#include <unistd.h>
|
|
+#include <sys/types.h>
|
|
+#include <sys/stat.h>
|
|
+#include <linux/fs.h>
|
|
+#include "zgetdump.h"
|
|
+
|
|
+/*
|
|
+ * File local static data
|
|
+ */
|
|
+static struct {
|
|
+ struct df_s390_hdr hdr; /* s390 dump header */
|
|
+ struct df_s390_em em; /* s390 end marker */
|
|
+} l;
|
|
+
|
|
+/*
|
|
+ * S390 mem chunk read callback
|
|
+ */
|
|
+static void dfi_s390_mem_chunk_read(struct dfi_mem_chunk *mem_chunk, u64 off,
|
|
+ void *buf, u64 cnt)
|
|
+{
|
|
+ (void) mem_chunk;
|
|
+
|
|
+ zg_seek(g.fh, off + DF_S390_HDR_SIZE, ZG_CHECK);
|
|
+ zg_read(g.fh, buf, cnt, ZG_CHECK);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read s390 dump header
|
|
+ */
|
|
+static int read_s390_hdr(void)
|
|
+{
|
|
+ if ((zg_type(g.fh) == ZG_TYPE_FILE) && (zg_size(g.fh) < sizeof(l.hdr)))
|
|
+ return -ENODEV;
|
|
+ zg_read(g.fh, &l.hdr, sizeof(l.hdr), ZG_CHECK);
|
|
+ if (l.hdr.magic != DF_S390_MAGIC)
|
|
+ return -ENODEV;
|
|
+ df_s390_hdr_add(&l.hdr);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Init end marker
|
|
+ */
|
|
+static int read_s390_em(void)
|
|
+{
|
|
+ u64 rc;
|
|
+
|
|
+ rc = zg_seek(g.fh, l.hdr.mem_size + DF_S390_HDR_SIZE, ZG_CHECK_NONE);
|
|
+ if (rc != l.hdr.mem_size + DF_S390_HDR_SIZE)
|
|
+ return -EINVAL;
|
|
+ rc = zg_read(g.fh, &l.em, sizeof(l.em), ZG_CHECK_ERR);
|
|
+ if (rc != sizeof(l.em))
|
|
+ return -EINVAL;
|
|
+ if (df_s390_em_verify(&l.em, &l.hdr) != 0)
|
|
+ return -EINVAL;
|
|
+ df_s390_em_add(&l.em);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize s390 DFI
|
|
+ */
|
|
+static int dfi_s390_init(void)
|
|
+{
|
|
+ if (read_s390_hdr() != 0)
|
|
+ return -ENODEV;
|
|
+ dfi_mem_chunk_add(0, l.hdr.mem_size, NULL, dfi_s390_mem_chunk_read);
|
|
+ if (read_s390_em() != 0)
|
|
+ return -EINVAL;
|
|
+ df_s390_cpu_info_add(&l.hdr, l.hdr.mem_size);
|
|
+ zg_seek(g.fh, sizeof(l.hdr), ZG_CHECK);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * S390 DFI operations
|
|
+ */
|
|
+struct dfi dfi_s390 = {
|
|
+ .name = "s390",
|
|
+ .init = dfi_s390_init,
|
|
+ .feat_bits = DFI_FEAT_COPY | DFI_FEAT_SEEK,
|
|
+};
|
|
diff --git a/zdump/dfi_s390mv.c b/zdump/dfi_s390mv.c
|
|
new file mode 100644
|
|
index 0000000..f79188d
|
|
--- /dev/null
|
|
+++ b/zdump/dfi_s390mv.c
|
|
@@ -0,0 +1,547 @@
|
|
+/*
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
+ *
|
|
+ * S390 multi-volume dump input format
|
|
+ *
|
|
+ * Copyright IBM Corp. 2001, 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include <string.h>
|
|
+#include <time.h>
|
|
+#include <fcntl.h>
|
|
+#include <stdio.h>
|
|
+#include <unistd.h>
|
|
+#include <dirent.h>
|
|
+#include <sys/ioctl.h>
|
|
+#include <sys/types.h>
|
|
+#include <sys/stat.h>
|
|
+#include <linux/fs.h>
|
|
+#include "zgetdump.h"
|
|
+
|
|
+#define SYSFS_BUSDIR "/sys/bus/ccw/devices"
|
|
+#define MAX_VOLUMES 32
|
|
+
|
|
+/*
|
|
+ * Parameter for DASD multi-volume dump
|
|
+ */
|
|
+struct vol_parm {
|
|
+ u16 devno;
|
|
+ u32 start_blk;
|
|
+ u32 end_blk;
|
|
+ u8 blk_size;
|
|
+ u8 end_sec;
|
|
+ u8 num_heads;
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+struct vol_parm_table {
|
|
+ u64 timestamp;
|
|
+ u16 vol_cnt;
|
|
+ struct vol_parm vol_parm[MAX_VOLUMES];
|
|
+} __attribute__ ((packed));
|
|
+
|
|
+/*
|
|
+ * Device signature
|
|
+ */
|
|
+enum dev_sign {
|
|
+ SIGN_INVALID = 0, /* No dumper installed */
|
|
+ SIGN_VALID = 1, /* dumper installed, but volume not used */
|
|
+ SIGN_ACTIVE = 2, /* dumper installed and volume userd */
|
|
+};
|
|
+
|
|
+static char *dev_sign_str[] = {"invalid", "valid", "active"};
|
|
+#define dev_sign_str(x) (dev_sign_str[x])
|
|
+
|
|
+/*
|
|
+ * Device status
|
|
+ */
|
|
+enum dev_status {
|
|
+ DEV_ONLINE = 0,
|
|
+ DEV_OFFLINE = 1,
|
|
+ DEV_UNDEFINED = 2,
|
|
+};
|
|
+
|
|
+static char *dev_status_str[] = {"online", "offline", "undefined"};
|
|
+#define dev_status_str(x) (dev_status_str[x])
|
|
+
|
|
+/*
|
|
+ * Volume information
|
|
+ */
|
|
+struct vol {
|
|
+ dev_t dev;
|
|
+ struct zg_fh *fh;
|
|
+ char *devnode;
|
|
+ enum dev_status status;
|
|
+ enum dev_sign sign;
|
|
+ off_t part_off;
|
|
+ u64 part_size;
|
|
+ u64 mem_start;
|
|
+ u64 mem_end;
|
|
+ char bus_id[9];
|
|
+ u32 nr;
|
|
+ u16 blk_size;
|
|
+ struct df_s390_dumper dumper;
|
|
+ struct df_s390_hdr hdr;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * File local static data
|
|
+ */
|
|
+static struct {
|
|
+ struct df_s390_hdr hdr;
|
|
+ struct df_s390_em em;
|
|
+ struct vol vol_vec[MAX_VOLUMES];
|
|
+ struct vol_parm_table table;
|
|
+ int blk_size;
|
|
+ struct df_s390_dumper dumper;
|
|
+ int dump_incomplete;
|
|
+} l;
|
|
+
|
|
+/*
|
|
+ * Read volume parameter table
|
|
+ */
|
|
+static void table_read(struct zg_fh *fh, u16 blk_size,
|
|
+ struct vol_parm_table *table)
|
|
+{
|
|
+ int off;
|
|
+
|
|
+ off = DF_S390_MAGIC_BLK_ECKD * blk_size + df_s390_dumper_size(l.dumper);
|
|
+ zg_seek(fh, off, ZG_CHECK);
|
|
+ zg_read(fh, table, sizeof(*table), ZG_CHECK);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize dump end marker
|
|
+ */
|
|
+static void em_init(struct vol *vol)
|
|
+{
|
|
+ off_t em_off;
|
|
+
|
|
+ em_off = vol->part_off + (vol->mem_end + 1 - vol->mem_start) +
|
|
+ DF_S390_HDR_SIZE;
|
|
+ zg_seek(vol->fh, em_off, ZG_CHECK);
|
|
+ zg_read(vol->fh, &l.em, sizeof(l.em), ZG_CHECK);
|
|
+ if (df_s390_em_verify(&l.em, &l.hdr) != 0)
|
|
+ l.dump_incomplete = 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Check sysfs, whether a device specified by its bus ID is defined and online.
|
|
+ * Find out the corresponding dev_t
|
|
+ */
|
|
+static enum dev_status dev_from_busid(char *bus_id, dev_t *dev)
|
|
+{
|
|
+ char tmp_file[PATH_MAX], dev_file[PATH_MAX];
|
|
+ struct dirent *direntp;
|
|
+ int fh, minor, major;
|
|
+ char buf[10];
|
|
+ DIR *fh_dir;
|
|
+
|
|
+ snprintf(dev_file, PATH_MAX, "%s/%s", SYSFS_BUSDIR, bus_id);
|
|
+ fh_dir = opendir(dev_file);
|
|
+ if (!fh_dir)
|
|
+ return DEV_UNDEFINED;
|
|
+
|
|
+ snprintf(tmp_file, PATH_MAX, "%s/online", dev_file);
|
|
+ fh = open(tmp_file, O_RDONLY);
|
|
+ if (read(fh, buf, 1) == -1)
|
|
+ ERR_EXIT_ERRNO("Could not read online attribute");
|
|
+ close(fh);
|
|
+
|
|
+ if (buf[0] != '1')
|
|
+ return DEV_OFFLINE;
|
|
+
|
|
+ while ((direntp = readdir(fh_dir)))
|
|
+ if (strncmp(direntp->d_name, "block:", 6) == 0)
|
|
+ break;
|
|
+ closedir(fh_dir);
|
|
+
|
|
+ if (direntp == NULL) {
|
|
+ snprintf(dev_file, PATH_MAX, "%s/%s/block", SYSFS_BUSDIR,
|
|
+ bus_id);
|
|
+ fh_dir = opendir(dev_file);
|
|
+ if (!fh_dir)
|
|
+ ERR_EXIT_ERRNO("Could not open \"%s\"", dev_file);
|
|
+ while ((direntp = readdir(fh_dir)))
|
|
+ if (strncmp(direntp->d_name, "dasd", 4) == 0)
|
|
+ break;
|
|
+ closedir(fh_dir);
|
|
+ if (direntp == NULL)
|
|
+ ERR_EXIT("Problem with contents of \"%s\"", dev_file);
|
|
+ }
|
|
+ snprintf(tmp_file, PATH_MAX, "%s/%s/dev", dev_file, direntp->d_name);
|
|
+ fh = open(tmp_file, O_RDONLY);
|
|
+ if (read(fh, buf, sizeof(buf)) == -1)
|
|
+ ERR_EXIT_ERRNO("Could not read dev file");
|
|
+ close(fh);
|
|
+ if (sscanf(buf, "%i:%i", &major, &minor) != 2)
|
|
+ ERR_EXIT("Malformed content of \"%s\": %s", tmp_file, buf);
|
|
+ *dev = makedev(major, minor);
|
|
+ return DEV_ONLINE;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Check whether dump table on user specified dump device is
|
|
+ * identical to the one found on this device.
|
|
+ */
|
|
+static void check_vol_table(struct vol *vol)
|
|
+{
|
|
+ struct vol_parm_table vol_table;
|
|
+
|
|
+ table_read(vol->fh, vol->blk_size, &vol_table);
|
|
+ if (memcmp(&vol_table, &l.table, sizeof(vol_table)))
|
|
+ ERR_EXIT("Orphaned multi-volume dump device '%s'",
|
|
+ g.opts.device);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read dump tool, multi-volume dump parameter table, and dump header from the
|
|
+ * input dump volume. Check input dump volume for:
|
|
+ * - identical dump parameter table (that is it belongs to the same dump set)
|
|
+ * - valid magic number in the dump tool
|
|
+ * - valid dump sign in the dump header
|
|
+ *
|
|
+ * We read partition data via the device node. If another process
|
|
+ * has changed partition data via the partition node, the corresponding
|
|
+ * device node might still have old data in its buffers. Flush buffers
|
|
+ * to keep things in sync.
|
|
+ */
|
|
+void vol_read(struct vol *vol)
|
|
+{
|
|
+ zg_ioctl(vol->fh, BLKFLSBUF, NULL, "BLKFLSBUF", ZG_CHECK);
|
|
+ df_s390_dumper_read(vol->fh, vol->blk_size, &vol->dumper);
|
|
+ check_vol_table(vol);
|
|
+ zg_seek(vol->fh, vol->part_off, ZG_CHECK);
|
|
+ zg_read(vol->fh, &vol->hdr, DF_S390_HDR_SIZE, ZG_CHECK);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read memory
|
|
+ */
|
|
+static void df_s390mv_mem_read(struct dfi_mem_chunk *mem_chunk, u64 off,
|
|
+ void *buf, u64 cnt)
|
|
+{
|
|
+ struct vol *vol = mem_chunk->data;
|
|
+
|
|
+ zg_seek(vol->fh, vol->part_off + off + DF_S390_HDR_SIZE, ZG_CHECK);
|
|
+ zg_read(vol->fh, buf, cnt, ZG_CHECK);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initilize DASD volume
|
|
+ */
|
|
+static void vol_init(struct vol *vol, struct vol_parm *vol_parm, u64 *mem_off)
|
|
+{
|
|
+ u64 blk_cnt = vol_parm->end_blk - vol_parm->start_blk + 1;
|
|
+
|
|
+ sprintf(vol->bus_id, "0.0.%04x", vol_parm->devno);
|
|
+ vol->blk_size = vol_parm->blk_size << 8;
|
|
+ vol->part_off = vol_parm->start_blk * vol->blk_size;
|
|
+ vol->part_size = blk_cnt * vol->blk_size;
|
|
+ vol->status = dev_from_busid(vol->bus_id, &vol->dev);
|
|
+ vol->sign = SIGN_VALID;
|
|
+
|
|
+ if (vol->status != DEV_ONLINE)
|
|
+ return;
|
|
+
|
|
+ vol->devnode = zg_devnode_create(vol->dev);
|
|
+ vol->fh = zg_open(vol->devnode, O_RDONLY, ZG_CHECK);
|
|
+
|
|
+ vol_read(vol);
|
|
+
|
|
+ if ((vol->hdr.volnr == vol->nr) && (vol->hdr.mem_size != 0))
|
|
+ vol->sign = SIGN_ACTIVE;
|
|
+
|
|
+ if (vol->hdr.mvdump_sign != DF_S390_MAGIC) {
|
|
+ vol->sign = SIGN_INVALID;
|
|
+ l.dump_incomplete = 1;
|
|
+ }
|
|
+
|
|
+ if (strncmp(df_s390_dumper_magic(vol->dumper), "ZMULT64", 7) != 0) {
|
|
+ vol->sign = SIGN_INVALID;
|
|
+ l.dump_incomplete = 1;
|
|
+ }
|
|
+
|
|
+ if (vol->nr == 0)
|
|
+ l.hdr = vol->hdr;
|
|
+
|
|
+ if (*mem_off == l.hdr.mem_size) {
|
|
+ /* Unused volume */
|
|
+ vol->mem_start = 0;
|
|
+ vol->mem_end = 0;
|
|
+ if (vol->sign == SIGN_ACTIVE)
|
|
+ vol->sign = SIGN_VALID;
|
|
+ } else {
|
|
+ /* Used volume */
|
|
+ vol->mem_start = *mem_off;
|
|
+ vol->mem_end = *mem_off + PAGE_ALIGN(vol->part_size) -
|
|
+ DF_S390_HDR_SIZE - 1;
|
|
+ vol->mem_end = MIN(vol->mem_end, l.hdr.mem_size - 1);
|
|
+ if (vol->mem_end == l.hdr.mem_size - 1)
|
|
+ em_init(vol);
|
|
+ *mem_off += vol->mem_end - vol->mem_start + 1;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print volume information
|
|
+ */
|
|
+static void vol_print(struct vol *vol)
|
|
+{
|
|
+ STDERR(" Volume %i: %s (%s", vol->nr, vol->bus_id,
|
|
+ dev_status_str(vol->status));
|
|
+ if (vol->status == DEV_ONLINE)
|
|
+ STDERR("/%s)\n", dev_sign_str(vol->sign));
|
|
+ else
|
|
+ STDERR(")\n");
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print information for all volumes
|
|
+ */
|
|
+static void vol_print_all(void)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ for (i = 0; i < l.table.vol_cnt; i++)
|
|
+ vol_print(&l.vol_vec[i]);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Add memory chunks
|
|
+ */
|
|
+static void mem_chunks_add(void)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ for (i = 0; i < l.table.vol_cnt; i++) {
|
|
+ struct vol *vol = &l.vol_vec[i];
|
|
+ if (vol->sign != SIGN_ACTIVE)
|
|
+ continue;
|
|
+ dfi_mem_chunk_add(vol->mem_start,
|
|
+ vol->mem_end - vol->mem_start + 1,
|
|
+ vol, df_s390mv_mem_read);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print hint for setting all offline volumes online
|
|
+ */
|
|
+static void vol_offline_msg(void)
|
|
+{
|
|
+ unsigned int i, first = 1;
|
|
+
|
|
+ STDERR("\n");
|
|
+ STDERR("Set all devices online using:\n");
|
|
+ STDERR("# chccwdev -e ");
|
|
+ for (i = 0; i < l.table.vol_cnt; i++) {
|
|
+ if (l.vol_vec[i].status == DEV_OFFLINE) {
|
|
+ if (first)
|
|
+ first = 0;
|
|
+ else
|
|
+ STDERR(",");
|
|
+ STDERR("%s", l.vol_vec[i].bus_id);
|
|
+ }
|
|
+ }
|
|
+ STDERR("\n");
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print error for all undefined volumes
|
|
+ */
|
|
+static void vol_undefined_msg(void)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ STDERR("\n");
|
|
+ STDERR("Ensure that the following devices are available to the"
|
|
+ "system:\n");
|
|
+ for (i = 0; i < l.table.vol_cnt; i++) {
|
|
+ if (l.vol_vec[i].status == DEV_UNDEFINED)
|
|
+ STDERR("* %s\n", l.vol_vec[i].bus_id);
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Check that all volumes are in online state
|
|
+ */
|
|
+static int vol_online_check(void)
|
|
+{
|
|
+ unsigned int i, offline = 0, undefined = 0;
|
|
+
|
|
+ for (i = 0; i < l.table.vol_cnt; i++) {
|
|
+ if (l.vol_vec[i].status == DEV_OFFLINE)
|
|
+ offline = 1;
|
|
+ if (l.vol_vec[i].status == DEV_UNDEFINED)
|
|
+ undefined = 1;
|
|
+ }
|
|
+ if (!offline && !undefined)
|
|
+ return 0;
|
|
+
|
|
+ STDERR("Found multi-volume dump tool:\n\n");
|
|
+ vol_print_all();
|
|
+ if (offline)
|
|
+ vol_offline_msg();
|
|
+ if (undefined)
|
|
+ vol_undefined_msg();
|
|
+ return -ENODEV;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Check if on device is a multi-volume dump
|
|
+ */
|
|
+static int mvdump_hdr_check(const char *file)
|
|
+{
|
|
+ struct df_s390_hdr hdr;
|
|
+ struct zg_fh *fh;
|
|
+ int rc = -ENODEV;
|
|
+
|
|
+ fh = zg_open(file, O_RDONLY, ZG_CHECK);
|
|
+ zg_read(fh, &hdr, sizeof(hdr), ZG_CHECK);
|
|
+ if (hdr.magic != DF_S390_MAGIC)
|
|
+ goto fail;
|
|
+ if (hdr.mvdump_sign != DF_S390_MAGIC)
|
|
+ goto fail;
|
|
+ rc = 0;
|
|
+fail:
|
|
+ zg_close(fh);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Check if sysfs is available
|
|
+ */
|
|
+static void check_sysfs(void)
|
|
+{
|
|
+ DIR *fh_dir;
|
|
+
|
|
+ fh_dir = opendir(SYSFS_BUSDIR);
|
|
+ if (!fh_dir)
|
|
+ ERR_EXIT_ERRNO("Could not open %s\n", SYSFS_BUSDIR);
|
|
+ closedir(fh_dir);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print dump information (dfi operation)
|
|
+ */
|
|
+static void dfi_s390mvfo_dump(void)
|
|
+{
|
|
+ vol_print_all();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read dump tool from DASD and check if we have a multi-volume dump tool
|
|
+ */
|
|
+static int mv_dumper_read(void)
|
|
+{
|
|
+ if (zg_ioctl(g.fh, BLKSSZGET, &l.blk_size, "BLKSSZGET",
|
|
+ ZG_CHECK_NONE) == -1)
|
|
+ return -ENODEV;
|
|
+ df_s390_dumper_read(g.fh, l.blk_size, &l.dumper);
|
|
+ if (memcmp(df_s390_dumper_magic(l.dumper), "ZMULT64", 7) != 0)
|
|
+ return -ENODEV;
|
|
+ table_read(g.fh, l.blk_size, &l.table);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize all volumes
|
|
+ */
|
|
+static void volumes_init(void)
|
|
+{
|
|
+ u64 mem_off = 0;
|
|
+ unsigned int i;
|
|
+
|
|
+ check_sysfs();
|
|
+
|
|
+ for (i = 0; i < l.table.vol_cnt; i++) {
|
|
+ l.vol_vec[i].nr = i;
|
|
+ vol_init(&l.vol_vec[i], &l.table.vol_parm[i], &mem_off);
|
|
+ }
|
|
+ if (mem_off != l.hdr.mem_size)
|
|
+ l.dump_incomplete = 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Open dump - If partition is specified open device instead
|
|
+ */
|
|
+static int open_dump(void)
|
|
+{
|
|
+ const struct stat *stat = zg_stat(g.fh);
|
|
+ unsigned dev_minor;
|
|
+ enum zg_type type;
|
|
+ char *path;
|
|
+
|
|
+ type = zg_type(g.fh);
|
|
+ if (type != ZG_TYPE_DASD && type != ZG_TYPE_DASD_PART)
|
|
+ return -ENODEV;
|
|
+
|
|
+ if (type == ZG_TYPE_DASD_PART) {
|
|
+ dev_minor = minor(stat->st_rdev) - (minor(stat->st_rdev) % 4);
|
|
+ if (mvdump_hdr_check(zg_path(g.fh)) != 0)
|
|
+ return -ENODEV;
|
|
+ path = zg_devnode_create(makedev(major(stat->st_rdev),
|
|
+ dev_minor));
|
|
+ zg_close(g.fh);
|
|
+ g.fh = zg_open(path, O_RDONLY, ZG_CHECK);
|
|
+ }
|
|
+ if (mv_dumper_read() != 0)
|
|
+ return -ENODEV;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize s390 multi-volume input dump format
|
|
+ */
|
|
+static int dfi_s390mv_init(void)
|
|
+{
|
|
+ if (open_dump() != 0)
|
|
+ return -ENODEV;
|
|
+ volumes_init();
|
|
+ if (vol_online_check() != 0)
|
|
+ zg_exit(1);
|
|
+ if (l.hdr.mem_size == 0)
|
|
+ return -ENODEV;
|
|
+ df_s390_hdr_add(&l.hdr);
|
|
+ mem_chunks_add();
|
|
+ if (l.dump_incomplete)
|
|
+ return -EINVAL;
|
|
+ df_s390_cpu_info_add(&l.hdr, l.hdr.mem_end);
|
|
+ df_s390_em_add(&l.em);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize s390 multi-volume dump tool (for -d option)
|
|
+ */
|
|
+int dt_s390mv_init(void)
|
|
+{
|
|
+ if (open_dump() != 0)
|
|
+ return -ENODEV;
|
|
+ volumes_init();
|
|
+ dt_arch_set(DFI_ARCH_64);
|
|
+ dt_version_set(df_s390_dumper_version(l.dumper));
|
|
+ if (df_s390_dumper_mem(l.dumper) != U64_MAX)
|
|
+ dt_attr_mem_limit_set(df_s390_dumper_mem(l.dumper));
|
|
+
|
|
+ dt_attr_force_set(df_s390_dumper_force(l.dumper));
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * s390 multi-volume dump tool info function (for -d option)
|
|
+ */
|
|
+void dt_s390mv_info(void)
|
|
+{
|
|
+ vol_print_all();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * S390 multi-volume DFI operations
|
|
+ */
|
|
+struct dfi dfi_s390mv = {
|
|
+ .name = "s390mv",
|
|
+ .init = dfi_s390mv_init,
|
|
+ .info_dump = dfi_s390mvfo_dump,
|
|
+ .feat_bits = DFI_FEAT_COPY | DFI_FEAT_SEEK,
|
|
+};
|
|
diff --git a/zdump/dfi_s390tape.c b/zdump/dfi_s390tape.c
|
|
new file mode 100644
|
|
index 0000000..8528cce
|
|
--- /dev/null
|
|
+++ b/zdump/dfi_s390tape.c
|
|
@@ -0,0 +1,198 @@
|
|
+/*
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
+ *
|
|
+ * S390 tape dump input format
|
|
+ *
|
|
+ * Copyright IBM Corp. 2001, 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include <string.h>
|
|
+#include <time.h>
|
|
+#include <fcntl.h>
|
|
+#include <stdio.h>
|
|
+#include <unistd.h>
|
|
+#include <sys/types.h>
|
|
+#include <sys/stat.h>
|
|
+#include <sys/mtio.h>
|
|
+#include "zgetdump.h"
|
|
+
|
|
+#define TAPE_BLK_SIZE 32768 /* Defined by zipl tape dumper */
|
|
+
|
|
+/*
|
|
+ * File local static data
|
|
+ *
|
|
+ * blk_buf_addr: Memory address of last read memory block
|
|
+ * blk_buf: Content of the last read memory block
|
|
+ * blk: The next block number that will be read relative to blk_start
|
|
+ * blk_start: The absolute block number on the tape where the dump starts
|
|
+ */
|
|
+static struct {
|
|
+ char blk_buf[TAPE_BLK_SIZE];
|
|
+ u64 blk_buf_addr;
|
|
+ u64 blk;
|
|
+ int blk_start;
|
|
+} l;
|
|
+
|
|
+/*
|
|
+ * MT ioctls
|
|
+ */
|
|
+struct mtioctl {
|
|
+ int op;
|
|
+ const char *desc;
|
|
+};
|
|
+
|
|
+static struct mtioctl mt_fsfm = {MTFSFM, "forward space file"};
|
|
+static struct mtioctl mt_bsr = {MTBSR, "backward space record"};
|
|
+static struct mtioctl mt_tell = {MTTELL, "tell"};
|
|
+static struct mtioctl mt_seek = {MTSEEK, "seek"};
|
|
+
|
|
+/*
|
|
+ * Do MT ioctl with count argument
|
|
+ */
|
|
+static int mtioctl(struct mtioctl *op, int cnt, enum zg_check check)
|
|
+{
|
|
+ struct mtop mtop;
|
|
+
|
|
+ mtop.mt_count = cnt;
|
|
+ mtop.mt_op = op->op;
|
|
+ return zg_ioctl(g.fh, MTIOCTOP, &mtop, op->desc, check);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Verify end marker
|
|
+ */
|
|
+static int em_verify(struct df_s390_em *em)
|
|
+{
|
|
+ if ((memcmp(em->str, "DUMP_END", 8) == 0)) {
|
|
+ df_s390_em_add(em);
|
|
+ return 0;
|
|
+ } else {
|
|
+ return -EINVAL;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Verify dump header
|
|
+ */
|
|
+static void hdr_verify(struct df_s390_hdr *hdr)
|
|
+{
|
|
+ if (hdr->magic != DF_S390_MAGIC)
|
|
+ ERR_EXIT("No valid dump found on tape");
|
|
+ if (hdr->volnr != 0) {
|
|
+ STDERR_PR("Found volume number: %d\n", hdr->volnr);
|
|
+ ERR_EXIT("Multi-volume dumps are no longer supported");
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Seek to relative block number in dump (block 0 is the dump header)
|
|
+ */
|
|
+static void seek_blk(u64 blk)
|
|
+{
|
|
+ if (l.blk == blk)
|
|
+ return;
|
|
+ mtioctl(&mt_seek, l.blk_start + blk, ZG_CHECK);
|
|
+ l.blk = blk;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read memory from cartridge
|
|
+ */
|
|
+static void df_s390tape_mem_read(struct dfi_mem_chunk *mem_chunk, u64 addr,
|
|
+ void *buf, u64 cnt)
|
|
+{
|
|
+ unsigned int copied = 0, size;
|
|
+ (void) mem_chunk;
|
|
+ u64 blk, off;
|
|
+
|
|
+ do {
|
|
+ blk = addr / TAPE_BLK_SIZE + 1;
|
|
+ if (addr >= l.blk_buf_addr + TAPE_BLK_SIZE ||
|
|
+ addr < l.blk_buf_addr) {
|
|
+ seek_blk(blk);
|
|
+ zg_read(g.fh, l.blk_buf, sizeof(l.blk_buf),
|
|
+ ZG_CHECK);
|
|
+ l.blk_buf_addr = (l.blk - 1) * TAPE_BLK_SIZE;
|
|
+ l.blk++;
|
|
+ }
|
|
+ off = addr - l.blk_buf_addr;
|
|
+ size = MIN(cnt - copied, TAPE_BLK_SIZE - off);
|
|
+ memcpy(buf + copied, &l.blk_buf[off], size);
|
|
+ addr += size;
|
|
+ copied += size;
|
|
+ } while (copied != cnt);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize cache for memory read (block 0 is the dump header)
|
|
+ */
|
|
+static void mem_read_init(void)
|
|
+{
|
|
+ mtioctl(&mt_seek, l.blk_start + 1, ZG_CHECK);
|
|
+ zg_read(g.fh, l.blk_buf, sizeof(l.blk_buf), ZG_CHECK);
|
|
+ l.blk_buf_addr = 0;
|
|
+ l.blk = 2;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Init a new tape volume
|
|
+ */
|
|
+static int vol_init(void)
|
|
+{
|
|
+ struct df_s390_hdr hdr;
|
|
+ struct df_s390_em em;
|
|
+ int rc;
|
|
+
|
|
+ STDERR("Checking tape, this can take a while...\n");
|
|
+ /* Init dump header */
|
|
+ l.blk_start = mtioctl(&mt_tell, 1, ZG_CHECK);
|
|
+ zg_read(g.fh, &hdr, sizeof(hdr), ZG_CHECK);
|
|
+ hdr_verify(&hdr);
|
|
+ df_s390_hdr_add(&hdr);
|
|
+ dfi_mem_chunk_add(0, hdr.mem_size, NULL, df_s390tape_mem_read);
|
|
+
|
|
+ /* Init end marker */
|
|
+ mtioctl(&mt_fsfm, 1, ZG_CHECK_NONE);
|
|
+ mtioctl(&mt_bsr, 1, ZG_CHECK);
|
|
+ rc = zg_read(g.fh, &em, sizeof(em), ZG_CHECK_ERR);
|
|
+ if (rc != 8 && rc != 16)
|
|
+ return -EINVAL;
|
|
+ if (em_verify(&em) != 0)
|
|
+ return -EINVAL;
|
|
+
|
|
+ /* Init memory read & CPU info */
|
|
+ mem_read_init();
|
|
+ df_s390_cpu_info_add(&hdr, hdr.mem_size - 1);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Exit function: Seek to block 0
|
|
+ */
|
|
+static void dfi_s390tape_exit(void)
|
|
+{
|
|
+ seek_blk(0);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize s390 tape DFI
|
|
+ */
|
|
+static int dfi_s390tape_init(void)
|
|
+{
|
|
+ if (zg_type(g.fh) != ZG_TYPE_TAPE)
|
|
+ return -ENODEV;
|
|
+ if (vol_init() != 0)
|
|
+ return -EINVAL;
|
|
+ zg_atexit(dfi_s390tape_exit);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * S390 tape DFI operations
|
|
+ */
|
|
+struct dfi dfi_s390tape = {
|
|
+ .name = "s390tape",
|
|
+ .init = dfi_s390tape_init,
|
|
+ .feat_bits = DFI_FEAT_SEEK | DFI_FEAT_COPY,
|
|
+};
|
|
diff --git a/zdump/dfo.c b/zdump/dfo.c
|
|
new file mode 100644
|
|
index 0000000..333f5f2
|
|
--- /dev/null
|
|
+++ b/zdump/dfo.c
|
|
@@ -0,0 +1,204 @@
|
|
+/*
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
+ *
|
|
+ * Generic output dump format functions (DFO - Dump Format Output)
|
|
+ *
|
|
+ * Copyright IBM Corp. 2001, 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include <time.h>
|
|
+#include "zgetdump.h"
|
|
+
|
|
+#define dfo_chunk_iterate(dfo_chunk) \
|
|
+ list_iterate(dfo_chunk, &l.dump.chunk_list, list)
|
|
+
|
|
+/*
|
|
+ * DFO vector
|
|
+ */
|
|
+static struct dfo *dfo_vec[] = {
|
|
+ &dfo_s390,
|
|
+ &dfo_elf,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Dump (output) information
|
|
+ */
|
|
+struct dump {
|
|
+ u64 off; /* Current file offset in dump */
|
|
+ u64 size; /* Size of dump in bytes */
|
|
+ unsigned int chunk_cnt; /* Number of dump chunks */
|
|
+ struct list chunk_list; /* DFO chunk list */
|
|
+};
|
|
+
|
|
+/*
|
|
+ * File local static data
|
|
+ */
|
|
+static struct {
|
|
+ struct dump dump;
|
|
+ struct dfo *dfo;
|
|
+} l;
|
|
+
|
|
+/*
|
|
+ * Add dump chunk
|
|
+ */
|
|
+void dfo_chunk_add(u64 start, u64 size, void *data, dfo_chunk_read_fn read_fn)
|
|
+{
|
|
+ struct dfo_chunk *dfo_chunk;
|
|
+
|
|
+ dfo_chunk = zg_alloc(sizeof(*dfo_chunk));
|
|
+ dfo_chunk->start = start;
|
|
+ dfo_chunk->end = start + size - 1;
|
|
+ dfo_chunk->size = size;
|
|
+ dfo_chunk->data = data;
|
|
+ dfo_chunk->read_fn = read_fn;
|
|
+ list_add(&dfo_chunk->list, &l.dump.chunk_list);
|
|
+ l.dump.chunk_cnt++;
|
|
+ l.dump.size = MAX(l.dump.size, dfo_chunk->end + 1);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Dump chunk function: Copy zero pages for chunk
|
|
+ */
|
|
+void dfo_chunk_zero_fn(struct dfo_chunk *dfo_chunk, u64 off, void *buf, u64 cnt)
|
|
+{
|
|
+ (void) dfo_chunk;
|
|
+ (void) off;
|
|
+
|
|
+ memset(buf, 0, cnt);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Dump chunk function: Copy given buffer for chunk
|
|
+ */
|
|
+void dfo_chunk_buf_fn(struct dfo_chunk *dfo_chunk, u64 off, void *buf, u64 cnt)
|
|
+{
|
|
+ memcpy(buf, dfo_chunk->data + off, cnt);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Dump chunk function: Copy given memory range for chunk
|
|
+ */
|
|
+void dfo_chunk_mem_fn(struct dfo_chunk *dfo_chunk, u64 off, void *buf, u64 cnt)
|
|
+{
|
|
+ struct dfi_mem_chunk *mem_chunk = dfo_chunk->data;
|
|
+
|
|
+ mem_chunk->read_fn(mem_chunk, off, buf, cnt);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Get DFO name
|
|
+ */
|
|
+const char *dfo_name(void)
|
|
+{
|
|
+ return l.dfo->name;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Set DFO by name
|
|
+ */
|
|
+int dfo_set(const char *dfo_name)
|
|
+{
|
|
+ struct dfo *dfo;
|
|
+ int i = 0;
|
|
+
|
|
+ while ((dfo = dfo_vec[i])) {
|
|
+ if (strcmp(dfo->name, dfo_name) == 0) {
|
|
+ l.dfo = dfo;
|
|
+ return 0;
|
|
+ }
|
|
+ i++;
|
|
+ }
|
|
+ return -ENODEV;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize output dump format
|
|
+ */
|
|
+void dfo_init(void)
|
|
+{
|
|
+ if (!l.dfo)
|
|
+ ABORT("DFO not set");
|
|
+ list_init(&l.dump.chunk_list);
|
|
+ l.dfo->init();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Find dump chunk for offset "off"
|
|
+ *
|
|
+ * This function is a bit hacky. DFO chunks can overlap. If two DFO chunks
|
|
+ * overlap, the last registered chunk wins. The dfo_chunk_find() function
|
|
+ * reflects that by returning the first memory chunk that is found in
|
|
+ * the dfo chunk list.
|
|
+ *
|
|
+ * In addition to that it calculates the "virtual end" of that chunk. An
|
|
+ * overlapping chunk can limit the "virtual end" of an underlying chunk so
|
|
+ * that the "virtual end" of that chunk is lower than the "real end".
|
|
+ *
|
|
+ * Example:
|
|
+ *
|
|
+ * chunk 1.: |------|
|
|
+ * chunk 2.: |---------------------|
|
|
+ * off.....: ^
|
|
+ * virt end: ^
|
|
+ * real end: ^
|
|
+ *
|
|
+ * In this case chunk 2 will be returned and "end" is set to the start of
|
|
+ * chunk 1.
|
|
+ */
|
|
+static struct dfo_chunk *dfo_chunk_find(u64 off, u64 *end)
|
|
+{
|
|
+ struct dfo_chunk *dfo_chunk;
|
|
+
|
|
+ *end = U64_MAX;
|
|
+ dfo_chunk_iterate(dfo_chunk) {
|
|
+ if (dfo_chunk->start <= off && dfo_chunk->end >= off) {
|
|
+ *end = MIN(*end, dfo_chunk->end);
|
|
+ return dfo_chunk;
|
|
+ } else if (dfo_chunk->start > off) {
|
|
+ *end = MIN(*end, dfo_chunk->start - 1);
|
|
+ }
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Seek to output dump offset "off"
|
|
+ */
|
|
+void dfo_seek(u64 off)
|
|
+{
|
|
+ l.dump.off = off;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read "cnt" bytes of output dump at current offest
|
|
+ */
|
|
+u64 dfo_read(void *buf, u64 cnt)
|
|
+{
|
|
+ struct dfo_chunk *dfo_chunk;
|
|
+ u64 copied = 0, end, size;
|
|
+ u64 off = l.dump.off;
|
|
+
|
|
+ while (copied != cnt) {
|
|
+ dfo_chunk = dfo_chunk_find(off, &end);
|
|
+ if (!dfo_chunk)
|
|
+ goto out;
|
|
+ size = MIN(cnt - copied, end - off + 1);
|
|
+ dfo_chunk->read_fn(dfo_chunk, off - dfo_chunk->start,
|
|
+ buf + copied, size);
|
|
+ copied += size;
|
|
+ off += size;
|
|
+ }
|
|
+out:
|
|
+ l.dump.off = off;
|
|
+ return copied;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return output dump size
|
|
+ */
|
|
+u64 dfo_size(void)
|
|
+{
|
|
+ return l.dump.size;
|
|
+}
|
|
diff --git a/zdump/dfo.h b/zdump/dfo.h
|
|
new file mode 100644
|
|
index 0000000..70ca318
|
|
--- /dev/null
|
|
+++ b/zdump/dfo.h
|
|
@@ -0,0 +1,54 @@
|
|
+/*
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
+ *
|
|
+ * Generic output dump format functions (DFO - Dump Format Output)
|
|
+ *
|
|
+ * Copyright IBM Corp. 2001, 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#ifndef DFO_H
|
|
+#define DFO_H
|
|
+
|
|
+#include "list.h"
|
|
+#include "zg.h"
|
|
+
|
|
+struct dfo_chunk;
|
|
+
|
|
+typedef void (*dfo_chunk_read_fn)(struct dfo_chunk *chunk, u64 off,
|
|
+ void *buf, u64 cnt);
|
|
+
|
|
+struct dfo_chunk {
|
|
+ struct list list;
|
|
+ u64 start;
|
|
+ u64 end;
|
|
+ u64 size;
|
|
+ dfo_chunk_read_fn read_fn;
|
|
+ void *data;
|
|
+};
|
|
+
|
|
+extern void dfo_chunk_zero_fn(struct dfo_chunk *chunk, u64 off, void *buf,
|
|
+ u64 cnt);
|
|
+extern void dfo_chunk_buf_fn(struct dfo_chunk *chunk, u64 off, void *buf,
|
|
+ u64 cnt);
|
|
+extern void dfo_chunk_mem_fn(struct dfo_chunk *chunk, u64 off, void *buf,
|
|
+ u64 cnt);
|
|
+extern void dfo_chunk_add(u64 start, u64 size, void *data,
|
|
+ dfo_chunk_read_fn read_fn);
|
|
+
|
|
+extern u64 dfo_read(void *buf, u64 cnt);
|
|
+extern void dfo_seek(u64 addr);
|
|
+extern u64 dfo_size(void);
|
|
+extern const char *dfo_name(void);
|
|
+extern void dfo_init(void);
|
|
+extern int dfo_set(const char *dfo_name);
|
|
+
|
|
+/*
|
|
+ * DFO operations
|
|
+ */
|
|
+struct dfo {
|
|
+ const char *name;
|
|
+ void (*init)(void);
|
|
+};
|
|
+
|
|
+#endif /* DFO_H */
|
|
diff --git a/zdump/dfo_elf.c b/zdump/dfo_elf.c
|
|
new file mode 100644
|
|
index 0000000..bf3bc13
|
|
--- /dev/null
|
|
+++ b/zdump/dfo_elf.c
|
|
@@ -0,0 +1,299 @@
|
|
+/*
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
+ *
|
|
+ * ELF core dump output format
|
|
+ *
|
|
+ * Copyright IBM Corp. 2001, 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include <elf.h>
|
|
+#include <stdio.h>
|
|
+#include <string.h>
|
|
+#include <stdlib.h>
|
|
+#include <unistd.h>
|
|
+#include <assert.h>
|
|
+#include "zgetdump.h"
|
|
+
|
|
+#define HDR_PER_CPU_SIZE 0x200
|
|
+#define HDR_PER_MEMC_SIZE 0x100
|
|
+#define HDR_BASE_SIZE 0x2000
|
|
+
|
|
+/*
|
|
+ * File local static data
|
|
+ */
|
|
+static struct {
|
|
+ void *hdr;
|
|
+ u32 hdr_size;
|
|
+} l;
|
|
+
|
|
+/*
|
|
+ * Initialize ELF header
|
|
+ */
|
|
+static void *ehdr_init(Elf64_Ehdr *ehdr)
|
|
+{
|
|
+ memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
|
|
+ ehdr->e_ident[EI_CLASS] = ELFCLASS64;
|
|
+ ehdr->e_ident[EI_DATA] = ELFDATA2MSB;
|
|
+ ehdr->e_ident[EI_VERSION] = EV_CURRENT;
|
|
+ ehdr->e_ident[EI_OSABI] = ELFOSABI_SYSV;
|
|
+ ehdr->e_ident[EI_ABIVERSION] = 0;
|
|
+ memset(ehdr->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
|
|
+ ehdr->e_type = ET_CORE;
|
|
+ ehdr->e_machine = EM_S390;
|
|
+ ehdr->e_version = EV_CURRENT;
|
|
+ ehdr->e_entry = 0;
|
|
+ ehdr->e_phoff = sizeof(Elf64_Ehdr);
|
|
+ ehdr->e_shoff = 0;
|
|
+ ehdr->e_flags = 0;
|
|
+ ehdr->e_ehsize = sizeof(Elf64_Ehdr);
|
|
+ ehdr->e_phentsize = sizeof(Elf64_Phdr);
|
|
+ ehdr->e_phnum = dfi_mem_chunk_cnt() + 1;
|
|
+ ehdr->e_shentsize = 0;
|
|
+ ehdr->e_shnum = 0;
|
|
+ ehdr->e_shstrndx = 0;
|
|
+ return ehdr + 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize ELF loads
|
|
+ */
|
|
+static u64 loads_init(Elf64_Phdr *phdr, u64 loads_offset)
|
|
+{
|
|
+ struct dfi_mem_chunk *mem_chunk;
|
|
+ u64 mem_size = 0;
|
|
+
|
|
+ dfi_mem_chunk_iterate(mem_chunk) {
|
|
+ phdr->p_type = PT_LOAD;
|
|
+ phdr->p_offset = loads_offset;
|
|
+ phdr->p_vaddr = mem_chunk->start;
|
|
+ phdr->p_paddr = mem_chunk->start;
|
|
+ phdr->p_filesz = mem_chunk->end - mem_chunk->start + 1;
|
|
+ phdr->p_memsz = phdr->p_filesz;
|
|
+ phdr->p_flags = PF_R | PF_W | PF_X;
|
|
+ phdr->p_align = PAGE_SIZE;
|
|
+ loads_offset += phdr->p_filesz;
|
|
+ mem_size += phdr->p_memsz;
|
|
+ phdr++;
|
|
+ }
|
|
+ return mem_size;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize ELF note
|
|
+ */
|
|
+static void *nt_init(void *buf, Elf64_Word type, void *desc, int d_len,
|
|
+ const char *name)
|
|
+{
|
|
+ Elf64_Nhdr *note;
|
|
+ u64 len;
|
|
+
|
|
+ note = (Elf64_Nhdr *)buf;
|
|
+ note->n_namesz = strlen(name) + 1;
|
|
+ note->n_descsz = d_len;
|
|
+ note->n_type = type;
|
|
+ len = sizeof(Elf64_Nhdr);
|
|
+
|
|
+ memcpy(buf + len, name, note->n_namesz);
|
|
+ len = ROUNDUP(len + note->n_namesz, 4);
|
|
+
|
|
+ memcpy(buf + len, desc, note->n_descsz);
|
|
+ len = ROUNDUP(len + note->n_descsz, 4);
|
|
+
|
|
+ return PTR_ADD(buf, len);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize prstatus note
|
|
+ */
|
|
+static void *nt_prstatus(void *ptr, struct dfi_cpu *cpu)
|
|
+{
|
|
+ struct nt_prstatus_64 nt_prstatus;
|
|
+ static int cpu_nr = 1;
|
|
+
|
|
+ memset(&nt_prstatus, 0, sizeof(nt_prstatus));
|
|
+ memcpy(&nt_prstatus.gprs, cpu->gprs, sizeof(cpu->gprs));
|
|
+ memcpy(&nt_prstatus.psw, cpu->psw, sizeof(cpu->psw));
|
|
+ memcpy(&nt_prstatus.acrs, cpu->acrs, sizeof(cpu->acrs));
|
|
+ nt_prstatus.pr_pid = cpu_nr;
|
|
+ cpu_nr++;
|
|
+
|
|
+ return nt_init(ptr, NT_PRSTATUS, &nt_prstatus, sizeof(nt_prstatus),
|
|
+ "CORE");
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize fpregset (floating point) note
|
|
+ */
|
|
+static void *nt_fpregset(void *ptr, struct dfi_cpu *cpu)
|
|
+{
|
|
+ struct nt_fpregset_64 nt_fpregset;
|
|
+
|
|
+ memset(&nt_fpregset, 0, sizeof(nt_fpregset));
|
|
+ memcpy(&nt_fpregset.fpc, &cpu->fpc, sizeof(cpu->fpc));
|
|
+ memcpy(&nt_fpregset.fprs, &cpu->fprs, sizeof(cpu->fprs));
|
|
+
|
|
+ return nt_init(ptr, NT_FPREGSET, &nt_fpregset, sizeof(nt_fpregset),
|
|
+ "CORE");
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize timer note
|
|
+ */
|
|
+static void *nt_s390_timer(void *ptr, struct dfi_cpu *cpu)
|
|
+{
|
|
+ return nt_init(ptr, NT_S390_TIMER, &cpu->timer, sizeof(cpu->timer),
|
|
+ "LINUX");
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize TOD clock comparator note
|
|
+ */
|
|
+static void *nt_s390_tod_cmp(void *ptr, struct dfi_cpu *cpu)
|
|
+{
|
|
+ return nt_init(ptr, NT_S390_TODCMP, &cpu->todcmp,
|
|
+ sizeof(cpu->todcmp), "LINUX");
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize TOD programmable register note
|
|
+ */
|
|
+static void *nt_s390_tod_preg(void *ptr, struct dfi_cpu *cpu)
|
|
+{
|
|
+ return nt_init(ptr, NT_S390_TODPREG, &cpu->todpreg,
|
|
+ sizeof(cpu->todpreg), "LINUX");
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize control register note
|
|
+ */
|
|
+static void *nt_s390_ctrs(void *ptr, struct dfi_cpu *cpu)
|
|
+{
|
|
+ return nt_init(ptr, NT_S390_CTRS, &cpu->ctrs, sizeof(cpu->ctrs),
|
|
+ "LINUX");
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize prefix register note
|
|
+ */
|
|
+static void *nt_s390_prefix(void *ptr, struct dfi_cpu *cpu)
|
|
+{
|
|
+ return nt_init(ptr, NT_S390_PREFIX, &cpu->prefix,
|
|
+ sizeof(cpu->prefix), "LINUX");
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize prpsinfo note
|
|
+ */
|
|
+static void *nt_prpsinfo(void *ptr)
|
|
+{
|
|
+ struct nt_prpsinfo_64 prpsinfo;
|
|
+
|
|
+ memset(&prpsinfo, 0, sizeof(prpsinfo));
|
|
+ prpsinfo.pr_state = 0;
|
|
+ prpsinfo.pr_sname = 'R';
|
|
+ prpsinfo.pr_zomb = 0;
|
|
+ strcpy(prpsinfo.pr_fname, "vmlinux");
|
|
+
|
|
+ return nt_init(ptr, NT_PRPSINFO, &prpsinfo, sizeof(prpsinfo), "CORE");
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize notes
|
|
+ */
|
|
+static void *notes_init(Elf64_Phdr *phdr, void *ptr, u64 notes_offset)
|
|
+{
|
|
+ void *ptr_start = ptr;
|
|
+ struct dfi_cpu *cpu;
|
|
+
|
|
+ ptr = nt_prpsinfo(ptr);
|
|
+
|
|
+ if (dfi_cpu_content() != DFI_CPU_CONTENT_ALL)
|
|
+ goto out;
|
|
+
|
|
+ dfi_cpu_iterate(cpu) {
|
|
+ ptr = nt_prstatus(ptr, cpu);
|
|
+ ptr = nt_fpregset(ptr, cpu);
|
|
+ ptr = nt_s390_timer(ptr, cpu);
|
|
+ ptr = nt_s390_tod_cmp(ptr, cpu);
|
|
+ ptr = nt_s390_tod_preg(ptr, cpu);
|
|
+ ptr = nt_s390_ctrs(ptr, cpu);
|
|
+ ptr = nt_s390_prefix(ptr, cpu);
|
|
+ }
|
|
+out:
|
|
+ memset(phdr, 0, sizeof(*phdr));
|
|
+ phdr->p_type = PT_NOTE;
|
|
+ phdr->p_offset = notes_offset;
|
|
+ phdr->p_filesz = (unsigned long) PTR_SUB(ptr, ptr_start);
|
|
+ return ptr;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Setup dump chunks
|
|
+ */
|
|
+static void dump_chunks_init(void)
|
|
+{
|
|
+ struct dfi_mem_chunk *mem_chunk;
|
|
+ u64 off = 0;
|
|
+
|
|
+ dfo_chunk_add(0, l.hdr_size, l.hdr, dfo_chunk_buf_fn);
|
|
+ off = l.hdr_size;
|
|
+ dfi_mem_chunk_iterate(mem_chunk) {
|
|
+ dfo_chunk_add(off, mem_chunk->size, mem_chunk,
|
|
+ dfo_chunk_mem_fn);
|
|
+ off += mem_chunk->size;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * ELF DFO is only supported for 64 bit (s390x)
|
|
+ */
|
|
+static void ensure_s390x(void)
|
|
+{
|
|
+ if (dfi_arch() != DFI_ARCH_64)
|
|
+ ERR_EXIT("Error: The ELF dump format is only supported for "
|
|
+ "s390x source dumps");
|
|
+ df_elf_ensure_s390x();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize ELF output dump format
|
|
+ */
|
|
+static void dfo_elf_init(void)
|
|
+{
|
|
+ Elf64_Phdr *phdr_notes, *phdr_loads;
|
|
+ u64 mem_size, hdr_off;
|
|
+ u32 alloc_size;
|
|
+ void *ptr;
|
|
+
|
|
+ ensure_s390x();
|
|
+ alloc_size = HDR_BASE_SIZE +
|
|
+ dfi_cpu_cnt() * HDR_PER_CPU_SIZE +
|
|
+ dfi_mem_chunk_cnt() * HDR_PER_MEMC_SIZE;
|
|
+ l.hdr = zg_alloc(alloc_size);
|
|
+ /* Init elf header */
|
|
+ ptr = ehdr_init(l.hdr);
|
|
+ /* Init program headers */
|
|
+ phdr_notes = ptr;
|
|
+ ptr = PTR_ADD(ptr, sizeof(Elf64_Phdr));
|
|
+ phdr_loads = ptr;
|
|
+ ptr = PTR_ADD(ptr, sizeof(Elf64_Phdr) * dfi_mem_chunk_cnt());
|
|
+ /* Init notes */
|
|
+ hdr_off = PTR_DIFF(ptr, l.hdr);
|
|
+ ptr = notes_init(phdr_notes, ptr, hdr_off);
|
|
+ /* Init loads */
|
|
+ hdr_off = PTR_DIFF(ptr, l.hdr);
|
|
+ mem_size = loads_init(phdr_loads, hdr_off);
|
|
+ l.hdr_size = hdr_off;
|
|
+ if (l.hdr_size > alloc_size)
|
|
+ ABORT("hdr_size=%u alloc_size=%u", l.hdr_size, alloc_size);
|
|
+ dump_chunks_init();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * ELF DFO operations
|
|
+ */
|
|
+struct dfo dfo_elf = {
|
|
+ .name = "elf",
|
|
+ .init = dfo_elf_init,
|
|
+};
|
|
diff --git a/zdump/dfo_s390.c b/zdump/dfo_s390.c
|
|
new file mode 100644
|
|
index 0000000..7f51605
|
|
--- /dev/null
|
|
+++ b/zdump/dfo_s390.c
|
|
@@ -0,0 +1,200 @@
|
|
+/*
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
+ *
|
|
+ * S390 dump output format
|
|
+ *
|
|
+ * Copyright IBM Corp. 2001, 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include <string.h>
|
|
+#include <time.h>
|
|
+#include <fcntl.h>
|
|
+#include <stdio.h>
|
|
+#include <unistd.h>
|
|
+#include <sys/types.h>
|
|
+#include <sys/stat.h>
|
|
+#include "zgetdump.h"
|
|
+
|
|
+/*
|
|
+ * File local static data
|
|
+ */
|
|
+struct {
|
|
+ struct df_s390_hdr hdr;
|
|
+ struct df_s390_em em;
|
|
+} l;
|
|
+
|
|
+/*
|
|
+ * Copy internal register set to 64 bit lowcore
|
|
+ */
|
|
+static void cpu2lc_64(void *lc_64, struct dfi_cpu *cpu)
|
|
+{
|
|
+ struct dfi_lowcore_64 *lc = lc_64;
|
|
+ memcpy(lc->gpregs_save_area, &cpu->gprs, sizeof(cpu->gprs));
|
|
+ memcpy(lc->cregs_save_area, &cpu->ctrs, sizeof(cpu->ctrs));
|
|
+ memcpy(lc->access_regs_save_area, &cpu->acrs, sizeof(cpu->acrs));
|
|
+ memcpy(lc->floating_pt_save_area, &cpu->fprs, sizeof(cpu->fprs));
|
|
+ memcpy(&lc->fpt_creg_save_area, &cpu->fpc, sizeof(cpu->fpc));
|
|
+ memcpy(lc->st_status_fixed_logout, &cpu->psw, sizeof(cpu->psw));
|
|
+ memcpy(&lc->prefixreg_save_area, &cpu->prefix, sizeof(cpu->prefix));
|
|
+ memcpy(lc->timer_save_area, &cpu->timer, sizeof(cpu->timer));
|
|
+ memcpy(lc->clock_comp_save_area, &cpu->todcmp, sizeof(cpu->todcmp));
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Copy internal register set to 32 bit lowcore
|
|
+ */
|
|
+static void cpu2lc_32(void *lc_32, struct dfi_cpu *cpu_64)
|
|
+{
|
|
+ struct dfi_lowcore_32 *lc = lc_32;
|
|
+ struct dfi_cpu_32 cpu;
|
|
+
|
|
+ dfi_cpu_64_to_32(&cpu, cpu_64);
|
|
+ memcpy(lc->gpregs_save_area, &cpu.gprs, sizeof(cpu.gprs));
|
|
+ memcpy(lc->cregs_save_area, &cpu.ctrs, sizeof(cpu.ctrs));
|
|
+ memcpy(lc->access_regs_save_area, &cpu.acrs, sizeof(cpu.acrs));
|
|
+ memcpy(lc->floating_pt_save_area, &cpu.fprs, sizeof(cpu.fprs));
|
|
+ memcpy(lc->st_status_fixed_logout, &cpu.psw, sizeof(cpu.psw));
|
|
+ memcpy(&lc->prefixreg_save_area, &cpu.prefix, sizeof(cpu.prefix));
|
|
+ memcpy(lc->timer_save_area, &cpu.timer, sizeof(cpu.timer));
|
|
+ memcpy(lc->clock_comp_save_area, &cpu.todcmp, sizeof(cpu.todcmp));
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Convert timeval to s390 TOD clock
|
|
+ */
|
|
+static void timeval2tod(u64 *tod, struct timeval *xtime)
|
|
+{
|
|
+ u64 us = xtime->tv_sec * 1000000 + xtime->tv_usec;
|
|
+ *tod = (us << 12);
|
|
+ *tod += 0x8126d60e46000000LL - (0x3c26700LL * 1000000 * 4096);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Setup lowcore array in dump header
|
|
+ */
|
|
+static void lc_setup(struct df_s390_hdr *dh)
|
|
+{
|
|
+ struct dfi_cpu *cpu;
|
|
+ unsigned int i = 0;
|
|
+
|
|
+ dfi_cpu_iterate(cpu) {
|
|
+ if (i > DF_S390_CPU_MAX)
|
|
+ ERR_EXIT("Too many CPUs in source dump (%i)", i);
|
|
+ dh->lc_vec[i] = cpu->prefix;
|
|
+ i++;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Copy register set to prefix page
|
|
+ */
|
|
+static void dfo_s390_dump_chunk_lc_fn(struct dfo_chunk *dump_chunk,
|
|
+ u64 off, void *buf, u64 cnt)
|
|
+{
|
|
+ struct dfi_cpu *cpu = dump_chunk->data;
|
|
+ char lc[0x2000];
|
|
+
|
|
+ dfi_mem_read(cpu->prefix + off, &lc[off], cnt);
|
|
+ if (dfi_arch() == DFI_ARCH_64)
|
|
+ cpu2lc_64(lc, cpu);
|
|
+ else
|
|
+ cpu2lc_32(lc, cpu);
|
|
+ memcpy(buf, &lc[off], cnt);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Add register set to dump layout. We copy the register sets to the
|
|
+ * lowcore pages.
|
|
+ */
|
|
+static void add_cpu_to_dfo(struct dfi_cpu *cpu)
|
|
+{
|
|
+ if (dfi_cpu_content() != DFI_CPU_CONTENT_ALL)
|
|
+ return;
|
|
+
|
|
+ dfo_chunk_add(cpu->prefix + DF_S390_HDR_SIZE,
|
|
+ dfi_lc_size(dfi_arch()), cpu,
|
|
+ dfo_s390_dump_chunk_lc_fn);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Add memory chunk to dump layout
|
|
+ */
|
|
+static void add_mem_chunk_to_dfo(struct dfi_mem_chunk *mem_chunk)
|
|
+{
|
|
+ struct dfi_mem_chunk *mem_chunk_prev = dfi_mem_chunk_prev(mem_chunk);
|
|
+
|
|
+ if (mem_chunk_prev && (mem_chunk_prev->end + 1 != mem_chunk->start))
|
|
+ dfo_chunk_add(mem_chunk_prev->end + 1 + DF_S390_HDR_SIZE,
|
|
+ mem_chunk->start - mem_chunk_prev->end - 1,
|
|
+ NULL, dfo_chunk_zero_fn);
|
|
+
|
|
+ dfo_chunk_add(mem_chunk->start + DF_S390_HDR_SIZE, mem_chunk->size,
|
|
+ mem_chunk, dfo_chunk_mem_fn);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Setup dump chunks
|
|
+ */
|
|
+static void dump_chunks_init(void)
|
|
+{
|
|
+ struct dfi_mem_chunk *mem_chunk;
|
|
+ struct dfi_cpu *cpu;
|
|
+
|
|
+ dfo_chunk_add(0, DF_S390_HDR_SIZE, &l.hdr, dfo_chunk_buf_fn);
|
|
+ dfi_mem_chunk_iterate(mem_chunk)
|
|
+ add_mem_chunk_to_dfo(mem_chunk);
|
|
+ dfi_cpu_iterate(cpu)
|
|
+ add_cpu_to_dfo(cpu);
|
|
+ dfo_chunk_add(dfi_mem_range() + DF_S390_HDR_SIZE,
|
|
+ DF_S390_EM_SIZE,
|
|
+ &l.em, dfo_chunk_buf_fn);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize s390 output dump format
|
|
+ */
|
|
+static void df_s390_dump_init(void)
|
|
+{
|
|
+ struct df_s390_hdr *dh = &l.hdr;
|
|
+ struct df_s390_em *em = &l.em;
|
|
+
|
|
+ dh->magic = DF_S390_MAGIC;
|
|
+ dh->hdr_size = DF_S390_HDR_SIZE;
|
|
+ dh->page_size = PAGE_SIZE;
|
|
+ dh->dump_level = 4;
|
|
+ if (dfi_cpu_content() == DFI_CPU_CONTENT_NONE)
|
|
+ dh->version = 4;
|
|
+ else
|
|
+ dh->version = 5;
|
|
+ dh->mem_start = 0;
|
|
+ dh->mem_size = dh->mem_end = dfi_mem_range();
|
|
+ dh->num_pages = dh->mem_size / PAGE_SIZE;
|
|
+ dh->arch = df_s390_from_dfi_arch(dfi_arch());
|
|
+ if (dfi_attr_build_arch())
|
|
+ dh->build_arch = df_s390_from_dfi_arch(*dfi_attr_build_arch());
|
|
+ dh->cpu_cnt = dfi_cpu_cnt();
|
|
+ if (dfi_attr_real_cpu_cnt())
|
|
+ dh->real_cpu_cnt = *dfi_attr_real_cpu_cnt();
|
|
+ if (dfi_attr_cpu_id())
|
|
+ dh->cpu_id = *dfi_attr_cpu_id();
|
|
+ if (dfi_attr_mem_size_real())
|
|
+ dh->mem_size_real = *dfi_attr_mem_size_real();
|
|
+ if (dfi_attr_time()) {
|
|
+ timeval2tod(&dh->tod, dfi_attr_time());
|
|
+ timeval2tod(&em->tod, dfi_attr_time());
|
|
+ }
|
|
+ if (dfi_attr_time_end())
|
|
+ timeval2tod(&em->tod, dfi_attr_time_end());
|
|
+ lc_setup(dh);
|
|
+ memcpy(em->str, DF_S390_EM_STR, sizeof(em->str));
|
|
+ dump_chunks_init();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * S390 DFO operations
|
|
+ */
|
|
+struct dfo dfo_s390 = {
|
|
+ .name = "s390",
|
|
+ .init = df_s390_dump_init,
|
|
+};
|
|
diff --git a/zdump/dt.c b/zdump/dt.c
|
|
new file mode 100644
|
|
index 0000000..b19aa80
|
|
--- /dev/null
|
|
+++ b/zdump/dt.c
|
|
@@ -0,0 +1,131 @@
|
|
+/*
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
+ *
|
|
+ * Dump tool info generic functions
|
|
+ *
|
|
+ * Copyright IBM Corp. 2001, 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include "zgetdump.h"
|
|
+
|
|
+/*
|
|
+ * Supported dump tools
|
|
+ */
|
|
+static struct dt *dt_vec[] = {
|
|
+ &dt_s390mv,
|
|
+ &dt_s390sv,
|
|
+ NULL,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Dumper attribute information
|
|
+ */
|
|
+struct attr {
|
|
+ int *force;
|
|
+ u64 *mem_limit;
|
|
+ char *dasd_type;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * File local static data
|
|
+ */
|
|
+struct {
|
|
+ int version;
|
|
+ enum dfi_arch arch;
|
|
+ struct attr attr;
|
|
+ struct dt *dt;
|
|
+} l;
|
|
+
|
|
+/*
|
|
+ * Init dump tool backends
|
|
+ */
|
|
+void dt_init(void)
|
|
+{
|
|
+ struct dt *dt;
|
|
+ int i = 0;
|
|
+
|
|
+ while ((dt = dt_vec[i])) {
|
|
+ g.fh = zg_open(g.opts.device, O_RDONLY, ZG_CHECK);
|
|
+ if (zg_type(g.fh) != ZG_TYPE_DASD)
|
|
+ ERR_EXIT("Please specify DASD device node (e.g. "
|
|
+ "/dev/dasdd)");
|
|
+ if (dt->init() == 0) {
|
|
+ l.dt = dt;
|
|
+ return;
|
|
+ }
|
|
+ zg_close(g.fh);
|
|
+ i++;
|
|
+ }
|
|
+ ERR_EXIT("No dump tool found on \"%s\"", g.opts.device);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print info about dump tool
|
|
+ */
|
|
+void dt_info_print(void)
|
|
+{
|
|
+ STDERR("Dump device info:\n");
|
|
+ STDERR(" Dump tool.........: %s\n", l.dt->desc);
|
|
+ STDERR(" Version...........: %d\n", l.version);
|
|
+ STDERR(" Architecture......: %s\n", dfi_arch_str(l.arch));
|
|
+ if (l.attr.dasd_type)
|
|
+ STDERR(" DASD type.........: %s\n", l.attr.dasd_type);
|
|
+ if (l.attr.mem_limit)
|
|
+ STDERR(" Dump size limit...: %lld MB\n",
|
|
+ TO_MIB(*l.attr.mem_limit));
|
|
+ else
|
|
+ STDERR(" Dump size limit...: none\n");
|
|
+ if (l.attr.force) {
|
|
+ if (*l.attr.force == 0)
|
|
+ STDERR(" Force specified...: no\n");
|
|
+ else
|
|
+ STDERR(" Force specified...: yes\n");
|
|
+ }
|
|
+ if (l.dt->info) {
|
|
+ STDERR("\n");
|
|
+ l.dt->info();
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Set DT architecture
|
|
+ */
|
|
+void dt_arch_set(enum dfi_arch arch)
|
|
+{
|
|
+ l.arch = arch;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Set DT version
|
|
+ */
|
|
+void dt_version_set(int version)
|
|
+{
|
|
+ l.version = version;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Set DT memory limit attribute
|
|
+ */
|
|
+void dt_attr_mem_limit_set(u64 mem_limit)
|
|
+{
|
|
+ l.attr.mem_limit = zg_alloc(sizeof(*l.attr.mem_limit));
|
|
+ *l.attr.mem_limit = mem_limit;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Set DT force attribute
|
|
+ */
|
|
+void dt_attr_force_set(int force)
|
|
+{
|
|
+ l.attr.force = zg_alloc(sizeof(*l.attr.force));
|
|
+ *l.attr.force = force;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Set DT DASD type attribute
|
|
+ */
|
|
+void dt_attr_dasd_type_set(const char *dasd_type)
|
|
+{
|
|
+ l.attr.dasd_type = zg_strdup(dasd_type);
|
|
+}
|
|
diff --git a/zdump/dt.h b/zdump/dt.h
|
|
new file mode 100644
|
|
index 0000000..44e0a09
|
|
--- /dev/null
|
|
+++ b/zdump/dt.h
|
|
@@ -0,0 +1,29 @@
|
|
+/*
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
+ *
|
|
+ * Dump tool info generic functions
|
|
+ *
|
|
+ * Copyright IBM Corp. 2001, 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#ifndef DT_H
|
|
+#define DT_H
|
|
+
|
|
+#include "dfi.h"
|
|
+
|
|
+struct dt {
|
|
+ const char *desc;
|
|
+ int (*init)(void);
|
|
+ void (*info)(void);
|
|
+};
|
|
+
|
|
+extern void dt_init(void);
|
|
+extern void dt_info_print(void);
|
|
+extern void dt_arch_set(enum dfi_arch arch);
|
|
+extern void dt_version_set(int version);
|
|
+extern void dt_attr_mem_limit_set(u64 mem_limit);
|
|
+extern void dt_attr_force_set(int value);
|
|
+extern void dt_attr_dasd_type_set(const char *dasd_type);
|
|
+
|
|
+#endif
|
|
diff --git a/zdump/dt_s390mv.c b/zdump/dt_s390mv.c
|
|
new file mode 100644
|
|
index 0000000..17ac1fc
|
|
--- /dev/null
|
|
+++ b/zdump/dt_s390mv.c
|
|
@@ -0,0 +1,19 @@
|
|
+/*
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
+ *
|
|
+ * S390 multi-volume DASD dump tool
|
|
+ *
|
|
+ * Copyright IBM Corp. 2001, 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include "zgetdump.h"
|
|
+
|
|
+/*
|
|
+ * DT operations
|
|
+ */
|
|
+struct dt dt_s390mv = {
|
|
+ .desc = "Multi-volume DASD dump tool",
|
|
+ .init = dt_s390mv_init,
|
|
+ .info = dt_s390mv_info,
|
|
+};
|
|
diff --git a/zdump/dt_s390sv.c b/zdump/dt_s390sv.c
|
|
new file mode 100644
|
|
index 0000000..22966cd
|
|
--- /dev/null
|
|
+++ b/zdump/dt_s390sv.c
|
|
@@ -0,0 +1,129 @@
|
|
+/*
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
+ *
|
|
+ * S390 single-volume DASD dump tool
|
|
+ *
|
|
+ * Copyright IBM Corp. 2001, 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include <linux/fs.h>
|
|
+#include "zgetdump.h"
|
|
+
|
|
+#define HEXINSTR "\x0d\x10\x47\xf0" /* BASR + 1st halfword of BC */
|
|
+
|
|
+/*
|
|
+ * File local static data
|
|
+ */
|
|
+static struct {
|
|
+ struct df_s390_dumper dumper;
|
|
+ enum dfi_arch dumper_arch;
|
|
+} l;
|
|
+
|
|
+/*
|
|
+ * Read dump tool from ECKD DASD device
|
|
+ */
|
|
+static int dumper_read_eckd(int blk_size)
|
|
+{
|
|
+ df_s390_dumper_read(g.fh, blk_size, &l.dumper);
|
|
+
|
|
+ if (strncmp(l.dumper.magic, "ZECKD31", 7) == 0) {
|
|
+ l.dumper_arch = DFI_ARCH_32;
|
|
+ } else if (strncmp(l.dumper.magic, "ZECKD64", 7) == 0) {
|
|
+ l.dumper_arch = DFI_ARCH_64;
|
|
+ } else if ((memcmp(l.dumper.magic, HEXINSTR, 4) == 0) &&
|
|
+ (l.dumper.d.v1.code[0] == '\x0d') &&
|
|
+ (l.dumper.d.v1.code[1] == '\xd0')) {
|
|
+ /* We found basr r13,0 (old dumper) */
|
|
+ l.dumper.version = 0;
|
|
+ l.dumper_arch = DFI_ARCH_UNKNOWN;
|
|
+ } else {
|
|
+ return -ENODEV;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read dump tool from FBA DASD device
|
|
+ */
|
|
+static void dumper_read_fba_gen(int size, void *buffer)
|
|
+{
|
|
+ zg_seek_end(g.fh, -size, ZG_CHECK);
|
|
+ zg_read(g.fh, buffer, size, ZG_CHECK);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read dump tool on FBA disk and check its magic number
|
|
+ */
|
|
+int dumper_check_fba(void)
|
|
+{
|
|
+ if (strncmp(l.dumper.magic, "ZDFBA31", 7) == 0) {
|
|
+ l.dumper_arch = DFI_ARCH_32;
|
|
+ } else if (strncmp(l.dumper.magic, "ZDFBA64", 7) == 0) {
|
|
+ l.dumper_arch = DFI_ARCH_64;
|
|
+ } else if ((memcmp(l.dumper.magic, HEXINSTR, 4) == 0) &&
|
|
+ (l.dumper.d.v1.code[0] == '\x0d') &&
|
|
+ (l.dumper.d.v1.code[1] == '\xd0')) {
|
|
+ /* We found basr r13,0 (old dumper) */
|
|
+ l.dumper.version = 0;
|
|
+ l.dumper_arch = DFI_ARCH_UNKNOWN;
|
|
+ } else {
|
|
+ return -ENODEV;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read dump tool on FBA disk and check its magic number
|
|
+ */
|
|
+static int dumper_read_fba(void)
|
|
+{
|
|
+ dumper_read_fba_gen(DF_S390_DUMPER_SIZE_V1, &l.dumper);
|
|
+ if (dumper_check_fba() == 0)
|
|
+ return 0;
|
|
+ dumper_read_fba_gen(DF_S390_DUMPER_SIZE_V2, &l.dumper);
|
|
+ if (dumper_check_fba() == 0)
|
|
+ return 0;
|
|
+ return -ENODEV;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read single volume dumper from disk
|
|
+ */
|
|
+static int sv_dumper_read(void)
|
|
+{
|
|
+ int blk_size;
|
|
+
|
|
+ zg_ioctl(g.fh, BLKSSZGET, &blk_size, "BLKSSZGET", ZG_CHECK);
|
|
+ if (dumper_read_eckd(blk_size) == 0) {
|
|
+ dt_attr_dasd_type_set("ECKD");
|
|
+ return 0;
|
|
+ }
|
|
+ if (dumper_read_fba() == 0) {
|
|
+ dt_attr_dasd_type_set("FBA");
|
|
+ return 0;
|
|
+ }
|
|
+ return -ENODEV;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize s390 single-volume dump tool (for -d option)
|
|
+ */
|
|
+static int dt_s390sv_init(void)
|
|
+{
|
|
+ if (sv_dumper_read() != 0)
|
|
+ return -ENODEV;
|
|
+ dt_arch_set(l.dumper_arch);
|
|
+ dt_version_set(df_s390_dumper_version(l.dumper));
|
|
+ if (df_s390_dumper_mem(l.dumper) != U64_MAX)
|
|
+ dt_attr_mem_limit_set(df_s390_dumper_mem(l.dumper));
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * s390 single-volume DT operations
|
|
+ */
|
|
+struct dt dt_s390sv = {
|
|
+ .desc = "Single-volume DASD dump tool",
|
|
+ .init = dt_s390sv_init,
|
|
+};
|
|
diff --git a/zdump/opts.c b/zdump/opts.c
|
|
new file mode 100644
|
|
index 0000000..d00f5b1
|
|
--- /dev/null
|
|
+++ b/zdump/opts.c
|
|
@@ -0,0 +1,242 @@
|
|
+/*
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
+ *
|
|
+ * Option parsing
|
|
+ *
|
|
+ * Copyright IBM Corp. 2001, 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include <getopt.h>
|
|
+#include <stdio.h>
|
|
+#include <string.h>
|
|
+#include <stdlib.h>
|
|
+#include "zgetdump.h"
|
|
+#include "zt_common.h"
|
|
+
|
|
+/*
|
|
+ * Text for --help option
|
|
+ */
|
|
+static char help_text[] =
|
|
+"Usage: zgetdump [OPTIONS] [DUMPDEV] [DIR]\n"
|
|
+"\n"
|
|
+"The zgetdump tool takes as source a dump device or dump file (DUMPDEV)\n"
|
|
+"and writes its contents to standard output, which you can redirect to a\n"
|
|
+"specific file. Alternatively you can also mount the dump content.\n"
|
|
+"Because zgetdump is able to read and write different dump formats, it\n"
|
|
+"can be used to convert a dump from one format to another. zgetdump can\n"
|
|
+"also verify if a dump is valid or check, whether a DASD device contains\n"
|
|
+"a valid dump tool.\n"
|
|
+"\n"
|
|
+"-h, --help Print this help, then exit.\n"
|
|
+"-v, --version Print version information, then exit.\n"
|
|
+"-m, --mount Mount dump to mount point DIR\n"
|
|
+"-u, --umount Unmount dump from mount point DIR\n"
|
|
+"-f, --fmt <FMT> Specify target dump format FMT (\"elf\" or \"s390\")\n"
|
|
+"-i, --info Print dump information\n"
|
|
+"-d, --device Print dump device information\n";
|
|
+
|
|
+static const char copyright_str[] = "Copyright IBM Corp. 2001, 2010";
|
|
+
|
|
+/*
|
|
+ * Initialize default settings
|
|
+ */
|
|
+static void init_defaults(void)
|
|
+{
|
|
+ g.prog_name = "zgetdump";
|
|
+ g.opts.action = ZG_ACTION_STDOUT;
|
|
+ g.opts.fmt = "s390";
|
|
+ dfo_set(g.opts.fmt);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print "help" hint
|
|
+ */
|
|
+static void print_usage_exit(void)
|
|
+{
|
|
+ STDERR("Try '%s --help' for more information.\n", g.prog_name);
|
|
+ zg_exit(1);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print help text
|
|
+ */
|
|
+static void print_help_exit(void)
|
|
+{
|
|
+ STDOUT(help_text);
|
|
+ zg_exit(0);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print version information
|
|
+ */
|
|
+static void print_version_exit(void)
|
|
+{
|
|
+ STDOUT("%s: Tool for copying and converting dumps version %s\n",
|
|
+ g.prog_name, RELEASE_STRING);
|
|
+ STDOUT("%s\n", copyright_str);
|
|
+ zg_exit(0);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Set "--fmt" option
|
|
+ */
|
|
+static void fmt_set(const char *fmt)
|
|
+{
|
|
+ if (dfo_set(fmt) != 0)
|
|
+ ERR_EXIT("Invalid target format \"%s\" specified", fmt);
|
|
+ g.opts.fmt_specified = 1;
|
|
+ g.opts.fmt = fmt;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Set mount point
|
|
+ */
|
|
+static void mount_point_set(const char *mount_point)
|
|
+{
|
|
+ g.opts.mount_point = zg_strdup(mount_point);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Set device
|
|
+ */
|
|
+static void device_set(const char *path)
|
|
+{
|
|
+ g.opts.device = zg_strdup(path);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Set FUSE debug options
|
|
+ */
|
|
+static void argv_fuse_set(char **argv, int argc)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ g.opts.argv_fuse = argv;
|
|
+ g.opts.argc_fuse = argc;
|
|
+
|
|
+ STDERR_PR("Fuse Options: ");
|
|
+ for (i = 0; i < argc; i++)
|
|
+ STDERR("%s ", g.opts.argv_fuse[i]);
|
|
+ STDERR("\n");
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Set action
|
|
+ */
|
|
+static void action_set(enum zg_action action)
|
|
+{
|
|
+ if (g.opts.action_specified)
|
|
+ ERR_EXIT("Please specifiy only one of the \"-i\", \"-d\", "
|
|
+ "\"-m\" or \"-u\" option");
|
|
+ g.opts.action = action;
|
|
+ g.opts.action_specified = 1;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Verify option combinations
|
|
+ */
|
|
+static void verify_opts(void)
|
|
+{
|
|
+ if (!g.opts.fmt_specified)
|
|
+ return;
|
|
+
|
|
+ if (g.opts.action == ZG_ACTION_DUMP_INFO)
|
|
+ ERR_EXIT("The \"--fmt\" option cannot be specified "
|
|
+ "together with \"--info\"");
|
|
+ if (g.opts.action == ZG_ACTION_DEVICE_INFO)
|
|
+ ERR_EXIT("The \"--fmt\" option cannot be specified "
|
|
+ "together with \"--device\"");
|
|
+ if (g.opts.action == ZG_ACTION_UMOUNT)
|
|
+ ERR_EXIT("The \"--fmt\" option cannot be specified "
|
|
+ "together with \"--umount\"");
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Parse positional arguments
|
|
+ */
|
|
+static void parse_pos_args(char *argv[], int argc)
|
|
+{
|
|
+ int pos_args = argc - optind;
|
|
+
|
|
+ switch (g.opts.action) {
|
|
+ case ZG_ACTION_STDOUT:
|
|
+ case ZG_ACTION_DUMP_INFO:
|
|
+ case ZG_ACTION_DEVICE_INFO:
|
|
+ if (pos_args == 0)
|
|
+ ERR_EXIT("No device or dump specified");
|
|
+ if (pos_args > 1 && !g.opts.debug_specified)
|
|
+ ERR_EXIT("Too many positional paramters specified");
|
|
+ device_set(argv[optind]);
|
|
+ break;
|
|
+ case ZG_ACTION_MOUNT:
|
|
+ if (pos_args == 0)
|
|
+ ERR_EXIT("No dump specified");
|
|
+ if (pos_args == 1)
|
|
+ ERR_EXIT("No mount point specified");
|
|
+ if (pos_args > 2 && !g.opts.debug_specified)
|
|
+ ERR_EXIT("Too many positional paramters specified");
|
|
+ device_set(argv[optind]);
|
|
+ mount_point_set(argv[optind + 1]);
|
|
+ if (g.opts.debug_specified && pos_args > 2)
|
|
+ argv_fuse_set(&argv[optind + 2], pos_args - 2);
|
|
+ break;
|
|
+ case ZG_ACTION_UMOUNT:
|
|
+ if (pos_args == 0)
|
|
+ ERR_EXIT("No mount point specified");
|
|
+ mount_point_set(argv[optind]);
|
|
+ break;
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Main command line parsing function
|
|
+ */
|
|
+void opts_parse(int argc, char *argv[])
|
|
+{
|
|
+ int opt, idx;
|
|
+ static struct option long_opts[] = {
|
|
+ {"help", no_argument, NULL, 'h'},
|
|
+ {"version", no_argument, NULL, 'v'},
|
|
+ {"info", no_argument, NULL, 'i'},
|
|
+ {"device", no_argument, NULL, 'd'},
|
|
+ {"mount", no_argument, NULL, 'm'},
|
|
+ {"umount", no_argument, NULL, 'u'},
|
|
+ {"fmt", required_argument, NULL, 'f'},
|
|
+ {"debug", no_argument, NULL, 'X'},
|
|
+ {0, 0, 0, 0 }
|
|
+ };
|
|
+ static const char optstr[] = "hvidmuf:X";
|
|
+
|
|
+ init_defaults();
|
|
+ while ((opt = getopt_long(argc, argv, optstr, long_opts, &idx)) != -1) {
|
|
+ switch (opt) {
|
|
+ case 'h':
|
|
+ print_help_exit();
|
|
+ case 'v':
|
|
+ print_version_exit();
|
|
+ case 'i':
|
|
+ action_set(ZG_ACTION_DUMP_INFO);
|
|
+ break;
|
|
+ case 'd':
|
|
+ action_set(ZG_ACTION_DEVICE_INFO);
|
|
+ break;
|
|
+ case 'm':
|
|
+ action_set(ZG_ACTION_MOUNT);
|
|
+ break;
|
|
+ case 'u':
|
|
+ action_set(ZG_ACTION_UMOUNT);
|
|
+ break;
|
|
+ case 'f':
|
|
+ fmt_set(optarg);
|
|
+ break;
|
|
+ case 'X':
|
|
+ g.opts.debug_specified = 1;
|
|
+ break;
|
|
+ default:
|
|
+ print_usage_exit();
|
|
+ }
|
|
+ }
|
|
+ parse_pos_args(argv, argc);
|
|
+ verify_opts();
|
|
+}
|
|
diff --git a/zdump/stdout.c b/zdump/stdout.c
|
|
new file mode 100644
|
|
index 0000000..1c3722d
|
|
--- /dev/null
|
|
+++ b/zdump/stdout.c
|
|
@@ -0,0 +1,38 @@
|
|
+/*
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
+ *
|
|
+ * Write dump to standard output (stdout)
|
|
+ *
|
|
+ * Copyright IBM Corp. 2001, 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include "zgetdump.h"
|
|
+
|
|
+int stdout_write_dump(void)
|
|
+{
|
|
+ u64 cnt, written = 0;
|
|
+ char buf[32768];
|
|
+ ssize_t rc;
|
|
+
|
|
+ if (!dfi_feat_copy())
|
|
+ ERR_EXIT("Copying not possible for %s dumps", dfi_name());
|
|
+ STDERR("Format Info:\n");
|
|
+ STDERR(" Source: %s\n", dfi_name());
|
|
+ STDERR(" Target: %s\n", dfo_name());
|
|
+ STDERR("\n");
|
|
+ zg_progress_init("Copying dump", dfo_size());
|
|
+ do {
|
|
+ cnt = dfo_read(buf, sizeof(buf));
|
|
+ rc = write(STDOUT_FILENO, buf, cnt);
|
|
+ if (rc == -1)
|
|
+ ERR_EXIT_ERRNO("Error: Write failed");
|
|
+ if (rc != (ssize_t) cnt)
|
|
+ ERR_EXIT("Error: Could not write full block");
|
|
+ written += cnt;
|
|
+ zg_progress(written);
|
|
+ } while (written != dfo_size());
|
|
+ STDERR("\n");
|
|
+ STDERR("Success: Dump has been copied\n");
|
|
+ return 0;
|
|
+}
|
|
diff --git a/zdump/zfuse.c b/zdump/zfuse.c
|
|
new file mode 100644
|
|
index 0000000..d86e1c0
|
|
--- /dev/null
|
|
+++ b/zdump/zfuse.c
|
|
@@ -0,0 +1,238 @@
|
|
+/*
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
+ *
|
|
+ * FUSE functions
|
|
+ *
|
|
+ * Copyright IBM Corp. 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#define FUSE_USE_VERSION 25
|
|
+
|
|
+#include <fuse.h>
|
|
+#include <stdio.h>
|
|
+#include <string.h>
|
|
+#include <errno.h>
|
|
+#include <fcntl.h>
|
|
+#include <unistd.h>
|
|
+#include <time.h>
|
|
+#include <limits.h>
|
|
+#include <sys/types.h>
|
|
+#include <sys/stat.h>
|
|
+#include "zgetdump.h"
|
|
+
|
|
+#define DUMP_PATH_MAX 100
|
|
+
|
|
+/*
|
|
+ * File local static data
|
|
+ */
|
|
+static struct {
|
|
+ char path[DUMP_PATH_MAX];
|
|
+ struct stat stat_root;
|
|
+ struct stat stat_dump;
|
|
+} l;
|
|
+
|
|
+/*
|
|
+ * Initialize default values for stat buffer
|
|
+ */
|
|
+static void stat_default_init(struct stat *stat)
|
|
+{
|
|
+ if (dfi_attr_time()) {
|
|
+ stat->st_mtime = dfi_attr_time()->tv_sec;
|
|
+ stat->st_ctime = dfi_attr_time()->tv_sec;
|
|
+ stat->st_atime = dfi_attr_time()->tv_sec;
|
|
+ } else {
|
|
+ stat->st_mtime = zg_stat(g.fh)->st_mtime;
|
|
+ stat->st_ctime = zg_stat(g.fh)->st_ctime;
|
|
+ stat->st_atime = zg_stat(g.fh)->st_atime;
|
|
+ }
|
|
+ stat->st_uid = geteuid();
|
|
+ stat->st_gid = getegid();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize stat buffer for root directory
|
|
+ */
|
|
+static void stat_root_init(void)
|
|
+{
|
|
+ stat_default_init(&l.stat_root);
|
|
+ l.stat_root.st_mode = S_IFDIR | 0500;
|
|
+ l.stat_root.st_nlink = 2;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize stat buffer for dump
|
|
+ */
|
|
+static void stat_dump_init(void)
|
|
+{
|
|
+ stat_default_init(&l.stat_dump);
|
|
+ l.stat_dump.st_mode = S_IFREG | 0400;
|
|
+ l.stat_dump.st_nlink = 1;
|
|
+ l.stat_dump.st_size = dfo_size();
|
|
+ l.stat_dump.st_blksize = 4096;
|
|
+ l.stat_dump.st_blocks = l.stat_dump.st_size / 4096;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * FUSE callback: Getattr
|
|
+ */
|
|
+static int zfuse_getattr(const char *path, struct stat *stat)
|
|
+{
|
|
+ if (strcmp(path, "/") == 0) {
|
|
+ *stat = l.stat_root;
|
|
+ return 0;
|
|
+ }
|
|
+ if (strcmp(path, l.path) == 0) {
|
|
+ *stat = l.stat_dump;
|
|
+ return 0;
|
|
+ }
|
|
+ return -ENOENT;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * FUSE callback: Readdir - Return ".", ".." and dump file
|
|
+ */
|
|
+static int zfuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
|
+ off_t offset, struct fuse_file_info *fi)
|
|
+{
|
|
+ (void) offset;
|
|
+ (void) fi;
|
|
+
|
|
+ if (strcmp(path, "/") != 0)
|
|
+ return -ENOENT;
|
|
+
|
|
+ filler(buf, ".", NULL, 0);
|
|
+ filler(buf, "..", NULL, 0);
|
|
+ filler(buf, &l.path[1], NULL, 0);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * FUSE callback: Open
|
|
+ */
|
|
+static int zfuse_open(const char *path, struct fuse_file_info *fi)
|
|
+{
|
|
+ if (strcmp(path, l.path) != 0)
|
|
+ return -ENOENT;
|
|
+ if ((fi->flags & 3) != O_RDONLY)
|
|
+ return -EACCES;
|
|
+ l.stat_dump.st_atime = time(NULL);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * FUSE callback: Read
|
|
+ */
|
|
+static int zfuse_read(const char *path, char *buf, size_t size, off_t offset,
|
|
+ struct fuse_file_info *fi)
|
|
+{
|
|
+ (void) fi;
|
|
+
|
|
+ if (strcmp(path, l.path) != 0)
|
|
+ return -ENOENT;
|
|
+ dfo_seek(offset);
|
|
+ dfo_read(buf, size);
|
|
+ return size;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * FUSE callback: Statfs
|
|
+ */
|
|
+static int zfuse_statfs(const char *path, struct statvfs *buf)
|
|
+{
|
|
+ (void) path;
|
|
+
|
|
+ buf->f_bsize = buf->f_frsize = 4096;
|
|
+ buf->f_blocks = dfo_size() / 4096;
|
|
+ buf->f_bfree = buf->f_bavail = 0;
|
|
+ buf->f_files = 1;
|
|
+ buf->f_ffree = 0;
|
|
+ buf->f_namemax = strlen(l.path) + 1;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * FUSE operations
|
|
+ */
|
|
+static struct fuse_operations zfuse_ops = {
|
|
+ .getattr = zfuse_getattr,
|
|
+ .readdir = zfuse_readdir,
|
|
+ .open = zfuse_open,
|
|
+ .read = zfuse_read,
|
|
+ .statfs = zfuse_statfs,
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Add additional FUSE arguments
|
|
+ */
|
|
+static void add_argv_fuse(struct fuse_args *args)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ if (g.opts.argc_fuse == 0)
|
|
+ return;
|
|
+ STDERR("Adding Fuse options: ");
|
|
+ for (i = 0; i < g.opts.argc_fuse; i++) {
|
|
+ STDERR("%s ", g.opts.argv_fuse[i]);
|
|
+ fuse_opt_add_arg(args, g.opts.argv_fuse[i]);
|
|
+ }
|
|
+ STDERR("\n");
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Mount dump
|
|
+ *
|
|
+ * Add additional FUSE options:
|
|
+ * - s....................: Disable multi-threaded operation
|
|
+ * - o fsname.............: File system name (used for umount)
|
|
+ * - o ro.................: Read only
|
|
+ * - o default_permissions: Enable permission checking by kernel
|
|
+ */
|
|
+int zfuse_mount_dump(void)
|
|
+{
|
|
+ struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
|
|
+ char tmp_str[PATH_MAX];
|
|
+
|
|
+ if (!dfi_feat_seek())
|
|
+ ERR_EXIT("Mounting not possible for %s dumps", dfi_name());
|
|
+ fuse_opt_add_arg(&args, "zgetdump");
|
|
+ fuse_opt_add_arg(&args, "-s");
|
|
+ snprintf(tmp_str, sizeof(tmp_str),
|
|
+ "-ofsname=%s,ro,default_permissions,nonempty",
|
|
+ g.opts.device);
|
|
+ fuse_opt_add_arg(&args, tmp_str);
|
|
+ fuse_opt_add_arg(&args, g.opts.mount_point);
|
|
+ add_argv_fuse(&args);
|
|
+ stat_root_init();
|
|
+ stat_dump_init();
|
|
+ snprintf(l.path, sizeof(l.path), "/dump.%s", dfo_name());
|
|
+ return fuse_main(args.argc, args.argv, &zfuse_ops);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Unmount dump
|
|
+ */
|
|
+void zfuse_umount(void)
|
|
+{
|
|
+ char umount_cmd[PATH_MAX];
|
|
+ char *umount_tool;
|
|
+ struct stat sbuf;
|
|
+ int rc;
|
|
+
|
|
+ if (stat("/usr/bin/fusermount", &sbuf) == 0)
|
|
+ umount_tool = "/usr/bin/fusermount -u";
|
|
+ else if (stat("/bin/fusermount", &sbuf) == 0)
|
|
+ umount_tool = "/bin/fusermount -u";
|
|
+ else
|
|
+ umount_tool = "umount";
|
|
+
|
|
+ snprintf(umount_cmd, sizeof(umount_cmd), "%s %s", umount_tool,
|
|
+ g.opts.mount_point);
|
|
+ rc = system(umount_cmd);
|
|
+
|
|
+ if (rc == -1)
|
|
+ ERR_EXIT_ERRNO("\"%s\" failed", umount_cmd);
|
|
+ if (rc > 0)
|
|
+ ERR_EXIT("\"%s\" failed", umount_cmd);
|
|
+ exit(0);
|
|
+}
|
|
diff --git a/zdump/zg.c b/zdump/zg.c
|
|
new file mode 100644
|
|
index 0000000..e249011
|
|
--- /dev/null
|
|
+++ b/zdump/zg.c
|
|
@@ -0,0 +1,411 @@
|
|
+/*
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
+ *
|
|
+ * Helper functions
|
|
+ *
|
|
+ * Copyright IBM Corp. 2001, 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#include <stdlib.h>
|
|
+#include <limits.h>
|
|
+#include "zgetdump.h"
|
|
+
|
|
+#define MAX_EXIT_FN 10
|
|
+#define MAX_DEV_RETRIES 1000
|
|
+
|
|
+/*
|
|
+ * Progress information
|
|
+ */
|
|
+struct prog {
|
|
+ u64 addr_next;
|
|
+ u64 mem_size;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * At exit information
|
|
+ */
|
|
+struct atexit {
|
|
+ zg_atexit_fn_t fn_vec[MAX_EXIT_FN];
|
|
+ unsigned int cnt;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * Temp devnode information
|
|
+ */
|
|
+struct devnode {
|
|
+ char **vec;
|
|
+ int cnt;
|
|
+};
|
|
+
|
|
+/*
|
|
+ * File local static data
|
|
+ */
|
|
+static struct {
|
|
+ struct atexit atexit;
|
|
+ struct prog prog;
|
|
+ struct devnode devnode;
|
|
+} l;
|
|
+
|
|
+/*
|
|
+ * Call all registered exit handlers
|
|
+ */
|
|
+static void exit_fn(void)
|
|
+{
|
|
+ unsigned int i;
|
|
+
|
|
+ for (i = 0; i < l.atexit.cnt; i++)
|
|
+ l.atexit.fn_vec[i]();
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Register exit handler
|
|
+ */
|
|
+void zg_atexit(zg_atexit_fn_t fn)
|
|
+{
|
|
+ if (l.atexit.cnt >= MAX_EXIT_FN)
|
|
+ ABORT("Too many atexit handlers (%d)", l.atexit.cnt + 1);
|
|
+ l.atexit.fn_vec[l.atexit.cnt] = fn;
|
|
+ if (l.atexit.cnt == 0)
|
|
+ atexit(exit_fn);
|
|
+ l.atexit.cnt++;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Exit function (For having exit gdb break point)
|
|
+ */
|
|
+void zg_exit(int rc)
|
|
+{
|
|
+ exit(rc);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Alloc memory and check for errors
|
|
+ */
|
|
+void *zg_alloc(unsigned int size)
|
|
+{
|
|
+ void *ptr = calloc(size, 1);
|
|
+ if (!ptr)
|
|
+ ERR_EXIT("Alloc: Out of memory (%i KiB)", TO_KIB(size));
|
|
+ return ptr;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Realloc memory and check for errors
|
|
+ */
|
|
+void *zg_realloc(void *ptr, unsigned int size)
|
|
+{
|
|
+ void *new_ptr = realloc(ptr, size);
|
|
+ if (!new_ptr)
|
|
+ ERR_EXIT("Realloc: Out of memory (%i KiB)", TO_KIB(size));
|
|
+ return new_ptr;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Create duplicate for string
|
|
+ */
|
|
+char *zg_strdup(const char *str)
|
|
+{
|
|
+ char *new_str = strdup(str);
|
|
+
|
|
+ if (!new_str)
|
|
+ ERR_EXIT("Strdup: Out of memory (%s)\n", str);
|
|
+ return new_str;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Free memory
|
|
+ */
|
|
+void zg_free(void *ptr)
|
|
+{
|
|
+ free(ptr);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return path name of open file
|
|
+ */
|
|
+const char *zg_path(struct zg_fh *zg_fh)
|
|
+{
|
|
+ return zg_fh->path;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return stat buffer of open file
|
|
+ */
|
|
+const struct stat *zg_stat(struct zg_fh *zg_fh)
|
|
+{
|
|
+ return &zg_fh->sb;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Open file
|
|
+ */
|
|
+struct zg_fh *zg_open(const char *path, int flags, enum zg_check check)
|
|
+{
|
|
+ struct zg_fh *zg_fh = zg_alloc(sizeof(*zg_fh));
|
|
+
|
|
+ zg_fh->fh = open(path, flags);
|
|
+ if (zg_fh->fh == -1) {
|
|
+ if (check == ZG_CHECK_NONE)
|
|
+ goto fail;
|
|
+ ERR_EXIT_ERRNO("Could not open \"%s\"", path);
|
|
+ }
|
|
+ if (stat(path, &zg_fh->sb) == -1) {
|
|
+ if (check == ZG_CHECK_NONE)
|
|
+ goto fail;
|
|
+ ERR_EXIT_ERRNO("Could not access file \"%s\"", path);
|
|
+ }
|
|
+ zg_fh->path = zg_strdup(path);
|
|
+ return zg_fh;
|
|
+
|
|
+fail:
|
|
+ zg_free(zg_fh);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Close file
|
|
+ */
|
|
+void zg_close(struct zg_fh *zg_fh)
|
|
+{
|
|
+ close(zg_fh->fh);
|
|
+ free(zg_fh);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Read file
|
|
+ */
|
|
+ssize_t zg_read(struct zg_fh *zg_fh, void *buf, size_t cnt, enum zg_check check)
|
|
+{
|
|
+ size_t copied = 0;
|
|
+ ssize_t rc;
|
|
+
|
|
+ do {
|
|
+ rc = read(zg_fh->fh, buf + copied, cnt - copied);
|
|
+ if (rc == -1) {
|
|
+ if (check == ZG_CHECK_NONE)
|
|
+ return rc;
|
|
+ ERR_EXIT_ERRNO("Could not read \"%s\"", zg_fh->path);
|
|
+ }
|
|
+ if (rc == 0) {
|
|
+ if (check != ZG_CHECK)
|
|
+ return copied;
|
|
+ ERR_EXIT("Unexpected end of file for \"%s\"",
|
|
+ zg_fh->path);
|
|
+ }
|
|
+ copied += rc;
|
|
+ } while (copied != cnt);
|
|
+ return copied;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return file size
|
|
+ */
|
|
+u64 zg_size(struct zg_fh *zg_fh)
|
|
+{
|
|
+ return zg_fh->sb.st_size;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return file position
|
|
+ */
|
|
+off_t zg_tell(struct zg_fh *zg_fh, enum zg_check check)
|
|
+{
|
|
+ off_t rc;
|
|
+
|
|
+ rc = lseek(zg_fh->fh, 0, SEEK_CUR);
|
|
+ if (rc == -1 && check != ZG_CHECK_NONE)
|
|
+ ERR_EXIT_ERRNO("Could not get file position for \"%s\"",
|
|
+ zg_fh->path);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Seek to "off" relative to END
|
|
+ */
|
|
+off_t zg_seek_end(struct zg_fh *zg_fh, off_t off, enum zg_check check)
|
|
+{
|
|
+ off_t rc;
|
|
+
|
|
+ rc = lseek(zg_fh->fh, off, SEEK_END);
|
|
+ if (rc == -1 && check != ZG_CHECK_NONE)
|
|
+ ERR_EXIT_ERRNO("Could not seek \"%s\"", zg_fh->path);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Seek to "off" in file
|
|
+ */
|
|
+off_t zg_seek(struct zg_fh *zg_fh, off_t off, enum zg_check check)
|
|
+{
|
|
+ off_t rc;
|
|
+
|
|
+ rc = lseek(zg_fh->fh, off, SEEK_SET);
|
|
+ if (rc == -1 && check != ZG_CHECK_NONE)
|
|
+ ERR_EXIT_ERRNO("Could not seek \"%s\"", zg_fh->path);
|
|
+ if (rc != off && check == ZG_CHECK)
|
|
+ ERR_EXIT("Could not seek \"%s\"", zg_fh->path);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Seek from current position
|
|
+ */
|
|
+off_t zg_seek_cur(struct zg_fh *zg_fh, off_t off, enum zg_check check)
|
|
+{
|
|
+ off_t rc;
|
|
+
|
|
+ rc = lseek(zg_fh->fh, off, SEEK_CUR);
|
|
+ if (rc == -1 && check != ZG_CHECK_NONE)
|
|
+ ERR_EXIT_ERRNO("Could not seek \"%s\"", zg_fh->path);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Do ioctl and exit in case of an error
|
|
+ */
|
|
+int zg_ioctl(struct zg_fh *zg_fh, int rq, void *data, const char *op,
|
|
+ enum zg_check check)
|
|
+{
|
|
+ int rc;
|
|
+
|
|
+ rc = ioctl(zg_fh->fh, rq, data);
|
|
+ if (rc == -1 && check != ZG_CHECK_NONE)
|
|
+ ERR_EXIT_ERRNO("Operation \"%s\" failed on \"%s\"", op,
|
|
+ zg_fh->path);
|
|
+ return rc;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Return file type
|
|
+ */
|
|
+enum zg_type zg_type(struct zg_fh *zg_fh)
|
|
+{
|
|
+ struct mtop mtop;
|
|
+ struct stat *sb = &zg_fh->sb;
|
|
+
|
|
+ if (S_ISREG(sb->st_mode))
|
|
+ return ZG_TYPE_FILE;
|
|
+ if (S_ISBLK(sb->st_mode)) {
|
|
+ if (minor(sb->st_rdev) % 4 == 0)
|
|
+ return ZG_TYPE_DASD;
|
|
+ else
|
|
+ return ZG_TYPE_DASD_PART;
|
|
+ }
|
|
+ if (S_ISCHR(sb->st_mode)) {
|
|
+ mtop.mt_count = 1;
|
|
+ mtop.mt_op = MTTELL;
|
|
+ if (zg_ioctl(zg_fh, MTIOCTOP, &mtop, "MTIOCTOP",
|
|
+ ZG_CHECK_NONE) != -1)
|
|
+ return ZG_TYPE_TAPE;
|
|
+ }
|
|
+ return ZG_TYPE_UNKNOWN;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Initialize progress messages
|
|
+ */
|
|
+void zg_progress_init(const char *msg, u64 mem_size)
|
|
+{
|
|
+ STDERR("%s:\n", msg);
|
|
+ l.prog.addr_next = 0;
|
|
+ l.prog.mem_size = mem_size;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Print progress
|
|
+ */
|
|
+void zg_progress(u64 addr)
|
|
+{
|
|
+ if (addr < l.prog.addr_next)
|
|
+ return;
|
|
+
|
|
+ STDERR(" %08Lu / %08Lu MB\n", TO_MIB(addr), TO_MIB(l.prog.mem_size));
|
|
+ l.prog.addr_next += l.prog.mem_size / 6;
|
|
+ l.prog.addr_next = MIN(l.prog.addr_next, l.prog.mem_size);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Try to create device node in "dir"
|
|
+ */
|
|
+static char *devnode_create_dir(const char *dir, dev_t dev)
|
|
+{
|
|
+ char file_path[PATH_MAX];
|
|
+ int i, fh, rc;
|
|
+
|
|
+ for (i = 0; i < MAX_DEV_RETRIES; i++) {
|
|
+ snprintf(file_path, PATH_MAX, "%s/zgetdump.%04d", dir, i);
|
|
+ rc = mknod(file_path, S_IFBLK | S_IRWXU, dev);
|
|
+ if (rc == -1) {
|
|
+ if (errno == EEXIST)
|
|
+ continue;
|
|
+ else
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ /* Need this test to cover 'nodev'-mounted filesystems */
|
|
+ fh = open(file_path, O_RDWR);
|
|
+ if (fh == -1) {
|
|
+ remove(file_path);
|
|
+ break;
|
|
+ }
|
|
+ close(fh);
|
|
+ return zg_strdup(file_path);
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Delete temporary device node
|
|
+ */
|
|
+static void devnode_remove(char *dev_node)
|
|
+{
|
|
+ if (remove(dev_node))
|
|
+ ERR("Warning: Could not remove temporary file %s: %s",
|
|
+ dev_node, strerror(errno));
|
|
+ zg_free(dev_node);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Remove all temporary device nodes
|
|
+ */
|
|
+static void devnode_remove_all(void)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < l.devnode.cnt; i++)
|
|
+ devnode_remove(l.devnode.vec[i]);
|
|
+ if (l.devnode.vec) {
|
|
+ zg_free(l.devnode.vec);
|
|
+ l.devnode.vec = NULL;
|
|
+ }
|
|
+ l.devnode.cnt = 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Make temporary device node for input dev identified by its dev_t
|
|
+ */
|
|
+char *zg_devnode_create(dev_t dev)
|
|
+{
|
|
+ char *dir_vec[] = {getenv("TMPDIR"), "/tmp", getenv("HOME"), ".", "/"};
|
|
+ char *file_path;
|
|
+ unsigned int i;
|
|
+
|
|
+ for (i = 0; i < ARRAY_ELEMENT_CNT(dir_vec); i++) {
|
|
+ if (dir_vec[i] == NULL)
|
|
+ continue;
|
|
+ file_path = devnode_create_dir(dir_vec[i], dev);
|
|
+ if (file_path)
|
|
+ goto found;
|
|
+ }
|
|
+ ERR_EXIT_ERRNO("Unable to create temporary dev node");
|
|
+ return NULL;
|
|
+found:
|
|
+ l.devnode.cnt++;
|
|
+ l.devnode.vec = zg_realloc(l.devnode.vec, l.devnode.cnt *
|
|
+ sizeof(void *));
|
|
+ l.devnode.vec[l.devnode.cnt - 1] = file_path;
|
|
+ if (l.devnode.cnt == 1)
|
|
+ zg_atexit(devnode_remove_all);
|
|
+ return file_path;
|
|
+}
|
|
diff --git a/zdump/zg.h b/zdump/zg.h
|
|
new file mode 100644
|
|
index 0000000..2532146
|
|
--- /dev/null
|
|
+++ b/zdump/zg.h
|
|
@@ -0,0 +1,185 @@
|
|
+/*
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
+ *
|
|
+ * Helper functions
|
|
+ *
|
|
+ * Copyright IBM Corp. 2001, 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ */
|
|
+
|
|
+#ifndef ZG_H
|
|
+#define ZG_H
|
|
+
|
|
+#include <errno.h>
|
|
+#include <string.h>
|
|
+#include <unistd.h>
|
|
+#include <stdlib.h>
|
|
+#include <stdio.h>
|
|
+#include <fcntl.h>
|
|
+#include <assert.h>
|
|
+#include <sys/ioctl.h>
|
|
+#include <sys/types.h>
|
|
+#include <sys/stat.h>
|
|
+#include <sys/mtio.h>
|
|
+#include "zt_common.h"
|
|
+
|
|
+#define U64_MAX ((u64) -1)
|
|
+#define U32_MAX ((u32) -1)
|
|
+#define U16_MAX ((u16) -1)
|
|
+#define U8_MAX ((u8) -1)
|
|
+
|
|
+/*
|
|
+ * IEC definitions
|
|
+ */
|
|
+#define KIB_DIFF (1024)
|
|
+#define MIB_DIFF (1024 * 1024)
|
|
+#define GIB_DIFF (1024 * 1024 * 1024)
|
|
+
|
|
+#define TO_MIB(x) ((x + (MIB_DIFF / 2)) / MIB_DIFF)
|
|
+#define TO_KIB(x) ((x + (KIB_DIFF / 2)) / KIB_DIFF)
|
|
+
|
|
+/*
|
|
+ * Memory functions
|
|
+ */
|
|
+extern void *zg_alloc(unsigned int size);
|
|
+extern void *zg_realloc(void *ptr, unsigned int size);
|
|
+extern void zg_free(void *ptr);
|
|
+extern char *zg_strdup(const char *str);
|
|
+
|
|
+/*
|
|
+ * At exit functions
|
|
+ */
|
|
+typedef void (*zg_atexit_fn_t)(void);
|
|
+extern void zg_atexit(zg_atexit_fn_t fn);
|
|
+extern void zg_exit(int rc) __attribute__ ((__noreturn__));
|
|
+
|
|
+/*
|
|
+ * Temporary device node functions
|
|
+ */
|
|
+extern char *zg_devnode_create(dev_t dev);
|
|
+
|
|
+/*
|
|
+ * Progress bar functions
|
|
+ */
|
|
+extern void zg_progress_init(const char *msg, u64 mem_size);
|
|
+extern void zg_progress(u64 addr);
|
|
+
|
|
+/*
|
|
+ * Error and print functions
|
|
+ */
|
|
+#define ERR(x...) \
|
|
+do { \
|
|
+ fprintf(stderr, "%s: ", "zgetdump"); \
|
|
+ fprintf(stderr, x); \
|
|
+ fprintf(stderr, "\n"); \
|
|
+} while (0)
|
|
+
|
|
+#define ERR_EXIT(x...) \
|
|
+do { \
|
|
+ ERR(x); \
|
|
+ zg_exit(1); \
|
|
+} while (0)
|
|
+
|
|
+#define ABORT(x...) \
|
|
+do { \
|
|
+ ERR("Internal Error: " x); \
|
|
+ abort(); \
|
|
+} while (0)
|
|
+
|
|
+#define ERR_EXIT_ERRNO(x...) \
|
|
+ do { \
|
|
+ fflush(stdout); \
|
|
+ fprintf(stderr, "%s: ", "zgetdump"); \
|
|
+ fprintf(stderr, x); \
|
|
+ fprintf(stderr, " (%s)", strerror(errno)); \
|
|
+ fprintf(stderr, "\n"); \
|
|
+ zg_exit(1); \
|
|
+ } while (0)
|
|
+
|
|
+#define STDERR(x...) \
|
|
+do { \
|
|
+ fprintf(stderr, x); \
|
|
+ fflush(stderr); \
|
|
+} while (0)
|
|
+
|
|
+#define STDERR_PR(x...) \
|
|
+do { \
|
|
+ fprintf(stderr, "\r%s: ", "zgetdump"); \
|
|
+ fprintf(stderr, x); \
|
|
+} while (0)
|
|
+
|
|
+#define STDOUT(x...) \
|
|
+do { \
|
|
+ fprintf(stdout, x); \
|
|
+ fflush(stdout); \
|
|
+} while (0)
|
|
+
|
|
+/*
|
|
+ * Misc
|
|
+ */
|
|
+#define PAGE_SIZE 4096
|
|
+#define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a)-1)
|
|
+#define __ALIGN_MASK(x, mask) (((x) + (mask)) & ~(mask))
|
|
+#define PAGE_ALIGN(addr) ALIGN(addr, PAGE_SIZE)
|
|
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
|
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
|
|
+#define ARRAY_ELEMENT_CNT(x) (sizeof(x) / sizeof(x[0]))
|
|
+#define ROUNDUP(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
|
|
+
|
|
+/*
|
|
+ * Pointer atrithmetic
|
|
+ */
|
|
+#define PTR_SUB(x, y) (((char *) (x)) - ((unsigned long) (y)))
|
|
+#define PTR_ADD(x, y) (((char *) (x)) + ((unsigned long) (y)))
|
|
+#define PTR_DIFF(x, y) ((unsigned long)(((char *) (x)) - ((unsigned long) (y))))
|
|
+
|
|
+/*
|
|
+ * File functions
|
|
+ */
|
|
+struct zg_fh {
|
|
+ const char *path;
|
|
+ int fh;
|
|
+ struct stat sb;
|
|
+};
|
|
+
|
|
+enum zg_type {
|
|
+ ZG_TYPE_DASD,
|
|
+ ZG_TYPE_DASD_PART,
|
|
+ ZG_TYPE_FILE,
|
|
+ ZG_TYPE_TAPE,
|
|
+ ZG_TYPE_UNKNOWN,
|
|
+};
|
|
+
|
|
+enum zg_check {
|
|
+ ZG_CHECK,
|
|
+ ZG_CHECK_ERR,
|
|
+ ZG_CHECK_NONE,
|
|
+};
|
|
+
|
|
+extern const char *zg_path(struct zg_fh *zg_fh);
|
|
+extern const struct stat *zg_stat(struct zg_fh *zg_fh);
|
|
+extern struct zg_fh *zg_open(const char *path, int flags, enum zg_check check);
|
|
+extern void zg_close(struct zg_fh *zg_fh);
|
|
+extern ssize_t zg_read(struct zg_fh *zg_fh, void *buf, size_t cnt,
|
|
+ enum zg_check check);
|
|
+extern u64 zg_size(struct zg_fh *zg_fh);
|
|
+extern off_t zg_tell(struct zg_fh *zg_fh, enum zg_check check);
|
|
+extern off_t zg_seek(struct zg_fh *zg_fh, off_t off, enum zg_check check);
|
|
+extern off_t zg_seek_end(struct zg_fh *zg_fh, off_t off, enum zg_check check);
|
|
+extern off_t zg_seek_cur(struct zg_fh *zg_fh, off_t off, enum zg_check check);
|
|
+extern int zg_ioctl(struct zg_fh *zg_fh, int rq, void *data, const char *op,
|
|
+ enum zg_check check);
|
|
+extern enum zg_type zg_type(struct zg_fh *zg_fh);
|
|
+
|
|
+/*
|
|
+ * zgetdump actions
|
|
+ */
|
|
+enum zg_action {
|
|
+ ZG_ACTION_STDOUT,
|
|
+ ZG_ACTION_DUMP_INFO,
|
|
+ ZG_ACTION_DEVICE_INFO,
|
|
+ ZG_ACTION_MOUNT,
|
|
+ ZG_ACTION_UMOUNT,
|
|
+};
|
|
+
|
|
+#endif /* ZG_H */
|
|
diff --git a/zdump/zgetdump.8 b/zdump/zgetdump.8
|
|
index 81ea801..a9230b4 100644
|
|
--- a/zdump/zgetdump.8
|
|
+++ b/zdump/zgetdump.8
|
|
@@ -1,69 +1,317 @@
|
|
-.TH ZGETDUMP 8 "Apr 2006" "s390-tools"
|
|
+.TH ZGETDUMP 8 "Jan 2010" "s390-tools"
|
|
.SH NAME
|
|
-zgetdump \- tool for copying dumps.
|
|
+zgetdump \- Tool for copying and converting System z dumps
|
|
.SH SYNOPSIS
|
|
-\fBzgetdump\fR [-d] [-h] [-i] [-a] [-v] \fIdumpdevice\fR
|
|
+\fBzgetdump\fR [OPTIONS] [DUMP/DUMPDEV] [DIR]
|
|
.SH DESCRIPTION
|
|
-\fBzgetdump\fR takes as input the dump device and writes its contents
|
|
-to standard output, which you can redirect to a specific file.
|
|
-.br
|
|
-\fBzgetdump\fR can also check, whether a DASD device contains a valid dumper.
|
|
+The \fBzgetdump\fR tool reads or converts a dump. The dump can be located
|
|
+either on a dump device or on a file system. By default the dump content is
|
|
+written to standard output, which you can redirect to a specific file. You
|
|
+can also mount the dump content, print dump information, or check
|
|
+whether a DASD device contains a valid dump tool.
|
|
.SH OPTIONS
|
|
.TP
|
|
-\fB-d\fR
|
|
-Check DASD device \fIdumpdevice\fR for valid dumper.
|
|
+.BR "\-h" " or " "\-\-help"
|
|
+Print usage information, then exit.
|
|
+
|
|
+.TP
|
|
+.BR "\-v" " or " "\-\-version"
|
|
+Print version information, then exit.
|
|
+
|
|
+.TP
|
|
+.BR "\-m <DUMP> <DIR>" " or " "\-\-mount <DUMP> <DIR>"
|
|
+Mount the DUMP to mount point DIR and generate a virtual target
|
|
+dump file instead of writing the content to standard output. The virtual dump
|
|
+file gets the name "dump.FMT", where FMT is the name of the specified
|
|
+dump format (see "--fmt" option).
|
|
+
|
|
+.TP
|
|
+.BR "\-u <DIR>" " or " "\-\-umount <DIR>"
|
|
+Unmount the dump that is mounted at mount point DIR. This option is a wrapper
|
|
+for "fusermount -u". Instead of DIR also the the DUMP (e.g. /dev/dasdd1)
|
|
+can be specified.
|
|
+
|
|
+.TP
|
|
+.BR "\-d <DUMPDEV>" " or " "\-\-device <DUMPDEV>"
|
|
+Check DASD device DUMPDEV for valid dump tool and print information about it.
|
|
+
|
|
+.TP
|
|
+.BR "\-i <DUMP>" " or " "\-\-info <DUMP>"
|
|
+Print the dump header information reading from the DUMP and check if
|
|
+the dump is valid. See chapter DUMP INFORMATION below for more information.
|
|
+.TP
|
|
+.BR "\-f <FMT>" " or " "\-\-fmt <FMT>"
|
|
+Use the specified target dump format FMT when writing or mounting the dump.
|
|
+The following target dump formats are supported:
|
|
+
|
|
+.BR "- elf:"
|
|
+Executable and Linking Format core dump (64 bit only)
|
|
+
|
|
+.BR "- s390:"
|
|
+s390 dump (default)
|
|
+
|
|
+.TP
|
|
+\fBDUMP\fR
|
|
+This parameter specifies the file, partition or tape device node where the
|
|
+dump is located:
|
|
+.TP
|
|
+.BR
|
|
+- Regular dump file (e.g. /testdir/dump.0)
|
|
+.TP
|
|
+.BR
|
|
+- DASD partition device node (e.g. /dev/dasdc1)
|
|
+.TP
|
|
+.BR
|
|
+- DASD device node for multi-volume dump (e.g. /dev/dasdc)
|
|
+.TP
|
|
+.BR
|
|
+- Tape device node (e.g. /dev/ntibm0)
|
|
+
|
|
+Note: For DASD multi-volume dump it is sufficient to specify only one of the
|
|
+multi-volume DASD partitions as DUMP.
|
|
+
|
|
+.TP
|
|
+\fBDUMPDEV\fR
|
|
+When using the "--device" option, DUMPDEV must be the DASD device node of
|
|
+the dump disk that should be verified.
|
|
+
|
|
+.SH COPY DUMP
|
|
+The default action of zgetdump is to copy the DUMP to standard output. Read
|
|
+the examples section below for more information.
|
|
+
|
|
+.SH MOUNT DUMP
|
|
+Instead of writing the dump content to standard output you can also mount the
|
|
+dump using the "--mount" option. With that option it is possible to convert
|
|
+the dump without the need of copying it. The zgetdump tool generates a
|
|
+virtual target dump file that contains the dump in the requested target
|
|
+format. The virtual dump file is generated by mounting the source dump as a
|
|
+user space file system to the directory specified by the "--mount" option.
|
|
+The virtual target dump file is called dump.<FMT> where FMT denotes
|
|
+the format of the target dump. The virtual dump file exists as long as the
|
|
+directory containing the file is not unmounted.
|
|
+
|
|
+Mounting can be useful when you want to process the dump with a tool that
|
|
+cannot read the original dump format. To do this, mount the dump and
|
|
+specify the required target dump format with the "--fmt" option. Mounting is
|
|
+also for useful for multi-volume DASD dumps. After a multi-volume dump has been
|
|
+mounted, it is shown as a single dump file that can be accessed directly with
|
|
+dump processing tools like "makedumpfile", "crash" or "lcrash".
|
|
+
|
|
+Mounting is implemented with "fuse" (file system in user space). Therefore the
|
|
+"fuse" kernel module must to be loaded on the system before the "--mount"
|
|
+option can be used.
|
|
+
|
|
+A DASD dump can be mounted e.g. with "zgetdump /dev/dasdd1 -m
|
|
+/mnt" and unmounted with either "zgetdump -u /mnt", "fusermount -u /mnt" or
|
|
+"umount /mnt" (root only).
|
|
+
|
|
+.SH DUMP FORMATS
|
|
+zgetdump supports the following dump formats:
|
|
+.TP
|
|
+.BR "s390"
|
|
+This dump format is System z specific and is used for DASD and tape dumps.
|
|
+.TP
|
|
+.BR "elf"
|
|
+Executable and Linking Format core dump. This dump format is also used for
|
|
+Linux user space core dumps. The zgetdump tool supports this dump format only
|
|
+for 64 bit.
|
|
+.TP
|
|
+.BR "lkcd"
|
|
+This dump format has been used by the Linux Kernel Crash Dumps (LKCD) project
|
|
+and is used on System z for the vmconvert and zfcp (SCSI) dump tool. The
|
|
+zgetdump tool supports "lkcd" only as source format.
|
|
+
|
|
+.TP
|
|
+The default target format of zgetdump is "s390". Use the "--fmt" option to change the target format.
|
|
+
|
|
+.SH DUMP INFORMATION
|
|
+When calling zgetdump with the "--info" option depending on the dump format
|
|
+the following dump attributes are available:
|
|
+.TP
|
|
+.BR "Dump format"
|
|
+Name of the dump format.
|
|
+.TP
|
|
+.BR Version
|
|
+Version number of the dump format.
|
|
+.TP
|
|
+.BR "Dump created/ended"
|
|
+Time when the dump process was started or ended. The dump time information is
|
|
+printed in your local time zone. E.g. "Wed, 03 Feb 2010 10:47:37 +0100" shows
|
|
+the time at your location. The meaning of "+0100" is that your time zone is one
|
|
+hour behind GMT (Greenwich Mean Time). You can use the "TZ" environment
|
|
+variable or use the "tzselect" tool to change the time zone. For example, if you
|
|
+know that the dump has been created in Hawaii, you can get the correct
|
|
+time information with:
|
|
+.br
|
|
+
|
|
+# TZ='Pacific/Honolulu' zgetdump -i DUMP
|
|
+.TP
|
|
+.BR "Dump CPU ID"
|
|
+Identifier of the CPU that executed the dump tool.
|
|
+.TP
|
|
+.BR "Build arch"
|
|
+Architecture (s390 or s390x) on which the dump tool was built.
|
|
+.TP
|
|
+.BR "System arch"
|
|
+Architecture (s390 or s390x) of the dumped Linux system.
|
|
+.TP
|
|
+.BR "CPU count (online)"
|
|
+Number of online CPUs.
|
|
+.TP
|
|
+.BR "CPU count (real)"
|
|
+Number of total CPUs (online and offline).
|
|
+.TP
|
|
+.BR "Dump memory range"
|
|
+Memory range that was dumped. This value is the difference between the last
|
|
+dumped and the first dumped memory address.
|
|
+.TP
|
|
+.BR "Real memory range"
|
|
+Memory range that was available on system. This value is the difference
|
|
+between the last and the first memory address of the dumped system.
|
|
+The "real memory range" can differ from the "dump memory range" when
|
|
+the SIZE parameter was used when preparing the dump device with the zipl
|
|
+tool (see man zipl).
|
|
+.TP
|
|
+.BR "Memory map"
|
|
+Available memory chunks in the dump. Depending on the dump tool there
|
|
+can be multiple memory chunks, when a system with memory holes is dumped.
|
|
+
|
|
+.SH DUMP DEVICE INFORMATION
|
|
+When calling zgetdump with the "--device" option depending on the dump tool
|
|
+the following attributes are available:
|
|
+.TP
|
|
+.BR "Dump tool"
|
|
+Name of the dump tool.
|
|
+.TP
|
|
+.BR "Version"
|
|
+Version of the dump tool.
|
|
.TP
|
|
-\fB-h\fR
|
|
-Print usage and exit.
|
|
+.BR "Architecture"
|
|
+Architecture (s390 or s390x) of the dump tool.
|
|
.TP
|
|
-\fB-i\fR
|
|
-Print the dump header information reading from the \fIdumpdevice\fR and
|
|
-check if the dump is valid.
|
|
+.BR "DASD type"
|
|
+Type of the DASD where the dump tool is installed (ECKD or FBA).
|
|
.TP
|
|
-\fB-i -a\fR
|
|
-Print the dump header information and check if the dump is valid when
|
|
-\fIdumpdevice\fR is a multi-volume tape.
|
|
-(Mount and check all cartridges in sequence.)
|
|
+.BR "Dump size limit"
|
|
+If this attribute is set, the dump tool will dump memory only up to that
|
|
+limit even if there is more memory available.
|
|
.TP
|
|
-\fB-v\fR
|
|
-Output version information and exit.
|
|
+.BR "Force specified"
|
|
+If that attribute is set to "yes", the multi-volume DASD dump tool will not
|
|
+verify the dump signature on dump partitions. This can be useful, if the dump
|
|
+partition is also used for swap.
|
|
+
|
|
+.SH EXAMPLES
|
|
.TP
|
|
-\fBdumpdevice\fR
|
|
-This parameter specifies the device or partition where the dump is located.
|
|
-.SH EXAMPLE
|
|
-1. Scenario: DASD partition /dev/dasdx1 was prepared for dump by means of
|
|
+.B Copy single volume DASD dump
|
|
+
|
|
+The DASD partition /dev/dasdx1 was prepared for dump with:
|
|
.br
|
|
- zipl -d /dev/dasdx1
|
|
+
|
|
+ # zipl -d /dev/dasdx1
|
|
+
|
|
.br
|
|
-The corresponding single-volume dump tool was IPLed.
|
|
-.RB "The respective " "zgetdump " "call to copy the dump from the DASD
|
|
-partition to file dump_file is:
|
|
+The corresponding single-volume dump tool was IPLed. The respective zgetdump
|
|
+call to copy the dump from the DASD partition to file dump.s390 is:
|
|
.br
|
|
|
|
- zgetdump /dev/dasdx1 > dump_file
|
|
+ # zgetdump /dev/dasdx1 > dump.s390
|
|
|
|
-2. Scenario: DASD partitions /dev/dasdx1 and /dev/dasdy1 contained in file
|
|
-dump_list_conf were prepared for dump by means of
|
|
+.TP
|
|
+.B Copy multi-volume DASD dump
|
|
+
|
|
+DASD partitions /dev/dasdx1 and /dev/dasdy1 contained in file dev_list.conf
|
|
+were prepared for multi-volume dump with:
|
|
.br
|
|
- zipl -M dump_list_conf
|
|
+
|
|
+ # zipl -M dev_list.conf
|
|
+
|
|
.br
|
|
-The corresponding multi-volume dump tool was IPLed.
|
|
-.RB "The respective " "zgetdump " "call to copy the dump from the DASD
|
|
-partitions to file dump_file is:
|
|
+The corresponding multi-volume dump tool was IPLed. The respective zgetdump
|
|
+call to copy the dump from the DASD partitions to file dump.s390 is:
|
|
.br
|
|
|
|
- zgetdump /dev/dasdx > dump_file or equivalent
|
|
+ # zgetdump /dev/dasdx > dump.s390
|
|
+
|
|
.br
|
|
- zgetdump /dev/dasdy > dump_file
|
|
+.TP
|
|
+.B Copy tape dump
|
|
|
|
-3. Scenario: Tape device /dev/ntibm0 was prepared for dump by means of
|
|
+Tape device /dev/ntibm0 was prepared with:
|
|
.br
|
|
- zipl -d /dev/ntibm0
|
|
+
|
|
+ # zipl -d /dev/ntibm0
|
|
+
|
|
+.br
|
|
+The corresponding tape dump tool was IPLed. The respective zgetdump call to
|
|
+copy the dump from the tape to file dump.s390 is:
|
|
+.br
|
|
+
|
|
+ # zgetdump /dev/ntibm0 > dump.s390
|
|
+
|
|
+.br
|
|
+.TP
|
|
+.B Using pipes for network transfer
|
|
+
|
|
+You can redirect standard output to tools like ftp or ssh in order to
|
|
+transfer the dump over the network without copying it into the file system
|
|
+first.
|
|
+
|
|
+Copy DASD dump using ssh:
|
|
+.br
|
|
+
|
|
+ # zgetdump /dev/dasdd1 | ssh user@host "cat > dump.s390"
|
|
+
|
|
.br
|
|
-The corresponding tape dump tool was IPLed.
|
|
-.RB "The respective " "zgetdump " "call to copy the dump from the tape
|
|
-to file dump_file is:
|
|
+Copy and compress DASD dump using ftp and gzip (note that not all ftp clients
|
|
+can do this):
|
|
.br
|
|
|
|
- zgetdump /dev/ntibm0 > dump_file
|
|
+ # ftp host
|
|
+ ftp> put |"zgetdump /dev/dasdd1 | gzip" dump.s390.gz
|
|
|
|
+.br
|
|
+The same effect can also be achieved by using the "--mount" option and run
|
|
+scp or ftp directly on the mounted virtual dump file.
|
|
+
|
|
+.TP
|
|
+.B Using the "--mount" option
|
|
+
|
|
+Mount multi-volume DASD dump, process it with the "crash" tool and unmout
|
|
+it with zgetdump afterwards.
|
|
+.br
|
|
+
|
|
+ # zgetdump -m -f elf /dev/dasdx /dumps
|
|
+ # crash vmlinux /dumps/dump.elf
|
|
+ # zgetdump -u /dumps
|
|
+
|
|
+.br
|
|
+Convert an ELF dump to an s390 dump by mounting it with the "--fmt" option,
|
|
+process it with lcrash and unmount it with fusermount afterwards.
|
|
+.br
|
|
+
|
|
+ # zgetdump -m -f s390 dump.elf /dumps
|
|
+ # lcrash System.map /dumps/dump.s390 Kerntypes
|
|
+ # fusermount -u /dumps
|
|
+
|
|
+.br
|
|
+.TP
|
|
+.B Print dump information (--info)
|
|
+
|
|
+Print information on DASD dump on /dev/dasdd1:
|
|
+.br
|
|
+
|
|
+ # zgetdump -i /dev/dasdd1
|
|
+
|
|
+.br
|
|
+.TP
|
|
+.B Print DASD dump tool information (--device)
|
|
+
|
|
+Print information on DASD dump tool on /dev/dasdd:
|
|
+.br
|
|
+
|
|
+ # zgetdump -d /dev/dasdd
|
|
+
|
|
+.br
|
|
+.SH SEE ALSO
|
|
+.BR zipl (8), crash (8), lcrash (8), dumpconf (8), vmconvert (8), vmur (8)
|
|
diff --git a/zdump/zgetdump.c b/zdump/zgetdump.c
|
|
index 1ef312c..b3db463 100644
|
|
--- a/zdump/zgetdump.c
|
|
+++ b/zdump/zgetdump.c
|
|
@@ -1,1360 +1,159 @@
|
|
/*
|
|
- * zgetdump
|
|
- * Description: The zgetdump tool takes as input the dump device
|
|
- * and writes its contents to standard output,
|
|
- * which you can redirect to a specific file.
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
*
|
|
- * Copyright IBM Corp. 2001, 2006.
|
|
- * Author(s): Despina Papadopoulou
|
|
- * Frank Munzert <munzert@de.ibm.com>
|
|
+ * Main functions
|
|
+ *
|
|
+ * Copyright IBM Corp. 2001, 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ * Frank Munzert <munzert@de.ibm.com>
|
|
+ * Despina Papadopoulou
|
|
*/
|
|
|
|
-#include "zgetdump.h"
|
|
-#include "zt_common.h"
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
-#include <sys/ioctl.h>
|
|
-#include <sys/types.h>
|
|
-#include <sys/stat.h>
|
|
-#include <sys/time.h>
|
|
-#include <sys/utsname.h>
|
|
#include <fcntl.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
-#include <getopt.h>
|
|
#include <limits.h>
|
|
-#include <dirent.h>
|
|
#include <errno.h>
|
|
+#include <signal.h>
|
|
+#include <sys/ioctl.h>
|
|
+#include <sys/types.h>
|
|
+#include <sys/stat.h>
|
|
+#include <sys/time.h>
|
|
#include <sys/mtio.h>
|
|
+#include <linux/fs.h>
|
|
+#include "zgetdump.h"
|
|
|
|
-/* from linux/fs.h */
|
|
-#define BLKSSZGET _IO(0x12,104)
|
|
-#define BLKFLSBUF _IO(0x12,97)
|
|
-
|
|
-#define HEADER_SIZE 4096
|
|
-#define BLOCK_SIZE 32768
|
|
-#define MVDUMPER_SIZE 4096
|
|
-#define PARTN_MASK ((1 << 2) - 1)
|
|
-#define MAGIC_BLOCK_OFFSET_ECKD 3
|
|
-#define MAGIC_OFFSET_FBA -0x1000
|
|
-#define HEXINSTR "\x0d\x10\x47\xf0" /* BASR + 1st halfword of BC */
|
|
-#define ARCH_S390 1
|
|
-#define ARCH_S390X 2
|
|
-#define VERSION_NO_DUMP_DEVICE -1
|
|
-
|
|
-#define SYSFS_BUSDIR "/sys/bus/ccw/devices"
|
|
-
|
|
-#if defined(__s390x__)
|
|
- #define FMT64 "l"
|
|
-#else
|
|
- #define FMT64 "ll"
|
|
-#endif
|
|
-
|
|
-/* definitions */
|
|
-
|
|
-char *help_text =
|
|
-"The zgetdump tool takes as input the dump device and writes its contents\n"\
|
|
-"to standard output, which you can redirect to a specific file.\n"\
|
|
-"zgetdump can also check, whether a DASD device contains a valid dumper.\n\n"\
|
|
-"Usage:\n"\
|
|
-"Copy dump from <dumpdevice> to stdout:\n"\
|
|
-" > zgetdump <dumpdevice>\n"\
|
|
-"Print dump header and check if dump is valid - for single tape or DASD:\n"\
|
|
-" > zgetdump [-i | --info] <dumpdevice>\n"\
|
|
-"Print dump header and check if dump is valid - for all volumes of a\n"
|
|
-"multi-volume tape dump:\n"\
|
|
-" > zgetdump [-i | --info] [-a | --all] <dumpdevice>\n"\
|
|
-"Check dump device:\n"\
|
|
-" > zgetdump [-d | --device] <dasd_device>\n"\
|
|
-"Print version info:\n"\
|
|
-" > zgetdump [-v | --version]\n"\
|
|
-"Print this text:\n"\
|
|
-" > zgetdump [-h | --help]\n\n"\
|
|
-"Examples for single-volume DASD:\n"\
|
|
-"> zgetdump -d /dev/dasdc\n"\
|
|
-"> zgetdump -i /dev/dasdc1\n"\
|
|
-"> zgetdump /dev/dasdc1 > dump_file\n";
|
|
-
|
|
-char *usage_note =
|
|
-"Usage:\n"\
|
|
-"> zgetdump <dumpdevice>\n"\
|
|
-"> zgetdump -i <dumpdevice>\n"\
|
|
-"> zgetdump -i -a <dumpdevice>\n"\
|
|
-"> zgetdump -d <device>\n"\
|
|
-"More info:\n"\
|
|
-"> zgetdump -h\n";
|
|
-
|
|
-/* Version info */
|
|
-static const char version_text[] = "zgetdump: version "RELEASE_STRING;
|
|
-
|
|
-/* Copyright notice */
|
|
-static const char copyright_notice[] = "Copyright IBM Corp. 2001, 2008";
|
|
-
|
|
-/* global variables */
|
|
-
|
|
-s390_dump_header_t header;
|
|
-s390_dump_end_marker_t end_marker;
|
|
-char read_buffer[BLOCK_SIZE];
|
|
-struct timeval h_time_begin, h_time_end;
|
|
-
|
|
-int option_a_set;
|
|
-int option_i_set;
|
|
-int option_d_set;
|
|
-char dump_device[PATH_MAX];
|
|
-
|
|
-/* end of definitions */
|
|
-
|
|
-/* Use uname to check whether we run s390x kernel */
|
|
-int check_kernel_mode()
|
|
-{
|
|
- struct utsname uname_struct;
|
|
- if (uname(&uname_struct)) {
|
|
- fprintf(stderr, "Unable to get name and information about "
|
|
- "current kernel. \n");
|
|
- perror("");
|
|
- return 1;
|
|
- }
|
|
- if (strncmp(uname_struct.machine, "s390x", 5) == 0) {
|
|
- fprintf(stderr, "=========================================="
|
|
- "=======\n");
|
|
- fprintf(stderr, "WARNING: You are running an s390x (ESAME) "
|
|
- "kernel.\n");
|
|
- fprintf(stderr, " Your dump tool however is s390 "
|
|
- "(ESA).\n");
|
|
- fprintf(stderr, "=========================================="
|
|
- "=======\n");
|
|
- }
|
|
- return 0;
|
|
-}
|
|
-
|
|
-/* Read dump tool from DASD device */
|
|
-int read_dumper(int fd, int32_t offset, struct dump_tool *buffer, int whence)
|
|
-{
|
|
- if (lseek(fd, offset, whence) == -1) {
|
|
- perror("Cannot seek on device");
|
|
- return 1;
|
|
- }
|
|
- if (read(fd, buffer, sizeof(struct dump_tool)) !=
|
|
- sizeof(struct dump_tool)) {
|
|
- perror("Cannot read dump tool from device");
|
|
- return 1;
|
|
- }
|
|
- return 0;
|
|
-}
|
|
-
|
|
-/* Use stat to check whether user provided input is a block device or a
|
|
- * partition */
|
|
-enum devnode_type check_device(char *device, int print)
|
|
-{
|
|
- struct stat stat_struct;
|
|
-
|
|
- if (stat(device, &stat_struct)) {
|
|
- fprintf(stderr, "Unable to get device status for "
|
|
- "'%s'. \n", device);
|
|
- perror("");
|
|
- return IS_NOBLOCK;
|
|
- }
|
|
- if (!(S_ISBLK(stat_struct.st_mode))) {
|
|
- fprintf(stderr, "'%s' is not a block device. \n", dump_device);
|
|
- return IS_NOBLOCK;
|
|
- }
|
|
- if (minor(stat_struct.st_rdev) & PARTN_MASK) {
|
|
- if (print)
|
|
- fprintf(stderr, "Partition '%s' (%d/%d) specified where"
|
|
- " device is required.\n", dump_device,
|
|
- (unsigned short) major(stat_struct.st_rdev),
|
|
- (unsigned short) minor(stat_struct.st_rdev));
|
|
- return IS_PARTITION;
|
|
- }
|
|
- return IS_DEVICE;
|
|
-}
|
|
-
|
|
-/* Allocate SIZE bytes of memory. Upon success, return pointer to memory.
|
|
- * Return NULL otherwise. */
|
|
-void *misc_malloc(size_t size)
|
|
-{
|
|
- void* result;
|
|
-
|
|
- result = malloc(size);
|
|
- if (result == NULL) {
|
|
- fprintf(stderr, "Could not allocate %lld bytes of memory",
|
|
- (unsigned long long) size);
|
|
- }
|
|
- return result;
|
|
-}
|
|
-
|
|
-char* misc_make_path(char* dirname, char* filename)
|
|
-{
|
|
- char* result;
|
|
- size_t len;
|
|
-
|
|
- len = strlen(dirname) + strlen(filename) + 2;
|
|
- result = (char *) misc_malloc(len);
|
|
- if (result == NULL)
|
|
- return NULL;
|
|
- sprintf(result, "%s/%s", dirname, filename);
|
|
- return result;
|
|
-}
|
|
-
|
|
-#define TEMP_DEV_MAX_RETRIES 1000
|
|
-
|
|
-/* Make temporary device node for input device identified by its dev_t */
|
|
-int make_temp_devnode(dev_t dev, char** device_node)
|
|
-{
|
|
- char* result;
|
|
- char* pathname[] = { getenv("TMPDIR"), "/tmp",
|
|
- getenv("HOME"), "." , "/"};
|
|
- char filename[] = "zgetdump0000";
|
|
- mode_t mode;
|
|
- unsigned int path;
|
|
- int retry;
|
|
- int rc;
|
|
- int fd;
|
|
-
|
|
- mode = S_IFBLK | S_IRWXU;
|
|
- /* Try several locations as directory for the temporary device
|
|
- * node. */
|
|
- for (path=0; path < sizeof(pathname) / sizeof(pathname[0]); path++) {
|
|
- if (pathname[path] == NULL)
|
|
- continue;
|
|
- for (retry=0; retry < TEMP_DEV_MAX_RETRIES; retry++) {
|
|
- sprintf(filename, "zgetdump%04d", retry);
|
|
- result = misc_make_path(pathname[path], filename);
|
|
- if (result == NULL)
|
|
- return 1;
|
|
- rc = mknod(result, mode, dev);
|
|
- if (rc == 0) {
|
|
- /* Need this test to cover 'nodev'-mounted
|
|
- * filesystems. */
|
|
- fd = open(result, O_RDWR);
|
|
- if (fd != -1) {
|
|
- close(fd);
|
|
- *device_node = result;
|
|
- return 0;
|
|
- }
|
|
- remove(result);
|
|
- retry = TEMP_DEV_MAX_RETRIES;
|
|
- } else if (errno != EEXIST)
|
|
- retry = TEMP_DEV_MAX_RETRIES;
|
|
- free(result);
|
|
- }
|
|
- }
|
|
- fprintf(stderr, "Unable to create temporary device node: %s",
|
|
- strerror(errno));
|
|
- return 1;
|
|
-}
|
|
-
|
|
-/* Delete temporary device node and free memory allocated for device name. */
|
|
-void free_temp_devnode(char* device_node)
|
|
-{
|
|
- if (remove(device_node)) {
|
|
- fprintf(stderr, "Warning: Could not remove "
|
|
- "temporary file %s: %s",
|
|
- device_node, strerror(errno));
|
|
- }
|
|
- free(device_node);
|
|
-}
|
|
-
|
|
-
|
|
-int open_block_device(char *device)
|
|
-{
|
|
- int fd;
|
|
-
|
|
- if (check_device(device, 1) != IS_DEVICE)
|
|
- return -1;
|
|
- fd = open(device, O_RDONLY);
|
|
- if (fd == -1) {
|
|
- fprintf(stderr, "Cannot open device '%s'. \n", device);
|
|
- perror("");
|
|
- }
|
|
- return fd;
|
|
-}
|
|
-
|
|
-/* Check sysfs, whether a device specified by its bus id is defined and online.
|
|
- * Find out the corresponding dev_t */
|
|
-enum device_status get_device_from_busid(char* bus_id, dev_t *device)
|
|
-{
|
|
- char dev_file[PATH_MAX];
|
|
- char temp_file[PATH_MAX];
|
|
- char buffer[10];
|
|
- struct dirent *direntp;
|
|
- int fd, minor, major;
|
|
- DIR *fd1;
|
|
-
|
|
- fd1 = opendir(SYSFS_BUSDIR);
|
|
- if (!fd1) {
|
|
- fprintf(stderr, "Could not open %s (err = %i).\n",
|
|
- SYSFS_BUSDIR, errno);
|
|
- exit(1); /* sysfs info not available */
|
|
- }
|
|
- closedir(fd1);
|
|
- snprintf(dev_file, PATH_MAX, "%s/%s", SYSFS_BUSDIR, bus_id);
|
|
- fd1 = opendir(dev_file);
|
|
- if (!fd1)
|
|
- return UNDEFINED; /* device with devno does not exist */
|
|
- snprintf(temp_file, PATH_MAX, "%s/online", dev_file);
|
|
- fd = open(temp_file, O_RDONLY);
|
|
- if (read(fd, buffer, 1) == -1) {
|
|
- perror("Could not read online attribute.");
|
|
- exit(1);
|
|
- }
|
|
- close(fd);
|
|
- if (buffer[0] != '1')
|
|
- return OFFLINE; /* device with devno is not online */
|
|
- while ((direntp = readdir(fd1)))
|
|
- if (strncmp(direntp->d_name, "block:", 6) == 0)
|
|
- break;
|
|
- closedir(fd1);
|
|
- if (direntp == NULL) {
|
|
- snprintf(dev_file, PATH_MAX, "%s/%s/block", SYSFS_BUSDIR,
|
|
- bus_id);
|
|
- fd1 = opendir(dev_file);
|
|
- if (!fd1) {
|
|
- fprintf(stderr, "Could not open %s (err = %i).\n",
|
|
- dev_file, errno);
|
|
- exit(1);
|
|
- }
|
|
- while ((direntp = readdir(fd1)))
|
|
- if (strncmp(direntp->d_name, "dasd", 4) == 0)
|
|
- break;
|
|
- closedir(fd1);
|
|
- if (direntp == NULL) {
|
|
- fprintf(stderr, "Problem with contents of %s.\n",
|
|
- dev_file);
|
|
- exit(1);
|
|
- }
|
|
- }
|
|
- snprintf(temp_file, PATH_MAX, "%s/%s/dev", dev_file, direntp->d_name);
|
|
- fd = open(temp_file, O_RDONLY);
|
|
- if (read(fd, buffer, sizeof(buffer)) == -1) {
|
|
- perror("Could not read dev file.");
|
|
- exit(1);
|
|
- }
|
|
- close(fd);
|
|
- if (sscanf(buffer, "%i:%i", &major, &minor) != 2) {
|
|
- fprintf(stderr, "Malformed content of %s: %s\n",
|
|
- temp_file, buffer);
|
|
- exit(1);
|
|
- }
|
|
- *device = makedev(major, minor);
|
|
- return ONLINE;
|
|
-}
|
|
-
|
|
-/* Read dump tool, multi-volume dump parameter table, and dump header from the
|
|
- * input dump volume. Check input dump volume for
|
|
- * - identical dump parameter table (that is it belongs to the same dump set)
|
|
- * - valid magic number in the dump tool
|
|
- * - valid dump signature in the dump header
|
|
- * and set the volume's signature accordingly */
|
|
-int get_mvdump_volume_info(struct disk_info *vol, uint32_t vol_nr, off_t offset,
|
|
- struct mvdump_parm_table *table)
|
|
-{
|
|
- int fd, rc;
|
|
- ssize_t n_read;
|
|
- char* temp_devnode;
|
|
- struct dump_tool dumper;
|
|
- struct mvdump_parm_table vol_table;
|
|
-
|
|
- vol->signature = INVALID;
|
|
- rc = make_temp_devnode(vol->device, &temp_devnode);
|
|
- if (rc)
|
|
- return 1;
|
|
- fd = open_block_device(temp_devnode);
|
|
- if (fd == -1) {
|
|
- free_temp_devnode(temp_devnode);
|
|
- return 1;
|
|
- }
|
|
- /* We read partition data via the device node. If another process
|
|
- * has changed partition data via the partition node, the corresponding
|
|
- * device node might still have old data in its buffers. Flush buffers
|
|
- * to keep things in sync */
|
|
- if (ioctl(fd, BLKFLSBUF, 0)) {
|
|
- perror("BLKFLSBUF failed");
|
|
- goto out;
|
|
- }
|
|
- if (read_dumper(fd, offset, &dumper, SEEK_SET))
|
|
- goto out;
|
|
- if (lseek(fd, offset + MVDUMPER_SIZE, SEEK_SET) !=
|
|
- offset + MVDUMPER_SIZE) {
|
|
- perror("Cannot seek on device");
|
|
- goto out;
|
|
- }
|
|
- n_read = read(fd, &vol_table, sizeof(vol_table));
|
|
- if (n_read == -1) {
|
|
- perror("Cannot read multi-volume dump table");
|
|
- goto out;
|
|
- }
|
|
- /* Check whether dump table on user specified dump device is
|
|
- * identical to the one found on this device */
|
|
- if (memcmp(&vol_table, table, sizeof(vol_table))) {
|
|
- printf("ERROR: Orphaned multi-volume dump device '%s'\n",
|
|
- dump_device);
|
|
- goto out;
|
|
- }
|
|
- if (lseek(fd, vol->start_offset, SEEK_SET) != vol->start_offset) {
|
|
- perror("Cannot seek on device");
|
|
- goto out;
|
|
- }
|
|
- n_read = read(fd, &header, HEADER_SIZE);
|
|
- if (n_read == -1) {
|
|
- perror("Cannot read dump header");
|
|
- goto out;
|
|
- }
|
|
- free_temp_devnode(temp_devnode);
|
|
- close(fd);
|
|
- if ((header.dh_mvdump_signature == DUMP_MAGIC_S390) &&
|
|
- (strncmp(dumper.magic, "ZMULT64", 7) == 0)) {
|
|
- vol->signature = VALID;
|
|
- if ((header.dh_volnr == vol_nr) && (header.dh_memory_size != 0))
|
|
- vol->signature = ACTIVE;
|
|
- }
|
|
- return 0;
|
|
-out:
|
|
- free_temp_devnode(temp_devnode);
|
|
- close(fd);
|
|
- return 1;
|
|
-}
|
|
-
|
|
-/* Read multi-volume dump parameter table from dump device and fill in the
|
|
- * fields of the disk_info array */
|
|
-int get_mvdump_info(int fd, int block_size, int *count,
|
|
- struct disk_info vol[])
|
|
-{
|
|
- int i, rc = 0;
|
|
- off_t offset;
|
|
- ssize_t n_read;
|
|
- struct mvdump_parm_table table;
|
|
-
|
|
- offset = MAGIC_BLOCK_OFFSET_ECKD * block_size + MVDUMPER_SIZE;
|
|
- if (lseek(fd, offset, SEEK_SET) != offset) {
|
|
- fprintf(stderr, "Cannot seek on device '%s'.\n",
|
|
- dump_device);
|
|
- perror("");
|
|
- return 1;
|
|
- }
|
|
- n_read = read(fd, &table, sizeof(table));
|
|
- if (n_read == -1) {
|
|
- perror("Cannot read multi-volume dump table");
|
|
- return 1;
|
|
- }
|
|
- *count = table.num_param;
|
|
- for (i = 0; i < table.num_param; i++) {
|
|
- sprintf(vol[i].bus_id, "0.0.%04x", table.param[i].devno);
|
|
- vol[i].start_offset = table.param[i].start_blk;
|
|
- vol[i].start_offset *= table.param[i].blocksize << 8;
|
|
- vol[i].part_size = (table.param[i].end_blk -
|
|
- table.param[i].start_blk + 1);
|
|
- vol[i].part_size *= table.param[i].blocksize << 8;
|
|
- vol[i].status = get_device_from_busid(vol[i].bus_id,
|
|
- &vol[i].device);
|
|
- if (vol[i].status == ONLINE) {
|
|
- offset = MAGIC_BLOCK_OFFSET_ECKD *
|
|
- table.param[i].blocksize << 8;
|
|
- rc = get_mvdump_volume_info(&vol[i], i, offset,
|
|
- &table);
|
|
- if (rc)
|
|
- return rc;
|
|
- }
|
|
- }
|
|
- return 0;
|
|
-}
|
|
-
|
|
-/* Print dump size limit as specified in zipl -d or zipm -M */
|
|
-void print_size_limit_info(uint64_t memory)
|
|
-{
|
|
- fprintf(stderr, "Dump size limit: ");
|
|
- if (memory == (uint64_t) -1)
|
|
- fprintf(stderr, "none\n");
|
|
- else
|
|
- fprintf(stderr, "%lldMB\n", (unsigned long long) memory /
|
|
- (1024LL * 1024LL));
|
|
-}
|
|
+/*
|
|
+ * Globals
|
|
+ */
|
|
+struct zgetdump_globals g;
|
|
|
|
-/* Print multi-volume dump device information for --device option */
|
|
-void print_mvdump_info(int version, int count, struct disk_info vol[],
|
|
- uint64_t memory, int force)
|
|
+/*
|
|
+ * Signal handler for exiting zgetdump (the atexit handler will do the work)
|
|
+ */
|
|
+static void sig_exit(int sig)
|
|
{
|
|
- int i;
|
|
+ (void) sig;
|
|
|
|
- fprintf(stderr, "'%s' is part of Version %i multi-volume dump,\n"
|
|
- "which is spread along the following DASD volumes:\n",
|
|
- dump_device, version);
|
|
- for (i = 0; i < count; i++) {
|
|
- switch(vol[i].status) {
|
|
- case UNDEFINED:
|
|
- fprintf(stderr, "%s (not defined)\n", vol[i].bus_id);
|
|
- break;
|
|
- case OFFLINE:
|
|
- fprintf(stderr, "%s (offline)\n", vol[i].bus_id);
|
|
- break;
|
|
- case ONLINE:
|
|
- fprintf(stderr, "%s (online, ", vol[i].bus_id);
|
|
- if (vol[i].signature == INVALID)
|
|
- fprintf(stderr, "invalid)\n");
|
|
- else
|
|
- fprintf(stderr, "valid)\n");
|
|
- break;
|
|
- }
|
|
- }
|
|
- print_size_limit_info(memory);
|
|
- fprintf(stderr, "Force option specified: ");
|
|
- if (force)
|
|
- fprintf(stderr, "yes\n");
|
|
- else
|
|
- fprintf(stderr, "no\n");
|
|
+ STDERR("\n"); /* E.g. to get newline after '^C' */
|
|
+ ERR_EXIT("Got signal %i, exiting...", sig);
|
|
}
|
|
|
|
-/* Print single-volume dump device information for --device option */
|
|
-int print_dump_info(int version, int dumper_arch, uint64_t memory)
|
|
-{
|
|
- int rc = 0;
|
|
-
|
|
- if (version > 0) {
|
|
- if (dumper_arch == ARCH_S390) {
|
|
- fprintf(stderr, "'%s' is Version %i s390 (ESA) "
|
|
- "dump device.\n", dump_device, version);
|
|
- if (check_kernel_mode())
|
|
- rc = 1;
|
|
- } else
|
|
- fprintf(stderr, "'%s' is Version %i s390x (ESAME) "
|
|
- "dump device.\n", dump_device, version);
|
|
- } else
|
|
- fprintf(stderr, "'%s' is Version 0 dump device. \n",
|
|
- dump_device);
|
|
- print_size_limit_info(memory);
|
|
- return rc;
|
|
+/*
|
|
+ * Install signal handler
|
|
+ */
|
|
+static void sig_handler_init(void)
|
|
+{
|
|
+ struct sigaction sigact;
|
|
+
|
|
+ /* Ignore signals SIGUSR1 and SIGUSR2 */
|
|
+ if (sigemptyset(&sigact.sa_mask) < 0)
|
|
+ goto fail;
|
|
+ sigact.sa_handler = SIG_IGN;
|
|
+ if (sigaction(SIGUSR1, &sigact, NULL) < 0)
|
|
+ goto fail;
|
|
+ if (sigaction(SIGUSR2, &sigact, NULL) < 0)
|
|
+ goto fail;
|
|
+
|
|
+ /* Exit on SIGINT, SIGTERM, SIGHUP, ... */
|
|
+ if (sigemptyset(&sigact.sa_mask) < 0)
|
|
+ goto fail;
|
|
+ sigact.sa_handler = sig_exit;
|
|
+ if (sigaction(SIGINT, &sigact, NULL) < 0)
|
|
+ goto fail;
|
|
+ if (sigaction(SIGTERM, &sigact, NULL) < 0)
|
|
+ goto fail;
|
|
+ if (sigaction(SIGHUP, &sigact, NULL) < 0)
|
|
+ goto fail;
|
|
+ if (sigaction(SIGQUIT, &sigact, NULL) < 0)
|
|
+ goto fail;
|
|
+ if (sigaction(SIGALRM, &sigact, NULL) < 0)
|
|
+ goto fail;
|
|
+ if (sigaction(SIGPIPE, &sigact, NULL) < 0)
|
|
+ goto fail;
|
|
+ return;
|
|
+fail:
|
|
+ ERR_EXIT_ERRNO("Could not initialize signal handler");
|
|
}
|
|
|
|
-/* Read dump tool on FBA disk and check its magic number */
|
|
-int check_dump_tool_fba(int fd, int *version, int *arch, uint64_t *memory)
|
|
+/*
|
|
+ * Run "--umount" action
|
|
+ */
|
|
+static int do_umount(void)
|
|
{
|
|
- struct dump_tool dumper;
|
|
-
|
|
- if (read_dumper(fd, MAGIC_OFFSET_FBA, &dumper, SEEK_END))
|
|
- return 1;
|
|
- *memory = dumper.mem;
|
|
- if (strncmp(dumper.magic, "ZDFBA31", 7) == 0) {
|
|
- *version = dumper.version;
|
|
- *arch = ARCH_S390;
|
|
- } else if (strncmp(dumper.magic, "ZDFBA64", 7) == 0) {
|
|
- *version = dumper.version;
|
|
- *arch = ARCH_S390X;
|
|
- } else if ((memcmp(dumper.magic, HEXINSTR, 4) == 0) &&
|
|
- (dumper.code[0] == '\x0d') && (dumper.code[1] == '\xd0'))
|
|
- /* We found basr r13,0 (old dumper) */
|
|
- *version = 0;
|
|
- else
|
|
- *version = VERSION_NO_DUMP_DEVICE;
|
|
+ zfuse_umount();
|
|
return 0;
|
|
}
|
|
|
|
-/* Read dump tool on ECKD disk and check its magic number */
|
|
-int check_dump_tool_eckd(int fd, int *version, int *arch, int *dasd_mv_flag,
|
|
- int *block_size, int *force_specified,
|
|
- uint64_t *memory)
|
|
+/*
|
|
+ * Run "--device" action
|
|
+ */
|
|
+static int do_device_info(void)
|
|
{
|
|
- struct dump_tool dumper;
|
|
-
|
|
- if (ioctl(fd, BLKSSZGET, block_size)) {
|
|
- fprintf(stderr, "Cannot get blocksize of device %s.\n",
|
|
- dump_device);
|
|
- perror("");
|
|
- return 1;
|
|
- }
|
|
- if (read_dumper(fd, MAGIC_BLOCK_OFFSET_ECKD * *block_size, &dumper,
|
|
- SEEK_SET))
|
|
- return 1;
|
|
- *memory = dumper.mem;
|
|
- if (strncmp(dumper.magic, "ZECKD31", 7) == 0) {
|
|
- *version = dumper.version;
|
|
- *arch = ARCH_S390;
|
|
- } else if (strncmp(dumper.magic, "ZECKD64", 7) == 0) {
|
|
- *version = dumper.version;
|
|
- *arch = ARCH_S390X;
|
|
- } else if (strncmp(dumper.magic, "ZMULT64", 7) == 0) {
|
|
- *version = dumper.version;
|
|
- *arch = ARCH_S390X;
|
|
- *dasd_mv_flag = 1;
|
|
- *force_specified = dumper.force;
|
|
- } else if ((memcmp(dumper.magic, HEXINSTR, 4) == 0) &&
|
|
- (dumper.code[0] == '\x0d') && (dumper.code[1] == '\xd0'))
|
|
- /* We found basr r13,0 (old dumper) */
|
|
- *version = 0;
|
|
- else
|
|
- *version = VERSION_NO_DUMP_DEVICE;
|
|
+ dt_init();
|
|
+ dt_info_print();
|
|
return 0;
|
|
}
|
|
|
|
-void s390_tod_to_timeval(uint64_t todval, struct timeval *xtime)
|
|
-{
|
|
- /* adjust todclock to 1970 */
|
|
- todval -= 0x8126d60e46000000LL - (0x3c26700LL * 1000000 * 4096);
|
|
-
|
|
- todval >>= 12;
|
|
- xtime->tv_sec = todval / 1000000;
|
|
- xtime->tv_usec = todval % 1000000;
|
|
-}
|
|
-
|
|
-
|
|
-int open_dump(char *pathname)
|
|
-{
|
|
- int fd;
|
|
-
|
|
- fd = open(pathname, O_RDONLY);
|
|
- if (fd == -1) {
|
|
- perror("Cannot open dump device");
|
|
- exit(1);
|
|
- } else
|
|
- fprintf(stderr, "Dump device: %s\n", pathname);
|
|
- return fd;
|
|
-}
|
|
-
|
|
-
|
|
-/* check if device is dasd or tape */
|
|
-enum dump_type dev_type(int fd)
|
|
-{
|
|
- struct mtget mymtget;
|
|
-
|
|
- if (ioctl(fd, MTIOCGET, &mymtget) == -1)
|
|
- return IS_DASD;
|
|
- else
|
|
- return IS_TAPE;
|
|
-}
|
|
-
|
|
-/* print lkcd header information */
|
|
-void print_lkcd_header(int fd)
|
|
-{
|
|
- dump_header_4_1_t dump_header;
|
|
-
|
|
- lseek(fd, 0, SEEK_SET);
|
|
- if (read(fd, &dump_header, sizeof(dump_header)) == -1) {
|
|
- perror("Could not read dump header.");
|
|
- exit(1);
|
|
- }
|
|
- fprintf(stderr, "\nThis is a lkcd dump:\n\n");
|
|
- fprintf(stderr,
|
|
- "Memory start : 0x%"FMT64"x\n", dump_header.dh_memory_start);
|
|
- fprintf(stderr,
|
|
- "Memory end : 0x%"FMT64"x\n", dump_header.dh_memory_end);
|
|
- fprintf(stderr,
|
|
- "Physical memory: %"FMT64"d\n", dump_header.dh_memory_size);
|
|
- fprintf(stderr,
|
|
- "Panic string : %s\n", dump_header.dh_panic_string);
|
|
- fprintf(stderr,
|
|
- "Number of pages: %d\n", dump_header.dh_num_dump_pages);
|
|
- fprintf(stderr,
|
|
- "Page size : %d\n", dump_header.dh_dump_page_size);
|
|
- fprintf(stderr,
|
|
- "Magic number : 0x%"FMT64"x\n", dump_header.dh_magic_number);
|
|
- fprintf(stderr,
|
|
- "Version number : %d\n", dump_header.dh_version);
|
|
-}
|
|
-
|
|
-void print_s390_header(enum dump_type d_type)
|
|
-{
|
|
- s390_tod_to_timeval(header.dh_tod, &h_time_begin);
|
|
-
|
|
-/* as from version 2 of the dump tools */
|
|
-/* volume numbers are used */
|
|
-
|
|
- if ((d_type == IS_TAPE) && (header.dh_version >= 2)) {
|
|
- fprintf(stderr, "\nTape Volume %i", header.dh_volnr);
|
|
- if (header.dh_volnr != 0)
|
|
- fprintf(stderr, " of a multi volume dump.\n");
|
|
- else
|
|
- fprintf(stderr, "\n");
|
|
- }
|
|
-
|
|
-/* don't print header */
|
|
-/* for all subsequent tapes/disks */
|
|
-/* of a multi-volume tape/disk dump */
|
|
-
|
|
- if ((d_type == IS_DASD) || (header.dh_volnr == 0)) {
|
|
- if (header.dh_magic_number != DUMP_MAGIC_S390) {
|
|
- fprintf(stderr, "===================================="
|
|
- "===============\n");
|
|
- fprintf(stderr, "WARNING: This does not look like a "
|
|
- "valid s390 dump!\n");
|
|
- fprintf(stderr, "===================================="
|
|
- "===============\n");
|
|
- }
|
|
- fprintf(stderr, "\n>>> Dump header information <<<\n");
|
|
- fprintf(stderr, "Dump created on: %s\n",
|
|
- ctime(&h_time_begin.tv_sec));
|
|
- fprintf(stderr, "Magic number:\t 0x%"FMT64"x\n",
|
|
- header.dh_magic_number);
|
|
- fprintf(stderr, "Version number:\t %d\n", header.dh_version);
|
|
- fprintf(stderr, "Header size:\t %d\n", header.dh_header_size);
|
|
- fprintf(stderr, "Page size:\t %d\n", header.dh_page_size);
|
|
- fprintf(stderr, "Dumped memory:\t %"FMT64"d\n",
|
|
- header.dh_memory_size);
|
|
- fprintf(stderr, "Dumped pages:\t %u\n", header.dh_num_pages);
|
|
- if (header.dh_version >= 3) {
|
|
- fprintf(stderr, "Real memory:\t %"FMT64"d\n",
|
|
- header.dh_real_memory_size);
|
|
- }
|
|
- fprintf(stderr, "cpu id:\t\t 0x%"FMT64"x\n", header.dh_cpu_id);
|
|
- if (header.dh_version >= 2) {
|
|
- switch (header.dh_arch) {
|
|
- case 1: fprintf(stderr, "System Arch:\t s390 (ESA)\n");
|
|
- break;
|
|
- case 2: fprintf(stderr,
|
|
- "System Arch:\t s390x (ESAME)\n");
|
|
- break;
|
|
- default:
|
|
- fprintf(stderr, "System Arch:\t <unknown>\n");
|
|
- break;
|
|
- }
|
|
- switch (header.dh_build_arch) {
|
|
- case 1: fprintf(stderr, "Build Arch:\t s390 (ESA)\n");
|
|
- break;
|
|
- case 2: fprintf(stderr,
|
|
- "Build Arch:\t s390x (ESAME)\n");
|
|
- break;
|
|
- default:
|
|
- fprintf(stderr, "Build Arch:\t <unknown>\n");
|
|
- break;
|
|
- }
|
|
- }
|
|
- fprintf(stderr, ">>> End of Dump header <<<\n\n");
|
|
- }
|
|
-}
|
|
-
|
|
-/* print header information */
|
|
-void get_header(int fd)
|
|
-{
|
|
- ssize_t n_read;
|
|
-
|
|
- n_read = read(fd, &header, HEADER_SIZE);
|
|
- if (n_read == -1) {
|
|
- perror("Cannot read dump header");
|
|
- close(fd);
|
|
- exit(1);
|
|
- }
|
|
-}
|
|
-
|
|
-/* copy header to stdout */
|
|
-void write_header()
|
|
+/*
|
|
+ * Run "--info" action
|
|
+ */
|
|
+static int do_dump_info(void)
|
|
{
|
|
- ssize_t rc;
|
|
-
|
|
- memcpy(read_buffer, &header, sizeof(header));
|
|
- rc = write(STDOUT_FILENO, read_buffer, header.dh_header_size);
|
|
- if (rc == -1) {
|
|
- perror("\nwrite failed");
|
|
- exit(1);
|
|
- }
|
|
- if (rc < header.dh_header_size) {
|
|
- fprintf(stderr, "\nwrite failed: No space left on device\n");
|
|
- exit(1);
|
|
+ if (dfi_init() != 0) {
|
|
+ dfi_info_print();
|
|
+ STDERR("\nERROR: Dump is not complete\n");
|
|
+ zg_exit(1);
|
|
}
|
|
-}
|
|
-
|
|
-/* copy partition containing multi-volume dump data to stdout */
|
|
-int mvdump_copy(int fd, uint64_t partsize, uint64_t *totalsize)
|
|
-{
|
|
- ssize_t n_read, n_written;
|
|
- uint64_t part_offset;
|
|
- int done = 0;
|
|
- size_t count;
|
|
-
|
|
- part_offset = HEADER_SIZE;
|
|
- do {
|
|
- count = MIN(header.dh_memory_size - *totalsize, BLOCK_SIZE);
|
|
- if (count < BLOCK_SIZE)
|
|
- done = 1;
|
|
- if (partsize - part_offset < count) {
|
|
- count = partsize - part_offset;
|
|
- done = 1;
|
|
- }
|
|
- n_read = read(fd, read_buffer, count);
|
|
- if (n_read == -1) {
|
|
- perror("\nread failed");
|
|
- return 1;
|
|
- }
|
|
- n_read = (n_read >> 12) << 12;
|
|
- n_written = write(STDOUT_FILENO, read_buffer, n_read);
|
|
- if (n_written == -1) {
|
|
- perror("\nwrite failed");
|
|
- return 1;
|
|
- }
|
|
- if (n_written < n_read) {
|
|
- fprintf(stderr, "\nwrite failed: "
|
|
- "No space left on device\n");
|
|
- return 1;
|
|
- }
|
|
- part_offset += n_written;
|
|
- *totalsize += n_written;
|
|
- if (part_offset % (header.dh_memory_size / 32) == HEADER_SIZE)
|
|
- fprintf(stderr, ".");
|
|
- } while (!done);
|
|
- fprintf(stderr, "\n");
|
|
+ dfi_info_print();
|
|
return 0;
|
|
}
|
|
|
|
-/* copy the dump to stdout */
|
|
-int get_dump(int fd, int d_type)
|
|
-{
|
|
- int ret, bsr;
|
|
- ssize_t n_read, n_written;
|
|
- struct mtop mymtop;
|
|
- uint64_t i;
|
|
-
|
|
- ret = 0;
|
|
- if (d_type == IS_DASD) {
|
|
- i = 0;
|
|
- do {
|
|
- n_read = read(fd, read_buffer, BLOCK_SIZE);
|
|
- n_written = write(STDOUT_FILENO, read_buffer, n_read);
|
|
- if (n_written == -1) {
|
|
- perror("\nwrite failed");
|
|
- exit(1);
|
|
- }
|
|
- if (n_written < n_read) {
|
|
- fprintf(stderr, "\nwrite failed: "
|
|
- "No space left on device\n");
|
|
- exit(1);
|
|
- }
|
|
- i += n_read;
|
|
- if (i % (header.dh_memory_size / 32) == 0)
|
|
- fprintf(stderr, ".");
|
|
- } while (i < header.dh_memory_size && n_read != 0
|
|
- && n_written >= 0);
|
|
- } else if (d_type == IS_TAPE) {
|
|
- /* write to stdout while not ENDOFVOL or DUMP_END */
|
|
- if (header.dh_volnr != 0)
|
|
- fprintf(stderr, "Reading dump content ");
|
|
- for (i = 0; i < (header.dh_memory_size/BLOCK_SIZE); i++) {
|
|
- n_read = read(fd, read_buffer, BLOCK_SIZE);
|
|
- if (i % ((header.dh_memory_size/BLOCK_SIZE) / 32) == 0)
|
|
- fprintf(stderr, ".");
|
|
- if (strncmp(read_buffer, "ENDOFVOL", 8) == 0) {
|
|
- fprintf(stderr, "\nEnd of Volume reached.\n");
|
|
- ret = 1;
|
|
- break;
|
|
- } else if (strncmp(read_buffer, "DUMP_END", 8) == 0) {
|
|
- ret = 2;
|
|
- break;
|
|
- } else {
|
|
- n_written = write(STDOUT_FILENO, read_buffer,
|
|
- n_read);
|
|
- if (n_written == -1) {
|
|
- perror("\nwrite failed");
|
|
- exit(1);
|
|
- }
|
|
- if (n_written < n_read) {
|
|
- fprintf(stderr, "\nwrite failed: "
|
|
- "No space left on device\n");
|
|
- exit(1);
|
|
- }
|
|
- }
|
|
- }
|
|
- if (ret == 2) {
|
|
- /* we go back a record, so dump_end_times gets called */
|
|
- mymtop.mt_count = 1;
|
|
- mymtop.mt_op = MTBSR;
|
|
- bsr = ioctl(fd, MTIOCTOP, &mymtop);
|
|
- if (bsr != 0) {
|
|
- fprintf(stderr,
|
|
- "Tape operation MTBSR failed.\n");
|
|
- exit(1);
|
|
- }
|
|
- }
|
|
- }
|
|
- return ret;
|
|
-}
|
|
-
|
|
-/* check for DUMP_END and see */
|
|
-/* if dump ended after it started (!!!) */
|
|
-int dump_end_times(int fd)
|
|
-{
|
|
- int ret;
|
|
-
|
|
- if (read(fd, &end_marker, sizeof(end_marker)) == -1) {
|
|
- perror("Could not read end marker.");
|
|
- exit(1);
|
|
- }
|
|
- s390_tod_to_timeval(end_marker.end_time, &h_time_end);
|
|
- if ((strncmp(end_marker.end_string, "DUMP_END", 8) == 0) &&
|
|
- ((h_time_end.tv_sec - h_time_begin.tv_sec) >= 0)) {
|
|
- fprintf(stderr, "\nDump ended on:\t %s\n",
|
|
- ctime(&h_time_end.tv_sec));
|
|
- ret = 0;
|
|
- } else
|
|
- ret = -1;
|
|
- return ret;
|
|
-}
|
|
-
|
|
-int check_and_write_end_marker(int fd)
|
|
-{
|
|
- if (dump_end_times(fd) == 0) {
|
|
- ssize_t rc;
|
|
- rc = write(STDOUT_FILENO, &end_marker,
|
|
- sizeof(end_marker));
|
|
- if (rc == -1) {
|
|
- perror("\nwrite failed");
|
|
- return 1;
|
|
- }
|
|
- if (rc < (ssize_t) sizeof(end_marker)) {
|
|
- fprintf(stderr, "\nwrite failed: "
|
|
- "No space left on device\n");
|
|
- return 1;
|
|
- }
|
|
- fprintf(stderr, "\nDump End Marker found: "
|
|
- "this dump is valid.\n");
|
|
- return 0;
|
|
- } else {
|
|
- fprintf(stderr, "\nThis dump is NOT valid.\n");
|
|
- return 1;
|
|
- }
|
|
-}
|
|
-
|
|
-/* if a tape is part of the dump (not the last) */
|
|
-/* it should have and ENDOFVOL marker */
|
|
-int vol_end(void)
|
|
-{
|
|
- int ret;
|
|
-
|
|
- ret = strncmp(end_marker.end_string, "ENDOFVOL", 8);
|
|
- return ret;
|
|
-}
|
|
-
|
|
-/* position the tape in front of an end marker */
|
|
-/* with FSFM and BSR */
|
|
-void tape_forwards(int fd)
|
|
-{
|
|
- int ret;
|
|
- struct mtop mymtop;
|
|
-
|
|
- mymtop.mt_count = 1;
|
|
- mymtop.mt_op = MTFSFM;
|
|
- ret = ioctl(fd, MTIOCTOP, &mymtop);
|
|
- if (ret != 0) {
|
|
- fprintf(stderr, "Tape operation FSFM failed.\n");
|
|
- exit(1);
|
|
- }
|
|
-
|
|
- mymtop.mt_count = 1;
|
|
- mymtop.mt_op = MTBSR;
|
|
- ret = ioctl(fd, MTIOCTOP, &mymtop);
|
|
- if (ret != 0) {
|
|
- fprintf(stderr, "Tape operation BSR failed.\n");
|
|
- exit(1);
|
|
- }
|
|
-}
|
|
-
|
|
-/* put current tape offline */
|
|
-/* load & rewind next tape */
|
|
-void load_next(int fd)
|
|
+/*
|
|
+ * Run "--mount" action
|
|
+ */
|
|
+static int do_mount(void)
|
|
{
|
|
- int ret;
|
|
- struct mtop mymtop;
|
|
-
|
|
- mymtop.mt_count = 1;
|
|
- mymtop.mt_op = MTOFFL;
|
|
- ret = ioctl(fd, MTIOCTOP, &mymtop);
|
|
- if (ret != 0) {
|
|
- fprintf(stderr, "Tape operation OFFL failed.\n");
|
|
- exit(1);
|
|
- }
|
|
-
|
|
- mymtop.mt_count = 1;
|
|
- mymtop.mt_op = MTLOAD;
|
|
- ret = ioctl(fd, MTIOCTOP, &mymtop);
|
|
- if (ret != 0) {
|
|
- fprintf(stderr, "Tape operation LOAD failed.\n");
|
|
- exit(1);
|
|
- } else
|
|
- fprintf(stderr, "done\n");
|
|
-
|
|
- mymtop.mt_count = 1;
|
|
- mymtop.mt_op = MTREW;
|
|
- ret = ioctl(fd, MTIOCTOP, &mymtop);
|
|
- if (ret != 0) {
|
|
- fprintf(stderr, "Tape operation REW failed.\n");
|
|
- exit(1);
|
|
- }
|
|
+ if (dfi_init() != 0)
|
|
+ ERR_EXIT("Dump cannot be processed (is not complete)");
|
|
+ dfo_init();
|
|
+ return zfuse_mount_dump();
|
|
}
|
|
|
|
-/* parse the commandline options */
|
|
-void parse_opts(int argc, char *argv[])
|
|
-{
|
|
- int opt, index;
|
|
- static struct option long_options[] = {
|
|
- {"info", no_argument, 0, 'i'},
|
|
- {"help", no_argument, 0, 'h'},
|
|
- {"version", no_argument, 0, 'v'},
|
|
- {"all", no_argument, 0, 'a'},
|
|
- {"device", no_argument, 0, 'd'},
|
|
- {0, 0, 0, 0 }
|
|
- };
|
|
- static const char option_string[] = "iavhd";
|
|
-
|
|
- while ((opt = getopt_long(argc, argv, option_string, long_options,
|
|
- &index)) != -1) {
|
|
- switch (opt) {
|
|
- case 'd':
|
|
- option_d_set = 1;
|
|
- break;
|
|
- case 'a':
|
|
- option_a_set = 1;
|
|
- break;
|
|
- case 'i':
|
|
- option_i_set = 1;
|
|
- break;
|
|
- case 'h':
|
|
- printf(help_text);
|
|
- exit(0);
|
|
- case 'v':
|
|
- printf("%s\n", version_text);
|
|
- printf("%s\n", copyright_notice);
|
|
- exit(0);
|
|
- default:
|
|
- fprintf(stderr, "Try 'zgetdump --help' for more"
|
|
- " information.\n");
|
|
- exit(1);
|
|
- }
|
|
- }
|
|
-
|
|
- /* check if -a and -i options are used correctly and check */
|
|
- /* if devicename has been specified */
|
|
-
|
|
- if ((option_a_set && !option_i_set) || (optind != argc-1)
|
|
- || (option_d_set && option_i_set)) {
|
|
- printf(help_text);
|
|
- exit(1);
|
|
-
|
|
-
|
|
- }
|
|
- strcpy(dump_device, argv[optind]);
|
|
-}
|
|
-
|
|
-/* Loop along all involved volumes (dump partitions) and either check (for
|
|
- * option --info) or pick up dump data */
|
|
-int mvdump_check_or_copy(int vol_count, struct disk_info vol[])
|
|
+/*
|
|
+ * Run "copy to stdout" action
|
|
+ */
|
|
+static int do_stdout(void)
|
|
{
|
|
- int i, fd, rc = 1;
|
|
- uint64_t data_size, total_size = 0;
|
|
- char* temp_devnode;
|
|
-
|
|
- for (i = 0; i < vol_count; i++) {
|
|
- if (vol[i].status != ONLINE) {
|
|
- fprintf(stderr, "============================="
|
|
- "=======================\n");
|
|
- fprintf(stderr, "ERROR: Dump device %s is not "
|
|
- "available.\n", vol[i].bus_id);
|
|
- fprintf(stderr, "============================="
|
|
- "=======================\n");
|
|
- return 1;
|
|
- }
|
|
- if (vol[i].signature != ACTIVE) {
|
|
- fprintf(stderr, "============================="
|
|
- "=======================\n");
|
|
- fprintf(stderr, "ERROR: Invalid dump data on "
|
|
- "%s.\n", vol[i].bus_id);
|
|
- fprintf(stderr, "============================="
|
|
- "=======================\n");
|
|
- return 1;
|
|
- }
|
|
- if (make_temp_devnode(vol[i].device, &temp_devnode))
|
|
- return 1;
|
|
- fd = open_block_device(temp_devnode);
|
|
- if (fd == -1) {
|
|
- free_temp_devnode(temp_devnode);
|
|
- return 1;
|
|
- }
|
|
- if (lseek(fd, vol[i].start_offset, SEEK_SET) !=
|
|
- vol[i].start_offset) {
|
|
- perror("Cannot seek on device");
|
|
- goto out;
|
|
- }
|
|
- get_header(fd);
|
|
- print_s390_header(IS_MULT_DASD);
|
|
- fprintf(stderr, "\nMulti-volume dump: Disk %i (of %i)\n",
|
|
- i + 1, vol_count);
|
|
- if (option_i_set) {
|
|
- data_size = ((vol[i].part_size >> 12) << 12) -
|
|
- HEADER_SIZE;
|
|
- if (total_size + data_size > header.dh_memory_size) {
|
|
- if (lseek(fd, header.dh_memory_size -
|
|
- total_size, SEEK_CUR) == -1) {
|
|
- perror("Cannot seek on device");
|
|
- goto out;
|
|
- }
|
|
- fprintf(stderr, "Checking dump contents on "
|
|
- "%s\n", vol[i].bus_id);
|
|
- if (dump_end_times(fd) == 0) {
|
|
- fprintf(stderr, "Dump End Marker "
|
|
- "found: "
|
|
- "this dump is valid.\n\n");
|
|
- rc = 0;
|
|
- goto out;
|
|
- } else {
|
|
- fprintf(stderr, "Dump End Marker not "
|
|
- "found: "
|
|
- "this dump is NOT valid.\n\n");
|
|
- goto out;
|
|
- }
|
|
- } else if (i == vol_count - 1) {
|
|
- fprintf(stderr, "Dump End Marker not found: "
|
|
- "this dump is NOT valid.\n\n");
|
|
- goto out;
|
|
- }
|
|
- total_size += data_size;
|
|
- fprintf(stderr, "Skipping dump contents on %s\n",
|
|
- vol[i].bus_id);
|
|
- } else {
|
|
- if (i == 0)
|
|
- write_header();
|
|
- fprintf(stderr, "Reading dump contents from %s",
|
|
- vol[i].bus_id);
|
|
- if (mvdump_copy(fd, vol[i].part_size, &total_size))
|
|
- goto out;
|
|
- if ((i == vol_count - 1) ||
|
|
- (total_size == header.dh_memory_size)) {
|
|
- rc = check_and_write_end_marker(fd);
|
|
- goto out;
|
|
- }
|
|
- }
|
|
- free_temp_devnode(temp_devnode);
|
|
- close(fd);
|
|
- }
|
|
- return 0;
|
|
-out:
|
|
- free_temp_devnode(temp_devnode);
|
|
- close(fd);
|
|
- return rc;
|
|
+ if (dfi_init() != 0)
|
|
+ ERR_EXIT("Dump cannot be processed (is not complete)");
|
|
+ dfo_init();
|
|
+ return stdout_write_dump();
|
|
}
|
|
|
|
+/*
|
|
+ * The zgetdump main function
|
|
+ */
|
|
int main(int argc, char *argv[])
|
|
{
|
|
- uint64_t cur_time, size_limit;
|
|
- int vol_count, fd = -1;
|
|
- int version, dumper_arch, dasd_mv_flag = 0, block_size, rc;
|
|
- int force_specified = 0;
|
|
- enum dump_type d_type;
|
|
- enum devnode_type type;
|
|
- struct disk_info vol[MAX_DUMP_VOLUMES];
|
|
- uint32_t cur_volnr;
|
|
-
|
|
- rc = 0;
|
|
- parse_opts(argc, argv);
|
|
-
|
|
- if (option_d_set) {
|
|
- fd = open_block_device(dump_device);
|
|
- if (fd == -1) {
|
|
- rc = 1;
|
|
- goto out;
|
|
- }
|
|
- rc = check_dump_tool_fba(fd, &version, &dumper_arch,
|
|
- &size_limit);
|
|
- if (rc)
|
|
- goto out;
|
|
- if (version >= 0)
|
|
- goto is_dump_device;
|
|
- else
|
|
- rc = check_dump_tool_eckd(fd, &version, &dumper_arch,
|
|
- &dasd_mv_flag, &block_size,
|
|
- &force_specified,
|
|
- &size_limit);
|
|
- if (rc)
|
|
- goto out;
|
|
- if (version >= 0)
|
|
- goto is_dump_device;
|
|
- fprintf(stderr, "'%s' is no dump device.\n", dump_device);
|
|
- rc = 1;
|
|
- goto out;
|
|
-
|
|
-is_dump_device:
|
|
- if (dasd_mv_flag) {
|
|
- rc = get_mvdump_info(fd, block_size, &vol_count, vol);
|
|
- if (rc)
|
|
- goto out;
|
|
- print_mvdump_info(version, vol_count, vol, size_limit,
|
|
- force_specified);
|
|
- } else
|
|
- rc = print_dump_info(version, dumper_arch,
|
|
- size_limit);
|
|
- goto out; /* do not consider any other options */
|
|
- }
|
|
-
|
|
- fd = open_dump(dump_device);
|
|
- get_header(fd);
|
|
- d_type = dev_type(fd);
|
|
- if ((d_type == IS_DASD) &&
|
|
- ((header.dh_magic_number == DUMP_MAGIC_LKCD)
|
|
- || (header.dh_magic_number == DUMP_MAGIC_LIVE))) {
|
|
- print_lkcd_header(fd);
|
|
- exit(0);
|
|
- }
|
|
- if (d_type != IS_TAPE) {
|
|
- type = check_device(dump_device, 0);
|
|
- if (type == IS_DEVICE) {
|
|
- /* This is a valid block device node, no partition */
|
|
- rc = check_dump_tool_eckd(fd, &version, &dumper_arch,
|
|
- &dasd_mv_flag, &block_size,
|
|
- &force_specified,
|
|
- &size_limit);
|
|
- if (rc)
|
|
- goto out;
|
|
- if (!dasd_mv_flag) {
|
|
- fprintf(stderr, "Device '%s' specified where"
|
|
- " partition is required.\n", dump_device);
|
|
- rc = 1;
|
|
- goto out;
|
|
- } else
|
|
- d_type = IS_MULT_DASD;
|
|
- } else if ((type == IS_PARTITION) &&
|
|
- (header.dh_mvdump_signature == DUMP_MAGIC_S390)) {
|
|
- fprintf(stderr, "'%s' is a multi-volume dump "
|
|
- "partition.\nSpecify the corresponding device "
|
|
- "node instead.\n", dump_device);
|
|
- rc = 1;
|
|
- goto out;
|
|
- }
|
|
- }
|
|
-
|
|
- if (dasd_mv_flag) {
|
|
- rc = get_mvdump_info(fd, block_size, &vol_count, vol);
|
|
- if (rc)
|
|
- goto out;
|
|
- rc = mvdump_check_or_copy(vol_count, vol);
|
|
- goto out;
|
|
- }
|
|
-
|
|
- if (!option_i_set) { /* copy the dump to stdout */
|
|
- print_s390_header(d_type);
|
|
- write_header();
|
|
- fprintf(stderr, "Reading dump content ");
|
|
-
|
|
- /* now get_dump returns 1 for all */
|
|
- /* except the last tape of a multi-volume dump */
|
|
-
|
|
- while (get_dump(fd, d_type) == 1) {
|
|
- fprintf(stderr, "\nWaiting for next volume to be "
|
|
- "loaded... ");
|
|
- load_next(fd);
|
|
- get_header(fd);
|
|
- print_s390_header(d_type);
|
|
- }
|
|
-
|
|
- /* if dev is DASD and dump is copied */
|
|
- /* check if the dump is valid */
|
|
-
|
|
- if (d_type == IS_DASD)
|
|
- lseek(fd, header.dh_header_size + header.dh_memory_size,
|
|
- SEEK_SET);
|
|
-
|
|
- if (!check_and_write_end_marker(fd))
|
|
- goto out;
|
|
- } else if (!option_a_set) { /* "-i" option */
|
|
- fprintf(stderr, "\n> \"zgetdump -i\" checks if a dump on "
|
|
- "either\n");
|
|
- fprintf(stderr, "> a dasd volume or single tape is valid.\n");
|
|
- fprintf(stderr, "> If the tape is part of a multi-volume tape "
|
|
- "dump,\n");
|
|
- fprintf(stderr, "> it checks if it is a valid portion of "
|
|
- "the dump.\n");
|
|
- print_s390_header(d_type);
|
|
- if (d_type == IS_DASD)
|
|
- lseek(fd,
|
|
- header.dh_header_size + header.dh_memory_size,
|
|
- SEEK_SET);
|
|
- else {
|
|
- fprintf(stderr, "Checking if the dump is valid - "
|
|
- "this might take a while...\n");
|
|
- tape_forwards(fd);
|
|
- }
|
|
- if (dump_end_times(fd) == 0) {
|
|
- fprintf(stderr, "Dump End Marker found: ");
|
|
- if (header.dh_volnr != 0)
|
|
- fprintf(stderr, "this is a valid part of "
|
|
- "a dump.\n\n");
|
|
- else
|
|
- fprintf(stderr, "this dump is valid.\n\n");
|
|
- goto out;
|
|
- } else if (d_type == IS_DASD) {
|
|
- fprintf(stderr, "Dump End Marker not found: "
|
|
- "this dump is NOT valid.\n\n");
|
|
- rc = 1;
|
|
- goto out;
|
|
- } else
|
|
- fprintf(stderr, "Checking for End of Volume...\n");
|
|
- if (vol_end() != 0) {
|
|
- fprintf(stderr, "End of Volume not found: "
|
|
- "this dump is NOT valid.\n\n");
|
|
- rc = 1;
|
|
- goto out;
|
|
- } else {
|
|
- fprintf(stderr, "Reached End of Volume %i of a "
|
|
- "multi-volume tape dump.\n", header.dh_volnr);
|
|
- fprintf(stderr, "This part of the dump is valid.\n\n");
|
|
- goto out;
|
|
- }
|
|
- } else { /* "-i -a" option */
|
|
- fprintf(stderr, "\n> \"zgetdump -i -a\" checks if a "
|
|
- "multi-volume tape dump is valid.\n");
|
|
- fprintf(stderr, "> Please make sure that all volumes are "
|
|
- "loaded in sequence.\n");
|
|
- if (d_type == IS_DASD) {
|
|
- fprintf(stderr, "\"-i -a\" is used for validation of "
|
|
- "multi-volume tape dumps.\n\n");
|
|
- rc = 1;
|
|
- goto out;
|
|
- }
|
|
- print_s390_header(d_type);
|
|
- cur_volnr = header.dh_volnr;
|
|
- cur_time = header.dh_tod;
|
|
- fprintf(stderr, "\nChecking if the dump is valid - "
|
|
- "this might take a while...\n");
|
|
- tape_forwards(fd);
|
|
- if (dump_end_times(fd) == 0) {
|
|
- fprintf(stderr, "Dump End Marker found: "
|
|
- "this dump is valid.\n\n");
|
|
- goto out;
|
|
- } else if (vol_end() != 0) {
|
|
- fprintf(stderr, "End of Volume not found: "
|
|
- "this dump is NOT valid.\n\n");
|
|
- rc = 1;
|
|
- goto out;
|
|
- }
|
|
- while (vol_end() == 0) {
|
|
- cur_volnr += 1;
|
|
- fprintf(stderr, "Reached End of Volume %i.\n",
|
|
- header.dh_volnr);
|
|
- fprintf(stderr, "Waiting for Volume %i to be "
|
|
- "loaded... ", cur_volnr);
|
|
- load_next(fd);
|
|
- get_header(fd);
|
|
- print_s390_header(d_type);
|
|
- if (header.dh_volnr != cur_volnr) {
|
|
- fprintf(stderr, "This is not Volume %i\n",
|
|
- cur_volnr);
|
|
- rc = 1;
|
|
- goto out;
|
|
- } else if (header.dh_tod != cur_time) {
|
|
- fprintf(stderr, "Time stamp of this volume "
|
|
- "does not match the previous one.\n");
|
|
- rc = 1;
|
|
- goto out;
|
|
- }
|
|
- tape_forwards(fd);
|
|
- if (dump_end_times(fd) == 0) {
|
|
- fprintf(stderr, "Dump End found: "
|
|
- "this dump is valid.\n\n");
|
|
- goto out;
|
|
- } else if (vol_end() != 0) {
|
|
- fprintf(stderr, "End of Volume not found: "
|
|
- "this dump is NOT valid.\n\n");
|
|
- rc = 1;
|
|
- goto out;
|
|
- }
|
|
- }
|
|
- }
|
|
-out:
|
|
- if (fd != -1)
|
|
- close(fd);
|
|
- return(rc);
|
|
+ sig_handler_init();
|
|
+ opts_parse(argc, argv);
|
|
+
|
|
+ switch (g.opts.action) {
|
|
+ case ZG_ACTION_STDOUT:
|
|
+ return do_stdout();
|
|
+ case ZG_ACTION_DUMP_INFO:
|
|
+ return do_dump_info();
|
|
+ case ZG_ACTION_DEVICE_INFO:
|
|
+ return do_device_info();
|
|
+ case ZG_ACTION_MOUNT:
|
|
+ return do_mount();
|
|
+ case ZG_ACTION_UMOUNT:
|
|
+ return do_umount();
|
|
+ }
|
|
+ ABORT("Invalid action: %i", g.opts.action);
|
|
}
|
|
diff --git a/zdump/zgetdump.h b/zdump/zgetdump.h
|
|
index 46b427d..daf0ea1 100644
|
|
--- a/zdump/zgetdump.h
|
|
+++ b/zdump/zgetdump.h
|
|
@@ -1,194 +1,90 @@
|
|
/*
|
|
- * header file for zgetdump
|
|
- * Copyright IBM Corp. 2001, 2006.
|
|
- * Author(s): Despina Papadopoulou
|
|
+ * zgetdump - Tool for copying and converting System z dumps
|
|
+ *
|
|
+ * Main include file - Should be included by all source files
|
|
+ *
|
|
+ * Copyright IBM Corp. 2001, 2010
|
|
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
|
|
+ * Frank Munzert <munzert@de.ibm.com>
|
|
+ * Despina Papadopoulou
|
|
*/
|
|
|
|
-/* This header file holds the architecture specific crash dump header */
|
|
-#ifndef _ZGETDUMP_H
|
|
-#define _ZGETDUMP_H
|
|
+#ifndef ZGETDUMP_H
|
|
+#define ZGETDUMP_H
|
|
|
|
-#include <sys/time.h>
|
|
-#include <stdint.h>
|
|
-#include <sys/types.h>
|
|
-
|
|
-/* definitions (this has to match with vmdump.h of lcrash */
|
|
-
|
|
-#define DUMP_MAGIC_S390 0xa8190173618f23fdULL /* s390 magic number */
|
|
-#define DUMP_MAGIC_LKCD 0xa8190173618f23edULL /* lkcd magic number */
|
|
-#define DUMP_MAGIC_LIVE 0xa8190173618f23cdULL /* live magic number */
|
|
-
|
|
-#define S390_DUMP_HEADER_SIZE 4096
|
|
-#define MAX_DUMP_VOLUMES 32
|
|
-#define DUMP_ASM_MAGIC_NUMBER 0xdeaddeadULL /* magic number */
|
|
-
|
|
-#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
|
+#include "zg.h"
|
|
+#include "dfo.h"
|
|
+#include "dfi.h"
|
|
+#include "dt.h"
|
|
+#include "list.h"
|
|
+#include "df_s390.h"
|
|
+#include "df_elf.h"
|
|
+#include "df_lkcd.h"
|
|
|
|
/*
|
|
- * Structure: s390_dump_header_t
|
|
- * Function: This is the header dumped at the top of every valid s390 crash
|
|
- * dump.
|
|
+ * zgetdump options
|
|
*/
|
|
-
|
|
-typedef struct _s390_dump_header_s {
|
|
- /* the dump magic number -- unique to verify dump is valid */
|
|
- uint64_t dh_magic_number; /* 0x000 */
|
|
-
|
|
- /* the version number of this dump */
|
|
- uint32_t dh_version; /* 0x008 */
|
|
-
|
|
- /* the size of this header (in case we can't read it) */
|
|
- uint32_t dh_header_size; /* 0x00c */
|
|
-
|
|
- /* the level of this dump (just a header?) */
|
|
- uint32_t dh_dump_level; /* 0x010 */
|
|
-
|
|
- /* the size of a Linux memory page (4K, 8K, 16K, etc.) */
|
|
- uint32_t dh_page_size; /* 0x014 */
|
|
-
|
|
- /* the size of all physical memory */
|
|
- uint64_t dh_memory_size; /* 0x018 */
|
|
-
|
|
- /* the start of physical memory */
|
|
- uint64_t dh_memory_start; /* 0x020 */
|
|
-
|
|
- /* the end of physical memory */
|
|
- uint64_t dh_memory_end; /* 0x028 */
|
|
-
|
|
- /* the number of pages in this dump specifically */
|
|
- uint32_t dh_num_pages; /* 0x030 */
|
|
-
|
|
- /* ensure that dh_tod and dh_cpu_id are 8 byte aligned */
|
|
- uint32_t dh_pad; /* 0x034 */
|
|
-
|
|
- /* the time of the dump generation using stck */
|
|
- uint64_t dh_tod; /* 0x038 */
|
|
-
|
|
- /* cpu id */
|
|
- uint64_t dh_cpu_id; /* 0x040 */
|
|
-
|
|
- /* arch */
|
|
- uint32_t dh_arch; /* 0x048 */
|
|
-
|
|
- /* volume number */
|
|
- uint32_t dh_volnr; /* 0x04c */
|
|
-
|
|
- /* build arch */
|
|
- uint32_t dh_build_arch; /* 0x050 */
|
|
-
|
|
- /* real mem size */
|
|
- uint64_t dh_real_memory_size; /* 0x054 */
|
|
-
|
|
- /* multi-volume dump indicator */
|
|
- uint8_t dh_mvdump; /* 0x05c */
|
|
-
|
|
- /* fill up to 512 bytes */
|
|
- unsigned char end_pad1[0x200-0x05d]; /* 0x05d */
|
|
-
|
|
- /* the dump signature to verify a multi-volume dump partition */
|
|
- uint64_t dh_mvdump_signature; /* 0x200 */
|
|
-
|
|
- /* the time the partition was prepared for multi-volume dump */
|
|
- uint64_t dh_mvdump_zipl_time; /* 0x208 */
|
|
-
|
|
- /* fill up to 4096 byte */
|
|
- unsigned char end_pad2[0x1000-0x210]; /* 0x210 */
|
|
-
|
|
-} __attribute__((packed)) s390_dump_header_t;
|
|
+struct options {
|
|
+ int action_specified;
|
|
+ enum zg_action action;
|
|
+ char *device;
|
|
+ char *mount_point;
|
|
+ int fmt_specified;
|
|
+ const char *fmt;
|
|
+ int debug_specified;
|
|
+ char **argv_fuse;
|
|
+ int argc_fuse;
|
|
+};
|
|
|
|
/*
|
|
- * Structure: s390_dump_end_marker_t
|
|
- * Function: This end marker should be at the end of every valid s390 crash
|
|
- * dump.
|
|
+ * zgetdump globals
|
|
*/
|
|
-
|
|
-typedef struct _s390_dump_end_marker_{
|
|
- char end_string[8];
|
|
- unsigned long long end_time;
|
|
-} __attribute__((packed)) s390_dump_end_marker_t;
|
|
+extern struct zgetdump_globals {
|
|
+ struct zg_fh *fh;
|
|
+ const char *prog_name;
|
|
+ struct options opts;
|
|
+} g;
|
|
|
|
/*
|
|
- * Structure: lkcd 4.1 dump header
|
|
+ * Misc fuctions
|
|
*/
|
|
+extern void opts_parse(int argc, char *argv[]);
|
|
+extern int stdout_write_dump(void);
|
|
+
|
|
+#ifndef WITHOUT_FUSE
|
|
+extern int zfuse_mount_dump(void);
|
|
+extern void zfuse_umount(void);
|
|
+#else
|
|
+static inline int zfuse_mount_dump(void)
|
|
+{
|
|
+ ERR_EXIT("Program compiled without fuse support");
|
|
+}
|
|
+static inline void zfuse_umount(void)
|
|
+{
|
|
+ ERR_EXIT("Program compiled without fuse support");
|
|
+}
|
|
+#endif
|
|
|
|
-typedef struct _dump_header_s {
|
|
- uint64_t dh_magic_number;
|
|
- uint32_t dh_version;
|
|
- uint32_t dh_header_size;
|
|
- uint32_t dh_dump_level;
|
|
- uint32_t dh_dump_page_size;
|
|
- uint64_t dh_memory_size;
|
|
- uint64_t dh_memory_start;
|
|
- uint64_t dh_memory_end;
|
|
- uint32_t dh_num_dump_pages;
|
|
- char dh_panic_string[0x100];
|
|
- struct timeval dh_time;
|
|
- char dh_utsname_sysname[65];
|
|
- char dh_utsname_nodename[65];
|
|
- char dh_utsname_release[65];
|
|
- char dh_utsname_version[65];
|
|
- char dh_utsname_machine[65];
|
|
- char dh_utsname_domainname[65];
|
|
- void *dh_current_task;
|
|
- uint32_t dh_dump_compress;
|
|
- uint32_t dh_dump_flags;
|
|
- uint32_t dh_dump_device;
|
|
-} dump_header_4_1_t;
|
|
-
|
|
-struct dump_tool {
|
|
- char magic[7];
|
|
- uint8_t version;
|
|
- char code[0xff7 - 0x8];
|
|
- uint8_t force;
|
|
- uint64_t mem;
|
|
-} __attribute__ ((packed));
|
|
-
|
|
-struct mvdump_param {
|
|
- uint16_t devno;
|
|
- uint32_t start_blk;
|
|
- uint32_t end_blk;
|
|
- uint8_t blocksize;
|
|
- uint8_t end_sec;
|
|
- uint8_t num_heads;
|
|
-} __attribute__ ((packed));
|
|
-
|
|
-struct mvdump_parm_table {
|
|
- uint64_t timestamp;
|
|
- uint16_t num_param;
|
|
- struct mvdump_param param[MAX_DUMP_VOLUMES];
|
|
-} __attribute__ ((packed));
|
|
-
|
|
-enum dump_type {
|
|
- IS_TAPE = 0,
|
|
- IS_DASD = 1,
|
|
- IS_MULT_DASD = 2,
|
|
-};
|
|
-
|
|
-enum devnode_type {
|
|
- IS_DEVICE = 0,
|
|
- IS_PARTITION = 1,
|
|
- IS_NOBLOCK = 2,
|
|
-};
|
|
-
|
|
-enum device_status {
|
|
- ONLINE = 0,
|
|
- OFFLINE = 1,
|
|
- UNDEFINED = 2,
|
|
-};
|
|
+/*
|
|
+ * Supported DFI dump formats
|
|
+ */
|
|
+extern struct dfi dfi_s390tape;
|
|
+extern struct dfi dfi_s390mv;
|
|
+extern struct dfi dfi_s390;
|
|
+extern struct dfi dfi_lkcd;
|
|
+extern struct dfi dfi_elf;
|
|
+extern struct dfi dfi_kdump;
|
|
|
|
-enum device_signature {
|
|
- INVALID = 0,
|
|
- VALID = 1,
|
|
- ACTIVE = 2,
|
|
-};
|
|
+/*
|
|
+ * Supported DFO dump formats
|
|
+ */
|
|
+extern struct dfo dfo_s390;
|
|
+extern struct dfo dfo_elf;
|
|
|
|
-struct disk_info {
|
|
- dev_t device;
|
|
- enum device_status status;
|
|
- enum device_signature signature;
|
|
- off_t start_offset;
|
|
- uint64_t part_size;
|
|
- char bus_id[9];
|
|
-};
|
|
+/*
|
|
+ * Supported s390 dumpers
|
|
+ */
|
|
+extern struct dt dt_s390mv;
|
|
+extern struct dt dt_s390sv;
|
|
|
|
-#endif /* _ASM_VMDUMP_H */
|
|
+#endif /* ZGETDUMP_H */
|
|
diff --git a/zipl/boot/dumpcommon.S b/zipl/boot/dumpcommon.S
|
|
index d70473c..b37017c 100644
|
|
--- a/zipl/boot/dumpcommon.S
|
|
+++ b/zipl/boot/dumpcommon.S
|
|
@@ -47,23 +47,27 @@
|
|
|
|
#define __LC_ARCH_MODE_ID 163 /* here is the arch flag in the lowcore */
|
|
#define __LC_IPIB 0xe00 /* IPL Parameter Information Block */
|
|
+#define __LC_CPU_ADDRESS 0x0084 /* CPU address in lowcore */
|
|
#define DIAG308_IPL 3 /* Subcode 3 - Perform Load Clear */
|
|
#define DIAG308_SET 5 /* Subcode 5 - Set IPL Parameters */
|
|
|
|
-#define PARAM_START 0x3000 /* 8-byte time stamp plus 2-byte count */
|
|
+#define PARAM_START 0x4000 /* 8-byte time stamp plus 2-byte count */
|
|
/* plus 32 13-byte entries */
|
|
-#define IDA_LIST_START 0x3200 /* 64 8-byte IDAW's */
|
|
-#define CCW_CHAIN_START 0x3400 /* chained write CCW's */
|
|
-#define ZERO_MEM_START 0x4000
|
|
-#define ZERO_MEM_SIZE 0x3000
|
|
+#define IDA_LIST_START 0x4200 /* 64 8-byte IDAW's */
|
|
+#define CCW_CHAIN_START 0x4400 /* chained write CCW's */
|
|
+#define ZERO_MEM_START 0x6000 /* Zero pages start */
|
|
+#define ZERO_MEM_SIZE 0x3000 /* Init three zero pages */
|
|
|
|
#define SCPINCR1_OFF 8
|
|
#define SCPA1_OFF 10
|
|
#define SCPA2_OFF 100
|
|
#define SCPINCR2_OFF 104
|
|
|
|
-#define ZERO_PAGE_START 0x5000
|
|
-#define TMP_PAGE_START 0x6000
|
|
+
|
|
+#define ZERO_PAGE_START 0x6000 /* Zero page */
|
|
+#define HEADER_PAGE_START 0x5000 /* Dump header page */
|
|
+#define PREFIX_ARR_START 0x5800 /* Prefix page array in dump header */
|
|
+#define TMP_PAGE_START 0x7000 /* Page for temp storage */
|
|
|
|
################################################################################
|
|
# MACRO: dump_header
|
|
@@ -77,7 +81,7 @@
|
|
#
|
|
.Ldh_dumpheader:
|
|
.Ldh_magic_number:.long S390_DUMP_MAGIC
|
|
-.Ldh_version: .long 0x00000004
|
|
+.Ldh_version: .long 0x00000005
|
|
.Ldh_header_size: .long HEADER_SIZE
|
|
.Ldh_dump_level: .long 0x00000004 # DUMP_ALL
|
|
.Ldh_page_size: .long PAGE_SIZE
|
|
@@ -97,6 +101,8 @@
|
|
#endif
|
|
.Ldh_real_mem_size: .long 0x00000000,0x00000000
|
|
.Ldh_mvdump: .byte 0x00
|
|
+.Ldh_cpu_cnt: .byte 0x00,0x00
|
|
+.Ldh_real_cpu_cnt:.byte 0x00,0x00
|
|
|
|
#
|
|
# Dump End Marker
|
|
@@ -258,10 +264,13 @@ _ssch_64:
|
|
lgr %r3,%r10 # and irb address as parameters
|
|
bas %r14,_wait4de_64-0b(%r13) # wait until DE or error
|
|
tm 9(%r10),0xff # test channel status
|
|
- bnz 5f-0b(%r13)
|
|
- tm 8(%r10),0xd2 # test device status
|
|
+ bz 3f-0b(%r13)
|
|
+ bct %r9,1b-0b(%r13) # something went wrong, retry.
|
|
+3: tm 8(%r10),0xd2 # test device status
|
|
bz 4f-0b(%r13)
|
|
bct %r9,1b-0b(%r13) # something went wrong, retry.
|
|
+ j 5f # retries failed -> panic
|
|
+
|
|
4: lmg %r6,%r15,248(%r15)
|
|
br %r14
|
|
|
|
@@ -364,7 +373,8 @@ _take_dump_64:
|
|
spt .Lcpu_timer-.Lbase(%r13) # set cpu timer to future
|
|
|
|
lghi %r6,ZERO_MEM_START # clear memory
|
|
- lghi %r7,ZERO_MEM_START + ZERO_MEM_SIZE
|
|
+ lgr %r7,%r6
|
|
+ aghi %r7,ZERO_MEM_SIZE
|
|
sgr %r7,%r6
|
|
sgr %r8,%r8
|
|
sgr %r9,%r9
|
|
@@ -395,6 +405,14 @@ _take_dump_64:
|
|
|
|
bas %r14,_count_mem_64-.Lbase(%r13)
|
|
|
|
+ # copy dump header
|
|
+
|
|
+ stck .Ldh_time-.Lbase(%r13) # store time
|
|
+ stidp .Ldh_cpuid-.Lbase(%r13) # store CPU ID
|
|
+
|
|
+ lghi %r3,HEADER_PAGE_START
|
|
+ mvc 0(256,%r3),.Ldh_dumpheader-.Lbase(%r13)
|
|
+
|
|
# dump memory
|
|
|
|
llgf %r14,.Ldump_mem_64-.Lbase(%r13)
|
|
@@ -448,7 +466,8 @@ _count_mem_64:
|
|
mlgr %r2,%r1 # mem size in bytes in %r3
|
|
|
|
stg %r3,.Ldh_real_mem_size-0b(%r13)
|
|
- lg %r6,.Lmem_upper_limit-0b(%r13) # check if we have an upper limit
|
|
+ larl %r7,.Lmem_upper_limit
|
|
+ lg %r6,0(%r7) # check if we have an upper limit
|
|
clgr %r3,%r6
|
|
bl .Lsavemem-0b(%r13)
|
|
lgr %r3,%r6 # upper mem limit set -> use it!
|
|
@@ -458,6 +477,11 @@ _count_mem_64:
|
|
srlg %r12,%r3,12 # calculate page count (/ 4096)
|
|
st %r12,.Ldh_num_pages-0b(%r13) # store page count
|
|
|
|
+ clgr %r6,%r3
|
|
+ bne .Lexit-0b(%r13)
|
|
+ larl %r2,.Lmsg_mem_limit_set # print mem limit warning
|
|
+ bras %r14,_sclp_print
|
|
+.Lexit:
|
|
lmg %r6,%r15,248(%r15)
|
|
br %r14
|
|
.Lonemb:
|
|
@@ -473,29 +497,31 @@ _store_status_64:
|
|
stmg %r6,%r15,48(%r15)
|
|
basr %r13,0 # base register
|
|
0: aghi %r15,-200
|
|
- lghi %r7,0x0 # base register for 0 page
|
|
-
|
|
- ######## move lowcore info (assume user has made store ########
|
|
- ######## status) to prefix-page ########
|
|
-
|
|
- bas %r14,_copy_lowcore_64-0b(%r13)
|
|
|
|
######## stop all cpus and store status in prefix pages ########
|
|
|
|
lghi %r8,0 # first cpu
|
|
stap .Lcurrent_cpu_64+2-0b(%r13) # store current cpu address
|
|
+ llgh %r6,.Lcurrent_cpu_64+2-0b(%r13)
|
|
+
|
|
+ ######## move lowcore info (assume user has made store ########
|
|
+ ######## status) to prefix-page ########
|
|
+
|
|
+ sth %r6,__LC_CPU_ADDRESS(%r0)
|
|
+ bas %r14,_copy_lowcore_64-0b(%r13)
|
|
|
|
1:
|
|
cl %r8,.Lcurrent_cpu_64-0b(%r13) # is ipl cpu ?
|
|
be 4f-0b(%r13) # if yes get next cpu
|
|
2:
|
|
- lgr %r9,%r7
|
|
+ lghi %r9,0
|
|
sigp %r9,%r8,0x9 # store status of cpu
|
|
bc 8,3f-0b(%r13) # accepted
|
|
bc 4,4f-0b(%r13) # status stored: next cpu
|
|
bc 2,2b-0b(%r13) # busy: try again
|
|
bc 1,4f-0b(%r13) # not op: next cpu
|
|
3:
|
|
+ sth %r8,__LC_CPU_ADDRESS(%r0)
|
|
bas %r14,_copy_lowcore_64-0b(%r13)
|
|
4:
|
|
aghi %r8,1 # next cpu (r8 +=1)
|
|
@@ -534,8 +560,17 @@ _copy_lowcore_64:
|
|
###### copy lowcore ######
|
|
|
|
llgf %r3,792(%r2) # get prefix page of current cpu
|
|
- lghi %r5,0x1000 # first page
|
|
- agr %r3,%r5 # get base register for second
|
|
+
|
|
+ lgh %r8,.Ldh_cpu_cnt-0b(%r13) # Save lowcore pointer (32 bit)
|
|
+ sll %r8,2 # in dump header
|
|
+ lghi %r9,PREFIX_ARR_START
|
|
+ agr %r9,%r8
|
|
+ st %r3,0(%r9)
|
|
+
|
|
+ lgh %r8,__LC_CPU_ADDRESS(%r0) # copy cpu address
|
|
+ sth %r8,__LC_CPU_ADDRESS(%r3) # to lowcore
|
|
+
|
|
+ aghi %r3,0x1000 # get base register for second
|
|
# page of prefix pages
|
|
|
|
# |-----------------------------------------------------------|
|
|
@@ -562,7 +597,14 @@ _copy_lowcore_64:
|
|
mvc 804(20,%r3),804(%r2) # 4900
|
|
mvc 832(192,%r3),832(%r2) # 4928
|
|
|
|
+ lgh %r8,.Ldh_cpu_cnt-0b(%r13) # Increment (online) CPU count
|
|
+ aghi %r8,1
|
|
+ sth %r8,.Ldh_cpu_cnt-0b(%r13)
|
|
.Lcpy_locore_exit_64:
|
|
+ lgh %r10,.Ldh_real_cpu_cnt-0b(%r13) # Increment real CPU count
|
|
+ aghi %r10,1
|
|
+ sth %r10,.Ldh_real_cpu_cnt-0b(%r13)
|
|
+
|
|
lmg %r6,%r15,248(%r15)
|
|
br %r14 # return to caller
|
|
.Lpage_align_64:
|
|
@@ -755,10 +797,12 @@ _ssch_32:
|
|
lr %r3,%r10 # and irb address as parameters
|
|
bas %r14,_wait4de_32-0b(%r13) # wait until DE or error
|
|
tm 9(%r10),0xff # test channel status
|
|
- bnz 5f-0b(%r13)
|
|
- tm 8(%r10),0xd2 # f3 test device status
|
|
+ bz 3f-0b(%r13)
|
|
+ bct %r9,1b-0b(%r13) # something went wrong, retry.
|
|
+3: tm 8(%r10),0xd2 # test device status
|
|
bz 4f-0b(%r13)
|
|
bct %r9,1b-0b(%r13) # something went wrong, retry.
|
|
+ j 5f # retries failed -> panic
|
|
|
|
4: lm %r6,%r15,120(%r15)
|
|
br %r14
|
|
@@ -864,7 +908,8 @@ _take_dump_32:
|
|
spt .Lcpu_timer-.Lbase(%r13) # set cpu timer to future
|
|
|
|
lhi %r6,ZERO_MEM_START # clear memory
|
|
- lhi %r7,ZERO_MEM_START + ZERO_MEM_SIZE
|
|
+ lhi %r7,ZERO_MEM_START
|
|
+ ahi %r7,ZERO_MEM_SIZE
|
|
sr %r7,%r6
|
|
sr %r8,%r8
|
|
sr %r9,%r9
|
|
@@ -897,6 +942,14 @@ _take_dump_32:
|
|
|
|
bas %r14,_count_mem_32-.Lbase(%r13)
|
|
|
|
+ # copy dump header
|
|
+
|
|
+ stck .Ldh_time-.Lbase(%r13) # store time
|
|
+ stidp .Ldh_cpuid-.Lbase(%r13) # store CPU ID
|
|
+
|
|
+ lhi %r3,HEADER_PAGE_START
|
|
+ mvc 0(256,%r3),.Ldh_dumpheader-.Lbase(%r13)
|
|
+
|
|
# dump memory
|
|
|
|
l %r14,.Ldump_mem_32-.Lbase(%r13)
|
|
@@ -952,15 +1005,22 @@ _count_mem_32:
|
|
mr %r2,%r1 # mem size in bytes in %r3
|
|
|
|
st %r3,.Ldh_real_mem_size+4-0b(%r13)
|
|
- cl %r6,.Lmem_upper_limit+4-0b(%r13) # check if we have an upper limit
|
|
+ larl %r7,.Lmem_upper_limit+4
|
|
+ l %r6,0(%r7) # check if we have an upper limit
|
|
+ clr %r3,%r6
|
|
bl .Lsavemem-0b(%r13)
|
|
- l %r3,.Lmem_upper_limit+4-0b(%r13) # upper mem limit set -> use it!
|
|
+ lr %r3,%r6 # upper mem limit set -> use it!
|
|
.Lsavemem:
|
|
st %r3,.Ldh_mem_size+4-0b(%r13) # store memory size
|
|
st %r3,.Ldh_mem_end+4-0b(%r13) # store memory end
|
|
srl %r3,12 # calculate page count (/ 4096)
|
|
st %r3,.Ldh_num_pages-0b(%r13) # store page count
|
|
|
|
+ clr %r6,%r3
|
|
+ bne .Lexit-0b(%r13)
|
|
+ larl %r2,.Lmsg_mem_limit_set # print mem limit warning
|
|
+ bras %r14,_sclp_print
|
|
+.Lexit:
|
|
lm %r6,%r15,120(%r15)
|
|
br %r14
|
|
.Lonemb:
|
|
@@ -1112,6 +1172,15 @@ _copy_lowcore_32:
|
|
|
|
###### copy lowcore ######
|
|
|
|
+ lh %r8,.Ldh_cpu_cnt-0b(%r13) # Save lowcore pointer (32 bit)
|
|
+ sll %r8,2 # in dump header
|
|
+ lhi %r9,PREFIX_ARR_START
|
|
+ ar %r9,%r8
|
|
+ st %r3,0(%r9)
|
|
+
|
|
+ lh %r8,__LC_CPU_ADDRESS(%r0) # copy cpu address
|
|
+ sth %r8,__LC_CPU_ADDRESS(%r3) # to lowcore
|
|
+
|
|
# |-----------------------------------------------------------|
|
|
# | Decimal | Length | Data |
|
|
# | Address | in Bytes | |
|
|
@@ -1131,7 +1200,14 @@ _copy_lowcore_32:
|
|
mvc 256(12,%r3),256(%r0)
|
|
mvc 288(224,%r3),288(%r0)
|
|
|
|
+ lh %r8,.Ldh_cpu_cnt-0b(%r13) # Increment (online) CPU count
|
|
+ ahi %r8,1
|
|
+ sth %r8,.Ldh_cpu_cnt-0b(%r13)
|
|
.Lcpy_locore_exit:
|
|
+ lh %r10,.Ldh_real_cpu_cnt-0b(%r13) # Increment real CPU count
|
|
+ ahi %r10,1
|
|
+ sth %r10,.Ldh_real_cpu_cnt-0b(%r13)
|
|
+
|
|
lm %r6,%r15,120(%r15)
|
|
br %r14 # return to caller
|
|
.Lpage_align:
|
|
@@ -1303,6 +1379,14 @@ _print_exit_message:
|
|
.byte 0xf4, 0x40, 0x82, 0x89, 0xa3, 0x40, 0xd6, 0xe2
|
|
.byte 0x00
|
|
|
|
+# INFO: Using memory limit
|
|
+
|
|
+.Lmsg_mem_limit_set:
|
|
+ .byte 0xc9, 0xd5, 0xc6, 0xd6, 0x7a
|
|
+ .byte 0x40, 0xe4, 0xa2, 0x89, 0x95, 0x87, 0x40, 0x94
|
|
+ .byte 0x85, 0x94, 0x96, 0x99, 0xa8, 0x40, 0x93, 0x89
|
|
+ .byte 0x94, 0x89, 0xa3, 0x25, 0x00
|
|
+
|
|
# "00000000 / 00000000 MB"
|
|
|
|
.Lmsg_progress_mb:
|
|
diff --git a/zipl/boot/eckd2dump.S b/zipl/boot/eckd2dump.S
|
|
index bb5a6f5..5c437bc 100644
|
|
--- a/zipl/boot/eckd2dump.S
|
|
+++ b/zipl/boot/eckd2dump.S
|
|
@@ -21,7 +21,7 @@
|
|
|
|
/* General defines */
|
|
|
|
-#define IPL_BS 0x1000
|
|
+#define IPL_BS 0x2000
|
|
#define BLOCKS_PER_WRITE 64 /* makes 256K with 4K blksize */
|
|
|
|
################################################################################
|
|
@@ -32,9 +32,9 @@
|
|
################################################################################
|
|
|
|
#if defined(__s390x__)
|
|
-dump_magic: .long 0x5a45434b, 0x44363401 # "ZECKD64", version 1
|
|
+dump_magic: .long 0x5a45434b, 0x44363402 # "ZECKD64", version 2
|
|
#else
|
|
-dump_magic: .long 0x5a45434b, 0x44333101 # "ZECKD31", version 1
|
|
+dump_magic: .long 0x5a45434b, 0x44333102 # "ZECKD31", version 2
|
|
#endif
|
|
|
|
#if defined(__s390x__)
|
|
@@ -153,20 +153,14 @@ _dump_mem_64:
|
|
# write header
|
|
|
|
.Lheaders: # write dump headers
|
|
- stck .Ldh_time-0b(%r13) # store time
|
|
- stidp .Ldh_cpuid-0b(%r13) # store cpu id
|
|
-
|
|
llgf %r11,.Ldev_start_blk-0b(%r13) # start block
|
|
|
|
lgr %r2,%r11
|
|
- lghi %r3,TMP_PAGE_START
|
|
- mvc 0(256,%r3),.Ldh_dumpheader-0b(%r13)
|
|
- # copy dump header to page
|
|
- # boundary
|
|
- llgf %r4,.Lheader_size-0b(%r13)
|
|
+ lghi %r3,HEADER_PAGE_START
|
|
+ lghi %r4,HEADER_SIZE
|
|
srda %r4,32 # shift ==> 64 bit number
|
|
llgf %r6,.Ldev_blk_size-0b(%r13) # get blocksize
|
|
-
|
|
+
|
|
dr %r4,%r6 # nr of blocks for header =
|
|
# HEADER_SIZE / BLOCKSIZE = r5
|
|
lgr %r4,%r5
|
|
@@ -208,7 +202,6 @@ _dump_mem_64:
|
|
lmg %r6,%r15,248(%r15)
|
|
br %r14 # return to caller
|
|
.Lbytes_per_write: .long 0x00000000
|
|
-.Lheader_size: .long HEADER_SIZE
|
|
.Lblocks_per_write: .word BLOCKS_PER_WRITE
|
|
|
|
################################################################################
|
|
@@ -503,20 +496,14 @@ _dump_mem_32:
|
|
# write header
|
|
|
|
.Lheaders: # write dump headers
|
|
- stck .Ldh_time-0b(%r13) # store time
|
|
- stidp .Ldh_cpuid-0b(%r13) # store cpu id
|
|
-
|
|
l %r11,.Ldev_start_blk-0b(%r13) # start block
|
|
|
|
lr %r2,%r11
|
|
- lhi %r3,TMP_PAGE_START
|
|
- mvc 0(256,%r3),.Ldh_dumpheader-0b(%r13)
|
|
- # copy dump header to page
|
|
- # boundary
|
|
- l %r4,.Lheader_size-0b(%r13)
|
|
+ lhi %r3,HEADER_PAGE_START
|
|
+ lhi %r4,HEADER_SIZE
|
|
srda %r4,32 # shift ==> 64 bit number
|
|
l %r6,.Ldev_blk_size-0b(%r13) # get blocksize
|
|
-
|
|
+
|
|
dr %r4,%r6 # nr of blocks for header =
|
|
# HEADER_SIZE / BLOCKSIZE = r5
|
|
lr %r4,%r5
|
|
@@ -559,7 +546,6 @@ _dump_mem_32:
|
|
lm %r6,%r15,120(%r15)
|
|
br %r14 # return to caller
|
|
.Lbytes_per_write: .long 0x00000000
|
|
-.Lheader_size: .long HEADER_SIZE
|
|
.Lblocks_per_write: .word BLOCKS_PER_WRITE
|
|
|
|
################################################################################
|
|
diff --git a/zipl/boot/eckd2mvdump.S b/zipl/boot/eckd2mvdump.S
|
|
index 2b1be43..17b959d 100644
|
|
--- a/zipl/boot/eckd2mvdump.S
|
|
+++ b/zipl/boot/eckd2mvdump.S
|
|
@@ -13,7 +13,7 @@
|
|
|
|
/* General defines */
|
|
|
|
-#define MVDUMP_TOOL_SIZE 0x1000 /* length of dump tool without parmtable */
|
|
+#define MVDUMP_TOOL_SIZE 0x2000 /* length of dump tool without parmtable */
|
|
#define BLOCKS_PER_WRITE 64 /* makes 256K with 4Ki blksize */
|
|
#define PTE_LENGTH 13 /* length of parameter table entry */
|
|
#define MAGIC_BLOCK_OFFSET 3 /* dump tool starts on track 0, block 3 */
|
|
@@ -25,7 +25,7 @@
|
|
# %r4 : load address
|
|
################################################################################
|
|
|
|
-dump_magic: .long 0x5a4d554c, 0x54363401 # "ZMULT64", version 1
|
|
+dump_magic: .long 0x5a4d554c, 0x54363402 # "ZMULT64", version 1
|
|
|
|
/******************************** 64 BIT only**********************************/
|
|
|
|
@@ -102,8 +102,6 @@ _dump_mem_64:
|
|
bras %r14,_init_print_progress_64
|
|
|
|
# prepare dump header info
|
|
- stck .Ldh_time-0b(%r13) # store time
|
|
- stidp .Ldh_cpuid-0b(%r13) # store cpu id
|
|
mvi .Ldh_mvdump-0b(%r13),0x01 # store mvdump indicator
|
|
|
|
.Lnextvol:
|
|
@@ -133,7 +131,9 @@ _dump_mem_64:
|
|
lghi %r2,CCW_CHAIN_START # point to 1st CCW in chain
|
|
mvi 16(%r2),0x86 # move read opcode into CCW
|
|
lhi %r2,MAGIC_BLOCK_OFFSET # start block of dump tool
|
|
- ar %r2,%r5 # start block of parameter table
|
|
+ lgr %r10,%r5 # mvdumptool size = 0x2000
|
|
+ sll %r10,1 # (header size * 2)
|
|
+ ar %r2,%r10 # start block of parameter table
|
|
lghi %r3,TMP_PAGE_START # destination of read operation
|
|
lghi %r4,1 # number of blocks to read
|
|
bas %r14,_ioblock_64-0b(%r13) # read parameter table
|
|
@@ -152,7 +152,8 @@ _dump_mem_64:
|
|
# The dump signature is located at offset 512 relative to the partition start
|
|
|
|
.Lcheck_sign:
|
|
- tm .Lforce-0b(%r13),0x01 # was zipl --force specified?
|
|
+ larl %r7,.Lforce
|
|
+ tm 0(%r7),0x01 # was zipl --force specified?
|
|
bo .Lheaders-0b(%r13) # yes, skip signature check
|
|
llgf %r2,.Ldev_start_blk-0b(%r13) # start block of partition
|
|
lghi %r3,TMP_PAGE_START # destination of read operation
|
|
@@ -178,9 +179,7 @@ _dump_mem_64:
|
|
llgf %r11,.Ldev_start_blk-0b(%r13) # start block
|
|
|
|
lgr %r2,%r11
|
|
- lghi %r3,TMP_PAGE_START
|
|
- mvc 0(256,%r3),.Ldh_dumpheader-0b(%r13) # copy dump header to page
|
|
- # boundary
|
|
+ lghi %r3,HEADER_PAGE_START
|
|
mvc 512(8,%r3),.Ldh_magic_number-0b(%r13) # preserve signature
|
|
lgr %r4,%r5
|
|
lgr %r12,%r5 # save nr of blocks
|
|
@@ -198,6 +197,8 @@ _dump_mem_64:
|
|
l %r12,.Ldh_vol_nr-0b(%r13) # get current volume number
|
|
ahi %r12,1 # increment volume number
|
|
st %r12,.Ldh_vol_nr-0b(%r13) # store next volume number
|
|
+ lghi %r3,HEADER_PAGE_START
|
|
+ mvc 0(256,%r3),.Ldh_dumpheader-0b(%r13)
|
|
lhi %r11,PARAM_START #
|
|
ch %r12,8(%r11) # last dump target?
|
|
bl .Lmloop2-0b(%r13) # no, initialize next target
|
|
diff --git a/zipl/boot/fba0.S b/zipl/boot/fba0.S
|
|
index 71f508f..14e65dc 100644
|
|
--- a/zipl/boot/fba0.S
|
|
+++ b/zipl/boot/fba0.S
|
|
@@ -35,9 +35,25 @@ _start:
|
|
.long 0x43000000+.Llo6+512,0x40000008 # locate record 6
|
|
.long 0x42002C00,0x60000200 # bytes 3072-3584 of 2nd stage
|
|
.long 0x43000000+.Llo7+512,0x40000008 # locate record 7
|
|
- .long 0x42002E00,0x20000200 # bytes 3584-4096 of 2nd stage
|
|
-# offset 2 in .Llo[0-7]: block count (unsigned short) = 1
|
|
-# offset 4 in .Llo[0-7]: block number (unsigned long)
|
|
+ .long 0x42002E00,0x60000200 # bytes 3584-4096 of 2nd stage
|
|
+ .long 0x43000000+.Llo8+512,0x40000008 # locate record 8
|
|
+ .long 0x42003000,0x60000200 # bytes 4096-4608 of 2nd stage
|
|
+ .long 0x43000000+.Llo9+512,0x40000008 # locate record 9
|
|
+ .long 0x42003200,0x60000200 # bytes 4608-5120 of 2nd stage
|
|
+ .long 0x43000000+.Llo10+512,0x40000008 # locate record 10
|
|
+ .long 0x42003400,0x60000200 # bytes 5120-5632 of 2nd stage
|
|
+ .long 0x43000000+.Llo11+512,0x40000008 # locate record 11
|
|
+ .long 0x42003600,0x60000200 # bytes 5632-6144 of 2nd stage
|
|
+ .long 0x43000000+.Llo12+512,0x40000008 # locate record 12
|
|
+ .long 0x42003800,0x60000200 # bytes 6144-6656 of 2nd stage
|
|
+ .long 0x43000000+.Llo13+512,0x40000008 # locate record 13
|
|
+ .long 0x42003A00,0x60000200 # bytes 6656-7168 of 2nd stage
|
|
+ .long 0x43000000+.Llo14+512,0x40000008 # locate record 14
|
|
+ .long 0x42003C00,0x60000200 # bytes 7168-7680 of 2nd stage
|
|
+ .long 0x43000000+.Llo15+512,0x40000008 # locate record 15
|
|
+ .long 0x42003E00,0x20000200 # bytes 7680-8192 of 2nd stage
|
|
+# offset 2 in .Llo[0-15]: block count (unsigned short) = 1
|
|
+# offset 4 in .Llo[0-15]: block number (unsigned long)
|
|
.Llo0: .long 0x06000001,0x00000000
|
|
.Llo1: .long 0x06000001,0x00000000
|
|
.Llo2: .long 0x06000001,0x00000000
|
|
@@ -46,4 +62,12 @@ _start:
|
|
.Llo5: .long 0x06000001,0x00000000
|
|
.Llo6: .long 0x06000001,0x00000000
|
|
.Llo7: .long 0x06000001,0x00000000
|
|
+.Llo8: .long 0x06000001,0x00000000
|
|
+.Llo9: .long 0x06000001,0x00000000
|
|
+.Llo10: .long 0x06000001,0x00000000
|
|
+.Llo11: .long 0x06000001,0x00000000
|
|
+.Llo12: .long 0x06000001,0x00000000
|
|
+.Llo13: .long 0x06000001,0x00000000
|
|
+.Llo14: .long 0x06000001,0x00000000
|
|
+.Llo15: .long 0x06000001,0x00000000
|
|
.Lend:
|
|
diff --git a/zipl/boot/fba2dump.S b/zipl/boot/fba2dump.S
|
|
index e14d047..c0366b6 100644
|
|
--- a/zipl/boot/fba2dump.S
|
|
+++ b/zipl/boot/fba2dump.S
|
|
@@ -20,7 +20,7 @@
|
|
|
|
/* General defines */
|
|
|
|
-#define IPL_BS 0x1000
|
|
+#define IPL_BS 0x2000
|
|
#define BLOCKS_PER_WRITE 64
|
|
#define FBA_BLK_SIZE 0x200
|
|
#define STAGE2_DESC 0x218
|
|
@@ -33,9 +33,9 @@
|
|
################################################################################
|
|
|
|
#if defined(__s390x__)
|
|
-dump_magic: .long 0x5a444642, 0x41363401 # ZDFBA64, version 1
|
|
+dump_magic: .long 0x5a444642, 0x41363402 # ZDFBA64, version 2
|
|
#else
|
|
-dump_magic: .long 0x5a444642, 0x41333101 # ZDFBA31, version 1
|
|
+dump_magic: .long 0x5a444642, 0x41333102 # ZDFBA31, version 2
|
|
#endif
|
|
|
|
#if defined(__s390x__)
|
|
@@ -129,18 +129,14 @@ _dump_mem_64:
|
|
|
|
# write header
|
|
|
|
- stck .Ldh_time-0b(%r13) # store time
|
|
- stidp .Ldh_cpuid-0b(%r13) # store cpu id
|
|
-
|
|
llgf %r11,.Ldev_start_blk-0b(%r13) # start block
|
|
|
|
lgr %r2,%r11
|
|
- lghi %r3, TMP_PAGE_START
|
|
- mvc 0(256,%r3),.Ldh_dumpheader-0b(%r13) # move to 4k boundary
|
|
- llgf %r4,.Lheader_size-0b(%r13)
|
|
+ lghi %r3,HEADER_PAGE_START
|
|
+ lghi %r4,HEADER_SIZE
|
|
srda %r4,32 # shift ==> 64 bit number
|
|
llgf %r6,.Ldev_blk_size-0b(%r13) # get blocksize
|
|
-
|
|
+
|
|
dr %r4,%r6 # nr of blocks for header =
|
|
# HEADER_SIZE / BLOCKSIZE = r5
|
|
lgr %r4,%r5
|
|
@@ -181,7 +177,6 @@ _dump_mem_64:
|
|
lmg %r6,%r15,248(%r15)
|
|
br %r14 # return to caller
|
|
.Lbytes_per_write: .long 0x00000000
|
|
-.Lheader_size: .long HEADER_SIZE
|
|
.Lblocks_per_write: .word BLOCKS_PER_WRITE
|
|
|
|
################################################################################
|
|
@@ -363,20 +358,14 @@ _dump_mem_32:
|
|
st %r11,.Lbytes_per_write-0b(%r13)
|
|
|
|
# write header
|
|
-
|
|
- stck .Ldh_time-0b(%r13) # store time
|
|
- stidp .Ldh_cpuid-0b(%r13) # store cpu id
|
|
-
|
|
l %r11,.Ldev_start_blk-0b(%r13) # start block
|
|
|
|
lr %r2,%r11
|
|
- lhi %r3, TMP_PAGE_START
|
|
- mvc 0(256,%r3),.Ldh_dumpheader-0b(%r13) # move to 4k boundary
|
|
-
|
|
- l %r4,.Lheader_size-0b(%r13)
|
|
+ lhi %r3,HEADER_PAGE_START
|
|
+ lhi %r4,HEADER_SIZE
|
|
srda %r4,32 # shift ==> 64 bit number
|
|
l %r6,.Ldev_blk_size-0b(%r13) # get blocksize
|
|
-
|
|
+
|
|
dr %r4,%r6 # nr of blocks for header =
|
|
# HEADER_SIZE / BLOCKSIZE = r5
|
|
lr %r4,%r5
|
|
@@ -418,7 +407,6 @@ _dump_mem_32:
|
|
lm %r6,%r15,120(%r15)
|
|
br %r14 # return to caller
|
|
.Lbytes_per_write: .long 0x00000000
|
|
-.Lheader_size: .long HEADER_SIZE
|
|
.Lblocks_per_write: .word BLOCKS_PER_WRITE
|
|
|
|
################################################################################
|
|
diff --git a/zipl/boot/tapedump.S b/zipl/boot/tapedump.S
|
|
index f7f25e6..aded0a8 100644
|
|
--- a/zipl/boot/tapedump.S
|
|
+++ b/zipl/boot/tapedump.S
|
|
@@ -21,20 +21,15 @@
|
|
#endif
|
|
#include "sclp.S"
|
|
|
|
-#define IPL_BS 0x1000
|
|
+#define IPL_BS 0x2000
|
|
#define BLOCK_SIZE 0x8000 /* 32 KB */
|
|
#define DUMP_TOOL_START 0x2000 /* tool is loaded to this address in order */
|
|
/* not to overwrite page 0 */
|
|
-#define EOV_MARKER_SIZE 8
|
|
-#define EOV_LABEL 0x454e444f,0x46564f4c /* ENDOFVOL */
|
|
-
|
|
/* Tape display messages */
|
|
|
|
#ifdef LOWER_CASE
|
|
-#define DISP_NEXT_VOL 0x409585a7,0xa35ca596,0x93000000 /* next*vol */
|
|
#define DISP_DUMP_END 0x2084a494,0x975c8595,0x84000000 /* dump*end */
|
|
#else
|
|
-#define DISP_NEXT_VOL 0x40d5c5e7,0xe35ce5d6,0xd3000000 /* NEXT*VOL */
|
|
#define DISP_DUMP_END 0x20c4e4d4,0xd75cc5d5,0xc4000000 /* DUMP*END */
|
|
#endif
|
|
|
|
@@ -73,18 +68,16 @@
|
|
_start:
|
|
basr %r13,0
|
|
.Linit_base:
|
|
- la %r9,0
|
|
- st %r9,.Ldh_arch-.Linit_base(%r13) # init arch
|
|
+ xc .Ldh_arch-.Linit_base(4,%r13),.Ldh_arch-.Linit_base(%r13)
|
|
l %r15,1f-.Linit_base(%r13) # load end of stack address
|
|
- tm __LC_ARCH_MODE_ID(%r9),0x01 # check arch mode
|
|
+ tm __LC_ARCH_MODE_ID(%r0),0x01 # check arch mode
|
|
bnz .Larch_64-.Linit_base(%r13)
|
|
|
|
/* 32 bit store status */
|
|
|
|
- l %r14,.Lstore_status_32-.Linit_base(%r13)
|
|
+ larl %r14,.Lstore_status_32
|
|
basr %r14,%r14
|
|
- la %r10,ARCH_S390_ID
|
|
- st %r10,.Ldh_arch-.Linit_base(%r13)
|
|
+ mvi .Ldh_arch+3-.Linit_base(%r13),ARCH_S390_ID
|
|
.Larch_64:
|
|
la %r7,2 # first try code 2:
|
|
la %r6,0 # 64 bit psws are restored
|
|
@@ -103,16 +96,16 @@ _start:
|
|
|
|
/* 64 bit store status */
|
|
|
|
- llgf %r14,.Lstore_status_64-0b(%r13)
|
|
+ larl %r14,_store_status_64
|
|
basr %r14,%r14
|
|
lghi %r10,ARCH_S390X_ID
|
|
st %r10,.Ldh_arch-0b(%r13)
|
|
-.Larch_32:
|
|
+.Larch_32:
|
|
llgf %r2,IPL_SC # load ipl device subchannel id
|
|
- llgf %r14,.Lenable_device_64-0b(%r13)
|
|
+ larl %r14,_enable_device_64
|
|
basr %r14,%r14
|
|
- bas %r14,_get_device_characteristics_64-0b(%r13)
|
|
- llgf %r14,.Ltake_dump_64-0b(%r13)
|
|
+ bas %r14,_get_device_characteristics_64-0b(%r13)
|
|
+ larl %r14,_take_dump_64
|
|
basr %r14,%r14
|
|
1: .long 0x10000-128 # end of stack
|
|
|
|
@@ -138,11 +131,8 @@ _dump_mem_64:
|
|
#
|
|
# write header
|
|
#
|
|
- stck .Ldh_time-0b(%r13) # store time
|
|
- stidp .Ldh_cpuid-0b(%r13) # store cpu id
|
|
- lghi %r2, TMP_PAGE_START
|
|
- mvc 0(256,%r2),.Ldh_dumpheader-0b(%r13) # move to 4k boundary
|
|
- llgf %r3,.Lheader_size-0b(%r13) # size of header
|
|
+ lghi %r2,HEADER_PAGE_START
|
|
+ lghi %r3,HEADER_SIZE # size of header
|
|
lgr %r4,%r3 # blocksize
|
|
bas %r14,_writer_64-0b(%r13)
|
|
|
|
@@ -153,24 +143,21 @@ _dump_mem_64:
|
|
lgr %r12,%r10 # save mem size
|
|
lghi %r11,0 # start
|
|
|
|
-1:
|
|
lgr %r2,%r11 # start
|
|
lgr %r3,%r10 # length
|
|
llgf %r4,.Lblock_size-0b(%r13) # blocksize
|
|
bas %r14,_writer_64-0b(%r13) # write page
|
|
-
|
|
+
|
|
clgr %r2,%r12
|
|
- bhe 2f-0b(%r13)
|
|
+ bhe 1f-0b(%r13)
|
|
|
|
- # Next Volume
|
|
+ # Cartridge full
|
|
|
|
- lgr %r11,%r2 # save next start addr
|
|
- bas %r14,_next_vol_64-0b(%r13)
|
|
- lgr %r10,%r12 # update length:
|
|
- sgr %r10,%r11 # memsize-act written
|
|
- b 1b-0b(%r13)
|
|
+ la %r2,EMEM
|
|
+ larl %r14,_panik_64
|
|
+ basr %r14,%r14
|
|
|
|
-2: # All memory written
|
|
+1: # All memory written
|
|
|
|
#
|
|
# write end marker
|
|
@@ -191,56 +178,12 @@ _dump_mem_64:
|
|
|
|
lmg %r6,%r15,248(%r15)
|
|
br %r14 # return to caller
|
|
-.Lheader_size:
|
|
- .long HEADER_SIZE
|
|
.Lblock_size:
|
|
.long BLOCK_SIZE
|
|
.Lend_text:
|
|
.long DISP_DUMP_END
|
|
|
|
################################################################################
|
|
-# _next_vol
|
|
-# - no parameters
|
|
-################################################################################
|
|
-
|
|
-_next_vol_64:
|
|
- stmg %r6,%r15,48(%r15)
|
|
- basr %r13,0 # base register
|
|
-0: aghi %r15,-200 # create stack frame
|
|
-
|
|
- /* write end of volume marker */
|
|
-
|
|
- lghi %r2, TMP_PAGE_START
|
|
- mvc 0(256,%r2),.Leov_marker-0b(%r13) # move to 4k boundary
|
|
- lghi %r3,EOV_MARKER_SIZE
|
|
- lghi %r4,EOV_MARKER_SIZE
|
|
- bas %r14,_writer_64-0b(%r13)
|
|
-
|
|
- /* write two tape marks (End of Tape) */
|
|
-
|
|
- bas %r14,_tapemark_64-0b(%r13)
|
|
- bas %r14,_tapemark_64-0b(%r13)
|
|
-
|
|
- /* rewind unload */
|
|
-
|
|
- bas %r14,_rewind_unload_64-0b(%r13)
|
|
-
|
|
- /* write header to next volume */
|
|
-
|
|
- l %r10,.Ldh_vol_nr-0b(%r13)
|
|
- ahi %r10,1
|
|
- st %r10,.Ldh_vol_nr-0b(%r13)
|
|
- la %r2,.Ldh_dumpheader-0b(%r13)
|
|
- llgf %r3,.Ldh_header_size-0b(%r13)
|
|
- llgf %r4,.Ldh_header_size-0b(%r13)
|
|
- bas %r14,_writer_64-0b(%r13)
|
|
-
|
|
- lmg %r6,%r15,248(%r15)
|
|
- br %r14 # return to caller
|
|
-.Leov_marker:
|
|
- .long EOV_LABEL
|
|
-
|
|
-################################################################################
|
|
# subroutine for writing to tape
|
|
# Parameters:
|
|
# -r2: start address
|
|
@@ -303,8 +246,7 @@ _writer_64:
|
|
|
|
/* build error code: first byte ERA, last byte our error code */
|
|
|
|
- lghi %r2,0
|
|
- ic %r2,.Ltmp_data+3-0b(%r13) # get ERA
|
|
+ llgc %r2,.Ltmp_data+3-0b(%r13) # get ERA
|
|
sll %r2,24 # move it to first byte
|
|
ahi %r2,ETAPE_WRITE
|
|
|
|
@@ -318,8 +260,7 @@ _writer_64:
|
|
|
|
# unit exception: We reached End of Tape
|
|
|
|
- lgr %r2,%r10 # r2 contains
|
|
- agr %r2,%r12 # next write addr
|
|
+ la %r2,0(%r12,%r10) # r2 contains next write addr
|
|
b 3f-0b(%r13) # return
|
|
|
|
1:
|
|
@@ -356,6 +297,9 @@ _writer_64:
|
|
.Lorbwrite:
|
|
.long 0x00000000,0x0082ff00,.Lccwwrite
|
|
.align 8
|
|
+.Lorbsense:
|
|
+ .long 0x00000000,0x0080ff00,.Lccwsense
|
|
+ .align 8
|
|
.Lccwwrite_compressed: /* note that 3480 does not support IDRC */
|
|
.long 0xdb400001,.Lmodsetbyte
|
|
.Lccwwrite:
|
|
@@ -365,7 +309,8 @@ _writer_64:
|
|
.Lmodsetbyte:
|
|
.long 0x08000000
|
|
.align 8
|
|
-
|
|
+.Lccwsense:
|
|
+ .long 0x04200020,.Ltmp_data
|
|
################################################################################
|
|
# Translate binary hex to decimal ebcdic
|
|
# -r2: value (bin)
|
|
@@ -385,125 +330,6 @@ _hex_to_ebcdic_64:
|
|
.long 0x0,0x0
|
|
|
|
################################################################################
|
|
-# rewind unload tape
|
|
-# - no parameters
|
|
-################################################################################
|
|
-
|
|
-_rewind_unload_64:
|
|
- stmg %r6,%r15,48(%r15)
|
|
- basr %r13,0 # base register
|
|
-0: aghi %r15,-200 # create stack frame
|
|
-
|
|
- /* 3480/3490/3590: rewind unload */
|
|
-
|
|
- llgf %r2,IPL_SC # subchannel id
|
|
- la %r3,.Lorbrew_unload-0b(%r13)
|
|
- la %r4,.Lirb-0b(%r13)
|
|
- lghi %r5,1 # 1 retries
|
|
- bas %r14,_ssch_64-0b(%r13) # do the rewind unload
|
|
-
|
|
- /* check for 3590 */
|
|
-
|
|
- lh %r9,.Ltape_type-0b(%r13)
|
|
- chi %r9,TAPE_3590
|
|
- bne .Lnot3590-0b(%r13)
|
|
-
|
|
- tm .Lirb+8-0b(%r13),0x2 # unit check?
|
|
- bz 3f-0b(%r13) # no unit check: rewunl worked
|
|
-
|
|
- /* 3590: retry rewind unload */
|
|
-
|
|
- llgf %r2,IPL_SC
|
|
- la %r3,.Lorbrew_unload-0b(%r13)
|
|
- la %r4,.Lirb-0b(%r13)
|
|
- la %r5,1
|
|
- bas %r14,_ssch_64-0b(%r13)
|
|
-
|
|
- b 3f-0b(%r13)
|
|
-
|
|
- /* 3480/90 */
|
|
-
|
|
-.Lnot3590:
|
|
-
|
|
- /* 3480/3490 sense */
|
|
-
|
|
- llgf %r2,IPL_SC
|
|
- la %r3,.Lorbsense-0b(%r13)
|
|
- la %r4,.Lirb-0b(%r13)
|
|
- lghi %r5,1
|
|
- bas %r14,_ssch_64-0b(%r13)
|
|
-
|
|
- cli .Ltmp_data+3-0b(%r13),0x2b # check load sense byte 3
|
|
- # 3480: ERA: rewunl completed (2b)
|
|
- be 3f-0b(%r13)
|
|
-
|
|
- lghi %r2,ETAPE_REWUNL_1
|
|
- cli .Ltmp_data+3-0b(%r13),0x51 # check load sense byte 3
|
|
- # 3490: ERA: EOV (51)
|
|
- bne 2f-0b(%r13)
|
|
-
|
|
- /* 3490: retry rewind unload */
|
|
-
|
|
- llgf %r2,IPL_SC
|
|
- la %r3,.Lorbrew_unload-0b(%r13)
|
|
- la %r4,.Lirb-0b(%r13)
|
|
- la %r5,1
|
|
- bas %r14,_ssch_64-0b(%r13)
|
|
-
|
|
- /* 3490: sense */
|
|
-
|
|
- l %r2,IPL_SC
|
|
- la %r3,.Lorbsense-0b(%r13)
|
|
- la %r4,.Lirb-0b(%r13)
|
|
- lghi %r5,1
|
|
- bas %r14,_ssch_64-0b(%r13)
|
|
-
|
|
- cli .Ltmp_data+3-0b(%r13),0x52 # check load sense byte 3
|
|
- # 3490: ERA: EOV Complete (52)
|
|
- be 3f-0b(%r13)
|
|
-
|
|
- lghi %r2,ETAPE_REWUNL_2
|
|
-
|
|
-2:
|
|
- /* Something went wrong --> panik */
|
|
-
|
|
- l %r14,.Lpanik_64-0b(%r13)
|
|
- basr %r14,%r14
|
|
-
|
|
-3:
|
|
- /* Tell operator to insert next cartridge */
|
|
-
|
|
- la %r2,.Lnext_vol_text-0b(%r13)
|
|
- bas %r14,_load_display_64-0b(%r13)
|
|
-
|
|
- /* wait for UException/DE/Attention (85) */
|
|
-
|
|
-.Lwait_loop:
|
|
- llgf %r2,IPL_SC
|
|
- la %r3,.Lirb-0b(%r13)
|
|
- bas %r14,_wait4de_64-0b(%r13)
|
|
- cli .Lirb+8-0b(%r13),0x85
|
|
- bne .Lwait_loop-0b(%r13)
|
|
-
|
|
-4: lmg %r6,%r15,248(%r15)
|
|
- br %r14
|
|
- .align 8
|
|
-.Lorbsense:
|
|
- .long 0x00000000,0x0080ff00,.Lccwsense
|
|
- .align 8
|
|
-.Lorbrew_unload:
|
|
- .long 0x00000000,0x0080ff00,.Lccwrew_unload
|
|
- .align 8
|
|
-.Lccwrew_unload:
|
|
- .long 0x0f200000,0x00000000
|
|
- .align 8
|
|
-.Lnext_vol_text:
|
|
- .long DISP_NEXT_VOL
|
|
- .align 8
|
|
-.Lccwsense:
|
|
- .long 0x04200020,.Ltmp_data
|
|
-
|
|
-################################################################################
|
|
# subroutine for reading the device characteristics
|
|
################################################################################
|
|
|
|
@@ -682,11 +508,9 @@ _dump_mem_32:
|
|
#
|
|
# write header
|
|
#
|
|
- stck .Ldh_time-0b(%r13) # store time
|
|
- stidp .Ldh_cpuid-0b(%r13) # store cpu id
|
|
- lhi %r2, TMP_PAGE_START
|
|
+ lhi %r2,HEADER_PAGE_START
|
|
mvc 0(256,%r2),.Ldh_dumpheader-0b(%r13) # move to 4k boundary
|
|
- l %r3,.Lheader_size-0b(%r13) # size of header
|
|
+ lhi %r3,HEADER_SIZE # size of header
|
|
lr %r4,%r3 # blocksize
|
|
bas %r14,_writer_32-0b(%r13)
|
|
|
|
@@ -697,25 +521,21 @@ _dump_mem_32:
|
|
lr %r12,%r10 # save mem size
|
|
la %r11,0 # start
|
|
|
|
-1:
|
|
-
|
|
lr %r2,%r11 # start
|
|
lr %r3,%r10 # length
|
|
l %r4,.Lblock_size-0b(%r13) # blocksize
|
|
bas %r14,_writer_32-0b(%r13) # write page
|
|
|
|
clr %r2,%r12
|
|
- bhe 2f-0b(%r13)
|
|
+ bhe 1f-0b(%r13)
|
|
|
|
- # Next Volume
|
|
+ # Cartridge full
|
|
|
|
- lr %r11,%r2 # save next start addr
|
|
- bas %r14,_next_vol_32-0b(%r13)
|
|
- lr %r10,%r12 # update length:
|
|
- sr %r10,%r11 # memsize-act written
|
|
- b 1b-0b(%r13)
|
|
-
|
|
-2: # All memory written
|
|
+ la %r2,EMEM
|
|
+ larl %r14,_panik_32
|
|
+ basr %r14,%r14
|
|
+
|
|
+1: # All memory written
|
|
#
|
|
# write end marker
|
|
#
|
|
@@ -735,8 +555,6 @@ _dump_mem_32:
|
|
|
|
lm %r6,%r15,120(%r15)
|
|
br %r14 # return to caller
|
|
-.Lheader_size:
|
|
- .long HEADER_SIZE
|
|
.Lblock_size:
|
|
.long BLOCK_SIZE
|
|
.Lend_text:
|
|
@@ -767,47 +585,6 @@ _init_tape_32:
|
|
.long 0x00000000 /* buffered mode + IDRC */
|
|
|
|
################################################################################
|
|
-# _next_vol
|
|
-################################################################################
|
|
-
|
|
-_next_vol_32:
|
|
- stm %r6,%r15,24(%r15)
|
|
- basr %r13,0 # base register
|
|
-0: ahi %r15,-96 # create stack frame
|
|
-
|
|
- /* write end of volume marker */
|
|
- la %r2,.Leov_marker-0b(%r13)
|
|
- lhi %r2, TMP_PAGE_START
|
|
- mvc 0(256,%r2),.Leov_marker-0b(%r13) # move to 4k boundary
|
|
- la %r3,EOV_MARKER_SIZE
|
|
- la %r4,EOV_MARKER_SIZE
|
|
- bas %r14,_writer_32-0b(%r13)
|
|
-
|
|
- /* write two tape marks (End of Tape) */
|
|
-
|
|
- bas %r14,_tapemark_32-0b(%r13)
|
|
- bas %r14,_tapemark_32-0b(%r13)
|
|
-
|
|
- /* rewind unload */
|
|
-
|
|
- bas %r14,_rewind_unload_32-0b(%r13)
|
|
-
|
|
- /* write header to next volume */
|
|
-
|
|
- l %r10,.Ldh_vol_nr-0b(%r13)
|
|
- ahi %r10,1
|
|
- st %r10,.Ldh_vol_nr-0b(%r13)
|
|
- la %r2,.Ldh_dumpheader-0b(%r13)
|
|
- l %r3,.Ldh_header_size-0b(%r13)
|
|
- l %r4,.Ldh_header_size-0b(%r13)
|
|
- bas %r14,_writer_32-0b(%r13)
|
|
-
|
|
- lm %r6,%r15,120(%r15)
|
|
- br %r14 # return to caller
|
|
-.Leov_marker:
|
|
- .long EOV_LABEL
|
|
-
|
|
-################################################################################
|
|
# subroutine for writing to tape
|
|
# Parameters:
|
|
# -r2: start address
|
|
@@ -925,6 +702,9 @@ _writer_32:
|
|
.Lorbwrite:
|
|
.long 0x00000000,0x0080ff00,.Lccwwrite
|
|
.align 8
|
|
+.Lorbsense:
|
|
+ .long 0x00000000,0x0080ff00,.Lccwsense
|
|
+ .align 8
|
|
.Lccwwrite_compressed: /* note that 3480 does not support IDRC */
|
|
.long 0xdb400001,.Lmodsetbyte
|
|
.Lccwwrite:
|
|
@@ -933,6 +713,8 @@ _writer_32:
|
|
.long 0x20000000,0x00000000,0x00000000
|
|
.Lmodsetbyte:
|
|
.long 0x08000000
|
|
+.Lccwsense:
|
|
+ .long 0x04200020,.Ltmp_data
|
|
|
|
################################################################################
|
|
# Translate binary hex to decimal ebcdic
|
|
@@ -952,100 +734,6 @@ _hex_to_ebcdic_32:
|
|
.Lout_packed:
|
|
.long 0x0,0x0
|
|
|
|
-################################################################################
|
|
-# rewind unload tape
|
|
-# - no parameters
|
|
-################################################################################
|
|
-
|
|
-_rewind_unload_32:
|
|
- stm %r6,%r15,24(%r15)
|
|
- basr %r13,0 # base register
|
|
-0: ahi %r15,-96 # create stack frame
|
|
-
|
|
- /* 3480/3490: rewind unload */
|
|
-
|
|
- l %r2,IPL_SC # subchannel id
|
|
- la %r3,.Lorbrew_unload-0b(%r13)
|
|
- la %r4,.Lirb-0b(%r13)
|
|
- la %r5,1 # no retries
|
|
- bas %r14,_ssch_32-0b(%r13) # do the rewind unload
|
|
-
|
|
- /* 3480/3490: sense */
|
|
-
|
|
- l %r2,IPL_SC
|
|
- la %r3,.Lorbsense-0b(%r13)
|
|
- la %r4,.Lirb-0b(%r13)
|
|
- la %r5,1
|
|
- bas %r14,_ssch_32-0b(%r13)
|
|
-
|
|
- cli .Ltmp_data+3-0b(%r13),0x2b # check load sense byte 3
|
|
- # 3480: ERA: rewunl completed (2b)
|
|
- be 3f-0b(%r13)
|
|
-
|
|
- la %r2,ETAPE_REWUNL_1
|
|
- cli .Ltmp_data+3-0b(%r13),0x51 # check load sense byte 3
|
|
- # 3490: ERA: EOV (51)
|
|
- bne 2f-0b(%r13)
|
|
-
|
|
- /* 3490: retry rewind unload */
|
|
-
|
|
- l %r2,IPL_SC
|
|
- la %r3,.Lorbrew_unload-0b(%r13)
|
|
- la %r4,.Lirb-0b(%r13)
|
|
- la %r5,1
|
|
- bas %r14,_ssch_32-0b(%r13)
|
|
-
|
|
- /* 3490: sense */
|
|
-
|
|
- l %r2,IPL_SC
|
|
- la %r3,.Lorbsense-0b(%r13)
|
|
- la %r4,.Lirb-0b(%r13)
|
|
- la %r5,1
|
|
- bas %r14,_ssch_32-0b(%r13)
|
|
-
|
|
- cli .Ltmp_data+3-0b(%r13),0x52 # check load sense byte 3
|
|
- # 3490: ERA: EOV Complete (52)
|
|
- be 3f-0b(%r13)
|
|
-
|
|
- la %r2,ETAPE_REWUNL_2
|
|
-
|
|
-2:
|
|
- /* Something went wrong --> panik */
|
|
-
|
|
- l %r14,.Lpanik_32-0b(%r13)
|
|
- basr %r14,%r14
|
|
-
|
|
-3:
|
|
- /* Tell operator to insert next cartridge */
|
|
-
|
|
- la %r2,.Lnext_vol_text-0b(%r13)
|
|
- bas %r14,_load_display_32-0b(%r13)
|
|
-
|
|
- /* wait for UException/DE/Attention (85) */
|
|
-
|
|
- l %r2,IPL_SC
|
|
- la %r3,.Lirb-0b(%r13)
|
|
- bas %r14,_wait4de_32-0b(%r13)
|
|
-
|
|
-
|
|
-4: lm %r6,%r15,120(%r15)
|
|
- br %r14
|
|
- .align 8
|
|
-.Lorbsense:
|
|
- .long 0x00000000,0x0080ff00,.Lccwsense
|
|
- .align 8
|
|
-.Lorbrew_unload:
|
|
- .long 0x00000000,0x0080ff00,.Lccwrew_unload
|
|
- .align 8
|
|
-.Lccwrew_unload:
|
|
- .long 0x0f200000,0x00000000
|
|
- .align 8
|
|
-.Lnext_vol_text:
|
|
- .long DISP_NEXT_VOL
|
|
- .align 8
|
|
-.Lccwsense:
|
|
- .long 0x04200020,.Ltmp_data
|
|
-
|
|
################################################################################# subroutine for reading the device characteristics
|
|
################################################################################
|
|
_get_device_characteristics_32:
|
|
diff --git a/zipl/include/boot.h b/zipl/include/boot.h
|
|
index a3cac61..e27c93a 100644
|
|
--- a/zipl/include/boot.h
|
|
+++ b/zipl/include/boot.h
|
|
@@ -39,8 +39,8 @@ struct boot_fba_stage0 {
|
|
uint64_t TIC;
|
|
uint64_t param1;
|
|
uint64_t param2;
|
|
- struct boot_fba_locread locread[8];
|
|
- struct boot_fba_locdata locdata[8];
|
|
+ struct boot_fba_locread locread[16];
|
|
+ struct boot_fba_locdata locdata[16];
|
|
} __attribute__ ((packed));
|
|
|
|
|
|
diff --git a/zipl/src/boot.c b/zipl/src/boot.c
|
|
index 8fe0b36..8c6314c 100644
|
|
--- a/zipl/src/boot.c
|
|
+++ b/zipl/src/boot.c
|
|
@@ -107,7 +107,7 @@ boot_init_fba_stage0(struct boot_fba_stage0* stage0,
|
|
/* Initialize stage 0 data */
|
|
memcpy(stage0, DATA_ADDR(fba0), sizeof(struct boot_fba_stage0));
|
|
/* Fill in blocklist for stage 2 loader */
|
|
- if (stage2_count > 8) {
|
|
+ if (stage2_count > 16) {
|
|
error_reason("Not enough room for FBA stage 2 loader "
|
|
"(try larger block size)");
|
|
return -1;
|
|
diff --git a/zipl/src/install.c b/zipl/src/install.c
|
|
index ec84821..3f72ff5 100644
|
|
--- a/zipl/src/install.c
|
|
+++ b/zipl/src/install.c
|
|
@@ -645,8 +645,12 @@ overwrite_partition_start(int fd, struct disk_info* info, int mv_dump_magic)
|
|
return 0;
|
|
}
|
|
|
|
-
|
|
-static int check_partition_bounds(struct disk_info* info)
|
|
+/*
|
|
+ * Ensure that end block is within bounds.
|
|
+ * Force block size of 4KiB because otherwise there is not enough space
|
|
+ * to write the dump tool.
|
|
+ */
|
|
+static int check_eckd_dump_partition(struct disk_info* info)
|
|
{
|
|
unsigned long long end_blk = info->geo.start + info->phy_blocks - 1;
|
|
|
|
@@ -658,6 +662,11 @@ static int check_partition_bounds(struct disk_info* info)
|
|
info->phy_block_size) >> 20);
|
|
return -1;
|
|
}
|
|
+ if (info->phy_block_size != 4096) {
|
|
+ error_reason("unsupported DASD block size %d (should be 4096)",
|
|
+ info->phy_block_size);
|
|
+ return -1;
|
|
+ }
|
|
return 0;
|
|
}
|
|
|
|
@@ -863,7 +872,7 @@ static int
|
|
install_dump_fba(int fd, struct disk_info* info, uint64_t mem)
|
|
{
|
|
struct boot_fba_stage0 stage0;
|
|
- disk_blockptr_t stage2_list[8];
|
|
+ disk_blockptr_t stage2_list[16];
|
|
blocknum_t block;
|
|
blocknum_t count;
|
|
void* buffer;
|
|
@@ -880,7 +889,7 @@ install_dump_fba(int fd, struct disk_info* info, uint64_t mem)
|
|
if (rc)
|
|
return rc;
|
|
count = (size + info->phy_block_size - 1) / info->phy_block_size;
|
|
- if (count > 8) {
|
|
+ if (count > 16) {
|
|
error_reason("FBA dump record is too large");
|
|
free(buffer);
|
|
return -1;
|
|
@@ -1017,7 +1026,7 @@ install_dump(const char* device, struct job_target_data* target, uint64_t mem)
|
|
switch (info->type) {
|
|
case disk_type_eckd_classic:
|
|
case disk_type_eckd_compatible:
|
|
- if (check_partition_bounds(info)) {
|
|
+ if (check_eckd_dump_partition(info)) {
|
|
error_text("Dump target '%s'", device);
|
|
rc = -1;
|
|
break;
|
|
@@ -1128,7 +1137,7 @@ install_mvdump(char* const device[], struct job_target_data* target, int count,
|
|
info[i]->device, device[i]);
|
|
if (rc)
|
|
goto out;
|
|
- if (check_partition_bounds(info[i])) {
|
|
+ if (check_eckd_dump_partition(info[i])) {
|
|
error_text("Dump target '%s'", device[i]);
|
|
rc = -1;
|
|
goto out;
|
|
--
|
|
1.7.3.5
|
|
|