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 * Copyright 2024 Oxide Computer Company 15 */ 16 17 #include <sys/x86_archext.h> 18 #include <sys/tsc.h> 19 20 /* For VMs, this leaf will contain the largest VM leaf supported in EAX */ 21 #define CPUID_VM_LEAF_MAX 0x40000000 22 23 /* 24 * From https://lwn.net/Articles/301888/: 25 * CPUID leaf 0x40000010 (when present on supported VMMs) will contain the TSC 26 * frequency in kHz. 27 */ 28 #define CPUID_VM_LEAF_FREQ 0x40000010 29 30 /* 31 * These get_hwenv() types correspond to the platforms which are known to have 32 * support for exposing the TSC frequency via the aforementioned leaf. 33 */ 34 #define HW_SUPPORTS_FREQ (HW_VMWARE | HW_KVM | HW_VIRTUALBOX | HW_ACRN) 35 36 /* 37 * Allow bypassing the platform identification step when trying to determine if 38 * the host has support for the VM frequency leaf. This allows use of the leaf 39 * on hypervisors which are otherwise unknown. 40 */ 41 int tscc_vmware_match_any = 0; 42 43 static boolean_t 44 tsc_calibrate_vmware(uint64_t *freqp) 45 { 46 struct cpuid_regs regs = { 0 }; 47 48 /* 49 * Are we on a platform with support? (or has the administrator 50 * expressed their intent to bypass this check via the config option.) 51 */ 52 if ((get_hwenv() & HW_SUPPORTS_FREQ) == 0 && 53 tscc_vmware_match_any == 0) { 54 return (B_FALSE); 55 } 56 57 /* ... And does it expose up through the required leaf? */ 58 regs.cp_eax = CPUID_VM_LEAF_MAX; 59 __cpuid_insn(®s); 60 if (regs.cp_eax < CPUID_VM_LEAF_FREQ) { 61 return (B_FALSE); 62 } 63 64 regs.cp_eax = CPUID_VM_LEAF_FREQ; 65 __cpuid_insn(®s); 66 67 /* 68 * While not observed in the wild, as a precautionary measure, 69 * we treat a value of 0 as a failure out of an excess of caution. 70 */ 71 if (regs.cp_eax == 0) { 72 return (B_FALSE); 73 } 74 75 /* Convert from kHz to Hz */ 76 *freqp = (uint64_t)regs.cp_eax * 1000; 77 78 return (B_TRUE); 79 } 80 81 static tsc_calibrate_t tsc_calibration_vmware = { 82 .tscc_source = "VMware", 83 .tscc_preference = 100, 84 .tscc_calibrate = tsc_calibrate_vmware, 85 }; 86 TSC_CALIBRATION_SOURCE(tsc_calibration_vmware); 87