/* * 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 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 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 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 *)¬es_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); }