---
 sys-utils/lscpu.1 |    4 +
 sys-utils/lscpu.c |  158 ++++++++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 158 insertions(+), 4 deletions(-)

Index: sys-utils/lscpu.c
===================================================================
--- sys-utils/lscpu.c.orig
+++ sys-utils/lscpu.c
@@ -34,6 +34,15 @@
 #include <bitops.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <stdint.h>
+#include <signal.h>
+#include <strings.h>
+#include <setjmp.h>
+#if defined(__x86_64__) || defined(__i386__)
+#ifdef HAVE_sys_io_h
+#include <sys/io.h>
+#endif
+#endif
 
 #include "cpuset.h"
 #include "nls.h"
@@ -47,6 +56,7 @@
 #define _PATH_PROC_XENCAP	_PATH_PROC_XEN "/capabilities"
 #define _PATH_PROC_CPUINFO	"/proc/cpuinfo"
 #define _PATH_PROC_PCIDEVS	"/proc/bus/pci/devices"
+#define _PATH_PROC_DEVICETREE   "/proc/device-tree"
 
 /* virtualization types */
 enum {
@@ -65,13 +75,37 @@ enum {
 	HYPER_NONE	= 0,
 	HYPER_XEN,
 	HYPER_KVM,
-	HYPER_MSHV
+	HYPER_MSHV,
+	HYPER_VMWARE,
+	HYPER_VBOX,
+	HYPER_OS400,
+	HYPER_PHYP
 };
 const char *hv_vendors[] = {
 	[HYPER_NONE]	= NULL,
 	[HYPER_XEN]	= "Xen",
 	[HYPER_KVM]	= "KVM",
-	[HYPER_MSHV]	= "Microsoft"
+	[HYPER_MSHV]	= "Microsoft",
+	[HYPER_VMWARE]	= "VMware",
+	[HYPER_VBOX]	= "Oracle",
+	[HYPER_OS400]	= "OS/400",
+	[HYPER_PHYP]	= "pHyp"
+};
+const int hv_vendor_pci[] = {
+	[HYPER_NONE]	= 0x0000,
+	[HYPER_XEN]	= 0x5853,
+	[HYPER_KVM]	= 0x0000,
+	[HYPER_MSHV]	= 0x1414,
+	[HYPER_VMWARE]	= 0x15ad,
+	[HYPER_VBOX]	= 0x80ee
+};
+const int hv_graphics_pci[] = {
+	[HYPER_NONE]	= 0x0000,
+	[HYPER_XEN]	= 0x0001,
+	[HYPER_KVM]	= 0x0000,
+	[HYPER_MSHV]	= 0x5353,
+	[HYPER_VMWARE]	= 0x0710,
+	[HYPER_VBOX]	= 0xbeef
 };
 
 /* CPU modes */
@@ -519,12 +553,116 @@ read_hypervisor_cpuid(struct lscpu_desc
 		desc->hyper = HYPER_KVM;
 	else if (!strncmp("Microsoft Hv", hyper_vendor_id, 12))
 		desc->hyper = HYPER_MSHV;
+	else if (!strncmp("VMwareVMware", hyper_vendor_id, 12))
+		desc->hyper = HYPER_VMWARE;
+}
+
+#define VMWARE_BDOOR_MAGIC          0x564D5868
+#define VMWARE_BDOOR_PORT           0x5658
+#define VMWARE_BDOOR_CMD_GETVERSION 10
+
+#define VMWARE_BDOOR(eax, ebx, ecx, edx)                                  \
+        __asm__("inl (%%dx)" :                                            \
+               "=a"(eax), "=c"(ecx), "=d"(edx), "=b"(ebx) :               \
+               "0"(VMWARE_BDOOR_MAGIC), "1"(VMWARE_BDOOR_CMD_GETVERSION), \
+               "2"(VMWARE_BDOOR_PORT), "3"(0) :                           \
+               "memory");
+
+static jmp_buf segv_handler_env;
+
+static void
+segv_handler(int sig, siginfo_t *info, void *ignored)
+{
+	siglongjmp(segv_handler_env, 1);
 }
 
+static int
+is_vmware_platform(void)
+{
+	uint32_t eax, ebx, ecx, edx;
+	struct sigaction act, oact;
+
+	/*
+	 * The assembly routine for vmware detection works
+	 * fine under vmware, even if ran as regular user. But
+	 * on real HW or under other hypervisors, it segfaults (which is
+	 * expected). So we temporarily install SIGSEGV handler to catch
+	 * the signal. All this magic is needed because lscpu
+	 * isn't supposed to require root privileges.
+	 */
+	if (sigsetjmp(segv_handler_env, 1))
+		return 0;
+
+	bzero(&act, sizeof(act));
+	act.sa_sigaction = segv_handler;
+	act.sa_flags = SA_SIGINFO;
+
+	if (sigaction(SIGSEGV, &act, &oact))
+		err(EXIT_FAILURE, _("error: can not set signal handler"));
+
+	VMWARE_BDOOR(eax, ebx, ecx, edx);
+
+	if (sigaction(SIGSEGV, &oact, NULL))
+		err(EXIT_FAILURE, _("error: can not restore signal handler"));
+
+	return eax != (uint32_t)-1 && ebx == VMWARE_BDOOR_MAGIC;
+}
+
+
 #else	/* ! __x86_64__ */
 static void
 read_hypervisor_cpuid(struct lscpu_desc *desc)
 {
+#ifdef __powerpc__
+	/* powerpc:
+	 * IBM iSeries: legacy, if /proc/iSeries exists, its para-virtualized on top of OS/400
+	 * IBM pSeries: always has a hypervisor
+	 *              if partition-name is "full", its kind of "bare-metal": full-system-partition
+	 *              otherwise its some partition created by Hardware Management Console
+	 *              in any case, its always some sort of HVM
+	 * KVM: "linux,kvm" in /hypervisor/compatible indicates a KVM guest
+	 * Xen: not in use, not detected
+	 */
+	if (path_exist("/proc/iSeries")) {
+		desc->hyper = HYPER_OS400;
+		desc->virtype = VIRT_FULL;
+	} else if (path_exist(_PATH_PROC_DEVICETREE "/ibm,partition-name")) {
+		FILE *fd;
+		desc->hyper = HYPER_PHYP;
+		desc->virtype = VIRT_FULL;
+		fd = fopen(_PATH_PROC_DEVICETREE "/ibm,partition-name", "r");
+		if (fd) {
+			char buf[256];
+			if (fscanf(fd, "%s", buf) == 1 && !strcmp(buf, "full"))
+				desc->virtype = VIRT_NONE;
+			fclose(fd);
+		}
+	} else if (path_exist(_PATH_PROC_DEVICETREE "/hypervisor/compatible")) {
+		FILE *fd;
+		fd = fopen(_PATH_PROC_DEVICETREE "/hypervisor/compatible", "r");
+		if (fd) {
+			char buf[256];
+			int i;
+			memset(buf, 0, sizeof(buf));
+			fread(buf, sizeof(buf) - 1, 1, fd);
+			fclose(fd);
+			for (i = 0; i < sizeof(buf);) {
+				if (!strcmp(&buf[i], "linux,kvm")) {
+					desc->hyper = HYPER_KVM;
+					desc->virtype = VIRT_FULL;
+					break;
+				}
+				i += strlen(&buf[i]);
+				i++;
+			}
+		}
+	}
+#endif
+}
+
+static int is_vmware_platform(void)
+{
+	return 0;
 }
 #endif
 
@@ -553,10 +691,19 @@ read_hypervisor(struct lscpu_desc *desc)
 		desc->virtype = dom0 ? VIRT_NONE : VIRT_PARA;
 		desc->hyper = HYPER_XEN;
 
-	} else if (has_pci_device(0x5853, 0x0001)) {
+	} else if (has_pci_device( hv_vendor_pci[HYPER_XEN], hv_graphics_pci[HYPER_XEN])) {
 		/* Xen full-virt on non-x86_64 */
 		desc->hyper = HYPER_XEN;
 		desc->virtype = VIRT_FULL;
+	} else if (is_vmware_platform()) {
+		desc->hyper = HYPER_VMWARE;
+		desc->virtype = VIRT_FULL;
+	} else if (has_pci_device( hv_vendor_pci[HYPER_VMWARE], hv_graphics_pci[HYPER_VMWARE])) {
+		desc->hyper = HYPER_VMWARE;
+		desc->virtype = VIRT_FULL;
+	} else if (has_pci_device( hv_vendor_pci[HYPER_VBOX], hv_graphics_pci[HYPER_VBOX])) {
+		desc->hyper = HYPER_VBOX;
+		desc->virtype = VIRT_FULL;
 	}
 }
 
@@ -743,6 +890,7 @@ print_parsable(struct lscpu_desc *desc)
 		for (i = desc->ncaches - 1; i >= 0; i--)
 			printf(",%s", desc->caches[i].name);
 	}
+	printf(",HvVendor,VirtType");
 	putchar('\n');
 
 	for (i = 0; i < desc->ncpus; i++) {
@@ -800,7 +948,9 @@ print_parsable(struct lscpu_desc *desc)
 			if (x == ca->nsharedmaps)
 				putchar(',');
 		}
-		putchar('\n');
+		printf(",%s,%s\n",
+		       hv_vendors[desc->hyper] ? hv_vendors[desc->hyper] : "none",
+		       virt_types[desc->virtype]);
 	}
 }
 
Index: sys-utils/lscpu.1
===================================================================
--- sys-utils/lscpu.1.orig
+++ sys-utils/lscpu.1
@@ -18,6 +18,10 @@ a human-readable format.  It supports bo
 It can also print out in a parsable format,
 including how different caches are shared by different CPUs,
 which can be fed to other programs.
+.B lscpu
+also tries to detect which virtualization environment the operating
+system is running in (if any).
+
 .SH OPTIONS
 .TP
 .BR \-h , " \-\-help"
