]> git.saurik.com Git - apple/xnu.git/blobdiff - osfmk/i386/tsc.c
xnu-4570.41.2.tar.gz
[apple/xnu.git] / osfmk / i386 / tsc.c
index d299aff344291ee2e92406ba3e8bd66577fe617b..c776541db5f04723619740476c7b7e5e4e59ce5a 100644 (file)
@@ -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@
@@ -29,8 +35,6 @@
  *                     factors needed by other parts of the system.
  */
 
-#include <platforms.h>
-#include <mach_kdb.h>
 
 #include <mach/mach_types.h>
 
 #include <mach/vm_prot.h>
 #include <vm/pmap.h>
 #include <vm/vm_kern.h>                /* for kernel_map */
-#include <i386/ipl.h>
-#include <i386/pit.h>
 #include <architecture/i386/pio.h>
-#include <i386/misc_protos.h>
-#include <i386/proc_reg.h>
 #include <i386/machine_cpu.h>
-#include <i386/mp.h>
 #include <i386/cpuid.h>
-#include <i386/cpu_data.h>
-#include <i386/cpu_threads.h>
-#include <i386/perfmon.h>
+#include <i386/mp.h>
 #include <i386/machine_routines.h>
+#include <i386/proc_reg.h>
+#include <i386/tsc.h>
+#include <i386/misc_protos.h>
 #include <pexpert/pexpert.h>
 #include <machine/limits.h>
 #include <machine/commpage.h>
 #include <sys/kdebug.h>
 #include <pexpert/device_tree.h>
-#include <i386/tsc.h>
 
 uint64_t       busFCvtt2n = 0;
 uint64_t       busFCvtn2t = 0;
@@ -71,6 +70,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,38 +88,45 @@ uint64_t    bus2tsc = 0;
 #define Tera (kilo * Giga)
 #define Peta (kilo * Tera)
 
-static const char      FSB_Frequency_prop[] = "FSBFrequency";
+#define CPU_FAMILY_PENTIUM_M   (0x6)
+
 /*
- * This routine extracts the front-side bus frequency in Hz from
- * the device tree.
+ * This routine extracts a frequency property 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)
+EFI_get_frequency(const char *prop)
 {
        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");
+               kprintf("EFI_get_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");
+       if (DTGetProperty(entry,prop,&value,&size) != kSuccess) {
+               kprintf("EFI_get_frequency: property %s not found\n", prop);
                return 0;
        }
        if (size == sizeof(uint64_t)) {
                frequency = *(uint64_t *) value;
-               kprintf("EFI_FSB_frequency: read %s value: %llu\n",
-                       FSB_Frequency_prop, frequency);
-               if (!(90*Mega < frequency && frequency < 10*Giga)) {
-                       kprintf("EFI_FSB_frequency: value out of range\n");
-                       frequency = 0;
+               kprintf("EFI_get_frequency: read %s value: %llu\n",
+                       prop, frequency);
+       }
+
+       /*
+        * While we're here, see if EFI published an initial TSC value.
+        */
+       if (DTGetProperty(entry,"InitialTSC",&value,&size) == kSuccess) {
+               if (size == sizeof(uint64_t)) {
+                       tsc_at_boot = *(uint64_t *) value;
+                       kprintf("EFI_get_frequency: read InitialTSC: %llu\n",
+                               tsc_at_boot);
                }
-       } else {
-               kprintf("EFI_FSB_frequency: unexpected size %d\n", size);
        }
+
        return frequency;
 }
 
@@ -121,95 +137,167 @@ 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;
+               }
+       }
+
+       switch (cpuid_cpufamily()) {
+       case CPUFAMILY_INTEL_KABYLAKE:
+       case CPUFAMILY_INTEL_SKYLAKE: {
+               /*
+                * SkyLake and later has an Always Running Timer (ART) providing
+                * the reference frequency. CPUID leaf 0x15 determines the
+                * rationship between this and the TSC frequency expressed as
+                *   -  multiplier (numerator, N), and 
+                *   -  divisor (denominator, M).
+                * So that TSC = ART * N / M.
+                */
+               cpuid_tsc_leaf_t *tsc_leafp = &cpuid_info()->cpuid_tsc_leaf;
+               uint64_t         N = (uint64_t) tsc_leafp->numerator;
+               uint64_t         M = (uint64_t) tsc_leafp->denominator;
+               uint64_t         refFreq;
+
+               refFreq = EFI_get_frequency("ARTFrequency");
+               if (refFreq == 0)
+                       refFreq = BASE_ART_CLOCK_SOURCE;
+
+               assert(N != 0);
+               assert(M != 1);
+               tscFreq = refFreq * N / M;
+               busFreq = tscFreq;              /* bus is APIC frequency */
+
+               kprintf(" ART: Frequency = %6d.%06dMHz, N/M = %lld/%llu\n",
+                       (uint32_t)(refFreq / Mega),
+                       (uint32_t)(refFreq % Mega), 
+                       N, M);
+
+               break;
+           }
+       default: {
+               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;
+               }
+
+               busFreq = EFI_get_frequency("FSBFrequency");
+               /* If EFI isn't configured correctly, use a constant 
+                * value. See 6036811.
+                */
+               if (busFreq == 0)
+                   busFreq = BASE_NHM_CLOCK_SOURCE;
+
+               break;
+            }
+       case CPUFAMILY_INTEL_PENRYN: {
+               uint64_t        prfsts;
+
+               prfsts = rdmsr64(IA32_PERF_STS);
+               tscGranularity = (uint32_t)bitfield(prfsts, 44, 40);
+               N_by_2_bus_ratio = (prfsts & bit(46)) != 0;
+
+               busFreq = EFI_get_frequency("FSBFrequency");
+           }
+       }
+
+       if (busFreq != 0) {
+               busFCvtt2n = ((1 * Giga) << 32) / busFreq;
+               busFCvtn2t = 0xFFFFFFFFFFFFFFFFULL / busFCvtt2n;
+       } else {
+               panic("tsc_init: EFI not supported!\n");
+       }
+
+       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);
+
+       if (tscFreq == busFreq) {
+               bus2tsc = 1;
+               tscGranularity = 1;
+               tscFCvtn2t = busFCvtn2t;
+               tscFCvtt2n = busFCvtt2n;
        } else {
-           panic("rtclock_init: unknown CPU model: 0x%X\n",
-             cpuModel);
+               /*
+                * 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 (N_by_2_bus_ratio)
+                       tscFCvtt2n = busFCvtt2n * 2 / (1 + 2*tscGranularity);
+               else
+                       tscFCvtt2n = busFCvtt2n / tscGranularity;
+
+               tscFreq = ((1 * Giga)  << 32) / tscFCvtt2n;
+               tscFCvtn2t = 0xFFFFFFFFFFFFFFFFULL / tscFCvtt2n;
+
+               /*
+                * Calculate conversion from BUS to TSC
+                */
+               bus2tsc = tmrCvt(busFCvtt2n, tscFCvtn2t);
        }
-    } 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(" 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)" : "");
 }
 
 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;
 }