1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2021 Jason King 14 */ 15 16 #include <sys/x86_archext.h> 17 #include <sys/tsc.h> 18 19 /* For VMs, this leaf will contain the largest VM leaf supported in EAX */ 20 #define CPUID_VM_LEAF_MAX 0x40000000 21 22 /* 23 * From https://lwn.net/Articles/301888/, CPUID leaf 0x40000010 (when present) 24 * on VMware will contain the TSC frequency in kHz. While it would have been 25 * nice to locate an official bit of documentation from VMware, implementations 26 * in both Linux and FreeBSD agree with the above link, so it seems reasonable 27 * to use it as well. 28 */ 29 #define CPUID_VM_LEAF_FREQ 0x40000010 30 31 static boolean_t 32 tsc_calibrate_vmware(uint64_t *freqp) 33 { 34 if (get_hwenv() != HW_VMWARE) 35 return (B_FALSE); 36 37 struct cpuid_regs regs = { 0 }; 38 39 /* First determine the largest VM leaf supported */ 40 regs.cp_eax = CPUID_VM_LEAF_MAX; 41 __cpuid_insn(®s); 42 43 if (regs.cp_eax < CPUID_VM_LEAF_FREQ) 44 return (B_FALSE); 45 46 regs.cp_eax = CPUID_VM_LEAF_FREQ; 47 __cpuid_insn(®s); 48 49 /* 50 * While not observed in the wild, as a precautionary measure, 51 * we treat a value of 0 as a failure out of an excess of caution. 52 */ 53 if (regs.cp_eax == 0) 54 return (B_FALSE); 55 56 /* Convert from kHz to Hz */ 57 *freqp = (uint64_t)regs.cp_eax * 1000; 58 59 return (B_TRUE); 60 } 61 62 static tsc_calibrate_t tsc_calibration_vmware = { 63 .tscc_source = "VMware", 64 .tscc_preference = 100, 65 .tscc_calibrate = tsc_calibrate_vmware, 66 }; 67 TSC_CALIBRATION_SOURCE(tsc_calibration_vmware); 68