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,25 @@
|
||||
include ../common.mak
|
||||
|
||||
CPIOINIT = $(call echocmd," CPIOINI ",/$@)./cpioinit
|
||||
|
||||
all: $(ZFCPDUMP_PART_RD)
|
||||
|
||||
cpioinit: cpioinit.c
|
||||
$(HOSTCC) $(HOSTCFLAGS) -o $@ $^
|
||||
|
||||
zfcpdump_part: zfcpdump.o zfcpdump_part.o
|
||||
$(LINK) $(ALL_LDFLAGS) $^ -static -o $@
|
||||
$(STRIP) -s $@
|
||||
|
||||
$(ZFCPDUMP_PART_RD): cpioinit zfcpdump_part
|
||||
$(CPIOINIT) zfcpdump_part > $@.tmp
|
||||
$(GZIP) -f $@.tmp
|
||||
$(MV) $@.tmp.gz $(ZFCPDUMP_PART_RD)
|
||||
|
||||
install: all
|
||||
$(INSTALL) -m 611 $(ZFCPDUMP_PART_RD) $(DESTDIR)$(ZFCPDUMP_DIR)
|
||||
|
||||
clean:
|
||||
rm -f *.o *.gz *.tmp *~ zfcpdump_part cpioinit $(ZFCPDUMP_PART_RD)
|
||||
|
||||
.PHONY: all clean install
|
||||
@@ -0,0 +1,54 @@
|
||||
zfcpdump: S390 SCSI dump tool (Version 3)
|
||||
=========================================
|
||||
zfcpdump is used for creating System dumps for Linux on System z. It has two
|
||||
parts: a zfcpdump enabled Linux kernel and a user space application.
|
||||
|
||||
This is the 3rd version of zfcpdump which uses the upstream kernel
|
||||
version 3.12 or above. This version writes the dump to a partition in
|
||||
contrast to the previous versions where the dump was written to
|
||||
a file system.
|
||||
|
||||
The user space application of zfcpdump can reside either in an intitramfs or an
|
||||
initrd. It reads from /proc/vmcore, provided by the kernel part, and writes the
|
||||
system dump to a SCSI disk partition.
|
||||
|
||||
To build a zfcpdump enabled kernel use the following settings in your kernel
|
||||
configuration:
|
||||
|
||||
* CONFIG_ZFCPDUMP=y
|
||||
* CONFIG_BLK_DEV_INITRD=y
|
||||
* CONFIG_EFI_PARTITION=y for using GPT disk layout
|
||||
* CONFIG_MSDOS_PARTITION=y for using MSDOS disk layout
|
||||
* BLK_DEV_SD=y
|
||||
* Enable ZFCP driver
|
||||
* Enable SCSI driver
|
||||
* Disable as many features as possible to keep the kernel small.
|
||||
E.g. network and file system support is not needed at all.
|
||||
|
||||
You can use "make zfcpdump_defconfig" as a starting point for your kernel
|
||||
configuration.
|
||||
|
||||
* Issue "make bzImage" to build the zfcpdump kernel image.
|
||||
|
||||
In a Linux distribution the zfcpdump enabled kernel image must be copied to
|
||||
/lib/s390-tools/zfcpdump/zfcpdump_part.image, where the s390 zipl tool is
|
||||
looking for the dump kernel when preparing a SCSI dump disk.
|
||||
|
||||
Create and install initrd
|
||||
=========================
|
||||
|
||||
* make
|
||||
|
||||
Builds "cpioinit" and statically linked "zfcpdump" application. Then cpioinit
|
||||
is used to integrate the zfcpdump application into the cpio archive
|
||||
zfcpdump_part.rd.
|
||||
|
||||
* make install
|
||||
|
||||
The initrd zfcpdump_part.rd is installed to "/lib/s390-tools/zfcpdump/".
|
||||
|
||||
Additional information
|
||||
======================
|
||||
For more information on how to use zfcpdump and zipl refer to the s390
|
||||
'Using the Dump Tools' book, which is available from:
|
||||
http://www.ibm.com/developerworks/linux/linux390.
|
||||
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* cpioinit - Tool to create cpio archives
|
||||
*
|
||||
* This tool can be used to create a cpio initrd for the linux kernel.
|
||||
*
|
||||
* The executable init program needs to be specified as parameter.
|
||||
* The cpio archive is printed to stdout.
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
* Cpio archive new header structure
|
||||
*/
|
||||
struct cpio_newc_header {
|
||||
char c_magic[6];
|
||||
char c_ino[8];
|
||||
char c_mode[8];
|
||||
char c_uid[8];
|
||||
char c_gid[8];
|
||||
char c_nlink[8];
|
||||
char c_mtime[8];
|
||||
char c_filesize[8];
|
||||
char c_devmajor[8];
|
||||
char c_devminor[8];
|
||||
char c_rdevmajor[8];
|
||||
char c_rdevminor[8];
|
||||
char c_namesize[8];
|
||||
char c_check[8];
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure to specify a cpio archive entry
|
||||
*/
|
||||
struct cpio_line {
|
||||
struct cpio_newc_header head;
|
||||
const char *filename;
|
||||
char *data;
|
||||
unsigned int datasize;
|
||||
};
|
||||
|
||||
/*
|
||||
* Fill line with zeros to be aligned to 4 bytes
|
||||
*/
|
||||
static int cpio_fill_aligned(int size)
|
||||
{
|
||||
while (size % 4) {
|
||||
printf("%c", 0);
|
||||
size++;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print a cpio_line structure to stdout
|
||||
*/
|
||||
static void cpio_print_ln(struct cpio_line *line)
|
||||
{
|
||||
unsigned int i, lnsize = 0;
|
||||
|
||||
for (i = 0; i < sizeof(struct cpio_newc_header); i++)
|
||||
printf("%c", ((char *)&line->head)[i]);
|
||||
lnsize += sizeof(struct cpio_newc_header);
|
||||
|
||||
if (line->filename != NULL) {
|
||||
printf("%s", line->filename);
|
||||
printf("%c", 0); /* print ending zero */
|
||||
lnsize += strlen(line->filename) + 1;
|
||||
lnsize = cpio_fill_aligned(lnsize);
|
||||
}
|
||||
|
||||
if (line->data != NULL) {
|
||||
for (i = 0; i < line->datasize; i++)
|
||||
printf("%c", line->data[i]);
|
||||
lnsize += line->datasize;
|
||||
lnsize = cpio_fill_aligned(lnsize);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Write number in cpio new ascii format into header field with
|
||||
* size of 6 chars
|
||||
*/
|
||||
static void cpio_insert_num6(char *head, int num)
|
||||
{
|
||||
char buf[7];
|
||||
|
||||
/* Need memcpy because snprintf addes zero at the end */
|
||||
snprintf(buf, sizeof(buf), "%06x", num);
|
||||
memcpy(head, buf, 6);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write number in cpio new ascii format into header field with
|
||||
* size of 8 chars
|
||||
*/
|
||||
static void cpio_insert_num8(char *head, int num)
|
||||
{
|
||||
char buf[9];
|
||||
|
||||
/* Need memcpy because snprintf addes zero at the end */
|
||||
snprintf(buf, sizeof(buf), "%08x", num);
|
||||
memcpy(head, buf, 8);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write basics to cpio_newc_header managing the entry number,
|
||||
* thus each structure write has to call this function once
|
||||
*/
|
||||
static void cpio_header_init(struct cpio_newc_header *head)
|
||||
{
|
||||
static int number;
|
||||
|
||||
cpio_insert_num6(head->c_magic, 0x070701);
|
||||
cpio_insert_num8(head->c_ino, 0x2d1 + number);
|
||||
cpio_insert_num8(head->c_uid, 0);
|
||||
cpio_insert_num8(head->c_gid, 0);
|
||||
cpio_insert_num8(head->c_nlink, number);
|
||||
cpio_insert_num8(head->c_mtime, 0);
|
||||
cpio_insert_num8(head->c_check, 0);
|
||||
cpio_insert_num8(head->c_devmajor, 3);
|
||||
cpio_insert_num8(head->c_devminor, 1);
|
||||
number++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes directory in cpio archive structure to stdout
|
||||
*/
|
||||
static void cpio_write_dir(const char *dir)
|
||||
{
|
||||
struct cpio_line line;
|
||||
|
||||
cpio_header_init(&line.head);
|
||||
cpio_insert_num8(line.head.c_mode, 0x41ed);
|
||||
cpio_insert_num8(line.head.c_filesize, 0);
|
||||
cpio_insert_num8(line.head.c_rdevmajor, 0);
|
||||
cpio_insert_num8(line.head.c_rdevminor, 0);
|
||||
cpio_insert_num8(line.head.c_namesize, strlen(dir) + 1);
|
||||
line.filename = dir;
|
||||
line.data = NULL;
|
||||
cpio_print_ln(&line);
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes end of cpio archive to stdout
|
||||
*/
|
||||
static void cpio_write_tail()
|
||||
{
|
||||
struct cpio_line line;
|
||||
int i;
|
||||
|
||||
cpio_header_init(&line.head);
|
||||
cpio_insert_num8(line.head.c_ino, 0);
|
||||
cpio_insert_num8(line.head.c_mode, 0);
|
||||
cpio_insert_num8(line.head.c_filesize, 0);
|
||||
cpio_insert_num8(line.head.c_devmajor, 0);
|
||||
cpio_insert_num8(line.head.c_devminor, 0);
|
||||
cpio_insert_num8(line.head.c_rdevmajor, 0);
|
||||
cpio_insert_num8(line.head.c_rdevminor, 0);
|
||||
cpio_insert_num8(line.head.c_namesize, 11);
|
||||
line.filename = "TRAILER!!!";
|
||||
line.data = NULL;
|
||||
cpio_print_ln(&line);
|
||||
for (i = 0; i < 30; i++)
|
||||
printf("%c", 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes device node in cpio archive structure to stdout
|
||||
*/
|
||||
static void cpio_write_nod(char *name, int major, int minor, int chardev)
|
||||
{
|
||||
struct cpio_line line;
|
||||
|
||||
cpio_header_init(&line.head);
|
||||
if (chardev == 1)
|
||||
cpio_insert_num8(line.head.c_mode, 0x21a4);
|
||||
else
|
||||
cpio_insert_num8(line.head.c_mode, 0x61a4);
|
||||
cpio_insert_num8(line.head.c_filesize, 0);
|
||||
cpio_insert_num8(line.head.c_rdevmajor, major);
|
||||
cpio_insert_num8(line.head.c_rdevminor, minor);
|
||||
cpio_insert_num8(line.head.c_namesize, strlen(name) + 1);
|
||||
line.filename = name;
|
||||
line.data = NULL;
|
||||
cpio_print_ln(&line);
|
||||
}
|
||||
|
||||
/*
|
||||
* Writes file in cpio archiv structure to stdout
|
||||
*/
|
||||
static void cpio_write_file(char *name, char *filedata, int filesize)
|
||||
{
|
||||
struct cpio_line line;
|
||||
|
||||
cpio_header_init(&line.head);
|
||||
cpio_insert_num8(line.head.c_mode, 0x81ed);
|
||||
cpio_insert_num8(line.head.c_filesize, filesize);
|
||||
cpio_insert_num8(line.head.c_rdevmajor, 0);
|
||||
cpio_insert_num8(line.head.c_rdevminor, 0);
|
||||
cpio_insert_num8(line.head.c_namesize, strlen(name) + 1);
|
||||
line.filename = name;
|
||||
line.data = filedata;
|
||||
line.datasize = filesize;
|
||||
cpio_print_ln(&line);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read size bytes from file into buffer
|
||||
*/
|
||||
static int read_file(const char *file, char *buf, int size)
|
||||
{
|
||||
int fh;
|
||||
|
||||
fh = open(file, O_RDONLY);
|
||||
if (fh == -1) {
|
||||
fprintf(stderr, "Open %s failed (%s)\n", file, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
if (read(fh, buf, size) < 0) {
|
||||
fprintf(stderr, "Read %s failed (%s)\n", file, strerror(errno));
|
||||
close(fh);
|
||||
return -1;
|
||||
}
|
||||
close(fh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Program writes a cpio archive to stdout.
|
||||
* - containing device nodes needed to run scsi dumper
|
||||
* - expecting init program as parameter
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char path[10], *filebuffer, *filename;
|
||||
struct stat st;
|
||||
int i;
|
||||
|
||||
/* First read in init file */
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "Usage: cpioinit <init program>\n");
|
||||
return -1;
|
||||
}
|
||||
filename = argv[1];
|
||||
if (stat(filename, &st)) {
|
||||
fprintf(stderr, "Cannot open file: %s (%s)\n", filename,
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
filebuffer = malloc(st.st_size);
|
||||
if (filebuffer == NULL) {
|
||||
fprintf(stderr, "Malloc %lu bytes failed\n", st.st_size);
|
||||
return -1;
|
||||
}
|
||||
if (read_file(filename, filebuffer, st.st_size))
|
||||
return -1;
|
||||
|
||||
/* Write init */
|
||||
cpio_write_file("init", filebuffer, st.st_size);
|
||||
free(filebuffer);
|
||||
|
||||
/* Define device nodes */
|
||||
cpio_write_dir("dev");
|
||||
cpio_write_dir("proc");
|
||||
cpio_write_dir("sys");
|
||||
cpio_write_dir("mnt");
|
||||
cpio_write_nod("dev/console", 5, 1, 1);
|
||||
cpio_write_nod("dev/null", 1, 3, 1);
|
||||
|
||||
/* Write sda for access via part offset */
|
||||
cpio_write_nod("dev/sda", 8, 0, 0);
|
||||
|
||||
/* Write sda 1 - 15 nodes */
|
||||
for (i = 1; i < 15; i++) {
|
||||
snprintf(path, sizeof(path), "dev/sda%d", i);
|
||||
cpio_write_nod(path, 8, i, 0);
|
||||
}
|
||||
cpio_write_tail();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
* zfcpdump - Write /proc/vmcore to SCSI partition
|
||||
*
|
||||
* This tool should be used in an intitramfs together with a kernel with
|
||||
* enabled CONFIG_ZFCPDUMP kernel build option. The tool is able to write
|
||||
* standalone system dumps on SCSI disks.
|
||||
*
|
||||
* See Documentation/s390/zfcpdump.txt for more information!
|
||||
*
|
||||
* Copyright IBM Corp. 2003, 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 <asm/types.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/reboot.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#ifdef GZIP_SUPPORT
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
#include "lib/zt_common.h"
|
||||
#include "zfcpdump.h"
|
||||
|
||||
struct globals g;
|
||||
|
||||
/*
|
||||
* Print a newline
|
||||
*
|
||||
* The leading blank is needed to force the SCLP console printing the newline
|
||||
*/
|
||||
void print_newline(void)
|
||||
{
|
||||
PRINT(" \n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize start time and return start time string
|
||||
*/
|
||||
static void start_time_init(char *str, unsigned int len)
|
||||
{
|
||||
struct tm *tm;
|
||||
time_t ti;
|
||||
|
||||
gettimeofday(&g.start_time, NULL);
|
||||
time(&ti);
|
||||
tm = localtime(&ti);
|
||||
strftime(str, len, "%a, %d %b %Y %H:%M:%S %z", tm);
|
||||
}
|
||||
|
||||
/*
|
||||
* parse one kernel parameter in the form keyword=value
|
||||
*/
|
||||
static int parse_parameter(char *parameter)
|
||||
{
|
||||
char *token;
|
||||
|
||||
token = strtok(parameter, "=");
|
||||
if (token == NULL)
|
||||
return 0;
|
||||
|
||||
if (strcmp(token, PARM_DEBUG) == 0) {
|
||||
/* Dump Debug */
|
||||
char *s = strtok(NULL, "=");
|
||||
if (s == NULL) {
|
||||
PRINT_WARN("No value for '%s' parameter "
|
||||
"specified\n", PARM_DEBUG);
|
||||
PRINT_WARN("Using default: %d\n", PARM_DEBUG_DFLT);
|
||||
} else {
|
||||
g.parm_debug = atoi(s);
|
||||
if ((g.parm_debug < PARM_DEBUG_MIN) ||
|
||||
(g.parm_debug > PARM_DEBUG_MAX)) {
|
||||
PRINT_WARN("Invalid value (%i) for %s "
|
||||
"parameter specified (allowed range is "
|
||||
"%i - %i)\n", g.parm_debug, PARM_DEBUG,
|
||||
PARM_DEBUG_MIN, PARM_DEBUG_MAX);
|
||||
PRINT_WARN("Using default: %i\n",
|
||||
PARM_DEBUG_DFLT);
|
||||
g.parm_debug = PARM_DEBUG_DFLT;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get dump parameters from /proc/cmdline
|
||||
* Return: 0 - ok
|
||||
* (!= 0) - error
|
||||
*/
|
||||
static int parse_parmline(void)
|
||||
{
|
||||
int fh, i, count, token_cnt;
|
||||
char *parms[KERN_PARM_MAX];
|
||||
char *token;
|
||||
|
||||
/* setting defaults */
|
||||
g.parm_debug = PARM_DEBUG_DFLT;
|
||||
|
||||
fh = open(PROC_CMDLINE, O_RDONLY);
|
||||
if (fh == -1) {
|
||||
PRINT_PERR("open %s failed\n", PROC_CMDLINE);
|
||||
return -1;
|
||||
}
|
||||
count = read(fh, g.parmline, CMDLINE_MAX_LEN);
|
||||
if (count == -1) {
|
||||
PRINT_PERR("read %s failed\n", PROC_CMDLINE);
|
||||
close(fh);
|
||||
return -1;
|
||||
}
|
||||
g.parmline[count-1] = '\0'; /* remove \n */
|
||||
token_cnt = 0;
|
||||
token = strtok(g.parmline, " \t\n");
|
||||
while (token != NULL) {
|
||||
parms[token_cnt] = token;
|
||||
token = strtok(NULL, " \t\n");
|
||||
token_cnt++;
|
||||
if (token_cnt >= KERN_PARM_MAX) {
|
||||
PRINT_WARN("More than %i kernel parmameters "
|
||||
"specified\n", KERN_PARM_MAX);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < token_cnt; i++) {
|
||||
if (parse_parameter(parms[i])) {
|
||||
close(fh);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
PRINT_TRACE("dump debug: %d\n", g.parm_debug);
|
||||
close(fh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_to_file(const char *file, const char *command)
|
||||
{
|
||||
int fh;
|
||||
|
||||
PRINT_TRACE("Write: %s - %s\n", file, command);
|
||||
fh = open(file, O_WRONLY);
|
||||
if (fh == -1) {
|
||||
PRINT_PERR("Could not open %s\n", file);
|
||||
return -1;
|
||||
}
|
||||
if (write(fh, command, strlen(command)) == -1) {
|
||||
PRINT_PERR("Write to %s failed\n", file);
|
||||
close(fh);
|
||||
return -1;
|
||||
};
|
||||
close(fh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_file(const char *file, char *buf, int size)
|
||||
{
|
||||
ssize_t count;
|
||||
int fh;
|
||||
|
||||
PRINT_TRACE("Read: %s:\n", file);
|
||||
fh = open(file, O_RDONLY);
|
||||
if (fh == -1) {
|
||||
PRINT_PERR("open %s failed\n", file);
|
||||
return -1;
|
||||
}
|
||||
count = read(fh, buf, size - 1);
|
||||
if (count < 0) {
|
||||
PRINT_PERR("read %s failed\n", file);
|
||||
close(fh);
|
||||
return -1;
|
||||
}
|
||||
buf[count] = 0;
|
||||
if (buf[strlen(buf) - 1] == '\n')
|
||||
buf[strlen(buf) - 1] = 0; /* strip newline */
|
||||
close(fh);
|
||||
PRINT_TRACE("'%s'\n", buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get HSA size
|
||||
*/
|
||||
__u64 get_hsa_size(void)
|
||||
{
|
||||
char buf[128];
|
||||
|
||||
if (read_file(DEV_ZCORE_HSA, buf, sizeof(buf)))
|
||||
return 0;
|
||||
return strtoul(buf, NULL, 16);
|
||||
}
|
||||
|
||||
/*
|
||||
* Release HSA
|
||||
*/
|
||||
void release_hsa(void)
|
||||
{
|
||||
write_to_file(DEV_ZCORE_HSA, "0");
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable the scsi disk for dumping
|
||||
* Return: 0 - ok
|
||||
* != 0 - error
|
||||
*/
|
||||
static int enable_zfcp_device(void)
|
||||
{
|
||||
char command[1024], file[1024];
|
||||
struct stat s;
|
||||
|
||||
/* Prevent setting all LUNs online for NPIV */
|
||||
if (stat("/sys/module/zfcp/parameters/allow_lun_scan", &s) == 0)
|
||||
write_to_file("/sys/module/zfcp/parameters/allow_lun_scan",
|
||||
"0\n");
|
||||
/* device */
|
||||
if (read_file(IPL_DEVNO, g.dump_devno, sizeof(g.dump_devno)))
|
||||
return -1;
|
||||
sprintf(file, "/sys/bus/ccw/drivers/zfcp/%s/online", g.dump_devno);
|
||||
if (write_to_file(file, "1\n"))
|
||||
return -1;
|
||||
|
||||
/* wwpn */
|
||||
if (read_file(IPL_WWPN, g.dump_wwpn, sizeof(g.dump_wwpn)))
|
||||
return -1;
|
||||
sprintf(file, "/sys/bus/ccw/drivers/zfcp/%s/port_add", g.dump_devno);
|
||||
/* The port_add attribute has been removed in recent kernels */
|
||||
if (stat(file, &s) == 0) {
|
||||
sprintf(command, "%s\n", g.dump_wwpn);
|
||||
if (write_to_file(file, command))
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* lun */
|
||||
if (read_file(IPL_LUN, g.dump_lun, sizeof(g.dump_lun)))
|
||||
return -1;
|
||||
sprintf(file, "/sys/bus/ccw/drivers/zfcp/%s/%s/unit_add", g.dump_devno,
|
||||
g.dump_wwpn);
|
||||
sprintf(command, "%s\n", g.dump_lun);
|
||||
if (write_to_file(file, command))
|
||||
return -1;
|
||||
|
||||
/* bootprog */
|
||||
read_file("/sys/firmware/ipl/bootprog", g.dump_bootprog,
|
||||
sizeof(g.dump_bootprog));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Terminate the system dumper
|
||||
*/
|
||||
int terminate(int rc)
|
||||
{
|
||||
int fd;
|
||||
|
||||
print_newline();
|
||||
if (rc)
|
||||
PRINT("Dump failed\n");
|
||||
else
|
||||
PRINT("Dump successful\n");
|
||||
fflush(stdout);
|
||||
|
||||
sleep(WAIT_TIME_END); /* give the messages time to be displayed */
|
||||
fd = open(DEV_ZCORE_REIPL, O_WRONLY, 0);
|
||||
if (fd == -1)
|
||||
goto no_reipl;
|
||||
if (write(fd, REIPL, 1) == -1)
|
||||
PRINT_PERR("Write to %s failed\n", DEV_ZCORE_REIPL);
|
||||
close(fd);
|
||||
no_reipl:
|
||||
reboot(LINUX_REBOOT_CMD_POWER_OFF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Signal handler for zfcp_dumper
|
||||
*/
|
||||
static __sighandler_t dump_sig_handler(int sig, siginfo_t *UNUSED(sip),
|
||||
void *UNUSED(p))
|
||||
{
|
||||
PRINT_ERR("Got signal: %i\n", sig);
|
||||
terminate(1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup the Signal handler for zfcp_dumper
|
||||
* Return: 0 - ok
|
||||
* !=0 - error
|
||||
*/
|
||||
static int init_sig(void)
|
||||
{
|
||||
g.sigact.sa_flags = (SA_NODEFER | SA_SIGINFO | SA_RESETHAND);
|
||||
g.sigact.sa_handler = (__sighandler_t)dump_sig_handler;
|
||||
if (sigemptyset(&g.sigact.sa_mask) < 0)
|
||||
return -1;
|
||||
if (sigaction(SIGINT, &g.sigact, NULL) < 0)
|
||||
return -1;
|
||||
if (sigaction(SIGTERM, &g.sigact, NULL) < 0)
|
||||
return -1;
|
||||
if (sigaction(SIGPIPE, &g.sigact, NULL) < 0)
|
||||
return -1;
|
||||
if (sigaction(SIGABRT, &g.sigact, NULL) < 0)
|
||||
return -1;
|
||||
if (sigaction(SIGSEGV, &g.sigact, NULL) < 0)
|
||||
return -1;
|
||||
if (sigaction(SIGBUS, &g.sigact, NULL) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write progress information to screen
|
||||
* Parameter: done - So many bytes have been written since last call
|
||||
*/
|
||||
void show_progress(unsigned long done)
|
||||
{
|
||||
unsigned long byte_per_sec, eta_sec, eta_min, eta_hrs;
|
||||
struct timeval tv, tv_sub;
|
||||
char eta_str[128];
|
||||
static unsigned long vmcore_done;
|
||||
static time_t time_next;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
vmcore_done += done;
|
||||
if ((tv.tv_sec < time_next) && (vmcore_done < g.vmcore_size))
|
||||
return;
|
||||
timersub(&tv, &g.start_time, &tv_sub);
|
||||
byte_per_sec = tv_sub.tv_sec ? (vmcore_done / tv_sub.tv_sec) : 0;
|
||||
if (byte_per_sec) {
|
||||
eta_sec = (g.vmcore_size - vmcore_done) / byte_per_sec;
|
||||
eta_hrs = eta_sec / 3600;
|
||||
eta_sec -= eta_hrs * 3600;
|
||||
eta_min = eta_sec / 60;
|
||||
eta_sec -= eta_min * 60;
|
||||
snprintf(eta_str, sizeof(eta_str), "%lu:%02lu:%02lu",
|
||||
eta_hrs, eta_min, eta_sec);
|
||||
} else {
|
||||
strcpy(eta_str, "unknown");
|
||||
}
|
||||
PRINT(" %6llu of %llu MB %5.1f%% %4llu MB/s %-5s ETA\n",
|
||||
TO_MIB(vmcore_done), TO_MIB(g.vmcore_size),
|
||||
((double) vmcore_done / (double) g.vmcore_size) * 100.0,
|
||||
TO_MIB(byte_per_sec), eta_str);
|
||||
time_next = tv.tv_sec + 10;
|
||||
}
|
||||
|
||||
/*
|
||||
* Load a kernel module
|
||||
*/
|
||||
static void modprobe(const char *module)
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
PRINT_PERR("fork failed\n");
|
||||
return;
|
||||
} else if (pid == 0) {
|
||||
execl("/bin/modprobe", "modprobe", module, "-q", NULL);
|
||||
execl("/sbin/modprobe", "modprobe", module, "-q", NULL);
|
||||
exit(1);
|
||||
} else {
|
||||
waitpid(pid, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Load all required kernel modules
|
||||
*/
|
||||
static void load_modules(const char *module_list[])
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; module_list[i]; i++)
|
||||
modprobe(module_list[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize zfcpdump
|
||||
*/
|
||||
int zfcpdump_init(const char *module_list[])
|
||||
{
|
||||
char start_time_str[128], linux_version[256];
|
||||
|
||||
#ifdef __s390x__
|
||||
PRINT("Linux System Dumper starting\n");
|
||||
PRINT("\n");
|
||||
PRINT("Version %s (64 bit)\n", ZFCPDUMP_VERSION);
|
||||
#else
|
||||
PRINT("Linux System Dumper starting\n");
|
||||
PRINT("\n");
|
||||
PRINT("Version %s (32 bit)\n", ZFCPDUMP_VERSION);
|
||||
#endif
|
||||
if (init_sig()) {
|
||||
PRINT_ERR("Init Signals failed!\n");
|
||||
return -1;
|
||||
}
|
||||
if (mount("proc", "/proc", "proc", 0, NULL)) {
|
||||
if (errno != EBUSY) {
|
||||
PRINT_PERR("Unable to mount proc\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
read_file("/proc/version", linux_version, sizeof(linux_version));
|
||||
PRINT("%s\n", linux_version);
|
||||
print_newline();
|
||||
|
||||
if (mount("sysfs", "/sys", "sysfs", 0, NULL)) {
|
||||
if (errno != EBUSY) {
|
||||
PRINT_PERR("Unable to mount sysfs\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (mount("debugfs", "/sys/kernel/debug", "debugfs", 0, NULL)) {
|
||||
if (errno != EBUSY) {
|
||||
PRINT_PERR("Unable to mount debugfs\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (parse_parmline()) {
|
||||
PRINT_ERR("Could not parse parmline\n");
|
||||
return -1;
|
||||
}
|
||||
load_modules(module_list);
|
||||
if (enable_zfcp_device()) {
|
||||
PRINT_ERR("Could not enable dump device\n");
|
||||
return -1;
|
||||
}
|
||||
start_time_init(start_time_str, sizeof(start_time_str));
|
||||
PRINT("Dump start at:\n");
|
||||
PRINT(" %s\n", start_time_str);
|
||||
print_newline();
|
||||
sleep(WAIT_TIME_ONLINE);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* zfcpdump - Write /proc/vmcore to SCSI partition
|
||||
*
|
||||
* Copyright IBM Corp. 2003, 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 ZFCPDUMP_H
|
||||
#define ZFCPDUMP_H
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define ZFCPDUMP_VERSION "3.0"
|
||||
|
||||
#define PRINT_TRACE(x...) \
|
||||
do { \
|
||||
if (g.parm_debug >= 3) { \
|
||||
fprintf(stderr, "TRACE: "); \
|
||||
fprintf(stderr, ##x); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PRINT_ERR(x...) \
|
||||
do { \
|
||||
fprintf(stderr, "ERROR: "); \
|
||||
fprintf(stderr, ##x); \
|
||||
} while (0)
|
||||
|
||||
#define PRINT_WARN(x...) \
|
||||
do { \
|
||||
fprintf(stderr, "WARNING: "); \
|
||||
fprintf(stderr, ##x); \
|
||||
} while (0)
|
||||
|
||||
#define PRINT_PERR(x...) \
|
||||
do { \
|
||||
fprintf(stderr, "ERROR: "); \
|
||||
fprintf(stderr, ##x); \
|
||||
perror(""); \
|
||||
} while (0)
|
||||
|
||||
#define PRINT(x...) fprintf(stdout, ##x)
|
||||
#define CMDLINE_MAX_LEN 1024
|
||||
#define KERN_PARM_MAX 100
|
||||
|
||||
#define DUMP_FLAGS (O_CREAT | O_RDWR | O_TRUNC | O_DIRECT)
|
||||
#define DUMP_MODE (S_IRUSR | S_IWUSR | S_IRGRP)
|
||||
|
||||
#define MIB (1024ULL * 1024)
|
||||
#define TO_MIB(x) ((x + (MIB / 2)) / MIB)
|
||||
|
||||
struct globals {
|
||||
int parm_debug;
|
||||
char parmline[CMDLINE_MAX_LEN];
|
||||
struct sigaction sigact;
|
||||
char dump_devno[16];
|
||||
char dump_wwpn[32];
|
||||
char dump_lun[32];
|
||||
char dump_bootprog[32];
|
||||
char start_time_str[128];
|
||||
struct timeval start_time;
|
||||
unsigned long vmcore_size;
|
||||
};
|
||||
|
||||
extern struct globals g;
|
||||
|
||||
#define PROC_CMDLINE "/proc/cmdline"
|
||||
#define DEV_ZCORE "/sys/kernel/debug/zcore/mem"
|
||||
#define DEV_ZCORE_MAP "/sys/kernel/debug/zcore/memmap"
|
||||
#define DEV_ZCORE_REIPL "/sys/kernel/debug/zcore/reipl"
|
||||
#define DEV_ZCORE_HSA "/sys/kernel/debug/zcore/hsa"
|
||||
#define REIPL "1"
|
||||
#define DEV_SCSI "/dev/sda"
|
||||
|
||||
#define IPL_WWPN "/sys/firmware/ipl/wwpn"
|
||||
#define IPL_DEVNO "/sys/firmware/ipl/device"
|
||||
#define IPL_LUN "/sys/firmware/ipl/lun"
|
||||
|
||||
#define PARM_DEBUG "dump_debug"
|
||||
#define PARM_DEBUG_DFLT 2
|
||||
#define PARM_DEBUG_MIN 1
|
||||
#define PARM_DEBUG_MAX 6
|
||||
|
||||
#define WAIT_TIME_END 3 /* seconds */
|
||||
#define WAIT_TIME_ONLINE 2 /* seconds */
|
||||
|
||||
#define PAGE_SIZE 4096
|
||||
|
||||
#define CHUNK_INFO_SIZE 34 /* 2 16-byte char, each followed by blank */
|
||||
|
||||
struct mem_chunk {
|
||||
__u64 addr; /* Start address of this memory chunk */
|
||||
__u64 size; /* Length of this memory chunk */
|
||||
struct mem_chunk *next; /* Pointer to next memory chunk */
|
||||
};
|
||||
|
||||
/*
|
||||
* Function prototypes
|
||||
*/
|
||||
void release_hsa(void);
|
||||
__u64 get_hsa_size(void);
|
||||
int zfcpdump_init(const char *module_list[]);
|
||||
void print_newline(void);
|
||||
void show_progress(unsigned long done);
|
||||
int terminate(int rc);
|
||||
|
||||
#endif /* ZFCPDUMP_H */
|
||||
@@ -0,0 +1,31 @@
|
||||
#
|
||||
# initramfs definition for zfcpdump
|
||||
# See also Documentation/s390/zfcpdump.txt
|
||||
#
|
||||
|
||||
dir /dev 755 0 0
|
||||
nod /dev/console 644 0 0 c 5 1
|
||||
nod /dev/null 644 0 0 c 1 3
|
||||
nod /dev/sda1 644 0 0 b 8 1
|
||||
nod /dev/sda2 644 0 0 b 8 2
|
||||
nod /dev/sda3 644 0 0 b 8 3
|
||||
nod /dev/sda4 644 0 0 b 8 4
|
||||
nod /dev/sda5 644 0 0 b 8 5
|
||||
nod /dev/sda6 644 0 0 b 8 6
|
||||
nod /dev/sda7 644 0 0 b 8 7
|
||||
nod /dev/sda8 644 0 0 b 8 8
|
||||
nod /dev/sda9 644 0 0 b 8 9
|
||||
nod /dev/sda10 644 0 0 b 8 10
|
||||
nod /dev/sda11 644 0 0 b 8 11
|
||||
nod /dev/sda12 644 0 0 b 8 12
|
||||
nod /dev/sda13 644 0 0 b 8 13
|
||||
nod /dev/sda14 644 0 0 b 8 14
|
||||
nod /dev/sda15 644 0 0 b 8 15
|
||||
dir /proc 755 0 0
|
||||
dir /sys 755 0 0
|
||||
dir /mnt 755 0 0
|
||||
dir /sbin 755 0 0
|
||||
dir /etc 755 0 0
|
||||
file /init arch/s390/boot/zfcpdump 755 0 0
|
||||
file /sbin/e2fsck arch/s390/boot/e2fsck 755 0 0
|
||||
slink /etc/mtab /proc/mounts 755 0 0
|
||||
@@ -0,0 +1,457 @@
|
||||
/*
|
||||
* zfcpdump - Write /proc/vmcore to SCSI partition
|
||||
*
|
||||
* This tool should be used in an intitramfs together with a kernel with
|
||||
* enabled CONFIG_ZFCPDUMP kernel build option. The tool is able to write
|
||||
* standalone system dumps on SCSI disks.
|
||||
*
|
||||
* See Documentation/s390/zfcpdump.txt for more information!
|
||||
*
|
||||
* Copyright IBM Corp. 2003, 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 <asm/types.h>
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#include <elf.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/reboot.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "lib/util_base.h"
|
||||
#include "lib/zt_common.h"
|
||||
|
||||
#include "zfcpdump.h"
|
||||
|
||||
#define COPY_BUF_SIZE 0x10000UL
|
||||
#define COPY_TABLE_ENTRY_COUNT 4
|
||||
|
||||
/*
|
||||
* Copy table entry
|
||||
*/
|
||||
struct copy_table_entry {
|
||||
unsigned long size;
|
||||
unsigned long off;
|
||||
};
|
||||
|
||||
/*
|
||||
* 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));
|
||||
|
||||
/*
|
||||
* Layout of SCSI disk block pointer
|
||||
*/
|
||||
struct linear_blockptr {
|
||||
uint64_t blockno;
|
||||
uint16_t size;
|
||||
uint16_t blockct;
|
||||
uint8_t reserved[4];
|
||||
} __attribute((packed));
|
||||
|
||||
/* From boot.h in zipl */
|
||||
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));
|
||||
|
||||
/* From install.c in zipl */
|
||||
struct scsi_mbr {
|
||||
uint8_t magic[4];
|
||||
uint32_t version_id;
|
||||
uint8_t reserved[8];
|
||||
struct linear_blockptr lin;
|
||||
uint8_t reserverd[0x50];
|
||||
struct boot_info boot_info;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* From boot.h in zipl */
|
||||
#define BOOT_INFO_VERSION 1
|
||||
#define BOOT_INFO_MAGIC "zIPL"
|
||||
#define BOOT_INFO_DEV_TYPE_SCSI 0x02
|
||||
#define BOOT_INFO_BP_TYPE_DUMP 0x01
|
||||
|
||||
/*
|
||||
* Globals
|
||||
*/
|
||||
static const char *module_list[] = {"zfcp", "sd_mod", "zcore_mod", NULL};
|
||||
static struct scsi_dump_sb dump_sb;
|
||||
static struct scsi_mbr mbr;
|
||||
|
||||
/*
|
||||
* Read file at given offset
|
||||
*/
|
||||
static int pread_file(const char *path, char *buf, int size, uint64_t off)
|
||||
{
|
||||
int fd;
|
||||
|
||||
PRINT_TRACE("Read: %s:\n", path);
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
PRINT_PERR("open %s failed\n", path);
|
||||
return -1;
|
||||
}
|
||||
if (lseek(fd, off, SEEK_SET) < 0) {
|
||||
PRINT_PERR("seek %s offset %llu failed\n", path,
|
||||
(unsigned long long) off);
|
||||
return -1;
|
||||
}
|
||||
if (read(fd, buf, size) < 0) {
|
||||
PRINT_PERR("read %s failed\n", path);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create checksum for buffer
|
||||
*/
|
||||
static inline uint32_t csum_partial(const void *buf, int len, uint32_t 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create checksum on SCSI device
|
||||
*/
|
||||
static int csum_get(uint64_t off, uint64_t len, uint64_t *result)
|
||||
{
|
||||
char buf[len];
|
||||
|
||||
if (pread_file(DEV_SCSI, (char *)&buf, sizeof(buf), off) < 0) {
|
||||
PRINT_ERR("Error reading checksum from disk\n");
|
||||
return -1;
|
||||
}
|
||||
*result = (uint64_t)csum_partial(&buf, len, 0x12345678);
|
||||
PRINT_TRACE("Got crc %llx\n", (unsigned long long) *result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update superblock checksum on SCSI disk
|
||||
*/
|
||||
static int csum_update(int fd)
|
||||
{
|
||||
/* Write crc into zfcpdump header */
|
||||
if (csum_get(dump_sb.part_start + dump_sb.csum_off,
|
||||
dump_sb.csum_size, &dump_sb.csum)) {
|
||||
PRINT_ERR("Get check sum failed\n");
|
||||
return -1;
|
||||
}
|
||||
if (lseek(fd, mbr.boot_info.sb_off, SEEK_SET) < 0) {
|
||||
PRINT_PERR("Seek failed\n");
|
||||
return -1;
|
||||
}
|
||||
if (write(fd, &dump_sb, sizeof(dump_sb)) < 0) {
|
||||
PRINT_PERR("Write failed\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize copy table to copy HSA first
|
||||
*/
|
||||
static int copy_table_init(int fd, struct copy_table_entry *table)
|
||||
{
|
||||
Elf64_Ehdr ehdr;
|
||||
Elf64_Phdr phdr;
|
||||
int i;
|
||||
|
||||
g.vmcore_size = lseek(fd, (off_t) 0, SEEK_END);
|
||||
lseek(fd, 0L, SEEK_SET);
|
||||
if (g.vmcore_size < sizeof(ehdr))
|
||||
return -1;
|
||||
if (read(fd, &ehdr, sizeof(ehdr)) < 0)
|
||||
return -1;
|
||||
if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0)
|
||||
return -1;
|
||||
if (ehdr.e_type != ET_CORE)
|
||||
return -1;
|
||||
if (ehdr.e_machine != EM_S390 || ehdr.e_ident[EI_CLASS] != ELFCLASS64) {
|
||||
PRINT_ERR("Only 64 bit core dump files are supported\n");
|
||||
return -1;
|
||||
}
|
||||
/* Find first memory chunk to determine HSA size */
|
||||
for (i = 0; i < ehdr.e_phnum; i++) {
|
||||
if (read(fd, &phdr, sizeof(phdr)) < 0)
|
||||
return -1;
|
||||
if (phdr.p_type != PT_LOAD)
|
||||
continue;
|
||||
if (phdr.p_vaddr != 0)
|
||||
continue;
|
||||
/* 1st entry is the HSA (vaddr = 0) */
|
||||
table[0].off = phdr.p_offset;
|
||||
table[0].size = get_hsa_size();
|
||||
/* 2nd entry defines area from 2nd page to HSA start */
|
||||
table[1].off = PAGE_SIZE;
|
||||
table[1].size = table[0].off - PAGE_SIZE;
|
||||
/*
|
||||
* 3rd entry defines area from HSA end to end of file.
|
||||
*/
|
||||
table[2].off = table[0].off + table[0].size;
|
||||
table[2].size = g.vmcore_size - table[2].off;
|
||||
/*
|
||||
* We write the last page at the end of the dump processing.
|
||||
* This ensures that the dump stays invalid until all data
|
||||
* is written. We can use one page because it is ensured that
|
||||
* the HSA starts page aligned.
|
||||
*/
|
||||
table[3].off = 0;
|
||||
table[3].size = PAGE_SIZE;
|
||||
return 0;
|
||||
}
|
||||
PRINT_ERR("Could not find HSA ELF load section\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy one copy table entry form /proc/vmcore to dump partition
|
||||
*/
|
||||
static int copy_table_entry_write(int fdin, int fdout,
|
||||
struct copy_table_entry *entry,
|
||||
unsigned long offset)
|
||||
{
|
||||
unsigned long buf_size, bytes_left, off;
|
||||
void *map;
|
||||
|
||||
if (entry->size == 0)
|
||||
return 0;
|
||||
off = entry->off;
|
||||
bytes_left = entry->size;
|
||||
if (lseek(fdout, entry->off + offset, SEEK_SET) < 0)
|
||||
return -1;
|
||||
while (bytes_left > 0) {
|
||||
buf_size = MIN(COPY_BUF_SIZE, bytes_left);
|
||||
map = mmap(0, buf_size, PROT_READ, MAP_SHARED, fdin, off);
|
||||
if (map == (void *)-1) {
|
||||
PRINT_PERR("Mapping failed\n");
|
||||
return -1;
|
||||
}
|
||||
if (write(fdout, map, buf_size) < 0) {
|
||||
PRINT_PERR("Write to partition failed\n");
|
||||
return -1;
|
||||
}
|
||||
munmap(map, buf_size);
|
||||
bytes_left -= buf_size;
|
||||
off += buf_size;
|
||||
show_progress(buf_size);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy dump using mmap (copy HSA first)
|
||||
*/
|
||||
static int copy_dump(const char *in, const char *out, unsigned long offset)
|
||||
{
|
||||
struct copy_table_entry table[COPY_TABLE_ENTRY_COUNT];
|
||||
char busy_str[] = "zfcpdump busy";
|
||||
int fdout, fdin, i, rc = -1;
|
||||
|
||||
fdin = open(in, O_RDONLY);
|
||||
if (fdin < 0) {
|
||||
PRINT_ERR("Open %s failed\n", in);
|
||||
return -1;
|
||||
}
|
||||
g.vmcore_size = lseek(fdin, (off_t) 0, SEEK_END);
|
||||
lseek(fdin, 0L, SEEK_SET);
|
||||
if (g.vmcore_size > dump_sb.dump_size) {
|
||||
PRINT_ERR("Disk too small: dump=%lldMB (diskspace=%lldMB)\n",
|
||||
TO_MIB(g.vmcore_size), TO_MIB(dump_sb.dump_size));
|
||||
goto out_close_fdin;
|
||||
}
|
||||
fdout = open(out, O_WRONLY);
|
||||
if (fdout < 0) {
|
||||
PRINT_ERR("Open %s failed\n", out);
|
||||
goto out_close_fdin;
|
||||
}
|
||||
/* Overwrite old header */
|
||||
if (lseek(fdout, offset, SEEK_SET) < 0)
|
||||
goto out_close_fdin;
|
||||
if (write(fdout, busy_str, sizeof(busy_str)) == -1)
|
||||
goto out_close_fdin;
|
||||
if (csum_update(fdout))
|
||||
goto out_close_fdin;
|
||||
if (copy_table_init(fdin, table))
|
||||
goto out_close_fdin;
|
||||
show_progress(0);
|
||||
for (i = 0; i < COPY_TABLE_ENTRY_COUNT; i++) {
|
||||
if (copy_table_entry_write(fdin, fdout, &table[i], offset))
|
||||
goto out_close_fdout;
|
||||
if (i == 0) /* 0 is the HSA */
|
||||
release_hsa();
|
||||
}
|
||||
rc = 0;
|
||||
out_close_fdout:
|
||||
if (csum_update(fdout))
|
||||
rc = -1;
|
||||
fsync(fdout);
|
||||
close(fdout);
|
||||
out_close_fdin:
|
||||
close(fdin);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Finds the matching partition to a given start and end. If a matching
|
||||
* partition is found, the partition number is returned.
|
||||
*/
|
||||
int find_part_num(uint64_t start, uint64_t size)
|
||||
{
|
||||
struct hd_geometry geo;
|
||||
uint32_t block_size;
|
||||
uint64_t part_size;
|
||||
char path[11];
|
||||
int fd, i;
|
||||
|
||||
PRINT_TRACE("Partiton to dump start: 0x%llx end: 0x%llx\n",
|
||||
(unsigned long long) start, (unsigned long long) size);
|
||||
for (i = 1; i < 16; i++) {
|
||||
snprintf(path, sizeof(path), DEV_SCSI "%d", i);
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd == -1)
|
||||
continue;
|
||||
if (ioctl(fd, HDIO_GETGEO, &geo) != 0) {
|
||||
PRINT_PERR("Could not retrieve partition"
|
||||
" geometry information\n");
|
||||
return -1;
|
||||
}
|
||||
if (ioctl(fd, BLKGETSIZE64, &part_size)) {
|
||||
PRINT_PERR("Could not retrieve partition"
|
||||
" size information\n");
|
||||
return -1;
|
||||
}
|
||||
if (ioctl(fd, BLKSSZGET, &block_size)) {
|
||||
PRINT_PERR("Could not get blocksize");
|
||||
return -1;
|
||||
}
|
||||
PRINT_TRACE("Partiton %s start: 0x%llx end: 0x%llx\n", path,
|
||||
(unsigned long long) geo.start * block_size,
|
||||
(unsigned long long) part_size);
|
||||
if ((start == geo.start * block_size) && (size == part_size))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the on-disk zfcpdump boot info and superblock into global variables
|
||||
*/
|
||||
static int get_scsi_dump_params(void)
|
||||
{
|
||||
uint64_t csum;
|
||||
int part_num;
|
||||
|
||||
if (pread_file(DEV_SCSI, (char *)&mbr, sizeof(mbr), 0) < 0) {
|
||||
PRINT_ERR("Cannot read MBR\n");
|
||||
return -1;
|
||||
}
|
||||
if (memcmp(&mbr.boot_info.magic, BOOT_INFO_MAGIC,
|
||||
sizeof(mbr.boot_info.magic))) {
|
||||
PRINT_ERR("Boot_Info wrong magic\n");
|
||||
return -1;
|
||||
}
|
||||
if (mbr.boot_info.dev_type != BOOT_INFO_DEV_TYPE_SCSI) {
|
||||
PRINT_ERR("Boot_Info wrong dev type: %d\n",
|
||||
mbr.boot_info.dev_type);
|
||||
return -1;
|
||||
}
|
||||
if (mbr.boot_info.bp_type != BOOT_INFO_BP_TYPE_DUMP) {
|
||||
PRINT_ERR("Boot_Info wrong bp type: %d\n",
|
||||
mbr.boot_info.bp_type);
|
||||
return -1;
|
||||
}
|
||||
if (mbr.boot_info.version != BOOT_INFO_VERSION) {
|
||||
PRINT_ERR("Boot_Info wrong version: %d\n",
|
||||
mbr.boot_info.version);
|
||||
return -1;
|
||||
}
|
||||
if (pread_file(DEV_SCSI, (char *)&dump_sb, sizeof(dump_sb),
|
||||
mbr.boot_info.sb_off) < 0) {
|
||||
PRINT_ERR("Cannot read superblock\n");
|
||||
return -1;
|
||||
}
|
||||
if (dump_sb.magic != 0x5a46435044554d50ULL) { /* ZFCPDUMP */
|
||||
PRINT_ERR("Dump data block wrong magic\n");
|
||||
return -1;
|
||||
}
|
||||
part_num = find_part_num(dump_sb.part_start, dump_sb.part_size);
|
||||
if (part_num < 0) {
|
||||
PRINT_ERR("Specified dump partition not found\n");
|
||||
return -1;
|
||||
}
|
||||
if (csum_get(dump_sb.part_start + dump_sb.csum_off,
|
||||
dump_sb.csum_size, &csum)) {
|
||||
PRINT_ERR("Getting Checksum failed\n");
|
||||
return -1;
|
||||
}
|
||||
if (csum != dump_sb.csum) {
|
||||
PRINT_ERR("Checksum wrong, filesystem changed\n");
|
||||
return -1;
|
||||
}
|
||||
PRINT(" partition: " DEV_SCSI "%d\n", part_num);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Main routine of the zfcpdump tool
|
||||
*/
|
||||
int main(int UNUSED(argc), char *UNUSED(argv[]))
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (zfcpdump_init(module_list))
|
||||
return terminate(1);
|
||||
PRINT("Dump parameters:\n");
|
||||
PRINT(" devno....: %s\n", g.dump_devno);
|
||||
PRINT(" wwpn.....: %s\n", g.dump_wwpn);
|
||||
PRINT(" lun......: %s\n", g.dump_lun);
|
||||
PRINT(" conf.....: %s\n", g.dump_bootprog);
|
||||
if (get_scsi_dump_params())
|
||||
return terminate(1);
|
||||
print_newline();
|
||||
PRINT("Writing dump:\n");
|
||||
rc = copy_dump("/proc/vmcore", DEV_SCSI,
|
||||
dump_sb.part_start + dump_sb.dump_off);
|
||||
return terminate(rc);
|
||||
}
|
||||
Reference in New Issue
Block a user