Subject: [PATCH] [FEAT RAS1101] kdump: Add s390-tools kdump support
From: Michael Holzheu <holzheu@linux.vnet.ibm.com>

Summary:     kdump: Add s390-tools kdump support
Description: The following features have been added for s390-tools kdump
             support:
             - kdump base support:
               The lsshut tool prints information when kdump is enabled.

             - PSW restart support:
               * dumpconf: The dumpconf service now configures the same action
                 for the restart trigger as for the panic trigger.
               * ipl_tools: The lsshut tool now shows the action for the
                 restart trigger.

             - zgetdump: vmcoreinfo support
               The zgetdump tool now prints UTS name information for dumps
               and creates a VMCOREINFO note section for ELF target dump
               format.

             - zgetdump: makedumpfile kdump format support
               The zgetdump tools can now print information on makedumpfile
               generated dumps (kdump format) with the --info option.

             - zgetdump: kdump failure recovery support
               If kdump failed and a stand-alone dump is created afterwards,
               with the new zgetdump --select option, the user can choose
               between the production system dump and the failed kdump dump. 
Problem-ID:  RAS1101
---
 etc/init.d/dumpconf     |   10 +
 ipl_tools/Makefile      |    2 
 ipl_tools/cmd_chreipl.c |    2 
 ipl_tools/cmd_chshut.c  |  107 +++++------------
 ipl_tools/cmd_lsreipl.c |   41 +++---
 ipl_tools/cmd_lsshut.c  |   72 ++++++-----
 ipl_tools/ipl_tools.h   |   33 ++++-
 ipl_tools/main.c        |    4 
 ipl_tools/man/chshut.8  |   23 +--
 ipl_tools/man/lsshut.8  |    8 -
 ipl_tools/shutdown.c    |   97 +++++++++++++++
 ipl_tools/system.c      |   45 +++++--
 man/dumpconf.8          |   34 +++--
 zdump/Makefile          |    3 
 zdump/dfi.c             |  297 ++++++++++++++++++++++++++++++++++++++++++------
 zdump/dfi.h             |   30 ++++
 zdump/dfi_kdump.c       |  149 +++++++++++++++++++-----
 zdump/dfi_vmcoreinfo.c  |  218 +++++++++++++++++++++++++++++++++++
 zdump/dfo.c             |    5 
 zdump/dfo_elf.c         |   15 ++
 zdump/opts.c            |   66 ++++++++--
 zdump/zg.c              |   26 ++++
 zdump/zg.h              |   12 +
 zdump/zgetdump.c        |   21 +++
 zdump/zgetdump.h        |    6 
 25 files changed, 1079 insertions(+), 247 deletions(-)

--- a/etc/init.d/dumpconf
+++ b/etc/init.d/dumpconf
@@ -71,6 +71,8 @@ check_environment()
 	DUMP_CONFIG_DIR=/$SYSFSDIR/firmware/dump
 	ON_PANIC_CONFIG_FILE=/$SYSFSDIR/firmware/shutdown_act\
 ions/on_panic
+	ON_RESTART_CONFIG_FILE=/$SYSFSDIR/firmware/shutdown_act\
+ions/on_restart
 	if [ ! -d $DUMP_CONFIG_DIR ]; then
 		pr_info "kernel has no dump on panic support"
 		exit 0
@@ -448,7 +450,7 @@ start()
 		fi
 	fi
 	if [ "$ON_PANIC" == "" ]; then
-		ON_PANIC="stop"
+		ON_PANIC="$(cat $ON_PANIC_CONFIG_FILE)"
 	fi
 
 	case "$ON_PANIC" in
@@ -474,6 +476,9 @@ start()
 		return
 	fi
 
+	if [ -f $ON_RESTART_CONFIG_FILE ]; then
+		echo $ON_PANIC > $ON_RESTART_CONFIG_FILE 2> /dev/null || RETVAL=1
+	fi
 	echo $ON_PANIC > $ON_PANIC_CONFIG_FILE 2> /dev/null || RETVAL=1
 
 	# check for errors
@@ -490,6 +495,9 @@ stop()
 		killproc -p $PIDFILE -TERM $DUMPCONF_BIN
 	fi
 	echo none > $DUMP_CONFIG_DIR/dump_type || RETVAL=1
+	if [ -f $ON_RESTART_CONFIG_FILE ]; then
+		echo stop > $ON_RESTART_CONFIG_FILE 2> /dev/null || RETVAL=1
+	fi
 	echo stop > $ON_PANIC_CONFIG_FILE || RETVAL=1
 	if [ $RETVAL -eq 0 ]; then
 		pr_info "Dump on panic is disabled now"
--- a/ipl_tools/Makefile
+++ b/ipl_tools/Makefile
@@ -4,7 +4,7 @@ CPPFLAGS += -I../include
 
 all: chreipl lsreipl chshut lsshut
 
-objects = main.o ccw.o fcp.o system.o \
+objects = main.o ccw.o fcp.o system.o shutdown.o \
 	  cmd_lsshut.o cmd_chshut.o cmd_lsreipl.o cmd_chreipl.o proc.o
 $(objects): ipl_tools.h
 
--- a/ipl_tools/cmd_chreipl.c
+++ b/ipl_tools/cmd_chreipl.c
@@ -568,7 +568,7 @@ static void chreipl_fcp(void)
 		sprintf(l.bootprog, "0");
 	write_str(l.bootprog,  "reipl/fcp/bootprog");
 
-	print_fcp(0);
+	print_fcp(0, 0);
 }
 
 static void chreipl_nss(void)
--- a/ipl_tools/cmd_chshut.c
+++ b/ipl_tools/cmd_chshut.c
@@ -3,32 +3,17 @@
  *
  * Command: chshut
  *
- * Copyright IBM Corp. 2008, 2010
+ * Copyright IBM Corp. 2008, 2011
  * Author(s): Hans-Joachim Picht <hans@linux.vnet.ibm.com>
  *            Michael Holzheu <holzheu@linux.vnet.ibm.com>
  */
 
 #include "ipl_tools.h"
 
-enum shutdown_action {
-	SA_IPL,
-	SA_REIPL,
-	SA_DUMP,
-	SA_STOP,
-	SA_VMCMD,
-};
-
-enum shutdown_trigger {
-	ST_HALT,
-	ST_POFF,
-	ST_REBOOT,
-	ST_PANIC,
-};
-
 static const char *const usage_chshut =
 "Usage: %s TRIGGER ACTION [COMMAND] [OPTIONS]\n"
 "\n"
-"Change action to be performed after shutdown.\n"
+"Change the shutdown actions for Linux on System z.\n"
 "\n"
 "TRIGGER specifies when the action is performed:\n"
 "  halt      System has been shut down (e.g. shutdown -h -H now)\n"
@@ -76,40 +61,38 @@ static void parse_chshut_options(int arg
 		ERR_EXIT("You must be root to perform this operation");
 }
 
-static enum shutdown_trigger shutdown_trigger_get(const char *trigger)
+static struct shutdown_trigger *shutdown_trigger_get(const char *trigger)
 {
-	if (strcmp(trigger, "halt") == 0)
-		return ST_HALT;
-	if (strcmp(trigger, "poff") == 0)
-		return ST_POFF;
-	if (strcmp(trigger, "reboot") == 0)
-		return ST_REBOOT;
-	if (strcmp(trigger, "panic") == 0)
-		return ST_PANIC;
+	int i;
+
+	for (i = 0; shutdown_trigger_vec[i]; i++) {
+		if (strcmp(trigger, shutdown_trigger_vec[i]->name) != 0)
+			continue;
+		if (shutdown_trigger_vec[i]->available)
+			return shutdown_trigger_vec[i];
+		ERR_EXIT("Shutdown trigger \"%s\" is not available on "
+			 "your system", trigger);
+	}
 	ERR_EXIT("Unknown shutdown trigger \"%s\" specified", trigger);
 }
 
