Index: util-linux-2.24.1/sys-utils/lscpu.c =================================================================== --- util-linux-2.24.1.orig/sys-utils/lscpu.c +++ util-linux-2.24.1/sys-utils/lscpu.c @@ -32,6 +32,15 @@ #include #include #include +#include +#include +#include +#include +#if defined(__x86_64__) || defined(__i386__) +#ifdef HAVE_sys_io_h +#include +#endif +#endif #include "cpuset.h" #include "nls.h" @@ -59,6 +68,7 @@ #define _PATH_PROC_STATUS "/proc/self/status" #define _PATH_PROC_VZ "/proc/vz" #define _PATH_PROC_BC "/proc/bc" +#define _PATH_PROC_DEVICETREE "/proc/device-tree" #define _PATH_DEV_MEM "/dev/mem" /* virtualization types */ @@ -86,7 +96,28 @@ const char *hv_vendors[] = { [HYPER_UML] = "User-mode Linux", [HYPER_INNOTEK] = "Innotek GmbH", [HYPER_HITACHI] = "Hitachi", - [HYPER_PARALLELS] = "Parallels" + [HYPER_PARALLELS] = "Parallels", + [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 */ @@ -550,10 +581,111 @@ read_hypervisor_cpuid(struct lscpu_desc 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 __attribute__((__unused__))) { +#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 @@ -588,9 +720,18 @@ read_hypervisor(struct lscpu_desc *desc, desc->hyper = HYPER_XEN; /* Xen full-virt on non-x86_64 */ - } else if (has_pci_device(0x5853, 0x0001)) { + } else if (has_pci_device( hv_vendor_pci[HYPER_XEN], hv_graphics_pci[HYPER_XEN])) { 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; /* IBM PR/SM */ } else if (path_exist(_PATH_PROC_SYSINFO)) { Index: util-linux-2.24.1/sys-utils/lscpu.h =================================================================== --- util-linux-2.24.1.orig/sys-utils/lscpu.h +++ util-linux-2.24.1/sys-utils/lscpu.h @@ -13,7 +13,10 @@ enum { HYPER_UML, HYPER_INNOTEK, /* VBOX */ HYPER_HITACHI, - HYPER_PARALLELS /* OpenVZ/VIrtuozzo */ + HYPER_PARALLELS, /* OpenVZ/VIrtuozzo */ + HYPER_VBOX, + HYPER_OS400, + HYPER_PHYP }; extern int read_hypervisor_dmi(void);