mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
The build_arch field in s390 DASD dump header has originally been used to indicate whether the dump tool has been built on s390 or s390x system. Since no other architectures but s390x are supported for Linux on z, do not process build_arch attribute. Bail out if any build architecture other than ARCH_64 has been detected in s390_ext or s390mv_ext DASD dump header. Remove build architecture line from 'zgetdump -i' output: Build arch.........: s390x (64 bit) The man file for zgetdump is updated accordingly. Signed-off-by: Mikhail Zaslonko <zaslonko@linux.ibm.com> Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
561 lines
13 KiB
C
561 lines
13 KiB
C
/*
|
|
* zgetdump - Tool for copying and converting System z dumps
|
|
*
|
|
* S390x multi-volume dump input format
|
|
*
|
|
* Copyright IBM Corp. 2001, 2025
|
|
*
|
|
* s390-tools is free software; you can redistribute it and/or modify
|
|
* it under the terms of the MIT license. See LICENSE for details.
|
|
*/
|
|
|
|
#include <dirent.h>
|
|
#include <err.h>
|
|
#include <fcntl.h>
|
|
#include <linux/fs.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/sysmacros.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
|
|
#include "lib/util_file.h"
|
|
#include "lib/util_path.h"
|
|
#include "dump/s390_dump.h"
|
|
|
|
#include "zgetdump.h"
|
|
#include "dt.h"
|
|
#include "df_s390.h"
|
|
#include "dfi.h"
|
|
#include "dfi_s390mv.h"
|
|
#include "dfi_mem_chunk.h"
|
|
|
|
/*
|
|
* Volume information
|
|
*/
|
|
struct vol {
|
|
dev_t dev;
|
|
struct zg_fh *fh;
|
|
char *devnode;
|
|
enum dev_status status;
|
|
enum dev_sign sign;
|
|
off_t part_off;
|
|
u64 part_size;
|
|
u64 mem_start;
|
|
u64 mem_end;
|
|
char bus_id[10];
|
|
u32 nr;
|
|
u16 blk_size;
|
|
struct df_s390_dumper dumper;
|
|
struct df_s390_hdr hdr;
|
|
};
|
|
|
|
/*
|
|
* Mem chunk helper structure for extended dump format
|
|
*/
|
|
struct vol_mem_chunk {
|
|
struct vol *vol;
|
|
u64 off; /* Offset to the memory chunk from the start of the volume */
|
|
};
|
|
|
|
/*
|
|
* File local static data
|
|
*/
|
|
static struct {
|
|
struct df_s390_hdr hdr;
|
|
struct df_s390_em em;
|
|
struct vol vol_vec[MAX_VOLUMES];
|
|
struct vol_parm_table table;
|
|
int blk_size;
|
|
struct df_s390_dumper dumper;
|
|
int dump_incomplete;
|
|
} l;
|
|
|
|
/*
|
|
* Read volume parameter table
|
|
*/
|
|
static void table_read(struct zg_fh *fh, u16 blk_size,
|
|
struct vol_parm_table *table)
|
|
{
|
|
int off;
|
|
|
|
off = DF_S390_MAGIC_BLK_ECKD * blk_size + l.dumper.size;
|
|
zg_seek(fh, off, ZG_CHECK);
|
|
zg_read(fh, table, sizeof(*table), ZG_CHECK);
|
|
}
|
|
|
|
/*
|
|
* Check whether a device with a given busid is online
|
|
*/
|
|
static unsigned int dev_is_online(const char *busid)
|
|
{
|
|
unsigned int online;
|
|
char *path;
|
|
|
|
path = util_path_sysfs("%s/%s/online", SYSFS_BUSDIR, busid);
|
|
if (util_file_read_ui(&online, 10, path) != 0) {
|
|
warnx("Could not open \"%s\" (%s)", path, strerror(errno));
|
|
free(path);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
free(path);
|
|
|
|
return online;
|
|
}
|
|
|
|
/*
|
|
* Check sysfs, whether a device specified by its bus ID is defined and online.
|
|
* Find out the corresponding dev_t
|
|
*/
|
|
static enum dev_status dev_from_busid(char *bus_id, dev_t *dev)
|
|
{
|
|
struct dirent *direntp;
|
|
int minor, major;
|
|
char buf[10];
|
|
DIR *fh_dir;
|
|
char *sysfs;
|
|
|
|
sysfs = util_path_sysfs("%s/%s", SYSFS_BUSDIR, bus_id);
|
|
if (!util_path_is_dir(sysfs)) {
|
|
free(sysfs);
|
|
return DEV_UNDEFINED;
|
|
}
|
|
free(sysfs);
|
|
|
|
if (!dev_is_online(bus_id))
|
|
return DEV_OFFLINE;
|
|
|
|
sysfs = util_path_sysfs("%s/%s/block", SYSFS_BUSDIR, bus_id);
|
|
fh_dir = opendir(sysfs);
|
|
if (!fh_dir) {
|
|
warnx("Could not open \"%s\" (%s) ", sysfs, strerror(errno));
|
|
goto err;
|
|
}
|
|
while ((direntp = readdir(fh_dir)))
|
|
if (strncmp(direntp->d_name, "dasd", 4) == 0)
|
|
break;
|
|
if (!direntp) {
|
|
warnx("Problem with contents of \"%s\"", sysfs);
|
|
goto err;
|
|
}
|
|
if (util_file_read_line(buf, sizeof(buf), "%s/%s/dev", sysfs, direntp->d_name)) {
|
|
warnx("Could not read dev file (%s)", strerror(errno));
|
|
goto err;
|
|
}
|
|
closedir(fh_dir);
|
|
|
|
if (sscanf(buf, "%i:%i", &major, &minor) != 2) {
|
|
warnx("Malformed content of \"%s\": %s", sysfs, buf);
|
|
goto err;
|
|
}
|
|
*dev = makedev(major, minor);
|
|
|
|
free(sysfs);
|
|
|
|
return DEV_ONLINE;
|
|
|
|
err:
|
|
if (fh_dir)
|
|
closedir(fh_dir);
|
|
free(sysfs);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/*
|
|
* Check whether dump table on user specified dump device is
|
|
* identical to the one found on this device.
|
|
*/
|
|
static void check_vol_table(struct vol *vol)
|
|
{
|
|
struct vol_parm_table vol_table;
|
|
|
|
table_read(vol->fh, vol->blk_size, &vol_table);
|
|
if (memcmp(&vol_table, &l.table, sizeof(vol_table)))
|
|
ERR_EXIT("Orphaned multi-volume dump device '%s'",
|
|
g.opts.device);
|
|
}
|
|
|
|
/*
|
|
* Read dump tool, multi-volume dump parameter table, and dump header from the
|
|
* input dump volume. Check input dump volume for:
|
|
* - identical dump parameter table (that is it belongs to the same dump set)
|
|
* - valid magic number in the dump tool
|
|
* - valid dump sign in the dump header
|
|
*
|
|
* We read partition data via the device node. If another process
|
|
* has changed partition data via the partition node, the corresponding
|
|
* device node might still have old data in its buffers. Flush buffers
|
|
* to keep things in sync.
|
|
*/
|
|
static void vol_read(struct vol *vol)
|
|
{
|
|
zg_ioctl(vol->fh, BLKFLSBUF, NULL, "BLKFLSBUF", ZG_CHECK);
|
|
if (df_s390_dumper_read(vol->fh, vol->blk_size, &vol->dumper))
|
|
ERR_EXIT("No dump tool found on the multi-volume dump device '%s'",
|
|
vol->bus_id);
|
|
check_vol_table(vol);
|
|
zg_seek(vol->fh, vol->part_off, ZG_CHECK);
|
|
zg_read(vol->fh, &vol->hdr, DF_S390_HDR_SIZE, ZG_CHECK);
|
|
}
|
|
|
|
/*
|
|
* Read memory chunk (extended dump format)
|
|
*/
|
|
static void dfi_s390mv_ext_mem_read(struct dfi_mem_chunk *mem_chunk, u64 off,
|
|
void *buf, u64 cnt)
|
|
{
|
|
struct vol_mem_chunk *vol_mem_chunk = mem_chunk->data;
|
|
struct vol *vol = vol_mem_chunk->vol;
|
|
|
|
zg_seek(vol->fh, vol_mem_chunk->off + off, ZG_CHECK);
|
|
zg_read(vol->fh, buf, cnt, ZG_CHECK);
|
|
}
|
|
|
|
/*
|
|
* Initialize DASD volume
|
|
*/
|
|
static void vol_init(struct vol *vol, struct vol_parm *vol_parm, int ssid)
|
|
{
|
|
u64 blk_cnt = vol_parm->end_blk - vol_parm->start_blk + 1;
|
|
|
|
sprintf(vol->bus_id, "0.%x.%04x", ssid, vol_parm->devno);
|
|
vol->blk_size = vol_parm->blk_size << 8;
|
|
vol->part_off = (u64)vol_parm->start_blk * vol->blk_size;
|
|
vol->part_size = blk_cnt * vol->blk_size;
|
|
vol->status = dev_from_busid(vol->bus_id, &vol->dev);
|
|
vol->sign = SIGN_VALID;
|
|
|
|
if (vol->status != DEV_ONLINE)
|
|
return;
|
|
|
|
vol->devnode = zg_devnode_create(vol->dev);
|
|
vol->fh = dfi_dump_open(vol->devnode);
|
|
|
|
vol_read(vol);
|
|
|
|
if ((vol->hdr.volnr == vol->nr) && (vol->hdr.mem_size != 0))
|
|
vol->sign = SIGN_ACTIVE;
|
|
|
|
if (vol->hdr.mvdump_sign != DF_S390_MAGIC_EXT) {
|
|
vol->sign = SIGN_INVALID;
|
|
l.dump_incomplete = 1;
|
|
}
|
|
|
|
if (strncmp(vol->dumper.magic, DF_S390_DUMPER_MAGIC_MV_EXT,
|
|
DF_S390_DUMPER_MAGIC_SIZE) != 0) {
|
|
vol->sign = SIGN_INVALID;
|
|
l.dump_incomplete = 1;
|
|
}
|
|
|
|
if (vol->nr == 0)
|
|
l.hdr = vol->hdr;
|
|
}
|
|
|
|
/*
|
|
* Print volume information
|
|
*/
|
|
static void vol_print(struct vol *vol)
|
|
{
|
|
STDERR(" Volume %i: %s (%s", vol->nr, vol->bus_id,
|
|
dev_status_str(vol->status));
|
|
if (vol->status == DEV_ONLINE)
|
|
STDERR("/%s)\n", dev_sign_str(vol->sign));
|
|
else
|
|
STDERR(")\n");
|
|
}
|
|
|
|
/*
|
|
* Print information for all volumes
|
|
*/
|
|
static void vol_print_all(void)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < l.table.vol_cnt; i++)
|
|
vol_print(&l.vol_vec[i]);
|
|
}
|
|
|
|
/*
|
|
* Add memory chunks (extended dump format) and verify the end marker
|
|
*/
|
|
static int mem_chunks_add_ext(void)
|
|
{
|
|
u64 off, rc, part_end, old = 0, dump_size = 0;
|
|
struct df_s390_dump_segm_hdr dump_segm;
|
|
struct vol_mem_chunk *vol_mem_chunk;
|
|
struct vol *vol;
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < l.table.vol_cnt; i++) {
|
|
vol = &l.vol_vec[i];
|
|
if (vol->sign != SIGN_ACTIVE)
|
|
continue;
|
|
off = vol->part_off + DF_S390_HDR_SIZE;
|
|
part_end = vol->part_off + vol->part_size;
|
|
rc = zg_seek(vol->fh, off, ZG_CHECK_NONE);
|
|
if (rc != off)
|
|
return -EINVAL;
|
|
/*
|
|
* Reading dump segments from the start of partition (skipping
|
|
* the dump header) till the end of partition (considering that
|
|
* minimum dump segment size is one megabyte + one page and one
|
|
* more page is reserved for the end marker)
|
|
*/
|
|
while (off < part_end - MIB - PAGE_SIZE) {
|
|
rc = zg_read(vol->fh, &dump_segm, sizeof(dump_segm),
|
|
ZG_CHECK_ERR);
|
|
if (rc != PAGE_SIZE)
|
|
return -EINVAL;
|
|
off += PAGE_SIZE;
|
|
vol_mem_chunk = zg_alloc(sizeof(*vol_mem_chunk));
|
|
vol_mem_chunk->vol = vol;
|
|
vol_mem_chunk->off = off;
|
|
/* Add zero memory chunk */
|
|
dfi_mem_chunk_add_vol(old, dump_segm.start - old, NULL,
|
|
dfi_mem_chunk_read_zero, NULL,
|
|
vol->nr);
|
|
/* Add memory chunk for a dump segment */
|
|
dfi_mem_chunk_add_vol(dump_segm.start, dump_segm.len,
|
|
vol_mem_chunk,
|
|
dfi_s390mv_ext_mem_read, NULL,
|
|
vol->nr);
|
|
old = dump_segm.start + dump_segm.len;
|
|
dump_size += dump_segm.len;
|
|
off = zg_seek_cur(vol->fh, dump_segm.len,
|
|
ZG_CHECK_NONE);
|
|
if (dump_segm.stop_marker)
|
|
break;
|
|
}
|
|
if (dump_segm.stop_marker) {
|
|
/* Add zero memory chunk at the end*/
|
|
dfi_mem_chunk_add_vol(old, l.hdr.mem_size - old, NULL,
|
|
dfi_mem_chunk_read_zero, NULL,
|
|
vol->nr);
|
|
/* Set the actual size of the dump file */
|
|
dfi_attr_file_size_set(dump_size);
|
|
/* Read and verify the end marker */
|
|
rc = zg_read(vol->fh, &l.em, sizeof(l.em), ZG_CHECK);
|
|
if (rc != sizeof(l.em) ||
|
|
df_s390_em_verify(&l.em, &l.hdr) != 0)
|
|
return -EINVAL;
|
|
return 0;
|
|
}
|
|
}
|
|
/* No dump segment with the stop marker found */
|
|
return -EINVAL;
|
|
}
|
|
|
|
/*
|
|
* Print hint for setting all offline volumes online
|
|
*/
|
|
static void vol_offline_msg(void)
|
|
{
|
|
unsigned int i, first = 1;
|
|
|
|
STDERR("\n");
|
|
STDERR("Set all devices online using:\n");
|
|
STDERR("# chccwdev -e ");
|
|
for (i = 0; i < l.table.vol_cnt; i++) {
|
|
if (l.vol_vec[i].status == DEV_OFFLINE) {
|
|
if (first)
|
|
first = 0;
|
|
else
|
|
STDERR(",");
|
|
STDERR("%s", l.vol_vec[i].bus_id);
|
|
}
|
|
}
|
|
STDERR("\n");
|
|
}
|
|
|
|
/*
|
|
* Print error for all undefined volumes
|
|
*/
|
|
static void vol_undefined_msg(void)
|
|
{
|
|
unsigned int i;
|
|
|
|
STDERR("\n");
|
|
STDERR("Ensure that the following devices are available to the system:\n");
|
|
for (i = 0; i < l.table.vol_cnt; i++) {
|
|
if (l.vol_vec[i].status == DEV_UNDEFINED)
|
|
STDERR("* %s\n", l.vol_vec[i].bus_id);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check that all volumes are in online state
|
|
*/
|
|
static int vol_online_check(void)
|
|
{
|
|
unsigned int i, offline = 0, undefined = 0;
|
|
|
|
for (i = 0; i < l.table.vol_cnt; i++) {
|
|
if (l.vol_vec[i].status == DEV_OFFLINE)
|
|
offline = 1;
|
|
if (l.vol_vec[i].status == DEV_UNDEFINED)
|
|
undefined = 1;
|
|
}
|
|
if (!offline && !undefined)
|
|
return 0;
|
|
|
|
STDERR("Found multi-volume dump tool:\n\n");
|
|
vol_print_all();
|
|
if (offline)
|
|
vol_offline_msg();
|
|
if (undefined)
|
|
vol_undefined_msg();
|
|
return -ENODEV;
|
|
}
|
|
|
|
/*
|
|
* Check if on device is a multi-volume dump
|
|
*/
|
|
static int mvdump_hdr_check(const char *file)
|
|
{
|
|
struct df_s390_hdr hdr;
|
|
struct zg_fh *fh;
|
|
int rc = -ENODEV;
|
|
|
|
fh = zg_open(file, O_RDONLY, ZG_CHECK);
|
|
if (zg_read(fh, &hdr, sizeof(hdr), ZG_CHECK_ERR) != sizeof(hdr))
|
|
goto fail;
|
|
if (hdr.magic != DF_S390_MAGIC_EXT)
|
|
goto fail;
|
|
if (hdr.mvdump_sign != DF_S390_MAGIC_EXT)
|
|
goto fail;
|
|
rc = 0;
|
|
fail:
|
|
zg_close(fh);
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Read dump tool from DASD and check if we have a multi-volume dump tool
|
|
*/
|
|
static int mv_dumper_read(void)
|
|
{
|
|
if (zg_ioctl(g.fh, BLKSSZGET, &l.blk_size, "BLKSSZGET",
|
|
ZG_CHECK_NONE) == -1)
|
|
return -ENODEV;
|
|
if (df_s390_dumper_read(g.fh, l.blk_size, &l.dumper))
|
|
return -ENODEV;
|
|
if (strncmp(l.dumper.magic, DF_S390_DUMPER_MAGIC_MV_EXT, DF_S390_DUMPER_MAGIC_SIZE) != 0)
|
|
return -ENODEV;
|
|
table_read(g.fh, l.blk_size, &l.table);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Initialize all volumes
|
|
*/
|
|
static void volumes_init(void)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < l.table.vol_cnt; i++) {
|
|
l.vol_vec[i].nr = i;
|
|
vol_init(&l.vol_vec[i], &l.table.vol_parm[i], l.table.ssid[i]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Open dump - If partition is specified open device instead
|
|
*/
|
|
static int open_dump(void)
|
|
{
|
|
const struct stat *stat = zg_stat(g.fh);
|
|
unsigned int dev_minor;
|
|
enum zg_type type;
|
|
char *path;
|
|
|
|
type = zg_type(g.fh);
|
|
if (type != ZG_TYPE_DASD && type != ZG_TYPE_DASD_PART)
|
|
return -ENODEV;
|
|
|
|
if (type == ZG_TYPE_DASD_PART) {
|
|
dev_minor = minor(stat->st_rdev) - (minor(stat->st_rdev) % 4);
|
|
if (mvdump_hdr_check(zg_path(g.fh)) != 0)
|
|
return -ENODEV;
|
|
path = zg_devnode_create(makedev(major(stat->st_rdev),
|
|
dev_minor));
|
|
zg_close(g.fh);
|
|
g.fh = zg_open(path, O_RDONLY, ZG_CHECK);
|
|
}
|
|
if (mv_dumper_read() != 0)
|
|
return -ENODEV;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Initialize s390 extended multi-volume input dump format function
|
|
*/
|
|
static int dfi_s390mv_ext_init(void)
|
|
{
|
|
int rc;
|
|
|
|
if (open_dump() != 0)
|
|
return -ENODEV;
|
|
volumes_init();
|
|
if (vol_online_check() != 0)
|
|
zg_exit(1);
|
|
if (l.hdr.arch != DF_S390_ARCH_64)
|
|
ERR_EXIT("Dump architecture is not supported!");
|
|
if (l.hdr.build_arch && l.hdr.build_arch != DF_S390_ARCH_64)
|
|
ERR_EXIT("Dump-tool build architecture is not supported!");
|
|
if (l.hdr.mem_size == 0)
|
|
return -ENODEV;
|
|
df_s390_hdr_add(&l.hdr);
|
|
if (mem_chunks_add_ext() != 0)
|
|
return -EINVAL;
|
|
if (l.dump_incomplete)
|
|
return -EINVAL;
|
|
rc = df_s390_cpu_info_add(&l.hdr, l.hdr.mem_end);
|
|
if (rc)
|
|
return rc;
|
|
df_s390_em_add(&l.em);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Print dump information (dfi operation)
|
|
*/
|
|
static void dfi_s390mv_info(void)
|
|
{
|
|
vol_print_all();
|
|
}
|
|
|
|
/*
|
|
* Initialize s390 multi-volume dump tool (extedend) for -d option
|
|
*/
|
|
int dt_s390mv_ext_init(void)
|
|
{
|
|
if (open_dump() != 0)
|
|
return -ENODEV;
|
|
volumes_init();
|
|
dt_version_set(l.dumper.version);
|
|
dt_attr_mem_limit_set(l.dumper.mem);
|
|
dt_attr_force_set(l.dumper.force);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* s390 multi-volume dump tool info function (for -d option)
|
|
*/
|
|
void dt_s390mv_info(void)
|
|
{
|
|
vol_print_all();
|
|
}
|
|
|
|
/*
|
|
* s390 multi-volume DFI (extedned) operations
|
|
*/
|
|
struct dfi dfi_s390mv_ext = {
|
|
.name = "s390mv_ext",
|
|
.init = dfi_s390mv_ext_init,
|
|
.info_dump = dfi_s390mv_info,
|
|
.feat_bits = DFI_FEAT_COPY | DFI_FEAT_SEEK,
|
|
};
|