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 | |