2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_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 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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
34 #include <mach/mach_types.h>
36 #include <kern/lock.h>
38 #include <kern/sched_prim.h>
39 #include <kern/thread.h>
40 #include <kern/clock.h>
41 #include <kern/host_notify.h>
43 #include <IOKit/IOPlatformExpert.h>
45 #include <machine/commpage.h>
47 #include <mach/mach_traps.h>
48 #include <mach/mach_time.h>
50 decl_simple_lock_data(static,clock_lock
)
53 * Time of day (calendar) variables.
57 * TOD <- (seconds + epoch, fraction) <- CONV(current absolute time + offset)
59 * where CONV converts absolute time units into seconds and a fraction.
61 static struct clock_calend
{
67 * Calendar adjustment variables and values.
69 #define calend_adjperiod (NSEC_PER_SEC / 100) /* adjustment period, ns */
70 #define calend_adjskew (40 * NSEC_PER_USEC) /* "standard" skew, ns / period */
71 #define calend_adjbig (NSEC_PER_SEC) /* use 10x skew above adjbig ns */
73 static uint64_t calend_adjstart
; /* Absolute time value for start of this adjustment period */
74 static uint32_t calend_adjoffset
; /* Absolute time offset for this adjustment period as absolute value */
76 static int32_t calend_adjdelta
; /* Nanosecond time delta for this adjustment period */
77 static int64_t calend_adjtotal
; /* Nanosecond remaining total adjustment */
79 static uint64_t calend_adjdeadline
; /* Absolute time value for next adjustment period */
80 static uint32_t calend_adjinterval
; /* Absolute time interval of adjustment period */
82 static timer_call_data_t calend_adjcall
;
83 static uint32_t calend_adjactive
;
85 static uint32_t calend_set_adjustment(
89 static void calend_adjust_call(void);
90 static uint32_t calend_adjust(void);
92 static thread_call_data_t calend_wakecall
;
94 extern void IOKitResetTime(void);
96 static uint64_t clock_boottime
; /* Seconds boottime epoch */
98 #define TIME_ADD(rsecs, secs, rfrac, frac, unit) \
100 if (((rfrac) += (frac)) >= (unit)) { \
107 #define TIME_SUB(rsecs, secs, rfrac, frac, unit) \
109 if ((int32_t)((rfrac) -= (frac)) < 0) { \
119 * Called once at boot to configure the clock subsystem.
124 simple_lock_init(&clock_lock
, 0);
126 timer_call_setup(&calend_adjcall
, (timer_call_func_t
)calend_adjust_call
, NULL
);
127 thread_call_setup(&calend_wakecall
, (thread_call_func_t
)IOKitResetTime
, NULL
);
132 * Initialize the timer callouts.
134 timer_call_initialize();
140 * Called on a processor each time started.
149 * clock_timebase_init:
151 * Called by machine dependent code
152 * to initialize areas dependent on the
153 * timebase value. May be called multiple
154 * times during start up.
157 clock_timebase_init(void)
161 nanoseconds_to_absolutetime(calend_adjperiod
, &abstime
);
162 calend_adjinterval
= abstime
;
164 sched_timebase_init();
168 * mach_timebase_info_trap:
170 * User trap returns timebase constant.
173 mach_timebase_info_trap(
174 struct mach_timebase_info_trap_args
*args
)
176 mach_vm_address_t out_info_addr
= args
->info
;
177 mach_timebase_info_data_t info
;
179 clock_timebase_info(&info
);
181 copyout((void *)&info
, out_info_addr
, sizeof (info
));
183 return (KERN_SUCCESS
);
191 * clock_get_calendar_microtime:
193 * Returns the current calendar value,
194 * microseconds as the fraction.
197 clock_get_calendar_microtime(
205 simple_lock(&clock_lock
);
207 now
= mach_absolute_time();
209 if (calend_adjdelta
< 0) {
212 if (now
> calend_adjstart
) {
213 t32
= now
- calend_adjstart
;
215 if (t32
> calend_adjoffset
)
216 now
-= calend_adjoffset
;
218 now
= calend_adjstart
;
222 now
+= clock_calend
.offset
;
224 absolutetime_to_microtime(now
, secs
, microsecs
);
226 *secs
+= clock_calend
.epoch
;
228 simple_unlock(&clock_lock
);
233 * clock_get_calendar_nanotime:
235 * Returns the current calendar value,
236 * nanoseconds as the fraction.
238 * Since we do not have an interface to
239 * set the calendar with resolution greater
240 * than a microsecond, we honor that here.
243 clock_get_calendar_nanotime(
251 simple_lock(&clock_lock
);
253 now
= mach_absolute_time();
255 if (calend_adjdelta
< 0) {
258 if (now
> calend_adjstart
) {
259 t32
= now
- calend_adjstart
;
261 if (t32
> calend_adjoffset
)
262 now
-= calend_adjoffset
;
264 now
= calend_adjstart
;
268 now
+= clock_calend
.offset
;
270 absolutetime_to_microtime(now
, secs
, nanosecs
);
271 *nanosecs
*= NSEC_PER_USEC
;
273 *secs
+= clock_calend
.epoch
;
275 simple_unlock(&clock_lock
);
280 * clock_gettimeofday:
282 * Kernel interface for commpage implementation of
283 * gettimeofday() syscall.
285 * Returns the current calendar value, and updates the
286 * commpage info as appropriate. Because most calls to
287 * gettimeofday() are handled in user mode by the commpage,
288 * this routine should be used infrequently.
299 simple_lock(&clock_lock
);
301 now
= mach_absolute_time();
303 if (calend_adjdelta
>= 0) {
304 clock_gettimeofday_set_commpage(now
, clock_calend
.epoch
, clock_calend
.offset
, secs
, microsecs
);
309 if (now
> calend_adjstart
) {
310 t32
= now
- calend_adjstart
;
312 if (t32
> calend_adjoffset
)
313 now
-= calend_adjoffset
;
315 now
= calend_adjstart
;
318 now
+= clock_calend
.offset
;
320 absolutetime_to_microtime(now
, secs
, microsecs
);
322 *secs
+= clock_calend
.epoch
;
325 simple_unlock(&clock_lock
);
330 * clock_set_calendar_microtime:
332 * Sets the current calendar value by
333 * recalculating the epoch and offset
334 * from the system clock.
336 * Also adjusts the boottime to keep the
337 * value consistent, writes the new
338 * calendar value to the platform clock,
339 * and sends calendar change notifications.
342 clock_set_calendar_microtime(
346 uint32_t sys
, microsys
;
350 newsecs
= (microsecs
< 500*USEC_PER_SEC
)?
354 simple_lock(&clock_lock
);
356 commpage_set_timestamp(0,0,0);
359 * Calculate the new calendar epoch based on
360 * the new value and the system clock.
362 clock_get_system_microtime(&sys
, µsys
);
363 TIME_SUB(secs
, sys
, microsecs
, microsys
, USEC_PER_SEC
);
366 * Adjust the boottime based on the delta.
368 clock_boottime
+= secs
- clock_calend
.epoch
;
371 * Set the new calendar epoch.
373 clock_calend
.epoch
= secs
;
374 nanoseconds_to_absolutetime((uint64_t)microsecs
* NSEC_PER_USEC
, &clock_calend
.offset
);
377 * Cancel any adjustment in progress.
379 calend_adjdelta
= calend_adjtotal
= 0;
381 simple_unlock(&clock_lock
);
384 * Set the new value for the platform clock.
386 PESetGMTTimeOfDay(newsecs
);
391 * Send host notifications.
393 host_notify_calendar_change();
397 * clock_initialize_calendar:
399 * Set the calendar and related clocks
400 * from the platform clock at boot or
403 * Also sends host notifications.
406 clock_initialize_calendar(void)
408 uint32_t sys
, microsys
;
409 uint32_t microsecs
= 0, secs
= PEGetGMTTimeOfDay();
413 simple_lock(&clock_lock
);
415 commpage_set_timestamp(0,0,0);
417 if ((int32_t)secs
>= (int32_t)clock_boottime
) {
419 * Initialize the boot time based on the platform clock.
421 if (clock_boottime
== 0)
422 clock_boottime
= secs
;
425 * Calculate the new calendar epoch based on
426 * the platform clock and the system clock.
428 clock_get_system_microtime(&sys
, µsys
);
429 TIME_SUB(secs
, sys
, microsecs
, microsys
, USEC_PER_SEC
);
432 * Set the new calendar epoch.
434 clock_calend
.epoch
= secs
;
435 nanoseconds_to_absolutetime((uint64_t)microsecs
* NSEC_PER_USEC
, &clock_calend
.offset
);
438 * Cancel any adjustment in progress.
440 calend_adjdelta
= calend_adjtotal
= 0;
443 simple_unlock(&clock_lock
);
447 * Send host notifications.
449 host_notify_calendar_change();
453 * clock_get_boottime_nanotime:
455 * Return the boottime, used by sysctl.
458 clock_get_boottime_nanotime(
462 *secs
= clock_boottime
;
469 * Interface to adjtime() syscall.
471 * Calculates adjustment variables and
472 * initiates adjustment.
483 simple_lock(&clock_lock
);
485 interval
= calend_set_adjustment(secs
, microsecs
);
487 calend_adjdeadline
= mach_absolute_time() + interval
;
488 if (!timer_call_enter(&calend_adjcall
, calend_adjdeadline
))
492 if (timer_call_cancel(&calend_adjcall
))
495 simple_unlock(&clock_lock
);
500 calend_set_adjustment(
505 int64_t total
, ototal
;
506 uint32_t interval
= 0;
508 total
= (int64_t)*secs
* NSEC_PER_SEC
+ *microsecs
* NSEC_PER_USEC
;
510 commpage_set_timestamp(0,0,0);
512 now
= mach_absolute_time();
514 ototal
= calend_adjtotal
;
517 int32_t delta
= calend_adjskew
;
520 if (total
> calend_adjbig
)
525 nanoseconds_to_absolutetime((uint64_t)delta
, &t64
);
526 calend_adjoffset
= t64
;
529 if (total
< -calend_adjbig
)
535 calend_adjstart
= now
;
537 nanoseconds_to_absolutetime((uint64_t)-delta
, &t64
);
538 calend_adjoffset
= t64
;
541 calend_adjtotal
= total
;
542 calend_adjdelta
= delta
;
544 interval
= calend_adjinterval
;
547 calend_adjdelta
= calend_adjtotal
= 0;
550 *secs
= ototal
/ NSEC_PER_SEC
;
551 *microsecs
= (ototal
% NSEC_PER_SEC
) / NSEC_PER_USEC
;
554 *secs
= *microsecs
= 0;
560 calend_adjust_call(void)
566 simple_lock(&clock_lock
);
568 if (--calend_adjactive
== 0) {
569 interval
= calend_adjust();
571 clock_deadline_for_periodic_event(interval
, mach_absolute_time(),
572 &calend_adjdeadline
);
574 if (!timer_call_enter(&calend_adjcall
, calend_adjdeadline
))
579 simple_unlock(&clock_lock
);
588 uint32_t interval
= 0;
590 commpage_set_timestamp(0,0,0);
592 now
= mach_absolute_time();
594 delta
= calend_adjdelta
;
597 clock_calend
.offset
+= calend_adjoffset
;
599 calend_adjtotal
-= delta
;
600 if (delta
> calend_adjtotal
) {
601 calend_adjdelta
= delta
= calend_adjtotal
;
603 nanoseconds_to_absolutetime((uint64_t)delta
, &t64
);
604 calend_adjoffset
= t64
;
609 clock_calend
.offset
-= calend_adjoffset
;
611 calend_adjtotal
-= delta
;
612 if (delta
< calend_adjtotal
) {
613 calend_adjdelta
= delta
= calend_adjtotal
;
615 nanoseconds_to_absolutetime((uint64_t)-delta
, &t64
);
616 calend_adjoffset
= t64
;
619 if (calend_adjdelta
!= 0)
620 calend_adjstart
= now
;
623 if (calend_adjdelta
!= 0)
624 interval
= calend_adjinterval
;
630 * clock_wakeup_calendar:
632 * Interface to power management, used
633 * to initiate the reset of the calendar
634 * on wake from sleep event.
637 clock_wakeup_calendar(void)
639 thread_call_enter(&calend_wakecall
);
643 * Wait / delay routines.
646 mach_wait_until_continue(
647 __unused
void *parameter
,
648 wait_result_t wresult
)
650 thread_syscall_return((wresult
== THREAD_INTERRUPTED
)? KERN_ABORTED
: KERN_SUCCESS
);
655 mach_wait_until_trap(
656 struct mach_wait_until_trap_args
*args
)
658 uint64_t deadline
= args
->deadline
;
659 wait_result_t wresult
;
661 wresult
= assert_wait_deadline((event_t
)mach_wait_until_trap
, THREAD_ABORTSAFE
, deadline
);
662 if (wresult
== THREAD_WAITING
)
663 wresult
= thread_block(mach_wait_until_continue
);
665 return ((wresult
== THREAD_INTERRUPTED
)? KERN_ABORTED
: KERN_SUCCESS
);
672 uint64_t now
= mach_absolute_time();
677 if ( (deadline
- now
) < (8 * sched_cswtime
) ||
678 get_preemption_level() != 0 ||
679 ml_get_interrupts_enabled() == FALSE
)
680 machine_delay_until(deadline
);
682 assert_wait_deadline((event_t
)clock_delay_until
, THREAD_UNINT
, deadline
- sched_cswtime
);
684 thread_block(THREAD_CONTINUE_NULL
);
691 uint32_t scale_factor
)
695 clock_interval_to_deadline(interval
, scale_factor
, &end
);
697 clock_delay_until(end
);
704 delay_for_interval((usec
< 0)? -usec
: usec
, NSEC_PER_USEC
);
708 * Miscellaneous routines.
711 clock_interval_to_deadline(
713 uint32_t scale_factor
,
718 clock_interval_to_absolutetime_interval(interval
, scale_factor
, &abstime
);
720 *result
= mach_absolute_time() + abstime
;
724 clock_absolutetime_interval_to_deadline(
728 *result
= mach_absolute_time() + abstime
;
735 *result
= mach_absolute_time();
739 clock_deadline_for_periodic_event(
744 assert(interval
!= 0);
746 *deadline
+= interval
;
748 if (*deadline
<= abstime
) {
749 *deadline
= abstime
+ interval
;
750 abstime
= mach_absolute_time();
752 if (*deadline
<= abstime
)
753 *deadline
= abstime
+ interval
;