2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_OSREFERENCE_HEADER_START@
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
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
36 #include <mach/mach_types.h>
38 #include <kern/lock.h>
40 #include <kern/sched_prim.h>
41 #include <kern/thread.h>
42 #include <kern/clock.h>
43 #include <kern/host_notify.h>
45 #include <IOKit/IOPlatformExpert.h>
47 #include <machine/commpage.h>
49 #include <mach/mach_traps.h>
50 #include <mach/mach_time.h>
52 decl_simple_lock_data(static,clock_lock
)
55 * Time of day (calendar) variables.
59 * TOD <- (seconds + epoch, fraction) <- CONV(current absolute time + offset)
61 * where CONV converts absolute time units into seconds and a fraction.
63 static struct clock_calend
{
69 * Calendar adjustment variables and values.
71 #define calend_adjperiod (NSEC_PER_SEC / 100) /* adjustment period, ns */
72 #define calend_adjskew (40 * NSEC_PER_USEC) /* "standard" skew, ns / period */
73 #define calend_adjbig (NSEC_PER_SEC) /* use 10x skew above adjbig ns */
75 static uint64_t calend_adjstart
; /* Absolute time value for start of this adjustment period */
76 static uint32_t calend_adjoffset
; /* Absolute time offset for this adjustment period as absolute value */
78 static int32_t calend_adjdelta
; /* Nanosecond time delta for this adjustment period */
79 static int64_t calend_adjtotal
; /* Nanosecond remaining total adjustment */
81 static uint64_t calend_adjdeadline
; /* Absolute time value for next adjustment period */
82 static uint32_t calend_adjinterval
; /* Absolute time interval of adjustment period */
84 static timer_call_data_t calend_adjcall
;
85 static uint32_t calend_adjactive
;
87 static uint32_t calend_set_adjustment(
91 static void calend_adjust_call(void);
92 static uint32_t calend_adjust(void);
94 static thread_call_data_t calend_wakecall
;
96 extern void IOKitResetTime(void);
98 static uint64_t clock_boottime
; /* Seconds boottime epoch */
100 #define TIME_ADD(rsecs, secs, rfrac, frac, unit) \
102 if (((rfrac) += (frac)) >= (unit)) { \
109 #define TIME_SUB(rsecs, secs, rfrac, frac, unit) \
111 if ((int32_t)((rfrac) -= (frac)) < 0) { \
121 * Called once at boot to configure the clock subsystem.
126 simple_lock_init(&clock_lock
, 0);
128 timer_call_setup(&calend_adjcall
, (timer_call_func_t
)calend_adjust_call
, NULL
);
129 thread_call_setup(&calend_wakecall
, (thread_call_func_t
)IOKitResetTime
, NULL
);
134 * Initialize the timer callouts.
136 timer_call_initialize();
142 * Called on a processor each time started.
151 * clock_timebase_init:
153 * Called by machine dependent code
154 * to initialize areas dependent on the
155 * timebase value. May be called multiple
156 * times during start up.
159 clock_timebase_init(void)
163 nanoseconds_to_absolutetime(calend_adjperiod
, &abstime
);
164 calend_adjinterval
= abstime
;
166 sched_timebase_init();
170 * mach_timebase_info_trap:
172 * User trap returns timebase constant.
175 mach_timebase_info_trap(
176 struct mach_timebase_info_trap_args
*args
)
178 mach_vm_address_t out_info_addr
= args
->info
;
179 mach_timebase_info_data_t info
;
181 clock_timebase_info(&info
);
183 copyout((void *)&info
, out_info_addr
, sizeof (info
));
185 return (KERN_SUCCESS
);
193 * clock_get_calendar_microtime:
195 * Returns the current calendar value,
196 * microseconds as the fraction.
199 clock_get_calendar_microtime(
207 simple_lock(&clock_lock
);
209 now
= mach_absolute_time();
211 if (calend_adjdelta
< 0) {
214 if (now
> calend_adjstart
) {
215 t32
= now
- calend_adjstart
;
217 if (t32
> calend_adjoffset
)
218 now
-= calend_adjoffset
;
220 now
= calend_adjstart
;
224 now
+= clock_calend
.offset
;
226 absolutetime_to_microtime(now
, secs
, microsecs
);
228 *secs
+= clock_calend
.epoch
;
230 simple_unlock(&clock_lock
);
235 * clock_get_calendar_nanotime:
237 * Returns the current calendar value,
238 * nanoseconds as the fraction.
240 * Since we do not have an interface to
241 * set the calendar with resolution greater
242 * than a microsecond, we honor that here.
245 clock_get_calendar_nanotime(
253 simple_lock(&clock_lock
);
255 now
= mach_absolute_time();
257 if (calend_adjdelta
< 0) {
260 if (now
> calend_adjstart
) {
261 t32
= now
- calend_adjstart
;
263 if (t32
> calend_adjoffset
)
264 now
-= calend_adjoffset
;
266 now
= calend_adjstart
;
270 now
+= clock_calend
.offset
;
272 absolutetime_to_microtime(now
, secs
, nanosecs
);
273 *nanosecs
*= NSEC_PER_USEC
;
275 *secs
+= clock_calend
.epoch
;
277 simple_unlock(&clock_lock
);
282 * clock_gettimeofday:
284 * Kernel interface for commpage implementation of
285 * gettimeofday() syscall.
287 * Returns the current calendar value, and updates the
288 * commpage info as appropriate. Because most calls to
289 * gettimeofday() are handled in user mode by the commpage,
290 * this routine should be used infrequently.
301 simple_lock(&clock_lock
);
303 now
= mach_absolute_time();
305 if (calend_adjdelta
>= 0) {
306 clock_gettimeofday_set_commpage(now
, clock_calend
.epoch
, clock_calend
.offset
, secs
, microsecs
);
311 if (now
> calend_adjstart
) {
312 t32
= now
- calend_adjstart
;
314 if (t32
> calend_adjoffset
)
315 now
-= calend_adjoffset
;
317 now
= calend_adjstart
;
320 now
+= clock_calend
.offset
;
322 absolutetime_to_microtime(now
, secs
, microsecs
);
324 *secs
+= clock_calend
.epoch
;
327 simple_unlock(&clock_lock
);
332 * clock_set_calendar_microtime:
334 * Sets the current calendar value by
335 * recalculating the epoch and offset
336 * from the system clock.
338 * Also adjusts the boottime to keep the
339 * value consistent, writes the new
340 * calendar value to the platform clock,
341 * and sends calendar change notifications.
344 clock_set_calendar_microtime(
348 uint32_t sys
, microsys
;
352 newsecs
= (microsecs
< 500*USEC_PER_SEC
)?
356 simple_lock(&clock_lock
);
358 commpage_set_timestamp(0,0,0);
361 * Calculate the new calendar epoch based on
362 * the new value and the system clock.
364 clock_get_system_microtime(&sys
, µsys
);
365 TIME_SUB(secs
, sys
, microsecs
, microsys
, USEC_PER_SEC
);
368 * Adjust the boottime based on the delta.
370 clock_boottime
+= secs
- clock_calend
.epoch
;
373 * Set the new calendar epoch.
375 clock_calend
.epoch
= secs
;
376 nanoseconds_to_absolutetime((uint64_t)microsecs
* NSEC_PER_USEC
, &clock_calend
.offset
);
379 * Cancel any adjustment in progress.
381 calend_adjdelta
= calend_adjtotal
= 0;
383 simple_unlock(&clock_lock
);
386 * Set the new value for the platform clock.
388 PESetGMTTimeOfDay(newsecs
);
393 * Send host notifications.
395 host_notify_calendar_change();
399 * clock_initialize_calendar:
401 * Set the calendar and related clocks
402 * from the platform clock at boot or
405 * Also sends host notifications.
408 clock_initialize_calendar(void)
410 uint32_t sys
, microsys
;
411 uint32_t microsecs
= 0, secs
= PEGetGMTTimeOfDay();
415 simple_lock(&clock_lock
);
417 commpage_set_timestamp(0,0,0);
419 if ((int32_t)secs
>= (int32_t)clock_boottime
) {
421 * Initialize the boot time based on the platform clock.
423 if (clock_boottime
== 0)
424 clock_boottime
= secs
;
427 * Calculate the new calendar epoch based on
428 * the platform clock and the system clock.
430 clock_get_system_microtime(&sys
, µsys
);
431 TIME_SUB(secs
, sys
, microsecs
, microsys
, USEC_PER_SEC
);
434 * Set the new calendar epoch.
436 clock_calend
.epoch
= secs
;
437 nanoseconds_to_absolutetime((uint64_t)microsecs
* NSEC_PER_USEC
, &clock_calend
.offset
);
440 * Cancel any adjustment in progress.
442 calend_adjdelta
= calend_adjtotal
= 0;
445 simple_unlock(&clock_lock
);
449 * Send host notifications.
451 host_notify_calendar_change();
455 * clock_get_boottime_nanotime:
457 * Return the boottime, used by sysctl.
460 clock_get_boottime_nanotime(
464 *secs
= clock_boottime
;
471 * Interface to adjtime() syscall.
473 * Calculates adjustment variables and
474 * initiates adjustment.
485 simple_lock(&clock_lock
);
487 interval
= calend_set_adjustment(secs
, microsecs
);
489 calend_adjdeadline
= mach_absolute_time() + interval
;
490 if (!timer_call_enter(&calend_adjcall
, calend_adjdeadline
))
494 if (timer_call_cancel(&calend_adjcall
))
497 simple_unlock(&clock_lock
);
502 calend_set_adjustment(
507 int64_t total
, ototal
;
508 uint32_t interval
= 0;
510 total
= (int64_t)*secs
* NSEC_PER_SEC
+ *microsecs
* NSEC_PER_USEC
;
512 commpage_set_timestamp(0,0,0);
514 now
= mach_absolute_time();
516 ototal
= calend_adjtotal
;
519 int32_t delta
= calend_adjskew
;
522 if (total
> calend_adjbig
)
527 nanoseconds_to_absolutetime((uint64_t)delta
, &t64
);
528 calend_adjoffset
= t64
;
531 if (total
< -calend_adjbig
)
537 calend_adjstart
= now
;
539 nanoseconds_to_absolutetime((uint64_t)-delta
, &t64
);
540 calend_adjoffset
= t64
;
543 calend_adjtotal
= total
;
544 calend_adjdelta
= delta
;
546 interval
= calend_adjinterval
;
549 calend_adjdelta
= calend_adjtotal
= 0;
552 *secs
= ototal
/ NSEC_PER_SEC
;
553 *microsecs
= (ototal
% NSEC_PER_SEC
) / NSEC_PER_USEC
;
556 *secs
= *microsecs
= 0;
562 calend_adjust_call(void)
568 simple_lock(&clock_lock
);
570 if (--calend_adjactive
== 0) {
571 interval
= calend_adjust();
573 clock_deadline_for_periodic_event(interval
, mach_absolute_time(),
574 &calend_adjdeadline
);
576 if (!timer_call_enter(&calend_adjcall
, calend_adjdeadline
))
581 simple_unlock(&clock_lock
);
590 uint32_t interval
= 0;
592 commpage_set_timestamp(0,0,0);
594 now
= mach_absolute_time();
596 delta
= calend_adjdelta
;
599 clock_calend
.offset
+= calend_adjoffset
;
601 calend_adjtotal
-= delta
;
602 if (delta
> calend_adjtotal
) {
603 calend_adjdelta
= delta
= calend_adjtotal
;
605 nanoseconds_to_absolutetime((uint64_t)delta
, &t64
);
606 calend_adjoffset
= t64
;
611 clock_calend
.offset
-= calend_adjoffset
;
613 calend_adjtotal
-= delta
;
614 if (delta
< calend_adjtotal
) {
615 calend_adjdelta
= delta
= calend_adjtotal
;
617 nanoseconds_to_absolutetime((uint64_t)-delta
, &t64
);
618 calend_adjoffset
= t64
;
621 if (calend_adjdelta
!= 0)
622 calend_adjstart
= now
;
625 if (calend_adjdelta
!= 0)
626 interval
= calend_adjinterval
;
632 * clock_wakeup_calendar:
634 * Interface to power management, used
635 * to initiate the reset of the calendar
636 * on wake from sleep event.
639 clock_wakeup_calendar(void)
641 thread_call_enter(&calend_wakecall
);
645 * Wait / delay routines.
648 mach_wait_until_continue(
649 __unused
void *parameter
,
650 wait_result_t wresult
)
652 thread_syscall_return((wresult
== THREAD_INTERRUPTED
)? KERN_ABORTED
: KERN_SUCCESS
);
657 mach_wait_until_trap(
658 struct mach_wait_until_trap_args
*args
)
660 uint64_t deadline
= args
->deadline
;
661 wait_result_t wresult
;
663 wresult
= assert_wait_deadline((event_t
)mach_wait_until_trap
, THREAD_ABORTSAFE
, deadline
);
664 if (wresult
== THREAD_WAITING
)
665 wresult
= thread_block(mach_wait_until_continue
);
667 return ((wresult
== THREAD_INTERRUPTED
)? KERN_ABORTED
: KERN_SUCCESS
);
674 uint64_t now
= mach_absolute_time();
679 if ( (deadline
- now
) < (8 * sched_cswtime
) ||
680 get_preemption_level() != 0 ||
681 ml_get_interrupts_enabled() == FALSE
)
682 machine_delay_until(deadline
);
684 assert_wait_deadline((event_t
)clock_delay_until
, THREAD_UNINT
, deadline
- sched_cswtime
);
686 thread_block(THREAD_CONTINUE_NULL
);
693 uint32_t scale_factor
)
697 clock_interval_to_deadline(interval
, scale_factor
, &end
);
699 clock_delay_until(end
);
706 delay_for_interval((usec
< 0)? -usec
: usec
, NSEC_PER_USEC
);
710 * Miscellaneous routines.
713 clock_interval_to_deadline(
715 uint32_t scale_factor
,
720 clock_interval_to_absolutetime_interval(interval
, scale_factor
, &abstime
);
722 *result
= mach_absolute_time() + abstime
;
726 clock_absolutetime_interval_to_deadline(
730 *result
= mach_absolute_time() + abstime
;
737 *result
= mach_absolute_time();
741 clock_deadline_for_periodic_event(
746 assert(interval
!= 0);
748 *deadline
+= interval
;
750 if (*deadline
<= abstime
) {
751 *deadline
= abstime
+ interval
;
752 abstime
= mach_absolute_time();
754 if (*deadline
<= abstime
)
755 *deadline
= abstime
+ interval
;