From 907196e93dc94df104df21ba51a42a5de9277958 Mon Sep 17 00:00:00 2001
From: Dave Anderson <anderson@redhat.com>
Date: Mon, 26 Mar 2018 13:56:29 -0400
Subject: [PATCH] VMware VMSS dumpfiles contain the state of each vCPU at the
 time when the VM was suspended.  This patch enables crash to read the
 relevant registers from each vCPU state for use as the starting hooks by the
 "bt" command.  Also, support for "help -[D|n]" to display dumpfile contents,
 and "help -r" to display vCPU register sets has been implemented.  This is
 also the first step towards implementing automatic KASLR offset calculations
 for VMSS dumpfiles. (slp@redhat.com)

Patch-mainline: 7.2.2
Git-commit: 907196e93dc94df104df21ba51a42a5de9277958

---
 defs.h        |   6 +
 help.c        |   3 +
 kernel.c      |   2 +
 main.c        |   3 +
 memory.c      |   2 +
 vmware_vmss.c | 527 +++++++++++++++++++++++++++++++++++++++++++++++++-
 vmware_vmss.h |  62 ++++++
 x86_64.c      |  15 +-
 8 files changed, 610 insertions(+), 10 deletions(-)

diff --git a/defs.h b/defs.h
index 7998ebf..acaad7a 100644
--- a/defs.h
+++ b/defs.h
@@ -283,6 +283,7 @@ struct number_option {
 #define LKCD_KERNTYPES()    (pc->flags & KERNTYPES)
 #define KVMDUMP_DUMPFILE()  (pc->flags & KVMDUMP)
 #define SADUMP_DUMPFILE()  (pc->flags & SADUMP)
+#define VMSS_DUMPFILE()     (pc->flags & VMWARE_VMSS)
 
 #define NETDUMP_LOCAL    (0x1)  /* netdump_data flags */
 #define NETDUMP_REMOTE   (0x2)  
@@ -6388,6 +6389,11 @@ int vmware_vmss_init(char *filename, FILE *ofp);
 uint vmware_vmss_page_size(void);
 int read_vmware_vmss(int, void *, int, ulong, physaddr_t);
 int write_vmware_vmss(int, void *, int, ulong, physaddr_t);
+void vmware_vmss_display_regs(int, FILE *);
+void get_vmware_vmss_regs(struct bt_info *, ulong *, ulong *);
+int vmware_vmss_memory_dump(FILE *);
+void dump_registers_for_vmss_dump(void);
+int vmware_vmss_valid_regs(struct bt_info *);
 
 /*
  *  gnu_binutils.c
diff --git a/help.c b/help.c
index ee9dfb2..c5cec53 100644
--- a/help.c
+++ b/help.c
@@ -710,6 +710,9 @@ dump_registers(void)
 	} else if (NETDUMP_DUMPFILE() || KDUMP_DUMPFILE()) {
 		dump_registers_for_elf_dumpfiles();
 		return;
+	} else if (VMSS_DUMPFILE()) {
+		dump_registers_for_vmss_dump();
+		return;
 	}
 
 	error(FATAL, "-r option not supported on %s\n",
diff --git a/kernel.c b/kernel.c
index 1bf6251..7642217 100644
--- a/kernel.c
+++ b/kernel.c
@@ -2969,6 +2969,8 @@ back_trace(struct bt_info *bt)
 		get_xendump_regs(bt, &eip, &esp);
 	else if (SADUMP_DUMPFILE())
 		get_sadump_regs(bt, &eip, &esp);
+	else if (VMSS_DUMPFILE())
+		get_vmware_vmss_regs(bt, &eip, &esp);
         else if (REMOTE_PAUSED()) {
 		if (!is_task_active(bt->task) || !get_remote_regs(bt, &eip, &esp))
 			machdep->get_stack_frame(bt, &eip, &esp);
diff --git a/main.c b/main.c
index 2aae0c6..15834cb 100644
--- a/main.c
+++ b/main.c
@@ -1361,6 +1361,9 @@ dump_program_context(void)
         if (pc->flags & DISKDUMP)
                 sprintf(&buf[strlen(buf)],
                         "%sDISKDUMP", others++ ? "|" : "");
+        if (pc->flags & VMWARE_VMSS)
+                sprintf(&buf[strlen(buf)],
+                        "%sVMWARE_VMSS", others++ ? "|" : "");
         if (pc->flags & SYSMAP)
                 sprintf(&buf[strlen(buf)],
                         "%sSYSMAP", others++ ? "|" : "");
diff --git a/memory.c b/memory.c
index 0669276..9f752c2 100644
--- a/memory.c
+++ b/memory.c
@@ -16909,6 +16909,8 @@ dumpfile_memory(int cmd)
                         retval = kcore_memory_dump(fp);
 		else if (pc->flags & SADUMP)
 			retval = sadump_memory_dump(fp);
+		else if (pc->flags & VMWARE_VMSS)
+			retval = vmware_vmss_memory_dump(fp);
 		break;
 	
 	case DUMPFILE_ENVIRONMENT:
diff --git a/vmware_vmss.c b/vmware_vmss.c
index 667676a..ad577b2 100644
--- a/vmware_vmss.c
+++ b/vmware_vmss.c
@@ -25,6 +25,8 @@
 #define VMW_PAGE_SIZE (4096)
 #define VMW_PAGE_SHIFT (12)
 
+#define MAX_BLOCK_DUMP (128)
+
 static vmssdata vmss = { 0 };
 
 int
@@ -128,7 +130,8 @@ vmware_vmss_init(char *filename, FILE *ofp)
 		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) {
+		if (strcmp(grps[i].name, "memory") != 0 &&
+		    (strcmp(grps[i].name, "cpu") != 0 || !machine_type("X86_64"))) {
 			continue;
 		}
 
@@ -198,16 +201,11 @@ vmware_vmss_init(char *filename, FILE *ofp)
 				}
 				blockpos += padsize;
 
-				if (fseek(fp, blockpos + nbytes, SEEK_SET) == -1) {
-					error(INFO, LOGPRX"Cannot seek past block at %#llx.\n",
-					      (ulonglong)(blockpos + nbytes));
-					break;
-				}
-
 				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));
@@ -217,11 +215,61 @@ vmware_vmss_init(char *filename, FILE *ofp)
 						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);
@@ -253,6 +301,131 @@ vmware_vmss_init(char *filename, FILE *ofp)
 					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"));
@@ -288,6 +461,9 @@ vmware_vmss_init(char *filename, FILE *ofp)
 		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);
 	}
@@ -350,3 +526,340 @@ 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;
+}
diff --git a/vmware_vmss.h b/vmware_vmss.h
index a4b8937..f6966a5 100644
--- a/vmware_vmss.h
+++ b/vmware_vmss.h
@@ -89,16 +89,78 @@ struct memregion {
 };
 typedef struct memregion	memregion;
 
+#define VMW_GPREGS_SIZE (128)
+#define VMW_CR64_SIZE (72)
+#define VMW_IDTR_SIZE (10)
+struct vmssregs64 {
+	/* read from vmss */
+	uint64_t	rax;
+	uint64_t	rcx;
+	uint64_t	rdx;
+	uint64_t	rbx;
+	uint64_t	rbp;
+	uint64_t	rsp;
+	uint64_t	rsi;
+	uint64_t	rdi;
+	uint64_t	r8;
+	uint64_t	r9;
+	uint64_t	r10;
+	uint64_t	r11;
+	uint64_t	r12;
+	uint64_t	r13;
+	uint64_t	r14;
+	uint64_t	r15;
+	/* manually managed */
+	uint64_t	idtr;
+	uint64_t	cr[VMW_CR64_SIZE / 8];
+	uint64_t	rip;
+	uint64_t	rflags;
+};
+typedef struct vmssregs64 vmssregs64;
+
+#define REGS_PRESENT_RAX    1<<0
+#define REGS_PRESENT_RCX    1<<1
+#define REGS_PRESENT_RDX    1<<2
+#define REGS_PRESENT_RBX    1<<3
+#define REGS_PRESENT_RBP    1<<4
+#define REGS_PRESENT_RSP    1<<5
+#define REGS_PRESENT_RSI    1<<6
+#define REGS_PRESENT_RDI    1<<7
+#define REGS_PRESENT_R8     1<<8
+#define REGS_PRESENT_R9     1<<9
+#define REGS_PRESENT_R10    1<<10
+#define REGS_PRESENT_R11    1<<11
+#define REGS_PRESENT_R12    1<<12
+#define REGS_PRESENT_R13    1<<13
+#define REGS_PRESENT_R14    1<<14
+#define REGS_PRESENT_R15    1<<15
+#define REGS_PRESENT_IDTR   1<<16
+#define REGS_PRESENT_CR0    1<<17
+#define REGS_PRESENT_CR1    1<<18
+#define REGS_PRESENT_CR2    1<<19
+#define REGS_PRESENT_CR3    1<<20
+#define REGS_PRESENT_CR4    1<<21
+#define REGS_PRESENT_RIP    1<<22
+#define REGS_PRESENT_RFLAGS 1<<23
+#define REGS_PRESENT_GPREGS 65535
+#define REGS_PRESENT_CRS    4063232
+#define REGS_PRESENT_ALL    16777215
+
 #define MAX_REGIONS	3
 struct vmssdata {
 	int32_t	cpt64bit;
 	FILE	*dfp;
+	char	*filename;
 	/* about the memory */
 	uint32_t	alignmask;
 	uint32_t	regionscount;
         memregion	regions[MAX_REGIONS];
 	uint64_t	memoffset;
 	uint64_t	memsize;
+	int		separate_vmem;
+	uint32_t	*vcpu_regs;
+	uint64_t	num_vcpus;
+	vmssregs64	**regs64;
 };
 typedef struct vmssdata vmssdata;
 
