crash/xendump.c

2877 lines
76 KiB
C

/*
* xendump.c
*
* Copyright (C) 2006-2011, 2013-2014 David Anderson
* Copyright (C) 2006-2011, 2013-2014 Red Hat, Inc. All rights reserved.
*
* 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.
*/
#include "defs.h"
#include "xendump.h"
static struct xendump_data xendump_data = { 0 };
struct xendump_data *xd = &xendump_data;
static int xc_save_verify(char *);
static int xc_core_verify(char *, char *);
static int xc_save_read(void *, int, ulong, physaddr_t);
static int xc_core_read(void *, int, ulong, physaddr_t);
static int xc_core_mfns(ulong, FILE *);
static void poc_store(ulong, off_t);
static off_t poc_get(ulong, int *);
static void xen_dump_vmconfig(FILE *);
static void xc_core_create_pfn_tables(void);
static ulong xc_core_pfn_to_page_index(ulong);
static int xc_core_pfn_valid(ulong);
static void xendump_print(char *fmt, ...);
static int xc_core_elf_verify(char *, char *);
static void xc_core_elf_dump(void);
static char *xc_core_elf_mfn_to_page(ulong, char *);
static int xc_core_elf_mfn_to_page_index(ulong);
static ulong xc_core_elf_pfn_valid(ulong);
static ulong xc_core_elf_pfn_to_page_index(ulong);
static void xc_core_dump_Elf32_Ehdr(Elf32_Ehdr *);
static void xc_core_dump_Elf64_Ehdr(Elf64_Ehdr *);
static void xc_core_dump_Elf32_Shdr(Elf32_Off offset, int);
static void xc_core_dump_Elf64_Shdr(Elf64_Off offset, int);
static char *xc_core_strtab(uint32_t, char *);
static void xc_core_dump_elfnote(off_t, size_t, int);
static void xc_core_elf_pfn_init(void);
#define ELFSTORE 1
#define ELFREAD 0
/*
* Determine whether a file is a xendump creation, and if TRUE,
* initialize the xendump_data structure.
*/
int
is_xendump(char *file)
{
int verified;
char buf[BUFSIZE];
if ((xd->xfd = open(file, O_RDWR)) < 0) {
if ((xd->xfd = open(file, O_RDONLY)) < 0) {
sprintf(buf, "%s: open", file);
perror(buf);
return FALSE;
}
}
if (read(xd->xfd, buf, BUFSIZE) != BUFSIZE)
return FALSE;
if (machine_type("X86") || machine_type("X86_64"))
xd->page_size = 4096;
else if (machine_type("IA64") && !machdep->pagesize)
xd->page_size = 16384;
else
xd->page_size = machdep->pagesize;
verified = xc_save_verify(buf) || xc_core_verify(file, buf);
if (!verified)
close(xd->xfd);
return (verified);
}
/*
* Verify whether the dump was created by the xc_domain_dumpcore()
* library function in libxc/xc_core.c.
*/
static int
xc_core_verify(char *file, char *buf)
{
struct xc_core_header *xcp;
xcp = (struct xc_core_header *)buf;
if (xc_core_elf_verify(file, buf))
return TRUE;
if ((xcp->xch_magic != XC_CORE_MAGIC) &&
(xcp->xch_magic != XC_CORE_MAGIC_HVM))
return FALSE;
if (!xcp->xch_nr_vcpus) {
error(INFO,
"faulty xc_core dump file header: xch_nr_vcpus is 0\n\n");
fprintf(stderr, " xch_magic: %x\n", xcp->xch_magic);
fprintf(stderr, " xch_nr_vcpus: %d\n", xcp->xch_nr_vcpus);
fprintf(stderr, " xch_nr_pages: %d\n", xcp->xch_nr_pages);
fprintf(stderr, " xch_ctxt_offset: %d\n", xcp->xch_ctxt_offset);
fprintf(stderr, " xch_index_offset: %d\n", xcp->xch_index_offset);
fprintf(stderr, " xch_pages_offset: %d\n\n", xcp->xch_pages_offset);
clean_exit(1);
}
xd->xc_core.header.xch_magic = xcp->xch_magic;
xd->xc_core.header.xch_nr_vcpus = xcp->xch_nr_vcpus;
xd->xc_core.header.xch_nr_pages = xcp->xch_nr_pages;
xd->xc_core.header.xch_ctxt_offset = (off_t)xcp->xch_ctxt_offset;
xd->xc_core.header.xch_index_offset = (off_t)xcp->xch_index_offset;
xd->xc_core.header.xch_pages_offset = (off_t)xcp->xch_pages_offset;
xd->flags |= (XENDUMP_LOCAL | XC_CORE_ORIG | XC_CORE_P2M_CREATE);
if (xc_core_mfns(XC_CORE_64BIT_HOST, stderr))
xd->flags |= XC_CORE_64BIT_HOST;
if (!xd->page_size)
error(FATAL,
"unknown page size: use -p <pagesize> command line option\n");
if (!(xd->page = (char *)malloc(xd->page_size)))
error(FATAL, "cannot malloc page space.");
if (!(xd->poc = (struct pfn_offset_cache *)calloc
(PFN_TO_OFFSET_CACHE_ENTRIES,
sizeof(struct pfn_offset_cache))))
error(FATAL, "cannot malloc pfn_offset_cache\n");
xd->last_pfn = ~(0UL);
if (CRASHDEBUG(1))
xendump_memory_dump(stderr);
return TRUE;
}
/*
* Do the work for read_xendump() for the XC_CORE dumpfile format.
*/
static int
xc_core_read(void *bufptr, int cnt, ulong addr, physaddr_t paddr)
{
ulong pfn, page_index;
off_t offset;
int redundant;
if (xd->flags & (XC_CORE_P2M_CREATE|XC_CORE_PFN_CREATE))
xc_core_create_pfn_tables();
pfn = (ulong)BTOP(paddr);
if ((offset = poc_get(pfn, &redundant))) {
if (!redundant) {
if (lseek(xd->xfd, offset, SEEK_SET) == -1)
return SEEK_ERROR;
if (read(xd->xfd, xd->page, xd->page_size) !=
xd->page_size)
return READ_ERROR;
xd->last_pfn = pfn;
}
BCOPY(xd->page + PAGEOFFSET(paddr), bufptr, cnt);
return cnt;
}
if ((page_index = xc_core_pfn_to_page_index(pfn)) ==
PFN_NOT_FOUND)
return READ_ERROR;
offset = xd->xc_core.header.xch_pages_offset +
((off_t)(page_index) * (off_t)xd->page_size);
if (lseek(xd->xfd, offset, SEEK_SET) == -1)
return SEEK_ERROR;
if (read(xd->xfd, xd->page, xd->page_size) != xd->page_size)
return READ_ERROR;
poc_store(pfn, offset);
BCOPY(xd->page + PAGEOFFSET(paddr), bufptr, cnt);
return cnt;
}
/*
* Verify whether the dumpfile was created by the "xm save" facility.
* This gets started by the "save" function in XendCheckpoint.py, and
* then by xc_save.c, with the work done in the xc_linux_save() library
* function in libxc/xc_linux_save.c.
*/
#define MAX_BATCH_SIZE 1024
/*
* Number of P2M entries in a page.
*/
#define ULPP (xd->page_size/sizeof(unsigned long))
/*
* Number of P2M entries in the pfn_to_mfn_frame_list.
*/
#define P2M_FL_ENTRIES (((xd->xc_save.nr_pfns)+ULPP-1)/ULPP)
/*
* Size in bytes of the pfn_to_mfn_frame_list.
*/
#define P2M_FL_SIZE ((P2M_FL_ENTRIES)*sizeof(unsigned long))
#define XTAB (0xf<<28) /* invalid page */
#define LTAB_MASK XTAB
static int
xc_save_verify(char *buf)
{
int i, batch_count, done_batch, *intptr;
ulong flags, *ulongptr;
ulong batch_index, total_pages_read;
ulong N;
if (!STRNEQ(buf, XC_SAVE_SIGNATURE))
return FALSE;
if (lseek(xd->xfd, strlen(XC_SAVE_SIGNATURE), SEEK_SET) == -1)
return FALSE;
flags = XC_SAVE;
if (CRASHDEBUG(1)) {
fprintf(stderr, "\"%s\"\n", buf);
fprintf(stderr, "endian: %d %s\n", __BYTE_ORDER,
__BYTE_ORDER == __BIG_ENDIAN ? "__BIG_ENDIAN" :
(__BYTE_ORDER == __LITTLE_ENDIAN ?
"__LITTLE_ENDIAN" : "???"));
}
/*
* size of vmconfig data structure (big-endian)
*/
if (read(xd->xfd, buf, sizeof(int)) != sizeof(int))
return FALSE;
intptr = (int *)buf;
if (CRASHDEBUG(1) && BYTE_SWAP_REQUIRED(__BIG_ENDIAN)) {
fprintf(stderr, "byte-swap required for this:\n");
for (i = 0; i < sizeof(int); i++)
fprintf(stderr, "[%x]", buf[i] & 0xff);
fprintf(stderr, ": %x -> ", *intptr);
}
xd->xc_save.vmconfig_size = swab32(*intptr);
if (CRASHDEBUG(1))
fprintf(stderr, "%x\n", xd->xc_save.vmconfig_size);
if (!(xd->xc_save.vmconfig_buf = (char *)malloc
(xd->xc_save.vmconfig_size)))
error(FATAL, "cannot malloc xc_save vmconfig space.");
if (!xd->page_size)
error(FATAL,
"unknown page size: use -p <pagesize> command line option\n");
if (!(xd->page = (char *)malloc(xd->page_size)))
error(FATAL, "cannot malloc page space.");
if (!(xd->poc = (struct pfn_offset_cache *)calloc
(PFN_TO_OFFSET_CACHE_ENTRIES,
sizeof(struct pfn_offset_cache))))
error(FATAL, "cannot malloc pfn_offset_cache\n");
xd->last_pfn = ~(0UL);
if (!(xd->xc_save.region_pfn_type = (ulong *)calloc
(MAX_BATCH_SIZE, sizeof(ulong))))
error(FATAL, "cannot malloc region_pfn_type\n");
if (read(xd->xfd, xd->xc_save.vmconfig_buf,
xd->xc_save.vmconfig_size) != xd->xc_save.vmconfig_size)
goto xc_save_bailout;
/*
* nr_pfns (native byte order)
*/
if (read(xd->xfd, buf, sizeof(ulong)) != sizeof(ulong))
goto xc_save_bailout;
ulongptr = (ulong *)buf;
if (CRASHDEBUG(1)) {
for (i = 0; i < sizeof(ulong); i++)
fprintf(stderr, "[%x]", buf[i] & 0xff);
fprintf(stderr, ": %lx (nr_pfns)\n", *ulongptr);
}
xd->xc_save.nr_pfns = *ulongptr;
if (machine_type("IA64"))
goto xc_save_ia64;
/*
* Get a local copy of the live_P2M_frame_list
*/
if (!(xd->xc_save.p2m_frame_list = (unsigned long *)malloc(P2M_FL_SIZE)))
error(FATAL, "cannot allocate p2m_frame_list array");
if (!(xd->xc_save.batch_offsets = (off_t *)calloc((size_t)P2M_FL_ENTRIES,
sizeof(off_t))))
error(FATAL, "cannot allocate batch_offsets array");
xd->xc_save.batch_count = P2M_FL_ENTRIES;
if (read(xd->xfd, xd->xc_save.p2m_frame_list, P2M_FL_SIZE) !=
P2M_FL_SIZE)
goto xc_save_bailout;
if (CRASHDEBUG(1))
fprintf(stderr, "pre-batch file pointer: %lld\n",
(ulonglong)lseek(xd->xfd, 0L, SEEK_CUR));
/*
* ...
* int batch_count
* ulong region pfn_type[batch_count]
* page 0
* page 1
* ...
* page batch_count-1
* (repeat)
*/
total_pages_read = 0;
batch_index = 0;
done_batch = FALSE;
while (!done_batch) {
xd->xc_save.batch_offsets[batch_index] = (off_t)
lseek(xd->xfd, 0L, SEEK_CUR);
if (read(xd->xfd, &batch_count, sizeof(int)) != sizeof(int))
goto xc_save_bailout;
if (CRASHDEBUG(1))
fprintf(stderr, "batch[%ld]: %d ",
batch_index, batch_count);
batch_index++;
if (batch_index >= P2M_FL_ENTRIES) {
fprintf(stderr, "more than %ld batches encountered?\n",
P2M_FL_ENTRIES);
goto xc_save_bailout;
}
switch (batch_count)
{
case 0:
if (CRASHDEBUG(1)) {
fprintf(stderr,
": Batch work is done: %ld pages read (P2M_FL_ENTRIES: %ld)\n",
total_pages_read, P2M_FL_ENTRIES);
}
done_batch = TRUE;
continue;
case -1:
if (CRASHDEBUG(1))
fprintf(stderr, ": Entering page verify mode\n");
continue;
default:
if (batch_count > MAX_BATCH_SIZE) {
if (CRASHDEBUG(1))
fprintf(stderr,
": Max batch size exceeded. Giving up.\n");
done_batch = TRUE;
continue;
}
if (CRASHDEBUG(1))
fprintf(stderr, "\n");
break;
}
if (read(xd->xfd, xd->xc_save.region_pfn_type, batch_count * sizeof(ulong)) !=
batch_count * sizeof(ulong))
goto xc_save_bailout;
for (i = 0; i < batch_count; i++) {
unsigned long pagetype;
unsigned long pfn;
pfn = xd->xc_save.region_pfn_type[i] & ~LTAB_MASK;
pagetype = xd->xc_save.region_pfn_type[i] & LTAB_MASK;
if (pagetype == XTAB)
/* a bogus/unmapped page: skip it */
continue;
if (pfn > xd->xc_save.nr_pfns) {
if (CRASHDEBUG(1))
fprintf(stderr,
"batch_count: %d pfn %ld out of range",
batch_count, pfn);
}
if (lseek(xd->xfd, xd->page_size, SEEK_CUR) == -1)
goto xc_save_bailout;
total_pages_read++;
}
}
/*
* Get the list of PFNs that are not in the psuedo-phys map
*/
if (read(xd->xfd, &xd->xc_save.pfns_not,
sizeof(xd->xc_save.pfns_not)) != sizeof(xd->xc_save.pfns_not))
goto xc_save_bailout;
if (CRASHDEBUG(1))
fprintf(stderr, "PFNs not in pseudo-phys map: %d\n",
xd->xc_save.pfns_not);
if ((total_pages_read + xd->xc_save.pfns_not) !=
xd->xc_save.nr_pfns)
error(WARNING,
"nr_pfns: %ld != (total pages: %ld + pages not saved: %d)\n",
xd->xc_save.nr_pfns, total_pages_read,
xd->xc_save.pfns_not);
xd->xc_save.pfns_not_offset = lseek(xd->xfd, 0L, SEEK_CUR);
if (lseek(xd->xfd, sizeof(ulong) * xd->xc_save.pfns_not, SEEK_CUR) == -1)
goto xc_save_bailout;
xd->xc_save.vcpu_ctxt_offset = lseek(xd->xfd, 0L, SEEK_CUR);
lseek(xd->xfd, 0, SEEK_END);
lseek(xd->xfd, -((off_t)(xd->page_size)), SEEK_CUR);
xd->xc_save.shared_info_page_offset = lseek(xd->xfd, 0L, SEEK_CUR);
xd->flags |= (XENDUMP_LOCAL | flags);
kt->xen_flags |= (CANONICAL_PAGE_TABLES|XEN_SUSPEND);
if (CRASHDEBUG(1))
xendump_memory_dump(stderr);
return TRUE;
xc_save_ia64:
/*
* Completely different format for ia64:
*
* ...
* pfn #
* page data
* pfn #
* page data
* ...
*/
free(xd->poc);
xd->poc = NULL;
free(xd->xc_save.region_pfn_type);
xd->xc_save.region_pfn_type = NULL;
if (!(xd->xc_save.ia64_page_offsets =
(ulong *)calloc(xd->xc_save.nr_pfns, sizeof(off_t))))
error(FATAL, "cannot allocate ia64_page_offsets array");
/*
* version
*/
if (read(xd->xfd, buf, sizeof(ulong)) != sizeof(ulong))
goto xc_save_bailout;
xd->xc_save.ia64_version = *((ulong *)buf);
if (CRASHDEBUG(1))
fprintf(stderr, "ia64 version: %lx\n",
xd->xc_save.ia64_version);
/*
* xen_domctl_arch_setup structure
*/
if (read(xd->xfd, buf, sizeof(xen_domctl_arch_setup_t)) !=
sizeof(xen_domctl_arch_setup_t))
goto xc_save_bailout;
if (CRASHDEBUG(1)) {
xen_domctl_arch_setup_t *setup =
(xen_domctl_arch_setup_t *)buf;
fprintf(stderr, "xen_domctl_arch_setup:\n");
fprintf(stderr, " flags: %lx\n", (ulong)setup->flags);
fprintf(stderr, " bp: %lx\n", (ulong)setup->bp);
fprintf(stderr, " maxmem: %lx\n", (ulong)setup->maxmem);
fprintf(stderr, " xsi_va: %lx\n", (ulong)setup->xsi_va);
fprintf(stderr, "hypercall_imm: %x\n", setup->hypercall_imm);
}
for (i = N = 0; i < xd->xc_save.nr_pfns; i++) {
if (read(xd->xfd, &N, sizeof(N)) != sizeof(N))
goto xc_save_bailout;
if (N < xd->xc_save.nr_pfns)
xd->xc_save.ia64_page_offsets[N] =
lseek(xd->xfd, 0, SEEK_CUR);
else
error(WARNING,
"[%d]: pfn of %lx (0x%lx) in ia64 canonical page list exceeds %ld\n",
i, N, N, xd->xc_save.nr_pfns);
if (CRASHDEBUG(1)) {
if ((i < 10) || (N >= (xd->xc_save.nr_pfns-10)))
fprintf(stderr, "[%d]: %ld\n%s", i, N,
i == 9 ? "...\n" : "");
}
if ((N+1) >= xd->xc_save.nr_pfns)
break;
if (lseek(xd->xfd, xd->page_size, SEEK_CUR) == -1)
goto xc_save_bailout;
}
if (CRASHDEBUG(1)) {
for (i = N = 0; i < xd->xc_save.nr_pfns; i++) {
if (!xd->xc_save.ia64_page_offsets[i])
N++;
}
fprintf(stderr, "%ld out of %ld pfns not dumped\n",
N, xd->xc_save.nr_pfns);
}
xd->flags |= (XENDUMP_LOCAL | flags | XC_SAVE_IA64);
kt->xen_flags |= (CANONICAL_PAGE_TABLES|XEN_SUSPEND);
if (CRASHDEBUG(1))
xendump_memory_dump(stderr);
return TRUE;
xc_save_bailout:
error(INFO,
"xc_save_verify: \"LinuxGuestRecord\" file handling/format error\n");
if (xd->xc_save.p2m_frame_list) {
free(xd->xc_save.p2m_frame_list);
xd->xc_save.p2m_frame_list = NULL;
}
if (xd->xc_save.batch_offsets) {
free(xd->xc_save.batch_offsets);
xd->xc_save.batch_offsets = NULL;
}
if (xd->xc_save.vmconfig_buf) {
free(xd->xc_save.vmconfig_buf);
xd->xc_save.vmconfig_buf = NULL;
}
if (xd->page) {
free(xd->page);
xd->page = NULL;
}
return FALSE;
}
/*
* Do the work for read_xendump() for the XC_SAVE dumpfile format.
*/
static int
xc_save_read(void *bufptr, int cnt, ulong addr, physaddr_t paddr)
{
int b, i, redundant;
ulong reqpfn;
int batch_count;
off_t file_offset;
reqpfn = (ulong)BTOP(paddr);
if (CRASHDEBUG(8))
fprintf(xd->ofp,
"xc_save_read(bufptr: %lx cnt: %d addr: %lx paddr: %llx (%ld, 0x%lx)\n",
(ulong)bufptr, cnt, addr, (ulonglong)paddr, reqpfn, reqpfn);
if (xd->flags & XC_SAVE_IA64) {
if (reqpfn >= xd->xc_save.nr_pfns) {
if (CRASHDEBUG(1))
fprintf(xd->ofp,
"xc_save_read: pfn %lx too large: nr_pfns: %lx\n",
reqpfn, xd->xc_save.nr_pfns);
return SEEK_ERROR;
}
file_offset = xd->xc_save.ia64_page_offsets[reqpfn];
if (!file_offset) {
if (CRASHDEBUG(1))
fprintf(xd->ofp,
"xc_save_read: pfn %lx not stored in xendump\n",
reqpfn);
return SEEK_ERROR;
}
if (reqpfn != xd->last_pfn) {
if (lseek(xd->xfd, file_offset, SEEK_SET) == -1)
return SEEK_ERROR;
if (read(xd->xfd, xd->page, xd->page_size) != xd->page_size)
return READ_ERROR;
} else {
xd->redundant++;
xd->cache_hits++;
}
xd->accesses++;
xd->last_pfn = reqpfn;
BCOPY(xd->page + PAGEOFFSET(paddr), bufptr, cnt);
return cnt;
}
if ((file_offset = poc_get(reqpfn, &redundant))) {
if (!redundant) {
if (lseek(xd->xfd, file_offset, SEEK_SET) == -1)
return SEEK_ERROR;
if (read(xd->xfd, xd->page, xd->page_size) != xd->page_size)
return READ_ERROR;
xd->last_pfn = reqpfn;
} else if (CRASHDEBUG(1))
console("READ %ld (0x%lx) skipped!\n", reqpfn, reqpfn);
BCOPY(xd->page + PAGEOFFSET(paddr), bufptr, cnt);
return cnt;
}
/*
* ...
* int batch_count
* ulong region pfn_type[batch_count]
* page 0
* page 1
* ...
* page batch_count-1
* (repeat)
*/
for (b = 0; b < xd->xc_save.batch_count; b++) {
if (lseek(xd->xfd, xd->xc_save.batch_offsets[b], SEEK_SET) == -1)
return SEEK_ERROR;
if (CRASHDEBUG(8))
fprintf(xd->ofp, "check batch[%d]: offset: %llx\n",
b, (ulonglong)xd->xc_save.batch_offsets[b]);
if (read(xd->xfd, &batch_count, sizeof(int)) != sizeof(int))
return READ_ERROR;
switch (batch_count)
{
case 0:
if (CRASHDEBUG(1) && !STREQ(pc->curcmd, "search")) {
fprintf(xd->ofp,
"batch[%d]: has count of zero -- bailing out on pfn %ld\n",
b, reqpfn);
}
return READ_ERROR;
case -1:
return READ_ERROR;
default:
if (CRASHDEBUG(8))
fprintf(xd->ofp,
"batch[%d]: offset: %llx batch count: %d\n",
b, (ulonglong)xd->xc_save.batch_offsets[b],
batch_count);
break;
}
if (read(xd->xfd, xd->xc_save.region_pfn_type, batch_count * sizeof(ulong)) !=
batch_count * sizeof(ulong))
return READ_ERROR;
for (i = 0; i < batch_count; i++) {
unsigned long pagetype;
unsigned long pfn;
pfn = xd->xc_save.region_pfn_type[i] & ~LTAB_MASK;
pagetype = xd->xc_save.region_pfn_type[i] & LTAB_MASK;
if (pagetype == XTAB)
/* a bogus/unmapped page: skip it */
continue;
if (pfn > xd->xc_save.nr_pfns) {
if (CRASHDEBUG(1))
fprintf(stderr,
"batch_count: %d pfn %ld out of range",
batch_count, pfn);
}
if (pfn == reqpfn) {
file_offset = lseek(xd->xfd, 0, SEEK_CUR);
poc_store(pfn, file_offset);
if (read(xd->xfd, xd->page, xd->page_size) !=
xd->page_size)
return READ_ERROR;
BCOPY(xd->page + PAGEOFFSET(paddr), bufptr, cnt);
return cnt;
}
if (lseek(xd->xfd, xd->page_size, SEEK_CUR) == -1)
return SEEK_ERROR;
}
}
return READ_ERROR;
}
/*
* Stash a pfn's offset. If they're all in use, put it in the
* least-used slot that's closest to the beginning of the array.
*/
static void
poc_store(ulong pfn, off_t file_offset)
{
int i;
struct pfn_offset_cache *poc, *plow;
ulong curlow;
curlow = ~(0UL);
plow = NULL;
poc = xd->poc;
for (i = 0; i < PFN_TO_OFFSET_CACHE_ENTRIES; i++, poc++) {
if (poc->cnt == 0) {
poc->cnt = 1;
poc->pfn = pfn;
poc->file_offset = file_offset;
xd->last_pfn = pfn;
return;
}
if (poc->cnt < curlow) {
curlow = poc->cnt;
plow = poc;
}
}
plow->cnt = 1;
plow->pfn = pfn;
plow->file_offset = file_offset;
xd->last_pfn = pfn;
}
/*
* Check whether a pfn's offset has been cached.
*/
static off_t
poc_get(ulong pfn, int *redundant)
{
int i;
struct pfn_offset_cache *poc;
xd->accesses++;
if (pfn == xd->last_pfn) {
xd->redundant++;
*redundant = TRUE;
return 1;
} else
*redundant = FALSE;
poc = xd->poc;
for (i = 0; i < PFN_TO_OFFSET_CACHE_ENTRIES; i++, poc++) {
if (poc->cnt && (poc->pfn == pfn)) {
poc->cnt++;
xd->cache_hits++;
return poc->file_offset;
}
}
return 0;
}
/*
* Perform any post-dumpfile determination stuff here.
*/
int
xendump_init(char *unused, FILE *fptr)
{
if (!XENDUMP_VALID())
return FALSE;
xd->ofp = fptr;
return TRUE;
}
int
read_xendump(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr)
{
if (pc->curcmd_flags & XEN_MACHINE_ADDR)
return READ_ERROR;
switch (xd->flags & (XC_SAVE|XC_CORE_ORIG|XC_CORE_ELF))
{
case XC_SAVE:
return xc_save_read(bufptr, cnt, addr, paddr);
case XC_CORE_ORIG:
case XC_CORE_ELF:
return xc_core_read(bufptr, cnt, addr, paddr);
default:
return READ_ERROR;
}
}
int
read_xendump_hyper(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr)
{
ulong pfn, page_index;
off_t offset;
pfn = (ulong)BTOP(paddr);
/* ODA: pfn == mfn !!! */
if ((page_index = xc_core_mfn_to_page_index(pfn)) == PFN_NOT_FOUND)
return READ_ERROR;
offset = xd->xc_core.header.xch_pages_offset +
((off_t)(page_index) * (off_t)xd->page_size);
if (lseek(xd->xfd, offset, SEEK_SET) == -1)
return SEEK_ERROR;
if (read(xd->xfd, xd->page, xd->page_size) != xd->page_size)
return READ_ERROR;
BCOPY(xd->page + PAGEOFFSET(paddr), bufptr, cnt);
return cnt;
}
int
write_xendump(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr)
{
return WRITE_ERROR;
}
uint
xendump_page_size(void)
{
if (!XENDUMP_VALID())
return 0;
return xd->page_size;
}
/*
* xendump_free_memory(), and xendump_memory_used()
* are debug only, and typically unnecessary to implement.
*/
int
xendump_free_memory(void)
{
return 0;
}
int
xendump_memory_used(void)
{
return 0;
}
/*
* This function is dump-type independent, used here to
* to dump the xendump_data structure contents.
*/
int
xendump_memory_dump(FILE *fp)
{
int i, linefeed, used, others;
ulong *ulongptr;
Elf32_Off offset32;
Elf64_Off offset64;
FILE *fpsave;
fprintf(fp, " flags: %lx (", xd->flags);
others = 0;
if (xd->flags & XENDUMP_LOCAL)
fprintf(fp, "%sXENDUMP_LOCAL", others++ ? "|" : "");
if (xd->flags & XC_SAVE)
fprintf(fp, "%sXC_SAVE", others++ ? "|" : "");
if (xd->flags & XC_CORE_ORIG)
fprintf(fp, "%sXC_CORE_ORIG", others++ ? "|" : "");
if (xd->flags & XC_CORE_ELF)
fprintf(fp, "%sXC_CORE_ELF", others++ ? "|" : "");
if (xd->flags & XC_CORE_P2M_CREATE)
fprintf(fp, "%sXC_CORE_P2M_CREATE", others++ ? "|" : "");
if (xd->flags & XC_CORE_PFN_CREATE)
fprintf(fp, "%sXC_CORE_PFN_CREATE", others++ ? "|" : "");
if (xd->flags & XC_CORE_NO_P2M)
fprintf(fp, "%sXC_CORE_NO_P2M", others++ ? "|" : "");
if (xd->flags & XC_SAVE_IA64)
fprintf(fp, "%sXC_SAVE_IA64", others++ ? "|" : "");
if (xd->flags & XC_CORE_64BIT_HOST)
fprintf(fp, "%sXC_CORE_64BIT_HOST", others++ ? "|" : "");
fprintf(fp, ")\n");
fprintf(fp, " xfd: %d\n", xd->xfd);
fprintf(fp, " page_size: %d\n", xd->page_size);
fprintf(fp, " ofp: %lx\n", (ulong)xd->ofp);
fprintf(fp, " page: %lx\n", (ulong)xd->page);
fprintf(fp, " panic_pc: %lx\n", xd->panic_pc);
fprintf(fp, " panic_sp: %lx\n", xd->panic_sp);
fprintf(fp, " accesses: %ld\n", (ulong)xd->accesses);
fprintf(fp, " cache_hits: %ld ", (ulong)xd->cache_hits);
if (xd->accesses)
fprintf(fp, "(%ld%%)\n", xd->cache_hits * 100 / xd->accesses);
else
fprintf(fp, "\n");
fprintf(fp, " last_pfn: %ld\n", xd->last_pfn);
fprintf(fp, " redundant: %ld ", (ulong)xd->redundant);
if (xd->accesses)
fprintf(fp, "(%ld%%)\n", xd->redundant * 100 / xd->accesses);
else
fprintf(fp, "\n");
for (i = used = 0; i < PFN_TO_OFFSET_CACHE_ENTRIES; i++)
if (xd->poc && xd->poc[i].cnt)
used++;
if (xd->poc)
fprintf(fp, " poc[%d]: %lx %s", PFN_TO_OFFSET_CACHE_ENTRIES,
(ulong)xd->poc, xd->poc ? "" : "(none)");
else
fprintf(fp, " poc[0]: (unused)\n");
for (i = 0; i < PFN_TO_OFFSET_CACHE_ENTRIES; i++) {
if (!xd->poc)
break;
if (!xd->poc[i].cnt) {
if (!i)
fprintf(fp, "(none used)\n");
break;
} else if (!i)
fprintf(fp, "(%d used)\n", used);
if (CRASHDEBUG(2))
fprintf(fp,
" [%d]: pfn: %ld (0x%lx) count: %ld file_offset: %llx\n",
i,
xd->poc[i].pfn,
xd->poc[i].pfn,
xd->poc[i].cnt,
(ulonglong)xd->poc[i].file_offset);
}
if (!xd->poc)
fprintf(fp, "\n");
fprintf(fp, "\n xc_save:\n");
fprintf(fp, " nr_pfns: %ld (0x%lx)\n",
xd->xc_save.nr_pfns, xd->xc_save.nr_pfns);
fprintf(fp, " vmconfig_size: %d (0x%x)\n", xd->xc_save.vmconfig_size,
xd->xc_save.vmconfig_size);
fprintf(fp, " vmconfig_buf: %lx\n", (ulong)xd->xc_save.vmconfig_buf);
if (xd->flags & XC_SAVE)
xen_dump_vmconfig(fp);
fprintf(fp, " p2m_frame_list: %lx ", (ulong)xd->xc_save.p2m_frame_list);
if ((xd->flags & XC_SAVE) && xd->xc_save.p2m_frame_list) {
fprintf(fp, "\n");
ulongptr = xd->xc_save.p2m_frame_list;
for (i = 0; i < P2M_FL_ENTRIES; i++, ulongptr++)
fprintf(fp, "%ld ", *ulongptr);
fprintf(fp, "\n");
} else
fprintf(fp, "(none)\n");
fprintf(fp, " pfns_not: %d\n", xd->xc_save.pfns_not);
fprintf(fp, " pfns_not_offset: %lld\n",
(ulonglong)xd->xc_save.pfns_not_offset);
fprintf(fp, " vcpu_ctxt_offset: %lld\n",
(ulonglong)xd->xc_save.vcpu_ctxt_offset);
fprintf(fp, " shared_info_page_offset: %lld\n",
(ulonglong)xd->xc_save.shared_info_page_offset);
fprintf(fp, " region_pfn_type: %lx\n", (ulong)xd->xc_save.region_pfn_type);
fprintf(fp, " batch_count: %ld\n", (ulong)xd->xc_save.batch_count);
fprintf(fp, " batch_offsets: %lx %s\n",
(ulong)xd->xc_save.batch_offsets,
xd->xc_save.batch_offsets ? "" : "(none)");
for (i = linefeed = 0; i < xd->xc_save.batch_count; i++) {
fprintf(fp, "[%d]: %llx ", i,
(ulonglong)xd->xc_save.batch_offsets[i]);
if (((i+1)%4) == 0) {
fprintf(fp, "\n");
linefeed = FALSE;
} else
linefeed = TRUE;
}
if (linefeed)
fprintf(fp, "\n");
fprintf(fp, " ia64_version: %ld\n", (ulong)xd->xc_save.ia64_version);
fprintf(fp, " ia64_page_offsets: %lx ", (ulong)xd->xc_save.ia64_page_offsets);
if (xd->xc_save.ia64_page_offsets)
fprintf(fp, "(%ld entries)\n\n", xd->xc_save.nr_pfns);
else
fprintf(fp, "(none)\n\n");
fprintf(fp, " xc_core:\n");
fprintf(fp, " header:\n");
fprintf(fp, " xch_magic: %x ",
xd->xc_core.header.xch_magic);
if (xd->xc_core.header.xch_magic == XC_CORE_MAGIC)
fprintf(fp, "(XC_CORE_MAGIC)\n");
else if (xd->xc_core.header.xch_magic == XC_CORE_MAGIC_HVM)
fprintf(fp, "(XC_CORE_MAGIC_HVM)\n");
else
fprintf(fp, "(unknown)\n");
fprintf(fp, " xch_nr_vcpus: %d\n",
xd->xc_core.header.xch_nr_vcpus);
fprintf(fp, " xch_nr_pages: %d (0x%x)\n",
xd->xc_core.header.xch_nr_pages,
xd->xc_core.header.xch_nr_pages);
fprintf(fp, " xch_ctxt_offset: %llu (0x%llx)\n",
(ulonglong)xd->xc_core.header.xch_ctxt_offset,
(ulonglong)xd->xc_core.header.xch_ctxt_offset);
fprintf(fp, " xch_index_offset: %llu (0x%llx)\n",
(ulonglong)xd->xc_core.header.xch_index_offset,
(ulonglong)xd->xc_core.header.xch_index_offset);
fprintf(fp, " xch_pages_offset: %llu (0x%llx)\n",
(ulonglong)xd->xc_core.header.xch_pages_offset,
(ulonglong)xd->xc_core.header.xch_pages_offset);
fprintf(fp, " elf_class: %s\n", xd->xc_core.elf_class == ELFCLASS64 ? "ELFCLASS64" :
xd->xc_core.elf_class == ELFCLASS32 ? "ELFCLASS32" : "n/a");
fprintf(fp, " elf_strtab_offset: %lld (0x%llx)\n",
(ulonglong)xd->xc_core.elf_strtab_offset,
(ulonglong)xd->xc_core.elf_strtab_offset);
fprintf(fp, " format_version: %016llx\n",
(ulonglong)xd->xc_core.format_version);
fprintf(fp, " shared_info_offset: %lld (0x%llx)\n",
(ulonglong)xd->xc_core.shared_info_offset,
(ulonglong)xd->xc_core.shared_info_offset);
if (machine_type("IA64"))
fprintf(fp, " ia64_mapped_regs_offset: %lld (0x%llx)\n",
(ulonglong)xd->xc_core.ia64_mapped_regs_offset,
(ulonglong)xd->xc_core.ia64_mapped_regs_offset);
fprintf(fp, " elf_index_pfn[%d]: %s", INDEX_PFN_COUNT,
xd->xc_core.elf_class ? "\n" : "(none used)\n");
if (xd->xc_core.elf_class) {
for (i = 0; i < INDEX_PFN_COUNT; i++) {
fprintf(fp, "%ld:%ld ",
xd->xc_core.elf_index_pfn[i].index,
xd->xc_core.elf_index_pfn[i].pfn);
}
fprintf(fp, "\n");
}
fprintf(fp, " last_batch:\n");
fprintf(fp, " index: %ld (%ld - %ld)\n",
xd->xc_core.last_batch.index,
xd->xc_core.last_batch.start, xd->xc_core.last_batch.end);
fprintf(fp, " accesses: %ld\n",
xd->xc_core.last_batch.accesses);
fprintf(fp, " duplicates: %ld ",
xd->xc_core.last_batch.duplicates);
if (xd->xc_core.last_batch.accesses)
fprintf(fp, "(%ld%%)\n",
xd->xc_core.last_batch.duplicates * 100 /
xd->xc_core.last_batch.accesses);
else
fprintf(fp, "\n");
fprintf(fp, " elf32: %lx\n", (ulong)xd->xc_core.elf32);
fprintf(fp, " elf64: %lx\n", (ulong)xd->xc_core.elf64);
fprintf(fp, " p2m_frames: %d\n",
xd->xc_core.p2m_frames);
fprintf(fp, " p2m_frame_index_list: %s\n",
(xd->flags & (XC_CORE_NO_P2M|XC_SAVE)) ? "(not used)" : "");
for (i = 0; i < xd->xc_core.p2m_frames; i++) {
fprintf(fp, "%ld ",
xd->xc_core.p2m_frame_index_list[i]);
}
fprintf(fp, xd->xc_core.p2m_frames ? "\n" : "");
if ((xd->flags & XC_CORE_ORIG) && CRASHDEBUG(8))
xc_core_mfns(XENDUMP_LOCAL, fp);
switch (xd->xc_core.elf_class)
{
case ELFCLASS32:
fpsave = xd->ofp;
xd->ofp = fp;
xc_core_elf_dump();
offset32 = xd->xc_core.elf32->e_shoff;
for (i = 0; i < xd->xc_core.elf32->e_shnum; i++) {
xc_core_dump_Elf32_Shdr(offset32, ELFREAD);
offset32 += xd->xc_core.elf32->e_shentsize;
}
xendump_print("\n");
xd->ofp = fpsave;
break;
case ELFCLASS64:
fpsave = xd->ofp;
xd->ofp = fp;
xc_core_elf_dump();
offset64 = xd->xc_core.elf64->e_shoff;
for (i = 0; i < xd->xc_core.elf64->e_shnum; i++) {
xc_core_dump_Elf64_Shdr(offset64, ELFREAD);
offset64 += xd->xc_core.elf64->e_shentsize;
}
xendump_print("\n");
xd->ofp = fpsave;
break;
}
return 0;
}
static void
xen_dump_vmconfig(FILE *fp)
{
int i, opens, closes;
char *p;
opens = closes = 0;
p = xd->xc_save.vmconfig_buf;
for (i = 0; i < xd->xc_save.vmconfig_size; i++, p++) {
if (ascii(*p))
fprintf(fp, "%c", *p);
else
fprintf(fp, "<%x>", *p);
if (*p == '(')
opens++;
else if (*p == ')')
closes++;
}
fprintf(fp, "\n");
if (opens != closes)
error(WARNING, "invalid vmconfig contents?\n");
}
/*
* Looking at the active set, try to determine who panicked,
* or who was the "suspend" kernel thread.
*/
ulong get_xendump_panic_task(void)
{
int i;
ulong task;
struct task_context *tc;
switch (xd->flags & (XC_CORE_ORIG|XC_CORE_ELF|XC_SAVE))
{
case XC_CORE_ORIG:
case XC_CORE_ELF:
if (machdep->xendump_panic_task)
return (machdep->xendump_panic_task((void *)xd));
break;
case XC_SAVE:
for (i = 0; i < NR_CPUS; i++) {
if (!(task = tt->active_set[i]))
continue;
tc = task_to_context(task);
if (is_kernel_thread(task) &&
STREQ(tc->comm, "suspend"))
return tc->task;
}
break;
}
return NO_TASK;
}
/*
* Figure out the back trace hooks.
*/
void get_xendump_regs(struct bt_info *bt, ulong *pc, ulong *sp)
{
int i;
ulong *up;
if ((tt->panic_task == bt->task) &&
(xd->panic_pc && xd->panic_sp)) {
*pc = xd->panic_pc;
*sp = xd->panic_sp;
return;
}
switch (xd->flags & (XC_CORE_ORIG|XC_CORE_ELF|XC_SAVE))
{
case XC_CORE_ORIG:
case XC_CORE_ELF:
if (machdep->get_xendump_regs)
return (machdep->get_xendump_regs(xd, bt, pc, sp));
break;
case XC_SAVE:
if (tt->panic_task != bt->task)
break;
for (i = 0, up = (ulong *)bt->stackbuf;
i < LONGS_PER_STACK; i++, up++) {
if (is_kernel_text(*up) &&
(STREQ(closest_symbol(*up),
"__do_suspend"))) {
*pc = *up;
*sp = tt->flags & THREAD_INFO ?
bt->tc->thread_info +
(i * sizeof(long)) :
bt->task +
(i * sizeof(long));
xd->panic_pc = *pc;
xd->panic_sp = *sp;
return;
}
}
}
machdep->get_stack_frame(bt, pc, sp);
}
/*
* Farm out most of the work to the proper architecture to create
* the p2m table. For ELF core dumps, create the index;pfn table.
*/
static void
xc_core_create_pfn_tables(void)
{
if (xd->flags & XC_CORE_P2M_CREATE) {
if (!machdep->xendump_p2m_create)
error(FATAL,
"xen xc_core dumpfiles not supported on this architecture");
if (!machdep->xendump_p2m_create((void *)xd))
error(FATAL,
"cannot create xen pfn-to-mfn mapping\n");
}
if (xd->flags & XC_CORE_PFN_CREATE)
xc_core_elf_pfn_init();
xd->flags &= ~(XC_CORE_P2M_CREATE|XC_CORE_PFN_CREATE);
if (CRASHDEBUG(1))
xendump_memory_dump(xd->ofp);
}
/*
* Find the page index containing the mfn, and read the
* machine page into the buffer.
*/
char *
xc_core_mfn_to_page(ulong mfn, char *pgbuf)
{
int i, b, idx, done;
ulong tmp[MAX_BATCH_SIZE];
off_t offset;
size_t size;
uint nr_pages;
if (xd->flags & XC_CORE_ELF)
return xc_core_elf_mfn_to_page(mfn, pgbuf);
if (lseek(xd->xfd, xd->xc_core.header.xch_index_offset,
SEEK_SET) == -1) {
error(INFO, "cannot lseek to page index\n");
return NULL;
}
nr_pages = xd->xc_core.header.xch_nr_pages;
if (xd->flags & XC_CORE_64BIT_HOST)
nr_pages *= 2;
for (b = 0, idx = -1, done = FALSE;
!done && (b < nr_pages); b += MAX_BATCH_SIZE) {
size = sizeof(ulong) * MIN(MAX_BATCH_SIZE, nr_pages - b);
if (read(xd->xfd, tmp, size) != size) {
error(INFO, "cannot read index page %d\n", b);
return NULL;
}
for (i = 0; i < MAX_BATCH_SIZE; i++) {
if ((b+i) >= nr_pages) {
done = TRUE;
break;
}
if (tmp[i] == mfn) {
idx = i+b;
if (CRASHDEBUG(4))
fprintf(xd->ofp,
"page: found mfn 0x%lx (%ld) at index %d\n",
mfn, mfn, idx);
done = TRUE;
}
}
}
if (idx == -1) {
error(INFO, "cannot find mfn %ld (0x%lx) in page index\n",
mfn, mfn);
return NULL;
}
if (lseek(xd->xfd, xd->xc_core.header.xch_pages_offset,
SEEK_SET) == -1) {
error(INFO, "cannot lseek to xch_pages_offset\n");
return NULL;
}
offset = (off_t)(idx) * (off_t)xd->page_size;
if (lseek(xd->xfd, offset, SEEK_CUR) == -1) {
error(INFO, "cannot lseek to mfn-specified page\n");
return NULL;
}
if (read(xd->xfd, pgbuf, xd->page_size) != xd->page_size) {
error(INFO, "cannot read mfn-specified page\n");
return NULL;
}
return pgbuf;
}
/*
* Find the page index containing the mfn, and read the
* machine page into the buffer.
*/
static char *
xc_core_elf_mfn_to_page(ulong mfn, char *pgbuf)
{
int i, b, idx, done;
off_t offset;
size_t size;
uint nr_pages;
ulong tmp;
struct xen_dumpcore_p2m p2m_batch[MAX_BATCH_SIZE];
offset = xd->xc_core.header.xch_index_offset;
nr_pages = xd->xc_core.header.xch_nr_pages;
if (lseek(xd->xfd, offset, SEEK_SET) == -1)
error(FATAL, "cannot lseek to page index\n");
for (b = 0, idx = -1, done = FALSE;
!done && (b < nr_pages); b += MAX_BATCH_SIZE) {
size = sizeof(struct xen_dumpcore_p2m) *
MIN(MAX_BATCH_SIZE, nr_pages - b);
if (read(xd->xfd, &p2m_batch[0], size) != size) {
error(INFO, "cannot read index page %d\n", b);
return NULL;
}
for (i = 0; i < MAX_BATCH_SIZE; i++) {
if ((b+i) >= nr_pages) {
done = TRUE;
break;
}
tmp = (ulong)p2m_batch[i].gmfn;
if (tmp == mfn) {
idx = i+b;
if (CRASHDEBUG(4))
fprintf(xd->ofp,
"page: found mfn 0x%lx (%ld) at index %d\n",
mfn, mfn, idx);
done = TRUE;
}
}
}
if (idx == -1) {
error(INFO, "cannot find mfn %ld (0x%lx) in page index\n",
mfn, mfn);
return NULL;
}
if (lseek(xd->xfd, xd->xc_core.header.xch_pages_offset,
SEEK_SET) == -1)
error(FATAL, "cannot lseek to xch_pages_offset\n");
offset = (off_t)(idx) * (off_t)xd->page_size;
if (lseek(xd->xfd, offset, SEEK_CUR) == -1) {
error(INFO, "cannot lseek to mfn-specified page\n");
return NULL;
}
if (read(xd->xfd, pgbuf, xd->page_size) != xd->page_size) {
error(INFO, "cannot read mfn-specified page\n");
return NULL;
}
return pgbuf;
}
/*
* Find and return the page index containing the mfn.
*/
int
xc_core_mfn_to_page_index(ulong mfn)
{
int i, b;
ulong tmp[MAX_BATCH_SIZE];
uint nr_pages;
size_t size;
if (xd->flags & XC_CORE_ELF)
return xc_core_elf_mfn_to_page_index(mfn);
if (lseek(xd->xfd, xd->xc_core.header.xch_index_offset,
SEEK_SET) == -1) {
error(INFO, "cannot lseek to page index\n");
return MFN_NOT_FOUND;
}
nr_pages = xd->xc_core.header.xch_nr_pages;
if (xd->flags & XC_CORE_64BIT_HOST)
nr_pages *= 2;
for (b = 0; b < nr_pages; b += MAX_BATCH_SIZE) {
size = sizeof(ulong) * MIN(MAX_BATCH_SIZE, nr_pages - b);
if (read(xd->xfd, tmp, size) != size) {
error(INFO, "cannot read index page %d\n", b);
return MFN_NOT_FOUND;
}
for (i = 0; i < MAX_BATCH_SIZE; i++) {
if ((b+i) >= nr_pages)
break;
if (tmp[i] == mfn) {
if (CRASHDEBUG(4))
fprintf(xd->ofp,
"index: batch: %d found mfn %ld (0x%lx) at index %d\n",
b/MAX_BATCH_SIZE, mfn, mfn, i+b);
return (i+b);
}
}
}
return MFN_NOT_FOUND;
}
/*
* Find and return the page index containing the mfn.
*/
static int
xc_core_elf_mfn_to_page_index(ulong mfn)
{
int i, b;
off_t offset;
size_t size;
uint nr_pages;
ulong tmp;
struct xen_dumpcore_p2m p2m_batch[MAX_BATCH_SIZE];
offset = xd->xc_core.header.xch_index_offset;
nr_pages = xd->xc_core.header.xch_nr_pages;
if (lseek(xd->xfd, offset, SEEK_SET) == -1)
error(FATAL, "cannot lseek to page index\n");
for (b = 0; b < nr_pages; b += MAX_BATCH_SIZE) {
size = sizeof(struct xen_dumpcore_p2m) *
MIN(MAX_BATCH_SIZE, nr_pages - b);
if (read(xd->xfd, &p2m_batch[0], size) != size) {
error(INFO, "cannot read index page %d\n", b);
return MFN_NOT_FOUND;
}
for (i = 0; i < MAX_BATCH_SIZE; i++) {
if ((b+i) >= nr_pages)
break;
tmp = (ulong)p2m_batch[i].gmfn;
if (tmp == mfn) {
if (CRASHDEBUG(4))
fprintf(xd->ofp,
"index: batch: %d found mfn %ld (0x%lx) at index %d\n",
b/MAX_BATCH_SIZE, mfn, mfn, i+b);
return (i+b);
}
}
}
return MFN_NOT_FOUND;
}
/*
* XC_CORE mfn-related utility function.
*/
static int
xc_core_mfns(ulong arg, FILE *ofp)
{
int i, b;
uint nr_pages;
ulong tmp[MAX_BATCH_SIZE];
ulonglong tmp64[MAX_BATCH_SIZE];
size_t size;
if (lseek(xd->xfd, xd->xc_core.header.xch_index_offset,
SEEK_SET) == -1) {
error(INFO, "cannot lseek to page index\n");
return FALSE;
}
switch (arg)
{
case XC_CORE_64BIT_HOST:
/*
* Determine whether this is a 32-bit guest xendump that
* was taken on a 64-bit xen host.
*/
if (machine_type("X86_64") || machine_type("IA64"))
return FALSE;
check_next_4:
if (read(xd->xfd, tmp, sizeof(ulong) * 4) != (4 * sizeof(ulong))) {
error(INFO, "cannot read index pages\n");
return FALSE;
}
if ((tmp[0] == 0xffffffff) || (tmp[1] == 0xffffffff) ||
(tmp[2] == 0xffffffff) || (tmp[3] == 0xffffffff) ||
(!tmp[0] && !tmp[1]) || (!tmp[2] && !tmp[3]))
goto check_next_4;
if (CRASHDEBUG(2))
fprintf(ofp, "mfns: %08lx %08lx %08lx %08lx\n",
tmp[0], tmp[1], tmp[2], tmp[3]);
if (tmp[0] && !tmp[1] && tmp[2] && !tmp[3])
return TRUE;
else
return FALSE;
case XENDUMP_LOCAL:
if (BITS64() || (xd->flags & XC_CORE_64BIT_HOST))
goto show_64bit_mfns;
fprintf(ofp, "xch_index_offset mfn list:\n");
nr_pages = xd->xc_core.header.xch_nr_pages;
for (b = 0; b < nr_pages; b += MAX_BATCH_SIZE) {
size = sizeof(ulong) *
MIN(MAX_BATCH_SIZE, nr_pages - b);
if (read(xd->xfd, tmp, size) != size) {
error(INFO, "cannot read index page %d\n", b);
return FALSE;
}
if (b) fprintf(ofp, "\n");
for (i = 0; i < MAX_BATCH_SIZE; i++) {
if ((b+i) >= nr_pages)
break;
if ((i%8) == 0)
fprintf(ofp, "%s[%d]:",
i ? "\n" : "", b+i);
if (tmp[i] == 0xffffffff)
fprintf(ofp, " INVALID");
else
fprintf(ofp, " %lx", tmp[i]);
}
}
fprintf(ofp, "\nxch_nr_pages: %d\n",
xd->xc_core.header.xch_nr_pages);
return TRUE;
show_64bit_mfns:
fprintf(ofp, "xch_index_offset mfn list: %s\n",
BITS32() ? "(64-bit mfns)" : "");
nr_pages = xd->xc_core.header.xch_nr_pages;
for (b = 0; b < nr_pages; b += MAX_BATCH_SIZE) {
size = sizeof(ulonglong) *
MIN(MAX_BATCH_SIZE, nr_pages - b);
if (read(xd->xfd, tmp64, size) != size) {
error(INFO, "cannot read index page %d\n", b);
return FALSE;
}
if (b) fprintf(ofp, "\n");
for (i = 0; i < MAX_BATCH_SIZE; i++) {
if ((b+i) >= nr_pages)
break;
if ((i%8) == 0)
fprintf(ofp, "%s[%d]:",
i ? "\n" : "", b+i);
if (tmp64[i] == 0xffffffffffffffffULL)
fprintf(ofp, " INVALID");
else
fprintf(ofp, " %llx", tmp64[i]);
}
}
fprintf(ofp, "\nxch_nr_pages: %d\n", nr_pages);
return TRUE;
default:
return FALSE;
}
}
/*
* Given a normal kernel pfn, determine the page index in the dumpfile.
*
* - First determine which of the pages making up the
* phys_to_machine_mapping[] array would contain the pfn.
* - From the phys_to_machine_mapping page, determine the mfn.
* - Find the mfn in the dumpfile page index.
*/
#define PFNS_PER_PAGE (xd->page_size/sizeof(unsigned long))
static ulong
xc_core_pfn_to_page_index(ulong pfn)
{
ulong idx, p2m_idx, mfn_idx;
ulong *up, mfn;
off_t offset;
/*
* This function does not apply when there's no p2m
* mapping and/or if this is an ELF format dumpfile.
*/
switch (xd->flags & (XC_CORE_NO_P2M|XC_CORE_ELF))
{
case (XC_CORE_NO_P2M|XC_CORE_ELF):
return xc_core_elf_pfn_valid(pfn);
case XC_CORE_NO_P2M:
return(xc_core_pfn_valid(pfn) ? pfn : PFN_NOT_FOUND);
case XC_CORE_ELF:
return xc_core_elf_pfn_to_page_index(pfn);
}
idx = pfn/PFNS_PER_PAGE;
if (idx >= xd->xc_core.p2m_frames) {
error(INFO, "pfn: %lx is too large for dumpfile\n",
pfn);
return PFN_NOT_FOUND;
}
p2m_idx = xd->xc_core.p2m_frame_index_list[idx];
if (lseek(xd->xfd, xd->xc_core.header.xch_pages_offset,
SEEK_SET) == -1) {
error(INFO, "cannot lseek to xch_pages_offset\n");
return PFN_NOT_FOUND;
}
offset = (off_t)(p2m_idx) * (off_t)xd->page_size;
if (lseek(xd->xfd, offset, SEEK_CUR) == -1) {
error(INFO, "cannot lseek to pfn-specified page\n");
return PFN_NOT_FOUND;
}
if (read(xd->xfd, xd->page, xd->page_size) != xd->page_size) {
error(INFO, "cannot read pfn-specified page\n");
return PFN_NOT_FOUND;
}
up = (ulong *)xd->page;
up += (pfn%PFNS_PER_PAGE);
mfn = *up;
if ((mfn_idx = xc_core_mfn_to_page_index(mfn)) == MFN_NOT_FOUND) {
if (!STREQ(pc->curcmd, "search"))
error(INFO, "cannot find mfn in page index\n");
return PFN_NOT_FOUND;
}
return mfn_idx;
}
/*
* Search the .xen_p2m array for the target pfn, starting at a
* higher batch if appropriate. This presumes that the pfns
* are laid out in ascending order.
*/
static ulong
xc_core_elf_pfn_to_page_index(ulong pfn)
{
int i, b, start_index;
off_t offset;
size_t size;
uint nr_pages;
ulong tmp;
struct xen_dumpcore_p2m p2m_batch[MAX_BATCH_SIZE];
offset = xd->xc_core.header.xch_index_offset;
nr_pages = xd->xc_core.header.xch_nr_pages;
/*
* Initialize the start_index.
*/
xd->xc_core.last_batch.accesses++;
start_index = 0;
if ((pfn >= xd->xc_core.last_batch.start) &&
(pfn <= xd->xc_core.last_batch.end)) {
xd->xc_core.last_batch.duplicates++;
start_index = xd->xc_core.last_batch.index;
} else {
for (i = 0; i <= INDEX_PFN_COUNT; i++) {
if ((i == INDEX_PFN_COUNT) ||
(pfn < xd->xc_core.elf_index_pfn[i].pfn)) {
if (--i < 0)
i = 0;
start_index = xd->xc_core.elf_index_pfn[i].index;
break;
}
}
}
offset += (start_index * sizeof(struct xen_dumpcore_p2m));
if (lseek(xd->xfd, offset, SEEK_SET) == -1)
error(FATAL, "cannot lseek to page index\n");
for (b = start_index; b < nr_pages; b += MAX_BATCH_SIZE) {
size = sizeof(struct xen_dumpcore_p2m) *
MIN(MAX_BATCH_SIZE, nr_pages - b);
if (read(xd->xfd, &p2m_batch[0], size) != size) {
error(INFO, "cannot read index page %d\n", b);
return PFN_NOT_FOUND;
}
for (i = 0; i < MAX_BATCH_SIZE; i++) {
if ((b+i) >= nr_pages)
break;
tmp = (ulong)p2m_batch[i].pfn;
if (tmp == pfn) {
if (CRASHDEBUG(4))
fprintf(xd->ofp,
"index: batch: %d found pfn %ld (0x%lx) at index %d\n",
b/MAX_BATCH_SIZE, pfn, pfn, i+b);
if ((b+MAX_BATCH_SIZE) < nr_pages) {
xd->xc_core.last_batch.index = b;
xd->xc_core.last_batch.start = p2m_batch[0].pfn;
xd->xc_core.last_batch.end = p2m_batch[MAX_BATCH_SIZE-1].pfn;
}
return (i+b);
}
}
}
return PFN_NOT_FOUND;
}
/*
* In xendumps containing INVALID_MFN markers in the page index,
* return the validity of the pfn.
*/
static int
xc_core_pfn_valid(ulong pfn)
{
ulong mfn;
off_t offset;
if (pfn >= (ulong)xd->xc_core.header.xch_nr_pages)
return FALSE;
offset = xd->xc_core.header.xch_index_offset;
if (xd->flags & XC_CORE_64BIT_HOST)
offset += (off_t)(pfn * sizeof(ulonglong));
else
offset += (off_t)(pfn * sizeof(ulong));
/*
* The lseek and read should never fail, so report
* any errors unconditionally.
*/
if (lseek(xd->xfd, offset, SEEK_SET) == -1) {
error(INFO,
"xendump: cannot lseek to page index for pfn %lx\n",
pfn);
return FALSE;
}
if (read(xd->xfd, &mfn, sizeof(ulong)) != sizeof(ulong)) {
error(INFO,
"xendump: cannot read index page for pfn %lx\n",
pfn);
return FALSE;
}
/*
* If it's an invalid mfn, let the caller decide whether
* to display an error message (unless debugging).
*/
if (mfn == INVALID_MFN) {
if (CRASHDEBUG(1) && !STREQ(pc->curcmd, "search"))
error(INFO,
"xendump: pfn %lx contains INVALID_MFN\n",
pfn);
return FALSE;
}
return TRUE;
}
/*
* Return the index into the .xen_pfn array containing the pfn.
* If not found, return PFN_NOT_FOUND.
*/
static ulong
xc_core_elf_pfn_valid(ulong pfn)
{
int i, b, start_index;
off_t offset;
size_t size;
uint nr_pages;
ulong tmp;
uint64_t pfn_batch[MAX_BATCH_SIZE];
offset = xd->xc_core.header.xch_index_offset;
nr_pages = xd->xc_core.header.xch_nr_pages;
/*
* Initialize the start_index.
*/
xd->xc_core.last_batch.accesses++;
start_index = 0;
if ((pfn >= xd->xc_core.last_batch.start) &&
(pfn <= xd->xc_core.last_batch.end)) {
xd->xc_core.last_batch.duplicates++;
start_index = xd->xc_core.last_batch.index;
} else {
for (i = 0; i <= INDEX_PFN_COUNT; i++) {
if ((i == INDEX_PFN_COUNT) ||
(pfn < xd->xc_core.elf_index_pfn[i].pfn)) {
if (--i < 0)
i = 0;
start_index = xd->xc_core.elf_index_pfn[i].index;
break;
}
}
}
offset += (start_index * sizeof(uint64_t));
if (lseek(xd->xfd, offset, SEEK_SET) == -1)
error(FATAL, "cannot lseek to page index\n");
for (b = start_index; b < nr_pages; b += MAX_BATCH_SIZE) {
size = sizeof(uint64_t) * MIN(MAX_BATCH_SIZE, nr_pages - b);
if (read(xd->xfd, &pfn_batch[0], size) != size) {
error(INFO, "cannot read index page %d\n", b);
return PFN_NOT_FOUND;
}
for (i = 0; i < MAX_BATCH_SIZE; i++) {
if ((b+i) >= nr_pages)
break;
tmp = (ulong)pfn_batch[i];
if (tmp == pfn) {
if (CRASHDEBUG(4))
fprintf(xd->ofp,
"index: batch: %d found pfn %ld (0x%lx) at index %d\n",
b/MAX_BATCH_SIZE, pfn, pfn, i+b);
if ((b+MAX_BATCH_SIZE) < nr_pages) {
xd->xc_core.last_batch.index = b;
xd->xc_core.last_batch.start = (ulong)pfn_batch[0];
xd->xc_core.last_batch.end = (ulong)pfn_batch[MAX_BATCH_SIZE-1];
}
return (i+b);
}
}
}
return PFN_NOT_FOUND;
}
/*
* Store the panic task's stack hooks from where it was found
* in get_active_set_panic_task().
*/
void
xendump_panic_hook(char *stack)
{
int i, err, argc;
char *arglist[MAXARGS];
char buf[BUFSIZE];
ulong value, *sp;
if (machine_type("IA64")) /* needs switch_stack address */
return;
strcpy(buf, stack);
argc = parse_line(buf, arglist);
if ((value = htol(strip_ending_char(arglist[0], ':'),
RETURN_ON_ERROR, &err)) == BADADDR)
return;
for (sp = (ulong *)value, i = 1; i < argc; i++, sp++) {
if (strstr(arglist[i], "xen_panic_event")) {
if (!readmem((ulong)sp, KVADDR, &value,
sizeof(ulong), "xen_panic_event address",
RETURN_ON_ERROR))
return;
xd->panic_sp = (ulong)sp;
xd->panic_pc = value;
} else if (strstr(arglist[i], "panic") && !xd->panic_sp) {
if (!readmem((ulong)sp, KVADDR, &value,
sizeof(ulong), "xen_panic_event address",
RETURN_ON_ERROR))
return;
xd->panic_sp = (ulong)sp;
xd->panic_pc = value;
}
}
}
static void
xendump_print(char *fmt, ...)
{
char buf[BUFSIZE];
va_list ap;
if (!fmt || !strlen(fmt))
return;
va_start(ap, fmt);
(void)vsnprintf(buf, BUFSIZE, fmt, ap);
va_end(ap);
if (xd->ofp)
fprintf(xd->ofp, "%s", buf);
else if (!XENDUMP_VALID() && CRASHDEBUG(7))
fprintf(stderr, "%s", buf);
}
/*
* Support for xc_core ELF dumpfile format.
*/
static int
xc_core_elf_verify(char *file, char *buf)
{
int i;
Elf32_Ehdr *elf32;
Elf64_Ehdr *elf64;
Elf32_Off offset32;
Elf64_Off offset64;
char *eheader;
int swap;
eheader = buf;
if (!STRNEQ(eheader, ELFMAG) || eheader[EI_VERSION] != EV_CURRENT)
goto bailout;
swap = (((eheader[EI_DATA] == ELFDATA2LSB) &&
(__BYTE_ORDER == __BIG_ENDIAN)) ||
((eheader[EI_DATA] == ELFDATA2MSB) &&
(__BYTE_ORDER == __LITTLE_ENDIAN)));
elf32 = (Elf32_Ehdr *)buf;
elf64 = (Elf64_Ehdr *)buf;
if ((elf32->e_ident[EI_CLASS] == ELFCLASS32) &&
(swap16(elf32->e_type, swap) == ET_CORE) &&
(swap32(elf32->e_version, swap) == EV_CURRENT) &&
(swap16(elf32->e_shnum, swap) > 0)) {
switch (swap16(elf32->e_machine, swap))
{
case EM_386:
if (machine_type_mismatch(file, "X86", NULL, 0))
goto bailout;
break;
default:
if (machine_type_mismatch(file, "(unknown)", NULL, 0))
goto bailout;
break;
}
if (endian_mismatch(file, elf32->e_ident[EI_DATA], 0))
goto bailout;
xd->xc_core.elf_class = ELFCLASS32;
if ((xd->xc_core.elf32 = (Elf32_Ehdr *)malloc(sizeof(Elf32_Ehdr))) == NULL) {
fprintf(stderr, "cannot malloc ELF header buffer\n");
clean_exit(1);
}
BCOPY(buf, xd->xc_core.elf32, sizeof(Elf32_Ehdr));
} else if ((elf64->e_ident[EI_CLASS] == ELFCLASS64) &&
(swap16(elf64->e_type, swap) == ET_CORE) &&
(swap32(elf64->e_version, swap) == EV_CURRENT) &&
(swap16(elf64->e_shnum, swap) > 0)) {
switch (swap16(elf64->e_machine, swap))
{
case EM_IA_64:
if (machine_type_mismatch(file, "IA64", NULL, 0))
goto bailout;
break;
case EM_X86_64:
if (machine_type_mismatch(file, "X86_64", "X86", 0))
goto bailout;
break;
case EM_386:
if (machine_type_mismatch(file, "X86", NULL, 0))
goto bailout;
break;
case EM_ARM:
if (machine_type_mismatch(file, "ARM", NULL, 0))
goto bailout;
break;
case EM_AARCH64:
if (machine_type_mismatch(file, "ARM64", NULL, 0))
goto bailout;
break;
default:
if (machine_type_mismatch(file, "(unknown)", NULL, 0))
goto bailout;
}
if (endian_mismatch(file, elf64->e_ident[EI_DATA], 0))
goto bailout;
xd->xc_core.elf_class = ELFCLASS64;
if ((xd->xc_core.elf64 = (Elf64_Ehdr *)malloc(sizeof(Elf64_Ehdr))) == NULL) {
fprintf(stderr, "cannot malloc ELF header buffer\n");
clean_exit(1);
}
BCOPY(buf, xd->xc_core.elf64, sizeof(Elf64_Ehdr));
} else {
if (CRASHDEBUG(1))
error(INFO, "%s: not a xen ELF core file\n", file);
goto bailout;
}
xc_core_elf_dump();
switch (xd->xc_core.elf_class)
{
case ELFCLASS32:
offset32 = xd->xc_core.elf32->e_shoff;
for (i = 0; i < xd->xc_core.elf32->e_shnum; i++) {
xc_core_dump_Elf32_Shdr(offset32, ELFSTORE);
offset32 += xd->xc_core.elf32->e_shentsize;
}
xendump_print("\n");
break;
case ELFCLASS64:
offset64 = xd->xc_core.elf64->e_shoff;
for (i = 0; i < xd->xc_core.elf64->e_shnum; i++) {
xc_core_dump_Elf64_Shdr(offset64, ELFSTORE);
offset64 += xd->xc_core.elf64->e_shentsize;
}
xendump_print("\n");
break;
}
xd->flags |= (XENDUMP_LOCAL | XC_CORE_ELF);
if (!xd->page_size)
error(FATAL,
"unknown page size: use -p <pagesize> command line option\n");
if (!(xd->page = (char *)malloc(xd->page_size)))
error(FATAL, "cannot malloc page space.");
if (!(xd->poc = (struct pfn_offset_cache *)calloc
(PFN_TO_OFFSET_CACHE_ENTRIES,
sizeof(struct pfn_offset_cache))))
error(FATAL, "cannot malloc pfn_offset_cache\n");
xd->last_pfn = ~(0UL);
for (i = 0; i < INDEX_PFN_COUNT; i++)
xd->xc_core.elf_index_pfn[i].pfn = ~0UL;
if (CRASHDEBUG(1))
xendump_memory_dump(fp);
return TRUE;
bailout:
return FALSE;
}
/*
* Dump the relevant ELF header.
*/
static void
xc_core_elf_dump(void)
{
switch (xd->xc_core.elf_class)
{
case ELFCLASS32:
xc_core_dump_Elf32_Ehdr(xd->xc_core.elf32);
break;
case ELFCLASS64:
xc_core_dump_Elf64_Ehdr(xd->xc_core.elf64);
break;
}
}
/*
* Dump the 32-bit ELF header, and grab a pointer to the strtab section.
*/
static void
xc_core_dump_Elf32_Ehdr(Elf32_Ehdr *elf)
{
char buf[BUFSIZE];
Elf32_Off offset32;
Elf32_Shdr shdr;
BZERO(buf, BUFSIZE);
BCOPY(elf->e_ident, buf, SELFMAG);
xendump_print("\nElf32_Ehdr:\n");
xendump_print(" e_ident: \\%o%s\n", buf[0],
&buf[1]);
xendump_print(" e_ident[EI_CLASS]: %d ", elf->e_ident[EI_CLASS]);
switch (elf->e_ident[EI_CLASS])
{
case ELFCLASSNONE:
xendump_print("(ELFCLASSNONE)");
break;
case ELFCLASS32:
xendump_print("(ELFCLASS32)\n");
break;
case ELFCLASS64:
xendump_print("(ELFCLASS64)\n");
break;
case ELFCLASSNUM:
xendump_print("(ELFCLASSNUM)\n");
break;
default:
xendump_print("(?)\n");
break;
}
xendump_print(" e_ident[EI_DATA]: %d ", elf->e_ident[EI_DATA]);
switch (elf->e_ident[EI_DATA])
{
case ELFDATANONE:
xendump_print("(ELFDATANONE)\n");
break;
case ELFDATA2LSB:
xendump_print("(ELFDATA2LSB)\n");
break;
case ELFDATA2MSB:
xendump_print("(ELFDATA2MSB)\n");
break;
case ELFDATANUM:
xendump_print("(ELFDATANUM)\n");
break;
default:
xendump_print("(?)\n");
}
xendump_print(" e_ident[EI_VERSION]: %d ",
elf->e_ident[EI_VERSION]);
if (elf->e_ident[EI_VERSION] == EV_CURRENT)
xendump_print("(EV_CURRENT)\n");
else
xendump_print("(?)\n");
xendump_print(" e_ident[EI_OSABI]: %d ", elf->e_ident[EI_OSABI]);
switch (elf->e_ident[EI_OSABI])
{
case ELFOSABI_SYSV:
xendump_print("(ELFOSABI_SYSV)\n");
break;
case ELFOSABI_HPUX:
xendump_print("(ELFOSABI_HPUX)\n");
break;
case ELFOSABI_ARM:
xendump_print("(ELFOSABI_ARM)\n");
break;
case ELFOSABI_STANDALONE:
xendump_print("(ELFOSABI_STANDALONE)\n");
break;
default:
xendump_print("(?)\n");
}
xendump_print(" e_ident[EI_ABIVERSION]: %d\n",
elf->e_ident[EI_ABIVERSION]);
xendump_print(" e_type: %d ", elf->e_type);
switch (elf->e_type)
{
case ET_NONE:
xendump_print("(ET_NONE)\n");
break;
case ET_REL:
xendump_print("(ET_REL)\n");
break;
case ET_EXEC:
xendump_print("(ET_EXEC)\n");
break;
case ET_DYN:
xendump_print("(ET_DYN)\n");
break;
case ET_CORE:
xendump_print("(ET_CORE)\n");
break;
case ET_NUM:
xendump_print("(ET_NUM)\n");
break;
case ET_LOOS:
xendump_print("(ET_LOOS)\n");
break;
case ET_HIOS:
xendump_print("(ET_HIOS)\n");
break;
case ET_LOPROC:
xendump_print("(ET_LOPROC)\n");
break;
case ET_HIPROC:
xendump_print("(ET_HIPROC)\n");
break;
default:
xendump_print("(?)\n");
}
xendump_print(" e_machine: %d ", elf->e_machine);
switch (elf->e_machine)
{
case EM_386:
xendump_print("(EM_386)\n");
break;
default:
xendump_print("(unsupported)\n");
break;
}
xendump_print(" e_version: %ld ", (ulong)elf->e_version);
xendump_print("%s\n", elf->e_version == EV_CURRENT ?
"(EV_CURRENT)" : "");
xendump_print(" e_entry: %lx\n", (ulong)elf->e_entry);
xendump_print(" e_phoff: %lx\n", (ulong)elf->e_phoff);
xendump_print(" e_shoff: %lx\n", (ulong)elf->e_shoff);
xendump_print(" e_flags: %lx\n", (ulong)elf->e_flags);
xendump_print(" e_ehsize: %x\n", elf->e_ehsize);
xendump_print(" e_phentsize: %x\n", elf->e_phentsize);
xendump_print(" e_phnum: %x\n", elf->e_phnum);
xendump_print(" e_shentsize: %x\n", elf->e_shentsize);
xendump_print(" e_shnum: %x\n", elf->e_shnum);
xendump_print(" e_shstrndx: %x\n", elf->e_shstrndx);
/* Determine the strtab location. */
offset32 = elf->e_shoff +
(elf->e_shstrndx * elf->e_shentsize);
if (lseek(xd->xfd, offset32, SEEK_SET) != offset32)
error(FATAL,
"xc_core_dump_Elf32_Ehdr: cannot seek to strtab Elf32_Shdr\n");
if (read(xd->xfd, &shdr, sizeof(Elf32_Shdr)) != sizeof(Elf32_Shdr))
error(FATAL,
"xc_core_dump_Elf32_Ehdr: cannot read strtab Elf32_Shdr\n");
xd->xc_core.elf_strtab_offset = (ulonglong)shdr.sh_offset;
}
/*
* Dump the 64-bit ELF header, and grab a pointer to the strtab section.
*/
static void
xc_core_dump_Elf64_Ehdr(Elf64_Ehdr *elf)
{
char buf[BUFSIZE];
Elf64_Off offset64;
Elf64_Shdr shdr;
BZERO(buf, BUFSIZE);
BCOPY(elf->e_ident, buf, SELFMAG);
xendump_print("\nElf64_Ehdr:\n");
xendump_print(" e_ident: \\%o%s\n", buf[0],
&buf[1]);
xendump_print(" e_ident[EI_CLASS]: %d ", elf->e_ident[EI_CLASS]);
switch (elf->e_ident[EI_CLASS])
{
case ELFCLASSNONE:
xendump_print("(ELFCLASSNONE)");
break;
case ELFCLASS32:
xendump_print("(ELFCLASS32)\n");
break;
case ELFCLASS64:
xendump_print("(ELFCLASS64)\n");
break;
case ELFCLASSNUM:
xendump_print("(ELFCLASSNUM)\n");
break;
default:
xendump_print("(?)\n");
break;
}
xendump_print(" e_ident[EI_DATA]: %d ", elf->e_ident[EI_DATA]);
switch (elf->e_ident[EI_DATA])
{
case ELFDATANONE:
xendump_print("(ELFDATANONE)\n");
break;
case ELFDATA2LSB:
xendump_print("(ELFDATA2LSB)\n");
break;
case ELFDATA2MSB:
xendump_print("(ELFDATA2MSB)\n");
break;
case ELFDATANUM:
xendump_print("(ELFDATANUM)\n");
break;
default:
xendump_print("(?)\n");
}
xendump_print(" e_ident[EI_VERSION]: %d ",
elf->e_ident[EI_VERSION]);
if (elf->e_ident[EI_VERSION] == EV_CURRENT)
xendump_print("(EV_CURRENT)\n");
else
xendump_print("(?)\n");
xendump_print(" e_ident[EI_OSABI]: %d ", elf->e_ident[EI_OSABI]);
switch (elf->e_ident[EI_OSABI])
{
case ELFOSABI_SYSV:
xendump_print("(ELFOSABI_SYSV)\n");
break;
case ELFOSABI_HPUX:
xendump_print("(ELFOSABI_HPUX)\n");
break;
case ELFOSABI_ARM:
xendump_print("(ELFOSABI_ARM)\n");
break;
case ELFOSABI_STANDALONE:
xendump_print("(ELFOSABI_STANDALONE)\n");
break;
default:
xendump_print("(?)\n");
}
xendump_print(" e_ident[EI_ABIVERSION]: %d\n",
elf->e_ident[EI_ABIVERSION]);
xendump_print(" e_type: %d ", elf->e_type);
switch (elf->e_type)
{
case ET_NONE:
xendump_print("(ET_NONE)\n");
break;
case ET_REL:
xendump_print("(ET_REL)\n");
break;
case ET_EXEC:
xendump_print("(ET_EXEC)\n");
break;
case ET_DYN:
xendump_print("(ET_DYN)\n");
break;
case ET_CORE:
xendump_print("(ET_CORE)\n");
break;
case ET_NUM:
xendump_print("(ET_NUM)\n");
break;
case ET_LOOS:
xendump_print("(ET_LOOS)\n");
break;
case ET_HIOS:
xendump_print("(ET_HIOS)\n");
break;
case ET_LOPROC:
xendump_print("(ET_LOPROC)\n");
break;
case ET_HIPROC:
xendump_print("(ET_HIPROC)\n");
break;
default:
xendump_print("(?)\n");
}
xendump_print(" e_machine: %d ", elf->e_machine);
switch (elf->e_machine)
{
case EM_386:
xendump_print("(EM_386)\n");
break;
case EM_IA_64:
xendump_print("(EM_IA_64)\n");
break;
case EM_PPC64:
xendump_print("(EM_PPC64)\n");
break;
case EM_X86_64:
xendump_print("(EM_X86_64)\n");
break;
default:
xendump_print("(unsupported)\n");
break;
}
xendump_print(" e_version: %ld ", (ulong)elf->e_version);
xendump_print("%s\n", elf->e_version == EV_CURRENT ?
"(EV_CURRENT)" : "");
xendump_print(" e_entry: %lx\n", (ulong)elf->e_entry);
xendump_print(" e_phoff: %lx\n", (ulong)elf->e_phoff);
xendump_print(" e_shoff: %lx\n", (ulong)elf->e_shoff);
xendump_print(" e_flags: %lx\n", (ulong)elf->e_flags);
xendump_print(" e_ehsize: %x\n", elf->e_ehsize);
xendump_print(" e_phentsize: %x\n", elf->e_phentsize);
xendump_print(" e_phnum: %x\n", elf->e_phnum);
xendump_print(" e_shentsize: %x\n", elf->e_shentsize);
xendump_print(" e_shnum: %x\n", elf->e_shnum);
xendump_print(" e_shstrndx: %x\n", elf->e_shstrndx);
/* Determine the strtab location. */
offset64 = elf->e_shoff +
(elf->e_shstrndx * elf->e_shentsize);
if (lseek(xd->xfd, offset64, SEEK_SET) != offset64)
error(FATAL,
"xc_core_dump_Elf64_Ehdr: cannot seek to strtab Elf32_Shdr\n");
if (read(xd->xfd, &shdr, sizeof(Elf32_Shdr)) != sizeof(Elf32_Shdr))
error(FATAL,
"xc_core_dump_Elf64_Ehdr: cannot read strtab Elf32_Shdr\n");
xd->xc_core.elf_strtab_offset = (ulonglong)shdr.sh_offset;
}
/*
* Dump each 32-bit section header and the data that they reference.
*/
static void
xc_core_dump_Elf32_Shdr(Elf32_Off offset, int store)
{
Elf32_Shdr shdr;
char name[BUFSIZE];
int i;
char c;
if (lseek(xd->xfd, offset, SEEK_SET) != offset)
error(FATAL,
"xc_core_dump_Elf32_Shdr: cannot seek to Elf32_Shdr\n");
if (read(xd->xfd, &shdr, sizeof(Elf32_Shdr)) != sizeof(Elf32_Shdr))
error(FATAL,
"xc_core_dump_Elf32_Shdr: cannot read Elf32_Shdr\n");
xendump_print("\nElf32_Shdr:\n");
xendump_print(" sh_name: %lx ", shdr.sh_name);
xendump_print("\"%s\"\n", xc_core_strtab(shdr.sh_name, name));
xendump_print(" sh_type: %lx ", shdr.sh_type);
switch (shdr.sh_type)
{
case SHT_NULL:
xendump_print("(SHT_NULL)\n");
break;
case SHT_PROGBITS:
xendump_print("(SHT_PROGBITS)\n");
break;
case SHT_STRTAB:
xendump_print("(SHT_STRTAB)\n");
break;
case SHT_NOTE:
xendump_print("(SHT_NOTE)\n");
break;
default:
xendump_print("\n");
break;
}
xendump_print(" sh_flags: %lx\n", shdr.sh_flags);
xendump_print(" sh_addr: %lx\n", shdr.sh_addr);
xendump_print(" sh_offset: %lx\n", shdr.sh_offset);
xendump_print(" sh_size: %lx\n", shdr.sh_size);
xendump_print(" sh_link: %lx\n", shdr.sh_link);
xendump_print(" sh_info: %lx\n", shdr.sh_info);
xendump_print(" sh_addralign: %lx\n", shdr.sh_addralign);
xendump_print(" sh_entsize: %lx\n", shdr.sh_entsize);
if (STREQ(name, ".shstrtab")) {
if (lseek(xd->xfd, xd->xc_core.elf_strtab_offset, SEEK_SET) !=
xd->xc_core.elf_strtab_offset)
error(FATAL,
"xc_core_dump_Elf32_Shdr: cannot seek to strtab data\n");
xendump_print(" ");
for (i = 0; i < shdr.sh_size; i++) {
if (read(xd->xfd, &c, sizeof(char)) != sizeof(char))
error(FATAL,
"xc_core_dump_Elf32_Shdr: cannot read strtab data\n");
if (i && !c)
xendump_print("\n ");
else
xendump_print("%c", c);
}
}
if (STREQ(name, ".note.Xen"))
xc_core_dump_elfnote((off_t)shdr.sh_offset,
(size_t)shdr.sh_size, store);
if (!store)
return;
if (STREQ(name, ".xen_prstatus"))
xd->xc_core.header.xch_ctxt_offset =
(off_t)shdr.sh_offset;
if (STREQ(name, ".xen_shared_info"))
xd->xc_core.shared_info_offset = (off_t)shdr.sh_offset;
if (STREQ(name, ".xen_pfn")) {
xd->xc_core.header.xch_index_offset =
(off_t)shdr.sh_offset;
xd->flags |= (XC_CORE_NO_P2M|XC_CORE_PFN_CREATE);
}
if (STREQ(name, ".xen_p2m")) {
xd->xc_core.header.xch_index_offset =
(off_t)shdr.sh_offset;
xd->flags |= XC_CORE_PFN_CREATE;
}
if (STREQ(name, ".xen_pages"))
xd->xc_core.header.xch_pages_offset =
(off_t)shdr.sh_offset;
if (STREQ(name, ".xen_ia64_mapped_regs"))
xd->xc_core.ia64_mapped_regs_offset =
(off_t)shdr.sh_offset;
}
/*
* Dump each 64-bit section header and the data that they reference.
*/
static void
xc_core_dump_Elf64_Shdr(Elf64_Off offset, int store)
{
Elf64_Shdr shdr;
char name[BUFSIZE];
int i;
char c;
if (lseek(xd->xfd, offset, SEEK_SET) != offset)
error(FATAL,
"xc_core_dump_Elf64_Shdr: cannot seek to Elf64_Shdr\n");
if (read(xd->xfd, &shdr, sizeof(Elf64_Shdr)) != sizeof(Elf64_Shdr))
error(FATAL,
"xc_core_dump_Elf64_Shdr: cannot read Elf64_Shdr\n");
xendump_print("\nElf64_Shdr:\n");
xendump_print(" sh_name: %x ", shdr.sh_name);
xendump_print("\"%s\"\n", xc_core_strtab(shdr.sh_name, name));
xendump_print(" sh_type: %x ", shdr.sh_type);
switch (shdr.sh_type)
{
case SHT_NULL:
xendump_print("(SHT_NULL)\n");
break;
case SHT_PROGBITS:
xendump_print("(SHT_PROGBITS)\n");
break;
case SHT_STRTAB:
xendump_print("(SHT_STRTAB)\n");
break;
case SHT_NOTE:
xendump_print("(SHT_NOTE)\n");
break;
default:
xendump_print("\n");
break;
}
xendump_print(" sh_flags: %lx\n", shdr.sh_flags);
xendump_print(" sh_addr: %lx\n", shdr.sh_addr);
xendump_print(" sh_offset: %lx\n", shdr.sh_offset);
xendump_print(" sh_size: %lx\n", shdr.sh_size);
xendump_print(" sh_link: %x\n", shdr.sh_link);
xendump_print(" sh_info: %x\n", shdr.sh_info);
xendump_print(" sh_addralign: %lx\n", shdr.sh_addralign);
xendump_print(" sh_entsize: %lx\n", shdr.sh_entsize);
if (STREQ(name, ".shstrtab")) {
if (lseek(xd->xfd, xd->xc_core.elf_strtab_offset, SEEK_SET) !=
xd->xc_core.elf_strtab_offset)
error(FATAL,
"xc_core_dump_Elf64_Shdr: cannot seek to strtab data\n");
xendump_print(" ");
for (i = 0; i < shdr.sh_size; i++) {
if (read(xd->xfd, &c, sizeof(char)) != sizeof(char))
error(FATAL,
"xc_core_dump_Elf64_Shdr: cannot read strtab data\n");
if (i && !c)
xendump_print("\n ");
else
xendump_print("%c", c);
}
}
if (STREQ(name, ".note.Xen"))
xc_core_dump_elfnote((off_t)shdr.sh_offset,
(size_t)shdr.sh_size, store);
if (!store)
return;
if (STREQ(name, ".xen_prstatus"))
xd->xc_core.header.xch_ctxt_offset =
(off_t)shdr.sh_offset;
if (STREQ(name, ".xen_shared_info"))
xd->xc_core.shared_info_offset = (off_t)shdr.sh_offset;
if (STREQ(name, ".xen_pfn")) {
xd->xc_core.header.xch_index_offset =
(off_t)shdr.sh_offset;
xd->flags |= (XC_CORE_NO_P2M|XC_CORE_PFN_CREATE);
}
if (STREQ(name, ".xen_p2m")) {
xd->xc_core.header.xch_index_offset =
(off_t)shdr.sh_offset;
xd->flags |= XC_CORE_PFN_CREATE;
}
if (STREQ(name, ".xen_pages"))
xd->xc_core.header.xch_pages_offset =
(off_t)shdr.sh_offset;
if (STREQ(name, ".xen_ia64_mapped_regs"))
xd->xc_core.ia64_mapped_regs_offset =
(off_t)shdr.sh_offset;
}
/*
* Return the string found at the specified index into
* the dumpfile's strtab.
*/
static char *
xc_core_strtab(uint32_t index, char *buf)
{
off_t offset;
int i;
offset = xd->xc_core.elf_strtab_offset + index;
if (lseek(xd->xfd, offset, SEEK_SET) != offset)
error(FATAL,
"xc_core_strtab: cannot seek to Elf64_Shdr\n");
BZERO(buf, BUFSIZE);
i = 0;
while (read(xd->xfd, &buf[i], sizeof(char)) == sizeof(char)) {
if (buf[i] == NULLCHAR)
break;
i++;
}
return buf;
}
/*
* Dump the array of elfnote structures, storing relevant info
* when requested during initialization. This function is
* common to both 32-bit and 64-bit ELF files.
*/
static void
xc_core_dump_elfnote(off_t sh_offset, size_t sh_size, int store)
{
int i, lf, index;
char *notes_buffer;
struct elfnote *elfnote;
ulonglong *data;
struct xen_dumpcore_elfnote_header_desc *elfnote_header;
struct xen_dumpcore_elfnote_format_version_desc *format_version;
elfnote_header = NULL;
format_version = NULL;
if (!(notes_buffer = (char *)malloc(sh_size)))
error(FATAL, "cannot malloc notes space.");
if (lseek(xd->xfd, sh_offset, SEEK_SET) != sh_offset)
error(FATAL,
"xc_core_dump_elfnote: cannot seek to sh_offset\n");
if (read(xd->xfd, notes_buffer, sh_size) != sh_size)
error(FATAL,
"xc_core_dump_elfnote: cannot read elfnote data\n");
for (index = 0; index < sh_size; ) {
elfnote = (struct elfnote *)&notes_buffer[index];
xendump_print(" namesz: %d\n", elfnote->namesz);
xendump_print(" descz: %d\n", elfnote->descsz);
xendump_print(" type: %x ", elfnote->type);
switch (elfnote->type)
{
case XEN_ELFNOTE_DUMPCORE_NONE:
xendump_print("(XEN_ELFNOTE_DUMPCORE_NONE)\n");
break;
case XEN_ELFNOTE_DUMPCORE_HEADER:
xendump_print("(XEN_ELFNOTE_DUMPCORE_HEADER)\n");
elfnote_header = (struct xen_dumpcore_elfnote_header_desc *)
(elfnote+1);
break;
case XEN_ELFNOTE_DUMPCORE_XEN_VERSION:
xendump_print("(XEN_ELFNOTE_DUMPCORE_XEN_VERSION)\n");
break;
case XEN_ELFNOTE_DUMPCORE_FORMAT_VERSION:
xendump_print("(XEN_ELFNOTE_DUMPCORE_FORMAT_VERSION)\n");
format_version = (struct xen_dumpcore_elfnote_format_version_desc *)
(elfnote+1);
break;
default:
xendump_print("(unknown)\n");
break;
}
xendump_print(" name: %s\n", elfnote->name);
data = (ulonglong *)(elfnote+1);
for (i = lf = 0; i < elfnote->descsz/sizeof(ulonglong); i++) {
if (((i%2)==0)) {
xendump_print("%s ",
i ? "\n" : "");
lf++;
} else
lf = 0;
xendump_print("%016llx ", *data++);
}
if (!elfnote->descsz)
xendump_print(" (empty)");
xendump_print("\n");
index += sizeof(struct elfnote) + elfnote->descsz;
}
if (!store) {
free(notes_buffer);
return;
}
if (elfnote_header) {
xd->xc_core.header.xch_magic = elfnote_header->xch_magic;
xd->xc_core.header.xch_nr_vcpus = elfnote_header->xch_nr_vcpus;
xd->xc_core.header.xch_nr_pages = elfnote_header->xch_nr_pages;
xd->page_size = elfnote_header->xch_page_size;
}
if (format_version) {
switch (format_version->version)
{
case FORMAT_VERSION_0000000000000001:
break;
default:
error(WARNING,
"unsupported xen dump-core format version: %016llx\n",
format_version->version);
}
xd->xc_core.format_version = format_version->version;
}
free(notes_buffer);
}
/*
* Initialize the batching list for the .xen_p2m or .xen_pfn
* arrays.
*/
static void
xc_core_elf_pfn_init(void)
{
int i, c, chunk;
off_t offset;
struct xen_dumpcore_p2m p2m;
uint64_t pfn;
switch (xd->flags & (XC_CORE_ELF|XC_CORE_NO_P2M))
{
case (XC_CORE_ELF|XC_CORE_NO_P2M):
chunk = xd->xc_core.header.xch_nr_pages/INDEX_PFN_COUNT;
for (i = c = 0; i < INDEX_PFN_COUNT; i++, c += chunk) {
offset = xd->xc_core.header.xch_index_offset +
(off_t)(c * sizeof(uint64_t));
if (lseek(xd->xfd, offset, SEEK_SET) == -1)
error(FATAL,
"cannot lseek to page index %d\n", c);
if (read(xd->xfd, &pfn, sizeof(uint64_t)) !=
sizeof(uint64_t))
error(FATAL,
"cannot read page index %d\n", c);
xd->xc_core.elf_index_pfn[i].index = c;
xd->xc_core.elf_index_pfn[i].pfn = (ulong)pfn;
}
break;
case XC_CORE_ELF:
chunk = xd->xc_core.header.xch_nr_pages/INDEX_PFN_COUNT;
for (i = c = 0; i < INDEX_PFN_COUNT; i++, c += chunk) {
offset = xd->xc_core.header.xch_index_offset +
(off_t)(c * sizeof(struct xen_dumpcore_p2m));
if (lseek(xd->xfd, offset, SEEK_SET) == -1)
error(FATAL,
"cannot lseek to page index %d\n", c);
if (read(xd->xfd, &p2m, sizeof(struct xen_dumpcore_p2m)) !=
sizeof(struct xen_dumpcore_p2m))
error(FATAL,
"cannot read page index %d\n", c);
xd->xc_core.elf_index_pfn[i].index = c;
xd->xc_core.elf_index_pfn[i].pfn = (ulong)p2m.pfn;
}
break;
}
}
struct xendump_data *
get_xendump_data(void)
{
return (XENDUMP_VALID() ? xd : NULL);
}