X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/c0fea4742e91338fffdcf79f86a7c1d5e2b97eb1..15129b1c8dbb3650c63b70adb1cad9af601c6c17:/osfmk/i386/tsc.c diff --git a/osfmk/i386/tsc.c b/osfmk/i386/tsc.c index d299aff34..f79a8fcb2 100644 --- a/osfmk/i386/tsc.c +++ b/osfmk/i386/tsc.c @@ -1,23 +1,29 @@ /* - * Copyright (c) 2005-2006 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2005-2007 Apple Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * @OSF_COPYRIGHT@ @@ -30,7 +36,6 @@ */ #include -#include #include @@ -45,24 +50,19 @@ #include #include #include /* for kernel_map */ -#include -#include #include -#include -#include #include -#include #include -#include -#include -#include +#include #include +#include +#include +#include #include #include #include #include #include -#include uint64_t busFCvtt2n = 0; uint64_t busFCvtn2t = 0; @@ -71,6 +71,16 @@ uint64_t tscFCvtt2n = 0; uint64_t tscFCvtn2t = 0; uint64_t tscGranularity = 0; uint64_t bus2tsc = 0; +uint64_t busFreq = 0; +uint32_t flex_ratio = 0; +uint32_t flex_ratio_min = 0; +uint32_t flex_ratio_max = 0; + +uint64_t tsc_at_boot = 0; + +#define bit(n) (1ULL << (n)) +#define bitmask(h,l) ((bit(h)|(bit(h)-1)) & ~(bit(l)-1)) +#define bitfield(x,h,l) (((x) & bitmask(h,l)) >> l) /* Decimal powers: */ #define kilo (1000ULL) @@ -79,10 +89,13 @@ uint64_t bus2tsc = 0; #define Tera (kilo * Giga) #define Peta (kilo * Tera) +#define CPU_FAMILY_PENTIUM_M (0x6) + static const char FSB_Frequency_prop[] = "FSBFrequency"; +static const char TSC_at_boot_prop[] = "InitialTSC"; /* - * This routine extracts the front-side bus frequency in Hz from - * the device tree. + * This routine extracts the bus frequency in Hz from the device tree. + * Also reads any initial TSC value at boot from the device tree. */ static uint64_t EFI_FSB_frequency(void) @@ -90,14 +103,15 @@ EFI_FSB_frequency(void) uint64_t frequency = 0; DTEntry entry; void *value; - int size; + unsigned int size; if (DTLookupEntry(0, "/efi/platform", &entry) != kSuccess) { kprintf("EFI_FSB_frequency: didn't find /efi/platform\n"); return 0; } if (DTGetProperty(entry,FSB_Frequency_prop,&value,&size) != kSuccess) { - kprintf("EFI_FSB_frequency: property %s not found\n"); + kprintf("EFI_FSB_frequency: property %s not found\n", + FSB_Frequency_prop); return 0; } if (size == sizeof(uint64_t)) { @@ -111,6 +125,18 @@ EFI_FSB_frequency(void) } else { kprintf("EFI_FSB_frequency: unexpected size %d\n", size); } + + /* + * While we're here, see if EFI published an initial TSC value. + */ + if (DTGetProperty(entry,TSC_at_boot_prop,&value,&size) == kSuccess) { + if (size == sizeof(uint64_t)) { + tsc_at_boot = *(uint64_t *) value; + kprintf("EFI_FSB_frequency: read %s value: %llu\n", + TSC_at_boot_prop, tsc_at_boot); + } + } + return frequency; } @@ -121,95 +147,138 @@ EFI_FSB_frequency(void) void tsc_init(void) { - uint64_t busFreq; - uint64_t busFCvtInt; - uint32_t cpuModel; - uint32_t cpuFamily; - uint32_t xcpuid[4]; - - /* - * Get the FSB frequency and conversion factors. - */ - busFreq = EFI_FSB_frequency(); - if (busFreq != 0) { - busFCvtt2n = ((1 * Giga) << 32) / busFreq; - busFCvtn2t = 0xFFFFFFFFFFFFFFFFULL / busFCvtt2n; - busFCvtInt = tmrCvt(1 * Peta, 0xFFFFFFFFFFFFFFFFULL / busFreq); - } else { - panic("rtclock_init: EFI not supported!\n"); - } - - kprintf(" BUS: Frequency = %6d.%04dMHz, " - "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X, " - "cvtInt = %08X.%08X\n", - (uint32_t)(busFreq / Mega), - (uint32_t)(busFreq % Mega), - (uint32_t)(busFCvtt2n >> 32), (uint32_t)busFCvtt2n, - (uint32_t)(busFCvtn2t >> 32), (uint32_t)busFCvtn2t, - (uint32_t)(busFCvtInt >> 32), (uint32_t)busFCvtInt); - - do_cpuid(1, xcpuid); - cpuFamily = ( xcpuid[eax] >> 8 ) & 0xf; - /* - * Get the extended family if necessary. - */ - if (cpuFamily == 0x0f) - cpuFamily += (xcpuid[eax] >> 20) & 0x00ff; - - cpuModel = ( xcpuid[eax] >> 4 ) & 0xf; - /* - * Get the extended model if necessary. - */ - if (cpuFamily == CPUID_FAMILY_686 - || cpuFamily == CPUID_FAMILY_EXTENDED) - cpuModel += ((xcpuid[eax] >> 16) & 0xf) << 4; - - /* - * Get the TSC increment. The TSC is incremented by this - * on every bus tick. Calculate the TSC conversion factors - * to and from nano-seconds. - */ - if (cpuFamily == CPUID_FAMILY_686) { - if (cpuModel == CPUID_MODEL_CORE || cpuModel == CPUID_MODEL_CORE2) { - uint64_t prfsts; - - prfsts = rdmsr64(IA32_PERF_STS); - tscGranularity = (uint32_t)(prfsts >> BusRatioShift) & BusRatioMask; + boolean_t N_by_2_bus_ratio = FALSE; + + if (cpuid_vmm_present()) { + kprintf("VMM vendor %u TSC frequency %u KHz bus frequency %u KHz\n", + cpuid_vmm_info()->cpuid_vmm_family, + cpuid_vmm_info()->cpuid_vmm_tsc_frequency, + cpuid_vmm_info()->cpuid_vmm_bus_frequency); + + if (cpuid_vmm_info()->cpuid_vmm_tsc_frequency && + cpuid_vmm_info()->cpuid_vmm_bus_frequency) { + + busFreq = (uint64_t)cpuid_vmm_info()->cpuid_vmm_bus_frequency * kilo; + busFCvtt2n = ((1 * Giga) << 32) / busFreq; + busFCvtn2t = 0xFFFFFFFFFFFFFFFFULL / busFCvtt2n; + + tscFreq = (uint64_t)cpuid_vmm_info()->cpuid_vmm_tsc_frequency * kilo; + tscFCvtt2n = ((1 * Giga) << 32) / tscFreq; + tscFCvtn2t = 0xFFFFFFFFFFFFFFFFULL / tscFCvtt2n; + + tscGranularity = tscFreq / busFreq; + + bus2tsc = tmrCvt(busFCvtt2n, tscFCvtn2t); + + return; + } + } + + /* + * Get the FSB frequency and conversion factors from EFI. + */ + busFreq = EFI_FSB_frequency(); + + switch (cpuid_cpufamily()) { + case CPUFAMILY_INTEL_HASWELL: + case CPUFAMILY_INTEL_IVYBRIDGE: + case CPUFAMILY_INTEL_SANDYBRIDGE: + case CPUFAMILY_INTEL_WESTMERE: + case CPUFAMILY_INTEL_NEHALEM: { + uint64_t msr_flex_ratio; + uint64_t msr_platform_info; + + /* See if FLEX_RATIO is being used */ + msr_flex_ratio = rdmsr64(MSR_FLEX_RATIO); + msr_platform_info = rdmsr64(MSR_PLATFORM_INFO); + flex_ratio_min = (uint32_t)bitfield(msr_platform_info, 47, 40); + flex_ratio_max = (uint32_t)bitfield(msr_platform_info, 15, 8); + /* No BIOS-programed flex ratio. Use hardware max as default */ + tscGranularity = flex_ratio_max; + if (msr_flex_ratio & bit(16)) { + /* Flex Enabled: Use this MSR if less than max */ + flex_ratio = (uint32_t)bitfield(msr_flex_ratio, 15, 8); + if (flex_ratio < flex_ratio_max) + tscGranularity = flex_ratio; + } + + /* If EFI isn't configured correctly, use a constant + * value. See 6036811. + */ + if (busFreq == 0) + busFreq = BASE_NHM_CLOCK_SOURCE; + + break; + } + default: { + uint64_t prfsts; + + prfsts = rdmsr64(IA32_PERF_STS); + tscGranularity = (uint32_t)bitfield(prfsts, 44, 40); + N_by_2_bus_ratio = (prfsts & bit(46)) != 0; + } + } + + if (busFreq != 0) { + busFCvtt2n = ((1 * Giga) << 32) / busFreq; + busFCvtn2t = 0xFFFFFFFFFFFFFFFFULL / busFCvtt2n; } else { - panic("rtclock_init: unknown CPU model: 0x%X\n", - cpuModel); + panic("tsc_init: EFI not supported!\n"); } - } else { - panic("rtclock_init: unknown CPU family: 0x%X\n", - cpuFamily); - } - - tscFCvtt2n = busFCvtt2n / (uint64_t)tscGranularity; - tscFreq = ((1 * Giga) << 32) / tscFCvtt2n; - tscFCvtn2t = 0xFFFFFFFFFFFFFFFFULL / tscFCvtt2n; - - kprintf(" TSC: Frequency = %6d.%04dMHz, " - "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X, gran = %d\n", - (uint32_t)(tscFreq / Mega), - (uint32_t)(tscFreq % Mega), - (uint32_t)(tscFCvtt2n >> 32), (uint32_t)tscFCvtt2n, - (uint32_t)(tscFCvtn2t >> 32), (uint32_t)tscFCvtn2t, - tscGranularity); - - /* - * Calculate conversion from BUS to TSC - */ - bus2tsc = tmrCvt(busFCvtt2n, tscFCvtn2t); + + kprintf(" BUS: Frequency = %6d.%06dMHz, " + "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X\n", + (uint32_t)(busFreq / Mega), + (uint32_t)(busFreq % Mega), + (uint32_t)(busFCvtt2n >> 32), (uint32_t)busFCvtt2n, + (uint32_t)(busFCvtn2t >> 32), (uint32_t)busFCvtn2t); + + /* + * Get the TSC increment. The TSC is incremented by this + * on every bus tick. Calculate the TSC conversion factors + * to and from nano-seconds. + * The tsc granularity is also called the "bus ratio". If the N/2 bit + * is set this indicates the bus ration is 0.5 more than this - i.e. + * that the true bus ratio is (2*tscGranularity + 1)/2. If we cannot + * determine the TSC conversion, assume it ticks at the bus frequency. + */ + if (tscGranularity == 0) + tscGranularity = 1; + + if (N_by_2_bus_ratio) + tscFCvtt2n = busFCvtt2n * 2 / (1 + 2*tscGranularity); + else + tscFCvtt2n = busFCvtt2n / tscGranularity; + + tscFreq = ((1 * Giga) << 32) / tscFCvtt2n; + tscFCvtn2t = 0xFFFFFFFFFFFFFFFFULL / tscFCvtt2n; + + kprintf(" TSC: Frequency = %6d.%06dMHz, " + "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X, gran = %lld%s\n", + (uint32_t)(tscFreq / Mega), + (uint32_t)(tscFreq % Mega), + (uint32_t)(tscFCvtt2n >> 32), (uint32_t)tscFCvtt2n, + (uint32_t)(tscFCvtn2t >> 32), (uint32_t)tscFCvtn2t, + tscGranularity, N_by_2_bus_ratio ? " (N/2)" : ""); + + /* + * Calculate conversion from BUS to TSC + */ + bus2tsc = tmrCvt(busFCvtt2n, tscFCvtn2t); } void tsc_get_info(tscInfo_t *info) { - info->busFCvtt2n = busFCvtt2n; - info->busFCvtn2t = busFCvtn2t; - info->tscFreq = tscFreq; - info->tscFCvtt2n = tscFCvtt2n; - info->tscFCvtn2t = tscFCvtn2t; - info->tscGranularity = tscGranularity; - info->bus2tsc = bus2tsc; + info->busFCvtt2n = busFCvtt2n; + info->busFCvtn2t = busFCvtn2t; + info->tscFreq = tscFreq; + info->tscFCvtt2n = tscFCvtt2n; + info->tscFCvtn2t = tscFCvtn2t; + info->tscGranularity = tscGranularity; + info->bus2tsc = bus2tsc; + info->busFreq = busFreq; + info->flex_ratio = flex_ratio; + info->flex_ratio_min = flex_ratio_min; + info->flex_ratio_max = flex_ratio_max; }