Changeset View
Changeset View
Standalone View
Standalone View
sys/x86/x86/tsc.c
Show First 20 Lines • Show All 259 Lines • ▼ Show 20 Lines | tsc_freq_8254(uint64_t *res) | ||||
DELAY(100000); | DELAY(100000); | ||||
tsc2 = rdtsc_ordered(); | tsc2 = rdtsc_ordered(); | ||||
tsc_freq = (tsc2 - tsc1 - overhead) * 10; | tsc_freq = (tsc2 - tsc1 - overhead) * 10; | ||||
} | } | ||||
static void | static void | ||||
probe_tsc_freq(void) | probe_tsc_freq(void) | ||||
{ | { | ||||
if (cpu_power_ecx & CPUID_PERF_STAT) { | if ((cpu_feature & CPUID_TSC) == 0) | ||||
return; | |||||
#ifdef __i386__ | |||||
/* The TSC is known to be broken on certain CPUs. */ | |||||
switch (cpu_vendor_id) { | |||||
case CPU_VENDOR_AMD: | |||||
switch (cpu_id & 0xFF0) { | |||||
case 0x500: | |||||
/* K5 Model 0 */ | |||||
tsc_disabled = 1; | |||||
return; | |||||
} | |||||
break; | |||||
case CPU_VENDOR_CENTAUR: | |||||
switch (cpu_id & 0xff0) { | |||||
case 0x540: | |||||
/* | /* | ||||
* XXX Some emulators expose host CPUID without actual support | * http://www.centtech.com/c6_data_sheet.pdf | ||||
* for these MSRs. We must test whether they really work. | * | ||||
* I-12 RDTSC may return incoherent values in EDX:EAX | |||||
* I-13 RDTSC hangs when certain event counters are used | |||||
*/ | */ | ||||
wrmsr(MSR_MPERF, 0); | tsc_disabled = 1; | ||||
wrmsr(MSR_APERF, 0); | return; | ||||
DELAY(10); | |||||
if (rdmsr(MSR_MPERF) > 0 && rdmsr(MSR_APERF) > 0) | |||||
tsc_perf_stat = 1; | |||||
} | } | ||||
break; | |||||
case CPU_VENDOR_NSC: | |||||
switch (cpu_id & 0xff0) { | |||||
case 0x540: | |||||
if ((cpu_id & CPUID_STEPPING) == 0) { | |||||
tsc_disabled = 1; | |||||
return; | |||||
} | |||||
break; | |||||
} | |||||
break; | |||||
} | |||||
#endif | |||||
switch (cpu_vendor_id) { | switch (cpu_vendor_id) { | ||||
case CPU_VENDOR_AMD: | case CPU_VENDOR_AMD: | ||||
case CPU_VENDOR_HYGON: | case CPU_VENDOR_HYGON: | ||||
if ((amd_pminfo & AMDPM_TSC_INVARIANT) != 0 || | if ((amd_pminfo & AMDPM_TSC_INVARIANT) != 0 || | ||||
(vm_guest == VM_GUEST_NO && | (vm_guest == VM_GUEST_NO && | ||||
CPUID_TO_FAMILY(cpu_id) >= 0x10)) | CPUID_TO_FAMILY(cpu_id) >= 0x10)) | ||||
tsc_is_invariant = 1; | tsc_is_invariant = 1; | ||||
Show All 23 Lines | if (vm_guest == VM_GUEST_NO && | ||||
tsc_is_invariant = 1; | tsc_is_invariant = 1; | ||||
if (cpu_feature & CPUID_SSE2) { | if (cpu_feature & CPUID_SSE2) { | ||||
tsc_timecounter.tc_get_timecount = | tsc_timecounter.tc_get_timecount = | ||||
tsc_get_timecount_lfence; | tsc_get_timecount_lfence; | ||||
} | } | ||||
break; | break; | ||||
} | } | ||||
if (tsc_freq_cpuid_vm()) | if (tsc_freq_cpuid_vm()) { | ||||
return; | if (bootverbose) | ||||
printf( | |||||
if (vm_guest == VM_GUEST_VMWARE) { | "Early TSC frequency %juHz derived from hypervisor CPUID\n", | ||||
(uintmax_t)tsc_freq); | |||||
} else if (vm_guest == VM_GUEST_VMWARE) { | |||||
tsc_freq_vmware(); | tsc_freq_vmware(); | ||||
return; | if (bootverbose) | ||||
} | printf( | ||||
"Early TSC frequency %juHz derived from VMWare hypercall\n", | |||||
if (tsc_freq_cpuid(&tsc_freq)) { | (uintmax_t)tsc_freq); | ||||
} else if (tsc_freq_cpuid(&tsc_freq)) { | |||||
/* | /* | ||||
* If possible, use the value obtained from CPUID as the initial | * If possible, use the value obtained from CPUID as the initial | ||||
* frequency. This will be refined later during boot but is | * frequency. This will be refined later during boot but is | ||||
* good enough for now. The 8254 PIT is not functional on some | * good enough for now. The 8254 PIT is not functional on some | ||||
* newer platforms anyway, so don't delay our boot for what | * newer platforms anyway, so don't delay our boot for what | ||||
* might be a garbage result. Late calibration is required if | * might be a garbage result. Late calibration is required if | ||||
* the initial frequency was obtained from CPUID.16H, as the | * the initial frequency was obtained from CPUID.16H, as the | ||||
* derived value may be off by as much as 1%. | * derived value may be off by as much as 1%. | ||||
Show All 21 Lines | if (tsc_freq_cpuid_vm()) { | ||||
* refined later in tsc_calib(). | * refined later in tsc_calib(). | ||||
*/ | */ | ||||
tsc_freq_8254(&tsc_freq); | tsc_freq_8254(&tsc_freq); | ||||
if (bootverbose) | if (bootverbose) | ||||
printf( | printf( | ||||
"Early TSC frequency %juHz calibrated from 8254 PIT\n", | "Early TSC frequency %juHz calibrated from 8254 PIT\n", | ||||
(uintmax_t)tsc_freq); | (uintmax_t)tsc_freq); | ||||
} | } | ||||
if (cpu_power_ecx & CPUID_PERF_STAT) { | |||||
/* | |||||
* XXX Some emulators expose host CPUID without actual support | |||||
* for these MSRs. We must test whether they really work. | |||||
*/ | |||||
wrmsr(MSR_MPERF, 0); | |||||
wrmsr(MSR_APERF, 0); | |||||
DELAY(10); | |||||
if (rdmsr(MSR_MPERF) > 0 && rdmsr(MSR_APERF) > 0) | |||||
tsc_perf_stat = 1; | |||||
} | } | ||||
} | |||||
void | void | ||||
init_TSC(void) | start_TSC(void) | ||||
{ | { | ||||
if ((cpu_feature & CPUID_TSC) == 0 || tsc_disabled) | if ((cpu_feature & CPUID_TSC) == 0 || tsc_disabled) | ||||
return; | return; | ||||
#ifdef __i386__ | |||||
/* The TSC is known to be broken on certain CPUs. */ | |||||
switch (cpu_vendor_id) { | |||||
case CPU_VENDOR_AMD: | |||||
switch (cpu_id & 0xFF0) { | |||||
case 0x500: | |||||
/* K5 Model 0 */ | |||||
return; | |||||
} | |||||
break; | |||||
case CPU_VENDOR_CENTAUR: | |||||
switch (cpu_id & 0xff0) { | |||||
case 0x540: | |||||
/* | /* | ||||
* http://www.centtech.com/c6_data_sheet.pdf | |||||
* | |||||
* I-12 RDTSC may return incoherent values in EDX:EAX | |||||
* I-13 RDTSC hangs when certain event counters are used | |||||
*/ | |||||
return; | |||||
} | |||||
break; | |||||
case CPU_VENDOR_NSC: | |||||
switch (cpu_id & 0xff0) { | |||||
case 0x540: | |||||
if ((cpu_id & CPUID_STEPPING) == 0) | |||||
return; | |||||
break; | |||||
} | |||||
break; | |||||
} | |||||
#endif | |||||
probe_tsc_freq(); | |||||
/* | |||||
* Inform CPU accounting about our boot-time clock rate. This will | * Inform CPU accounting about our boot-time clock rate. This will | ||||
* be updated if someone loads a cpufreq driver after boot that | * be updated if someone loads a cpufreq driver after boot that | ||||
* discovers a new max frequency. | * discovers a new max frequency. | ||||
* | * | ||||
* The frequency may also be updated after late calibration is complete; | * The frequency may also be updated after late calibration is complete; | ||||
* however, we register the TSC as the ticker now to avoid switching | * however, we register the TSC as the ticker now to avoid switching | ||||
* counters after much of the kernel has already booted and potentially | * counters after much of the kernel has already booted and potentially | ||||
* sampled the CPU clock. | * sampled the CPU clock. | ||||
▲ Show 20 Lines • Show All 284 Lines • ▼ Show 20 Lines | |||||
SYSINIT(tsc_tc, SI_SUB_SMP, SI_ORDER_ANY, init_TSC_tc, NULL); | SYSINIT(tsc_tc, SI_SUB_SMP, SI_ORDER_ANY, init_TSC_tc, NULL); | ||||
static void | static void | ||||
tsc_update_freq(uint64_t new_freq) | tsc_update_freq(uint64_t new_freq) | ||||
{ | { | ||||
atomic_store_rel_64(&tsc_freq, new_freq); | atomic_store_rel_64(&tsc_freq, new_freq); | ||||
atomic_store_rel_64(&tsc_timecounter.tc_frequency, | atomic_store_rel_64(&tsc_timecounter.tc_frequency, | ||||
new_freq >> (int)(intptr_t)tsc_timecounter.tc_priv); | new_freq >> (int)(intptr_t)tsc_timecounter.tc_priv); | ||||
} | |||||
void | |||||
tsc_init(void) | |||||
{ | |||||
if ((cpu_feature & CPUID_TSC) == 0 || tsc_disabled) | |||||
return; | |||||
probe_tsc_freq(); | |||||
} | } | ||||
/* | /* | ||||
* Perform late calibration of the TSC frequency once ACPI-based timecounters | * Perform late calibration of the TSC frequency once ACPI-based timecounters | ||||
* are available. At this point timehands are not set up, so we read the | * are available. At this point timehands are not set up, so we read the | ||||
* highest-quality timecounter directly rather than using (s)binuptime(). | * highest-quality timecounter directly rather than using (s)binuptime(). | ||||
*/ | */ | ||||
void | void | ||||
▲ Show 20 Lines • Show All 226 Lines • Show Last 20 Lines |