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,335 @@
|
||||
/*
|
||||
* zgetdump - Tool for copying and converting System z dumps
|
||||
*
|
||||
* LKCD dump input format
|
||||
*
|
||||
* Copyright IBM Corp. 2001, 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 <zlib.h>
|
||||
#include "zgetdump.h"
|
||||
|
||||
#define MEM_HOLE_SIZE_MIN (1024 * 1024)
|
||||
#define IDX_KIB 64 /* One index entry per IDX_KIB */
|
||||
#define IDX_TO_ADDR(idx) (idx * IDX_KIB * 1024)
|
||||
#define ADDR_TO_IDX(addr) (addr / (1024 * IDX_KIB))
|
||||
|
||||
/*
|
||||
* File local static data
|
||||
*/
|
||||
static struct {
|
||||
u64 *pg_hdr_idx;
|
||||
u64 page_last;
|
||||
struct df_lkcd_hdr hdr;
|
||||
struct df_lkcd_hdr_asm hdr_asm;
|
||||
int dump_full;
|
||||
} l;
|
||||
|
||||
/*
|
||||
* Read LKCD page buffer, either compressed or uncompressed
|
||||
*/
|
||||
static void read_page_buf(struct df_lkcd_pg_hdr *pg_hdr, void *buf)
|
||||
{
|
||||
unsigned long size = PAGE_SIZE;
|
||||
unsigned char cbuf[PAGE_SIZE];
|
||||
|
||||
switch (pg_hdr->flags) {
|
||||
case DF_LKCD_DH_RAW:
|
||||
zg_read(g.fh, buf, pg_hdr->size, ZG_CHECK);
|
||||
break;
|
||||
case DF_LKCD_DH_COMPRESSED:
|
||||
zg_read(g.fh, cbuf, pg_hdr->size, ZG_CHECK);
|
||||
uncompress(buf, &size, cbuf, pg_hdr->size);
|
||||
if (size != PAGE_SIZE)
|
||||
ABORT("Invalid page size: %ld", size);
|
||||
break;
|
||||
default:
|
||||
ERR_EXIT("Unsupported page flags: %x at addr %Lx",
|
||||
pg_hdr->flags, pg_hdr->addr);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read next LKCD page from current file position
|
||||
*
|
||||
* If we find the page, we copy the page content. If the page is not present
|
||||
* we copy zeroes and skip it. If the page address is not yet reached, we just
|
||||
* skip it.
|
||||
*/
|
||||
static int read_next_page(u64 addr, void *buf)
|
||||
{
|
||||
struct df_lkcd_pg_hdr pg_hdr;
|
||||
|
||||
zg_read(g.fh, &pg_hdr, sizeof(pg_hdr), ZG_CHECK);
|
||||
l.page_last = pg_hdr.addr / PAGE_SIZE;
|
||||
if (pg_hdr.addr == addr) {
|
||||
read_page_buf(&pg_hdr, buf);
|
||||
return 0;
|
||||
}
|
||||
if (pg_hdr.addr > addr) {
|
||||
memset(buf, 0, PAGE_SIZE);
|
||||
zg_seek_cur(g.fh, pg_hdr.size, ZG_CHECK);
|
||||
return 0;
|
||||
}
|
||||
zg_seek_cur(g.fh, pg_hdr.size, ZG_CHECK);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read LKCD dump page for flex dump
|
||||
*
|
||||
* If the page after the last read page should be read, we just read
|
||||
* the next one. Otherwise we seek to the beginning of the page cluster
|
||||
* of the page index and search the page there.
|
||||
*/
|
||||
static void read_page_flex(u64 pg_num, void *buf)
|
||||
{
|
||||
u64 addr = pg_num * PAGE_SIZE;
|
||||
|
||||
if (l.pg_hdr_idx[ADDR_TO_IDX(addr)] == 0)
|
||||
ABORT("Dump page index broken");
|
||||
|
||||
if (l.page_last == pg_num - 1) {
|
||||
read_next_page(addr, buf);
|
||||
return;
|
||||
}
|
||||
|
||||
zg_seek(g.fh, l.pg_hdr_idx[ADDR_TO_IDX(addr)], ZG_CHECK);
|
||||
do {
|
||||
if (read_next_page(addr, buf) == 0)
|
||||
break;
|
||||
} while (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read lkcd page for full dump
|
||||
*/
|
||||
static void read_page_full(u64 pg_num, void *buf)
|
||||
{
|
||||
zg_seek(g.fh, DF_LKCD_HDR_SIZE + pg_num * DF_LKCD_UCP_SIZE +
|
||||
sizeof(struct df_lkcd_pg_hdr), ZG_CHECK);
|
||||
zg_read(g.fh, buf, PAGE_SIZE, ZG_CHECK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read lkcd page
|
||||
*/
|
||||
static void read_page(u64 pg_num, void *buf)
|
||||
{
|
||||
if (l.dump_full)
|
||||
read_page_full(pg_num, buf);
|
||||
else
|
||||
read_page_flex(pg_num, buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* LKCD mem chunk read callback
|
||||
*/
|
||||
static void dfi_lkcd_mem_chunk_read_fn(struct dfi_mem_chunk *mem_chunk, u64 off,
|
||||
void *buf, u64 cnt)
|
||||
{
|
||||
u64 copied = 0, size, pg_nr, addr = off + mem_chunk->start;
|
||||
char pg_buf[PAGE_SIZE];
|
||||
unsigned int pg_off;
|
||||
|
||||
while (copied != cnt) {
|
||||
pg_nr = (addr + copied) / PAGE_SIZE;
|
||||
pg_off = (addr + copied) % PAGE_SIZE;
|
||||
size = MIN(cnt - copied, PAGE_SIZE - pg_off);
|
||||
read_page(pg_nr, pg_buf);
|
||||
memcpy(buf + copied, &pg_buf[pg_off], size);
|
||||
copied += size;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Did we find the end of the LCKD dump?
|
||||
*/
|
||||
static int dump_end(u64 addr, struct df_lkcd_pg_hdr *pg_hdr)
|
||||
{
|
||||
if (addr == pg_hdr->addr) {
|
||||
/*
|
||||
* This is a workaroud for a bug in vmconvert,
|
||||
* where instaed of the end marker the last
|
||||
* page was written twice. Sorry for that...
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
if (pg_hdr->addr == 0 && pg_hdr->size == 4 && pg_hdr->flags == 0) {
|
||||
/*
|
||||
* zfcpdump bug (wrong end marker)
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
if (pg_hdr->flags == DF_LKCD_DH_END)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Init memory chunks for full dump
|
||||
*
|
||||
* Full dump: It is not compressed and it does not have any memory holes.
|
||||
*/
|
||||
static int mem_init_full(void)
|
||||
{
|
||||
dfi_mem_chunk_add(0, l.hdr.mem_end, NULL, dfi_lkcd_mem_chunk_read_fn,
|
||||
NULL);
|
||||
l.dump_full = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Init memory chunks for flex dump
|
||||
*
|
||||
* Flex dump: It is compressed and/or it has memory holes.
|
||||
*/
|
||||
static int mem_init_flex(void)
|
||||
{
|
||||
u64 addr = U64_MAX, idx = 0, mem_chunk_start = 0, rc;
|
||||
struct df_lkcd_pg_hdr pg_hdr;
|
||||
int dump_incomplete = 0;
|
||||
|
||||
l.pg_hdr_idx = zg_alloc(sizeof(u64) * (ADDR_TO_IDX(l.hdr.mem_end) + 1));
|
||||
zg_seek(g.fh, DF_LKCD_HDR_SIZE, ZG_CHECK_NONE);
|
||||
zg_progress_init("Analyzing dump", l.hdr.mem_end);
|
||||
do {
|
||||
rc = zg_read(g.fh, &pg_hdr, sizeof(pg_hdr), ZG_CHECK_ERR);
|
||||
if (rc != sizeof(pg_hdr)) {
|
||||
dump_incomplete = 1;
|
||||
break;
|
||||
}
|
||||
if (dump_end(addr, &pg_hdr))
|
||||
break;
|
||||
if (pg_hdr.addr - addr > MEM_HOLE_SIZE_MIN) {
|
||||
dfi_mem_chunk_add(mem_chunk_start,
|
||||
addr + PAGE_SIZE - mem_chunk_start,
|
||||
NULL, dfi_lkcd_mem_chunk_read_fn,
|
||||
NULL);
|
||||
mem_chunk_start = pg_hdr.addr;
|
||||
}
|
||||
addr = pg_hdr.addr;
|
||||
zg_progress(addr);
|
||||
if (addr >= IDX_TO_ADDR(idx)) {
|
||||
idx = ADDR_TO_IDX(addr);
|
||||
l.pg_hdr_idx[idx] = zg_tell(g.fh, ZG_CHECK) -
|
||||
sizeof(pg_hdr);
|
||||
idx++;
|
||||
}
|
||||
zg_seek_cur(g.fh, pg_hdr.size, ZG_CHECK);
|
||||
} while (1);
|
||||
|
||||
if (addr != mem_chunk_start) {
|
||||
dfi_mem_chunk_add(mem_chunk_start,
|
||||
l.hdr.mem_end - mem_chunk_start,
|
||||
NULL, dfi_lkcd_mem_chunk_read_fn, NULL);
|
||||
}
|
||||
zg_progress(l.hdr.mem_end);
|
||||
if (g.opts.action != ZG_ACTION_MOUNT)
|
||||
fprintf(stderr, "\n");
|
||||
if (dump_incomplete)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do we have a full dump?
|
||||
*/
|
||||
static int is_full_dump(void)
|
||||
{
|
||||
u64 full_size;
|
||||
int pages;
|
||||
|
||||
if (l.hdr.dump_compress != DF_LKCD_COMPRESS_NONE)
|
||||
return 0;
|
||||
pages = l.hdr.mem_end / PAGE_SIZE;
|
||||
full_size = DF_LKCD_HDR_SIZE + pages * DF_LKCD_UCP_SIZE +
|
||||
sizeof(struct df_lkcd_pg_hdr);
|
||||
if (zg_size(g.fh) != full_size)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Init memory chunks
|
||||
*/
|
||||
static int mem_init(void)
|
||||
{
|
||||
if (is_full_dump())
|
||||
return mem_init_full();
|
||||
else
|
||||
return mem_init_flex();
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize CPU information
|
||||
*/
|
||||
static void cpu_init(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (l.hdr_asm.magic != DF_LKCD_MAGIC_ASM) {
|
||||
/* Old LKCD dump without asm header */
|
||||
dfi_cpu_info_init(DFI_CPU_CONTENT_NONE);
|
||||
return;
|
||||
}
|
||||
|
||||
dfi_cpu_info_init(DFI_CPU_CONTENT_ALL);
|
||||
for (i = 0; i < l.hdr_asm.cpu_cnt; i++)
|
||||
dfi_cpu_add_from_lc(l.hdr_asm.lc_vec[i]);
|
||||
}
|
||||
|
||||
/*
|
||||
* Read LKCD dump header and dump asm header
|
||||
*/
|
||||
static int read_lkcd_hdr(void)
|
||||
{
|
||||
if (zg_size(g.fh) < DF_LKCD_HDR_SIZE)
|
||||
return -ENODEV;
|
||||
|
||||
/* Read dump header */
|
||||
zg_read(g.fh, &l.hdr, sizeof(l.hdr), ZG_CHECK);
|
||||
|
||||
if (l.hdr.magic != DF_LKCD_MAGIC)
|
||||
return -ENODEV;
|
||||
|
||||
/* Read asm header */
|
||||
zg_seek(g.fh, l.hdr.hdr_size, ZG_CHECK);
|
||||
zg_read(g.fh, &l.hdr_asm, sizeof(l.hdr_asm), ZG_CHECK);
|
||||
if (strncmp(l.hdr.utsname_machine, "s390x", sizeof("s390x")) == 0)
|
||||
dfi_arch_set(DFI_ARCH_64);
|
||||
else if (strncmp(l.hdr.utsname_machine, "s390", sizeof("s390")) == 0)
|
||||
dfi_arch_set(DFI_ARCH_32);
|
||||
else
|
||||
ERR_EXIT("Dump architecture \"%s\" is not supported",
|
||||
l.hdr.utsname_machine);
|
||||
if (l.hdr_asm.magic == DF_LKCD_MAGIC_ASM)
|
||||
dfi_attr_real_cpu_cnt_set(l.hdr_asm.real_cpu_cnt);
|
||||
dfi_attr_version_set(l.hdr.version);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize LKCD DFI
|
||||
*/
|
||||
static int dfi_lkcd_init(void)
|
||||
{
|
||||
if (read_lkcd_hdr() != 0)
|
||||
return -ENODEV;
|
||||
if (mem_init() != 0)
|
||||
return -EINVAL;
|
||||
cpu_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* LKCD DFI operations
|
||||
*/
|
||||
struct dfi dfi_lkcd = {
|
||||
.name = "lkcd",
|
||||
.init = dfi_lkcd_init,
|
||||
.feat_bits = DFI_FEAT_COPY | DFI_FEAT_SEEK,
|
||||
};
|
||||
Reference in New Issue
Block a user