-static enum shutdown_action shutdown_action_get(const char *action)
+static struct shutdown_action *shutdown_action_get(const char *action)
 {
-	if (strcmp(action, "ipl") == 0)
-		return SA_IPL;
-	if (strcmp(action, "reipl") == 0)
-		return SA_REIPL;
-	if (strcmp(action, "dump") == 0)
-		return SA_DUMP;
-	if (strcmp(action, "stop") == 0)
-		return SA_STOP;
-	if (strcmp(action, "vmcmd") == 0)
-		return SA_VMCMD;
+	int i;
+
+	for (i = 0; shutdown_action_vec[i]; i++) {
+		if (strcmp(action, shutdown_action_vec[i]->name) == 0)
+			return shutdown_action_vec[i];
+	}
 	ERR_EXIT("Unknown shutdown action \"%s\" specified", action);
 }
 
 /*
  * Multiple CP commands can be specified via "vmcmd XY1 vmcmd XY2 ..."
  */
-static void vmcmd_set(enum shutdown_trigger st, int argc, char *argv[])
+static void vmcmd_set(struct shutdown_trigger *st, int argc, char *argv[])
 {
-	char vmcmd[1024];
+	char vmcmd[1024], path[PATH_MAX];
 	int first = 1, i;
 	int vmcmd_length = 0;
 
@@ -136,25 +119,15 @@ static void vmcmd_set(enum shutdown_trig
 		i++;
 	}
 
-	switch (st) {
-	case ST_HALT:
-		write_str(vmcmd, "vmcmd/on_halt");
-		break;
-	case ST_POFF:
-		write_str(vmcmd, "vmcmd/on_poff");
-		break;
-	case ST_REBOOT:
-		write_str(vmcmd, "vmcmd/on_reboot");
-		break;
-	case ST_PANIC:
-		break;
-	}
+	sprintf(path, "vmcmd/%s", st->name_sysfs);
+	write_str(vmcmd, path);
 }
 
 void cmd_chshut(int argc, char *argv[])
 {
-	enum shutdown_trigger st;
-	enum shutdown_action sa;
+	struct shutdown_trigger *st;
+	struct shutdown_action *sa;
+	char path[PATH_MAX];
 
 	parse_chshut_options(argc, argv);
 
@@ -162,34 +135,26 @@ void cmd_chshut(int argc, char *argv[])
 		ERR("No trigger specified");
 		print_help_hint_exit();
 	}
+	shutdown_init();
 	st = shutdown_trigger_get(argv[1]);
-	if (st == ST_PANIC)
+	if (st == &shutdown_trigger_panic ||
+	    st == &shutdown_trigger_restart)
 		ERR_EXIT("Please use \"service dumpconf\" for "
-			 "configuring the panic action");
+			 "configuring the %s trigger",
+			 st->name);
 	if (argc < 3) {
 		ERR("No action specified");
 		print_help_hint_exit();
 	}
 	sa = shutdown_action_get(argv[2]);
 
-	if (sa == SA_VMCMD) {
+	if (sa == &shutdown_action_vmcmd) {
 		vmcmd_set(st, argc, argv);
 	} else if (argc != 3) {
 		ERR("Too many parameters specified");
 		print_help_hint_exit();
 	}
-
-	switch (st) {
-	case ST_HALT:
-		write_str(argv[2], "shutdown_actions/on_halt");
-		break;
-	case ST_POFF:
-		write_str(argv[2], "shutdown_actions/on_poff");
-		break;
-	case ST_REBOOT:
-		write_str(argv[2], "shutdown_actions/on_reboot");
-		break;
-	case ST_PANIC:
-		break;
-	}
+	sprintf(path, "shutdown_actions/%s", st->name_sysfs);
+	if (write_str_errno(argv[2], path))
+		ERR_EXIT_ERRNO("Could not set \"%s\"", path);
 }
--- a/ipl_tools/cmd_lsreipl.c
+++ b/ipl_tools/cmd_lsreipl.c
@@ -45,26 +45,29 @@ void print_nss(int show_ipl)
 		"/sys/firmware/reipl/nss/parm";
 
 	printf("%-12s nss\n", get_ipl_banner(show_ipl));
-	print_str("Name:        %s\n", dir, "name");
+	print_fw_str("Name:        %s\n", dir, "name");
 	if (access(path_bootparms, R_OK) == 0)
-		print_str("Bootparms:   \"%s\"\n", dir, "parm");
+		print_fw_str("Bootparms:   \"%s\"\n", dir, "parm");
 }
 
-void print_fcp(int show_ipl)
+void print_fcp(int show_ipl, int dump)
 {
 	char *dir = show_ipl ? "ipl" : "reipl/fcp";
 	char *path_bootparms = show_ipl ? "/sys/firmware/ipl/scp_data" :
 		"/sys/firmware/reipl/fcp/scp_data";
 
-	printf("%-12s fcp\n", get_ipl_banner(show_ipl));
+	if (dump)
+		printf("%-12s fcp_dump\n", get_ipl_banner(show_ipl));
+	else
+		printf("%-12s fcp\n", get_ipl_banner(show_ipl));
 
-	print_str("WWPN:        %s\n", dir, "wwpn");
-	print_str("LUN:         %s\n", dir, "lun");
-	print_str("Device:      %s\n", dir, "device");
-	print_str("bootprog:    %s\n", dir, "bootprog");
-	print_str("br_lba:      %s\n", dir, "br_lba");
+	print_fw_str("WWPN:        %s\n", dir, "wwpn");
+	print_fw_str("LUN:         %s\n", dir, "lun");
+	print_fw_str("Device:      %s\n", dir, "device");
+	print_fw_str("bootprog:    %s\n", dir, "bootprog");
+	print_fw_str("br_lba:      %s\n", dir, "br_lba");
 	if (access(path_bootparms, R_OK) == 0)
-		print_str("Bootparms:   \"%s\"\n", dir, "scp_data");
+		print_fw_str("Bootparms:   \"%s\"\n", dir, "scp_data");
 }
 
 void print_ccw(int show_ipl)
@@ -77,16 +80,16 @@ void print_ccw(int show_ipl)
 		"/sys/firmware/reipl/ccw/parm";
 
 	printf("%-12s ccw\n", get_ipl_banner(show_ipl));
-	print_str("Device:      %s\n", dir, "device");
+	print_fw_str("Device:      %s\n", dir, "device");
 	if (access(path_loadparm, R_OK) == 0) {
 		sprintf(loadparm_path, "%s/%s", dir, "loadparm");
-		read_str(loadparm, loadparm_path, sizeof(loadparm));
+		read_fw_str(loadparm, loadparm_path, sizeof(loadparm));
 		if (strcmp(loadparm, "        ") == 0)
 			loadparm[0] = 0;
 		printf("Loadparm:    \"%s\"\n", loadparm);
 	}
 	if (access(path_bootparms, R_OK) == 0)
-		print_str("Bootparms:   \"%s\"\n", dir, "parm");
+		print_fw_str("Bootparms:   \"%s\"\n", dir, "parm");
 }
 
 static void parse_lsreipl_options(int argc, char *argv[])
@@ -125,14 +128,16 @@ void cmd_lsreipl(int argc, char *argv[])
 	parse_lsreipl_options(argc, argv);
 
 	if (l.ipl_set)
-		read_str(reipl_type_str, "ipl/ipl_type",
-			 sizeof(reipl_type_str));
+		read_fw_str(reipl_type_str, "ipl/ipl_type",
+			    sizeof(reipl_type_str));
 	else
-		read_str(reipl_type_str, "reipl/reipl_type",
-			 sizeof(reipl_type_str));
+		read_fw_str(reipl_type_str, "reipl/reipl_type",
+			    sizeof(reipl_type_str));
 
 	if (strcmp(reipl_type_str, "fcp") == 0)
