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:
Michael Holzheu
2017-08-07 16:13:17 +02:00
commit b627b8d8e1
647 changed files with 168974 additions and 0 deletions
+25
View File
@@ -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
+54
View File
@@ -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.
+292
View File
@@ -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;
}
+450
View File
@@ -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;
}
+111
View File
@@ -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 */
+31
View File
@@ -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
+457
View File
@@ -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);
}