>From de205a0b9aa246641e9db2d2d1ef5e9970de3f27 Mon Sep 17 00:00:00 2001
From: HATAYAMA Daisuke <d.hatayama@jp.fujitsu.com>
Date: Thu, 15 Sep 2011 10:25:04 +0900
Subject: [PATCH 03/12] sadump: Add sadump core module

This commit implements basic functionality for an access to sadump data, most
of which is similar to other dumpfiles', in particular, diskdump's.

  * is_sadump() is an interface to be used in main.c, and is called some times
  on diskset configurations. The first call of is_sadump() calls
  read_dump_header() and the second and later calls on diskset configuration
  calls add_disk().

  * read_dump_header() reads the head dumpfile, identifies its configuration
  and initializes internal sadump_data in a proper way: just as other dumpfiles,
  SADUMP_LOCAL is set to sd->flags if the dumpfile is valid. Also, SADUMP_MEDIA
  if on media file and SADUMP_DISKSET on diskset configuration.

  Shapes of head dumpfile format on the three configurations are as below. Note
  that diskset header exists only on diskset configuration, and media header only
  on media file configuration.

    single partition)   diskset)            media file)

      partition header    partition header    media header
      dump header         diskset header      partition header
      dump sub header     dump header         dump header
      bitmap              dump sub header     dump sub header
      dumpable bitmap     bitmap              bitmap
      (memory data)       dumpable bitmap     dumpable bitmap
                          (memory data)       (memory data)

  The second or later disks on diskset configuration has partion header only as
  follows:

      partition header
      (memory data)

  * sd->block_table is quite similar to dd->valid_pages except that the former
  records offset in blocks while the latter in bytes. sadump has now no
  compression feature, so calculation of the actual block offset can be simply
  done without considering page headers.
---
 defs.h   |   12 +
 sadump.c |  872 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 sadump.h |  210 +++++++++++++++
 3 files changed, 1094 insertions(+), 0 deletions(-)
 create mode 100644 sadump.c
 create mode 100644 sadump.h

