Enhancement to the support of VMware .vmss suspended state dumpfiles.

There may be holes in the memory address saved for PCI, etc.  In such
cases, the memory dump is divided into regions.  With this patch, up
to 3 memory regions are supported.
(hfu@vmware.com)
This commit is contained in:
Dave Anderson 2015-03-31 10:46:30 -04:00
parent 384a3f0bb7
commit 1ed90b28af
2 changed files with 151 additions and 96 deletions

View File

@ -21,6 +21,10 @@
#define LOGPRX "vmw: "
/* VMware only supports X86/X86_64 virtual machines. */
#define VMW_PAGE_SIZE (4096)
#define VMW_PAGE_SHIFT (12)
static vmssdata vmss = { 0 };
int
@ -30,12 +34,14 @@ is_vmware_vmss(char *filename)
FILE *fp;
if ((fp = fopen(filename, "r")) == NULL) {
if (CRASHDEBUG(1))
error(INFO, LOGPRX"%s: %s\n", filename, strerror(errno));
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;
}
@ -59,59 +65,67 @@ int
vmware_vmss_init(char *filename, FILE *ofp)
{
cptdumpheader hdr;
cptgroupdesc *grps;
cptgroupdesc *grps = NULL;
unsigned grpsize;
unsigned i;
FILE *fp;
FILE *fp = NULL;
int result = TRUE;
if (!machine_type("X86") && !machine_type("X86_64")) {
error(FATAL,
LOGPRX"invalid or unsupported host architecture for .vmss file: %s\n",
error(INFO,
LOGPRX"Invalid or unsupported host architecture for .vmss file: %s\n",
MACHINE_TYPE);
return FALSE;
result = FALSE;
goto exit;
}
if ((fp = fopen(filename, "r")) == NULL) {
error(INFO, LOGPRX"%s: %s\n", filename, strerror(errno));
return FALSE;
error(INFO, LOGPRX"Failed to open '%s': %s [Error %d] %s\n",
filename, errno, strerror(errno));
result = FALSE;
goto exit;
}
vmss.dfp = fp;
vmss.ofp = ofp;
if (fread(&hdr, sizeof(cptdumpheader), 1, vmss.dfp) != 1)
return FALSE;
DEBUG_PARSE_PRINT((vmss.ofp, LOGPRX"Header: id=%x version=%d numgroups=%d\n",
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((vmss.ofp, LOGPRX"Checkpoint is %d-bit\n",
vmss.cpt64bit ? 64 : 32));
DEBUG_PARSE_PRINT((ofp, LOGPRX"Checkpoint is %d-bit\n", vmss.cpt64bit ? 64 : 32));
if (!vmss.cpt64bit) {
fprintf(vmss.ofp, LOGPRX"Not implemented for 32-bit VMSS file!\n");
return FALSE;
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) {
fprintf(vmss.ofp, LOGPRX"Out of memory! failed to allocate groups.\n");
return FALSE;
error(INFO, LOGPRX"Failed to allocate memory! [Error %d] %s\n",
errno, strerror(errno));
result = FALSE;
goto exit;
}
if (fread(grps, sizeof(cptgroupdesc), grpsize, vmss.dfp) != grpsize) {
fprintf(vmss.ofp, LOGPRX"Cannot read VMSS groups in %s!\n", filename);
free(grps);
return FALSE;
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(vmss.dfp, grps[i].position, SEEK_SET) == -1) {
fprintf(vmss.ofp, LOGPRX"Bad offset of VMSS Group['%s'] in '%s' at %#llx.\n",
grps[i].name, filename, (ulonglong)grps[i].position);
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((vmss.ofp, LOGPRX"Group: %-20s offset=%#llx size=0x%#llx.\n",
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) {
@ -125,36 +139,39 @@ vmware_vmss_init(char *filename, FILE *ofp)
unsigned nindx;
int idx[3];
unsigned j;
int nextgroup = FALSE;
if (fread(&tag, sizeof(tag), 1, vmss.dfp) != 1) {
fprintf(vmss.ofp, LOGPRX"Cannot read tag.\n");
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, vmss.dfp) != 1) {
fprintf(vmss.ofp, LOGPRX"Cannot read tag name.\n");
if (fread(name, nameLen, 1, fp) != 1) {
error(INFO, LOGPRX"Cannot read tag name.\n");
break;
}
name[nameLen] = 0;
DEBUG_PARSE_PRINT((vmss.ofp, LOGPRX"\t Item %20s",
name));
DEBUG_PARSE_PRINT((ofp, LOGPRX"\t Item %20s", name));
nindx = TAG_NINDX(tag);
if (nindx > 3) {
fprintf(vmss.ofp, LOGPRX"Cannot handle %d indexes\n", nindx);
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, vmss.dfp) != 1) {
fprintf(vmss.ofp, LOGPRX"Cannot read index.\n");
if (fread(&idx[j], sizeof(idx[0]), 1, fp) != 1) {
error(INFO, LOGPRX"Cannot read index.\n");
nextgroup = TRUE;
break;
}
DEBUG_PARSE_PRINT((vmss.ofp, "[%d]", idx[j]));
DEBUG_PARSE_PRINT((ofp, "[%d]", idx[j]));
}
if (nextgroup)
break;
if (IS_BLOCK_TAG(tag)) {
uint64_t nbytes;
@ -163,135 +180,165 @@ vmware_vmss_init(char *filename, FILE *ofp)
int compressed = IS_BLOCK_COMPRESSED_TAG(tag);
uint16_t padsize;
if (fread(&nbytes, sizeof(nbytes), 1, vmss.dfp) != 1) {
fprintf(vmss.ofp, LOGPRX"Cannot read block size.\n");
if (fread(&nbytes, sizeof(nbytes), 1, fp) != 1) {
error(INFO, LOGPRX"Cannot read block size.\n");
break;
}
if (fread(&nbytesinmem, sizeof(nbytesinmem), 1, vmss.dfp) != 1) {
fprintf(vmss.ofp, LOGPRX"Cannot read block memory size.\n");
if (fread(&nbytesinmem, sizeof(nbytesinmem), 1, fp) != 1) {
error(INFO, LOGPRX"Cannot read block memory size.\n");
break;
}
if (fread(&padsize, sizeof(padsize), 1, vmss.dfp) != 1) {
fprintf(vmss.ofp, LOGPRX"Cannot read block padding size.\n");
if (fread(&padsize, sizeof(padsize), 1, fp) != 1) {
error(INFO, LOGPRX"Cannot read block padding size.\n");
break;
}
if ((blockpos = ftell(vmss.dfp)) == -1) {
fprintf(vmss.ofp, LOGPRX"Cannot determine location within VMSS file.\n");
if ((blockpos = ftell(fp)) == -1) {
error(INFO, LOGPRX"Cannot determine location within VMSS file.\n");
break;
}
blockpos += padsize;
if (fseek(vmss.dfp, blockpos + nbytes, SEEK_SET) == -1) {
fprintf(vmss.ofp, LOGPRX"Cannot seek past block at %#llx.\n",
(ulonglong)(blockpos + nbytes));
if (fseek(fp, blockpos + nbytes, SEEK_SET) == -1) {
error(INFO, LOGPRX"Cannot seek past block at %#llx.\n",
(ulonglong)(blockpos + nbytes));
break;
}
/* The things that we really care about...*/
if (strcmp(grps[i].name, "memory") == 0 &&
strcmp(name, "Memory") == 0) {
if (strcmp(name, "Memory") == 0) {
/* The things that we really care about...*/
vmss.memoffset = blockpos;
vmss.memsize = nbytesinmem;
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;
}
}
DEBUG_PARSE_PRINT((vmss.ofp, "\t=> %sBLOCK: position=%#llx size=%#llx memsize=%#llx\n",
compressed ? "COMPRESSED " : "",
(ulonglong)blockpos, (ulonglong)nbytes, (ulonglong)nbytesinmem));
} else {
uint8_t val[TAG_VALSIZE_MASK];
union {
uint8_t val[TAG_VALSIZE_MASK];
uint32_t val32;
} u;
unsigned k;
unsigned valsize = TAG_VALSIZE(tag);
uint64_t blockpos = ftell(vmss.dfp);
uint64_t blockpos = ftell(fp);
DEBUG_PARSE_PRINT((vmss.ofp, "\t=> position=%#llx size=%#x: ", (ulonglong)blockpos, valsize));
if (fread(val, sizeof(val[0]), valsize, vmss.dfp) != valsize) {
fprintf(vmss.ofp, LOGPRX"Cannot read item.\n");
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((vmss.ofp, "%02X", val[valsize - k - 1]));
DEBUG_PARSE_PRINT((ofp, "%02X", u.val[valsize - k - 1]));
}
if (strcmp(grps[i].name, "memory") == 0) {
if (strcmp(name, "regionsCount") == 0) {
vmss.regionscount = (uint32_t) *val;
if (vmss.regionscount != 0) {
fprintf(vmss.ofp, LOGPRX"regionsCount=%d (!= 0) NOT TESTED!",
vmss.regionscount);
}
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 = (uint32_t) *val;
if (vmss.alignmask != 0xff) {
fprintf(vmss.ofp, LOGPRX"align_mask=%d (!= 0xff) NOT TESTED!",
vmss.regionscount);
}
vmss.alignmask = u.val32;
}
}
DEBUG_PARSE_PRINT((vmss.ofp, "\n"));
DEBUG_PARSE_PRINT((ofp, "\n"));
}
}
}
free(grps);
if (vmss.memsize == 0) {
char *vmem_filename, *p;
fprintf(vmss.ofp, LOGPRX"Memory dump is not part of this vmss file.\n");
fclose(vmss.dfp);
fprintf(ofp, LOGPRX"Memory dump is not part of this vmss file.\n");
fclose(fp);
fp = NULL;
fprintf(vmss.ofp, LOGPRX"Try to locate the companion vmem file ...\n");
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);
return FALSE;
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);
return FALSE;
result = FALSE;
goto exit;
}
vmss.dfp = fp;
fseek(vmss.dfp, 0L, SEEK_END);
vmss.memsize = ftell(vmss.dfp);
fseek(vmss.dfp, 0L, SEEK_SET);
fseek(fp, 0L, SEEK_END);
vmss.memsize = ftell(fp);
fseek(fp, 0L, SEEK_SET);
fprintf(vmss.ofp, LOGPRX"vmem file: %s\n\n", vmem_filename);
fprintf(ofp, LOGPRX"vmem file: %s\n\n", vmem_filename);
free(vmem_filename);
}
return TRUE;
vmss.dfp = fp;
exit:
if (grps)
free(grps);
if (!result && fp)
fclose(fp);
return result;
}
uint vmware_vmss_page_size(void)
{
return 4096;
return VMW_PAGE_SIZE;
}
int
read_vmware_vmss(int fd, void *bufptr, int cnt, ulong addr, physaddr_t paddr)
{
uint64_t pos = vmss.memoffset + paddr;
uint64_t pos = paddr;
if (pos + cnt > vmss.memoffset + vmss.memsize) {
cnt -= ((pos + cnt) - (vmss.memoffset + vmss.memsize));
if (cnt < 0) {
error(INFO, LOGPRX"Read beyond the end of file! paddr=%#lx\n",
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)
if (fread(bufptr, 1, cnt, vmss.dfp) != cnt)
return READ_ERROR;
return cnt;

View File

@ -82,13 +82,21 @@ struct cptgroupdesc {
};
typedef struct cptgroupdesc cptgroupdesc;
struct memregion {
uint32_t startpagenum;
uint32_t startppn;
uint32_t size;
};
typedef struct memregion memregion;
#define MAX_REGIONS 3
struct vmssdata {
int32_t cpt64bit;
FILE *dfp;
FILE *ofp;
/* about the memory */
uint32_t alignmask;
uint32_t regionscount;
memregion regions[MAX_REGIONS];
uint64_t memoffset;
uint64_t memsize;
};