mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
- Initialize dump and dump-tool architecture to DFI_ARCH_64 at the start of dfi_init() and dt_init() respectively. - Bail out if any dump architecture other than ARCH_64 has been detected in s390_ext or s390mv_ext DASD dump header. - Remove redundant dfi_arch_set() and dt_arch_set() functions. - Get rid of l.arch local variables in dfi* and dt* source files and drop dfi_arch() function. - Drop the usage of DFI_ARCH_32 and compeletely remove DFI_ARCH_UNKNOWN. - Drop special register and lowcore processing functions used for DFI_ARCH_32. - Drop df_s390_from_dfi_arch() and df_s390_to_dfi_arch() funcitons. - Update the man file for zgetdump. 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>
505 lines
16 KiB
C
505 lines
16 KiB
C
/*
|
|
* zgetdump - Tool for copying and converting IBM zSystem dumps
|
|
*
|
|
* VMDUMP input format - Convert a vmdump 64big format to internal format.
|
|
*
|
|
* Copyright IBM Corp. 2023
|
|
*
|
|
* 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 <ctype.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <iconv.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/mtio.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <zlib.h>
|
|
|
|
#include "zgetdump.h"
|
|
#include "df_vmdump.h"
|
|
#include "dfi.h"
|
|
#include "dfi_mem_chunk.h"
|
|
#include "lib/util_log.h"
|
|
#include "lib/util_libc.h"
|
|
|
|
static struct {
|
|
struct vmd_adsr adsr; /* Dump file symptom record */
|
|
struct vmd_fmbk fmbk; /* Dump file map record */
|
|
struct vmd_fir_basic fir_basic; /* Dump file info record */
|
|
struct vmd_albk albk; /* Dump access list record */
|
|
|
|
struct vmd_asizbk asizbk;
|
|
struct vmd_fir_64 fir;
|
|
struct vmd_fir_other_64 *fir_other;
|
|
|
|
u64 memory_start_record;
|
|
u64 index_pages_count;
|
|
u64 bitkey_pages_count;
|
|
u64 total_pages_count;
|
|
u64 stored_pages_count;
|
|
u8 *bitmap;
|
|
} l;
|
|
|
|
/* Convert EBCDIC to ASCII. Used for very few selective fields. */
|
|
static void ebc_2_asc(u8 *in, u8 *out, const size_t size)
|
|
{
|
|
iconv_t etoa = iconv_open("ISO-8859-1", "EBCDIC-US");
|
|
size_t size_out = size;
|
|
size_t size_in = size;
|
|
size_t rc;
|
|
|
|
rc = iconv(etoa, (char **)&in, &size_in, (char **)&out, &size_out);
|
|
if (rc == (size_t)-1)
|
|
errx(EXIT_FAILURE, "Code page translation EBCDIC-ASCII failed");
|
|
iconv_close(etoa);
|
|
}
|
|
|
|
static void vmdump_tod_to_timeval(u64 todval, struct timeval *xtime)
|
|
{
|
|
/* adjust todclock to 1970 */
|
|
todval -= 0x8126d60e46000000LL - (0x3c26700LL * 1000000 * 4096);
|
|
|
|
todval >>= 12;
|
|
xtime->tv_sec = todval / 1000000;
|
|
xtime->tv_usec = todval % 1000000;
|
|
}
|
|
|
|
static void vmdump64big_debug(void)
|
|
{
|
|
u8 fmbk_id[sizeof(FMBK_MAGIC)];
|
|
u8 albk_id[sizeof(ALBK_MAGIC)];
|
|
struct timeval time;
|
|
unsigned int i;
|
|
|
|
util_log_print(UTIL_LOG_DEBUG, "Highest Defined Byte (vmcp q v storage): %#lx\n",
|
|
l.asizbk.storage_size_def_store);
|
|
util_log_print(UTIL_LOG_DEBUG, "Requested Memory Ranges:\n");
|
|
for (i = 0; i < l.asizbk.range_table_entry_count; i++)
|
|
util_log_print(UTIL_LOG_DEBUG, " %#lx - %#lx\n",
|
|
l.asizbk.requested_range_table[i].start_addr,
|
|
l.asizbk.requested_range_table[i].end_addr);
|
|
util_log_print(UTIL_LOG_DEBUG, "Dump Memory Size: %#lx\n", l.total_pages_count * PAGE_SIZE);
|
|
util_log_print(UTIL_LOG_DEBUG, "Index Pages : %lu\n", l.index_pages_count);
|
|
util_log_print(UTIL_LOG_DEBUG, "Bit-Key Pages: %lu\n", l.bitkey_pages_count);
|
|
util_log_print(UTIL_LOG_DEBUG, "Memory Offset: %#lx\n", l.memory_start_record);
|
|
util_log_print(UTIL_LOG_DEBUG, "Total Pages : %lu\n", l.total_pages_count);
|
|
util_log_print(UTIL_LOG_DEBUG, "Stored Pages : %lu\n", l.stored_pages_count);
|
|
|
|
/* adsr */
|
|
vmdump_tod_to_timeval(l.adsr.tod, &time);
|
|
util_log_print(UTIL_LOG_DEBUG, "Time: %s", ctime(&time.tv_sec));
|
|
util_log_print(UTIL_LOG_DEBUG, "Statusflag 1: %#x\n", l.adsr.record_status_flag1);
|
|
util_log_print(UTIL_LOG_DEBUG, "Statusflag 2: %#x\n", l.adsr.record_status_flag2);
|
|
util_log_print(UTIL_LOG_DEBUG, "Section 2 len: %i\n", l.adsr.sec2_len);
|
|
util_log_print(UTIL_LOG_DEBUG, "Section 2.1 len: %i/%i\n", l.adsr.sec2_1_len,
|
|
l.adsr.sec2_1_offset);
|
|
util_log_print(UTIL_LOG_DEBUG, "Section 3 len: %i/%i\n", l.adsr.sec3_len,
|
|
l.adsr.sec3_offset);
|
|
util_log_print(UTIL_LOG_DEBUG, "Section 4 len: %i/%i\n", l.adsr.sec4_len,
|
|
l.adsr.sec4_offset);
|
|
util_log_print(UTIL_LOG_DEBUG, "Section 5 len: %i/%i\n", l.adsr.sec5_len,
|
|
l.adsr.sec5_offset);
|
|
util_log_print(UTIL_LOG_DEBUG, "Section 6 len: %i/%i\n", l.adsr.sec6_len,
|
|
l.adsr.sec6_offset);
|
|
|
|
/* fmbk */
|
|
ebc_2_asc(l.fmbk.id, fmbk_id, sizeof(l.fmbk.id));
|
|
fmbk_id[sizeof(fmbk_id) - 1] = 0;
|
|
util_log_print(UTIL_LOG_DEBUG, "Fmbk id: %8.8s\n", fmbk_id);
|
|
util_log_print(UTIL_LOG_DEBUG, "Fir rec nr: %i\n", l.fmbk.rec_nr_fir);
|
|
util_log_print(UTIL_LOG_DEBUG, "Vec rec nr: %i\n", l.fmbk.rec_nr_vector);
|
|
util_log_print(UTIL_LOG_DEBUG, "Access rec nr: %i\n", l.fmbk.rec_nr_access);
|
|
|
|
/* albk */
|
|
ebc_2_asc(l.albk.id, albk_id, sizeof(l.albk.id));
|
|
albk_id[sizeof(albk_id) - 1] = 0;
|
|
util_log_print(UTIL_LOG_DEBUG, "Albk id: %8.8s\n", albk_id);
|
|
|
|
/* fir */
|
|
util_log_print(UTIL_LOG_DEBUG, "Cpus: %d\n", l.fir.online_cpus + 1);
|
|
util_log_print(UTIL_LOG_DEBUG, "PSW: %#016lx %#016lx\n", l.fir.psw[0], l.fir.psw[1]);
|
|
util_log_print(UTIL_LOG_DEBUG, "Prefix (CPU 0): %#010x\n", l.fir.prefix);
|
|
for (i = 0; i < l.fir.online_cpus; i++)
|
|
util_log_print(UTIL_LOG_DEBUG, "Prefix (CPU %i): %#010x\n", i + 1,
|
|
l.fir_other[i].prefix);
|
|
}
|
|
|
|
static bool test_bitmap_key_page(u8 *page, const u64 bit)
|
|
{
|
|
return page[bit] & 0x01;
|
|
}
|
|
|
|
static bool test_page_bit(u8 *bitmap, const u64 bit)
|
|
{
|
|
return bitmap[bit / 8] & (1 << (7 - (bit % 8)));
|
|
}
|
|
|
|
static void set_page_bit(u8 *bitmap, const u64 bit)
|
|
{
|
|
bitmap[bit / 8] |= (1 << (7 - (bit % 8)));
|
|
}
|
|
|
|
static u64 get_total_pages_count(void)
|
|
{
|
|
unsigned int i;
|
|
u64 addr = 0;
|
|
|
|
for (i = 0; i < l.asizbk.range_table_entry_count; i++)
|
|
addr = MAX(addr, l.asizbk.requested_range_table[i].end_addr);
|
|
/*
|
|
* Range end address points to the last byte of the page, hence +1 is required.
|
|
* If range table entries are missing for some reason, use the full amount of
|
|
* defined machine storage.
|
|
*/
|
|
addr = addr ? (addr + 1) : l.asizbk.storage_size_def_store;
|
|
return addr / PAGE_SIZE;
|
|
}
|
|
|
|
/*
|
|
* Read VMDUMP headers, page index bit maps and page bit maps to
|
|
* construct a list of non-zero pages with memory locations. Zeroed pages
|
|
* are not bitmapped and are detected by memory location without bitmap
|
|
* entry.
|
|
*/
|
|
static void vmdump64big_init(void)
|
|
{
|
|
u64 page_num = 0;
|
|
size_t bitmap_sz;
|
|
unsigned int i;
|
|
|
|
/* Record 1: adsr */
|
|
zg_seek(g.fh, 0, ZG_CHECK);
|
|
zg_read(g.fh, &l.adsr, sizeof(l.adsr), ZG_CHECK);
|
|
|
|
if (g.opts.verbose) {
|
|
u8 buf_asc[1024], buf[1024];
|
|
|
|
zg_seek(g.fh, l.adsr.sec5_offset, ZG_CHECK);
|
|
zg_read(g.fh, buf, l.adsr.sec5_len, ZG_CHECK);
|
|
ebc_2_asc(buf, buf_asc, l.adsr.sec5_len);
|
|
for (i = 0; i < l.adsr.sec5_len; i++) {
|
|
if (buf_asc[i] == 0 || iscntrl(buf_asc[i]))
|
|
buf_asc[i] = ' ';
|
|
}
|
|
buf_asc[l.adsr.sec5_len] = 0;
|
|
util_log_print(UTIL_LOG_DEBUG, "Symptom string: %s\n", buf_asc);
|
|
}
|
|
|
|
/* Record 2: fmbk */
|
|
zg_seek(g.fh, PAGE_SIZE, ZG_CHECK);
|
|
zg_read(g.fh, &l.fmbk, sizeof(l.fmbk), ZG_CHECK);
|
|
|
|
/* Record 3-7: fir records */
|
|
zg_seek(g.fh, (l.fmbk.rec_nr_fir - 1) * PAGE_SIZE, ZG_CHECK);
|
|
zg_read(g.fh, &l.fir, sizeof(l.fir), ZG_CHECK);
|
|
|
|
bitmap_sz = sizeof(struct vmd_fir_other_64) * l.fir.online_cpus;
|
|
l.fir_other = util_zalloc(bitmap_sz);
|
|
for (i = 0; i < l.fir.online_cpus; i++)
|
|
zg_read(g.fh, &l.fir_other[i], sizeof(l.fir_other[0]), ZG_CHECK);
|
|
|
|
/* Record 8: albk */
|
|
zg_seek(g.fh, (l.fmbk.rec_nr_access - 1) * PAGE_SIZE, SEEK_SET);
|
|
zg_read(g.fh, &l.albk, sizeof(l.albk), SEEK_SET);
|
|
|
|
/* Record 9: asizbk */
|
|
zg_seek(g.fh, l.fmbk.rec_nr_access * PAGE_SIZE, ZG_CHECK);
|
|
zg_read(g.fh, &l.asizbk, sizeof(l.asizbk), ZG_CHECK);
|
|
if (memcmp(l.asizbk.id, ASIZBK_MAGIC, sizeof(l.asizbk.id)))
|
|
ERR_EXIT("Dump file inconsistent, invalid ASIZBK record detected");
|
|
|
|
l.memory_start_record = (l.fmbk.rec_nr_access + 1) * PAGE_SIZE;
|
|
|
|
/*
|
|
* Record 10: bitmaps:
|
|
* Read all bitmap pages and setup bitmap array
|
|
*/
|
|
l.total_pages_count = get_total_pages_count();
|
|
bitmap_sz = ROUNDUP(l.total_pages_count, 8) / 8;
|
|
if (!bitmap_sz)
|
|
ERR_EXIT("Dump file inconsistent, no bitmap detected");
|
|
l.bitmap = util_zalloc(bitmap_sz);
|
|
zg_seek(g.fh, (l.fmbk.rec_nr_access + 1) * PAGE_SIZE, ZG_CHECK);
|
|
|
|
do {
|
|
u8 bm_index_page[PAGE_SIZE];
|
|
|
|
zg_read(g.fh, bm_index_page, sizeof(bm_index_page), ZG_CHECK);
|
|
l.memory_start_record += PAGE_SIZE;
|
|
l.index_pages_count++;
|
|
for (i = 0; i < 8 * PAGE_SIZE; i++) {
|
|
if (test_page_bit(bm_index_page, i)) {
|
|
u8 bm_page[PAGE_SIZE];
|
|
|
|
zg_read(g.fh, bm_page, sizeof(bm_page), ZG_CHECK);
|
|
l.memory_start_record += PAGE_SIZE;
|
|
l.bitkey_pages_count++;
|
|
for (unsigned int j = 0; j < PAGE_SIZE; j++) {
|
|
if (page_num / 8 >= bitmap_sz)
|
|
ERR_EXIT("Dump file inconsistent,"
|
|
" corrupted bitmap detected");
|
|
if (test_bitmap_key_page(bm_page, j)) {
|
|
set_page_bit(l.bitmap, page_num);
|
|
l.stored_pages_count++;
|
|
}
|
|
page_num++;
|
|
if (page_num == l.total_pages_count)
|
|
goto out;
|
|
}
|
|
} else {
|
|
page_num += PAGE_SIZE; /* Empty page */
|
|
}
|
|
}
|
|
|
|
} while (page_num < l.total_pages_count);
|
|
|
|
out:
|
|
vmdump64big_debug();
|
|
}
|
|
|
|
static void display_register(const struct dfi_cpu *cpu)
|
|
{
|
|
unsigned int i;
|
|
|
|
util_log_print(UTIL_LOG_TRACE, "CPU %d\n", cpu->cpu_id);
|
|
for (i = 0; i < ARRAY_SIZE(cpu->gprs); i += 2)
|
|
util_log_print(UTIL_LOG_TRACE, "gpr%02d: %016lx\t gpr%02d: %016lx\n", i,
|
|
cpu->gprs[i], i + 1, cpu->gprs[i + 1]);
|
|
for (i = 0; i < ARRAY_SIZE(cpu->ctrs); i += 2)
|
|
util_log_print(UTIL_LOG_TRACE, "ctr%02d: %016lx\t ctr%02d: %016lx\n", i,
|
|
cpu->ctrs[i], i + 1, cpu->ctrs[i + 1]);
|
|
for (i = 0; i < ARRAY_SIZE(cpu->acrs); i += 2)
|
|
util_log_print(UTIL_LOG_TRACE, "acr%02d: %016lx\t acr%02d: %016lx\n", i,
|
|
cpu->acrs[i], i + 1, cpu->acrs[i + 1]);
|
|
for (i = 0; i < ARRAY_SIZE(cpu->fprs); i += 2)
|
|
util_log_print(UTIL_LOG_TRACE, "fpr%02d: %016lx\t fpr%02d: %016lx\n", i,
|
|
cpu->fprs[i], i + 1, cpu->fprs[i + 1]);
|
|
}
|
|
|
|
/*
|
|
* Read out register setting for each CPU.
|
|
*/
|
|
static void vmdump_read_register(int cpu_nr)
|
|
{
|
|
struct dfi_cpu *cpu;
|
|
|
|
if (cpu_nr >= l.fir.online_cpus + 1)
|
|
return;
|
|
|
|
cpu = dfi_cpu_alloc();
|
|
cpu->cpu_id = dfi_cpu_cnt();
|
|
if (!cpu_nr) { /* First CPU in fir */
|
|
memcpy(cpu->gprs, l.fir.gprs, sizeof(cpu->gprs));
|
|
memcpy(cpu->ctrs, &l.fir.crs, sizeof(cpu->ctrs));
|
|
memcpy(cpu->acrs, &l.fir.acrs, sizeof(cpu->acrs));
|
|
memcpy(cpu->fprs, &l.fir.fprs, sizeof(cpu->fprs));
|
|
memcpy(cpu->psw, &l.fir.psw, sizeof(cpu->psw));
|
|
memcpy(&cpu->prefix, &l.fir.prefix, sizeof(cpu->prefix));
|
|
memcpy(&cpu->timer, &l.fir.cpu_timer, sizeof(cpu->timer));
|
|
memcpy(&cpu->todcmp, &l.fir.clock_cmp, sizeof(cpu->todcmp));
|
|
memcpy(&cpu->fpc, &l.fir.fp_cntrl_reg, sizeof(cpu->fpc));
|
|
} else {
|
|
cpu_nr -= 1; /* Other CPUs start at offset 0 in fir_other */
|
|
memcpy(cpu->gprs, l.fir_other[cpu_nr].gprs, sizeof(cpu->gprs));
|
|
memcpy(cpu->ctrs, &l.fir_other[cpu_nr].crs, sizeof(cpu->ctrs));
|
|
memcpy(cpu->acrs, &l.fir_other[cpu_nr].acrs, sizeof(cpu->acrs));
|
|
memcpy(cpu->fprs, &l.fir_other[cpu_nr].fprs, sizeof(cpu->fprs));
|
|
memcpy(cpu->psw, &l.fir_other[cpu_nr].psw, sizeof(cpu->psw));
|
|
memcpy(&cpu->prefix, &l.fir_other[cpu_nr].prefix, sizeof(cpu->prefix));
|
|
memcpy(&cpu->timer, &l.fir_other[cpu_nr].cpu_timer, sizeof(cpu->timer));
|
|
memcpy(&cpu->todcmp, &l.fir_other[cpu_nr].clock_cmp, sizeof(cpu->todcmp));
|
|
memcpy(&cpu->fpc, &l.fir_other[cpu_nr].fp_cntrl_reg, sizeof(cpu->fpc));
|
|
}
|
|
display_register(cpu);
|
|
dfi_cpu_add(cpu);
|
|
}
|
|
|
|
/*
|
|
* Initialize z/VM VMDUMP file DFI. Check if the input file is vmdump file
|
|
* with format 64big. Other formats are not supported.
|
|
*/
|
|
static int read_vmdump_hdr(void)
|
|
{
|
|
if (zg_type(g.fh) != ZG_TYPE_FILE)
|
|
return -EBADF;
|
|
if (zg_size(g.fh) < DF_VMDUMP_HDR_SIZE)
|
|
return -ENODEV;
|
|
zg_read(g.fh, &l.adsr, sizeof(l.adsr), ZG_CHECK);
|
|
if (memcmp(l.adsr.sr, ADSR_MAGIC, sizeof(l.adsr.sr)))
|
|
return -EBADF; /* Not an ADSR record */
|
|
if (memcmp(l.adsr.dump_type, VMDUMP_MAGIC, sizeof(l.adsr.dump_type)))
|
|
return -EBADF; /* Not a vmdump */
|
|
zg_seek(g.fh, PAGE_SIZE, ZG_CHECK);
|
|
zg_read(g.fh, &l.fmbk, sizeof(l.fmbk), ZG_CHECK);
|
|
if (memcmp(l.fmbk.id, FMBK_MAGIC, sizeof(l.fmbk.id)))
|
|
return -EBADF; /* Not a FMBK record */
|
|
|
|
/* Record 3-7: fir */
|
|
zg_seek(g.fh, (l.fmbk.rec_nr_fir - 1) * PAGE_SIZE, ZG_CHECK);
|
|
zg_read(g.fh, &l.fir, sizeof(l.fir), ZG_CHECK);
|
|
switch (l.fir.fir_format) {
|
|
case 0x0:
|
|
case 0x82:
|
|
util_log_print(UTIL_LOG_DEBUG, "Vmdump %d bit format not supported anymore\n",
|
|
l.fir.fir_format ? 64 : 32);
|
|
return -EBADF;
|
|
case 0x2:
|
|
util_log_print(UTIL_LOG_DEBUG, "%s: Vmdump 64big format\n", g.opts.device);
|
|
break;
|
|
default:
|
|
util_log_print(UTIL_LOG_DEBUG, "Vmdump unknown format\n");
|
|
return -EBADF;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Read vmdump page. Need to check if page was dumped or is located in a
|
|
* hole. Holes are ranges of pages full of zeroes.
|
|
* The vmdump file format has a 64KB header followed by some bitmaps for
|
|
* valid non-zero pages and the 4KB memory pages itself. Only non-zero pages
|
|
* are stored. The memory pages start at vmdump file location stored in
|
|
* member named memory_start_record.
|
|
*
|
|
* Bit map organization:
|
|
* Page 0: bitmap byte 0 bit 0 (most signification bit)
|
|
* Page 1: bitmap byte 0 bit 1 (seconds most signification bit)
|
|
* ...
|
|
* Page 15: bitmap byte 1 bit 7 (least signification bit)
|
|
*
|
|
* If a bit for a page is set, this page is stored in the vmdump file.
|
|
* The location in the file depends on the number of previous non-zero pages.
|
|
* If bitmap byte zero has value 1 and bitmap byte one has value 5, three
|
|
* bits are set and page 15 is located at file offset:
|
|
* memory_start_record + 2 * PAGE_SIZE.
|
|
*
|
|
* Calculate the number of bits set to determine the file location of a
|
|
* given page in the vmdump file.
|
|
*/
|
|
static unsigned int count_bits(u8 x)
|
|
{
|
|
unsigned int bits_set = 0;
|
|
|
|
while (x) {
|
|
bits_set += x & 1;
|
|
x >>= 1;
|
|
}
|
|
return bits_set;
|
|
}
|
|
|
|
/*
|
|
* Return byte offset into vmdump file for a given page number.
|
|
*/
|
|
static u64 bitmap_2_fileoffset(const u64 pg_num)
|
|
{
|
|
unsigned int bytes_to_check = pg_num / 8;
|
|
unsigned int bits_to_check = pg_num % 8;
|
|
unsigned int bits_set = 0;
|
|
|
|
/* Count bits set in first to second last byte */
|
|
for (unsigned int i = 0; i < bytes_to_check; i++)
|
|
bits_set += count_bits(l.bitmap[i]);
|
|
|
|
/* Count bits set in last byte */
|
|
bits_set += count_bits(l.bitmap[bytes_to_check] >> (8 - bits_to_check));
|
|
return bits_set * PAGE_SIZE;
|
|
}
|
|
|
|
static void read_page(const u64 pg_num, void *buf)
|
|
{
|
|
if (test_page_bit(l.bitmap, pg_num)) {
|
|
u64 file_off = bitmap_2_fileoffset(pg_num);
|
|
|
|
zg_seek(g.fh, l.memory_start_record + file_off, ZG_CHECK);
|
|
zg_read(g.fh, buf, PAGE_SIZE, ZG_CHECK);
|
|
} else {
|
|
memset(buf, 0, PAGE_SIZE);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* VMDUMP mem chunk read callback
|
|
*/
|
|
static void dfi_vmdump_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;
|
|
}
|
|
}
|
|
|
|
static void cpu_init(void)
|
|
{
|
|
dfi_cpu_info_init(DFI_CPU_CONTENT_ALL);
|
|
for (unsigned int i = 0; i <= l.fir.online_cpus; i++)
|
|
vmdump_read_register(i);
|
|
}
|
|
|
|
/*
|
|
* Walk bitmap and find consecutive bitstring consisting of '1' or '0'.
|
|
* Return its length.
|
|
*/
|
|
static u64 find_bitrange(u8 *bitmap, const u64 bit, const u64 max, const bool isset)
|
|
{
|
|
u64 pos;
|
|
|
|
for (pos = bit + 1; pos < max && test_page_bit(bitmap, pos) == isset; pos++)
|
|
;
|
|
return pos - bit;
|
|
}
|
|
|
|
static void mem_init(void)
|
|
{
|
|
u64 pos, more;
|
|
|
|
for (pos = 0; pos < l.total_pages_count;) {
|
|
bool isset = test_page_bit(l.bitmap, pos);
|
|
|
|
more = find_bitrange(l.bitmap, pos, l.total_pages_count, isset);
|
|
dfi_mem_chunk_add(pos * PAGE_SIZE, more * PAGE_SIZE, NULL,
|
|
isset ? dfi_vmdump_mem_chunk_read_fn : dfi_mem_chunk_read_zero,
|
|
NULL);
|
|
pos += more;
|
|
}
|
|
}
|
|
|
|
static int dfi_vmdump_init(void)
|
|
{
|
|
if (read_vmdump_hdr())
|
|
return -ENODEV;
|
|
|
|
vmdump64big_init();
|
|
dfi_attr_version_set(l.fir.fir_format);
|
|
dfi_attr_real_cpu_cnt_set(l.fir.online_cpus + 1);
|
|
mem_init();
|
|
cpu_init();
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* z/VM VMDUMP DFI operations
|
|
*/
|
|
struct dfi dfi_vmdump = {
|
|
.name = "vmdump",
|
|
.init = dfi_vmdump_init,
|
|
.feat_bits = DFI_FEAT_SEEK | DFI_FEAT_COPY,
|
|
};
|