mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Initial s390-tools-2.0.0 import
This commit is based on the s390-tools-1.39.0 version. Changes on top of s390-tools-1.39.0: - Add MIT license to all source files - Add LICENSE file - Transform REAMDE to README.md (markdown) - Add AUTHORS.md file - Add CONTRIBUTING.md file - Move changelog from README to CHANGELOG.md file Reviewed-by: Stefan Haberland <sth@linux.vnet.ibm.com> Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
include ../common.mak
|
||||
|
||||
#
|
||||
# HAVE_FUSE: Allow to build zgetdump without mount support
|
||||
#
|
||||
ifeq (${HAVE_FUSE},0)
|
||||
|
||||
check_dep_fuse:
|
||||
|
||||
else
|
||||
|
||||
check_dep_fuse:
|
||||
$(call check_dep, \
|
||||
"zgetdump mount support", \
|
||||
"fuse.h", \
|
||||
"fuse-devel or libfuse-dev", \
|
||||
"HAVE_FUSE=0")
|
||||
endif
|
||||
|
||||
#
|
||||
# HAVE_ZLIB: Allow skip zgetdump build, when no zlib-devel is available
|
||||
#
|
||||
ifeq (${HAVE_ZLIB},0)
|
||||
|
||||
all:
|
||||
$(SKIP) HAVE_ZLIB=0
|
||||
|
||||
install:
|
||||
$(SKIP) HAVE_ZLIB=0
|
||||
|
||||
else
|
||||
|
||||
check_dep_zlib:
|
||||
$(call check_dep, \
|
||||
"zgetdump", \
|
||||
"zlib.h", \
|
||||
"zlib-devel or libz-dev", \
|
||||
"HAVE_ZLIB=0")
|
||||
|
||||
all: check_dep_fuse check_dep_zlib zgetdump
|
||||
|
||||
OBJECTS = zgetdump.o opts.o zg.o \
|
||||
dfi.o dfi_vmcoreinfo.o \
|
||||
dfi_lkcd.o dfi_elf.o dfi_s390.o dfi_s390mv.o dfi_s390tape.o \
|
||||
dfi_kdump.o dfi_devmem.o \
|
||||
dfo.o dfo_elf.o dfo_s390.o \
|
||||
df_s390.o \
|
||||
dt.o dt_s390sv.o dt_s390mv.o dt_scsi.o \
|
||||
stdout.o
|
||||
|
||||
ifeq ("$(HAVE_FUSE)","0")
|
||||
FUSE_CFLAGS = -DHAVE_FUSE=0 -D_FILE_OFFSET_BITS=64
|
||||
FUSE_LDLIBS =
|
||||
else ifneq ($(shell sh -c 'command -v pkg-config'),)
|
||||
FUSE_CFLAGS = -DHAVE_FUSE=1 $(shell pkg-config --silence-errors --cflags fuse)
|
||||
FUSE_LDLIBS = $(shell pkg-config --silence-errors --libs fuse)
|
||||
else
|
||||
FUSE_CFLAGS = -DHAVE_FUSE=1 -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse
|
||||
FUSE_LDLIBS = -lfuse
|
||||
endif
|
||||
LDLIBS += -lz $(FUSE_LDLIBS)
|
||||
ALL_CFLAGS += $(FUSE_CFLAGS)
|
||||
|
||||
ifneq ("$(HAVE_FUSE)","0")
|
||||
OBJECTS += zfuse.o
|
||||
endif
|
||||
|
||||
libs = $(rootdir)/libutil/libutil.a
|
||||
zgetdump: $(OBJECTS) $(libs)
|
||||
|
||||
install: all
|
||||
$(INSTALL) -d -m 755 $(DESTDIR)$(MANDIR)/man8 $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -m 755 zgetdump $(DESTDIR)$(BINDIR)
|
||||
$(INSTALL) -m 644 zgetdump.8 $(DESTDIR)$(MANDIR)/man8
|
||||
|
||||
clean:
|
||||
rm -f *.o *~ zgetdump core.*
|
||||
endif
|
||||
|
||||
.PHONY: all install clean check_dep_fuse check_dep_zlib
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* ELF core dump format definitions
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef DF_ELF_H
|
||||
#define DF_ELF_H
|
||||
|
||||
#include <elf.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "dfo.h"
|
||||
#include "zg.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
|
||||
|
||||
/*
|
||||
* S390 vector registers 0-15 upper half note (16 * u64)
|
||||
*/
|
||||
#ifndef NT_S390_VXRS_LOW
|
||||
#define NT_S390_VXRS_LOW 0x309
|
||||
#endif
|
||||
|
||||
/*
|
||||
* S390 vector registers 16-31 note (16 * u128)
|
||||
*/
|
||||
#ifndef NT_S390_VXRS_HIGH
|
||||
#define NT_S390_VXRS_HIGH 0x30a
|
||||
#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 */
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* LKCD dump format definitions
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#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 */
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* S390 dump format common functions
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.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);
|
||||
}
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* S390 dump format common functions
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#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 (version 3 and 4)
|
||||
*/
|
||||
struct df_s390_dumper_v3 {
|
||||
char code[0x2ff7 - 0x8];
|
||||
u8 force;
|
||||
u64 mem;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define DF_S390_DUMPER_SIZE_V3 0x3000
|
||||
|
||||
/*
|
||||
* 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;
|
||||
struct df_s390_dumper_v3 v3;
|
||||
} d;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/*
|
||||
* Dumper member access helpers
|
||||
*/
|
||||
#define df_s390_dumper_magic(dumper) ((dumper).magic)
|
||||
#define df_s390_dumper_version(dumper) ((dumper).version)
|
||||
static inline u64 df_s390_dumper_mem(struct df_s390_dumper *dumper)
|
||||
{
|
||||
switch (dumper->version) {
|
||||
case 1:
|
||||
return dumper->d.v1.mem;
|
||||
case 2:
|
||||
return dumper->d.v2.mem;
|
||||
case 3:
|
||||
default:
|
||||
return dumper->d.v3.mem;
|
||||
}
|
||||
}
|
||||
|
||||
static inline u8 df_s390_dumper_force(struct df_s390_dumper *dumper)
|
||||
{
|
||||
switch (dumper->version) {
|
||||
case 1:
|
||||
return dumper->d.v1.force;
|
||||
case 2:
|
||||
return dumper->d.v2.force;
|
||||
case 3:
|
||||
default:
|
||||
return dumper->d.v3.force;
|
||||
}
|
||||
}
|
||||
|
||||
static inline unsigned long df_s390_dumper_size(struct df_s390_dumper *dumper)
|
||||
{
|
||||
switch (dumper->version) {
|
||||
case 1:
|
||||
return 0x1000;
|
||||
case 2:
|
||||
return 0x2000;
|
||||
case 3:
|
||||
default:
|
||||
return 0x3000;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
+1141
File diff suppressed because it is too large
Load Diff
+283
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* Generic input dump format functions (DFI - Dump Format Input)
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef DFI_H
|
||||
#define DFI_H
|
||||
|
||||
#include <linux/utsname.h>
|
||||
#include "lib/util_list.h"
|
||||
#include "zg.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[0x11b0 - 0x0086]; /* 0x0086 */
|
||||
u64 vector_save_area_addr; /* 0x11b0 */
|
||||
u8 pad_0x11b8[0x1200 - 0x11b8]; /* 0x11b8 */
|
||||
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_vxrs {
|
||||
u64 low;
|
||||
u64 high;
|
||||
};
|
||||
|
||||
struct dfi_cpu {
|
||||
struct util_list_node 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;
|
||||
u64 vxrs_low[16];
|
||||
struct dfi_vxrs vxrs_high[16];
|
||||
};
|
||||
|
||||
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;
|
||||
u64 vxrs_low[16];
|
||||
struct dfi_vxrs vxrs_high[16];
|
||||
};
|
||||
|
||||
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_CONTENT_FAC_VX 0x00000001
|
||||
extern int dfi_cpu_content_fac_check(int falgs);
|
||||
extern void dfi_cpu_content_fac_add(int flags);
|
||||
|
||||
#define dfi_cpu_iterate(cpu) \
|
||||
util_list_iterate(dfi_cpu_list(), cpu)
|
||||
|
||||
extern struct util_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);
|
||||
|
||||
#define DFI_VX_SA_SIZE (32 * 16)
|
||||
extern int dfi_cpu_lc_has_vx_sa(void *lc);
|
||||
extern void dfi_cpu_vx_copy(void *buf, struct dfi_cpu *cpu);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
typedef void (*dfi_mem_chunk_free_fn)(void *data);
|
||||
|
||||
struct dfi_mem_chunk {
|
||||
struct util_list_node 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 */
|
||||
dfi_mem_chunk_free_fn free_fn; /* Free data 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,
|
||||
dfi_mem_chunk_free_fn free_fn);
|
||||
extern void dfi_mem_chunk_virt_add(u64 start, u64 size, void *data,
|
||||
dfi_mem_chunk_read_fn read_fn,
|
||||
dfi_mem_chunk_free_fn free_fn);
|
||||
extern u64 dfi_mem_range(void);
|
||||
extern int dfi_mem_range_valid(u64 addr, u64 len);
|
||||
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 util_list *dfi_mem_chunk_list(void);
|
||||
#define dfi_mem_chunk_iterate(mem_chunk) \
|
||||
util_list_iterate(dfi_mem_chunk_list(), mem_chunk)
|
||||
|
||||
/*
|
||||
* 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_utsname_set(struct new_utsname *utsname);
|
||||
extern struct new_utsname *dfi_attr_utsname(void);
|
||||
|
||||
extern void dfi_attr_dump_method_set(char *dump_method);
|
||||
extern char *dfi_attr_dump_method(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 int dfi_mem_read_rc(u64 addr, void *buf, size_t cnt);
|
||||
extern void dfi_mem_phys_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 kdump functions
|
||||
*/
|
||||
extern unsigned long dfi_kdump_base(void);
|
||||
|
||||
/*
|
||||
* DFI vmcoreinfo functions
|
||||
*/
|
||||
extern void dfi_vmcoreinfo_init(void);
|
||||
extern char *dfi_vmcoreinfo_get(void);
|
||||
extern int dfi_vmcoreinfo_tag(char *str, int len, const char *sym);
|
||||
extern int dfi_vmcoreinfo_symbol(unsigned long *val, const char *sym);
|
||||
extern int dfi_vmcoreinfo_offset(unsigned long *offs, const char *sym);
|
||||
extern int dfi_vmcoreinfo_size(unsigned long *size, const char *sym);
|
||||
extern int dfi_vmcoreinfo_length(unsigned long *len, const char *sym);
|
||||
extern int dfi_vmcoreinfo_val(unsigned long *val, const char *sym);
|
||||
|
||||
/*
|
||||
* DFI operations
|
||||
*/
|
||||
struct dfi {
|
||||
const char *name;
|
||||
int (*init)(void);
|
||||
void (*exit)(void);
|
||||
void (*info_dump)(void);
|
||||
int feat_bits;
|
||||
};
|
||||
|
||||
extern const char *dfi_name(void);
|
||||
extern int dfi_init(void);
|
||||
extern void dfi_exit(void);
|
||||
|
||||
/*
|
||||
* Dump access
|
||||
*/
|
||||
extern struct zg_fh *dfi_dump_open(const char *path);
|
||||
|
||||
/*
|
||||
* Live dump memory magic
|
||||
*/
|
||||
extern u64 dfi_live_dump_magic;
|
||||
|
||||
/*
|
||||
* Dump methods
|
||||
*/
|
||||
#define DFI_DUMP_METHOD_LIVE "live"
|
||||
|
||||
#endif /* DFI_H */
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* /dev/mem and /dev/crash dump input format
|
||||
*
|
||||
* Copyright IBM Corp. 2012, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <linux/fs.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "zgetdump.h"
|
||||
|
||||
/*
|
||||
* Add live dump magic to buffer
|
||||
*/
|
||||
static void add_live_magic(void *buf, u64 off, u64 cnt)
|
||||
{
|
||||
if (off >= sizeof(dfi_live_dump_magic))
|
||||
return;
|
||||
memcpy(buf, &dfi_live_dump_magic,
|
||||
MIN(cnt, sizeof(dfi_live_dump_magic)));
|
||||
}
|
||||
|
||||
/*
|
||||
* "devmem" mem chunk read callback
|
||||
*
|
||||
* This functions reads page wise in order to detect unreadable pages
|
||||
*/
|
||||
static void dfi_devmem_mem_chunk_read(struct dfi_mem_chunk *mem_chunk, u64 off,
|
||||
void *buf, u64 cnt)
|
||||
{
|
||||
u64 copied = 0, len = PAGE_SIZE;
|
||||
(void) mem_chunk;
|
||||
|
||||
if (off % PAGE_SIZE)
|
||||
len = MIN(len, PAGE_SIZE - off % PAGE_SIZE);
|
||||
len = MIN(len, cnt);
|
||||
do {
|
||||
zg_seek(g.fh, mem_chunk->start + off + copied, ZG_CHECK);
|
||||
if (zg_read(g.fh, buf + copied, len, ZG_CHECK_NONE) < 0) {
|
||||
if (errno == EFAULT) {
|
||||
/* This can happen when using CMM */
|
||||
memset(buf + copied, 0, len);
|
||||
} else {
|
||||
ERR_EXIT_ERRNO("Could not read %s",
|
||||
g.opts.device);
|
||||
}
|
||||
}
|
||||
copied += len;
|
||||
len = MIN(PAGE_SIZE, cnt - copied);
|
||||
} while (len);
|
||||
add_live_magic(buf, mem_chunk->start + off, cnt);
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect memory chunks via /proc/iomem
|
||||
*
|
||||
* check = 0: Initialize memory map
|
||||
* check = 1: Verifiy that current system memory map is same as DFI memory map
|
||||
*/
|
||||
static int detect_mem_chunks(int check)
|
||||
{
|
||||
char line[4096], type1[4096], type2[4096];
|
||||
unsigned long start, end, cnt = 0;
|
||||
struct dfi_mem_chunk *mem_chunk;
|
||||
struct zg_fh *fh;
|
||||
ssize_t rc;
|
||||
|
||||
fh = zg_open("/proc/iomem", O_RDONLY, ZG_CHECK);
|
||||
do {
|
||||
rc = zg_gets(fh, line, sizeof(line), ZG_CHECK);
|
||||
if (rc == 0)
|
||||
break;
|
||||
sscanf(line, "%lx-%lx : %s %s", &start, &end, type1, type2);
|
||||
if (strcmp(type1, "System") != 0)
|
||||
continue;
|
||||
if (strcmp(type2, "RAM") != 0 && strcmp(type2, "ROM") != 0)
|
||||
continue;
|
||||
if (check) {
|
||||
mem_chunk = dfi_mem_chunk_find(start);
|
||||
if (!mem_chunk)
|
||||
return -EINVAL;
|
||||
if (mem_chunk->start != start)
|
||||
return -EINVAL;
|
||||
if (mem_chunk->end != end)
|
||||
return -EINVAL;
|
||||
cnt++;
|
||||
} else {
|
||||
dfi_mem_chunk_add(start, end - start + 1, NULL,
|
||||
dfi_devmem_mem_chunk_read, NULL);
|
||||
}
|
||||
} while (1);
|
||||
if (check && cnt != dfi_mem_chunk_cnt())
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return architecture of running system
|
||||
*/
|
||||
static enum dfi_arch system_arch(void)
|
||||
{
|
||||
struct utsname utsname;
|
||||
|
||||
uname(&utsname);
|
||||
if (memcmp(utsname.machine, "s390x", 5) == 0)
|
||||
return DFI_ARCH_64;
|
||||
if (memcmp(utsname.machine, "s390", 4) == 0)
|
||||
return DFI_ARCH_32;
|
||||
return DFI_ARCH_UNKNOWN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize devmem DFI
|
||||
*/
|
||||
static int dfi_devmem_init(void)
|
||||
{
|
||||
if (strcmp(g.fh->path, "/dev/mem") != 0 &&
|
||||
strcmp(g.fh->path, "/dev/crash") != 0)
|
||||
return -ENODEV;
|
||||
dfi_arch_set(system_arch());
|
||||
dfi_cpu_info_init(DFI_CPU_CONTENT_NONE);
|
||||
detect_mem_chunks(0);
|
||||
dfi_attr_dump_method_set(DFI_DUMP_METHOD_LIVE);
|
||||
zg_seek(g.fh, 0, ZG_CHECK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cleanup devmem DFI
|
||||
*/
|
||||
static void dfi_devmem_exit(void)
|
||||
{
|
||||
if (detect_mem_chunks(1))
|
||||
STDERR("Warning: memory map has changed\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* devmem DFI operations
|
||||
*/
|
||||
struct dfi dfi_devmem = {
|
||||
.name = "devmem",
|
||||
.init = dfi_devmem_init,
|
||||
.exit = dfi_devmem_exit,
|
||||
.feat_bits = DFI_FEAT_COPY | DFI_FEAT_SEEK,
|
||||
};
|
||||
+322
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* ELF core dump input format
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <elf.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.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_filesz, off_ptr,
|
||||
dfi_elf_mem_chunk_read_fn, zg_free);
|
||||
if (phdr->p_offset + phdr->p_filesz > 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read s390 vxrs_low note
|
||||
*/
|
||||
static int nt_s390_vxrs_low_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||
{
|
||||
check_cpu(cpu, "S390_VXRS_LOW");
|
||||
return nt_read(note, &cpu->vxrs_low);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read s390 vxrs_high note
|
||||
*/
|
||||
static int nt_s390_vxrs_high_read(struct dfi_cpu *cpu, Elf64_Nhdr *note)
|
||||
{
|
||||
check_cpu(cpu, "S390_VXRS_HIGH");
|
||||
return nt_read(note, &cpu->vxrs_high);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add all notes for notes phdr
|
||||
*/
|
||||
static int pt_notes_add(Elf64_Phdr *phdr)
|
||||
{
|
||||
u64 start_off = zg_tell(g.fh, ZG_CHECK);
|
||||
struct dfi_cpu *cpu_current = NULL;
|
||||
u64 notes_start_off;
|
||||
Elf64_Nhdr note;
|
||||
int rc;
|
||||
|
||||
zg_seek(g.fh, phdr->p_offset, ZG_CHECK);
|
||||
notes_start_off = zg_tell(g.fh, ZG_CHECK);
|
||||
while (zg_tell(g.fh, ZG_CHECK) - notes_start_off < phdr->p_filesz) {
|
||||
rc = zg_read(g.fh, ¬e, sizeof(note), ZG_CHECK_ERR);
|
||||
if (rc != sizeof(note))
|
||||
return -EINVAL;
|
||||
switch (note.n_type) {
|
||||
case NT_PRSTATUS:
|
||||
cpu_current = nt_prstatus_read(¬e);
|
||||
if (!cpu_current)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NT_FPREGSET:
|
||||
if (nt_fpregset_read(cpu_current, ¬e))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NT_S390_TIMER:
|
||||
if (nt_s390_timer_read(cpu_current, ¬e))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NT_S390_TODCMP:
|
||||
if (nt_s390_todcmp_read(cpu_current, ¬e))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NT_S390_TODPREG:
|
||||
if (nt_s390_todpreg_read(cpu_current, ¬e))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NT_S390_CTRS:
|
||||
if (nt_s390_ctrs_read(cpu_current, ¬e))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NT_S390_PREFIX:
|
||||
if (nt_s390_prefix_read(cpu_current, ¬e))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case NT_S390_VXRS_LOW:
|
||||
if (nt_s390_vxrs_low_read(cpu_current, ¬e))
|
||||
return -EINVAL;
|
||||
dfi_cpu_content_fac_add(DFI_CPU_CONTENT_FAC_VX);
|
||||
break;
|
||||
case NT_S390_VXRS_HIGH:
|
||||
if (nt_s390_vxrs_high_read(cpu_current, ¬e))
|
||||
return -EINVAL;
|
||||
dfi_cpu_content_fac_add(DFI_CPU_CONTENT_FAC_VX);
|
||||
break;
|
||||
default:
|
||||
if (nt_skip(¬e))
|
||||
return -EINVAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
zg_seek(g.fh, start_off, ZG_CHECK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read ELF header
|
||||
*/
|
||||
static int read_elf_hdr(Elf64_Ehdr *ehdr)
|
||||
{
|
||||
if (zg_size(g.fh) < sizeof(*ehdr))
|
||||
return -ENODEV;
|
||||
zg_read(g.fh, ehdr, sizeof(*ehdr), ZG_CHECK);
|
||||
if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0)
|
||||
return -ENODEV;
|
||||
if (ehdr->e_type != ET_CORE)
|
||||
return -ENODEV;
|
||||
if (ehdr->e_machine != EM_S390 || ehdr->e_ident[EI_CLASS] != ELFCLASS64)
|
||||
ERR_EXIT("Only s390x (64 bit) core dump files are supported");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize ELF input dump format
|
||||
*/
|
||||
static int dfi_elf_init(void)
|
||||
{
|
||||
Elf64_Ehdr ehdr;
|
||||
Elf64_Phdr phdr;
|
||||
int i;
|
||||
|
||||
if (read_elf_hdr(&ehdr) != 0)
|
||||
return -ENODEV;
|
||||
|
||||
df_elf_ensure_s390x();
|
||||
dfi_arch_set(DFI_ARCH_64);
|
||||
dfi_cpu_info_init(DFI_CPU_CONTENT_ALL);
|
||||
|
||||
for (i = 0; i < ehdr.e_phnum; i++) {
|
||||
zg_read(g.fh, &phdr, sizeof(phdr), ZG_CHECK);
|
||||
switch (phdr.p_type) {
|
||||
case PT_LOAD:
|
||||
if (pt_load_add(&phdr))
|
||||
return -EINVAL;
|
||||
break;
|
||||
case PT_NOTE:
|
||||
if (pt_notes_add(&phdr))
|
||||
return -EINVAL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
dfi_attr_version_set(ehdr.e_ident[EI_VERSION]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* ELF DFI operations
|
||||
*/
|
||||
struct dfi dfi_elf = {
|
||||
.name = "elf",
|
||||
.init = dfi_elf_init,
|
||||
.feat_bits = DFI_FEAT_COPY | DFI_FEAT_SEEK,
|
||||
};
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* kdump and kdump_flat input format
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include "zgetdump.h"
|
||||
|
||||
struct df_kdump_hdr {
|
||||
char signature[8];
|
||||
int header_version;
|
||||
struct 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;
|
||||
};
|
||||
|
||||
struct df_kdump_flat_hdr {
|
||||
char signature[16];
|
||||
u64 type;
|
||||
u64 version;
|
||||
};
|
||||
|
||||
struct df_kdump_flat_data_hdr {
|
||||
s64 offs;
|
||||
s64 size;
|
||||
};
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
static void print_sub_header(void)
|
||||
{
|
||||
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
|
||||
|
||||
/*
|
||||
* Check for kdump flat end marker
|
||||
*/
|
||||
static inline int kdump_flat_endmarker(struct df_kdump_flat_data_hdr *d_hdr)
|
||||
{
|
||||
return (d_hdr->offs == -1) && (d_hdr->size == -1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Init kdump dump header
|
||||
*/
|
||||
static int init_kdump_hdr(struct df_kdump_hdr *hdr)
|
||||
{
|
||||
if (memcmp(hdr->signature, "KDUMP", 5) != 0)
|
||||
return -ENODEV;
|
||||
dfi_attr_version_set(hdr->header_version);
|
||||
dfi_attr_real_cpu_cnt_set(hdr->nr_cpus);
|
||||
dfi_attr_utsname_set(&hdr->utsname);
|
||||
dfi_attr_time_set(&hdr->timestamp);
|
||||
dfi_arch_set(DFI_ARCH_64);
|
||||
dfi_mem_chunk_add(0, (unsigned long) hdr->max_mapnr * PAGE_SIZE,
|
||||
NULL, NULL, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize kdump DFI
|
||||
*/
|
||||
static int dfi_kdump_init(void)
|
||||
{
|
||||
if ((zg_type(g.fh) == ZG_TYPE_FILE) && (zg_size(g.fh) < sizeof(l.hdr)))
|
||||
return -ENODEV;
|
||||
if (zg_read(g.fh, &l.hdr, sizeof(l.hdr), ZG_CHECK_ERR) != sizeof(l.hdr))
|
||||
return -ENODEV;
|
||||
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);
|
||||
#ifdef DEBUG
|
||||
print_header();
|
||||
print_sub_header();
|
||||
#endif
|
||||
return init_kdump_hdr(&l.hdr);
|
||||
}
|
||||
|
||||
/*
|
||||
* kdump DFI operations
|
||||
*/
|
||||
struct dfi dfi_kdump = {
|
||||
.name = "kdump",
|
||||
.init = dfi_kdump_init,
|
||||
.feat_bits = 0,
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
static void print_kdump_flat_header(struct df_kdump_flat_hdr *hdr)
|
||||
{
|
||||
STDERR("diskdump main header\n");
|
||||
STDERR(" signature : %s\n", hdr->signature);
|
||||
STDERR(" version : %lld\n", hdr->version);
|
||||
STDERR(" type : %lld\n", hdr->type);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Read makedumpfile dump header
|
||||
*/
|
||||
static int read_kdump_flat_hdr(void)
|
||||
{
|
||||
struct df_kdump_flat_hdr hdr;
|
||||
|
||||
if ((zg_type(g.fh) == ZG_TYPE_FILE) && (zg_size(g.fh) < sizeof(l.hdr)))
|
||||
return -ENODEV;
|
||||
if (zg_read(g.fh, &hdr, sizeof(hdr), ZG_CHECK_ERR) != sizeof(hdr))
|
||||
return -ENODEV;
|
||||
if (memcmp(hdr.signature, "makedumpfile", 12) != 0)
|
||||
return -ENODEV;
|
||||
if (hdr.type != 1)
|
||||
return -ENODEV;
|
||||
#ifdef DEBUG
|
||||
print_kdump_flat_header(&hdr);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read kdump header from fh
|
||||
*/
|
||||
static int read_kdump_hdr(struct zg_fh *fh, s64 size)
|
||||
{
|
||||
struct df_kdump_hdr hdr;
|
||||
|
||||
if (size < (s64) sizeof(hdr))
|
||||
ERR_EXIT("Can't get kdump header");
|
||||
if (zg_read(fh, &hdr, sizeof(hdr), ZG_CHECK_ERR) != sizeof(hdr))
|
||||
return -EINVAL;
|
||||
if (init_kdump_hdr(&hdr))
|
||||
return -EINVAL;
|
||||
zg_seek_cur(fh, -sizeof(hdr), ZG_CHECK_NONE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize kdump_flat DFI
|
||||
*/
|
||||
static int dfi_kdump_flat_init(void)
|
||||
{
|
||||
struct df_kdump_flat_data_hdr d_hdr;
|
||||
|
||||
if (read_kdump_flat_hdr() != 0)
|
||||
return -ENODEV;
|
||||
zg_seek(g.fh, 4096, ZG_CHECK);
|
||||
zg_read(g.fh, &d_hdr, sizeof(d_hdr), ZG_CHECK);
|
||||
do {
|
||||
if (d_hdr.offs == 0 && read_kdump_hdr(g.fh, d_hdr.size) != 0)
|
||||
return -EINVAL;
|
||||
zg_seek_cur(g.fh, d_hdr.size, ZG_CHECK_NONE);
|
||||
if (zg_read(g.fh, &d_hdr, sizeof(d_hdr),
|
||||
ZG_CHECK_ERR) != sizeof(d_hdr))
|
||||
return -EINVAL;
|
||||
} while ((d_hdr.offs >= 0) && (d_hdr.size > 0));
|
||||
if (kdump_flat_endmarker(&d_hdr))
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* kdump_flat DFI operations
|
||||
*/
|
||||
struct dfi dfi_kdump_flat = {
|
||||
.name = "kdump_flat",
|
||||
.init = dfi_kdump_flat_init,
|
||||
.feat_bits = 0,
|
||||
};
|
||||
@@ -0,0 +1,335 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* LKCD dump input format
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#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,
|
||||
NULL);
|
||||
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,
|
||||
NULL);
|
||||
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, NULL);
|
||||
}
|
||||
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(void)
|
||||
{
|
||||
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,
|
||||
};
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* S390 dump input format
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <linux/fs.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.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;
|
||||
if (zg_read(g.fh, &l.hdr, sizeof(l.hdr), ZG_CHECK_ERR) != sizeof(l.hdr))
|
||||
return -ENODEV;
|
||||
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,
|
||||
NULL);
|
||||
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,
|
||||
};
|
||||
@@ -0,0 +1,554 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* S390 multi-volume dump input format
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/fs.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <time.h>
|
||||
#include <unistd.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];
|
||||
u8 ssid[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.
|
||||
*/
|
||||
static 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize DASD volume
|
||||
*/
|
||||
static void vol_init(struct vol *vol, struct vol_parm *vol_parm, int ssid,
|
||||
u64 *mem_off)
|
||||
{
|
||||
u64 blk_cnt = vol_parm->end_blk - vol_parm->start_blk + 1;
|
||||
|
||||
sprintf(vol->bus_id, "0.%x.%04x", ssid, 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 = dfi_dump_open(vol->devnode);
|
||||
|
||||
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, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
if (zg_read(fh, &hdr, sizeof(hdr), ZG_CHECK_ERR) != sizeof(hdr))
|
||||
goto fail;
|
||||
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], l.table.ssid[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;
|
||||
zg_close(g.fh);
|
||||
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));
|
||||
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,
|
||||
};
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* S390 tape dump input format
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/mtio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.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, NULL);
|
||||
|
||||
/* 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,
|
||||
};
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* vmcoreinfo access functions
|
||||
*
|
||||
* Copyright IBM Corp. 2011, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <elf.h>
|
||||
#include "zgetdump.h"
|
||||
|
||||
#ifdef __s390x__
|
||||
#define LC_VMCORE_INFO 0xe0c
|
||||
#else
|
||||
#define LC_VMCORE_INFO 0xe08
|
||||
#endif
|
||||
|
||||
#define LC_OS_INFO 0xe18
|
||||
#define OS_INFO_MAGIC 0x4f53494e464f535aULL /* OSINFOSZ */
|
||||
|
||||
struct os_info {
|
||||
u64 magic;
|
||||
u32 csum;
|
||||
u16 version_major;
|
||||
u16 version_minor;
|
||||
u64 crashkernel_addr;
|
||||
u64 crashkernel_size;
|
||||
u64 vmcoreinfo_addr;
|
||||
u64 vmcoreinfo_size;
|
||||
u32 vmcoreinfo_csum;
|
||||
u64 reipl_block_addr;
|
||||
u64 reipl_block_size;
|
||||
u32 reipl_block_csum;
|
||||
u64 init_fn_addr;
|
||||
u64 init_fn_size;
|
||||
u32 init_fn_csum;
|
||||
u8 reserved[4004];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/*
|
||||
* File local static data
|
||||
*/
|
||||
static struct {
|
||||
char *vmcoreinfo;
|
||||
struct os_info *os_info;
|
||||
} l;
|
||||
|
||||
static u32 os_info_csum(struct os_info *os_info)
|
||||
{
|
||||
int size = sizeof(*os_info) - offsetof(struct os_info, version_major);
|
||||
return zg_csum_partial(&os_info->version_major, size, 0);
|
||||
}
|
||||
|
||||
static struct os_info *os_info_get(void)
|
||||
{
|
||||
static struct os_info os_info;
|
||||
unsigned long addr;
|
||||
|
||||
dfi_mem_read(LC_OS_INFO, &addr, sizeof(addr));
|
||||
if (addr % 0x1000)
|
||||
return NULL;
|
||||
if (dfi_mem_read_rc(addr, &os_info, sizeof(os_info)))
|
||||
return NULL;
|
||||
if (os_info.magic != OS_INFO_MAGIC)
|
||||
return NULL;
|
||||
if (os_info.csum != os_info_csum(&os_info))
|
||||
return NULL;
|
||||
return &os_info;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize vmcoreinfo
|
||||
*/
|
||||
void dfi_vmcoreinfo_init(void)
|
||||
{
|
||||
unsigned long addr, size;
|
||||
Elf64_Nhdr note;
|
||||
char str[128];
|
||||
|
||||
l.os_info = os_info_get();
|
||||
|
||||
if (l.os_info && l.os_info->vmcoreinfo_size) {
|
||||
addr = l.os_info->vmcoreinfo_addr;
|
||||
size = l.os_info->vmcoreinfo_size;
|
||||
} else {
|
||||
dfi_mem_read(LC_VMCORE_INFO, &addr, sizeof(addr));
|
||||
if (addr == 0)
|
||||
return;
|
||||
if (dfi_mem_read_rc(addr, ¬e, sizeof(note)))
|
||||
return;
|
||||
memset(str, 0, sizeof(str));
|
||||
if (dfi_mem_read_rc(addr + sizeof(note), str, note.n_namesz))
|
||||
return;
|
||||
if (memcmp(str, "VMCOREINFO", sizeof("VMCOREINFO")) != 0)
|
||||
return;
|
||||
size = note.n_descsz;
|
||||
addr += 24;
|
||||
}
|
||||
l.vmcoreinfo = zg_alloc(size + 1);
|
||||
if (dfi_mem_read_rc(addr, l.vmcoreinfo, size)) {
|
||||
zg_free(l.vmcoreinfo);
|
||||
l.vmcoreinfo = NULL;
|
||||
return;
|
||||
}
|
||||
l.vmcoreinfo[size] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return vmcoreinfo data
|
||||
*/
|
||||
char *dfi_vmcoreinfo_get(void)
|
||||
{
|
||||
return l.vmcoreinfo;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic function: Return vmcoreinfo item (-1 on failure)
|
||||
*/
|
||||
static int vmcoreinfo_item(char *buf, int UNUSED(len), const char *fmt,
|
||||
const char *sym)
|
||||
{
|
||||
|
||||
char str[1024], *sym_str, *sym_str_end;
|
||||
|
||||
if (!l.vmcoreinfo)
|
||||
return -1;
|
||||
if (fmt)
|
||||
snprintf(str, sizeof(str), "%s(%s)=", fmt, sym);
|
||||
else
|
||||
snprintf(str, sizeof(str), "%s=", sym);
|
||||
sym_str = strstr(l.vmcoreinfo, str);
|
||||
if (!sym_str)
|
||||
return -1;
|
||||
sym_str += strlen(str);
|
||||
sym_str_end = strchr(sym_str, '\n');
|
||||
if (!sym_str_end)
|
||||
sym_str_end = strchr(sym_str, '\0');
|
||||
memset(str, 0, sizeof(str));
|
||||
memcpy(str, sym_str, (unsigned long) (sym_str_end - sym_str));
|
||||
strcpy(buf, str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic function: Return vmcoreinfo ulong item (-1 on failure)
|
||||
*/
|
||||
static int vmcoreinfo_item_ulong(unsigned long *val, const char *fmt,
|
||||
const char *sym, unsigned long base)
|
||||
{
|
||||
char str[1024];
|
||||
int rc;
|
||||
|
||||
rc = vmcoreinfo_item(str, sizeof(str), fmt, sym);
|
||||
if (rc)
|
||||
return rc;
|
||||
*val = strtoul(str, NULL, base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return vmcoreinfo tag (-1 on failure)
|
||||
*/
|
||||
int dfi_vmcoreinfo_tag(char *str, int len, const char *sym)
|
||||
{
|
||||
return vmcoreinfo_item(str, len, NULL, sym);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return vmcoreinfo symbol address (-1 on failure)
|
||||
*/
|
||||
int dfi_vmcoreinfo_symbol(unsigned long *addr, const char *sym)
|
||||
{
|
||||
return vmcoreinfo_item_ulong(addr, "SYMBOL", sym, 16);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return vmcoreinfo offset of a member of a datastructure (-1 on failure)
|
||||
*/
|
||||
int dfi_vmcoreinfo_offset(unsigned long *off, const char *sym)
|
||||
{
|
||||
return vmcoreinfo_item_ulong(off, "OFFSET", sym, 10);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return vmcoreinfo datatype size (-1 on failure)
|
||||
*/
|
||||
int dfi_vmcoreinfo_size(unsigned long *size, const char *sym)
|
||||
{
|
||||
return vmcoreinfo_item_ulong(size, "SIZE", sym, 10);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return vmcoreinfo symbol length (-1 on failure)
|
||||
*/
|
||||
int dfi_vmcoreinfo_length(unsigned long *len, const char *sym)
|
||||
{
|
||||
return vmcoreinfo_item_ulong(len, "LENGTH", sym, 10);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return vmcoreinfo number (-1 on failure)
|
||||
*/
|
||||
int dfi_vmcoreinfo_val(unsigned long *val, const char *sym)
|
||||
{
|
||||
return vmcoreinfo_item_ulong(val, NULL, sym, 10);
|
||||
}
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* Generic output dump format functions (DFO - Dump Format Output)
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include "zgetdump.h"
|
||||
|
||||
#define dfo_chunk_iterate(dfo_chunk) \
|
||||
util_list_iterate(&l.dump.chunk_list, dfo_chunk)
|
||||
|
||||
/*
|
||||
* 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 util_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;
|
||||
util_list_add_head(&l.dump.chunk_list, dfo_chunk);
|
||||
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");
|
||||
util_list_init(&l.dump.chunk_list, struct dfo_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;
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* Generic output dump format functions (DFO - Dump Format Output)
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef DFO_H
|
||||
#define DFO_H
|
||||
|
||||
#include "lib/util_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 util_list_node 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 */
|
||||
+337
@@ -0,0 +1,337 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* ELF core dump output format
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <elf.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "zgetdump.h"
|
||||
|
||||
#define HDR_PER_CPU_SIZE 0x4a0
|
||||
#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 vxrs_low register note
|
||||
*/
|
||||
static void *nt_s390_vxrs_low(void *ptr, struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_VXRS_LOW, &cpu->vxrs_low,
|
||||
sizeof(cpu->vxrs_low), "LINUX");
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize vxrs_high register note
|
||||
*/
|
||||
static void *nt_s390_vxrs_high(void *ptr, struct dfi_cpu *cpu)
|
||||
{
|
||||
return nt_init(ptr, NT_S390_VXRS_HIGH, &cpu->vxrs_high,
|
||||
sizeof(cpu->vxrs_high), "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 vmcoreinfo note
|
||||
*/
|
||||
static void *nt_vmcoreinfo(void *ptr)
|
||||
{
|
||||
char *vmcoreinfo = dfi_vmcoreinfo_get();
|
||||
|
||||
if (!vmcoreinfo)
|
||||
return ptr;
|
||||
return nt_init(ptr, 0, vmcoreinfo, strlen(vmcoreinfo), "VMCOREINFO");
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
if (dfi_cpu_content_fac_check(DFI_CPU_CONTENT_FAC_VX)) {
|
||||
ptr = nt_s390_vxrs_low(ptr, cpu);
|
||||
ptr = nt_s390_vxrs_high(ptr, cpu);
|
||||
}
|
||||
}
|
||||
out:
|
||||
ptr = nt_vmcoreinfo(ptr);
|
||||
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;
|
||||
u32 alloc_size;
|
||||
u64 hdr_off;
|
||||
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);
|
||||
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,
|
||||
};
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* S390 dump output format
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "zgetdump.h"
|
||||
|
||||
/*
|
||||
* File local static data
|
||||
*/
|
||||
static 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy register set to prefix page
|
||||
*/
|
||||
static void dfo_s390_dump_chunk_vx_fn(struct dfo_chunk *dump_chunk,
|
||||
u64 off, void *buf, u64 cnt)
|
||||
{
|
||||
char *vx_regs = dump_chunk->data;
|
||||
|
||||
memcpy(buf, &vx_regs[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)
|
||||
{
|
||||
struct dfi_lowcore_64 lc;
|
||||
void *vx_regs;
|
||||
|
||||
if (dfi_cpu_content() != DFI_CPU_CONTENT_ALL)
|
||||
return;
|
||||
if (!dfi_mem_range_valid(cpu->prefix, dfi_lc_size(dfi_arch()))) {
|
||||
STDERR("Info: Could not read CPU prefix page: %x\n",
|
||||
cpu->prefix);
|
||||
return;
|
||||
}
|
||||
/* Add lowcore to memory */
|
||||
dfo_chunk_add(cpu->prefix + DF_S390_HDR_SIZE,
|
||||
dfi_lc_size(dfi_arch()), cpu,
|
||||
dfo_s390_dump_chunk_lc_fn);
|
||||
/* Add VX save area to memory */
|
||||
if (dfi_arch() != DFI_ARCH_64)
|
||||
return;
|
||||
if (!dfi_cpu_content_fac_check(DFI_CPU_CONTENT_FAC_VX))
|
||||
return;
|
||||
dfi_mem_read(cpu->prefix, &lc, sizeof(lc));
|
||||
if (!dfi_cpu_lc_has_vx_sa(&lc))
|
||||
return;
|
||||
vx_regs = zg_alloc(DFI_VX_SA_SIZE);
|
||||
dfi_cpu_vx_copy(vx_regs, cpu);
|
||||
dfo_chunk_add(lc.vector_save_area_addr + DF_S390_HDR_SIZE,
|
||||
DFI_VX_SA_SIZE, vx_regs, dfo_s390_dump_chunk_vx_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,
|
||||
};
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* Dump tool info generic functions
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include "zgetdump.h"
|
||||
|
||||
/*
|
||||
* Supported dump tools
|
||||
*/
|
||||
static struct dt *dt_vec[] = {
|
||||
&dt_s390mv,
|
||||
&dt_s390sv,
|
||||
&dt_scsi,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/*
|
||||
* Dumper attribute information
|
||||
*/
|
||||
struct attr {
|
||||
int *force;
|
||||
u64 *mem_limit;
|
||||
char *dasd_type;
|
||||
};
|
||||
|
||||
/*
|
||||
* File local static data
|
||||
*/
|
||||
static 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 (!S_ISBLK(g.fh->sb.st_mode))
|
||||
ERR_EXIT("Please specify DASD or SCSI device node"
|
||||
"(e.g. /dev/dasdd or /dev/sda )");
|
||||
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) {
|
||||
if (*l.attr.mem_limit != U64_MAX)
|
||||
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);
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* Dump tool info generic functions
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#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
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* S390 multi-volume DASD dump tool
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include "zgetdump.h"
|
||||
|
||||
/*
|
||||
* DT operations
|
||||
*/
|
||||
struct dt dt_s390mv = {
|
||||
.desc = "Multi-volume DASD dump tool",
|
||||
.init = dt_s390mv_init,
|
||||
.info = dt_s390mv_info,
|
||||
};
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* S390 single-volume DASD dump tool
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#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
|
||||
*/
|
||||
static 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;
|
||||
dumper_read_fba_gen(DF_S390_DUMPER_SIZE_V3, &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;
|
||||
|
||||
if (zg_type(g.fh) == ZG_TYPE_DASD_PART)
|
||||
return -ENODEV;
|
||||
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));
|
||||
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,
|
||||
};
|
||||
+233
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* Single-volume SCSI dump tool
|
||||
*
|
||||
* Copyright IBM Corp. 2013, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "lib/util_part.h"
|
||||
#include "zgetdump.h"
|
||||
|
||||
/*
|
||||
* Single volume SCSI dump superblock
|
||||
*/
|
||||
struct scsi_dump_sb {
|
||||
uint64_t magic;
|
||||
uint64_t version;
|
||||
uint64_t part_start;
|
||||
uint64_t part_size;
|
||||
uint64_t dump_off;
|
||||
uint64_t dump_size;
|
||||
uint64_t csum_off;
|
||||
uint64_t csum_size;
|
||||
uint64_t csum;
|
||||
} __attribute((packed));
|
||||
|
||||
/*
|
||||
* File local static data
|
||||
*/
|
||||
static struct {
|
||||
struct scsi_dump_sb sb;
|
||||
int blk_size;
|
||||
} l;
|
||||
|
||||
/*
|
||||
* zipl on-disc / bootloader structs from zipl includes
|
||||
*/
|
||||
struct scsi_blockptr {
|
||||
uint64_t blockno;
|
||||
uint16_t size;
|
||||
uint16_t blockct;
|
||||
uint8_t reserved[4];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
enum component_entry_type {
|
||||
component_execute = 0x01,
|
||||
component_load = 0x02
|
||||
};
|
||||
|
||||
struct component_entry {
|
||||
struct scsi_blockptr data;
|
||||
uint8_t pad[7];
|
||||
uint8_t component_type;
|
||||
union {
|
||||
uint64_t load_address;
|
||||
uint64_t load_psw;
|
||||
} address;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
enum component_header_type {
|
||||
component_header_ipl = 0x00,
|
||||
component_header_dump = 0x01
|
||||
};
|
||||
|
||||
struct component_header {
|
||||
uint8_t magic[4];
|
||||
uint8_t type;
|
||||
uint8_t reserved[27];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct boot_info {
|
||||
char magic[4];
|
||||
uint8_t version;
|
||||
uint8_t bp_type;
|
||||
uint8_t dev_type;
|
||||
uint8_t flags;
|
||||
uint64_t sb_off;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct scsi_mbr {
|
||||
char magic[4];
|
||||
uint32_t version;
|
||||
uint8_t reserved1[8];
|
||||
struct scsi_blockptr blockptr;
|
||||
uint8_t reserved2[0x50];
|
||||
struct boot_info boot_info;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
#define BOOT_INFO_VERSION 1
|
||||
#define BOOT_INFO_MAGIC "zIPL"
|
||||
#define BOOT_INFO_DEV_TYPE_SCSI 0x02
|
||||
#define BOOT_INFO_BP_TYPE_DUMP 0x01
|
||||
|
||||
/*
|
||||
* Check the zIPL magic number
|
||||
*/
|
||||
static int check_zipl_magic(void *buf)
|
||||
{
|
||||
if (memcmp(buf, "zIPL", 4))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Final step looks into program to find dump flag
|
||||
*/
|
||||
static int check_dump_program(struct scsi_blockptr *blockptr)
|
||||
{
|
||||
uint64_t off = blockptr->blockno * l.blk_size;
|
||||
struct component_header header;
|
||||
|
||||
zg_seek(g.fh, off, ZG_CHECK_ERR);
|
||||
zg_read(g.fh, &header, sizeof(header), ZG_CHECK_ERR);
|
||||
if (check_zipl_magic(&header.magic))
|
||||
return -1;
|
||||
return (header.type == component_header_dump) ? 0 : -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse program table, see zipl docu to understand table structures
|
||||
*/
|
||||
static int check_program_table(uint64_t blockno)
|
||||
{
|
||||
struct scsi_blockptr entries[l.blk_size / sizeof(struct scsi_blockptr)];
|
||||
unsigned int i;
|
||||
|
||||
/* Entry 0, holds the magic, entry 1 the default */
|
||||
zg_seek(g.fh, blockno * l.blk_size, ZG_CHECK);
|
||||
zg_read(g.fh, &entries, l.blk_size, ZG_CHECK);
|
||||
if (check_zipl_magic(&entries[0]))
|
||||
return -1;
|
||||
for (i = 1; i < l.blk_size / sizeof(struct scsi_blockptr); i++) {
|
||||
if (entries[i].blockno == 0)
|
||||
break;
|
||||
if (check_dump_program(&entries[i]) == 0)
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check magic number and checksum of superblock at given offset
|
||||
*/
|
||||
static int check_sb(void)
|
||||
{
|
||||
char buf[l.sb.csum_size];
|
||||
|
||||
if (l.sb.magic != 0x5a46435044554d50ULL) /* ZFCPDUMP */
|
||||
return -1;
|
||||
/*
|
||||
* Verify checksum
|
||||
*/
|
||||
zg_seek(g.fh, l.sb.part_start + l.sb.csum_off, ZG_CHECK);
|
||||
zg_read(g.fh, &buf, sizeof(buf), ZG_CHECK);
|
||||
if (zg_csum_partial(&buf, l.sb.csum_size, 0x12345678) != l.sb.csum)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the SCSI dump boot info
|
||||
*/
|
||||
static int check_boot_info(struct boot_info *info)
|
||||
{
|
||||
if (memcmp(&info->magic, BOOT_INFO_MAGIC, sizeof(info->magic)))
|
||||
return -1;
|
||||
if (info->dev_type != BOOT_INFO_DEV_TYPE_SCSI)
|
||||
return -1;
|
||||
if (info->bp_type != BOOT_INFO_BP_TYPE_DUMP)
|
||||
return -1;
|
||||
zg_seek(g.fh, info->sb_off, ZG_CHECK);
|
||||
zg_read(g.fh, &l.sb, sizeof(l.sb), ZG_CHECK);
|
||||
return check_sb();
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect if the bootmap contains ZFCPDUMP.
|
||||
* Walks through bootmap structs to find the dump flag
|
||||
*/
|
||||
static int dt_scsi_init(void)
|
||||
{
|
||||
struct scsi_mbr mbr;
|
||||
|
||||
zg_read(g.fh, &mbr, sizeof(mbr), ZG_CHECK);
|
||||
if (zg_ioctl(g.fh, BLKSSZGET, &l.blk_size, "BLKSSZGET", ZG_CHECK_NONE))
|
||||
return -1;
|
||||
if (check_zipl_magic(mbr.magic))
|
||||
return -1;
|
||||
if (check_program_table(mbr.blockptr.blockno))
|
||||
return -1;
|
||||
if (check_boot_info(&mbr.boot_info))
|
||||
return -1;
|
||||
dt_arch_set(DFI_ARCH_64);
|
||||
dt_version_set(l.sb.version);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print partition information for dump device
|
||||
*/
|
||||
static void dt_scsi_info(void)
|
||||
{
|
||||
int part_num, part_ext;
|
||||
size_t start, cnt;
|
||||
|
||||
start = l.sb.part_start / l.blk_size;
|
||||
cnt = l.sb.part_size / l.blk_size;
|
||||
part_num = util_part_search_fh(g.fh->fh, start, cnt, l.blk_size,
|
||||
&part_ext);
|
||||
|
||||
STDERR("Partition info:\n");
|
||||
if (part_num > 0)
|
||||
STDERR(" Partition number..: %d\n", part_num);
|
||||
else
|
||||
STDERR(" Partition number..: unknown\n");
|
||||
STDERR(" Maximum dump size.: %llu MB\n",
|
||||
(unsigned long long) TO_MIB(l.sb.dump_size));
|
||||
}
|
||||
|
||||
/*
|
||||
* Single-volume SCSI DT operations
|
||||
*/
|
||||
struct dt dt_scsi = {
|
||||
.desc = "Single-volume SCSI dump tool",
|
||||
.init = dt_scsi_init,
|
||||
.info = dt_scsi_info,
|
||||
};
|
||||
+292
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* Option parsing
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "zgetdump.h"
|
||||
|
||||
/*
|
||||
* Text for --help option
|
||||
*/
|
||||
static char help_text[] =
|
||||
"Usage: zgetdump DUMP [-s SYS] [-f FMT] > DUMP_FILE\n"
|
||||
" -m DUMP [-s SYS] [-f FMT] DIR\n"
|
||||
" -i DUMP [-s SYS]\n"
|
||||
" -d DUMPDEV\n"
|
||||
" -u DIR\n"
|
||||
"\n"
|
||||
"The zgetdump tool can read different dump formats from a dump device or from\n"
|
||||
"a dump file. You can use zgetdump to:\n"
|
||||
"\n"
|
||||
" - Write the dump content to standard output or to a file\n"
|
||||
" - Mount the dump content to a Linux directory\n"
|
||||
" - Convert a dump to a different dump format\n"
|
||||
" - Check if a dump is valid\n"
|
||||
" - Check if a DASD contains a valid dump tool.\n"
|
||||
"\n"
|
||||
"In the syntax description, DUMP specifies a dump device or dump file to be\n"
|
||||
"read. The following options are available:\n"
|
||||
"\n"
|
||||
"-m, --mount Mount DUMP to mount point DIR\n"
|
||||
"-u, --umount Unmount dump from mount point DIR\n"
|
||||
"-i, --info Print DUMP information\n"
|
||||
"-f, --fmt Specify target dump format FMT (\"elf\" or \"s390\")\n"
|
||||
"-s, --select Select system data SYS (\"kdump\", \"prod\", or \"all\")\n"
|
||||
"-d, --device Print DUMPDEV (dump device) information\n"
|
||||
"-v, --version Print version information, then exit\n"
|
||||
"-h, --help Print this help, then exit\n";
|
||||
|
||||
static const char copyright_str[] = "Copyright IBM Corp. 2001, 2017";
|
||||
|
||||
/*
|
||||
* Select option strings
|
||||
*/
|
||||
const char *OPTS_SELECT_KDUMP = "kdump";
|
||||
const char *OPTS_SELECT_PROD = "prod";
|
||||
const char *OPTS_SELECT_ALL = "all";
|
||||
|
||||
/*
|
||||
* Initialize default settings
|
||||
*/
|
||||
static void init_defaults(void)
|
||||
{
|
||||
g.prog_name = "zgetdump";
|
||||
g.opts.action = ZG_ACTION_STDOUT;
|
||||
#ifdef __s390x__
|
||||
g.opts.fmt = "elf";
|
||||
#else
|
||||
g.opts.fmt = "s390";
|
||||
#endif
|
||||
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("%s", 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 "--select" option
|
||||
*/
|
||||
static void select_set(const char *select)
|
||||
{
|
||||
if (strcmp(select, OPTS_SELECT_KDUMP) == 0)
|
||||
g.opts.select = OPTS_SELECT_KDUMP;
|
||||
else if (strcmp(select, OPTS_SELECT_PROD) == 0)
|
||||
g.opts.select = OPTS_SELECT_PROD;
|
||||
else if (strcmp(select, OPTS_SELECT_ALL) == 0)
|
||||
g.opts.select = OPTS_SELECT_ALL;
|
||||
else
|
||||
ERR_EXIT("Invalid select argument \"%s\" specified", select);
|
||||
g.opts.select_specified = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.select_specified) {
|
||||
if (g.opts.action != ZG_ACTION_MOUNT &&
|
||||
g.opts.action != ZG_ACTION_STDOUT &&
|
||||
g.opts.action != ZG_ACTION_DUMP_INFO)
|
||||
ERR_EXIT("The \"--select\" option can only be "
|
||||
"specifed for info, mount, or copy");
|
||||
}
|
||||
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 parameters 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 parameters 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'},
|
||||
{"select", required_argument, NULL, 's'},
|
||||
{"debug", no_argument, NULL, 'X'},
|
||||
{NULL, 0, NULL, 0 }
|
||||
};
|
||||
static const char optstr[] = "hvidmus:f: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 's':
|
||||
select_set(optarg);
|
||||
break;
|
||||
case 'X':
|
||||
g.opts.debug_specified = 1;
|
||||
break;
|
||||
default:
|
||||
print_usage_exit();
|
||||
}
|
||||
}
|
||||
parse_pos_args(argv, argc);
|
||||
verify_opts();
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* Write dump to standard output (stdout)
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* FUSE functions
|
||||
*
|
||||
* Copyright IBM Corp. 2010, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#define FUSE_USE_VERSION 25
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <fuse.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.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);
|
||||
}
|
||||
+452
@@ -0,0 +1,452 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* Helper functions
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "zgetdump.h"
|
||||
|
||||
#define MAX_EXIT_FN 10
|
||||
#define MAX_DEV_RETRIES 1000
|
||||
#define PROGRESS_INTERVAL_SECS 10
|
||||
|
||||
/*
|
||||
* Progress information
|
||||
*/
|
||||
struct prog {
|
||||
time_t time_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 __noreturn 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);
|
||||
if (S_ISBLK(zg_fh->sb.st_mode)) {
|
||||
/* We have a block device - set correct size */
|
||||
zg_fh->sb.st_size = lseek(zg_fh->fh, 0, SEEK_END);
|
||||
if (zg_fh->sb.st_size == (off_t)-1)
|
||||
goto fail;
|
||||
if (lseek(zg_fh->fh, 0, SEEK_SET) == (off_t)-1)
|
||||
goto fail;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read line
|
||||
*/
|
||||
ssize_t zg_gets(struct zg_fh *zg_fh, void *ptr, size_t cnt, enum zg_check check)
|
||||
{
|
||||
size_t copied = 0;
|
||||
char *buf = ptr;
|
||||
ssize_t rc;
|
||||
|
||||
if (cnt == 0)
|
||||
return 0;
|
||||
do {
|
||||
rc = read(zg_fh->fh, &buf[copied], 1);
|
||||
if (rc == -1) {
|
||||
if (check == ZG_CHECK_NONE)
|
||||
return rc;
|
||||
ERR_EXIT_ERRNO("Could not read \"%s\"", zg_fh->path);
|
||||
}
|
||||
if (rc == 0 || buf[copied] == '\n' || copied == cnt - 1)
|
||||
break;
|
||||
copied++;
|
||||
} while (1);
|
||||
buf[copied] = '\0';
|
||||
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.time_next = 0;
|
||||
l.prog.mem_size = mem_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print progress
|
||||
*/
|
||||
void zg_progress(u64 addr)
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
if ((tv.tv_sec < l.prog.time_next) && (addr < l.prog.mem_size))
|
||||
return;
|
||||
STDERR(" %08Lu / %08Lu MB\n", TO_MIB(addr), TO_MIB(l.prog.mem_size));
|
||||
l.prog.time_next = tv.tv_sec + PROGRESS_INTERVAL_SECS;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* Helper functions
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef ZG_H
|
||||
#define ZG_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mtio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/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 (1024)
|
||||
#define MIB (1024 * 1024)
|
||||
#define GIB (1024 * 1024 * 1024)
|
||||
|
||||
#define TO_MIB(x) ((x + (MIB / 2)) / MIB)
|
||||
#define TO_KIB(x) ((x + (KIB / 2)) / KIB)
|
||||
|
||||
/*
|
||||
* 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 __noreturn zg_exit(int rc);
|
||||
|
||||
/*
|
||||
* 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 4096UL
|
||||
#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 ARRAY_ELEMENT_CNT(x) (sizeof(x) / sizeof(x[0]))
|
||||
#define ROUNDUP(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
|
||||
|
||||
static inline u32 zg_csum_partial(const void *buf, int len, u32 sum)
|
||||
{
|
||||
register unsigned long reg2 asm("2") = (unsigned long) buf;
|
||||
register unsigned long reg3 asm("3") = (unsigned long) len;
|
||||
|
||||
asm volatile(
|
||||
"0: cksm %0,%1\n" /* do checksum on longs */
|
||||
" jo 0b\n"
|
||||
: "+d" (sum), "+d" (reg2), "+d" (reg3) : : "cc", "memory");
|
||||
return sum;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 ssize_t zg_gets(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 */
|
||||
@@ -0,0 +1,447 @@
|
||||
.\" Copyright 2017 IBM Corp.
|
||||
.\" s390-tools is free software; you can redistribute it and/or modify
|
||||
.\" it under the terms of the MIT license. See LICENSE for details.
|
||||
.\"
|
||||
.TH ZGETDUMP 8 "April 2012" "s390-tools"
|
||||
.SH NAME
|
||||
zgetdump \- Tool for copying and converting System z dumps
|
||||
.SH SYNOPSIS
|
||||
|
||||
\fBzgetdump\fR DUMP [-s SYS] [-f FMT] > DUMP_FILE
|
||||
.br
|
||||
-m DUMP [-s SYS] [-f FMT] DIR
|
||||
.br
|
||||
-i DUMP [-s SYS]
|
||||
.br
|
||||
-d DUMPDEV
|
||||
.br
|
||||
-u DIR
|
||||
.br
|
||||
-h|-v
|
||||
.SH DESCRIPTION
|
||||
The \fBzgetdump\fR tool copies a source dump into a target dump with a
|
||||
configurable dump format. The source dump can be located either on a dump
|
||||
device or on a file system. By default the source 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, check
|
||||
whether a dump device contains a valid dump tool, or create a
|
||||
non-disruptive dump on a live system.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.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 source dump 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 DUMP (for example /dev/dasdd1)
|
||||
can be specified.
|
||||
|
||||
.TP
|
||||
.BR "\-d <DUMPDEV>" " or " "\-\-device <DUMPDEV>"
|
||||
Check the dump disk DUMPDEV for a valid dump tool and print information
|
||||
about it. See chapters DUMPDEV and DUMPDEV INFORMATION below for
|
||||
more information.
|
||||
|
||||
.TP
|
||||
.BR "\-i <DUMP>" " or " "\-\-info <DUMP>"
|
||||
Print the dump header information reading from the DUMP and check if
|
||||
the dump is valid. See chapters DUMP and 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 (default)
|
||||
|
||||
.BR "- s390:"
|
||||
s390 dump
|
||||
|
||||
.TP
|
||||
.BR "\-s <SYS>" " or " "\-\-select <SYS>"
|
||||
If kdump fails and a stand-alone dump is created, the resulting dump captures
|
||||
two systems: The crashed kdump system and the previously crashed Linux
|
||||
instance. With the "--select" option you can choose which system data
|
||||
to use:
|
||||
|
||||
.BR "- prod:"
|
||||
Data for the initial crashed Linux instance
|
||||
|
||||
.BR "- kdump:"
|
||||
Data for the crashed kdump system
|
||||
|
||||
.BR "- all:"
|
||||
Data for the initial crashed Linux and kdump system
|
||||
|
||||
The "-s" option returns an error for dumps that capture only a single crashed system.
|
||||
|
||||
.TP
|
||||
\fBDUMP\fR
|
||||
This parameter specifies the file, partition or tape device node where the
|
||||
source dump is located:
|
||||
.IP " -" 12
|
||||
Regular dump file (e.g. /dumps/dump.0)
|
||||
.IP " -" 12
|
||||
DASD partition device node (e.g. /dev/dasdc1)
|
||||
.IP " -" 12
|
||||
DASD device node for multi-volume dump (e.g. /dev/dasdc)
|
||||
.IP " -" 12
|
||||
Device mapper multipath partition device node of a FCP attached SCSI disk (e.g.
|
||||
/dev/mapper/3600..20c0-part1)
|
||||
.IP " -" 12
|
||||
Tape device node (e.g. /dev/ntibm0)
|
||||
.IP " -" 12
|
||||
Device node for live system (/dev/mem or /dev/crash)
|
||||
.PP
|
||||
.IP " " 8
|
||||
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 device node of
|
||||
the dump disk that should be verified. This can be either a DASD device
|
||||
node or a device mapper multipath device node of a FCP attached SCSI disk.
|
||||
|
||||
.SH COPY DUMP
|
||||
The default action of zgetdump is to copy the source dump to standard output in
|
||||
the target format specified by the \-\-fmt option. Read
|
||||
the examples section below for more information.
|
||||
|
||||
.SH MOUNT DUMP
|
||||
Use the "--mount" option to make a source dump accessible to tools that cannot
|
||||
directly read the original dump format. Rather than creating a converted
|
||||
copy of the dump, zgetdump creates a virtual dump file with the requested
|
||||
target format. This is fast and does not consume any additional disk space.
|
||||
Also multi-volume dumps can be assimilated into a single virtual dump file,
|
||||
which can then be accessed directly with dump-processing tools like
|
||||
makedumpfile or crash.
|
||||
|
||||
Specify a command of this form to mount and convert a dump:
|
||||
|
||||
.br
|
||||
# zgetdump --mount <DUMP> <DIR> --fmt <FMT>
|
||||
.br
|
||||
|
||||
Where:
|
||||
.TP
|
||||
.BR DUMP
|
||||
is the source dump or dump device
|
||||
.TP
|
||||
.BR DIR
|
||||
is the mount point where the virtual dump file is created
|
||||
.TP
|
||||
.BR FMT
|
||||
is the target dump format to which the virtual dump file is converted.
|
||||
The resulting virtual dump file is <DIR>/dump.<FMT>
|
||||
.P
|
||||
The virtual dump file exists until the directory is unmounted.
|
||||
Use zgetdump -u <DIR> to unmount a dump.
|
||||
|
||||
The zgetdump tool uses the file system in user space (fuse) to mount the source
|
||||
dump. Therefore, the fuse kernel module must to be loaded before using
|
||||
the "--mount" option.
|
||||
|
||||
Read the examples section below for more information.
|
||||
.SH DUMP FORMATS
|
||||
The default target format of zgetdump is "elf". Use the "--fmt" option to
|
||||
change the target format. The following dump formats are supported for
|
||||
target and source dump:
|
||||
.TP
|
||||
.BR "elf"
|
||||
Executable and Linking Format core dump. This dump format is also used for
|
||||
Linux user space core dumps.
|
||||
.TP
|
||||
.BR "s390"
|
||||
This dump format is System z specific and is used for DASD and tape dumps.
|
||||
.TP
|
||||
The following dump formats are supported for the source dump only:
|
||||
.TP
|
||||
.BR "lkcd"
|
||||
This dump format is used by the Linux Kernel Crash Dumps (LKCD) project
|
||||
and also on System z for the "vmconvert" dump tool.
|
||||
.TP
|
||||
.BR "devmem"
|
||||
On live systems the /dev/mem or /dev/crash device nodes can be used as source
|
||||
dumps for creating live dumps.
|
||||
.TP
|
||||
.BR "kdump" / "kdump_flat"
|
||||
Dump formats created by the "makedumpfile" tool. For these formats only the
|
||||
"--info" option can be used.
|
||||
|
||||
.SH DUMP INFORMATION
|
||||
Depending on the dump format, the following dump attributes are available
|
||||
when calling zgetdump with the "--info" option:
|
||||
.TP
|
||||
.BR "Dump format"
|
||||
Name of the dump format.
|
||||
.TP
|
||||
.BR Version
|
||||
Version number of the dump format.
|
||||
.TP
|
||||
.BR "Dump method"
|
||||
Dump method that has been used to create the dump. Currently the only
|
||||
supported value for this attribute is "live" which indicates that the
|
||||
dump has been created from a live system and therefore is not consistent.
|
||||
.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. 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 ran the dump tool.
|
||||
.TP
|
||||
.BR "UTS node name"
|
||||
The network node hostname of the Linux system.
|
||||
.TP
|
||||
.BR "UTS kernel release"
|
||||
The kernel release of the Linux system.
|
||||
.TP
|
||||
.BR "UTS kernel version"
|
||||
The kernel version of the Linux system.
|
||||
.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 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 the system. This value is the difference
|
||||
between the last and the first memory address of the system on which the
|
||||
dump was created.
|
||||
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. Some dump tools create multiple memory
|
||||
chunks when creating a dump on a system with memory gaps
|
||||
|
||||
.SH DUMPDEV INFORMATION
|
||||
Depending on the dump tool, the following attributes are available
|
||||
when calling zgetdump with the "--device" option:
|
||||
.TP
|
||||
.BR "Dump tool"
|
||||
Name of the dump tool.
|
||||
.TP
|
||||
.BR "Version"
|
||||
Version of the dump tool.
|
||||
.TP
|
||||
.BR "Architecture"
|
||||
Architecture (s390 or s390x) of the dump tool.
|
||||
.TP
|
||||
.BR "DASD type"
|
||||
Type of the DASD where the dump tool is installed (ECKD or FBA).
|
||||
.TP
|
||||
.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
|
||||
.BR "Force specified"
|
||||
If this 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.
|
||||
.TP
|
||||
.BR "Partition info"
|
||||
For SCSI partition dump, the partition number and the maximum dump size is
|
||||
printed. The partition number corresponds to the output of
|
||||
"parted /dev/sdx print" or "fdisk -l /dev/sdx".
|
||||
|
||||
.SH EXAMPLES
|
||||
.TP
|
||||
.B Copy single-volume DASD dump
|
||||
|
||||
The DASD partition /dev/dasdx1 was prepared for dump with:
|
||||
.br
|
||||
|
||||
# zipl -d /dev/dasdx1
|
||||
|
||||
.br
|
||||
An IPL was performed on the corresponding single-volume dump tool and a dump
|
||||
has been created. To copy the dump from the DASD partition to file dump.elf
|
||||
issue:
|
||||
.br
|
||||
|
||||
# zgetdump /dev/dasdx1 > dump.elf
|
||||
|
||||
.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 dev_list.conf
|
||||
|
||||
.br
|
||||
An IPL was performed on the corresponding multi-volume dump tool and a dump
|
||||
has been created. To copy the dump from the DASD partitions to file dump.elf
|
||||
issue:
|
||||
.br
|
||||
|
||||
# zgetdump /dev/dasdx > dump.elf
|
||||
|
||||
.br
|
||||
.TP
|
||||
.B Copy SCSI dump
|
||||
|
||||
The device mapper multipath partition on a SCSI disk
|
||||
/dev/mapper/3600..20c0-part1 was prepared for dump with:
|
||||
.br
|
||||
|
||||
# zipl -d /dev/mapper/3600..20c0-part1
|
||||
|
||||
.br
|
||||
An IPL was performed on the corresponding dump tool and a dump
|
||||
has been created. To copy the dump from the device mapper partition to file
|
||||
dump.elf issue:
|
||||
.br
|
||||
|
||||
# zgetdump /dev/mapper/3600..20c0-part1 > dump.elf
|
||||
|
||||
.TP
|
||||
.B Copy tape dump
|
||||
|
||||
Tape device /dev/ntibm0 was prepared with:
|
||||
.br
|
||||
|
||||
# zipl -d /dev/ntibm0
|
||||
|
||||
.br
|
||||
An IPL was performed on the corresponding tape dump tool and a dump
|
||||
has been created. To copy the dump from the tape to file dump.elf
|
||||
issue:
|
||||
.br
|
||||
|
||||
# zgetdump /dev/ntibm0 > dump.elf
|
||||
|
||||
.br
|
||||
.TP
|
||||
.B Create live dump
|
||||
|
||||
To store an ELF-format dump from a live system in a file called dump.elf
|
||||
issue:
|
||||
.br
|
||||
|
||||
# nice -n -20 zgetdump /dev/mem > dump.elf
|
||||
|
||||
.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.elf"
|
||||
|
||||
.br
|
||||
Copy and compress DASD dump using ftp and gzip (note that not all ftp clients
|
||||
can do this):
|
||||
.br
|
||||
|
||||
# ftp host
|
||||
ftp> put |"zgetdump /dev/dasdd1 | gzip" dump.elf.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 a single-volume DASD dump as virtual ELF dump file, compress
|
||||
it with the makedumpfile tool, and unmount it with zgetdump:
|
||||
.br
|
||||
|
||||
# zgetdump -m /dev/dasdc1 /dumps
|
||||
# makedumpfile -c -d 31 -x vmlinux.debug \\
|
||||
/dumps/dump.elf dump.kdump
|
||||
# zgetdump -u /dumps
|
||||
|
||||
.br
|
||||
Mount a multi-volume DASD dump, process it with the "crash" tool, and
|
||||
unmount it with fusermount:
|
||||
.br
|
||||
|
||||
# zgetdump -m /dev/dasdx /dumps
|
||||
# crash vmlinux /dumps/dump.elf
|
||||
# fusermount -u /dumps
|
||||
|
||||
.br
|
||||
.TP
|
||||
.B Print dump information (--info)
|
||||
|
||||
Print information about a DASD dump on /dev/dasdd1:
|
||||
.br
|
||||
|
||||
# zgetdump -i /dev/dasdd1
|
||||
|
||||
.br
|
||||
Print information about a dump on a device mapper multipath partition device
|
||||
node of a SCSI disk:
|
||||
.br
|
||||
|
||||
# zgetdump -i /dev/mapper/3600..20c0-part1
|
||||
|
||||
.br
|
||||
.TP
|
||||
.B Print dump tool information (--device)
|
||||
|
||||
Print information about a DASD dump tool on /dev/dasdd:
|
||||
.br
|
||||
|
||||
# zgetdump -d /dev/dasdd
|
||||
|
||||
.br
|
||||
Print information about a dump tool on a SCSI multipath device:
|
||||
.br
|
||||
|
||||
# zgetdump -d /dev/mapper/3600..02c0
|
||||
|
||||
.br
|
||||
.SH NOTES
|
||||
The ELF dump format is not supported by the zgetdump tool under 31 bit.
|
||||
|
||||
While it is not recommended for reasons of recovery and redundancy, FCP
|
||||
attached SCSI disks can also be accessed directly without multipathing,
|
||||
for example via the "/dev/disk/by-path/" device nodes.
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR zipl (8), crash (8), makedumpfile (8), dumpconf (8), vmconvert (1), vmur (8)
|
||||
.BR fdisk (8), parted (8)
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* Main functions
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <linux/fs.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mtio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "zgetdump.h"
|
||||
|
||||
/*
|
||||
* Globals
|
||||
*/
|
||||
struct zgetdump_globals g;
|
||||
|
||||
/*
|
||||
* Signal handler for exiting zgetdump (the atexit handler will do the work)
|
||||
*/
|
||||
static void sig_exit(int sig)
|
||||
{
|
||||
(void) sig;
|
||||
|
||||
STDERR("\n"); /* E.g. to get newline after '^C' */
|
||||
ERR_EXIT("Got signal %i, exiting...", sig);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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");
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if dump contains kdump dump and production system dump
|
||||
*/
|
||||
static void kdump_select_check(void)
|
||||
{
|
||||
static char *msg =
|
||||
"The dump contains \"kdump\" and \"production system\"\n"
|
||||
" Access \"production system\" with \"-s prod\"\n"
|
||||
" Access \"kdump\" with \"-s kdump\"\n"
|
||||
" Access the complete dump with \"-s all\"\n"
|
||||
" Send both dumps to your service organization";
|
||||
|
||||
if (g.opts.select_specified)
|
||||
return;
|
||||
if (!dfi_kdump_base())
|
||||
return;
|
||||
ERR_EXIT("%s", msg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Run "--umount" action
|
||||
*/
|
||||
static int do_umount(void)
|
||||
{
|
||||
zfuse_umount();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Run "--device" action
|
||||
*/
|
||||
static int do_device_info(void)
|
||||
{
|
||||
dt_init();
|
||||
dt_info_print();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Run "--info" action
|
||||
*/
|
||||
static int do_dump_info(void)
|
||||
{
|
||||
if (dfi_init() != 0) {
|
||||
dfi_info_print();
|
||||
STDERR("\nERROR: Dump is not complete\n");
|
||||
zg_exit(1);
|
||||
}
|
||||
kdump_select_check();
|
||||
dfi_info_print();
|
||||
dfi_exit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Run "--mount" action
|
||||
*/
|
||||
static int do_mount(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (dfi_init() != 0)
|
||||
ERR_EXIT("Dump cannot be processed (is not complete)");
|
||||
dfo_init();
|
||||
kdump_select_check();
|
||||
rc = zfuse_mount_dump();
|
||||
dfi_exit();
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Run "copy to stdout" action
|
||||
*/
|
||||
static int do_stdout(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (dfi_init() != 0)
|
||||
ERR_EXIT("Dump cannot be processed (is not complete)");
|
||||
dfo_init();
|
||||
kdump_select_check();
|
||||
rc = stdout_write_dump();
|
||||
dfi_exit();
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* The zgetdump main function
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* Main include file - Should be included by all source files
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 2017
|
||||
*
|
||||
* s390-tools is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the MIT license. See LICENSE for details.
|
||||
*/
|
||||
|
||||
#ifndef ZGETDUMP_H
|
||||
#define ZGETDUMP_H
|
||||
|
||||
#include "df_elf.h"
|
||||
#include "df_lkcd.h"
|
||||
#include "df_s390.h"
|
||||
#include "dfi.h"
|
||||
#include "dfo.h"
|
||||
#include "dt.h"
|
||||
#include "zg.h"
|
||||
|
||||
/*
|
||||
* zgetdump options
|
||||
*/
|
||||
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;
|
||||
const char *select;
|
||||
int select_specified;
|
||||
};
|
||||
|
||||
extern const char *OPTS_SELECT_KDUMP;
|
||||
extern const char *OPTS_SELECT_PROD;
|
||||
extern const char *OPTS_SELECT_ALL;
|
||||
|
||||
/*
|
||||
* zgetdump globals
|
||||
*/
|
||||
extern struct zgetdump_globals {
|
||||
struct zg_fh *fh;
|
||||
const char *prog_name;
|
||||
struct options opts;
|
||||
} g;
|
||||
|
||||
/*
|
||||
* Misc fuctions
|
||||
*/
|
||||
extern void opts_parse(int argc, char *argv[]);
|
||||
extern int stdout_write_dump(void);
|
||||
|
||||
#if HAVE_FUSE == 0
|
||||
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");
|
||||
}
|
||||
#else
|
||||
int zfuse_mount_dump(void);
|
||||
void zfuse_umount(void);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 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;
|
||||
extern struct dfi dfi_kdump_flat;
|
||||
extern struct dfi dfi_devmem;
|
||||
|
||||
/*
|
||||
* Supported DFO dump formats
|
||||
*/
|
||||
extern struct dfo dfo_s390;
|
||||
extern struct dfo dfo_elf;
|
||||
|
||||
/*
|
||||
* Supported s390 dumpers
|
||||
*/
|
||||
extern struct dt dt_s390mv;
|
||||
extern struct dt dt_s390sv;
|
||||
extern struct dt dt_scsi;
|
||||
|
||||
#endif /* ZGETDUMP_H */
|
||||
Reference in New Issue
Block a user