crash/vmware_vmss.c
Dave Anderson 5d172b230c Commit 45b74b8953 added support for
calculating phys_base and the mapped kernel offset for KASLR-enabled
kernels on SADUMP dumpfiles by using a technique developed by Takao
Indoh. Originally, the patchset included support for kdumps, but this
was dropped in v2, as it was deemed unnecessary due to the upstream
implementation of the "vmcoreinfo device" in QEMU.  However, there
are still several reasons for which the vmcoreinfo device may not be
present at the time when a memory dump is taken from a VM, ranging
from a host running older QEMU/libvirt versions, to misconfigured VMs
or environments running Hypervisors that doesn't support this device.
This patchset generalizes the KASLR-related functions from sadump.c
and moves them to kaslr_helper.c, and makes kdump analysis fall back
to KASLR offset calculation if vmcoreinfo data is missing.
(slp@redhat.com)
2018-03-29 10:26:29 -04:00

896 lines
24 KiB
C

/*
* vmware_vmss.c
*
* Copyright (c) 2015 VMware, Inc.
* Copyright (c) 2018 Red Hat Inc.
*
* 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.
*
* Authors: Dyno Hongjun Fu <hfu@vmware.com>
* Sergio Lopez <slp@redhat.com>
*/
#include "defs.h"
#include "vmware_vmss.h"
#define LOGPRX "vmw: "
/* VMware only supports X86/X86_64 virtual machines. */
#define VMW_PAGE_SIZE (4096)
#define VMW_PAGE_SHIFT (12)
#define MAX_BLOCK_DUMP (128)
static vmssdata vmss = { 0 };
int
is_vmware_vmss(char *filename)
{
struct cptdumpheader hdr;
FILE *fp;
if ((fp = fopen(filename, "r")) == NULL) {
error(INFO, LOGPRX"Failed to open '%s': [Error %d] %s\n",
filename, errno, strerror(errno));
return FALSE;
}
if (fread(&hdr, sizeof(cptdumpheader), 1, fp) != 1) {
error(INFO, LOGPRX"Failed to read '%s': [Error %d] %s\n",
filename, errno, strerror(errno));
fclose(fp);
return FALSE;
}
fclose(fp);
if (hdr.id != CPTDUMP_OLD_MAGIC_NUMBER &&
hdr.id != CPTDUMP_MAGIC_NUMBER &&
hdr.id != CPTDUMP_PARTIAL_MAGIC_NUMBER &&
hdr.id != CPTDUMP_RESTORED_MAGIC_NUMBER &&
hdr.id != CPTDUMP_NORESTORE_MAGIC_NUMBER) {
if (CRASHDEBUG(1))
error(INFO, LOGPRX"Unrecognized .vmss file (magic %x).\n", hdr.id);
return FALSE;
}
return TRUE;
}
int
vmware_vmss_init(char *filename, FILE *ofp)
{
cptdumpheader hdr;
cptgroupdesc *grps = NULL;
unsigned grpsize;
unsigned i;
FILE *fp = NULL;
int result = TRUE;
if (!machine_type("X86") && !machine_type("X86_64")) {
error(INFO,
LOGPRX"Invalid or unsupported host architecture for .vmss file: %s\n",
MACHINE_TYPE);
result = FALSE;
goto exit;
}
if ((fp = fopen(filename, "r")) == NULL) {
error(INFO, LOGPRX"Failed to open '%s': %s [Error %d] %s\n",
filename, errno, strerror(errno));
result = FALSE;
goto exit;
}
if (fread(&hdr, sizeof(cptdumpheader), 1, fp) != 1) {
error(INFO, LOGPRX"Failed to read '%s': %s [Error %d] %s\n",
filename, errno, strerror(errno));
result = FALSE;
goto exit;
}
DEBUG_PARSE_PRINT((ofp, LOGPRX"Header: id=%x version=%d numgroups=%d\n",
hdr.id, hdr.version, hdr.numgroups));
vmss.cpt64bit = (hdr.id != CPTDUMP_OLD_MAGIC_NUMBER);
DEBUG_PARSE_PRINT((ofp, LOGPRX"Checkpoint is %d-bit\n", vmss.cpt64bit ? 64 : 32));
if (!vmss.cpt64bit) {
error(INFO, LOGPRX"Not implemented for 32-bit VMSS file!\n");
result = FALSE;
goto exit;
}
grpsize = hdr.numgroups * sizeof (cptgroupdesc);
grps = (cptgroupdesc *) malloc(grpsize * sizeof(cptgroupdesc));
if (grps == NULL) {
error(INFO, LOGPRX"Failed to allocate memory! [Error %d] %s\n",
errno, strerror(errno));
result = FALSE;
goto exit;
}
if (fread(grps, sizeof(cptgroupdesc), grpsize, fp) != grpsize) {
error(INFO, LOGPRX"Failed to read '%s': [Error %d] %s\n",
filename, errno, strerror(errno));
result = FALSE;
goto exit;
}
for (i = 0; i < hdr.numgroups; i++) {
if (fseek(fp, grps[i].position, SEEK_SET) == -1) {
error(INFO, LOGPRX"Bad offset of VMSS Group['%s'] in '%s' at %#llx.\n",
grps[i].name, filename, (ulonglong)grps[i].position);
continue;
}
DEBUG_PARSE_PRINT((ofp, LOGPRX"Group: %-20s offset=%#llx size=0x%#llx.\n",
grps[i].name, (ulonglong)grps[i].position, (ulonglong)grps[i].size));
if (strcmp(grps[i].name, "memory") != 0 &&
(strcmp(grps[i].name, "cpu") != 0 || !machine_type("X86_64"))) {
continue;
}
for (;;) {
uint16_t tag;
char name[TAG_NAMELEN_MASK + 1];
unsigned nameLen;
unsigned nindx;
int idx[3];
unsigned j;
int nextgroup = FALSE;
if (fread(&tag, sizeof(tag), 1, fp) != 1) {
error(INFO, LOGPRX"Cannot read tag.\n");
break;
}
if (tag == NULL_TAG)
break;
nameLen = TAG_NAMELEN(tag);
if (fread(name, nameLen, 1, fp) != 1) {
error(INFO, LOGPRX"Cannot read tag name.\n");
break;
}
name[nameLen] = 0;
DEBUG_PARSE_PRINT((ofp, LOGPRX"\t Item %20s", name));
nindx = TAG_NINDX(tag);
if (nindx > 3) {
error(INFO, LOGPRX"Too many indexes %d (> 3).\n", nindx);
break;
}
idx[0] = idx[1] = idx[2] = NO_INDEX;
for (j= 0; j < nindx; j++) {
if (fread(&idx[j], sizeof(idx[0]), 1, fp) != 1) {
error(INFO, LOGPRX"Cannot read index.\n");
nextgroup = TRUE;
break;
}
DEBUG_PARSE_PRINT((ofp, "[%d]", idx[j]));
}
if (nextgroup)
break;
if (IS_BLOCK_TAG(tag)) {
uint64_t nbytes;
uint64_t blockpos;
uint64_t nbytesinmem;
int compressed = IS_BLOCK_COMPRESSED_TAG(tag);
uint16_t padsize;
if (fread(&nbytes, sizeof(nbytes), 1, fp) != 1) {
error(INFO, LOGPRX"Cannot read block size.\n");
break;
}
if (fread(&nbytesinmem, sizeof(nbytesinmem), 1, fp) != 1) {
error(INFO, LOGPRX"Cannot read block memory size.\n");
break;
}
if (fread(&padsize, sizeof(padsize), 1, fp) != 1) {
error(INFO, LOGPRX"Cannot read block padding size.\n");
break;
}
if ((blockpos = ftell(fp)) == -1) {
error(INFO, LOGPRX"Cannot determine location within VMSS file.\n");
break;
}
blockpos += padsize;
if (strcmp(name, "Memory") == 0) {
/* The things that we really care about...*/
vmss.memoffset = blockpos;
vmss.memsize = nbytesinmem;
vmss.separate_vmem = FALSE;
DEBUG_PARSE_PRINT((ofp, "\t=> %sBLOCK: position=%#llx size=%#llx memsize=%#llx\n",
compressed ? "COMPRESSED " : "",
(ulonglong)blockpos, (ulonglong)nbytes, (ulonglong)nbytesinmem));
if (compressed) {
error(INFO, LOGPRX"Cannot handle compressed memory dump yet!\n");
result = FALSE;
goto exit;
}
if (fseek(fp, blockpos + nbytes, SEEK_SET) == -1) {
error(INFO, LOGPRX"Cannot seek past block at %#llx.\n",
(ulonglong)(blockpos + nbytes));
break;
}
} else if (strcmp(name, "gpregs") == 0 &&
nbytes == VMW_GPREGS_SIZE &&
idx[0] < vmss.num_vcpus) {
int cpu = idx[0];
if (fread(vmss.regs64[cpu], VMW_GPREGS_SIZE, 1, fp) != 1) {
error(INFO, LOGPRX"Failed to read '%s': [Error %d] %s\n",
filename, errno, strerror(errno));
break;
}
vmss.vcpu_regs[cpu] |= REGS_PRESENT_GPREGS;
} else if (strcmp(name, "CR64") == 0 &&
nbytes == VMW_CR64_SIZE &&
idx[0] < vmss.num_vcpus) {
int cpu = idx[0];
if (fread(&vmss.regs64[cpu]->cr[0], VMW_CR64_SIZE, 1, fp) != 1) {
error(INFO, LOGPRX"Failed to read '%s': [Error %d] %s\n",
filename, errno, strerror(errno));
break;
}
vmss.vcpu_regs[cpu] |= REGS_PRESENT_CRS;
} else if (strcmp(name, "IDTR") == 0 &&
nbytes == VMW_IDTR_SIZE &&
idx[0] < vmss.num_vcpus) {
int cpu = idx[0];
uint64_t idtr;
if (fseek(fp, blockpos + 2, SEEK_SET) == -1) {
error(INFO, LOGPRX"Cannot seek past block at %#llx.\n",
(ulonglong)(blockpos + 2));
break;
}
if (fread(&idtr, sizeof(idtr), 1, fp) != 1) {
error(INFO, LOGPRX"Failed to read '%s': [Error %d] %s\n",
filename, errno, strerror(errno));
break;
}
vmss.regs64[cpu]->idtr = idtr;
vmss.vcpu_regs[cpu] |= REGS_PRESENT_IDTR;
} else {
if (fseek(fp, blockpos + nbytes, SEEK_SET) == -1) {
error(INFO, LOGPRX"Cannot seek past block at %#llx.\n",
(ulonglong)(blockpos + nbytes));
break;
}
}
} else {
union {
uint8_t val[TAG_VALSIZE_MASK];
uint32_t val32;
uint64_t val64;
} u;
unsigned k;
unsigned valsize = TAG_VALSIZE(tag);
uint64_t blockpos = ftell(fp);
DEBUG_PARSE_PRINT((ofp, "\t=> position=%#llx size=%#x: ", (ulonglong)blockpos, valsize));
if (fread(u.val, sizeof(u.val[0]), valsize, fp) != valsize) {
error(INFO, LOGPRX"Cannot read item.\n");
break;
}
for (k = 0; k < valsize; k++) {
/* Assume Little Endian */
DEBUG_PARSE_PRINT((ofp, "%02X", u.val[valsize - k - 1]));
}
if (strcmp(grps[i].name, "memory") == 0) {
if (strcmp(name, "regionsCount") == 0) {
vmss.regionscount = u.val32;
}
if (strcmp(name, "regionPageNum") == 0) {
vmss.regions[idx[0]].startpagenum = u.val32;
}
if (strcmp(name, "regionPPN") == 0) {
vmss.regions[idx[0]].startppn = u.val32;
}
if (strcmp(name, "regionSize") == 0) {
vmss.regions[idx[0]].size = u.val32;
}
if (strcmp(name, "align_mask") == 0) {
vmss.alignmask = u.val32;
}
} else if (strcmp(grps[i].name, "cpu") == 0) {
if (strcmp(name, "cpu:numVCPUs") == 0) {
if (vmss.regs64 != NULL) {
error(INFO, LOGPRX"Duplicated cpu:numVCPUs entry.\n");
break;
}
vmss.num_vcpus = u.val32;
vmss.regs64 = malloc(vmss.num_vcpus * sizeof(void *));
vmss.vcpu_regs = malloc(vmss.num_vcpus * sizeof(uint32_t));
for (k = 0; k < vmss.num_vcpus; k++) {
vmss.regs64[k] = malloc(sizeof(vmssregs64));
memset(vmss.regs64[k], 0, sizeof(vmssregs64));
vmss.vcpu_regs[k] = 0;
}
} else if (strcmp(name, "rax") == 0) {
int cpu = idx[0];
vmss.regs64[cpu]->rax = u.val64;
vmss.vcpu_regs[cpu] |= REGS_PRESENT_RAX;
} else if (strcmp(name, "rcx") == 0) {
int cpu = idx[0];
vmss.regs64[cpu]->rcx = u.val64;
vmss.vcpu_regs[cpu] |= REGS_PRESENT_RCX;
} else if (strcmp(name, "rdx") == 0) {
int cpu = idx[0];
vmss.regs64[cpu]->rdx = u.val64;
vmss.vcpu_regs[cpu] |= REGS_PRESENT_RDX;
} else if (strcmp(name, "rbx") == 0) {
int cpu = idx[0];
vmss.regs64[cpu]->rbx = u.val64;
vmss.vcpu_regs[cpu] |= REGS_PRESENT_RBX;
} else if (strcmp(name, "rbp") == 0) {
int cpu = idx[0];
vmss.regs64[cpu]->rbp = u.val64;
vmss.vcpu_regs[cpu] |= REGS_PRESENT_RBP;
} else if (strcmp(name, "rsp") == 0) {
int cpu = idx[0];
vmss.regs64[cpu]->rsp = u.val64;
vmss.vcpu_regs[cpu] |= REGS_PRESENT_RSP;
} else if (strcmp(name, "rsi") == 0) {
int cpu = idx[0];
vmss.regs64[cpu]->rsi = u.val64;
vmss.vcpu_regs[cpu] |= REGS_PRESENT_RSI;
} else if (strcmp(name, "rdi") == 0) {
int cpu = idx[0];
vmss.regs64[cpu]->rdi = u.val64;
vmss.vcpu_regs[cpu] |= REGS_PRESENT_RDI;
} else if (strcmp(name, "r8") == 0) {
int cpu = idx[0];
vmss.regs64[cpu]->r8 = u.val64;
vmss.vcpu_regs[cpu] |= REGS_PRESENT_R8;
} else if (strcmp(name, "r9") == 0) {
int cpu = idx[0];
vmss.regs64[cpu]->r9 = u.val64;
vmss.vcpu_regs[cpu] |= REGS_PRESENT_R9;
} else if (strcmp(name, "r10") == 0) {
int cpu = idx[0];
vmss.regs64[cpu]->r10 = u.val64;
vmss.vcpu_regs[cpu] |= REGS_PRESENT_R10;
} else if (strcmp(name, "r11") == 0) {
int cpu = idx[0];
vmss.regs64[cpu]->r11 = u.val64;
vmss.vcpu_regs[cpu] |= REGS_PRESENT_R11;
} else if (strcmp(name, "r12") == 0) {
int cpu = idx[0];
vmss.regs64[cpu]->r12 = u.val64;
vmss.vcpu_regs[cpu] |= REGS_PRESENT_R12;
} else if (strcmp(name, "r13") == 0) {
int cpu = idx[0];
vmss.regs64[cpu]->r13 = u.val64;
vmss.vcpu_regs[cpu] |= REGS_PRESENT_R13;
} else if (strcmp(name, "r14") == 0) {
int cpu = idx[0];
vmss.regs64[cpu]->r14 = u.val64;
vmss.vcpu_regs[cpu] |= REGS_PRESENT_R14;
} else if (strcmp(name, "r15") == 0) {
int cpu = idx[0];
vmss.regs64[cpu]->r15 = u.val64;
vmss.vcpu_regs[cpu] |= REGS_PRESENT_R15;
} else if (strcmp(name, "CR64") == 0) {
int cpu = idx[0];
switch (idx[1]) {
case 0:
vmss.regs64[cpu]->cr[0] = u.val64;
vmss.vcpu_regs[cpu] |= REGS_PRESENT_CR0;
break;
case 1:
vmss.regs64[cpu]->cr[1] = u.val64;
vmss.vcpu_regs[cpu] |= REGS_PRESENT_CR1;
break;
case 2:
vmss.regs64[cpu]->cr[2] = u.val64;
vmss.vcpu_regs[cpu] |= REGS_PRESENT_CR2;
break;
case 3:
vmss.regs64[cpu]->cr[3] = u.val64;
vmss.vcpu_regs[cpu] |= REGS_PRESENT_CR3;
break;
case 4:
vmss.regs64[cpu]->cr[4] = u.val64;
vmss.vcpu_regs[cpu] |= REGS_PRESENT_CR4;
break;
}
} else if (strcmp(name, "IDTR") == 0) {
int cpu = idx[0];
if (idx[1] == 1)
vmss.regs64[cpu]->idtr = u.val32;
else if (idx[1] == 2) {
vmss.regs64[cpu]->idtr |= (uint64_t) u.val32 << 32;
vmss.vcpu_regs[cpu] |= REGS_PRESENT_IDTR;
}
} else if (strcmp(name, "rip") == 0) {
int cpu = idx[0];
vmss.regs64[cpu]->rip = u.val64;
vmss.vcpu_regs[cpu] |= REGS_PRESENT_RIP;
} else if (strcmp(name, "eflags") == 0) {
int cpu = idx[0];
vmss.regs64[cpu]->rflags |= u.val32;
vmss.vcpu_regs[cpu] |= REGS_PRESENT_RFLAGS;
} else if (strcmp(name, "EFLAGS") == 0) {
int cpu = idx[0];
vmss.regs64[cpu]->rflags |= u.val32;
vmss.vcpu_regs[cpu] |= REGS_PRESENT_RFLAGS;
}
}
DEBUG_PARSE_PRINT((ofp, "\n"));
}
}
}
if (vmss.memsize == 0) {
char *vmem_filename, *p;
fprintf(ofp, LOGPRX"Memory dump is not part of this vmss file.\n");
fclose(fp);
fp = NULL;
fprintf(ofp, LOGPRX"Try to locate the companion vmem file ...\n");
/* check the companion vmem file */
vmem_filename = strdup(filename);
p = vmem_filename + strlen(vmem_filename) - 4;
if (strcmp(p, "vmss") != 0 && strcmp(p, "vmsn") != 0) {
free(vmem_filename);
result = FALSE;
goto exit;
}
strcpy(p, "vmem");
if ((fp = fopen(vmem_filename, "r")) == NULL) {
error(INFO, LOGPRX"%s: %s\n", vmem_filename, strerror(errno));
free(vmem_filename);
result = FALSE;
goto exit;
}
fseek(fp, 0L, SEEK_END);
vmss.memsize = ftell(fp);
fseek(fp, 0L, SEEK_SET);
vmss.separate_vmem = TRUE;
vmss.filename = filename;
fprintf(ofp, LOGPRX"vmem file: %s\n\n", vmem_filename);
free(vmem_filename);
}
vmss.dfp = fp;
exit:
if (grps)
free(grps);
if (!result && fp)
fclose(fp);
return result;
}
uint vmware_vmss_page_size(void)
{
return VMW_PAGE_SIZE;
}
int
read_vmware_vmss(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr)
{
uint64_t pos = paddr;
if (vmss.regionscount > 0) {
/* Memory is divided into regions and there are holes between them. */
uint32_t ppn = (uint32_t) (pos >> VMW_PAGE_SHIFT);
int i;
for (i = 0; i < vmss.regionscount; i++) {
if (ppn < vmss.regions[i].startppn)
break;
/* skip holes. */
pos -= ((vmss.regions[i].startppn - vmss.regions[i].startpagenum)
<< VMW_PAGE_SHIFT);
}
}
if (pos + cnt > vmss.memsize) {
error(INFO, LOGPRX"Read beyond the end of file! paddr=%#lx cnt=%d\n",
paddr, cnt);
}
pos += vmss.memoffset;
if (fseek(vmss.dfp, pos, SEEK_SET) != 0)
return SEEK_ERROR;
if (fread(bufptr, 1, cnt, vmss.dfp) != cnt)
return READ_ERROR;
return cnt;
}
int
write_vmware_vmss(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr)
{
return SEEK_ERROR;
}
void
vmware_vmss_display_regs(int cpu, FILE *ofp)
{
if (cpu >= vmss.num_vcpus)
return;
if (machine_type("X86_64")) {
fprintf(ofp,
" RIP: %016llx RSP: %016llx RFLAGS: %08llx\n"
" RAX: %016llx RBX: %016llx RCX: %016llx\n"
" RDX: %016llx RSI: %016llx RDI: %016llx\n"
" RBP: %016llx R8: %016llx R9: %016llx\n"
" R10: %016llx R11: %016llx R12: %016llx\n"
" R13: %016llx R14: %016llx R15: %016llx\n",
(ulonglong)vmss.regs64[cpu]->rip,
(ulonglong)vmss.regs64[cpu]->rsp,
(ulonglong)vmss.regs64[cpu]->rflags,
(ulonglong)vmss.regs64[cpu]->rax,
(ulonglong)vmss.regs64[cpu]->rbx,
(ulonglong)vmss.regs64[cpu]->rcx,
(ulonglong)vmss.regs64[cpu]->rdx,
(ulonglong)vmss.regs64[cpu]->rsi,
(ulonglong)vmss.regs64[cpu]->rdi,
(ulonglong)vmss.regs64[cpu]->rbp,
(ulonglong)vmss.regs64[cpu]->r8,
(ulonglong)vmss.regs64[cpu]->r9,
(ulonglong)vmss.regs64[cpu]->r10,
(ulonglong)vmss.regs64[cpu]->r11,
(ulonglong)vmss.regs64[cpu]->r12,
(ulonglong)vmss.regs64[cpu]->r13,
(ulonglong)vmss.regs64[cpu]->r14,
(ulonglong)vmss.regs64[cpu]->r15
);
}
}
void
get_vmware_vmss_regs(struct bt_info *bt, ulong *ipp, ulong *spp)
{
ulong ip, sp;
ip = sp = 0;
if (bt->tc->processor >= vmss.num_vcpus ||
vmss.regs64 == NULL ||
vmss.vcpu_regs[bt->tc->processor] != REGS_PRESENT_ALL) {
machdep->get_stack_frame(bt, ipp, spp);
return;
}
if (!is_task_active(bt->task)) {
machdep->get_stack_frame(bt, ipp, spp);
return;
}
bt->flags |= BT_DUMPFILE_SEARCH;
if (machine_type("X86_64"))
machdep->get_stack_frame(bt, ipp, spp);
else if (machine_type("X86"))
get_netdump_regs_x86(bt, ipp, spp);
if (bt->flags & BT_DUMPFILE_SEARCH)
return;
ip = (ulong)vmss.regs64[bt->tc->processor]->rip;
sp = (ulong)vmss.regs64[bt->tc->processor]->rsp;
if (is_kernel_text(ip) &&
(((sp >= GET_STACKBASE(bt->task)) &&
(sp < GET_STACKTOP(bt->task))) ||
in_alternate_stack(bt->tc->processor, sp))) {
*ipp = ip;
*spp = sp;
bt->flags |= BT_KERNEL_SPACE;
return;
}
if (!is_kernel_text(ip) &&
in_user_stack(bt->tc->task, sp))
bt->flags |= BT_USER_SPACE;
}
int
vmware_vmss_memory_dump(FILE *ofp)
{
cptdumpheader hdr;
cptgroupdesc *grps = NULL;
unsigned grpsize;
unsigned i;
int result = TRUE;
FILE *fp = vmss.dfp;
if (vmss.separate_vmem) {
if ((fp = fopen(vmss.filename, "r")) == NULL) {
error(INFO, LOGPRX"Failed to open '%s': %s [Error %d] %s\n",
vmss.filename, errno, strerror(errno));
return FALSE;
}
}
if (fseek(fp, 0, SEEK_SET) != 0) {
fprintf(ofp, "Error seeking to position 0.\n");
fclose(fp);
return FALSE;
}
if (fread(&hdr, sizeof(cptdumpheader), 1, fp) != 1) {
fprintf(ofp, "Failed to read vmss file: [Error %d] %s\n",
errno, strerror(errno));
fclose(fp);
return FALSE;
}
fprintf(ofp, "vmware_vmss:\n");
fprintf(ofp, " Header: id=%x version=%d numgroups=%d\n",
hdr.id, hdr.version, hdr.numgroups);
vmss.cpt64bit = (hdr.id != CPTDUMP_OLD_MAGIC_NUMBER);
fprintf(ofp, " Checkpoint is %d-bit\n", vmss.cpt64bit ? 64 : 32);
grpsize = hdr.numgroups * sizeof (cptgroupdesc);
grps = (cptgroupdesc *) malloc(grpsize * sizeof(cptgroupdesc));
if (grps == NULL) {
fprintf(ofp, "Failed to allocate memory! [Error %d] %s\n",
errno, strerror(errno));
fclose(fp);
return FALSE;
}
if (fread(grps, sizeof(cptgroupdesc), grpsize, fp) != grpsize) {
fprintf(ofp, "Failed to read vmss file: [Error %d] %s\n",
errno, strerror(errno));
result = FALSE;
goto exit;
}
for (i = 0; i < hdr.numgroups; i++) {
if (fseek(fp, grps[i].position, SEEK_SET) == -1) {
fprintf(ofp, "Bad offset of VMSS Group['%s'] in vmss file at %#llx.\n",
grps[i].name, (ulonglong)grps[i].position);
continue;
}
fprintf(ofp, "\nGroup: %s offset=%#llx size=0x%#llx\n",
grps[i].name, (ulonglong)grps[i].position, (ulonglong)grps[i].size);
for (;;) {
uint16_t tag;
char name[TAG_NAMELEN_MASK + 1];
unsigned nameLen;
unsigned nindx;
int idx[3];
unsigned j;
int nextgroup = FALSE;
if (fread(&tag, sizeof(tag), 1, fp) != 1) {
fprintf(ofp, "Cannot read tag.\n");
break;
}
if (tag == NULL_TAG)
break;
nameLen = TAG_NAMELEN(tag);
if (fread(name, nameLen, 1, fp) != 1) {
fprintf(ofp, "Cannot read tag name.\n");
break;
}
name[nameLen] = 0;
fprintf(ofp, " Item %20s", name);
nindx = TAG_NINDX(tag);
if (nindx > 3) {
fprintf(ofp, "Too many indexes %d (> 3).\n", nindx);
break;
}
idx[0] = idx[1] = idx[2] = NO_INDEX;
for (j= 0; j < 3; j++) {
if (j < nindx) {
if (fread(&idx[j], sizeof(idx[0]), 1, fp) != 1) {
fprintf(ofp, "Cannot read index.\n");
nextgroup = TRUE;
break;
}
fprintf(ofp, "[%d]", idx[j]);
} else
fprintf(ofp, " ");
}
if (nextgroup)
break;
if (IS_BLOCK_TAG(tag)) {
uint64_t nbytes;
uint64_t blockpos;
uint64_t nbytesinmem;
int compressed = IS_BLOCK_COMPRESSED_TAG(tag);
uint16_t padsize;
unsigned k, l;
char byte;
if (fread(&nbytes, sizeof(nbytes), 1, fp) != 1) {
fprintf(ofp, "Cannot read block size.\n");
break;
}
if (fread(&nbytesinmem, sizeof(nbytesinmem), 1, fp) != 1) {
fprintf(ofp, "Cannot read block memory size.\n");
break;
}
if (fread(&padsize, sizeof(padsize), 1, fp) != 1) {
fprintf(ofp, "Cannot read block padding size.\n");
break;
}
if ((blockpos = ftell(fp)) == -1) {
fprintf(ofp, "Cannot determine location within VMSS file.\n");
break;
}
blockpos += padsize;
fprintf(ofp, " => %sBLOCK: position=%#llx size=%#llx memsize=%#llx\n",
compressed ? "COMPRESSED " : "",
(ulonglong)blockpos, (ulonglong)nbytes, (ulonglong)nbytesinmem);
if (nbytes && nbytes <= MAX_BLOCK_DUMP && !compressed) {
fprintf(ofp, "Hex dump: \n");
l = 0;
for (k = 0; k < nbytes; k++) {
if (fread(&byte, 1, 1, fp) != 1) {
fprintf(ofp, "Cannot read byte.\n");
result = FALSE;
goto exit;
}
fprintf(ofp, " %02hhX", byte);
if (l++ == 15) {
fprintf(ofp, "\n");
l = 0;
}
}
if (l)
fprintf(ofp, "\n\n");
else
fprintf(ofp, "\n");
} else {
if (fseek(fp, blockpos + nbytes, SEEK_SET) == -1) {
fprintf(ofp, "Cannot seek past block at %#llx.\n",
(ulonglong)(blockpos + nbytes));
result = FALSE;
goto exit;
}
}
} else {
union {
uint8_t val[TAG_VALSIZE_MASK];
uint32_t val32;
uint64_t val64;
} u;
unsigned k;
unsigned valsize = TAG_VALSIZE(tag);
uint64_t blockpos = ftell(fp);
fprintf(ofp, " => position=%#llx size=%#x: ",
(ulonglong)blockpos, valsize);
if (fread(u.val, sizeof(u.val[0]), valsize, fp) != valsize) {
fprintf(ofp, "Cannot read item.\n");
break;
}
for (k = 0; k < valsize; k++) {
/* Assume Little Endian */
fprintf(ofp, "%02X", u.val[valsize - k - 1]);
}
fprintf(ofp, "\n");
}
}
}
exit:
if (vmss.separate_vmem)
fclose(fp);
if (grps)
free(grps);
return result;
}
void
dump_registers_for_vmss_dump(void)
{
int i;
vmssregs64 *regs;
if (!machine_type("X86_64")) {
fprintf(fp, "-r option not supported on this dumpfile type\n");
return;
}
for (i = 0; i < vmss.num_vcpus; i++) {
regs = vmss.regs64[i];
if (i)
fprintf(fp, "\n");
fprintf(fp, "CPU %d:\n", i);
if (vmss.vcpu_regs[i] != REGS_PRESENT_ALL) {
fprintf(fp, "Missing registers for this CPU: 0x%x\n", vmss.vcpu_regs[i]);
continue;
}
fprintf(fp, " RAX: %016llx RBX: %016llx RCX: %016llx\n",
(ulonglong)regs->rax, (ulonglong)regs->rbx, (ulonglong)regs->rcx);
fprintf(fp, " RDX: %016llx RSI: %016llx RDI: %016llx\n",
(ulonglong)regs->rdx, (ulonglong)regs->rsi, (ulonglong)regs->rdi);
fprintf(fp, " RSP: %016llx RBP: %016llx R8: %016llx\n",
(ulonglong)regs->rsp, (ulonglong)regs->rbp, (ulonglong)regs->r8);
fprintf(fp, " R9: %016llx R10: %016llx R11: %016llx\n",
(ulonglong)regs->r9, (ulonglong)regs->r10, (ulonglong)regs->r11);
fprintf(fp, " R12: %016llx R13: %016llx R14: %016llx\n",
(ulonglong)regs->r12, (ulonglong)regs->r13, (ulonglong)regs->r14);
fprintf(fp, " R15: %016llx RIP: %016llx RFLAGS: %08llx\n",
(ulonglong)regs->r15, (ulonglong)regs->rip, (ulonglong)regs->rflags);
fprintf(fp, " IDT: base: %016llx\n",
(ulonglong)regs->idtr);
fprintf(fp, " CR0: %016llx CR1: %016llx CR2: %016llx\n",
(ulonglong)regs->cr[0], (ulonglong)regs->cr[1], (ulonglong)regs->cr[2]);
fprintf(fp, " CR3: %016llx CR4: %016llx\n",
(ulonglong)regs->cr[3], (ulonglong)regs->cr[4]);
}
}
int
vmware_vmss_valid_regs(struct bt_info *bt)
{
if (vmss.vcpu_regs[bt->tc->processor] == REGS_PRESENT_ALL)
return TRUE;
return FALSE;
}
int
vmware_vmss_get_cr3_idtr(ulong *cr3, ulong *idtr)
{
if (vmss.num_vcpus == 0 || vmss.vcpu_regs[0] != REGS_PRESENT_ALL)
return FALSE;
*cr3 = vmss.regs64[0]->cr[3];
*idtr = vmss.regs64[0]->idtr;
return TRUE;
}
int
vmware_vmss_phys_base(ulong *phys_base)
{
*phys_base = vmss.phys_base;
return TRUE;
}
int
vmware_vmss_set_phys_base(ulong phys_base)
{
vmss.phys_base = phys_base;
return TRUE;
}