X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/c910b4d9d2451126ae3917b931cd4390c11e1d52..527f99514973766e9c0382a4d8550dfb00f54939:/osfmk/i386/tsc.c diff --git a/osfmk/i386/tsc.c b/osfmk/i386/tsc.c index 669bc401f..c776541db 100644 --- a/osfmk/i386/tsc.c +++ b/osfmk/i386/tsc.c @@ -35,8 +35,6 @@ * factors needed by other parts of the system. */ -#include -#include #include @@ -51,21 +49,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 uint64_t busFCvtt2n = 0; uint64_t busFCvtn2t = 0; @@ -79,6 +75,7 @@ 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)) @@ -93,12 +90,12 @@ uint32_t flex_ratio_max = 0; #define CPU_FAMILY_PENTIUM_M (0x6) -static const char FSB_Frequency_prop[] = "FSBFrequency"; /* - * This routine extracts the 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; @@ -106,25 +103,30 @@ EFI_FSB_frequency(void) 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", - FSB_Frequency_prop); + 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; } @@ -135,22 +137,66 @@ EFI_FSB_frequency(void) void tsc_init(void) { - uint64_t busFCvtInt = 0; boolean_t N_by_2_bus_ratio = FALSE; - /* - * Get the FSB frequency and conversion factors from EFI. - */ - busFreq = EFI_FSB_frequency(); + 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); - if (cpuid_info()->cpuid_family != CPU_FAMILY_PENTIUM_M) { - panic("tsc_init: unknown CPU family: 0x%X\n", - cpuid_info()->cpuid_family); + return; + } } - switch (cpuid_info()->cpuid_model) { - case CPUID_MODEL_NEHALEM: { - uint64_t cpu_mhz; + 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; @@ -168,74 +214,76 @@ tsc_init(void) 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; - - cpu_mhz = tscGranularity * BASE_NHM_CLOCK_SOURCE; + busFreq = BASE_NHM_CLOCK_SOURCE; - kprintf("[NHM] Maximum Non-Turbo Ratio = [%d]\n", - (uint32_t)tscGranularity); - kprintf("[NHM] CPU: Frequency = %6d.%04dMhz\n", - (uint32_t)(cpu_mhz / Mega), (uint32_t)(cpu_mhz % Mega)); break; } - default: { + 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; - busFCvtInt = tmrCvt(1 * Peta, 0xFFFFFFFFFFFFFFFFULL / busFreq); } else { panic("tsc_init: EFI not supported!\n"); } - kprintf(" BUS: Frequency = %6d.%04dMHz, " - "cvtt2n = %08X.%08X, cvtn2t = %08X.%08X, " - "cvtInt = %08X.%08X\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, - (uint32_t)(busFCvtInt >> 32), (uint32_t)busFCvtInt); + (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 (N_by_2_bus_ratio) - tscFCvtt2n = busFCvtt2n * 2 / (1 + 2*tscGranularity); - else - tscFCvtt2n = busFCvtt2n / tscGranularity; + if (tscFreq == busFreq) { + bus2tsc = 1; + tscGranularity = 1; + tscFCvtn2t = busFCvtn2t; + tscFCvtt2n = busFCvtt2n; + } else { + /* + * 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; - tscFreq = ((1 * Giga) << 32) / tscFCvtt2n; - tscFCvtn2t = 0xFFFFFFFFFFFFFFFFULL / tscFCvtt2n; + /* + * Calculate conversion from BUS to TSC + */ + bus2tsc = tmrCvt(busFCvtt2n, tscFCvtn2t); + } - kprintf(" TSC: Frequency = %6d.%04dMHz, " + 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