Logo Search packages:      
Sourcecode: s390-tools version File versions  Download package

dfi_lkcd.c

/*
 * zgetdump - Tool for copying and converting System z dumps
 *
 * LKCD dump input format
 *
 * Copyright IBM Corp. 2001, 2010
 * Author(s): Michael Holzheu <holzheu@linux.vnet.ibm.com>
 */

#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);
      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);
                  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);
      }
      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()
{
      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,
};

Generated by  Doxygen 1.6.0   Back to index