diff --git a/zdump/Makefile b/zdump/Makefile index 934dc47f..6538ef80 100644 --- a/zdump/Makefile +++ b/zdump/Makefile @@ -76,7 +76,7 @@ OBJECTS = zgetdump.o opts.o zg.o zg_error.o zg_print.o \ dt.o dt_s390sv.o dt_s390sv_ext.o \ dt_s390mv.o dt_s390mv_ext.o \ dt_scsi.o output.o \ - ngdump.o dt_ngdump.o dfi_ngdump.o \ + ngdump.o dt_ngdump.o dfi_ngdump.o dfi_vmdump.o \ pv_utils.o ifneq ($(shell sh -c 'command -v pkg-config'),) diff --git a/zdump/df_vmdump.h b/zdump/df_vmdump.h new file mode 100644 index 00000000..0d79a751 --- /dev/null +++ b/zdump/df_vmdump.h @@ -0,0 +1,208 @@ +/* + * zgetdump - Tool for copying and converting System z dumps + * + * VMDUMP format definitions + * + * 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. + */ + +#ifndef DF_VMDUMP_H +#define DF_VMDUMP_H + +/* + * The format of an vmdump + * + * Symptom Record: (ADSR COPY) Record 1 + * Dump File Map Record: (HCPDFMBK COPY) Record 2 + * Dump File Info Record: (HCPDFIR COPY) Records 3-7 + * Vector Registers: (optional) + * Access Lists (HCPDALBK): + * Address Space A: Information and Map Record (HCPASIBK) + * Address Space A: Bit Maps + * Address Space A: Key Maps + * Address Space A: Guest Storage + * Additional Address Spaces (not in linux): + * - ASIBK + * - Bit Maps + * - Key Maps + * - Guest Storage + */ + +/* + * Defines for VMDUMP magic numbers. + */ +/* ID 0xe2d9 --> 'S' 'R' */ +const u8 ADSR_MAGIC[2] = { 0xe2, 0xd9 }; +/* ID e5d4c4e4d4d74040 --> 'V' 'M' 'D' 'U' 'M' 'P' ' ' ' ' */ +const u8 VMDUMP_MAGIC[8] = { 0xe5, 0xd4, 0xc4, 0xe4, 0xd4, 0xd7, 0x40, 0x40 }; +/* ID c8c3d7c4c6d4c2d3 --> 'H' 'C' 'P' 'D' 'F' 'M' 'B' 'K' */ +const u8 FMBK_MAGIC[8] = { 0xc8, 0xc3, 0xd7, 0xc4, 0xc6, 0xd4, 0xc2, 0xd2 }; +/* ID c8c3d7c4c6d4c2d3 --> 'H' 'C' 'P' 'D' 'A' 'L' 'B' 'K' */ +const u8 ALBK_MAGIC[8] = { 0xc8, 0xc3, 0xd7, 0xc4, 0xc1, 0xd3, 0xc2, 0xd2 }; + +/* + * Layout of dump symptom record . The ADSR is always the first record in + * a dump file. Its size varies between 200 bytes and 4000 bytes + */ +struct vmd_adsr { + /* Section 1*/ + u8 sr[sizeof(ADSR_MAGIC)]; + u32 cpu_model; + u8 cpu_serial[6]; + u32 time_zone_conversion_factor; + u64 tod; + u8 time_stamp_str[4]; + u8 date_str[6]; + u8 node_name[8]; + u8 product_id[4]; + u8 feature_level[8]; + u8 record_status_flag1; + u8 record_status_flag2; + u8 dump_type[sizeof(VMDUMP_MAGIC)]; + + /* Section 2*/ + u8 arch_level[2]; + u16 sec2_len; + u16 sec2_1_len; + u16 sec2_1_offset; + u16 sec3_len; + u16 sec3_offset; + u16 sec4_len; + u16 sec4_offset; + u16 sec5_len; + u16 sec5_offset; + u16 sec6_len; + u16 sec6_offset; +} __packed; + +/* + * Layout of Dump file map record. The DFMBK is always the second record in + * a dump file. Its size is 4 KB + */ +struct vmd_fmbk { + u8 id[sizeof(FMBK_MAGIC)]; + u32 rec_nr_fir; + u32 rec_nr_vector; + u32 rec_nr_access; + u32 num_acc_recs; + u32 num_addr_spaces; + u32 rec_nr_asibk; +} __packed; + +struct vmd_fir_basic { /* Dump file (basic) information record */ + u8 filler1[15]; + u8 dump_format; /* 0x1 --> big storage dump + * 0x2 --> cp hard abend + * 0x3 --> cp soft abend + */ + u8 filler2[171]; + u8 fir_format; /* 0x02 --> big esame (only one supported) + * 0x82 --> esame + * 0x00 --> esa + */ +} __packed; + +struct vmd_albk { + u8 id[sizeof(ALBK_MAGIC)]; +} __packed; + +struct vmd_asibk_64_new { + u8 id[8]; + u8 as_token[8]; + u8 spaceid[33]; + u8 reserved1[2]; + u8 asibk_format; + u8 filler1[12]; + u64 storage_size_with_dcss; + u64 storage_size_def_store; + u8 filler2[136]; + u64 online_storage_table[8]; /* For "def store config" */ + u64 fence1; + u64 requested_range_table[8]; + u64 fence2; + u32 record_number_of_first_bit_map; +} __packed; + +struct vmd_fir_64 { + u8 id[8]; + u64 reserved1; + u64 gprs[16]; + u32 prefix; + u8 reserved2[5]; + u64 tod; + u8 reserved3[8]; + u64 cpu_timer; + u8 reserved4[7]; + u8 flag; + u8 type; + u8 complete; + u8 fir_format; /* 0x82 for esame - 0x00 for esa */ + u8 cont_flags; + u8 crypto_domain_index_reg; + u8 virt_cpu_info; + u8 arch_mode_id; + u64 psw[2]; + u64 crs[16]; + u64 fprs[16]; + u8 reserved5; + u64 clock_cmp; + u8 reserved6[3]; + u32 tod_programmable_reg; + u32 reserved_for_dvf[20]; + u32 acrs[16]; + u32 storage_size_2GB; + u32 reserved7; + u32 hcpsys_addr; + u32 reserved8; + u64 storage_size; + u32 snap_area_map_blk; + u32 reserved9; + u8 loc_mem[256]; + u16 online_cpus; + u16 cpu_addr; + u16 section_size_vector; + u16 reserved10; + u8 asit_primary[8]; + u8 space_id_primary[33]; + u8 reserved12[3]; + u16 crypto_domain_index_mask; + u16 reserved11; + u32 fp_cntrl_reg; + u32 reserved13; + u64 reserved14[16]; +} __packed; + +struct vmd_fir_other_64 { + u16 cpu_addr; + u16 vector_sec_size; + u8 crypto_index_reg; + u8 virt_cpu_info; + u16 crypto_index_mask; + u32 reserved1[2]; + u64 fprs[16]; + u64 gprs[16]; + u64 psw[2]; + u32 reserved2[2]; + u32 prefix; + u32 fp_cntrl_reg; + u32 reserved3; + u32 tod; + u64 cpu_timer; + u64 clock_cmp; + u32 reserved4[2]; + u32 acrs[16]; + u64 crs[16]; + u64 mc_interrupt_code; + u32 reserved5; + u32 external_damage_code; + u64 mc_failing_storage_addr; +} __packed; + +#define DF_VMDUMP_HDR_SIZE \ + (sizeof(struct vmd_adsr) + sizeof(struct vmd_fmbk) + sizeof(struct vmd_fir_basic) + \ + sizeof(struct vmd_albk) + sizeof(struct vmd_asibk_64_new) + sizeof(struct vmd_fir_64)) + +#endif diff --git a/zdump/dfi.c b/zdump/dfi.c index 0d252a9c..d99ed6e5 100644 --- a/zdump/dfi.c +++ b/zdump/dfi.c @@ -3,7 +3,7 @@ * * Generic input dump format functions (DFI - Dump Format Input) * - * Copyright IBM Corp. 2001, 2018 + * Copyright IBM Corp. 2001, 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. @@ -34,6 +34,7 @@ static struct dfi *dfi_vec[] = { &dfi_s390_ext, &dfi_s390, &dfi_lkcd, + &dfi_vmdump, &dfi_pv_elf, &dfi_elf, &dfi_kdump, diff --git a/zdump/dfi.h b/zdump/dfi.h index 79fe2b49..75fc51fc 100644 --- a/zdump/dfi.h +++ b/zdump/dfi.h @@ -3,7 +3,7 @@ * * Generic input dump format functions (DFI - Dump Format Input) * - * Copyright IBM Corp. 2001, 2018 + * Copyright IBM Corp. 2001, 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. @@ -255,5 +255,6 @@ extern struct dfi dfi_kdump; extern struct dfi dfi_kdump_flat; extern struct dfi dfi_devmem; extern struct dfi dfi_ngdump; +extern struct dfi dfi_vmdump; #endif /* DFI_H */ diff --git a/zdump/dfi_vmdump.c b/zdump/dfi_vmdump.c new file mode 100644 index 00000000..4e4d3c74 --- /dev/null +++ b/zdump/dfi_vmdump.c @@ -0,0 +1,468 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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_asibk_64_new asibk_new; + struct vmd_fir_64 fir; + struct vmd_fir_other_64 *fir_other; + + u64 memory_start_record; + 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; + + util_log_print(UTIL_LOG_DEBUG, "Memory Offset: %#lx\n", l.memory_start_record); + util_log_print(UTIL_LOG_DEBUG, "Dumped Pages: %d\n", + l.asibk_new.storage_size_def_store / PAGE_SIZE); + + /* 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 (unsigned int 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))); +} + +/* + * 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, nr_dumped_pages; + 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: asibk */ + zg_seek(g.fh, l.fmbk.rec_nr_access * PAGE_SIZE, ZG_CHECK); + zg_read(g.fh, &l.asibk_new, sizeof(l.asibk_new), ZG_CHECK); + + l.memory_start_record = (l.fmbk.rec_nr_access + 1) * PAGE_SIZE; + + /* + * Record 10: bitmaps: + * Read all bitmap pages and setup bitmap array + */ + nr_dumped_pages = l.asibk_new.storage_size_def_store / PAGE_SIZE; + bitmap_sz = l.asibk_new.storage_size_def_store / (PAGE_SIZE * 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; + for (i = 0; i < 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; + 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); + page_num++; + if (page_num == nr_dumped_pages) + goto out; + } + } else { + page_num += PAGE_SIZE; /* Empty page */ + } + } + } while (page_num < nr_dumped_pages); + +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 nr_dumped_pages = l.asibk_new.storage_size_def_store / PAGE_SIZE; + u64 pos, more; + + for (pos = 0; pos < nr_dumped_pages;) { + bool isset = test_page_bit(l.bitmap, pos); + + more = find_bitrange(l.bitmap, pos, nr_dumped_pages, 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_arch_set(DFI_ARCH_64); + 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, +}; diff --git a/zdump/zgetdump.8 b/zdump/zgetdump.8 index 6f0da889..f6d7e904 100644 --- a/zdump/zgetdump.8 +++ b/zdump/zgetdump.8 @@ -1,10 +1,10 @@ -.\" Copyright 2022 IBM Corp. +.\" Copyright 2023 IBM Corp. .\" s390-tools is free software; you can redistribute it and/or modify .\" it under the terms of the MIT license. See LICENSE for details. .\" -.TH ZGETDUMP 8 "October 2022" "s390-tools" +.TH ZGETDUMP 8 "January 2023" "s390-tools" .SH NAME -zgetdump \- Tool for copying and converting System z dumps +zgetdump \- Tool for copying and converting IBM zSystems dumps .SH SYNOPSIS \fBzgetdump\fR DUMP [-s SYS] [-f FMT] [-k KEY] > DUMP_FILE @@ -107,6 +107,8 @@ source dump is located: .IP " -" 12 Regular dump file (e.g. /dumps/dump.0) .IP " -" 12 +vmdump file (e. g. VMDUMP.FILE) +.IP " -" 12 DASD partition device node (e.g. /dev/dasdc1) .IP " -" 12 DASD device node for multi-volume dump (e.g. /dev/dasdc) @@ -186,16 +188,22 @@ Linux user space core dumps. Protected virtualization vmcore dump in Executable and Linkable Format .TP .BR "s390" -This dump format is System z specific and is used for DASD and tape dumps. +This dump format is IBM zSystems specific and is used for DASD and tape dumps. .TP The following dump formats are supported for the source dump only: .TP .BR "s390_ext" -This dump format is System z specific and is used for DASD dumps only. +This dump format is IBM zSystems specific and is used for DASD dumps only. +.TP +.BR "vmdump" +Dumps with this format are created by the z/VM vmdump command +and stored in the reader device. +Use the "vmur" tool to extract such dumps from the reader +device and save them on disk. .TP .BR "lkcd" This dump format is used by the Linux Kernel Crash Dumps (LKCD) project -and also on System z for the "vmconvert" dump tool. +and also on IBM zSystems for the "vmconvert" dump tool. .TP .BR "devmem" On live systems the /dev/mem or /dev/crash device nodes can be used as source