diff --git a/defs.h b/defs.h
index 781b02f..4c73750 100755
--- a/defs.h
+++ b/defs.h
@@ -4716,6 +4716,18 @@ struct kvm_register_set {
 int get_kvm_register_set(int, struct kvm_register_set *);
 
 /*
+ * sadump.c
+ */
+int is_sadump(char *);
+int read_sadump(int, void *, int, ulong, physaddr_t);
+int write_sadump(int, void *, int, ulong, physaddr_t);
+int sadump_init(char *, FILE *);
+int sadump_is_diskset(void);
+ulong get_sadump_panic_task(void);
+ulong get_sadump_switch_stack(ulong);
+FILE *set_sadump_fp(FILE *);
+
+/*
  * qemu.c
  */
 int qemu_init(char *);
diff --git a/sadump.c b/sadump.c
new file mode 100644
index 0000000..1657d3d
--- /dev/null
+++ b/sadump.c
@@ -0,0 +1,872 @@
+/*
+ * sadump.h - core analysis suite
+ *
+ * Copyright (c) 2011 FUJITSU LIMITED
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Author: HATAYAMA Daisuke <d.hatayama@jp.fujitsu.com>
+ */
+
+#include "defs.h"
+#include "sadump.h"
+#include <elf.h>
+
+enum {
+	failed = -1
+};
+
+static struct sadump_data sadump_data = { 0 };
+static struct sadump_data *sd = &sadump_data;
+
+static int read_device(void *buf, size_t bytes, ulong *offset);
+static int read_dump_header(char *file);
+static int add_disk(char *file);
+static int open_dump_file(char *file);
+static int open_disk(char *file);
+static ulong paddr_to_pfn(physaddr_t paddr);
+static inline int is_set_bit(char *bitmap, ulong pfn);
+static inline int page_is_ram(unsigned int nr);
+static inline int page_is_dumpable(unsigned int nr);
+static int lookup_diskset(ulong whole_offset, int *diskid, ulong *disk_offset);
+static struct tm *efi_time_t_to_tm(const efi_time_t *e, struct tm *t);
+static char * guid_to_str(efi_guid_t *guid, char *buf, size_t buflen);
+static int verify_magic_number(uint32_t magicnum[DUMP_PART_HEADER_MAGICNUM_SIZE]);
+static int block_table_init(void);
+static ulong pfn_to_block(ulong pfn);
+
+struct sadump_data *
+sadump_get_sadump_data(void)
+{
+	if (!SADUMP_VALID() || !SADUMP_DUMPFILE())
+		return NULL;
+
+	return &sadump_data;
+}
+
+int
+sadump_cleanup_sadump_data(void)
+{
+	int i;
+
+	if (!SADUMP_VALID() || !SADUMP_DUMPFILE())
+		return FALSE;
+
+	if (sd->flags & SADUMP_DISKSET) {
+		for (i = 1; i < sd->sd_list_len; ++i) {
+			close(sd->sd_list[i]->dfd);
+			free(sd->sd_list[i]->header);
+			free(sd->sd_list[i]);
+		}
+	}
+
+	close(sd->dfd);
+	free(sd->header);
+	free(sd->dump_header);
+	free(sd->diskset_header);
+	free(sd->bitmap);
+	free(sd->dumpable_bitmap);
+	free(sd->page_buf);
+	free(sd->block_table);
+	free(sd->sd_list[0]);
+
+	memset(&sadump_data, 0, sizeof(sadump_data));
+
+	pc->flags2 &= ~SADUMP;
+	pc->dumpfile = NULL;
+	pc->readmem = NULL;
+	pc->writemem = NULL;
+
+	return TRUE;
+}
+
+static int
+read_device(void *buf, size_t bytes, ulong *offset)
+{
+	if (lseek(sd->dfd, *offset, SEEK_SET) == failed) {
+		error(INFO, "sadump: cannot lseek dump device\n");
+		return FALSE;
+	}
+	if (read(sd->dfd, buf, bytes) < bytes) {
+		error(INFO, "sadump: cannot read dump device\n");
+		return FALSE;
+	}
+	*offset += bytes;
+	return TRUE;
+}
+
+static int
+read_dump_header(char *file)
+{
+	struct sadump_part_header *sph = NULL;
+	struct sadump_header *sh = NULL;
+	struct sadump_disk_set_header *sdh = NULL;
+	struct sadump_media_header *smh = NULL;
+	struct sadump_diskset_data *sd_list_len_0 = NULL;
+	size_t block_size = SADUMP_DEFAULT_BLOCK_SIZE;
+	ulong flags = 0;
+	ulong offset = 0, sub_hdr_offset, data_offset;
+	uint32_t smram_cpu_state_size = 0;
+	ulong bitmap_len, dumpable_bitmap_len;
+	char *bitmap = NULL, *dumpable_bitmap = NULL, *page_buf = NULL;
+	char guid1[33], guid2[33];
+
+	sph = malloc(block_size);
+	if (!sph) {
+		error(INFO, "sadump: cannot allocate partition header buffer\n");
+		goto err;
+	}
+
+	sdh = malloc(block_size);
+	if (!sdh) {
+		error(INFO, "sadump: cannot allocate disk set header buffer\n");
+		goto err;
+	}
+
+	sh = malloc(block_size);
+	if (!sh) {
+		error(INFO, "sadump: cannot allocate dump header buffer\n");
+		goto err;
+	}
+
+	smh = malloc(block_size);
+	if (!smh) {
+		error(INFO, "sadump: cannot allocate media header buffer\n");
+		goto err;
+	}
+
+restart:
+	if (block_size < 0)
+		return FALSE;
+
+	if (!read_device(sph, block_size, &offset)) {
+		error(INFO, "sadump: cannot read partition header\n");
+		goto err;
+	}
+
+	if (sph->signature1 != SADUMP_SIGNATURE1 ||
+	    sph->signature2 != SADUMP_SIGNATURE2) {
+
+		flags |= SADUMP_MEDIA;
+
+		if (CRASHDEBUG(1))
+			error(INFO, "sadump: read dump device as media "
+			      "format\n");
+
+		offset = 0;
+
+		if (!read_device(smh, block_size, &offset)) {
+			error(INFO, "sadump: cannot read media header\n");
+			goto err;
+		}
+
+		if (!read_device(sph, block_size, &offset)) {
+			error(INFO, "sadump: cannot read partition header\n");
+			goto err;
+		}
+
+		if (sph->signature1 != SADUMP_SIGNATURE1 ||
+		    sph->signature2 != SADUMP_SIGNATURE2) {
+			if (CRASHDEBUG(1))
+				error(INFO, "sadump: does not have partition "
+				      "header\n");
+			goto err;
+		}
+
+	}
+
+	if (!verify_magic_number(sph->magicnum)) {
+		error(INFO, "sadump: invalid magic number\n");
+		goto err;
+	}
+
+	if (!(flags & SADUMP_MEDIA) && sph->set_disk_set) {
+		uint32_t header_blocks;
+		size_t header_size;
+
+		flags |= SADUMP_DISKSET;
+
+		if (CRASHDEBUG(1))
+			error(INFO, "sadump: read dump device as diskset\n");
+
+		if (sph->set_disk_set != 1 ||
+		    sph->set_disk_set > SADUMP_MAX_DISK_SET_NUM) {
+			if (CRASHDEBUG(1))
+				error(INFO, "sadump: invalid disk set number: "
+				      "%d\n",
+				      sph->set_disk_set);
+			goto err;
+		}
+
+		if (!read_device(&header_blocks, sizeof(uint32_t), &offset)) {
+			error(INFO, "sadump: cannot read disk set header "
+			      "size\n");
+			goto err;
+		}
+
+		offset -= sizeof(uint32_t);
+		header_size = header_blocks * block_size;
+
+		if (header_size > block_size) {
+			sdh = realloc(sdh, header_size);
+			if (!sdh) {
+				error(INFO, "sadump: cannot re-allocate disk "
+				      "set buffer\n");
+				goto err;
+			}
+		}
+
+		if (!read_device(sdh, header_size, &offset)) {
+			error(INFO, "sadump: cannot read disk set header\n");
+			goto err;
+		}
+
+	}
+
+	if (!read_device(sh, block_size, &offset)) {
+		error(INFO, "sadump: cannot read dump header\n");
+		goto err;
+	}
+
+	sub_hdr_offset = offset;
+
+	if (strncmp(sh->signature, SADUMP_SIGNATURE, 8) != 0) {
+		if (CRASHDEBUG(1))
+			error(INFO, "sadump: does not have dump header\n");
+		goto err;
+	}
+
+	if (flags & SADUMP_MEDIA) {
+
+		if (memcmp(&sph->sadump_id, &smh->sadump_id,
+			   sizeof(efi_guid_t)) != 0) {
+			if (CRASHDEBUG(1))
+				error(INFO, "sadump: system ID mismatch\n"
+				      "  partition header: %s\n"
+				      "  media header: %s\n",
+				      guid_to_str(&sph->sadump_id, guid1, sizeof(guid1)),
+				      guid_to_str(&smh->sadump_id, guid2, sizeof(guid2)));
+			goto err;
+		}
+
+		if (memcmp(&sph->disk_set_id, &smh->disk_set_id,
+			   sizeof(efi_guid_t)) != 0) {
+			if (CRASHDEBUG(1))
+				error(INFO, "sadump: disk set ID mismatch\n"
+				      "  partition header: %s\n"
+				      "  media header: %s\n",
+				      guid_to_str(&sph->disk_set_id, guid1, sizeof(guid1)),
+				      guid_to_str(&smh->disk_set_id, guid2, sizeof(guid2)));
+			goto err;
+		}
+
+		if (memcmp(&sph->time_stamp, &smh->time_stamp,
+			   sizeof(efi_guid_t)) != 0) {
+			struct tm tm;
+			if (CRASHDEBUG(1))
+				error(INFO, "sadump: time stamp mismatch\n"
+				      "  partition header: %s\n"
+				      "  media header: %s\n",
+				      strip_linefeeds(asctime
+						      (efi_time_t_to_tm
+						       (&sph->time_stamp,
+							&tm))),
+				      strip_linefeeds(asctime
+						      (efi_time_t_to_tm
+						       (&smh->time_stamp,
+							&tm))));
+			goto err;
+		}
+
+		if (smh->sequential_num != 1) {
+			error(INFO, "sadump: first media file has sequential "
+			      "number %d\n", smh->sequential_num);
+			goto err;
+		}
+
+	}
+
+	if (sh->block_size != block_size) {
+		block_size = sh->block_size;
+		offset = 0;
+		goto restart;
+	}
+
+	if (CRASHDEBUG(1)) {
+		if (flags & SADUMP_MEDIA)
+			error(INFO, "sadump: media backup file\n");
+
+		else if (flags & SADUMP_DISKSET)
+			error(INFO, "sadump: diskset configuration with %d "
+			      "disks\n", sdh->disk_num);
+
+		else
+			error(INFO, "sadump: single partition "
+			      "configuration\n");
+	}
+
+	flags |= SADUMP_LOCAL;
+
+	if (sh->sub_hdr_size > 0) {
+		if (!read_device(&smram_cpu_state_size, sizeof(uint32_t),
+				 &offset)) {
+			error(INFO,
+			      "sadump: cannot read SMRAM CPU STATE size\n");
+			goto err;
+		}
+		smram_cpu_state_size /= sh->nr_cpus;
+
+		offset -= sizeof(uint32_t);
+		offset += sh->sub_hdr_size * block_size;
+	}
+
+	if (!sh->bitmap_blocks) {
+		error(INFO, "sadump: bitmap_blocks is zero\n");
+		goto err;
+	}
+	bitmap_len = block_size * sh->bitmap_blocks;
+	bitmap = calloc(bitmap_len, 1);
+	if (!bitmap) {
+		error(INFO, "sadump: cannot allocate memory for bitmap "
+		      "buffer\n");
+		goto err;
+	}
+	if (!read_device(bitmap, bitmap_len, &offset)) {
+		error(INFO, "sadump: cannot read bitmap\n");
+		goto err;
+	}
+
+	if (!sh->dumpable_bitmap_blocks) {
+		error(INFO, "sadump: dumpable_bitmap_blocks is zero\n");
+		goto err;
+	}
+	dumpable_bitmap_len = block_size * sh->dumpable_bitmap_blocks;
+	dumpable_bitmap = calloc(dumpable_bitmap_len, 1);
+	if (!dumpable_bitmap) {
+		error(INFO, "sadump: cannot allocate memory for "
+		      "dumpable_bitmap buffer\n");
+		goto err;
+	}
+	if (!read_device(dumpable_bitmap, dumpable_bitmap_len, &offset)) {
+		error(INFO, "sadump: cannot read dumpable bitmap\n");
+		goto err;
+	}
+
+	data_offset = offset;
+
+	page_buf = malloc(block_size);
+	if (!page_buf) {
+		error(INFO, "sadump: cannot allocate page buffer\n");
+		goto err;
+	}
+
+	if (flags & SADUMP_DISKSET) {
+
+		sd_list_len_0 = malloc(sizeof(struct sadump_diskset_data));
+		if (!sd_list_len_0) {
+			error(INFO, "sadump: cannot allocate diskset data buffer\n");
+			goto err;
+		}
+
+	}
+
+	sd->filename = file;
+	sd->flags = flags;
+
+	if (machine_type("X86"))
+		sd->machine_type = EM_386;
+	else if (machine_type("X86_64"))
+		sd->machine_type = EM_X86_64;
+	else {
+		error(INFO, "sadump: unsupported machine type: %s\n",
+		      MACHINE_TYPE);
+		goto err;
+	}
+
+	sd->data_offset = data_offset;
+	sd->block_size = block_size;
+	sd->block_shift = ffs(sd->block_size) - 1;
+
+	sd->bitmap = bitmap;
+	sd->dumpable_bitmap = dumpable_bitmap;
+
+	sd->sub_hdr_offset = sub_hdr_offset;
+	sd->smram_cpu_state_size = smram_cpu_state_size;
+
+	sd->header = sph;
+	sd->dump_header = sh;
+	if (flags & SADUMP_DISKSET)
+		sd->diskset_header = sdh;
+	if (flags & SADUMP_MEDIA)
+		sd->media_header = smh;
+
+	sd->page_buf = page_buf;
+
+	if (flags & SADUMP_DISKSET) {
+		sd_list_len_0->filename = sd->filename;
+		sd_list_len_0->dfd = sd->dfd;
+		sd_list_len_0->header = sd->header;
+		sd_list_len_0->data_offset = sd->data_offset;
+
+		sd->sd_list_len = 1;
+		sd->sd_list[0] = sd_list_len_0;
+	}
+
+	if (!block_table_init()) {
+		error(INFO, "sadump: cannot initialize block hash table\n");
+		goto err;
+	}
+
+	if (!(flags & SADUMP_DISKSET))
+		free(sdh);
+
+	if (!(flags & SADUMP_MEDIA))
+		free(smh);
+
+	return TRUE;
+
+err:
+	close(sd->dfd);
+
+	free(sph);
+	free(sdh);
+	free(sh);
+	free(smh);
+	free(bitmap);
+	free(dumpable_bitmap);
+	free(page_buf);
+	free(sd_list_len_0);
+
+	return FALSE;
+}
+
+static int
+add_disk(char *file)
+{
+	struct sadump_part_header *ph;
+	struct sadump_diskset_data *this_disk;
+	int diskid;
+	char guid1[33], guid2[33];
+
+	diskid = sd->sd_list_len - 1;
+	this_disk = sd->sd_list[diskid];
+
+	ph = malloc(sd->block_size);
+	if (!ph) {
+		error(INFO, "sadump: cannot malloc block_size buffer\n");
+		return FALSE;
+	}
+
+	if (lseek(this_disk->dfd, 0, SEEK_SET) == failed) {
+		error(INFO, "sadump: cannot lseek dump partition header\n");
+		free(ph);
+		return FALSE;
+	}
+	if (read(this_disk->dfd, ph, sd->block_size) < sd->block_size) {
+		error(INFO, "sadump: cannot read dump partition header\n");
+		free(ph);
+		return FALSE;
+	}
+
+	if (ph->signature1 != SADUMP_SIGNATURE1 ||
+	    ph->signature2 != SADUMP_SIGNATURE2) {
+		if (CRASHDEBUG(1))
+			error(INFO, "sadump: does not have partition header\n");
+		free(ph);
+		return FALSE;
+	}
+
+	if (memcmp(&sd->header->sadump_id, &ph->sadump_id,
+		   sizeof(efi_guid_t)) != 0) {
+		if (CRASHDEBUG(1))
+			error(INFO, "sadump: system ID mismatch\n"
+			      "  partition header on disk #1: %s\n"
+			      "  partition header on disk #%d: %s\n",
+			      guid_to_str(&sd->header->sadump_id, guid1,
+					  sizeof(guid1)),
+			      diskid+1,
+			      guid_to_str(&ph->sadump_id, guid2,
+					  sizeof(guid2)));
+		free(ph);
+		return FALSE;
+	}
+
+	if (memcmp(&sd->header->disk_set_id, &ph->disk_set_id, sizeof(efi_guid_t)) != 0) {
+		if (CRASHDEBUG(1))
+			error(INFO, "sadump: disk set ID mismatch\n"
+			      "  partition header on disk #1: %s\n"
+			      "  partition header on disk #%d: %s\n",
+			      guid_to_str(&sd->header->disk_set_id, guid1,
+					  sizeof(guid1)),
+			      diskid+1,
+			      guid_to_str(&ph->disk_set_id, guid2,
+					  sizeof(guid2)));
+		free(ph);
+		return FALSE;
+	}
+
+	if (memcmp(&sd->diskset_header->vol_info[diskid - 1].id, &ph->vol_id,
+		   sizeof(efi_guid_t)) != 0) {
+		if (CRASHDEBUG(1))
+			error(INFO, "sadump: volume ID mismatch\n"
+			      "  disk set header on disk #1: %s\n"
+			      "  partition header on disk #%d: %s\n",
+			      guid_to_str(&sd->diskset_header->vol_info[diskid-1].id,
+					  guid1, sizeof(guid1)),
+			      diskid+1,
+			      guid_to_str(&ph->vol_id, guid2, sizeof(guid2)));
+		free(ph);
+		return FALSE;
+	}
+
+	if (memcmp(&sd->header->time_stamp, &ph->time_stamp,
+		   sizeof(efi_time_t)) != 0) {
+		struct tm tm;
+		if (CRASHDEBUG(1))
+			error(INFO, "sadump: time stamp mismatch\n"
+			      "  partition header on disk #1: %s\n"
+			      "  partition header on disk #%d: %s\n",
+			      strip_linefeeds(asctime(efi_time_t_to_tm
+						      (&sd->header->time_stamp,
+						       &tm))),
+			      diskid+1,
+			      strip_linefeeds(asctime(efi_time_t_to_tm
+						      (&ph->time_stamp,
+						       &tm))));
+		free(ph);
+		return FALSE;
+	}
+
+	if (diskid != ph->set_disk_set - 1) {
+		if (CRASHDEBUG(1))
+			error(INFO, "sadump: wrong disk order; "
+			      "#%d expected but #%d given\n",
+			      diskid+1, ph->set_disk_set);
+		free(ph);
+		return FALSE;
+	}
+
+	this_disk->header = ph;
+	this_disk->data_offset = sd->block_size;
+	this_disk->filename = file;
+
+	return TRUE;
+}
+
+static int
+open_dump_file(char *file)
+{
+	int fd;
+
+	fd = open(file, O_RDONLY);
+	if (fd < 0) {
+		error(INFO, "sadump: unable to open dump file %s", file);
+		return FALSE;
+	}
+
+	sd->dfd = fd;
+
+	return TRUE;
+}
+
+static int
+open_disk(char *file)
+{
+	struct sadump_diskset_data *this_disk;
+
+	sd->sd_list_len++;
+
+	if (sd->sd_list_len > sd->diskset_header->disk_num) {
+		error(INFO, "sadump: too many diskset arguments; "
+		      "this diskset consists of %d disks\n",
+		      sd->diskset_header->disk_num);
+		return FALSE;
+	}
+
+	this_disk = malloc(sizeof(struct sadump_diskset_data));
+	if (!this_disk) {
+		if (CRASHDEBUG(1)) {
+			error(INFO, "sadump: cannot malloc diskset data buffer\n");
+		}
+		return FALSE;
+	}
+
+	sd->sd_list[sd->sd_list_len - 1] = this_disk;
+
+	this_disk->dfd = open(file, O_RDONLY);
+	if (!this_disk->dfd) {
+		free(this_disk);
+		error(INFO, "sadump: unable to open dump file %s", file);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+int is_sadump(char *file)
+{
+	if (SADUMP_VALID()) {
+		if (sd->flags & SADUMP_DISKSET) {
+			if (!open_disk(file) || !add_disk(file)) {
+				(void) sadump_cleanup_sadump_data();
+				return FALSE;
+			}
+			return TRUE;
+		}
+		if (CRASHDEBUG(1))
+			error(INFO, "sadump: does not support multiple file formats\n");
+		(void) sadump_cleanup_sadump_data();
+		return FALSE;
+	}
+
+	if (!open_dump_file(file) || !read_dump_header(file))
+		return FALSE;
+
+	return TRUE;
+}
+
+int sadump_is_diskset(void)
+{
+	if (!SADUMP_VALID())
+		return FALSE;
+
+	return !!(sd->flags & SADUMP_DISKSET);
+}
+
+/*
+ * Translate physical address in paddr to PFN number. This means normally that
+ * we just shift paddr by some constant.
+ */
+static ulong
+paddr_to_pfn(physaddr_t paddr)
+{
+	return paddr >> sd->block_shift;
+}
+
+static inline int
+is_set_bit(char *bitmap, ulong pfn)
+{
+	ulong index, bit;
+
+	index = pfn >> 3;
+	bit = pfn & 7;
+
+	return !!(bitmap[index] & (1UL << bit));
+}
+
+static inline int
+page_is_ram(unsigned int nr)
+{
+	return is_set_bit(sd->bitmap, nr);
+}
+
+static inline int
+page_is_dumpable(unsigned int nr)
+{
+	return is_set_bit(sd->dumpable_bitmap, nr);
+}
+
+static int
+lookup_diskset(ulong whole_offset, int *diskid, ulong *disk_offset)
+{
+	ulong offset = whole_offset;
+	int i;
+
+	for (i = 0; i < sd->sd_list_len; ++i) {
+		ulong used_device_i, data_offset_i, ram_size;
+
+		used_device_i = sd->sd_list[i]->header->used_device;
+		data_offset_i = sd->sd_list[i]->data_offset;
+
+		ram_size = used_device_i - data_offset_i;
+
+		if (offset < ram_size)
+			break;
+		offset -= ram_size;
+	}
+
+	if (i == sd->sd_list_len)
+		return FALSE;
+
+	*diskid = i;
+	*disk_offset = offset;
+
+	return TRUE;
+}
+
+int read_sadump(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr)
+{
+	physaddr_t curpaddr;
+	ulong pfn, page_offset, block, whole_offset, perdisk_offset;
+	int dfd;
+
+	pfn = paddr_to_pfn(paddr);
+
+	curpaddr = paddr & ~((physaddr_t)(sd->block_size-1));
+	page_offset = paddr & ((physaddr_t)(sd->block_size-1));
+
+	if ((pfn >= sd->dump_header->max_mapnr) || !page_is_ram(pfn))
+		return SEEK_ERROR;
+	if (!page_is_dumpable(pfn)) {
+		memset(bufptr, 0, cnt);
+		return cnt;
+	}
+
+	block = pfn_to_block(pfn);
+
+	whole_offset = block * sd->block_size;
+
+	if (sd->flags & SADUMP_DISKSET) {
+		int diskid;
+
+		if (!lookup_diskset(whole_offset, &diskid, &perdisk_offset))
+			return SEEK_ERROR;
+
+		dfd = sd->sd_list[diskid]->dfd;
+		perdisk_offset += sd->sd_list[diskid]->data_offset;
+
+	} else {
+		dfd = sd->dfd;
+		perdisk_offset = whole_offset + sd->data_offset;
+
+	}
+
+	if (lseek(dfd, perdisk_offset, SEEK_SET) == failed)
+		return SEEK_ERROR;
+
+	if (read(dfd, sd->page_buf, sd->block_size) != sd->block_size)
+		return READ_ERROR;
+
+	memcpy(bufptr, sd->page_buf + page_offset, cnt);
+
+	return cnt;
+}
+
+int write_sadump(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr)
+{
+	return 0;
+}
+
+int sadump_init(char *unused, FILE *fptr)
+{
+	if (!SADUMP_VALID())
+		return FALSE;
+
+	return TRUE;
+}
+
+ulong get_sadump_panic_task(void)
+{
+	return NO_TASK;
+}
+
+ulong get_sadump_switch_stack(ulong task)
+{
+	return 0;
+}
+
+static struct tm *
+efi_time_t_to_tm(const efi_time_t *e, struct tm *t)
+{
+	time_t ti;
+
+	memset(t, 0, sizeof(*t));
+
+	t->tm_sec  = e->second;
+	t->tm_min  = e->minute;
+	t->tm_hour = e->hour;
+	t->tm_mday = e->day;
+	t->tm_mon  = e->month - 1;
+	t->tm_year = e->year - 1900;
+
+	if (e->timezone != EFI_UNSPECIFIED_TIMEZONE)
+		t->tm_hour += e->timezone;
+
+	else if (CRASHDEBUG(1))
+		error(INFO, "sadump: timezone information is missing\n");
+
+	ti = mktime(t);
+	if (ti == (time_t)-1)
+		return t;
+
+	return localtime_r(&ti, t);
+}
+
+static char *
+guid_to_str(efi_guid_t *guid, char *buf, size_t buflen)
+{
+	snprintf(buf, buflen,
+		 "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+		 guid->data1, guid->data2, guid->data3,
+		 guid->data4[0], guid->data4[1], guid->data4[2],
+		 guid->data4[3], guid->data4[4], guid->data4[5],
+		 guid->data4[6], guid->data4[7]);
+
+	return buf;
+}
+
+static int
+verify_magic_number(uint32_t magicnum[DUMP_PART_HEADER_MAGICNUM_SIZE])
+{
+	int i;
+
+	for (i = 1; i < DUMP_PART_HEADER_MAGICNUM_SIZE; ++i)
+		if (magicnum[i] != (magicnum[i - 1] + 7) * 11)
+			return FALSE;
+
+	return TRUE;
+}
+
+static int block_table_init(void)
+{
+	ulong section, max_section, pfn;
+	ulong *block_table;
+
+	max_section = divideup(sd->dump_header->max_mapnr, SADUMP_PF_SECTION_NUM);
+
+	block_table = calloc(sizeof(ulong), max_section);
+	if (!block_table) {
+		error(INFO, "sadump: cannot allocate memory for block_table\n");
+		return FALSE;
+	}
+
+	for (section = 0; section < max_section; ++section) {
+		if (section > 0)
+			block_table[section] = block_table[section-1];
+		for (pfn = section * SADUMP_PF_SECTION_NUM;
+		     pfn < (section + 1) * SADUMP_PF_SECTION_NUM;
+		     ++pfn)
+			if (page_is_dumpable(pfn))
+				block_table[section]++;
+	}
+
+	sd->block_table = block_table;
+
+	return TRUE;
+}
+
+static ulong pfn_to_block(ulong pfn)
+{
+	ulong block, section, p;
+
+	section = pfn / SADUMP_PF_SECTION_NUM;
+
+	if (section)
+		block = sd->block_table[section - 1];
+	else
+		block = 0;
+
+	for (p = section * SADUMP_PF_SECTION_NUM; p < pfn; ++p)
+		if (page_is_dumpable(p))
+			block++;
+
+	return block;
+}
diff --git a/sadump.h b/sadump.h
new file mode 100644
index 0000000..c5e3591
--- /dev/null
+++ b/sadump.h
@@ -0,0 +1,210 @@
+/*
+ * sadump.h - core analysis suite
+ *
+ * Copyright (c) 2011 FUJITSU LIMITED
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * Author: HATAYAMA Daisuke <d.hatayama@jp.fujitsu.com>
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+
+typedef struct efi_time {
+	uint16_t year;
+	uint8_t month;
+	uint8_t day;
+	uint8_t hour;
+	uint8_t minute;
+	uint8_t second;
+	uint8_t pad1;
+	uint32_t nanosecond;
+#define EFI_UNSPECIFIED_TIMEZONE 2047
+	int16_t timezone;
+	uint8_t daylight;
+	uint8_t pad2;
+} efi_time_t;
+
+typedef struct {
+	uint32_t data1;
+	uint16_t data2;
+	uint16_t data3;
+	uint8_t data4[8];
+} efi_guid_t;
+
+struct sadump_part_header {
+#define SADUMP_SIGNATURE1	0x75646173
+#define SADUMP_SIGNATURE2	0x0000706d
+	uint32_t signature1;	/* sadu */
+	uint32_t signature2;	/* mp\0\0 */
+	uint32_t enable;	/* set sadump service */
+	uint32_t reboot;	/* number of seconds until reboot. 1-3600 */
+	uint32_t compress;	/* memory image format. */
+	uint32_t recycle;	/* dump device recycle */
+	uint32_t label[16];	/* reserve */
+	efi_guid_t sadump_id;	/* system UUID */
+	efi_guid_t disk_set_id;	/* disk set UUID */
+	efi_guid_t vol_id;	/* device UUID */
+	efi_time_t time_stamp;	/* time stamp */
+	uint32_t set_disk_set;	/* device type */
+#define SADUMP_MAX_DISK_SET_NUM 16
+	uint32_t reserve;	/* Padding for Alignment */
+	uint64_t used_device;	/* used device */
+#define DUMP_PART_HEADER_MAGICNUM_SIZE 982
+	uint32_t magicnum[DUMP_PART_HEADER_MAGICNUM_SIZE]; /* magic number */
+};
+
+struct sadump_volume_info {
+	efi_guid_t id;		/* volume id */
+	uint64_t vol_size;	/* device size */
+	uint32_t status;	/* device status */
+	uint32_t cache_size;	/* cache size */
+};
+
+struct sadump_disk_set_header {
+	uint32_t disk_set_header_size;	/* disk set header size */
+	uint32_t disk_num;	/* disk number */
+	uint64_t disk_set_size;	/* disk set size */
+#define DUMP_DEVICE_MAX 16
+	struct sadump_volume_info vol_info[DUMP_DEVICE_MAX - 1];
+	/* struct VOL_INFO array */
+};
+
+struct sadump_header {
+#define SADUMP_SIGNATURE "sadump\0\0"
+	char signature[8];	/* = "sadump\0\0" */
+	uint32_t header_version;	/* Dump header version */
+	uint32_t reserve;	/* Padding for Alignment */
+	efi_time_t timestamp;	/* Time stamp */
+	uint32_t status;	/* Above flags */
+	uint32_t compress;	/* Above flags */
+	uint32_t block_size;	/* Size of a block in byte */
+#define SADUMP_DEFAULT_BLOCK_SIZE 4096
+	uint32_t extra_hdr_size;	/* Size of host dependent
+					 * header in blocks (reserve)
+					 */
+	uint32_t sub_hdr_size;	/* Size of arch dependent header in blocks */
+	uint32_t bitmap_blocks;	/* Size of Memory bitmap in block */
+	uint32_t dumpable_bitmap_blocks;	/* Size of Memory bitmap in block */
+	uint32_t max_mapnr;	/* = max_mapnr */
+	uint32_t total_ram_blocks;	/* Size of Memory in block */
+	uint32_t device_blocks;	/* Number of total blocks in the dump device */
+	uint32_t written_blocks;	/* Number of written blocks */
+	uint32_t current_cpu;	/* CPU# which handles dump */
+	uint32_t nr_cpus;	/* Number of CPUs */
+};
+
+struct sadump_apic_state {
+	uint64_t ApicId;	/* Local Apic ID register */
+	uint64_t Ldr;		/* Logical Destination Register */
+};
+
+struct sadump_smram_cpu_state {
+	uint64_t Reserved1[58];
+	uint32_t GdtUpper, LdtUpper, IdtUpper;
+	uint32_t Reserved2[3];
+	uint64_t IoEip;
+	uint64_t Reserved3[10];
+	uint32_t Cr4;
+	uint32_t Reserved4[18];
+	uint32_t GdtLower;
+	uint32_t GdtLimit;
+	uint32_t IdtLower;
+	uint32_t IdtLimit;
+	uint32_t LdtLower;
+	uint32_t LdtLimit;
+	uint32_t LdtInfo;
+	uint64_t Reserved5[6];
+	uint64_t Eptp;
+	uint32_t EptpSetting;
+	uint32_t Reserved6[5];
+	uint32_t Smbase;
+	uint32_t SmmRevisionId;
+	uint16_t IoInstructionRestart;
+	uint16_t AutoHaltRestart;
+	uint32_t Reserved7[6];
+	uint32_t R15Lower, R15Upper, R14Lower, R14Upper;
+	uint32_t R13Lower, R13Upper, R12Lower, R12Upper;
+	uint32_t R11Lower, R11Upper, R10Lower, R10Upper;
+	uint32_t R9Lower, R9Upper, R8Lower, R8Upper;
+	uint32_t RaxLower, RaxUpper, RcxLower, RcxUpper;
+	uint32_t RdxLower, RdxUpper, RbxLower, RbxUpper;
+	uint32_t RspLower, RspUpper, RbpLower, RbpUpper;
+	uint32_t RsiLower, RsiUpper, RdiLower, RdiUpper;
+	uint32_t IoMemAddrLower, IoMemAddrUpper;
+	uint32_t IoMisc, Es, Cs, Ss, Ds, Fs, Gs;
+	uint32_t Ldtr, Tr;
+	uint64_t Dr7, Dr6, Rip, Ia32Efer, Rflags;
+	uint64_t Cr3, Cr0;
+};
+
+struct sadump_page_header {
+	uint64_t page_flags;
+	uint32_t size;
+	uint32_t flags;
+};
+
+struct sadump_media_header {
+	efi_guid_t sadump_id;	// system UUID
+	efi_guid_t disk_set_id;	// disk set UUID
+	efi_time_t time_stamp;	/* time stamp */
+	char sequential_num;	// Medium sequential number
+	char term_cord;		// Termination cord
+	char disk_set_header_size;	// Size of original disk set header
+	char disks_in_use;	// Number of used disks of original dump device
+	char reserve[4044];	// reserve feild
+};
+
+#define divideup(x, y)	(((x) + ((y) - 1)) / (y))
+
+#define SADUMP_PF_SECTION_NUM 4096
+
+struct sadump_diskset_data {
+	char *filename;
+	int dfd;
+	struct sadump_part_header *header;
+	ulong data_offset;
+};
+
+struct sadump_data {
+	char *filename;
+	ulong flags;
+	int dfd;           /* dumpfile file descriptor */
+	int machine_type;  /* machine type identifier */
+
+	struct sadump_part_header *header;
+	struct sadump_header *dump_header;
+	struct sadump_disk_set_header *diskset_header;
+	struct sadump_media_header *media_header;
+
+	char *bitmap;
+	char *dumpable_bitmap;
+
+	size_t sub_hdr_offset;
+	uint32_t smram_cpu_state_size;
+
+	ulong data_offset;
+	int block_size;
+	int block_shift;
+
+	char *page_buf;
+	ulong *block_table;
+
+	int sd_list_len;
+	struct sadump_diskset_data *sd_list[SADUMP_MAX_DISK_SET_NUM];
+};
+
+struct sadump_data *sadump_get_sadump_data(void);
+int sadump_cleanup_sadump_data(void);
+ulong sadump_identify_format(int *block_size);
+int sadump_get_smram_cpu_state(int apicid,
+			       struct sadump_smram_cpu_state *smram);
-- 
1.7.4.4

