Mercurial > illumos > illumos-gate
changeset 14043:0291cd939b43
3506 Use "hypervisor" CPUID bit to detect hypervisor environment
Reviewed by: Hans Rosenfeld <hans.rosenfeld@nexenta.com>
Reviewed by: Albert Lee <trisk@nexenta.com>
Approved by: Robert Mustacchi <rm@joyent.com>
author | Yuri Pankov <yuri.pankov@nexenta.com> |
---|---|
date | Mon, 10 Jun 2013 09:51:40 -0700 |
parents | e5bd6d1f1685 |
children | 8fa61bfa2342 |
files | usr/src/uts/i86pc/os/cpuid.c usr/src/uts/i86pc/os/cpuid_subr.c usr/src/uts/i86pc/os/microcode.c usr/src/uts/i86pc/os/mlsetup.c usr/src/uts/i86pc/os/startup.c usr/src/uts/i86pc/os/timestamp.c usr/src/uts/intel/ia32/ml/i86_subr.s usr/src/uts/intel/sys/x86_archext.h |
diffstat | 8 files changed, 156 insertions(+), 83 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/uts/i86pc/os/cpuid.c Sun Jun 09 14:38:37 2013 +0400 +++ b/usr/src/uts/i86pc/os/cpuid.c Mon Jun 10 09:51:40 2013 -0700 @@ -21,6 +21,7 @@ /* * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011 by Delphix. All rights reserved. + * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ /* * Copyright (c) 2010, Intel Corporation. @@ -220,7 +221,7 @@ boolean_t xsave_force_disable = B_FALSE; /* - * This is set to platform type Solaris is running on. + * This is set to platform type we are running on. */ static int platform_type = -1; @@ -597,20 +598,20 @@ } #if !defined(__xpv) - /* * Determine the type of the underlying platform. This is used to customize * initialization of various subsystems (e.g. TSC). determine_platform() must * only ever be called once to prevent two processors from seeing different - * values of platform_type, it must be called before cpuid_pass1(), the - * earliest consumer to execute. + * values of platform_type. Must be called before cpuid_pass1(), the earliest + * consumer to execute (uses _cpuid_chiprev --> synth_amd_info --> get_hwenv). */ void determine_platform(void) { struct cpuid_regs cp; - char *xen_str; - uint32_t xen_signature[4], base; + uint32_t base; + uint32_t regs[4]; + char *hvstr = (char *)regs; ASSERT(platform_type == -1); @@ -620,31 +621,75 @@ return; /* - * In a fully virtualized domain, Xen's pseudo-cpuid function - * returns a string representing the Xen signature in %ebx, %ecx, - * and %edx. %eax contains the maximum supported cpuid function. - * We need at least a (base + 2) leaf value to do what we want - * to do. Try different base values, since the hypervisor might - * use a different one depending on whether hyper-v emulation - * is switched on by default or not. + * If Hypervisor CPUID bit is set, try to determine hypervisor + * vendor signature, and set platform type accordingly. + * + * References: + * http://lkml.org/lkml/2008/10/1/246 + * http://kb.vmware.com/kb/1009458 + */ + cp.cp_eax = 0x1; + (void) __cpuid_insn(&cp); + if ((cp.cp_ecx & CPUID_INTC_ECX_HV) != 0) { + cp.cp_eax = 0x40000000; + (void) __cpuid_insn(&cp); + regs[0] = cp.cp_ebx; + regs[1] = cp.cp_ecx; + regs[2] = cp.cp_edx; + regs[3] = 0; + if (strcmp(hvstr, HVSIG_XEN_HVM) == 0) { + platform_type = HW_XEN_HVM; + return; + } + if (strcmp(hvstr, HVSIG_VMWARE) == 0) { + platform_type = HW_VMWARE; + return; + } + if (strcmp(hvstr, HVSIG_KVM) == 0) { + platform_type = HW_KVM; + return; + } + if (strcmp(hvstr, HVSIG_MICROSOFT) == 0) + platform_type = HW_MICROSOFT; + } else { + /* + * Check older VMware hardware versions. VMware hypervisor is + * detected by performing an IN operation to VMware hypervisor + * port and checking that value returned in %ebx is VMware + * hypervisor magic value. + * + * References: http://kb.vmware.com/kb/1009458 + */ + vmware_port(VMWARE_HVCMD_GETVERSION, regs); + if (regs[1] == VMWARE_HVMAGIC) { + platform_type = HW_VMWARE; + return; + } + } + + /* + * Check Xen hypervisor. In a fully virtualized domain, + * Xen's pseudo-cpuid function returns a string representing the + * Xen signature in %ebx, %ecx, and %edx. %eax contains the maximum + * supported cpuid function. We need at least a (base + 2) leaf value + * to do what we want to do. Try different base values, since the + * hypervisor might use a different one depending on whether Hyper-V + * emulation is switched on by default or not. */ for (base = 0x40000000; base < 0x40010000; base += 0x100) { cp.cp_eax = base; (void) __cpuid_insn(&cp); - xen_signature[0] = cp.cp_ebx; - xen_signature[1] = cp.cp_ecx; - xen_signature[2] = cp.cp_edx; - xen_signature[3] = 0; - xen_str = (char *)xen_signature; - if (strcmp("XenVMMXenVMM", xen_str) == 0 && + regs[0] = cp.cp_ebx; + regs[1] = cp.cp_ecx; + regs[2] = cp.cp_edx; + regs[3] = 0; + if (strcmp(hvstr, HVSIG_XEN_HVM) == 0 && cp.cp_eax >= (base + 2)) { - platform_type = HW_XEN_HVM; + platform_type &= ~HW_NATIVE; + platform_type |= HW_XEN_HVM; return; } } - - if (vmware_platform()) /* running under vmware hypervisor? */ - platform_type = HW_VMWARE; } int @@ -2366,7 +2411,7 @@ * the other cpus. * * Fixup the brand string, and collect any information from cpuid - * that requires dynamicically allocated storage to represent. + * that requires dynamically allocated storage to represent. */ /*ARGSUSED*/ void
--- a/usr/src/uts/i86pc/os/cpuid_subr.c Sun Jun 09 14:38:37 2013 +0400 +++ b/usr/src/uts/i86pc/os/cpuid_subr.c Mon Jun 10 09:51:40 2013 -0700 @@ -22,6 +22,8 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. */ /* @@ -391,7 +393,7 @@ #endif platform = get_hwenv(); - if ((platform == HW_XEN_HVM) || (platform == HW_VMWARE)) { + if ((platform & HW_VIRTUAL) != 0) { *skt_p = X86_SOCKET_UNKNOWN; } else if (family == 0xf) { *skt_p = amd_skts[rmp->rm_sktidx][model & 0x3];
--- a/usr/src/uts/i86pc/os/microcode.c Sun Jun 09 14:38:37 2013 +0400 +++ b/usr/src/uts/i86pc/os/microcode.c Mon Jun 10 09:51:40 2013 -0700 @@ -22,6 +22,8 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. */ #include <sys/asm_linkage.h> @@ -202,17 +204,21 @@ * We also assume that we don't support a mix of Intel and * AMD processors in the same box. * - * An i86xpv guest domain can't update the microcode. + * An i86xpv guest domain or VM can't update the microcode. */ + +#define XPVDOMU_OR_HVM \ + ((hwenv == HW_XEN_PV && !is_controldom()) || (hwenv & HW_VIRTUAL) != 0) + /*ARGSUSED*/ static int ucode_capable_amd(cpu_t *cp) { int hwenv = get_hwenv(); - if (hwenv == HW_XEN_HVM || (hwenv == HW_XEN_PV && !is_controldom())) { + if (XPVDOMU_OR_HVM) return (0); - } + return (cpuid_getfamily(cp) >= 0x10); } @@ -221,9 +227,9 @@ { int hwenv = get_hwenv(); - if (hwenv == HW_XEN_HVM || (hwenv == HW_XEN_PV && !is_controldom())) { + if (XPVDOMU_OR_HVM) return (0); - } + return (cpuid_getfamily(cp) >= 6); }
--- a/usr/src/uts/i86pc/os/mlsetup.c Sun Jun 09 14:38:37 2013 +0400 +++ b/usr/src/uts/i86pc/os/mlsetup.c Mon Jun 10 09:51:40 2013 -0700 @@ -195,8 +195,7 @@ cpuid_pass1(cpu[0], x86_featureset); #if !defined(__xpv) - - if (get_hwenv() == HW_XEN_HVM) + if ((get_hwenv() & HW_XEN_HVM) != 0) xen_hvm_init(); /* @@ -218,7 +217,7 @@ * The Xen hypervisor does not correctly report whether rdtscp is * supported or not, so we must assume that it is not. */ - if (get_hwenv() != HW_XEN_HVM && + if ((get_hwenv() & HW_XEN_HVM) == 0 && is_x86_feature(x86_featureset, X86FSET_TSCP)) patch_tsc_read(X86_HAVE_TSCP); else if (cpuid_getvendor(CPU) == X86_VENDOR_AMD &&
--- a/usr/src/uts/i86pc/os/startup.c Sun Jun 09 14:38:37 2013 +0400 +++ b/usr/src/uts/i86pc/os/startup.c Mon Jun 10 09:51:40 2013 -0700 @@ -21,6 +21,7 @@ /* * Copyright (c) 1993, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2012 DEY Storage Systems, Inc. All rights reserved. + * Copyright 2013 Nexenta Systems, Inc. All rights reserved. */ /* * Copyright (c) 2010, Intel Corporation. @@ -1534,7 +1535,7 @@ */ microfind(); - if (get_hwenv() == HW_XEN_HVM) + if ((get_hwenv() & HW_XEN_HVM) != 0) update_default_path(); #endif @@ -1663,7 +1664,7 @@ * Initialize a handle for the boot cpu - others will initialize * as they startup. Do not do this if we know we are in an HVM domU. */ - if ((get_hwenv() != HW_XEN_HVM) && + if ((get_hwenv() & HW_XEN_HVM) == 0 && (hdl = cmi_init(CMI_HDL_NATIVE, cmi_ntv_hwchipid(CPU), cmi_ntv_hwcoreid(CPU), cmi_ntv_hwstrandid(CPU))) != NULL && is_x86_feature(x86_featureset, X86FSET_MCA)) { @@ -2298,7 +2299,7 @@ * Startup the memory scrubber. * XXPV This should be running somewhere .. */ - if (get_hwenv() != HW_XEN_HVM) + if ((get_hwenv() & HW_VIRTUAL) == 0) memscrub_init(); #endif }
--- a/usr/src/uts/i86pc/os/timestamp.c Sun Jun 09 14:38:37 2013 +0400 +++ b/usr/src/uts/i86pc/os/timestamp.c Mon Jun 10 09:51:40 2013 -0700 @@ -22,6 +22,8 @@ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. + * + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. */ #include <sys/types.h> @@ -432,8 +434,7 @@ int hwtype; hwtype = get_hwenv(); - if (!tsc_master_slave_sync_needed || hwtype == HW_XEN_HVM || - hwtype == HW_VMWARE) + if (!tsc_master_slave_sync_needed || (hwtype & HW_VIRTUAL) != 0) return; flags = clear_int_flag(); @@ -512,8 +513,7 @@ int hwtype; hwtype = get_hwenv(); - if (!tsc_master_slave_sync_needed || hwtype == HW_XEN_HVM || - hwtype == HW_VMWARE) + if (!tsc_master_slave_sync_needed || (hwtype & HW_VIRTUAL) != 0) return; flags = clear_int_flag();
--- a/usr/src/uts/intel/ia32/ml/i86_subr.s Sun Jun 09 14:38:37 2013 +0400 +++ b/usr/src/uts/intel/ia32/ml/i86_subr.s Mon Jun 10 09:51:40 2013 -0700 @@ -21,6 +21,7 @@ /* * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. */ /* @@ -4339,62 +4340,58 @@ #endif /* __lint */ /* - * This is how VMware lets the guests figure that they are running - * on top of VMWare platform : - * Write 0xA in the ECX register and put the I/O port address value of - * 0x564D5868 in the EAX register. Then read a word from port 0x5658. - * If VMWare is installed than this code will be executed correctly and - * the EBX register will contain the same I/O port address value of 0x564D5868. - * If VMWare is not installed then OS will return an exception on port access. + * VMware implements an I/O port that programs can query to detect if software + * is running in a VMware hypervisor. This hypervisor port behaves differently + * depending on magic values in certain registers and modifies some registers + * as a side effect. + * + * References: http://kb.vmware.com/kb/1009458 */ + #if defined(__lint) -int -vmware_platform(void) { return (1); } +/* ARGSUSED */ +void +vmware_port(int cmd, uint32_t *regs) { return; } #else #if defined(__amd64) - ENTRY(vmware_platform) + ENTRY(vmware_port) pushq %rbx - xorl %ebx, %ebx - movl $0x564d5868, %eax - movl $0xa, %ecx - movl $0x5658, %edx + movl $VMWARE_HVMAGIC, %eax + movl $0xffffffff, %ebx + movl %edi, %ecx + movl $VMWARE_HVPORT, %edx inl (%dx) - movl $0x564d5868, %ecx - xorl %eax, %eax - cmpl %ecx, %ebx - jne 1f - incl %eax -1: + movl %eax, (%rsi) + movl %ebx, 4(%rsi) + movl %ecx, 8(%rsi) + movl %edx, 12(%rsi) popq %rbx ret - SET_SIZE(vmware_platform) + SET_SIZE(vmware_port) #elif defined(__i386) - ENTRY(vmware_platform) + ENTRY(vmware_port) pushl %ebx - pushl %ecx - pushl %edx - xorl %ebx, %ebx - movl $0x564d5868, %eax - movl $0xa, %ecx - movl $0x5658, %edx + pushl %esi + movl $VMWARE_HVMAGIC, %eax + movl $0xffffffff, %ebx + movl 12(%esp), %ecx + movl $VMWARE_HVPORT, %edx inl (%dx) - movl $0x564d5868, %ecx - xorl %eax, %eax - cmpl %ecx, %ebx - jne 1f - incl %eax -1: - popl %edx - popl %ecx + movl 16(%esp), %esi + movl %eax, (%esi) + movl %ebx, 4(%esi) + movl %ecx, 8(%esi) + movl %edx, 12(%esi) + popl %esi popl %ebx ret - SET_SIZE(vmware_platform) + SET_SIZE(vmware_port) #endif /* __i386 */ #endif /* __lint */
--- a/usr/src/uts/intel/sys/x86_archext.h Sun Jun 09 14:38:37 2013 +0400 +++ b/usr/src/uts/intel/sys/x86_archext.h Mon Jun 10 09:51:40 2013 -0700 @@ -21,6 +21,7 @@ /* * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2011 by Delphix. All rights reserved. + * Copyright 2012 Nexenta Systems, Inc. All rights reserved. */ /* * Copyright (c) 2010, Intel Corporation. @@ -123,6 +124,7 @@ #define CPUID_INTC_ECX_AVX 0x10000000 /* AVX supported */ #define CPUID_INTC_ECX_F16C 0x20000000 /* F16C supported */ #define CPUID_INTC_ECX_RDRAND 0x40000000 /* RDRAND supported */ +#define CPUID_INTC_ECX_HV 0x80000000 /* Hypervisor */ #define FMT_CPUID_INTC_ECX \ "\20" \ @@ -759,7 +761,7 @@ extern int cpuid_arat_supported(void); extern int cpuid_iepb_supported(struct cpu *); extern int cpuid_deadline_tsc_supported(void); -extern int vmware_platform(void); +extern void vmware_port(int, uint32_t *); #endif struct cpu_ucode_info; @@ -820,16 +822,37 @@ extern void xsave_setup_msr(struct cpu *); /* + * Hypervisor signatures + */ +#define HVSIG_XEN_HVM "XenVMMXenVMM" +#define HVSIG_VMWARE "VMwareVMware" +#define HVSIG_KVM "KVMKVMKVM" +#define HVSIG_MICROSOFT "Microsoft Hv" + +/* * Defined hardware environments */ -#define HW_NATIVE 0x00 /* Running on bare metal */ -#define HW_XEN_PV 0x01 /* Running on Xen Hypervisor paravirutualized */ -#define HW_XEN_HVM 0x02 /* Running on Xen hypervisor HVM */ -#define HW_VMWARE 0x03 /* Running on VMware hypervisor */ +#define HW_NATIVE (1 << 0) /* Running on bare metal */ +#define HW_XEN_PV (1 << 1) /* Running on Xen PVM */ + +#define HW_XEN_HVM (1 << 2) /* Running on Xen HVM */ +#define HW_VMWARE (1 << 3) /* Running on VMware hypervisor */ +#define HW_KVM (1 << 4) /* Running on KVM hypervisor */ +#define HW_MICROSOFT (1 << 5) /* Running on Microsoft hypervisor */ + +#define HW_VIRTUAL (HW_XEN_HVM | HW_VMWARE | HW_KVM | HW_MICROSOFT) #endif /* _KERNEL */ -#endif +#endif /* !_ASM */ + +/* + * VMware hypervisor related defines + */ +#define VMWARE_HVMAGIC 0x564d5868 +#define VMWARE_HVPORT 0x5658 +#define VMWARE_HVCMD_GETVERSION 0x0a +#define VMWARE_HVCMD_GETTSCFREQ 0x2d #ifdef __cplusplus }