| 1 | /* |
| 2 | * Copyright (c) 2005-2007 Apple Inc. All rights reserved. |
| 3 | * |
| 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
| 5 | * |
| 6 | * This file contains Original Code and/or Modifications of Original Code |
| 7 | * as defined in and that are subject to the Apple Public Source License |
| 8 | * Version 2.0 (the 'License'). You may not use this file except in |
| 9 | * compliance with the License. The rights granted to you under the License |
| 10 | * may not be used to create, or enable the creation or redistribution of, |
| 11 | * unlawful or unlicensed copies of an Apple operating system, or to |
| 12 | * circumvent, violate, or enable the circumvention or violation of, any |
| 13 | * terms of an Apple operating system software license agreement. |
| 14 | * |
| 15 | * Please obtain a copy of the License at |
| 16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. |
| 17 | * |
| 18 | * The Original Code and all software distributed under the License are |
| 19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
| 20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
| 21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
| 22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
| 23 | * Please see the License for the specific language governing rights and |
| 24 | * limitations under the License. |
| 25 | * |
| 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
| 27 | */ |
| 28 | /* |
| 29 | * @OSF_COPYRIGHT@ |
| 30 | */ |
| 31 | |
| 32 | /* |
| 33 | * File: i386/tsc.c |
| 34 | * Purpose: Initializes the TSC and the various conversion |
| 35 | * factors needed by other parts of the system. |
| 36 | */ |
| 37 | |
| 38 | |
| 39 | #include <mach/mach_types.h> |
| 40 | |
| 41 | #include <kern/cpu_data.h> |
| 42 | #include <kern/cpu_number.h> |
| 43 | #include <kern/clock.h> |
| 44 | #include <kern/host_notify.h> |
| 45 | #include <kern/macro_help.h> |
| 46 | #include <kern/misc_protos.h> |
| 47 | #include <kern/spl.h> |
| 48 | #include <kern/assert.h> |
| 49 | #include <mach/vm_prot.h> |
| 50 | #include <vm/pmap.h> |
| 51 | #include <vm/vm_kern.h> /* for kernel_map */ |
| 52 | #include <architecture/i386/pio.h> |
| 53 | #include <i386/machine_cpu.h> |
| 54 | #include <i386/cpuid.h> |
| 55 | #include <i386/mp.h> |
| 56 | #include <i386/machine_routines.h> |
| 57 | #include <i386/proc_reg.h> |
| 58 | #include <i386/tsc.h> |
| 59 | #include <i386/misc_protos.h> |
| 60 | #include <pexpert/pexpert.h> |
| 61 | #include <machine/limits.h> |
| 62 | #include <machine/commpage.h> |
| 63 | #include <sys/kdebug.h> |
| 64 | #include <pexpert/device_tree.h> |
| 65 | |
| 66 | uint64_t busFCvtt2n = 0; |
| 67 | uint64_t busFCvtn2t = 0; |
| 68 | uint64_t tscFreq = 0; |
| 69 | uint64_t tscFCvtt2n = 0; |
| 70 | uint64_t tscFCvtn2t = 0; |
| 71 | uint64_t tscGranularity = 0; |
| 72 | uint64_t bus2tsc = 0; |
| 73 | uint64_t busFreq = 0; |
| 74 | uint32_t flex_ratio = 0; |
| 75 | uint32_t flex_ratio_min = 0; |
| 76 | uint32_t flex_ratio_max = 0; |
| 77 | |
| 78 | uint64_t tsc_at_boot = 0; |
| 79 | |
| 80 | #define bit(n) (1ULL << (n)) |
| 81 | #define bitmask(h,l) ((bit(h)|(bit(h)-1)) & ~(bit(l)-1)) |
| 82 | #define bitfield(x,h,l) (((x) & bitmask(h,l)) >> l) |
| 83 | |
| 84 | /* Decimal powers: */ |
| 85 | #define kilo (1000ULL) |
| 86 | #define Mega (kilo * kilo) |
| 87 | #define Giga (kilo * Mega) |
| 88 | #define Tera (kilo * Giga) |
| 89 | #define Peta (kilo * Tera) |
| 90 | |
| 91 | #define CPU_FAMILY_PENTIUM_M (0x6) |
| 92 | |
| 93 | /* |
| 94 | * This routine extracts a frequency property in Hz from the device tree. |
| 95 | * Also reads any initial TSC value at boot from the device tree. |
| 96 | */ |
| 97 | static uint64_t |
| 98 | EFI_get_frequency(const char *prop) |
| 99 | { |
| 100 | uint64_t frequency = 0; |
| 101 | DTEntry entry; |
| 102 | void *value; |
| 103 | unsigned int size; |
| 104 | |
| 105 | if (DTLookupEntry(0, "/efi/platform" , &entry) != kSuccess) { |
| 106 | kprintf("EFI_get_frequency: didn't find /efi/platform\n" ); |
| 107 | return 0; |
| 108 | } |
| 109 | if (DTGetProperty(entry,prop,&value,&size) != kSuccess) { |
| 110 | kprintf("EFI_get_frequency: property %s not found\n" , prop); |
| 111 | return 0; |
| 112 | } |
| 113 | if (size == sizeof(uint64_t)) { |
| 114 | frequency = *(uint64_t *) value; |
| 115 | kprintf("EFI_get_frequency: read %s value: %llu\n" , |
| 116 | prop, frequency); |
| 117 | } |
| 118 | |
| 119 | /* |
| 120 | * While we're here, see if EFI published an initial TSC value. |
| 121 | */ |
| 122 | if (DTGetProperty(entry,"InitialTSC" ,&value,&size) == kSuccess) { |
| 123 | if (size == sizeof(uint64_t)) { |
| 124 | tsc_at_boot = *(uint64_t *) value; |
| 125 | kprintf("EFI_get_frequency: read InitialTSC: %llu\n" , |
| 126 | tsc_at_boot); |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | return frequency; |
| 131 | } |
| 132 | |
| 133 | /* |
| 134 | * Initialize the various conversion factors needed by code referencing |
| 135 | * the TSC. |
| 136 | */ |
| 137 | void |
| 138 | tsc_init(void) |
| 139 | { |
| 140 | boolean_t N_by_2_bus_ratio = FALSE; |
| 141 | |
| 142 | if (cpuid_vmm_present()) { |
| 143 | kprintf("VMM vendor %u TSC frequency %u KHz bus frequency %u KHz\n" , |
| 144 | cpuid_vmm_info()->cpuid_vmm_family, |
| 145 | cpuid_vmm_info()->cpuid_vmm_tsc_frequency, |
| 146 | cpuid_vmm_info()->cpuid_vmm_bus_frequency); |
| 147 | |
| 148 | if (cpuid_vmm_info()->cpuid_vmm_tsc_frequency && |
| 149 | cpuid_vmm_info()->cpuid_vmm_bus_frequency) { |
| 150 | |
| 151 | busFreq = (uint64_t)cpuid_vmm_info()->cpuid_vmm_bus_frequency * kilo; |
| 152 | busFCvtt2n = ((1 * Giga) << 32) / busFreq; |
| 153 | busFCvtn2t = 0xFFFFFFFFFFFFFFFFULL / busFCvtt2n; |
| 154 | |
| 155 | tscFreq = (uint64_t)cpuid_vmm_info()->cpuid_vmm_tsc_frequency * kilo; |
| 156 | tscFCvtt2n = ((1 * Giga) << 32) / tscFreq; |
| 157 | tscFCvtn2t = 0xFFFFFFFFFFFFFFFFULL / tscFCvtt2n; |
| 158 | |
| 159 | tscGranularity = tscFreq / busFreq; |
| 160 | |
| 161 | bus2tsc = tmrCvt(busFCvtt2n, tscFCvtn2t); |
| 162 | |
| 163 | return; |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | switch (cpuid_cpufamily()) { |
| 168 | case CPUFAMILY_INTEL_KABYLAKE: |
| 169 | case CPUFAMILY_INTEL_SKYLAKE: { |
| 170 | /* |
| 171 | * SkyLake and later has an Always Running Timer (ART) providing |
| 172 | * the reference frequency. CPUID leaf 0x15 determines the |
| 173 | * rationship between this and the TSC frequency expressed as |
| 174 | * - multiplier (numerator, N), and |
| 175 | * - divisor (denominator, M). |
| 176 | * So that TSC = ART * N / M. |
| 177 | */ |
| 178 | cpuid_tsc_leaf_t *tsc_leafp = &cpuid_info()->cpuid_tsc_leaf; |
| 179 | uint64_t N = (uint64_t) tsc_leafp->numerator; |
| 180 | uint64_t M = (uint64_t) tsc_leafp->denominator; |
| 181 | uint64_t refFreq; |
| 182 | |
| 183 | refFreq = EFI_get_frequency("ARTFrequency" ); |
| 184 | if (refFreq == 0) |
| 185 | refFreq = BASE_ART_CLOCK_SOURCE; |
| 186 | |
| 187 | assert(N != 0); |
| 188 | assert(M != 1); |
| 189 | tscFreq = refFreq * N / M; |
| 190 | busFreq = tscFreq; /* bus is APIC frequency */ |
| 191 | |
| 192 | kprintf(" ART: Frequency = %6d.%06dMHz, N/M = %lld/%llu\n" , |
| 193 | (uint32_t)(refFreq / Mega), |
| 194 | (uint32_t)(refFreq % Mega), |
| 195 | N, M); |
| 196 | |
| 197 | break; |
| 198 | } |
| 199 | default: { |
| 200 | uint64_t msr_flex_ratio; |
| 201 | uint64_t msr_platform_info; |
| 202 | |
| 203 | /* See if FLEX_RATIO is being used */ |
| 204 | msr_flex_ratio = rdmsr64(MSR_FLEX_RATIO); |
| 205 | msr_platform_info = rdmsr64(MSR_PLATFORM_INFO); |
| 206 | flex_ratio_min = (uint32_t)bitfield(msr_platform_info, 47, 40); |
| 207 | flex_ratio_max = (uint32_t)bitfield(msr_platform_info, 15, 8); |
| 208 | /* No BIOS-programed flex ratio. Use hardware max as default */ |
| 209 | tscGranularity = flex_ratio_max; |
| 210 | if (msr_flex_ratio & bit(16)) { |
| 211 | /* Flex Enabled: Use this MSR if less than max */ |
| 212 | flex_ratio = (uint32_t)bitfield(msr_flex_ratio, 15, 8); |
| 213 | if (flex_ratio < flex_ratio_max) |
| 214 | tscGranularity = flex_ratio; |
| 215 | } |
| 216 | |
| 217 | busFreq = EFI_get_frequency("FSBFrequency" ); |
| 218 | /* If EFI isn't configured correctly, use a constant |
| 219 | * value. See 6036811. |
| 220 | */ |
| 221 | if (busFreq == 0) |
| 222 | busFreq = BASE_NHM_CLOCK_SOURCE; |
| 223 | |
| 224 | break; |
| 225 | } |
| 226 | case CPUFAMILY_INTEL_PENRYN: { |
| 227 | uint64_t prfsts; |
| 228 | |
| 229 | prfsts = rdmsr64(IA32_PERF_STS); |
| 230 | tscGranularity = (uint32_t)bitfield(prfsts, 44, 40); |
| 231 | N_by_2_bus_ratio = (prfsts & bit(46)) != 0; |
| 232 | |
| 233 | busFreq = EFI_get_frequency("FSBFrequency" ); |
| 234 | } |
| 235 | } |
| 236 | |
| 237 | if (busFreq != 0) { |
| 238 | busFCvtt2n = ((1 * Giga) << 32) / busFreq; |
| 239 | busFCvtn2t = 0xFFFFFFFFFFFFFFFFULL / busFCvtt2n; |
| 240 | } else { |
| 241 | panic("tsc_init: EFI not supported!\n" ); |
| 242 | } |
| 243 | |
| 244 | kprintf(" BUS: Frequency = %6d.%06dMHz, " |
| 245 | "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X\n" , |
| 246 | (uint32_t)(busFreq / Mega), |
| 247 | (uint32_t)(busFreq % Mega), |
| 248 | (uint32_t)(busFCvtt2n >> 32), (uint32_t)busFCvtt2n, |
| 249 | (uint32_t)(busFCvtn2t >> 32), (uint32_t)busFCvtn2t); |
| 250 | |
| 251 | if (tscFreq == busFreq) { |
| 252 | bus2tsc = 1; |
| 253 | tscGranularity = 1; |
| 254 | tscFCvtn2t = busFCvtn2t; |
| 255 | tscFCvtt2n = busFCvtt2n; |
| 256 | } else { |
| 257 | /* |
| 258 | * Get the TSC increment. The TSC is incremented by this |
| 259 | * on every bus tick. Calculate the TSC conversion factors |
| 260 | * to and from nano-seconds. |
| 261 | * The tsc granularity is also called the "bus ratio". |
| 262 | * If the N/2 bit is set this indicates the bus ration is |
| 263 | * 0.5 more than this - i.e. that the true bus ratio |
| 264 | * is (2*tscGranularity + 1)/2. |
| 265 | */ |
| 266 | if (N_by_2_bus_ratio) |
| 267 | tscFCvtt2n = busFCvtt2n * 2 / (1 + 2*tscGranularity); |
| 268 | else |
| 269 | tscFCvtt2n = busFCvtt2n / tscGranularity; |
| 270 | |
| 271 | tscFreq = ((1 * Giga) << 32) / tscFCvtt2n; |
| 272 | tscFCvtn2t = 0xFFFFFFFFFFFFFFFFULL / tscFCvtt2n; |
| 273 | |
| 274 | /* |
| 275 | * Calculate conversion from BUS to TSC |
| 276 | */ |
| 277 | bus2tsc = tmrCvt(busFCvtt2n, tscFCvtn2t); |
| 278 | } |
| 279 | |
| 280 | kprintf(" TSC: Frequency = %6d.%06dMHz, " |
| 281 | "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X, gran = %lld%s\n" , |
| 282 | (uint32_t)(tscFreq / Mega), |
| 283 | (uint32_t)(tscFreq % Mega), |
| 284 | (uint32_t)(tscFCvtt2n >> 32), (uint32_t)tscFCvtt2n, |
| 285 | (uint32_t)(tscFCvtn2t >> 32), (uint32_t)tscFCvtn2t, |
| 286 | tscGranularity, N_by_2_bus_ratio ? " (N/2)" : "" ); |
| 287 | } |
| 288 | |
| 289 | void |
| 290 | tsc_get_info(tscInfo_t *info) |
| 291 | { |
| 292 | info->busFCvtt2n = busFCvtt2n; |
| 293 | info->busFCvtn2t = busFCvtn2t; |
| 294 | info->tscFreq = tscFreq; |
| 295 | info->tscFCvtt2n = tscFCvtt2n; |
| 296 | info->tscFCvtn2t = tscFCvtn2t; |
| 297 | info->tscGranularity = tscGranularity; |
| 298 | info->bus2tsc = bus2tsc; |
| 299 | info->busFreq = busFreq; |
| 300 | info->flex_ratio = flex_ratio; |
| 301 | info->flex_ratio_min = flex_ratio_min; |
| 302 | info->flex_ratio_max = flex_ratio_max; |
| 303 | } |
| 304 | |