-		print_fcp(l.ipl_set);
+		print_fcp(l.ipl_set, 1);
+	else if (strcmp(reipl_type_str, "fcp_dump") == 0)
+		print_fcp(l.ipl_set, 1);
 	else if (strcmp(reipl_type_str, "ccw") == 0)
 		print_ccw(l.ipl_set);
 	else if (strcmp(reipl_type_str, "nss") == 0)
--- a/ipl_tools/cmd_lsshut.c
+++ b/ipl_tools/cmd_lsshut.c
@@ -3,7 +3,7 @@
  *
  * Command: lsshut
  *
- * Copyright IBM Corp. 2008, 2010
+ * Copyright IBM Corp. 2008, 2011
  * Author(s): Hans-Joachim Picht <hans@linux.vnet.ibm.com>
  *            Michael Holzheu <holzheu@linux.vnet.ibm.com>
  */
@@ -13,7 +13,7 @@
 static const char *const usage_lsshut =
 "Usage: %s [OPTIONS]\n"
 "\n"
-"Show actions to be taken after the kernel has shut down.\n"
+"Print the shutdown action configuration for Linux on System z.\n"
 "\n"
 "OPTIONS:\n"
 "  -h, --help           Print this help, then exit\n"
@@ -64,7 +64,7 @@ static void read_vmcmd(char *str, const
 
 	*str = 0;
 	ptr_old = ptr = buf;
-	read_str(buf, path, sizeof(buf));
+	read_fw_str(buf, path, sizeof(buf));
 	while ((ptr = strchr(ptr_old, '\n'))) {
 		*ptr = 0;
 		sprintf(tmp, "\"%s\",", ptr_old);
@@ -75,44 +75,52 @@ static void read_vmcmd(char *str, const
 	strcat(str, tmp);
 }
 
-void cmd_lsshut(int argc, char *argv[])
+static void print_kdump(void)
 {
-	char tmp[1024], cmd[1024];
+	struct stat sb;
+	char tmp[1024];
 
-	parse_lsshut_options(argc, argv);
+	if (stat("/sys/kernel/kexec_crash_loaded", &sb) != 0)
+		return;
+	read_str(tmp, "/sys/kernel/kexec_crash_loaded", sizeof(tmp));
+	if (strncmp(tmp, "1", 1) == 0)
+		printf("kdump,");
+}
 
-	printf("Trigger          Action\n");
-	printf("========================\n");
+static void shutdown_trigger_print(struct shutdown_trigger *trigger)
+{
+	char tmp[1024], cmd[1024], path[PATH_MAX];
 
-	read_str(tmp, "shutdown_actions/on_halt", sizeof(tmp));
-	if (strncmp(tmp, "vmcmd", strlen("vmcmd")) == 0) {
-		read_vmcmd(cmd, "vmcmd/on_halt");
-		printf("Halt             %s (%s)\n", tmp, cmd);
-	} else {
-		printf("Halt             %s\n", tmp);
-	}
+	sprintf(path, "shutdown_actions/%s", trigger->name_sysfs);
 
-	read_str(tmp, "shutdown_actions/on_panic", sizeof(tmp));
-	if (strncmp(tmp, "vmcmd", strlen("vmcmd")) == 0) {
-		read_vmcmd(cmd, "vmcmd/on_panic");
-		printf("Panic            %s (%s)\n", tmp, cmd);
-	} else {
-		printf("Panic            %s\n", tmp);
-	}
+	printf("%-16s ", trigger->name_print);
 
-	read_str(tmp, "shutdown_actions/on_poff", sizeof(tmp));
+	if ((trigger == &shutdown_trigger_panic ||
+	     trigger == &shutdown_trigger_restart))
+		print_kdump();
+	read_fw_str(tmp, path, sizeof(tmp));
 	if (strncmp(tmp, "vmcmd", strlen("vmcmd")) == 0) {
-		read_vmcmd(cmd, "vmcmd/on_poff");
-		printf("Power off        %s (%s)\n", tmp, cmd);
+		sprintf(path, "vmcmd/%s", trigger->name_sysfs);
+		read_vmcmd(cmd, path);
+		printf("vmcmd (%s)\n", cmd);
 	} else {
-		printf("Power off        %s\n", tmp);
+		printf("%s\n", tmp);
 	}
+}
 
-	read_str(tmp, "shutdown_actions/on_reboot", sizeof(tmp));
-	if (strncmp(tmp, "vmcmd", strlen("vmcmd")) == 0) {
-		read_vmcmd(cmd, "vmcmd/on_reboot");
-		printf("Reboot           %s (%s)\n", tmp, cmd);
-	} else {
-		printf("Reboot           %s\n", tmp);
+void cmd_lsshut(int argc, char *argv[])
+{
+	int i;
+
+	parse_lsshut_options(argc, argv);
+	shutdown_init();
+
+	printf("Trigger          Action\n");
+	printf("========================\n");
+
+	for (i = 0; shutdown_trigger_vec[i]; i++) {
+		if (!shutdown_trigger_vec[i]->available)
+			continue;
+		shutdown_trigger_print(shutdown_trigger_vec[i]);
 	}
 }
--- a/ipl_tools/ipl_tools.h
+++ b/ipl_tools/ipl_tools.h
@@ -3,7 +3,7 @@
  *
  * Common macro definitions and declarations
  *
- * Copyright IBM Corp. 2008, 2010
+ * Copyright IBM Corp. 2008, 2011
  * Author(s): Hans-Joachim Picht <hans@linux.vnet.ibm.com>
  *            Michael Holzheu <holzheu@linux.vnet.ibm.com>
  */
@@ -39,7 +39,7 @@ extern void cmd_lsreipl(int argc, char *
 extern void cmd_chreipl(int argc, char *argv[]);
 
 extern void print_ccw(int show_ipl);
-extern void print_fcp(int show_ipl);
+extern void print_fcp(int show_ipl, int dump);
 extern void print_nss(int show_ipl);
 
 /*
@@ -51,8 +51,10 @@ extern int is_root(void);
 extern void strlow(char *s);
 
 extern void write_str(char *string, char *file);
+extern int write_str_errno(char *string, char *file);
 extern void read_str(char *string, const char *file, size_t len);
-extern void print_str(const char *fmt, const char *dir, const char *file);
+extern void read_fw_str(char *string, const char *file, size_t len);
+extern void print_fw_str(const char *fmt, const char *dir, const char *file);
 
 extern void print_version_exit(void);
 extern void print_help_hint_exit(void);
@@ -72,6 +74,31 @@ extern int ccw_is_device(const char *dev
 extern void ccw_busid_get(const char *device, char *devno);
 
 /*
+ * Shutdown trigger
+ */
+struct shutdown_trigger {
+	const char	*name;
+	const char	*name_print;
+	const char	*name_sysfs;
+	int		available;
+};
+
+extern struct shutdown_trigger shutdown_trigger_panic;
+extern struct shutdown_trigger shutdown_trigger_restart;
+extern struct shutdown_trigger *shutdown_trigger_vec[];
+extern void shutdown_init(void);
+
+/*
+ * Shutdown actions
+ */
+struct shutdown_action {
+	const char	*name;
+};
+
+extern struct shutdown_action shutdown_action_vmcmd;
+extern struct shutdown_action *shutdown_action_vec[];
+
+/*
  * Error and print functions
  */
 #define ERR(x...) \
--- a/ipl_tools/main.c
+++ b/ipl_tools/main.c
@@ -3,7 +3,7 @@
  *
  * Main functions
  *
- * Copyright IBM Corp. 2008, 2010
+ * Copyright IBM Corp. 2008, 2011
  * Author(s): Hans-Joachim Picht <hans@linux.vnet.ibm.com>
  *            Michael Holzheu <holzheu@linux.vnet.ibm.com>
  */
@@ -24,7 +24,7 @@ void print_version_exit()
 {
 	printf("%s: Linux on System z shutdown actions version %s\n",
 		g.prog_name, RELEASE_STRING);
-	printf("Copyright IBM Corp. 2008, 2010\n");
+	printf("Copyright IBM Corp. 2008, 2011\n");
 	exit(0);
 }
 
--- a/ipl_tools/man/chshut.8
+++ b/ipl_tools/man/chshut.8
@@ -1,28 +1,27 @@
-.TH CHSHUT 8 "July 2010" "s390-tools"
+.TH CHSHUT 8 "Sept 2011" "s390-tools"
 
 .SH NAME
 chshut \- change the shutdown actions for Linux on System z
 
 .SH SYNOPSIS
-\fBchshut\fR [halt | poff | reboot] [ipl | reipl | stop | vmcmd] [VM Command]
+\fBchshut\fR TRIGGER ACTION [VM Command]
 
 .SH DESCRIPTION
-\fBchshut\fR is a tool that can be used to configure the Linux on System z
-shutdown actions.
+Use \fBchshut\fR to configure the Linux on System z shutdown actions.
 
-The tool handles up to three parameters. The first specifies
+The tool handles up to three parameters. The first parameter specifies
 the shutdown trigger. A shutdown trigger is an event that will stop Linux.
-The following shutdown triggers are supported: "halt", "power off" and
-"reboot". There exists a fourth shutdown trigger "panic" which is controlled
-by the
+The following shutdown triggers are supported: "halt", "poff", and "reboot".
+There are two other shutdown triggers "panic" and "restart" that are
+controlled by the
 .BR dumpconf (8)
 service script.
 
-The second  argument specifies the shutdown action that you want to execute
-in case of the specified trigger. Valid action arguments are "ipl", "reipl",
-"stop" and "vmcmd".
+The second parameter specifies the shutdown action that you want to run
+if the specified trigger occurs. Valid action arguments are "ipl", "reipl",
+"stop", and "vmcmd".
 
-In case you have chosen "vmcmd" as action a third parameter is used for the
+If you have chosen "vmcmd" as action, a third parameter is required for the
 CP command you want to execute under z/VM.
 
 .B Note:
--- a/ipl_tools/man/lsshut.8
+++ b/ipl_tools/man/lsshut.8
@@ -1,4 +1,4 @@
-.TH LSSHUT 8 "July 2010" "s390-tools"
+.TH LSSHUT 8 "Sept 2011" "s390-tools"
 
 .SH NAME
 lsshut \- print the shutdown action configuration for Linux on System z
@@ -9,8 +9,12 @@ lsshut \- print the shutdown action conf
 .SH DESCRIPTION
 The \fBlsshut\fR command lists the Linux on System z shutdown action
 configuration. This configuration handles what the system should do for
-the following shutdown triggers: "halt", "panic", "power off" and, "reboot".
+the following shutdown triggers: "halt", "poff", "reboot", "restart",
+and "panic".
 
+If kdump is enabled on your system, "restart" and "panic" also show the
+"kdump" action together with the action that is run in case of a "kdump"
+failure.
 .SH OPTIONS
 .TP
 \fB-h\fR or \fB--help\fR
--- /dev/null
+++ b/ipl_tools/shutdown.c
@@ -0,0 +1,97 @@
+/*
+ * ipl_tools - Linux for System z reipl and shutdown tools
+ *
+ * Shutdown actions and triggers common functions
+ *
+ * Copyright IBM Corp. 2008, 2011
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
+ */
+
+#include "ipl_tools.h"
+
+struct shutdown_trigger shutdown_trigger_halt = {
+	.name		= "halt",
+	.name_print	= "Halt",
+	.name_sysfs	= "on_halt",
+};
+
+struct shutdown_trigger shutdown_trigger_poff = {
+	.name		= "poff",
+	.name_print	= "Power off",
+	.name_sysfs	= "on_poff",
+};
+
+struct shutdown_trigger shutdown_trigger_reboot = {
+	.name		= "reboot",
+	.name_print	= "Reboot",
+	.name_sysfs	= "on_reboot",
+};
+
+struct shutdown_trigger shutdown_trigger_restart = {
+	.name		= "restart",
+	.name_print	= "Restart",
+	.name_sysfs	= "on_restart",
+};
+
+struct shutdown_trigger shutdown_trigger_panic = {
+	.name		= "panic",
+	.name_print	= "Panic",
+	.name_sysfs	= "on_panic",
+};
+
+struct shutdown_trigger *shutdown_trigger_vec[] = {
+	&shutdown_trigger_halt,
+	&shutdown_trigger_poff,
+	&shutdown_trigger_reboot,
+	&shutdown_trigger_restart,
+	&shutdown_trigger_panic,
+	NULL,
+};
+
+struct shutdown_action shutdown_action_ipl = {
+	.name		= "ipl",
+};
+
+struct shutdown_action shutdown_action_reipl = {
+	.name		= "reipl",
+};
+
+struct shutdown_action shutdown_action_dump = {
+	.name		= "dump",
+};
+
+struct shutdown_action shutdown_action_dump_reipl = {
+	.name		= "dump_reipl",
+};
+
+struct shutdown_action shutdown_action_stop = {
+	.name		= "stop",
+};
+
+struct shutdown_action shutdown_action_vmcmd = {
+	.name		= "vmcmd",
+};
+
+struct shutdown_action *shutdown_action_vec[] = {
+	&shutdown_action_ipl,
+	&shutdown_action_reipl,
+	&shutdown_action_dump,
+	&shutdown_action_dump_reipl,
+	&shutdown_action_stop,
+	&shutdown_action_vmcmd,
+	NULL,
+};
+
+void shutdown_init(void)
+{
+	char path[PATH_MAX];
+	struct stat sb;
+	int i;
+
+	for (i = 0; shutdown_trigger_vec[i]; i++) {
+		sprintf(path, "/sys/firmware/shutdown_actions/%s",
+			shutdown_trigger_vec[i]->name_sysfs);
+		if (stat(path, &sb) == 0)
+			shutdown_trigger_vec[i]->available = 1;
+	}
+}
--- a/ipl_tools/system.c
+++ b/ipl_tools/system.c
@@ -3,7 +3,7 @@
  *
  * Shared system functions
  *
- * Copyright IBM Corp. 2008, 2010
+ * Copyright IBM Corp. 2008, 2011
  * Author(s): Hans-Joachim Picht <hans@linux.vnet.ibm.com>
  *            Michael Holzheu <holzheu@linux.vnet.ibm.com>
  */
@@ -58,19 +58,17 @@ void strlow(char *s)
 /*
  * Read a string from a particular file
  */
-void read_str(char *string, const char *file, size_t len)
+void read_str(char *string, const char *path, size_t len)
 {
-	char path[PATH_MAX];
 	size_t rc;
 	FILE *fh;
 
-	snprintf(path, sizeof(path), "/sys/firmware/%s", file);
 	fh = fopen(path, "rb");
 	if (fh == NULL)
-		ERR_EXIT_ERRNO("Could not open \"%s\"", file);
+		ERR_EXIT_ERRNO("Could not open \"%s\"", path);
 	rc = fread(string, 1, len - 1, fh);
 	if (rc == 0 && ferror(fh))
-		ERR_EXIT_ERRNO("Could not read \"%s\"", file);
+		ERR_EXIT_ERRNO("Could not read \"%s\"", path);
 	fclose(fh);
 	string[rc] = 0;
 	if (string[strlen(string) - 1] == '\n')
@@ -78,14 +76,25 @@ void read_str(char *string, const char *
 }
 
 /*
+ * Read a string from a particular /sys/firmware file
+ */
+void read_fw_str(char *string, const char *file, size_t len)
+{
+	char path[PATH_MAX];
+
+	snprintf(path, sizeof(path), "/sys/firmware/%s", file);
+	return read_str(string, path, len);
+}
+
+/*
  * Print content of a file (path = dir/file)
  */
-void print_str(const char *fmt, const char *dir, const char *file)
+void print_fw_str(const char *fmt, const char *dir, const char *file)
 {
 	char path[PATH_MAX], str[4096];
 
 	snprintf(path, sizeof(path), "%s/%s", dir, file);
-	read_str(str, path, sizeof(str));
+	read_fw_str(str, path, sizeof(str));
 	printf(fmt, str);
 }
 
@@ -106,3 +115,23 @@ void write_str(char *string, char *file)
 		ERR_EXIT_ERRNO("Could not set \"%s\"", file);
 	close(fh);
 }
+
+/*
+ * Write a string to a file and return ERRNO
+ */
+int write_str_errno(char *string, char *file)
+{
+	char path[PATH_MAX], value[4096];
+	int fh;
+
+	snprintf(value, sizeof(value), "%s\n", string);
+	snprintf(path, sizeof(path), "/sys/firmware/%s", file);
+	fh = open(path, O_WRONLY);
+	if (fh < 0)
+		ERR_EXIT_ERRNO("Could not open \"%s\"", file);
+	if (write(fh, value, strlen(value)) < 0)
+		return errno;
+	close(fh);
+	return 0;
+}
+
--- a/man/dumpconf.8
+++ b/man/dumpconf.8
@@ -1,7 +1,7 @@
-.TH DUMPCONF 8 "Nov 2009" "s390-tools"
+.TH DUMPCONF 8 "Sept 2011" "s390-tools"
 
 .SH NAME
-dumpconf \- Configure an ON_PANIC action for Linux on System z.
+dumpconf \- Configure panic and PSW restart actions for Linux on System z
 
 .SH SYNOPSIS
 .br
@@ -11,30 +11,32 @@ dumpconf \- Configure an ON_PANIC action
 
 .SH DESCRIPTION
 \fBdumpconf\fR reads the /etc/sysconfig/dumpconf file
-and establishes the action to be taken in case a kernel panic occurs.
+and establishes the action to be taken if a kernel panic occurs
+or PSW restart is triggered.
 
 The following keywords can be used in the dumpconf file:
 
 .TP
 \fB  - ON_PANIC:\fR
-Shutdown action in case of a kernel panic. Possible values are 'dump', 'reipl', 'dump_reipl', 'stop' and 'vmcmd':
+Shutdown action in case of a kernel panic or a PSW restart. Possible values
+are 'dump', 'reipl', 'dump_reipl', 'stop' and 'vmcmd':
 .br
 
-dump: trigger dump according to the configuration in /etc/sysconfig/dumpconf.
+dump: Trigger dump according to the configuration in /etc/sysconfig/dumpconf.
 .br
 
-reipl: trigger re-IPL according to the configuration under /sys/firmware/reipl.
+reipl: Trigger re-IPL according to the configuration under /sys/firmware/reipl.
 .br
 
-dump_reipl: first trigger dump according to the configuration in
+dump_reipl: First trigger dump according to the configuration in
 /etc/sysconfig/dumpconf, then trigger re-IPL according to the configuration
 under /sys/firmware/reipl.
 .br
 
-stop: stop Linux and enter disabled wait (default).
+stop: Stop Linux and enter disabled wait (default).
 .br
 
-vmcmd: trigger CP command according to the 'VMCMD_X' configuration in
+vmcmd: Trigger CP command according to the 'VMCMD_X' configuration in
 /etc/sysconfig/dumpconf.
 
 .TP
@@ -63,7 +65,8 @@ Boot record logical block address.
 
 .TP
 \fB  - VMCMD_1, VMCMD_2 ... VMCMD_8:\fR
-Up to eight CP commands, which are triggered in case of a kernel panic.
+Up to eight CP commands, which are executed in case of a kernel panic
+or PSW restart.
 
 .TP
 \fB - DELAY_MINUTES:\fR
@@ -107,6 +110,11 @@ Print usage information, then exit.
 \fB-v\fR or \fB--version\fR
 Print version information, then exit.
 
+.SH PSW Restart
+PSW Restart can be triggered by the operator under z/VM with the CP
+command "#cp system restart" and under LPAR on the HMC with
+"Recovery/PSW Restart".
+
 .SH EXAMPLES:
 The following are examples of the /etc/sysconfig/dumpconf file:
 .br
@@ -149,7 +157,7 @@ BR_LBA=0
 
 #
 .br
-# Example configuration for CP commands on panic
+# Example configuration for CP commands
 .br
 #
 .br
@@ -163,7 +171,7 @@ VMCMD_3="IPL 3456"
 
 #
 .br
-# Example config for re-IPL on panic
+# Example config for re-IPL
 .br
 #
 .br
@@ -172,4 +180,4 @@ ON_PANIC=reipl
 DELAY_MINUTES=5
 
 .SH SEE ALSO
-Linux on zSeries: Using the Dump Tools
+Linux on System z: Using the Dump Tools
--- a/zdump/Makefile
+++ b/zdump/Makefile
@@ -6,7 +6,8 @@ LDLIBS += -lz
 all: zgetdump
 
 OBJECTS = zgetdump.o opts.o zg.o \
-	  dfi.o dfi_lkcd.o dfi_elf.o dfi_s390.o dfi_s390mv.o dfi_s390tape.o \
+	  dfi.o dfi_vmcoreinfo.o \
+	  dfi_lkcd.o dfi_elf.o dfi_s390.o dfi_s390mv.o dfi_s390tape.o \
 	  dfi_kdump.o \
 	  dfo.o dfo_elf.o dfo_s390.o \
 	  df_s390.o \
--- a/zdump/dfi.c
+++ b/zdump/dfi.c
@@ -3,7 +3,7 @@
  *
  * Generic input dump format functions (DFI - Dump Format Input)
  *
- * Copyright IBM Corp. 2001, 2010
+ * Copyright IBM Corp. 2001, 2011
  * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
  */
 
@@ -23,6 +23,7 @@ static struct dfi *dfi_vec[] = {
 	&dfi_lkcd,
 	&dfi_elf,
 	&dfi_kdump,
+	&dfi_kdump_flat,
 	NULL,
 };
 
@@ -50,14 +51,16 @@ struct mem {
  * Dump header attribute information
  */
 struct attr {
-	unsigned int	*dfi_version;
-	struct timeval	*time;
-	struct timeval	*time_end;
-	u64		*cpu_id;
-	u64		*mem_size_real;
-	enum dfi_arch	*build_arch;
-	unsigned int	*vol_nr;
-	u32		*real_cpu_cnt;
+	unsigned int		*dfi_version;
+	struct timeval		*time;
+	struct timeval		*time_end;
+	u64			*cpu_id;
+	u64			*mem_size_real;
+	enum dfi_arch		*build_arch;
+	unsigned int		*vol_nr;
+	u32			*real_cpu_cnt;
+	struct new_utsname	*utsname;
+	char			*dump_method;
 };
 
 /*
@@ -69,6 +72,8 @@ static struct {
 	struct mem	mem;
 	struct cpus	cpus;
 	struct dfi	*dfi;
+	unsigned long	kdump_base;
+	unsigned long	kdump_size;
 } l;
 
 /*
@@ -92,6 +97,16 @@ static void date_print(void)
 }
 
 /*
+ * Initialize DFI mem
+ */
+static void mem_init(void)
+{
+	l.mem.start_addr = U64_MAX;
+	l.mem.end_addr = 0;
+	list_init(&l.mem.chunk_list);
+}
+
+/*
  * Print memory map
  */
 static void mem_map_print(void)
@@ -106,6 +121,23 @@ static void mem_map_print(void)
 }
 
 /*
+ * Is memory range valid?
+ */
+int dfi_mem_range_valid(u64 addr, u64 len)
+{
+	struct dfi_mem_chunk *mem_chunk;
+	u64 addr_end = addr + len;
+
+	do {
+		mem_chunk = dfi_mem_chunk_find(addr);
+		if (!mem_chunk)
+			return 0;
+		addr += MIN(len, mem_chunk->end - addr + 1);
+	} while (addr < addr_end);
+	return 1;
+}
+
+/*
  * Print dump information (--info option)
  */
 void dfi_info_print(void)
@@ -115,8 +147,15 @@ void dfi_info_print(void)
 	if (l.attr.dfi_version)
 		STDERR("  Version............: %d\n", *l.attr.dfi_version);
 	date_print();
+	if (l.attr.dump_method)
+		STDERR("  Dump method........: %s\n", l.attr.dump_method);
 	if (l.attr.cpu_id)
 		STDERR("  Dump CPU ID........: %llx\n", *l.attr.cpu_id);
+	if (l.attr.utsname) {
+		STDERR("  UTS node name......: %s\n", l.attr.utsname->nodename);
+		STDERR("  UTS kernel release.: %s\n", l.attr.utsname->release);
+		STDERR("  UTS kernel version.: %s\n", l.attr.utsname->version);
+	}
 	if (l.attr.vol_nr)
 		STDERR("  Volume number......: %d\n", *l.attr.vol_nr);
 	if (l.attr.build_arch)
@@ -127,11 +166,14 @@ void dfi_info_print(void)
 		STDERR("  CPU count (online).: %d\n", l.cpus.cnt);
 	if (l.attr.real_cpu_cnt)
 		STDERR("  CPU count (real)...: %d\n", *l.attr.real_cpu_cnt);
-	STDERR("  Dump memory range..: %lld MB\n", TO_MIB(dfi_mem_range()));
+	if (dfi_mem_range())
+		STDERR("  Dump memory range..: %lld MB\n",
+		       TO_MIB(dfi_mem_range()));
 	if (l.attr.mem_size_real)
 		STDERR("  Real memory range..: %lld MB\n",
 		      TO_MIB(*l.attr.mem_size_real));
-	mem_map_print();
+	if (dfi_mem_range())
+		mem_map_print();
 	if (l.dfi->info_dump) {
 		STDERR("\nDump device info:\n");
 		l.dfi->info_dump();
@@ -181,6 +223,8 @@ unsigned int dfi_mem_chunk_cnt(void)
  */
 u64 dfi_mem_range(void)
 {
+	if (l.mem.start_addr == U64_MAX)
+		return 0;
 	return l.mem.end_addr - l.mem.start_addr + 1;
 }
 
@@ -246,6 +290,8 @@ struct dfi_mem_chunk *dfi_mem_chunk_find
 void dfi_cpu_info_init(enum dfi_cpu_content cpu_content)
 {
 	l.cpus.content = cpu_content;
+	list_init(&l.cpus.list);
+	l.cpus.cnt = 0;
 }
 
 /*
@@ -324,7 +370,7 @@ struct list *dfi_cpu_list(void)
 /*
  * Read memory at given address
  */
-void dfi_mem_read(u64 addr, void *buf, size_t cnt)
+static void dfi_mem_read_raw(u64 addr, void *buf, size_t cnt)
 {
 	struct dfi_mem_chunk *mem_chunk;
 	u64 size, copied = 0;
@@ -340,6 +386,55 @@ void dfi_mem_read(u64 addr, void *buf, s
 }
 
 /*
+ * Read memory at given address and do kdump swap if necessary
+ */
+void dfi_mem_read(u64 addr, void *buf, size_t cnt)
+{
+	u64 copied = 0;
+
+	if (!g.opts.kdump_swap)
+		return dfi_mem_read_raw(addr, buf, cnt);
+
+	if (addr < l.kdump_size) {
+		copied = MIN(cnt, l.kdump_size - addr);
+		dfi_mem_read_raw(addr + l.kdump_base, buf, copied);
+	}
+	dfi_mem_read_raw(addr + copied, buf + copied, cnt - copied);
+}
+
+/*
+ * Read memory at given address with return code
+ */
+static int dfi_mem_read_raw_rc(u64 addr, void *buf, size_t cnt)
+{
+	if (!dfi_mem_range_valid(addr, cnt))
+		return -EINVAL;
+	dfi_mem_read(addr, buf, cnt);
+	return 0;
+}
+
+/*
+ * Read memory at given address with return code and do kdump swap if necessary
+ * rc = 0: Read was successful
+ */
+int dfi_mem_read_rc(u64 addr, void *buf, size_t cnt)
+{
+	u64 copied = 0;
+	int rc;
+
+	if (!g.opts.kdump_swap)
+		return dfi_mem_read_raw_rc(addr, buf, cnt);
+
+	if (addr < l.kdump_size) {
+		copied = MIN(cnt, l.kdump_size - addr);
+		rc = dfi_mem_read_raw_rc(addr + l.kdump_base, buf, copied);
+		if (rc)
+			return rc;
+	}
+	return dfi_mem_read_raw_rc(addr + copied, buf + copied, cnt - copied);
+}
+
+/*
  * Get input dump format name
  */
 const char *dfi_name(void)
@@ -380,27 +475,11 @@ const char *dfi_arch_str(enum dfi_arch a
 }
 
 /*
- * Initialize input dump format.
+ * Initialize attributes
  */
-int dfi_init(void)
+static void attr_init(void)
 {
-	struct dfi *dfi;
-	int i = 0, rc;
-
-	l.mem.start_addr = U64_MAX;
-	l.mem.end_addr = 0;
-	list_init(&l.mem.chunk_list);
-	list_init(&l.cpus.list);
-	while ((dfi = dfi_vec[i])) {
-		l.dfi = dfi;
-		g.fh = zg_open(g.opts.device, O_RDONLY, ZG_CHECK);
-		rc = dfi->init();
-		if (rc == 0 || rc == -EINVAL)
-			return rc;
-		zg_close(g.fh);
-		i++;
-	}
-	ERR_EXIT("No valid dump found on \"%s\"", g.opts.device);
+	memset(&l.attr, 0, sizeof(l.attr));
 }
 
 /*
@@ -408,6 +487,8 @@ int dfi_init(void)
  */
 void dfi_attr_time_set(struct timeval *time)
 {
+	if (time->tv_sec == 0)
+		return;
 	l.attr.time = zg_alloc(sizeof(*l.attr.time));
 	*l.attr.time = *time;
 }
@@ -422,6 +503,8 @@ struct timeval *dfi_attr_time(void)
  */
 void dfi_attr_time_end_set(struct timeval *time_end)
 {
+	if (time_end->tv_sec == 0)
+		return;
 	l.attr.time_end = zg_alloc(sizeof(*l.attr.time_end));
 	*l.attr.time_end = *time_end;
 }
@@ -464,6 +547,33 @@ u64 *dfi_attr_cpu_id(void)
 }
 
 /*
+ * Attribute: utsname
+ */
+void dfi_attr_utsname_set(struct new_utsname *utsname)
+{
+	l.attr.utsname = zg_alloc(sizeof(*utsname));
+	memcpy(l.attr.utsname, utsname, sizeof(*utsname));
+}
+
+struct new_utsname *dfi_attr_utsname(void)
+{
+	return l.attr.utsname;
+}
+
+/*
+ * Attribute: dump method
+ */
+void dfi_attr_dump_method_set(char *dump_method)
+{
+	l.attr.dump_method = zg_strdup(dump_method);
+}
+
+char *dfi_attr_dump_method(void)
+{
+	return l.attr.dump_method;
+}
+
+/*
  * Attribute: Real memory size
  */
 void dfi_attr_mem_size_real_set(u64 mem_size_real)
@@ -612,3 +722,128 @@ void dfi_cpu_add_from_lc(u32 lc_addr)
 	dfi_cpu_add(cpu);
 }
 
+/*
+ * Return kdump base
+ */
+unsigned long dfi_kdump_base(void)
+{
+	return l.kdump_base;
+}
+
+/*
+ * Check if dump contains a kdump dump and initialize kdump_base and kdump_size
+ */
+static void kdump_init(void)
+{
+	unsigned long base, size;
+
+	dfi_mem_read_raw(0x10418, &base, sizeof(base));
+	dfi_mem_read_raw(0x10420, &size, sizeof(size));
+	if (base == 0 || size == 0)
+		return;
+	if (base % MIB || size % MIB)
+		return;
+	if (!dfi_mem_range_valid(base, size))
+		return;
+	l.kdump_base = base;
+	l.kdump_size = size;
+}
+
+/*
+ * If "--select prod" is set, modify DFI to show production system dump
+ */
+static void kdump_swap_init(void)
+{
+	unsigned long prefix, ptr, count, tv_sec, i;
+	struct timeval timeval;
+
+	if (g.opts.select_specified && !l.kdump_base)
+		ERR_EXIT("The \"--select\" option is not possible with this "
+			 "dump");
+	if (!g.opts.kdump_swap)
+		return;
+
+	attr_init();
+	dfi_arch_set(DFI_ARCH_64);
+	dfi_cpu_info_init(DFI_CPU_CONTENT_NONE);
+	if (dfi_vmcoreinfo_symbol(&ptr, "lowcore_ptr"))
+		return;
+	if (dfi_vmcoreinfo_length(&count, "lowcore_ptr"))
+		return;
+	if (dfi_vmcoreinfo_val(&tv_sec, "CRASHTIME") == 0) {
+		timeval.tv_sec = tv_sec;
+		timeval.tv_usec = 0;
+		dfi_attr_time_set(&timeval);
+	}
+	dfi_cpu_info_init(DFI_CPU_CONTENT_ALL);
+	for (i = 0; i < count; i++) {
+		if (dfi_mem_read_rc(ptr + i * sizeof(long), &prefix,
+				   sizeof(prefix)))
+			continue;
+		if (prefix == 0)
+			continue;
+		if (prefix % 0x1000)
+			continue;
+		dfi_cpu_add_from_lc(prefix);
+	}
+}
+
+/*
+ * Try to get utsname info from dump
+ */
+static void utsname_init(void)
+{
+	struct new_utsname *utsname;
+	unsigned long ptr;
+	char buf[1024];
+
+	if (dfi_vmcoreinfo_symbol(&ptr, "init_uts_ns"))
+		return;
+	if (dfi_mem_read_rc(ptr, buf, sizeof(buf)))
+		return;
+	utsname = memchr(buf, 'L', sizeof(buf) - sizeof(*utsname));
+	if (!utsname)
+		return;
+	if (strncmp(utsname->sysname, "Linux", sizeof(utsname->version) != 0))
+		return;
+	dfi_attr_utsname_set(utsname);
+}
+
+/*
+ * Initialize input dump format.
+ */
+int dfi_init(void)
+{
+	struct dfi *dfi;
+	int i = 0, rc;
+
+	l.arch = DFI_ARCH_UNKNOWN;
+	mem_init();
+	attr_init();
+	dfi_cpu_info_init(DFI_CPU_CONTENT_NONE);
+	while ((dfi = dfi_vec[i])) {
+		l.dfi = dfi;
+		g.fh = zg_open(g.opts.device, O_RDONLY, ZG_CHECK);
+		rc = dfi->init();
+		if (rc == 0 && dfi_feat_seek()) {
+			kdump_init();
+			dfi_vmcoreinfo_init();
+			kdump_swap_init();
+			utsname_init();
+		}
+		if (rc == 0 || rc == -EINVAL)
+			return rc;
+		zg_close(g.fh);
+		i++;
+	}
+	ERR_EXIT("No valid dump found on \"%s\"", g.opts.device);
+}
+
+/*
+ * Cleanup input dump format.
+ */
+void dfi_exit(void)
+{
+	if (l.dfi && l.dfi->exit)
+		l.dfi->exit();
+}
--- a/zdump/dfi.h
+++ b/zdump/dfi.h
@@ -3,13 +3,14 @@
  *
  * Generic input dump format functions (DFI - Dump Format Input)
  *
- * Copyright IBM Corp. 2001, 2010
+ * Copyright IBM Corp. 2001, 2011
  * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
  */
 
 #ifndef DFI_H
 #define DFI_H
 
+#include <linux/utsname.h>
 #include "zg.h"
 #include "list.h"
 
@@ -144,6 +145,7 @@ struct dfi_mem_chunk {
 extern void dfi_mem_chunk_add(u64 start, u64 size, void *data,
 			      dfi_mem_chunk_read_fn read_fn);
 extern u64 dfi_mem_range(void);
+extern 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);
@@ -166,6 +168,12 @@ extern struct timeval *dfi_attr_time_end
 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();
 
@@ -185,6 +193,7 @@ 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_info_print(void);
 
 /*
@@ -197,16 +206,35 @@ 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);
 
 #endif /* DFI_H */
--- a/zdump/dfi_kdump.c
+++ b/zdump/dfi_kdump.c
@@ -1,27 +1,18 @@
 /*
  * zgetdump - Tool for copying and converting System z dumps
  *
- * kdump input format
+ * kdump and kdump_flat input format
  *
- * Copyright IBM Corp. 2001, 2010
+ * Copyright IBM Corp. 2001, 2011
  * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
  */
 
 #include "zgetdump.h"
 
-struct l_new_utsname {
-	char sysname[65];
-	char nodename[65];
-	char release[65];
-	char version[65];
-	char machine[65];
-	char domainname[65];
-};
-
 struct df_kdump_hdr {
 	char			signature[8];
 	int			header_version;
-	struct l_new_utsname	utsname;
+	struct new_utsname	utsname;
 	struct timeval		timestamp;
 	unsigned int		status;
 	int			block_size;
@@ -46,6 +37,17 @@ struct df_kdump_sub_hdr {
 	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
  */
@@ -70,6 +72,10 @@ static void print_header(void)
 	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);
@@ -80,9 +86,34 @@ static void print_header(void)
 #endif
 
 /*
- * Read kdump dump header
+ * 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);
+	return 0;
+}
+
+/*
+ * Initialize kdump DFI
  */
-static int read_kdump_hdr(void)
+static int dfi_kdump_init(void)
 {
 	if ((zg_type(g.fh) == ZG_TYPE_FILE) && (zg_size(g.fh) < sizeof(l.hdr)))
 		return -ENODEV;
@@ -91,32 +122,98 @@ static int read_kdump_hdr(void)
 		return -ENODEV;
 	zg_seek(g.fh, l.hdr.block_size, ZG_CHECK);
 	zg_read(g.fh, &l.shdr, sizeof(l.shdr), ZG_CHECK);
-	dfi_attr_version_set(l.hdr.header_version);
-	dfi_attr_real_cpu_cnt_set(l.hdr.nr_cpus);
-	dfi_arch_set(DFI_ARCH_64);
 #ifdef DEBUG
 	print_header();
+	print_sub_header();
 #endif
-	return 0;
+	return init_kdump_hdr(&l.hdr);
 }
 
 /*
- * Initialize kdump DFI
+ * kdump DFI operations
  */
-static int dfi_kdump_init(void)
+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)
 {
-	if (read_kdump_hdr() != 0)
+	struct df_kdump_flat_hdr hdr;
+
+	if ((zg_type(g.fh) == ZG_TYPE_FILE) && (zg_size(g.fh) < sizeof(l.hdr)))
 		return -ENODEV;
-	dfi_mem_chunk_add(0, l.hdr.max_mapnr * PAGE_SIZE, NULL, NULL);
+	zg_read(g.fh, &hdr, sizeof(hdr), ZG_CHECK);
+	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;
 }
 
 /*
- * S390 DFI operations
+ * Initialize kdump_flat DFI
  */
-struct dfi dfi_kdump = {
-	.name		= "kdump",
-	.init		= dfi_kdump_init,
+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,
 };
--- /dev/null
+++ b/zdump/dfi_vmcoreinfo.c
@@ -0,0 +1,218 @@
+/*
+ * zgetdump - Tool for copying and converting System z dumps
+ *
+ * vmcoreinfo access functions
+ *
+ * Copyright IBM Corp. 2011
+ * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
+ */
+
+#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 inline u32 csum_partial(const void *buff, int len, u32 sum)
+{
+	register unsigned long reg2 asm("2") = (unsigned long) buff;
+	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;
+}
+
+static u32 os_info_csum(struct os_info *os_info)
+{
+	int size = sizeof(*os_info) - offsetof(struct os_info, version_major);
+	return 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, &note, 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 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);
+}
--- a/zdump/dfo.c
+++ b/zdump/dfo.c
@@ -84,7 +84,10 @@ void dfo_chunk_mem_fn(struct dfo_chunk *
 {
 	struct dfi_mem_chunk *mem_chunk = dfo_chunk->data;
 
-	mem_chunk->read_fn(mem_chunk, off, buf, cnt);
+	if (g.opts.kdump_swap)
+		dfi_mem_read(mem_chunk->start + off, buf, cnt);
+	else
+		mem_chunk->read_fn(mem_chunk, off, buf, cnt);
 }
 
 /*
--- a/zdump/dfo_elf.c
+++ b/zdump/dfo_elf.c
@@ -83,7 +83,7 @@ static u64 loads_init(Elf64_Phdr *phdr,
  * Initialize ELF note
  */
 static void *nt_init(void *buf, Elf64_Word type, void *desc, int d_len,
-		       const char *name)
+		     const char *name)
 {
 	Elf64_Nhdr *note;
 	u64 len;
@@ -199,6 +199,18 @@ static void *nt_prpsinfo(void *ptr)
 }
 
 /*
+ * 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)
@@ -221,6 +233,7 @@ static void *notes_init(Elf64_Phdr *phdr
 		ptr = nt_s390_prefix(ptr, cpu);
 	}
 out:
+	ptr = nt_vmcoreinfo(ptr);
 	memset(phdr, 0, sizeof(*phdr));
 	phdr->p_type = PT_NOTE;
 	phdr->p_offset = notes_offset;
--- a/zdump/opts.c
+++ b/zdump/opts.c
@@ -18,23 +18,32 @@
  * Text for --help option
  */
 static char help_text[] =
-"Usage: zgetdump [OPTIONS] [DUMPDEV] [DIR]\n"
+"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 takes as source a dump device or dump file (DUMPDEV)\n"
-"and writes its contents to standard output, which you can redirect to a\n"
-"specific file. Alternatively  you  can  also  mount the dump content.\n"
-"Because zgetdump is able to read and write different dump formats, it\n"
-"can  be used to convert a dump from one format to another. zgetdump can\n"
-"also verify if a dump is valid or check, whether a DASD device contains\n"
-"a valid dump tool.\n"
+"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"
-"-h, --help       Print this help, then exit.\n"
-"-v, --version    Print version information, then exit.\n"
-"-m, --mount      Mount dump to mount point DIR\n"
-"-u, --umount     Unmount dump from mount point DIR\n"
-"-f, --fmt <FMT>  Specify target dump format FMT (\"elf\" or \"s390\")\n"
-"-i, --info       Print dump information\n"
-"-d, --device     Print dump device information\n";
+"  - 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\" or \"prod\")\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, 2010";
 
@@ -90,6 +99,20 @@ static void fmt_set(const char *fmt)
 }
 
 /*
+ * Set "--select" option
+ */
+static void select_set(const char *select)
+{
+	if (strcmp(select, "kdump") != 0 && strcmp(select, "prod") != 0)
+		ERR_EXIT("Invalid select argument \"%s\" specified", select);
+
+	g.opts.select_specified = 1;
+	g.opts.select = select;
+	if (strcmp(select, "prod") == 0)
+		g.opts.kdump_swap = 1;
+}
+
+/*
  * Set mount point
  */
 static void mount_point_set(const char *mount_point)
@@ -138,6 +161,13 @@ static void action_set(enum zg_action ac
  */
 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;
 
@@ -203,10 +233,11 @@ void opts_parse(int argc, char *argv[])
 		{"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'},
 		{0,         0,                 0,     0 }
 	};
-	static const char optstr[] = "hvidmuf:X";
+	static const char optstr[] = "hvidmus:f:X";
 
 	init_defaults();
 	while ((opt = getopt_long(argc, argv, optstr, long_opts, &idx)) != -1) {
@@ -230,6 +261,9 @@ void opts_parse(int argc, char *argv[])
 		case 'f':
 			fmt_set(optarg);
 			break;
+		case 's':
+			select_set(optarg);
+			break;
 		case 'X':
 			g.opts.debug_specified = 1;
 			break;
--- a/zdump/zg.c
+++ b/zdump/zg.c
@@ -199,6 +199,32 @@ ssize_t zg_read(struct zg_fh *zg_fh, voi
 }
 
 /*
+ * 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)
--- a/zdump/zg.h
+++ b/zdump/zg.h
@@ -31,12 +31,12 @@
 /*
  * IEC definitions
  */
-#define KIB_DIFF	(1024)
-#define MIB_DIFF	(1024 * 1024)
-#define GIB_DIFF	(1024 * 1024 * 1024)
+#define KIB	(1024)
+#define MIB	(1024 * 1024)
+#define GIB	(1024 * 1024 * 1024)
 
-#define TO_MIB(x) ((x + (MIB_DIFF / 2)) / MIB_DIFF)
-#define TO_KIB(x) ((x + (KIB_DIFF / 2)) / KIB_DIFF)
+#define TO_MIB(x) ((x + (MIB / 2)) / MIB)
+#define TO_KIB(x) ((x + (KIB / 2)) / KIB)
 
 /*
  * Memory functions
@@ -162,6 +162,8 @@ extern struct zg_fh *zg_open(const char
 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);
--- a/zdump/zgetdump.c
+++ b/zdump/zgetdump.c
@@ -81,6 +81,24 @@ fail:
 }
 
 /*
+ * 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"
+		"          Send both dumps to your service organization";
+
+	if (g.opts.select_specified)
+		return;
+	if (!dfi_kdump_base())
+		return;
+	ERR_EXIT(msg);
+}
+
+/*
  * Run "--umount" action
  */
 static int do_umount(void)
@@ -109,6 +127,7 @@ static int do_dump_info(void)
 		STDERR("\nERROR: Dump is not complete\n");
 		zg_exit(1);
 	}
+	kdump_select_check();
 	dfi_info_print();
 	return 0;
 }
@@ -121,6 +140,7 @@ static int do_mount(void)
 	if (dfi_init() != 0)
 		ERR_EXIT("Dump cannot be processed (is not complete)");
 	dfo_init();
+	kdump_select_check();
 	return zfuse_mount_dump();
 }
 
@@ -132,6 +152,7 @@ static int do_stdout(void)
 	if (dfi_init() != 0)
 		ERR_EXIT("Dump cannot be processed (is not complete)");
 	dfo_init();
+	kdump_select_check();
 	return stdout_write_dump();
 }
 
--- a/zdump/zgetdump.h
+++ b/zdump/zgetdump.h
@@ -30,10 +30,13 @@ struct options {
 	char		*device;
 	char		*mount_point;
 	int		fmt_specified;
-	const char 	*fmt;
+	const char	*fmt;
 	int		debug_specified;
 	char		**argv_fuse;
 	int		argc_fuse;
+	const char	*select;
+	int		select_specified;
+	int		kdump_swap;
 };
 
 /*
@@ -74,6 +77,7 @@ 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;
 
 /*
  * Supported DFO dump formats
