commit 021f2aa054f015c59c3471fba681e6452c5e6eab Author: Dyno (Hongjun) Fu Date: Tue Mar 24 13:47:46 2015 -0700 add vmss memory regions support There might be holes in the guest os memory address saved for PCI etc. The memory dump will be divided into regions to skip these holes. On vSphere 5.5, RHEL 6.5 memory dump larger than 3GB is such a case. diff --git a/vmware_vmss.c b/vmware_vmss.c index aeb1c1d..2a7a1c1 100644 --- a/vmware_vmss.c +++ b/vmware_vmss.c @@ -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; } @@ -47,8 +53,7 @@ is_vmware_vmss(char *filename) 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); + error(INFO, LOGPRX"Unrecognized .vmss file (magic %x).\n", hdr.id); return FALSE; } @@ -59,59 +64,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 +138,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 +179,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 (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; - 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); + 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; diff --git a/vmware_vmss.h b/vmware_vmss.h index dcbde2d..a4b8937 100644 --- a/vmware_vmss.h +++ b/vmware_vmss.h @@ -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; };