mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
zdump: Add zgetdump -d support for ECKD ldipl-dump
Make 'zgetdump -d' to identify ldipl-dump tool (ngdump) installed on DASD volume just like it does for NVMe ngdump. Output sample: ------------- Dump device info: Dump tool.........: Next Generation (NGDump) dump tool Version...........: 1 Architecture......: s390x (64 bit) Partition info: Partition number..: 2 Signed-off-by: Mikhail Zaslonko <zaslonko@linux.ibm.com> Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com> Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
This commit is contained in:
committed by
Steffen Eiden
parent
a2f8b19c2a
commit
f3bcd94524
@@ -40,6 +40,7 @@
|
||||
#define LV_COMPAT_CYL 0xFFFE
|
||||
|
||||
#define VTOC_ERROR "VTOC error:"
|
||||
#define MAX_VTOC_ENTRIES 9 /* max number of VTOC labels for cdl formatted DASD */
|
||||
|
||||
typedef struct ttr
|
||||
{
|
||||
|
||||
@@ -105,7 +105,10 @@ ifneq ("$(HAVE_FUSE)","0")
|
||||
OBJECTS += zfuse.o
|
||||
endif
|
||||
|
||||
libs = $(rootdir)/libutil/libutil.a $(LIBPV)
|
||||
libs = $(rootdir)/libutil/libutil.a \
|
||||
$(rootdir)/libvtoc/libvtoc.a \
|
||||
$(rootdir)/libdasd/libdasd.a \
|
||||
$(LIBPV)
|
||||
|
||||
all: $(BUILD_TARGETS)
|
||||
|
||||
|
||||
@@ -27,11 +27,9 @@ static int dt_ngdump_init(void)
|
||||
char *part_path = NULL;
|
||||
int rc;
|
||||
|
||||
l.part_num = ngdump_get_dump_part(g.fh);
|
||||
l.part_num = ngdump_get_dump_part(g.fh, &part_path);
|
||||
if (l.part_num <= 0)
|
||||
return -1;
|
||||
if (ngdump_get_disk_part_path(g.fh->path, l.part_num, &part_path) < 0)
|
||||
return -1;
|
||||
rc = ngdump_read_meta_from_device(part_path, &l.meta);
|
||||
free(part_path);
|
||||
if (rc)
|
||||
|
||||
304
zdump/ngdump.c
304
zdump/ngdump.c
@@ -18,9 +18,11 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include "lib/dasd_base.h"
|
||||
#include "lib/util_libc.h"
|
||||
#include "lib/util_part.h"
|
||||
#include "lib/util_log.h"
|
||||
#include "lib/vtoc.h"
|
||||
#include "boot/boot_defs.h"
|
||||
#include "boot/linux_layout.h"
|
||||
|
||||
@@ -30,6 +32,16 @@
|
||||
#define NGDUMP_META_VERSION 1
|
||||
#define NGDUMP_META_FILENAME "ngdump.meta"
|
||||
|
||||
enum ngdump_disk_type {
|
||||
NG_TYPE_DASD,
|
||||
NG_TYPE_NVME,
|
||||
};
|
||||
|
||||
static const char *const ngtype2str[] = {
|
||||
[NG_TYPE_DASD] = "DASD",
|
||||
[NG_TYPE_NVME] = "NVME"
|
||||
};
|
||||
|
||||
static int read_meta_from_file(const char *filename, struct ngdump_meta *meta)
|
||||
{
|
||||
FILE *fp = NULL;
|
||||
@@ -212,56 +224,71 @@ out:
|
||||
}
|
||||
|
||||
/*
|
||||
* This function parses the bootloader program table stored on the given device
|
||||
* and returns the partition index where the dumper's kernel image is stored.
|
||||
* Convert disk blockpointer to the offset in blocks.
|
||||
* Use eckd blockpointer format if hd_geometry is provided, otherwise linear blockpointer.
|
||||
* Return u64(-1) in case the blockpointer contains zeroes.
|
||||
*/
|
||||
int ngdump_get_dump_part(struct zg_fh *zg_fh)
|
||||
static uint64_t blockptr2blk(union disk_blockptr *ptr, const struct hd_geometry *geo)
|
||||
{
|
||||
uint64_t blk;
|
||||
|
||||
/* For NVMe or SCSI use linear blockpointer format. */
|
||||
/* For DASD use eckd blockpointer format. */
|
||||
if (!geo) {
|
||||
blk = ptr->linear.blockno;
|
||||
if (blk == 0)
|
||||
return U64_MAX;
|
||||
} else {
|
||||
if (ptr->eckd.sec == 0)
|
||||
return U64_MAX;
|
||||
blk = ptr->eckd.cyl * geo->heads + ptr->eckd.head; /* Track number */
|
||||
blk *= geo->sectors; /* Track offset in records */
|
||||
blk += ptr->eckd.sec - 1; /* Record offset (skipping R0) */
|
||||
}
|
||||
return blk;
|
||||
}
|
||||
|
||||
/*
|
||||
* Based on the provided program table blockpointer find kernel image Boot Map Section
|
||||
* for dump Boot Map Script. Read the first data blockpointer from the section into
|
||||
* blockptr area.
|
||||
*/
|
||||
static int get_bootmap_dump_image_blkptr(struct zg_fh *zg_fh, union disk_blockptr *program_table,
|
||||
struct hd_geometry *geo, int blk_size,
|
||||
union disk_blockptr *blockptr)
|
||||
{
|
||||
int i, blk_size, max_entries, part_ext;
|
||||
struct component_entry comp_entry;
|
||||
struct component_header comp_hdr;
|
||||
struct linear_blockptr blockptr;
|
||||
struct scsi_mbr mbr;
|
||||
uint64_t off;
|
||||
|
||||
if (zg_ioctl(zg_fh, BLKSSZGET, &blk_size, "BLKSSZGET", ZG_CHECK_NONE))
|
||||
return -1;
|
||||
|
||||
util_log_print(UTIL_LOG_TRACE, "%s: Block size %d\n",
|
||||
__func__, blk_size);
|
||||
|
||||
/* Read Master Boot Record (MBR) and check its magic */
|
||||
zg_read(zg_fh, &mbr, sizeof(mbr), ZG_CHECK);
|
||||
if (memcmp(mbr.magic, ZIPL_MAGIC, ZIPL_MAGIC_SIZE))
|
||||
return -1;
|
||||
int i, max_entries;
|
||||
uint64_t blk;
|
||||
|
||||
/* Read Boot Map Table and check its magic */
|
||||
off = mbr.program_table_pointer.blockno * blk_size;
|
||||
blk = blockptr2blk(program_table, geo);
|
||||
if (blk == U64_MAX)
|
||||
return -1;
|
||||
util_log_print(UTIL_LOG_TRACE,
|
||||
"%s: Reading program table at offset 0x%016lx\n",
|
||||
__func__, off);
|
||||
zg_seek(zg_fh, off, ZG_CHECK);
|
||||
zg_read(zg_fh, &blockptr, sizeof(blockptr), ZG_CHECK);
|
||||
if (memcmp((const char*)&blockptr, ZIPL_MAGIC, ZIPL_MAGIC_SIZE))
|
||||
__func__, blk * blk_size);
|
||||
zg_seek(zg_fh, blk * blk_size, ZG_CHECK);
|
||||
zg_read(zg_fh, blockptr, sizeof(*blockptr), ZG_CHECK);
|
||||
if (memcmp((const char *)blockptr, ZIPL_MAGIC, ZIPL_MAGIC_SIZE))
|
||||
return -1;
|
||||
|
||||
/* Read Boot Map Script Pointer 0 */
|
||||
zg_read(zg_fh, &blockptr, sizeof(blockptr), ZG_CHECK);
|
||||
if (blockptr.blockno == 0)
|
||||
zg_read(zg_fh, blockptr, sizeof(*blockptr), ZG_CHECK);
|
||||
blk = blockptr2blk(blockptr, geo);
|
||||
if (blk == U64_MAX)
|
||||
return -1;
|
||||
/* Read 1st Boot Map Script, check its magic and type */
|
||||
off = blockptr.blockno * blk_size;
|
||||
util_log_print(UTIL_LOG_TRACE,
|
||||
"%s: Reading component header at offset 0x%016lx\n",
|
||||
__func__, off);
|
||||
zg_seek(zg_fh, off, ZG_CHECK_ERR);
|
||||
__func__, blk * blk_size);
|
||||
zg_seek(zg_fh, blk * blk_size, ZG_CHECK_ERR);
|
||||
zg_read(zg_fh, &comp_hdr, sizeof(comp_hdr), ZG_CHECK_ERR);
|
||||
if (memcmp(comp_hdr.magic, ZIPL_MAGIC, ZIPL_MAGIC_SIZE))
|
||||
return -1;
|
||||
/* We want only dump script */
|
||||
if (comp_hdr.type != COMPONENT_HEADER_DUMP)
|
||||
return -1;
|
||||
|
||||
/* Find kernel's Boot Map Section Pointer */
|
||||
max_entries = (blk_size - sizeof(comp_hdr)) / sizeof(comp_entry);
|
||||
for (i = 0; i < max_entries; i++) {
|
||||
@@ -282,27 +309,178 @@ int ngdump_get_dump_part(struct zg_fh *zg_fh)
|
||||
}
|
||||
|
||||
/* Read 1st Boot Map Data Pointer in kernel's Boot Map Section */
|
||||
off = ((struct linear_blockptr *)comp_entry.data)->blockno * blk_size;
|
||||
blk = blockptr2blk((union disk_blockptr *)comp_entry.data, geo);
|
||||
if (blk == U64_MAX)
|
||||
return -1;
|
||||
util_log_print(UTIL_LOG_TRACE,
|
||||
"%s: Reading component block pointer at offset 0x%016lx\n",
|
||||
__func__, off);
|
||||
zg_seek(zg_fh, off, ZG_CHECK_ERR);
|
||||
zg_read(zg_fh, &blockptr, sizeof(blockptr), ZG_CHECK);
|
||||
util_log_print(UTIL_LOG_TRACE,
|
||||
"%s: Component block address 0x%016lx block count %d\n",
|
||||
__func__, blockptr.blockno, blockptr.blockct);
|
||||
__func__, blk * blk_size);
|
||||
zg_seek(zg_fh, blk * blk_size, ZG_CHECK_ERR);
|
||||
zg_read(zg_fh, blockptr, sizeof(*blockptr), ZG_CHECK);
|
||||
|
||||
return util_part_search_fh(zg_fh->fh, blockptr.blockno,
|
||||
blockptr.blockct, blk_size, &part_ext);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ngdump_get_disk_part_path(const char *disk_path, int part_num,
|
||||
char **part_path)
|
||||
/*
|
||||
* This function parses the bootloader program table stored on the nvme device
|
||||
* and returns the partition index where the dumper's kernel image is stored.
|
||||
*/
|
||||
static int ngdump_get_nvme_part_num(struct zg_fh *zg_fh)
|
||||
{
|
||||
union disk_blockptr dump_image_blkptr;
|
||||
int blk_size, part_num, part_ext;
|
||||
struct linear_blockptr *blockptr;
|
||||
struct scsi_mbr mbr;
|
||||
|
||||
if (zg_ioctl(zg_fh, BLKSSZGET, &blk_size, "BLKSSZGET", ZG_CHECK_NONE))
|
||||
return -1;
|
||||
util_log_print(UTIL_LOG_TRACE, "%s: Block size %d\n",
|
||||
__func__, blk_size);
|
||||
/* Read Master Boot Record (MBR) and check its magic */
|
||||
zg_read(zg_fh, &mbr, sizeof(mbr), ZG_CHECK);
|
||||
if (memcmp(mbr.magic, ZIPL_MAGIC, ZIPL_MAGIC_SIZE))
|
||||
return -1;
|
||||
blockptr = &mbr.program_table_pointer;
|
||||
/*
|
||||
* Cast linear_blockptr to disk_blockptr before passing it to the function.
|
||||
* Since no hd_geometry provided, it will be treated as linear_blockptr later on.
|
||||
*/
|
||||
if (get_bootmap_dump_image_blkptr(zg_fh, (union disk_blockptr *)blockptr,
|
||||
NULL, blk_size, &dump_image_blkptr))
|
||||
return -1;
|
||||
blockptr = &dump_image_blkptr.linear;
|
||||
util_log_print(UTIL_LOG_TRACE,
|
||||
"%s: Component block address 0x%016lx block count %d\n",
|
||||
__func__, blockptr->blockno,
|
||||
blockptr->blockct);
|
||||
if (blockptr->blockno == 0)
|
||||
return -1;
|
||||
part_num = util_part_search_fh(zg_fh->fh, blockptr->blockno, blockptr->blockct,
|
||||
blk_size, &part_ext);
|
||||
return part_num;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function scans a VTOC record of cdl formatted DASD to identify a partition
|
||||
* the specified blockno (0-indexed) belongs to.
|
||||
*/
|
||||
static int find_vol1_cdl_part_fh(struct zg_fh *zg_fh, uint64_t blockno, int blk_size,
|
||||
cchhb_t *vtoc, struct hd_geometry *geo)
|
||||
{
|
||||
unsigned int part_count, i;
|
||||
struct format1_label f1;
|
||||
uint64_t blk;
|
||||
|
||||
blk = cchhb2blk(vtoc, geo);
|
||||
if (!blk)
|
||||
return -1;
|
||||
/* Get actual offset in blocks (special record zero accounted) */
|
||||
blk--;
|
||||
part_count = 0;
|
||||
for (i = 0; i < MAX_VTOC_ENTRIES; i++) {
|
||||
zg_seek(zg_fh, blk * blk_size, ZG_CHECK);
|
||||
zg_read(zg_fh, &f1, sizeof(f1), ZG_CHECK_ERR);
|
||||
/* Skip FMT4 / FMT5 / FMT7 / FMT9 labels */
|
||||
if (f1.DS1FMTID == 0xf4 ||
|
||||
f1.DS1FMTID == 0xf5 ||
|
||||
f1.DS1FMTID == 0xf7 ||
|
||||
f1.DS1FMTID == 0xf9) {
|
||||
blk++;
|
||||
continue;
|
||||
}
|
||||
/* only FMT1 and FMT8 labels valid at this point */
|
||||
if (f1.DS1FMTID != 0xf1 &&
|
||||
f1.DS1FMTID != 0xf8)
|
||||
break;
|
||||
/* OK, we got valid partition data. Check for partition boundaries */
|
||||
if (blockno >= cchh2blk(&f1.DS1EXT1.llimit, geo) &&
|
||||
blockno < cchh2blk(&f1.DS1EXT1.ulimit, geo) + geo->sectors) {
|
||||
return part_count + 1;
|
||||
}
|
||||
part_count++;
|
||||
blk++;
|
||||
}
|
||||
/* No matching partition found */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function parses the bootloader program table stored on the eckd device
|
||||
* and returns the partition index where the dumper's kernel image is stored.
|
||||
*/
|
||||
static int ngdump_get_eckd_part_num(struct zg_fh *zg_fh)
|
||||
{
|
||||
union disk_blockptr dump_image_blkptr;
|
||||
struct eckd_blockptr *blockptr;
|
||||
struct eckd_boot_record br;
|
||||
struct vol_label_cdl vl;
|
||||
struct hd_geometry geo;
|
||||
uint64_t blk, off;
|
||||
cchhb_t *vtoc;
|
||||
int blk_size;
|
||||
|
||||
if (zg_ioctl(zg_fh, BLKSSZGET, &blk_size, "BLKSSZGET", ZG_CHECK_NONE))
|
||||
return -1;
|
||||
util_log_print(UTIL_LOG_TRACE, "%s: Block size %d\n",
|
||||
__func__, blk_size);
|
||||
/* Obtain DASD geometry */
|
||||
if (dasd_get_geo(zg_fh->path, &geo) != 0)
|
||||
return -1;
|
||||
util_log_print(UTIL_LOG_TRACE,
|
||||
"%s: DASD geometry: cyl=%d, heads=%d, sect=%d\n",
|
||||
__func__, geo.cylinders, geo.heads, geo.sectors);
|
||||
/* Read a volume label from CDL-formatted DASD */
|
||||
off = 2 * blk_size;
|
||||
util_log_print(UTIL_LOG_TRACE,
|
||||
"%s: Reading a volume label at offset 0x%016lx\n",
|
||||
__func__, off);
|
||||
zg_seek(zg_fh, off, ZG_CHECK_ERR);
|
||||
zg_read(zg_fh, &vl, sizeof(vl), ZG_CHECK);
|
||||
/* Verify that we have a VOL1 label */
|
||||
if (!is_vol1(vl.vollbl))
|
||||
return -1;
|
||||
/* Read Master Boot Record and check its magic */
|
||||
blk = cchhb2blk(&vl.br, &geo);
|
||||
if (blk == 0)
|
||||
return -1;
|
||||
off = (blk - 1) * blk_size;
|
||||
util_log_print(UTIL_LOG_TRACE,
|
||||
"%s: Reading Master Boot Record at offset 0x%016lx\n",
|
||||
__func__, off);
|
||||
zg_seek(zg_fh, off, ZG_CHECK_ERR);
|
||||
zg_read(zg_fh, &br, sizeof(br), ZG_CHECK);
|
||||
if (memcmp(br.magic, ZIPL_MAGIC, ZIPL_MAGIC_SIZE))
|
||||
return -1;
|
||||
|
||||
blockptr = (struct eckd_blockptr *)&br.program_table_pointer;
|
||||
/*
|
||||
* Cast eckd_blockptr to disk_blockptr before passing it to the function.
|
||||
* With hd_geometry provided, it will be treated as eckd_blockptr later on.
|
||||
*/
|
||||
if (get_bootmap_dump_image_blkptr(zg_fh, (union disk_blockptr *)blockptr,
|
||||
&geo, blk_size, &dump_image_blkptr))
|
||||
return -1;
|
||||
blk = blockptr2blk(&dump_image_blkptr, &geo);
|
||||
if (blk == U64_MAX)
|
||||
return -1;
|
||||
util_log_print(UTIL_LOG_TRACE,
|
||||
"%s: Segment0 block number 0x%016lx\n",
|
||||
__func__, blk);
|
||||
vtoc = &((volume_label_t *)&vl)->vtoc;
|
||||
return find_vol1_cdl_part_fh(zg_fh, blk, blk_size, vtoc, &geo);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function composes the absolute partition device node name
|
||||
* based on the device name, device type and the partition number.
|
||||
*/
|
||||
static int ngdump_get_part_path(const char *disk_path, int part_num,
|
||||
enum ngdump_disk_type ng_type, char **part_path)
|
||||
{
|
||||
char *real_path;
|
||||
|
||||
util_log_print(UTIL_LOG_TRACE, "%s: Disk path %s\n",
|
||||
__func__, disk_path);
|
||||
util_log_print(UTIL_LOG_TRACE, "%s: Disk path %s; disk type: %s\n",
|
||||
__func__, disk_path, ngtype2str[ng_type]);
|
||||
|
||||
real_path = util_malloc(PATH_MAX);
|
||||
|
||||
@@ -315,7 +493,18 @@ int ngdump_get_disk_part_path(const char *disk_path, int part_num,
|
||||
__func__, real_path);
|
||||
|
||||
*part_path = NULL;
|
||||
util_asprintf(part_path, "%sp%d", real_path, part_num);
|
||||
switch (ng_type) {
|
||||
case NG_TYPE_DASD:
|
||||
util_asprintf(part_path, "%s%d", real_path, part_num);
|
||||
break;
|
||||
case NG_TYPE_NVME:
|
||||
util_asprintf(part_path, "%sp%d", real_path, part_num);
|
||||
break;
|
||||
default: /* Unknown type, bail out */
|
||||
free(real_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(real_path);
|
||||
|
||||
util_log_print(UTIL_LOG_TRACE, "%s: Disk partition path %s\n",
|
||||
@@ -323,3 +512,30 @@ int ngdump_get_disk_part_path(const char *disk_path, int part_num,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function checks for the ngdump device type in order to parse the
|
||||
* bootloader program table and identify the partition where the dumper's
|
||||
* kernel image is stored.
|
||||
* part_path is set to the absolute partition device node name and
|
||||
* the partition number is returned (or -1 when no partition found).
|
||||
*/
|
||||
int ngdump_get_dump_part(struct zg_fh *zg_fh, char **part_path)
|
||||
{
|
||||
dasd_information2_t dasd_info;
|
||||
enum ngdump_disk_type ng_type;
|
||||
int part_num;
|
||||
|
||||
if (dasd_get_info(zg_fh->path, &dasd_info) == 0) {
|
||||
ng_type = NG_TYPE_DASD;
|
||||
part_num = ngdump_get_eckd_part_num(zg_fh);
|
||||
} else {
|
||||
ng_type = NG_TYPE_NVME;
|
||||
part_num = ngdump_get_nvme_part_num(zg_fh);
|
||||
}
|
||||
if (part_num <= 0 ||
|
||||
ngdump_get_part_path(zg_fh->path, part_num, ng_type, part_path) < 0)
|
||||
return -1;
|
||||
|
||||
return part_num;
|
||||
}
|
||||
|
||||
@@ -21,10 +21,6 @@ struct ngdump_meta {
|
||||
};
|
||||
|
||||
int ngdump_read_meta_from_device(const char *device, struct ngdump_meta *meta);
|
||||
|
||||
int ngdump_get_dump_part(struct zg_fh *zg_fh);
|
||||
|
||||
int ngdump_get_disk_part_path(const char *disk_path, int part_num,
|
||||
char **part_path);
|
||||
int ngdump_get_dump_part(struct zg_fh *zg_fh, char **part_path);
|
||||
|
||||
#endif /* ZGETDUMP_NGDUMP_H */
|
||||
|
||||
Reference in New Issue
Block a user