diff --git a/x86_64.c b/x86_64.c
index 0d5e150..aaa0de6 100644
--- a/x86_64.c
+++ b/x86_64.c
@@ -3273,6 +3273,8 @@ x86_64_low_budget_back_trace_cmd(struct bt_info *bt_in)
 			diskdump_display_regs(bt->tc->processor, ofp);
 		else if (SADUMP_DUMPFILE())
 			sadump_display_regs(bt->tc->processor, ofp);
+		else if (VMSS_DUMPFILE())
+			vmware_vmss_display_regs(bt->tc->processor, ofp);
 		return;
 	}
 
@@ -3295,13 +3297,16 @@ x86_64_low_budget_back_trace_cmd(struct bt_info *bt_in)
 			diskdump_display_regs(bt->tc->processor, ofp);
 		else if (SADUMP_DUMPFILE())
 			sadump_display_regs(bt->tc->processor, ofp);
+		else if (VMSS_DUMPFILE())
+			vmware_vmss_display_regs(bt->tc->processor, ofp);
 		else if (pc->flags2 & QEMU_MEM_DUMP_ELF)
 			display_regs_from_elf_notes(bt->tc->processor, ofp);
 		return;
 	} else if ((bt->flags & BT_KERNEL_SPACE) &&
 		   (KVMDUMP_DUMPFILE() ||
 		    (ELF_NOTES_VALID() && DISKDUMP_DUMPFILE()) ||
-		    SADUMP_DUMPFILE() || (pc->flags2 & QEMU_MEM_DUMP_ELF))) {
+		    SADUMP_DUMPFILE() || (pc->flags2 & QEMU_MEM_DUMP_ELF) ||
+		    VMSS_DUMPFILE())) {
 		fprintf(ofp, "    [exception RIP: ");
 		if ((sp = value_search(bt->instptr, &offset))) {
 			fprintf(ofp, "%s", sp->name);
@@ -3317,6 +3322,8 @@ x86_64_low_budget_back_trace_cmd(struct bt_info *bt_in)
 			diskdump_display_regs(bt->tc->processor, ofp);
 		else if (SADUMP_DUMPFILE())
 			sadump_display_regs(bt->tc->processor, ofp);
+		else if (VMSS_DUMPFILE())
+			vmware_vmss_display_regs(bt->tc->processor, ofp);
 		else if (pc->flags2 & QEMU_MEM_DUMP_ELF)
 			display_regs_from_elf_notes(bt->tc->processor, ofp);
 
@@ -4941,7 +4948,8 @@ skip_stage:
 	if (halt_rip && halt_rsp) {
         	*rip = halt_rip;
 		*rsp = halt_rsp;
-		if (KVMDUMP_DUMPFILE() || SADUMP_DUMPFILE())
+		if (KVMDUMP_DUMPFILE() || SADUMP_DUMPFILE() ||
+		    (VMSS_DUMPFILE() && vmware_vmss_valid_regs(bt)))
 			bt_in->flags &= ~(ulonglong)BT_DUMPFILE_SEARCH;
 		return;
 	}
@@ -4986,7 +4994,8 @@ skip_stage:
 
         machdep->get_stack_frame(bt, rip, rsp);
 
-	if (KVMDUMP_DUMPFILE() || SADUMP_DUMPFILE())
+	if (KVMDUMP_DUMPFILE() || SADUMP_DUMPFILE() ||
+	    (VMSS_DUMPFILE() && vmware_vmss_valid_regs(bt)))
 		bt_in->flags &= ~(ulonglong)BT_DUMPFILE_SEARCH;
 }
 
-- 
2.30.1

