/********************************************************* * Copyright (C) 1998 VMware, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation version 2.1 and no later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * *********************************************************/ /* * hostinfo.c -- * * Platform-independent code that calls into hostinfo-specific * code. */ #include "vmware.h" #include #if defined(__i386__) || defined(__x86_64__) #include "cpuid_info.h" #endif #include "hostinfo.h" #include "hostinfoInt.h" #include "str.h" #include "util.h" #include "str.h" #include "dynbuf.h" #define LOGLEVEL_MODULE hostinfo #include "loglevel_user.h" #define LGPFX "HOSTINFO:" /* * HostinfoOSData caches its returned value. */ volatile Bool HostinfoOSNameCacheValid = FALSE; char HostinfoCachedOSName[MAX_OS_NAME_LEN]; char HostinfoCachedOSFullName[MAX_OS_FULLNAME_LEN]; #if defined(__i386__) || defined(__x86_64__) /* *---------------------------------------------------------------------- * * HostInfoGetIntelCPUCount -- * * For an Intel processor, determine the number of cores per physical * CPU and the number of threads per core. * * Results: * * TRUE if the information was successfully gathered, FALSE if * otherwise. * * Side effects: * None. * *---------------------------------------------------------------------- */ Bool HostInfoGetIntelCPUCount(CPUIDSummary *cpuid, // IN uint32 *numCoresPerPCPU, // OUT uint32 *numThreadsPerCore) // OUT { *numCoresPerPCPU = CPUIDSummary_IntelCoresPerPackage(cpuid, numThreadsPerCore); return TRUE; } /* *---------------------------------------------------------------------- * * HostInfoGetAMDCPUCount -- * * For an AMD processor, determine the number of cores per physical * CPU and the number of threads per core. * * Results: * * TRUE if the information was successfully gathered, FALSE if * otherwise. * * Side effects: * None. * *---------------------------------------------------------------------- */ Bool HostInfoGetAMDCPUCount(CPUIDSummary *cpuid, // IN uint32 *numCoresPerPCPU, // OUT uint32 *numThreadsPerCore) // OUT { *numCoresPerPCPU = CPUIDSummary_AMDCoresPerPackage(cpuid, numThreadsPerCore); return TRUE; } /* *----------------------------------------------------------------------------- * * HostInfoGetCpuidStrSection -- * * Append a section (either low or high) of CPUID as a string in DynBuf. * E.g. * 00000000:00000005756E65476C65746E49656E69- * 00000001:00000F4A000208000000649DBFEBFBFF- * or * 80000000:80000008000000000000000000000000- * 80000001:00000000000000000000000120100000- * 80000008:00003024000000000000000000000000- * * The returned eax of args[0] is used to determine the upper bound for * the following input arguments. And the input args should be in * ascending order. * * Results: * None. The string will be appended in buf. * * Side effect: * None * *----------------------------------------------------------------------------- */ static void HostInfoGetCpuidStrSection(const uint32 args[], // IN: input eax arguments const size_t args_size, // IN: size of the argument array DynBuf *buf) // IN/OUT: result string in DynBuf { static const char format[] = "%08X:%08X%08X%08X%08X-"; CPUIDRegs reg; uint32 max_arg; char temp[64]; int i; __GET_CPUID(args[0], ®); max_arg = reg.eax; if (max_arg < args[0]) { Warning(LGPFX" No CPUID information available. Based = %08X.\n", args[0]); return; } DynBuf_Append(buf, temp, Str_Sprintf(temp, sizeof temp, format, args[0], reg.eax, reg.ebx, reg.ecx, reg.edx)); for (i = 1; i < args_size && args[i] <= max_arg; i++) { ASSERT(args[i] > args[i - 1]); // Ascending order. __GET_CPUID(args[i], ®); DynBuf_Append(buf, temp, Str_Sprintf(temp, sizeof temp, format, args[i], reg.eax, reg.ebx, reg.ecx, reg.edx)); } } /* *----------------------------------------------------------------------------- * * Hostinfo_GetCpuidStr -- * * Get the basic and extended CPUID as a string. E.g. * 00000000:00000005756E65476C65746E49656E69- * 00000001:00000F4A000208000000649DBFEBFBFF- * 80000000:80000008000000000000000000000000- * 80000001:00000000000000000000000120100000- * 80000008:00003024000000000000000000000000 * * If the extended CPUID is not available, only returns the basic CPUID. * * Results: * The CPUID string if the processor supports the CPUID instruction and * this is a processor we recognize. It should never fail, since it * would at least return leaf 0. Caller needs to free the returned string. * * Side effect: * None * *----------------------------------------------------------------------------- */ char * Hostinfo_GetCpuidStr(void) { static const uint32 basic_args[] = {0x0, 0x1, 0xa}; static const uint32 extended_args[] = {0x80000000, 0x80000001, 0x80000008}; DynBuf buf; char *result; DynBuf_Init(&buf); HostInfoGetCpuidStrSection(basic_args, ARRAYSIZE(basic_args), &buf); HostInfoGetCpuidStrSection(extended_args, ARRAYSIZE(extended_args), &buf); // Trim buffer and set NULL character to replace last '-'. DynBuf_Trim(&buf); result = (char*)DynBuf_Get(&buf); ASSERT(result && result[0]); // We should at least get result from eax = 0x0. result[DynBuf_GetSize(&buf) - 1] = '\0'; return DynBuf_Detach(&buf); } #endif // defined(__i386__) || defined(__x86_64__) /* *----------------------------------------------------------------------------- * * Hostinfo_GetCpuid -- * * Get cpuid information for a CPU. Which CPU the information is for * depends on the OS scheduler. We are assuming that all CPUs in * the system have identical numbers of cores and threads. * * Results: * TRUE if the processor supports the cpuid instruction and this * is a process we recognize, FALSE otherwise. * * Side effect: * None * *----------------------------------------------------------------------------- */ Bool Hostinfo_GetCpuid(HostinfoCpuIdInfo *info) // OUT { #if defined(__i386__) || defined(__x86_64__) CPUIDSummary cpuid; CPUIDRegs id0; uint32 numCoresPerPCPU, numThreadsPerCore; /* * Can't do cpuid = {0} as an initializer above because gcc throws * some idiotic warning. */ memset(&cpuid, 0, sizeof(cpuid)); /* * Get basic and extended CPUID info. */ __GET_CPUID(0, &id0); cpuid.id0.numEntries = id0.eax; if (0 == cpuid.id0.numEntries) { Warning(LGPFX" No CPUID information available.\n"); return FALSE; } *(uint32*)(cpuid.id0.name + 0) = id0.ebx; *(uint32*)(cpuid.id0.name + 4) = id0.edx; *(uint32*)(cpuid.id0.name + 8) = id0.ecx; *(uint32*)(cpuid.id0.name + 12) = 0; __GET_CPUID(1, (CPUIDRegs*)&cpuid.id1); __GET_CPUID(0xa, (CPUIDRegs*)&cpuid.ida); __GET_CPUID(0x80000000, (CPUIDRegs*)&cpuid.id80); __GET_CPUID(0x80000001, (CPUIDRegs*)&cpuid.id81); __GET_CPUID(0x80000008, (CPUIDRegs*)&cpuid.id88); /* * Calculate vendor and CPU count information. */ if (0 == strcmp(cpuid.id0.name, CPUID_INTEL_VENDOR_STRING_FIXED)) { info->vendor = CPUID_VENDOR_INTEL; if (!HostInfoGetIntelCPUCount(&cpuid, &numCoresPerPCPU, &numThreadsPerCore)) { Warning(LGPFX" Failed to get Intel CPU count.\n"); return FALSE; } Log(LGPFX" Seeing Intel CPU, numCoresPerCPU %u numThreadsPerCore %u.\n", numCoresPerPCPU, numThreadsPerCore); } else if (strcmp(cpuid.id0.name, CPUID_AMD_VENDOR_STRING_FIXED) == 0) { info->vendor = CPUID_VENDOR_AMD; if (!HostInfoGetAMDCPUCount(&cpuid, &numCoresPerPCPU, &numThreadsPerCore)) { Warning(LGPFX" Failed to get AMD CPU count.\n"); return FALSE; } Log(LGPFX" Seeing AMD CPU, numCoresPerCPU %u numThreadsPerCore %u.\n", numCoresPerPCPU, numThreadsPerCore); } else { info->vendor = CPUID_VENDOR_UNKNOWN; // assume one core per CPU, one thread per core numCoresPerPCPU = 1; numThreadsPerCore = 1; Log(LGPFX" Unknown CPU vendor \"%s\" seen, assuming one core per CPU " "and one thread per core.\n", cpuid.id0.name); } info->numLogCPUs = Hostinfo_NumCPUs(); if (-1 == info->numLogCPUs) { Warning(LGPFX" Failed to get logical CPU count.\n"); return FALSE; } #ifdef VMX86_SERVER /* * The host can avoid scheduling hypertwins, regardless of the CPU * supporting it or hyperthreading being enabled in the BIOS. This leads * to numThreadsPerCore set to 2 when it should be 1. */ if (Hostinfo_HTDisabled()) { Log(LGPFX" hyperthreading disabled, setting number of threads per core " "to 1.\n"); numThreadsPerCore = 1; } #endif info->numPhysCPUs = info->numLogCPUs / (numCoresPerPCPU * numThreadsPerCore); if (0 == info->numPhysCPUs) { // UP kernel case, possibly Log(LGPFX" numPhysCPUs is 0, bumping to 1.\n"); info->numPhysCPUs = 1; } info->numCores = info->numLogCPUs / numThreadsPerCore; if (0 == info->numCores) { // UP kernel case, possibly Log(LGPFX" numCores is 0, bumping to 1.\n"); info->numCores = 1; } Log(LGPFX" This machine has %u physical CPUS, %u total cores, and %u " "logical CPUs.\n", info->numPhysCPUs, info->numCores, info->numLogCPUs); /* * Pull out versioning and feature information. */ info->version = cpuid.id1.version; info->family = CPUID_FAMILY(cpuid.id1.version); info->model = CPUID_MODEL(cpuid.id1.version); info->stepping = CPUID_STEPPING(cpuid.id1.version); info->type = (cpuid.id1.version >> 12) & 0x0003; info->extfeatures = cpuid.id1.ecxFeatures; info->features = cpuid.id1.edxFeatures; return TRUE; #else // defined(__i386__) || defined(__x86_64__) return FALSE; #endif // defined(__i386__) || defined(__x86_64__) } /* *----------------------------------------------------------------------------- * * Hostinfo_GetOSName -- * * Query the operating system and build a pair of strings to identify it. * The two strings are osName and osNameFull. Short osName strings are * the same as you'd see in a VM's .vmx file. * * The long names are a bit more descriptive: * Windows: (BUILD ) * example: Windows XP Professional Service Pack 2 (Build 2600) * * Linux: * example: Linux 2.4.18-3 Red Hat Linux release 7.3 (Valhalla) * * Return value: * Returns TRUE on success and FALSE on failure. * Returns the guest's full OS name (osFullName) * Returns the guest's OS name in the same format as .vmx file (osName) * * Side effects: * None * *----------------------------------------------------------------------------- */ Bool Hostinfo_GetOSName(uint32 outBufFullLen, // IN: length of osFullName buffer uint32 outBufLen, // IN: length of osName buffer char *osFullName, // OUT: Full OS name char *osName) // OUT: OS name { Bool retval; retval = HostinfoOSNameCacheValid ? TRUE : HostinfoOSData(); if (retval) { Str_Strcpy(osFullName, HostinfoCachedOSFullName, outBufFullLen); Str_Strcpy(osName, HostinfoCachedOSName, outBufLen); } return retval; } /* *---------------------------------------------------------------------- * * Hostinfo_HypervisorCPUIDSig -- * * Get the hypervisor signature string from CPUID. * * Results: * Unqualified 16 byte nul-terminated hypervisor string * String may contain garbage and caller must free * * Side effects: * None * *---------------------------------------------------------------------- */ char * Hostinfo_HypervisorCPUIDSig(void) { uint32 *name = NULL; #if defined(__i386__) || defined(__x86_64__) CPUIDRegs regs; __GET_CPUID(1, ®s); if (!(regs.ecx & CPUID_FEATURE_COMMON_ID1ECX_HYPERVISOR)) { return NULL; } regs.ebx = 0; regs.ecx = 0; regs.edx = 0; __GET_CPUID(0x40000000, ®s); if (regs.eax < 0x40000000) { Log(LGPFX" CPUID hypervisor bit is set, but no " "hypervisor vendor signature is present\n"); } name = Util_SafeMalloc(4 * sizeof *name); name[0] = regs.ebx; name[1] = regs.ecx; name[2] = regs.edx; name[3] = 0; #endif // defined(__i386__) || defined(__x86_64__) return (char *)name; } /* *---------------------------------------------------------------------- * * Hostinfo_TouchXen -- * * Check for Xen. * * Official way is to call Hostinfo_HypervisorCPUIDSig(), which * returns a hypervisor string. This is a secondary check * that guards against a backdoor failure. See PR156185, * http://xenbits.xensource.com/xen-unstable.hg?file/6a383beedf83/tools/misc/xen-detect.c * (Canonical way is /proc/xen, but CPUID is better). * * Results: * TRUE if we are running in a Xen dom0 or domU. * Linux: * Illegal instruction exception on real hardware. * Obscure Xen implementations might return FALSE. * Windows: * FALSE on real hardware. * * Side effects: * Linux: Will raise exception on native hardware. * Windows: None. * *---------------------------------------------------------------------- */ Bool Hostinfo_TouchXen(void) { #if defined(linux) && (defined(__i386__) || defined(__x86_64__)) #define XEN_CPUID 0x40000000 CPUIDRegs regs; uint32 name[4]; /* * PV mode: ud2a "xen" cpuid (faults on native hardware). * (Only Linux can run PV, so skip others here). * Since PV cannot trap CPUID, this is a Xen hook. */ regs.eax = XEN_CPUID; __asm__ __volatile__( "xchgl %%ebx, %0" "\n\t" "ud2a ; .ascii \"xen\" ; cpuid" "\n\t" "xchgl %%ebx, %0" : "=&r" (regs.ebx), "=&c" (regs.ecx), "=&d" (regs.edx) : "a" (regs.eax) ); name[0] = regs.ebx; name[1] = regs.ecx; name[2] = regs.edx; name[3] = 0; if (0 == strcmp(CPUID_XEN_HYPERVISOR_VENDOR_STRING, (const char*)name)) { return TRUE; } /* Passed checks. But native and anything non-Xen would #UD before here. */ NOT_TESTED(); Log("Xen detected but hypervisor unrecognized (Xen variant?)\n"); Log("CPUID 0x4000 0000: eax=%x ebx=%x ecx=%x edx=%x\n", regs.eax, regs.ebx, regs.ecx, regs.edx); #endif return FALSE; } /* *----------------------------------------------------------------------------- * * Hostinfo_SystemTimerUS -- * * Return the time. * - These timers are documented to never go backwards. * - These timers may take locks * * NOTES: * These are the routines to use when performing timing measurements. * * The value returned is valid (finish-time - start-time) only within a * single process. Don't send a time measurement obtained with these * routines to another process and expect a relative time measurement * to be correct. * * The actual resolution of these "clocks" are undefined - it varies * depending on hardware, OSen and OS versions. * * Results: * The time in microseconds is returned. Zero upon error. * * Side effects: * None. * *----------------------------------------------------------------------------- */ VmTimeType Hostinfo_SystemTimerUS(void) { return Hostinfo_SystemTimerNS() / 1000; }