/* * zgetdump - Tool for copying and converting System z dumps * * S390 tape 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 #include #include #include #include #include #include #include #include #include "dump/s390_dump.h" #include "zgetdump.h" #include "df_s390.h" #include "dfi.h" #include "dfi_mem_chunk.h" #define TAPE_BLK_SIZE 32768 /* Defined by zipl tape dumper */ /* * File local static data * * blk_buf_addr: Memory address of last read memory block * blk_buf: Content of the last read memory block * blk: The next block number that will be read relative to blk_start * blk_start: The absolute block number on the tape where the dump starts */ static struct { char blk_buf[TAPE_BLK_SIZE]; u64 blk_buf_addr; u64 blk; int blk_start; } l; /* * MT ioctls */ struct mtioctl { int op; const char *desc; }; static struct mtioctl mt_fsfm = {MTFSFM, "forward space file"}; static struct mtioctl mt_bsr = {MTBSR, "backward space record"}; static struct mtioctl mt_tell = {MTTELL, "tell"}; static struct mtioctl mt_seek = {MTSEEK, "seek"}; /* * Do MT ioctl with count argument */ static int mtioctl(struct mtioctl *op, int cnt, enum zg_check check) { struct mtop mtop; mtop.mt_count = cnt; mtop.mt_op = op->op; return zg_ioctl(g.fh, MTIOCTOP, &mtop, op->desc, check); } /* * Verify end marker */ static int em_verify(struct df_s390_em *em) { if ((memcmp(em->str, "DUMP_END", 8) == 0)) { df_s390_em_add(em); return 0; } else { return -EINVAL; } } /* * Verify dump header */ static void hdr_verify(struct df_s390_hdr *hdr) { if (hdr->magic != DF_S390_MAGIC && hdr->magic != DF_S390_MAGIC_EXT) ERR_EXIT("No valid dump found on tape"); if (hdr->volnr != 0) { STDERR_PR("Found volume number: %d\n", hdr->volnr); ERR_EXIT("Multi-volume dumps are no longer supported"); } } /* * Seek to relative block number in dump (block 0 is the dump header) */ static void seek_blk(u64 blk) { if (l.blk == blk) return; mtioctl(&mt_seek, l.blk_start + blk, ZG_CHECK); l.blk = blk; } /* * Read memory from cartridge */ static void df_s390tape_mem_read(struct dfi_mem_chunk *mem_chunk, u64 addr, void *buf, u64 cnt) { unsigned int copied = 0, size; (void) mem_chunk; u64 blk, off; do { blk = addr / TAPE_BLK_SIZE + 1; if (addr >= l.blk_buf_addr + TAPE_BLK_SIZE || addr < l.blk_buf_addr) { seek_blk(blk); zg_read(g.fh, l.blk_buf, sizeof(l.blk_buf), ZG_CHECK); l.blk_buf_addr = (l.blk - 1) * TAPE_BLK_SIZE; l.blk++; } off = addr - l.blk_buf_addr; size = MIN(cnt - copied, TAPE_BLK_SIZE - off); memcpy(buf + copied, &l.blk_buf[off], size); addr += size; copied += size; } while (copied != cnt); } /* * Initialize cache for memory read (block 0 is the dump header) */ static void mem_read_init(void) { mtioctl(&mt_seek, l.blk_start + 1, ZG_CHECK); zg_read(g.fh, l.blk_buf, sizeof(l.blk_buf), ZG_CHECK); l.blk_buf_addr = 0; l.blk = 2; } /* * Init a new tape volume */ static int vol_init(void) { struct df_s390_hdr hdr; struct df_s390_em em; int rc; STDERR("Checking tape, this can take a while...\n"); /* Init dump header */ l.blk_start = mtioctl(&mt_tell, 1, ZG_CHECK); zg_read(g.fh, &hdr, sizeof(hdr), ZG_CHECK); hdr_verify(&hdr); df_s390_hdr_add(&hdr); dfi_mem_chunk_add(0, hdr.mem_size, NULL, df_s390tape_mem_read, NULL); /* Init end marker */ mtioctl(&mt_fsfm, 1, ZG_CHECK_NONE); mtioctl(&mt_bsr, 1, ZG_CHECK); rc = zg_read(g.fh, &em, sizeof(em), ZG_CHECK_ERR); if (rc != 8 && rc != 16) return -EINVAL; if (em_verify(&em) != 0) return -EINVAL; /* Init memory read & CPU info */ mem_read_init(); rc = df_s390_cpu_info_add(&hdr, hdr.mem_size - 1); if (rc) return rc; return 0; } /* * Exit function: Seek to block 0 */ static void dfi_s390tape_exit(void) { seek_blk(0); } /* * Initialize s390 tape DFI */ static int dfi_s390tape_init(void) { if (zg_type(g.fh) != ZG_TYPE_TAPE) return -ENODEV; if (vol_init() != 0) return -EINVAL; zg_atexit(dfi_s390tape_exit); return 0; } /* * S390 tape DFI operations */ struct dfi dfi_s390tape = { .name = "s390tape", .init = dfi_s390tape_init, .feat_bits = DFI_FEAT_SEEK | DFI_FEAT_COPY, };