s390utils/0054-zgetdump-zipl-Add-ELF-dump-support-needed-for-makedu.patch
2011-03-25 14:59:32 +01:00

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, &note, sizeof(note), ZG_CHECK_ERR);
+ if (rc != sizeof(note))
+ return -EINVAL;
+ switch (note.n_type) {
+ case NT_PRSTATUS:
+ cpu_current = nt_prstatus_read(&note);
+ if (!cpu_current)
+ return -EINVAL;
+ break;
+ case NT_FPREGSET:
+ if (nt_fpregset_read(cpu_current, &note))
+ return -EINVAL;
+ break;
+ case NT_S390_TIMER:
+ if (nt_s390_timer_read(cpu_current, &note))
+ return -EINVAL;
+ break;
+ case NT_S390_TODCMP:
+ if (nt_s390_todcmp_read(cpu_current, &note))
+ return -EINVAL;
+ break;
+ case NT_S390_TODPREG:
+ if (nt_s390_todpreg_read(cpu_current, &note))
+ return -EINVAL;
+ break;
+ case NT_S390_CTRS:
+ if (nt_s390_ctrs_read(cpu_current, &note))
+ return -EINVAL;
+ break;
+ case NT_S390_PREFIX:
+ if (nt_s390_prefix_read(cpu_current, &note))
+ return -EINVAL;
+ break;
+ default:
+ if (nt_skip(&note))
+ 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