xref: /xnu-8792.61.2/osfmk/arm/rtclock.c (revision 42e220869062b56f8d7d0726fd4c88954f87902c)
1 /*
2  * Copyright (c) 2007-2021 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  * @APPLE_FREE_COPYRIGHT@
33  */
34 /*
35  * File: arm/rtclock.c
36  * Purpose: Routines for handling the machine dependent
37  *   real-time clock.
38  */
39 
40 #include <mach/mach_types.h>
41 
42 #include <kern/clock.h>
43 #include <kern/thread.h>
44 #include <kern/macro_help.h>
45 #include <kern/spl.h>
46 #include <kern/timer_queue.h>
47 
48 #include <kern/host_notify.h>
49 
50 #include <machine/commpage.h>
51 #include <machine/machine_routines.h>
52 #include <machine/config.h>
53 #include <arm/exception.h>
54 #include <arm/cpu_data_internal.h>
55 #if __arm64__
56 #include <arm64/proc_reg.h>
57 #else
58 #error Unsupported arch
59 #endif
60 #include <arm/rtclock.h>
61 
62 #include <IOKit/IOPlatformExpert.h>
63 #include <libkern/OSAtomic.h>
64 
65 #include <sys/kdebug.h>
66 
67 #define MAX_TIMEBASE_TRIES 10
68 
69 int rtclock_init(void);
70 
71 static int
72 deadline_to_decrementer(uint64_t deadline,
73     uint64_t now);
74 static void
75 timebase_callback(struct timebase_freq_t * freq);
76 
77 #if DEVELOPMENT || DEBUG
78 uint32_t timebase_validation = 0;
79 #endif
80 
81 /*
82  * Configure the real-time clock device at boot
83  */
84 void
rtclock_early_init(void)85 rtclock_early_init(void)
86 {
87 	PE_register_timebase_callback(timebase_callback);
88 #if DEVELOPMENT || DEBUG
89 	uint32_t tmp_mv = 1;
90 
91 #if defined(APPLE_ARM64_ARCH_FAMILY)
92 	/* Enable MAT validation on A0 hardware by default. */
93 	timebase_validation = ml_get_topology_info()->chip_revision == CPU_VERSION_A0;
94 #endif
95 
96 	if (kern_feature_override(KF_MATV_OVRD)) {
97 		timebase_validation = 0;
98 	}
99 	if (PE_parse_boot_argn("timebase_validation", &tmp_mv, sizeof(tmp_mv))) {
100 		timebase_validation = tmp_mv;
101 	}
102 #endif
103 }
104 
105 static void
timebase_callback(struct timebase_freq_t * freq)106 timebase_callback(struct timebase_freq_t * freq)
107 {
108 	unsigned long numer, denom;
109 	uint64_t      t64_1, t64_2;
110 	uint32_t      divisor;
111 
112 	if (freq->timebase_den < 1 || freq->timebase_den > 4 ||
113 	    freq->timebase_num < freq->timebase_den) {
114 		panic("rtclock timebase_callback: invalid constant %ld / %ld",
115 		    freq->timebase_num, freq->timebase_den);
116 	}
117 
118 	denom = freq->timebase_num;
119 	numer = freq->timebase_den * NSEC_PER_SEC;
120 	// reduce by the greatest common denominator to minimize overflow
121 	if (numer > denom) {
122 		t64_1 = numer;
123 		t64_2 = denom;
124 	} else {
125 		t64_1 = denom;
126 		t64_2 = numer;
127 	}
128 	while (t64_2 != 0) {
129 		uint64_t temp = t64_2;
130 		t64_2 = t64_1 % t64_2;
131 		t64_1 = temp;
132 	}
133 	numer /= t64_1;
134 	denom /= t64_1;
135 
136 	rtclock_timebase_const.numer = (uint32_t)numer;
137 	rtclock_timebase_const.denom = (uint32_t)denom;
138 	divisor = (uint32_t)(freq->timebase_num / freq->timebase_den);
139 
140 	rtclock_sec_divisor = divisor;
141 	rtclock_usec_divisor = divisor / USEC_PER_SEC;
142 }
143 
144 /*
145  * Initialize the system clock device for the current cpu
146  */
147 int
rtclock_init(void)148 rtclock_init(void)
149 {
150 	uint64_t     abstime;
151 	cpu_data_t * cdp;
152 
153 	clock_timebase_init();
154 	ml_init_lock_timeout();
155 
156 	cdp = getCpuDatap();
157 
158 	abstime = mach_absolute_time();
159 	cdp->rtcPop = EndOfAllTime;                                     /* Init Pop time */
160 	timer_resync_deadlines();                                       /* Start the timers going */
161 
162 	return 1;
163 }
164 
165 
166 uint64_t
mach_absolute_time(void)167 mach_absolute_time(void)
168 {
169 #if DEVELOPMENT || DEBUG
170 	if (__improbable(timebase_validation)) {
171 #if __ARM_ARCH_8_6__ || HAS_ACNTVCT
172 		static _Atomic uint64_t s_last_absolute_time = 1;
173 
174 		uint64_t old_absolute_time = os_atomic_load(&s_last_absolute_time, relaxed);
175 
176 		/*
177 		 * Because this timebase read is nonspeculative, it cannot begin reading
178 		 * the timebase value until after the load of the old value completes.
179 		 */
180 
181 		if (old_absolute_time == 0) {
182 			timebase_validation = 0; // we know it's bad, now prevent nested panics
183 			panic("old_absolute_time was 0");
184 		}
185 
186 		uint64_t new_absolute_time = ml_get_timebase();
187 
188 		if (old_absolute_time > new_absolute_time) {
189 			timebase_validation = 0; // prevent nested panics
190 			panic("mach_absolute_time returning non-monotonically increasing value 0x%llx (old value 0x%llx)",
191 			    new_absolute_time, old_absolute_time);
192 		}
193 
194 		if (old_absolute_time < new_absolute_time) {
195 			/* read again, to pretest the atomic max */
196 			uint64_t pretest_absolute_time = os_atomic_load(&s_last_absolute_time, relaxed);
197 			if (pretest_absolute_time < new_absolute_time) {
198 				uint64_t fresh_last_absolute_time = os_atomic_max_orig(&s_last_absolute_time, new_absolute_time, relaxed);
199 
200 				if (fresh_last_absolute_time != pretest_absolute_time) {
201 					/*
202 					 * Someone else published a newer time after we loaded s_last_absolute_time.
203 					 * Enforce that our timebase is not behind this new one.
204 					 * We can't compare it with our previous timebase read, as it is too old.
205 					 */
206 
207 					uint64_t newest_absolute_time = ml_get_timebase();
208 
209 					if (fresh_last_absolute_time > newest_absolute_time) {
210 						timebase_validation = 0; // prevent nested panics
211 						panic("mach_absolute_time returning non-monotonically increasing value 0x%llx (old values 0x%llx, 0x%llx, 0x%llx, 0x%llx)\n",
212 						    newest_absolute_time, fresh_last_absolute_time, pretest_absolute_time, old_absolute_time, new_absolute_time);
213 					}
214 				}
215 			}
216 		}
217 
218 		return new_absolute_time;
219 
220 #else /* !(__ARM_ARCH_8_6__ || HAS_ACNTVCT) */
221 		static volatile uint64_t s_last_absolute_time = 0;
222 		uint64_t                 new_absolute_time, old_absolute_time;
223 		int                      attempts = 0;
224 
225 		/* ARM 64: We need a dsb here to ensure that the load of s_last_absolute_time
226 		 * completes before the timebase read. Were the load to complete after the
227 		 * timebase read, there would be a window for another CPU to update
228 		 * s_last_absolute_time and leave us in an inconsistent state. Consider the
229 		 * following interleaving:
230 		 *
231 		 *   Let s_last_absolute_time = t0
232 		 *   CPU0: Read timebase at t1
233 		 *   CPU1: Read timebase at t2
234 		 *   CPU1: Update s_last_absolute_time to t2
235 		 *   CPU0: Load completes
236 		 *   CPU0: Update s_last_absolute_time to t1
237 		 *
238 		 * This would cause the assertion to fail even though time did not go
239 		 * backwards. Thus, we use a dsb to guarantee completion of the load before
240 		 * the timebase read.
241 		 */
242 		do {
243 			attempts++;
244 			old_absolute_time = s_last_absolute_time;
245 
246 			__builtin_arm_dsb(DSB_ISHLD);
247 
248 			new_absolute_time = ml_get_timebase();
249 		} while (attempts < MAX_TIMEBASE_TRIES && !OSCompareAndSwap64(old_absolute_time, new_absolute_time, &s_last_absolute_time));
250 
251 		if (attempts < MAX_TIMEBASE_TRIES && old_absolute_time > new_absolute_time) {
252 			timebase_validation = 0; // we know it's bad, now prevent nested panics
253 			panic("mach_absolute_time returning non-monotonically increasing value 0x%llx (old value 0x%llx\n)",
254 			    new_absolute_time, old_absolute_time);
255 		}
256 		return new_absolute_time;
257 #endif /* __ARM_ARCH_8_6__ || HAS_ACNTVCT */
258 	}
259 #endif /* DEVELOPMENT || DEBUG */
260 
261 	return ml_get_timebase();
262 }
263 
264 uint64_t
mach_approximate_time(void)265 mach_approximate_time(void)
266 {
267 #if __ARM_TIME__ || __ARM_TIME_TIMEBASE_ONLY__ || __arm64__
268 	/* Hardware supports a fast timestamp, so grab it without asserting monotonicity */
269 	return ml_get_timebase();
270 #else
271 	processor_t processor;
272 	uint64_t    approx_time;
273 
274 	disable_preemption();
275 	processor = current_processor();
276 	approx_time = processor->last_dispatch;
277 	enable_preemption();
278 
279 	return approx_time;
280 #endif
281 }
282 
283 void
clock_get_system_microtime(clock_sec_t * secs,clock_usec_t * microsecs)284 clock_get_system_microtime(clock_sec_t *  secs,
285     clock_usec_t * microsecs)
286 {
287 	absolutetime_to_microtime(mach_absolute_time(), secs, microsecs);
288 }
289 
290 void
clock_get_system_nanotime(clock_sec_t * secs,clock_nsec_t * nanosecs)291 clock_get_system_nanotime(clock_sec_t *  secs,
292     clock_nsec_t * nanosecs)
293 {
294 	uint64_t abstime;
295 	uint64_t t64;
296 
297 	abstime = mach_absolute_time();
298 	*secs = (t64 = abstime / rtclock_sec_divisor);
299 	abstime -= (t64 * rtclock_sec_divisor);
300 
301 	*nanosecs = (clock_nsec_t)((abstime * NSEC_PER_SEC) / rtclock_sec_divisor);
302 }
303 
304 void
clock_gettimeofday_set_commpage(uint64_t abstime,uint64_t sec,uint64_t frac,uint64_t scale,uint64_t tick_per_sec)305 clock_gettimeofday_set_commpage(uint64_t abstime,
306     uint64_t sec,
307     uint64_t frac,
308     uint64_t scale,
309     uint64_t tick_per_sec)
310 {
311 	commpage_set_timestamp(abstime, sec, frac, scale, tick_per_sec);
312 }
313 
314 void
clock_timebase_info(mach_timebase_info_t info)315 clock_timebase_info(mach_timebase_info_t info)
316 {
317 	*info = rtclock_timebase_const;
318 }
319 
320 /*
321  * Real-time clock device interrupt.
322  */
323 void
rtclock_intr(__unused unsigned int is_user_context)324 rtclock_intr(__unused unsigned int is_user_context)
325 {
326 	uint64_t                 abstime;
327 	cpu_data_t *             cdp;
328 	struct arm_saved_state * regs;
329 	unsigned int             user_mode;
330 	uintptr_t                pc;
331 
332 	cdp = getCpuDatap();
333 
334 	cdp->cpu_stat.timer_cnt++;
335 	SCHED_STATS_INC(timer_pop_count);
336 
337 	assert(!ml_get_interrupts_enabled());
338 
339 	abstime = mach_absolute_time();
340 
341 	if (cdp->cpu_idle_pop != 0x0ULL) {
342 		if ((cdp->rtcPop - abstime) < cdp->cpu_idle_latency) {
343 			cdp->cpu_idle_pop = 0x0ULL;
344 			while (abstime < cdp->rtcPop) {
345 				abstime = mach_absolute_time();
346 			}
347 		} else {
348 			ClearIdlePop(FALSE);
349 		}
350 	}
351 
352 	if ((regs = cdp->cpu_int_state)) {
353 		pc = get_saved_state_pc(regs);
354 
355 #if __arm64__
356 		user_mode = PSR64_IS_USER(get_saved_state_cpsr(regs));
357 #endif
358 	} else {
359 		pc = 0;
360 		user_mode = 0;
361 	}
362 	if (abstime >= cdp->rtcPop) {
363 		/* Log the interrupt service latency (-ve value expected by tool) */
364 		KDBG_RELEASE(DECR_TRAP_LATENCY | DBG_FUNC_NONE,
365 		    -(abstime - cdp->rtcPop),
366 		    user_mode ? pc : VM_KERNEL_UNSLIDE(pc), user_mode);
367 	}
368 
369 	/* call the generic etimer */
370 	timer_intr(user_mode, pc);
371 }
372 
373 static int
deadline_to_decrementer(uint64_t deadline,uint64_t now)374 deadline_to_decrementer(uint64_t deadline,
375     uint64_t now)
376 {
377 	uint64_t delt;
378 
379 	if (deadline <= now) {
380 		return DECREMENTER_MIN;
381 	} else {
382 		delt = deadline - now;
383 
384 		return (delt >= (DECREMENTER_MAX + 1)) ? DECREMENTER_MAX : ((delt >= (DECREMENTER_MIN + 1)) ? (int)delt : DECREMENTER_MIN);
385 	}
386 }
387 
388 /*
389  *	Request a decrementer pop
390  */
391 int
setPop(uint64_t time)392 setPop(uint64_t time)
393 {
394 	int          delay_time;
395 	uint64_t     current_time;
396 	cpu_data_t * cdp;
397 
398 	cdp = getCpuDatap();
399 	current_time = mach_absolute_time();
400 
401 	delay_time = deadline_to_decrementer(time, current_time);
402 	cdp->rtcPop = delay_time + current_time;
403 
404 	ml_set_decrementer((uint32_t) delay_time);
405 
406 	return delay_time;
407 }
408 
409 /*
410  *	Request decrementer Idle Pop. Return true if set
411  */
412 boolean_t
SetIdlePop(void)413 SetIdlePop(void)
414 {
415 	int          delay_time;
416 	uint64_t     time;
417 	uint64_t     current_time;
418 	cpu_data_t * cdp;
419 
420 	cdp = getCpuDatap();
421 	current_time = mach_absolute_time();
422 
423 	if (((cdp->rtcPop < current_time) ||
424 	    (cdp->rtcPop - current_time) < cdp->cpu_idle_latency)) {
425 		return FALSE;
426 	}
427 
428 	time = cdp->rtcPop - cdp->cpu_idle_latency;
429 
430 	delay_time = deadline_to_decrementer(time, current_time);
431 	cdp->cpu_idle_pop = delay_time + current_time;
432 	ml_set_decrementer((uint32_t) delay_time);
433 
434 	return TRUE;
435 }
436 
437 /*
438  *	Clear decrementer Idle Pop
439  */
440 void
ClearIdlePop(boolean_t wfi)441 ClearIdlePop(
442 	boolean_t wfi)
443 {
444 	cpu_data_t * cdp;
445 
446 	cdp = getCpuDatap();
447 	cdp->cpu_idle_pop = 0x0ULL;
448 
449 	/*
450 	 * Don't update the HW timer if there's a pending
451 	 * interrupt (we can lose interrupt assertion);
452 	 * we want to take the interrupt right now and update
453 	 * the deadline from the handler).
454 	 *
455 	 * ARM64_TODO: consider this more carefully.
456 	 */
457 	if (!(wfi && ml_get_timer_pending())) {
458 		setPop(cdp->rtcPop);
459 	}
460 }
461 
462 void
absolutetime_to_microtime(uint64_t abstime,clock_sec_t * secs,clock_usec_t * microsecs)463 absolutetime_to_microtime(uint64_t       abstime,
464     clock_sec_t *  secs,
465     clock_usec_t * microsecs)
466 {
467 	uint64_t t64;
468 
469 	*secs = t64 = abstime / rtclock_sec_divisor;
470 	abstime -= (t64 * rtclock_sec_divisor);
471 
472 	*microsecs = (uint32_t)(abstime / rtclock_usec_divisor);
473 }
474 
475 void
absolutetime_to_nanoseconds(uint64_t abstime,uint64_t * result)476 absolutetime_to_nanoseconds(uint64_t   abstime,
477     uint64_t * result)
478 {
479 	uint64_t        t64;
480 
481 	*result = (t64 = abstime / rtclock_sec_divisor) * NSEC_PER_SEC;
482 	abstime -= (t64 * rtclock_sec_divisor);
483 	*result += (abstime * NSEC_PER_SEC) / rtclock_sec_divisor;
484 }
485 
486 void
nanoseconds_to_absolutetime(uint64_t nanosecs,uint64_t * result)487 nanoseconds_to_absolutetime(uint64_t   nanosecs,
488     uint64_t * result)
489 {
490 	uint64_t        t64;
491 
492 	*result = (t64 = nanosecs / NSEC_PER_SEC) * rtclock_sec_divisor;
493 	nanosecs -= (t64 * NSEC_PER_SEC);
494 	*result += (nanosecs * rtclock_sec_divisor) / NSEC_PER_SEC;
495 }
496 
497 void
nanotime_to_absolutetime(clock_sec_t secs,clock_nsec_t nanosecs,uint64_t * result)498 nanotime_to_absolutetime(clock_sec_t  secs,
499     clock_nsec_t nanosecs,
500     uint64_t *   result)
501 {
502 	*result = ((uint64_t) secs * rtclock_sec_divisor) +
503 	    ((uint64_t) nanosecs * rtclock_sec_divisor) / NSEC_PER_SEC;
504 }
505 
506 void
clock_interval_to_absolutetime_interval(uint32_t interval,uint32_t scale_factor,uint64_t * result)507 clock_interval_to_absolutetime_interval(uint32_t   interval,
508     uint32_t   scale_factor,
509     uint64_t * result)
510 {
511 	uint64_t nanosecs = (uint64_t) interval * scale_factor;
512 	uint64_t t64;
513 
514 	*result = (t64 = nanosecs / NSEC_PER_SEC) * rtclock_sec_divisor;
515 	nanosecs -= (t64 * NSEC_PER_SEC);
516 	*result += (nanosecs * rtclock_sec_divisor) / NSEC_PER_SEC;
517 }
518 
519 void
machine_delay_until(uint64_t interval,uint64_t deadline)520 machine_delay_until(uint64_t interval,
521     uint64_t deadline)
522 {
523 #pragma unused(interval)
524 	uint64_t now;
525 
526 	do {
527 		__builtin_arm_wfe();
528 		now = mach_absolute_time();
529 	} while (now < deadline);
530 }
531