2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
27 #include <sys/appleapiopts.h>
28 #include <ppc/asm.h> // EXT, LEXT
29 #include <machine/cpu_capabilities.h>
30 #include <machine/commpage.h>
32 #define USEC_PER_SEC 1000000
35 /* The red zone is used to move data between GPRs and FPRs: */
37 #define rzTicks -8 // elapsed ticks since timestamp (double)
38 #define rzSeconds -16 // seconds since timestamp (double)
39 #define rzUSeconds -24 // useconds since timestamp (double)
44 .globl EXT(gettimeofday_32)
45 .globl EXT(gettimeofday_64)
48 // *********************************
49 // * G E T T I M E O F D A Y _ 3 2 *
50 // *********************************
52 // This is a subroutine of gettimeofday.c that gets the seconds and microseconds
53 // in user mode, usually without having to make a system call. We do not deal with
54 // the timezone. The kernel maintains the following values in the comm page:
56 // _COMM_PAGE_TIMESTAMP = a BSD-style pair of uint_32's for seconds and microseconds
58 // _COMM_PAGE_TIMEBASE = the timebase at which the timestamp was valid
60 // _COMM_PAGE_SEC_PER_TICK = multiply timebase ticks by this to get seconds (double)
62 // _COMM_PAGE_2_TO_52 = double precision constant 2**52
64 // _COMM_PAGE_10_TO_6 = double precision constant 10**6
66 // We have to be careful to read these values atomically. The kernel updates them
67 // asynchronously to account for drift or time changes (eg, ntp.) We adopt the
68 // convention that (timebase==0) means the timestamp is invalid, in which case we
69 // return a bad status so our caller can make the system call.
71 // r3 = ptr to user's timeval structure (should not be null)
73 gettimeofday_32: // int gettimeofday_32(timeval *tp);
75 lwz r5,_COMM_PAGE_TIMEBASE+0(0) // r5,r6 = TBR at timestamp
76 lwz r6,_COMM_PAGE_TIMEBASE+4(0)
77 lwz r7,_COMM_PAGE_TIMESTAMP+0(0) // r7 = timestamp seconds
78 lwz r8,_COMM_PAGE_TIMESTAMP+4(0) // r8 = timestamp microseconds
79 lfd f1,_COMM_PAGE_SEC_PER_TICK(0)
81 mftbu r10 // r10,r11 = current timebase
86 or. r0,r5,r6 // timebase 0? (ie, is timestamp invalid?)
88 sync // create a barrier (patched to NOP if UP)
90 lwz r0,_COMM_PAGE_TIMEBASE+0(0) // then load data a 2nd time
91 lwz r12,_COMM_PAGE_TIMEBASE+4(0)
92 lwz r2,_COMM_PAGE_TIMESTAMP+0(0)
93 lwz r9,_COMM_PAGE_TIMESTAMP+4(0)
94 cmplw cr6,r5,r0 // did we read a consistent set?
96 beq- 3f // timestamp is disabled so return bad status
99 crand cr0_eq,cr6_eq,cr7_eq
100 crand cr1_eq,cr1_eq,cr5_eq
101 crand cr0_eq,cr0_eq,cr1_eq
102 bne- 0b // loop until we have a consistent set of data
104 subfc r11,r6,r11 // compute ticks since timestamp
105 lwz r9,_COMM_PAGE_2_TO_52(0) // get exponent for (2**52)
106 subfe r10,r5,r10 // complete 64-bit subtract
107 lfd f2,_COMM_PAGE_2_TO_52(0) // f3 <- (2**52)
108 srwi. r0,r10,2 // if more than 2**34 ticks have elapsed...
109 stw r11,rzTicks+4(r1) // store elapsed ticks into red zone
110 or r10,r10,r9 // convert long-long in (r10,r11) into double
111 bne- 3f // ...call kernel to reprime timestamp
113 stw r10,rzTicks(r1) // complete double
114 lis r12,hi16(USEC_PER_SEC)
115 ori r12,r12,lo16(USEC_PER_SEC)
117 lfd f3,rzTicks(r1) // get elapsed ticks since timestamp + 2**52
118 fsub f4,f3,f2 // subtract 2**52 and normalize
119 fmul f5,f4,f1 // f5 <- elapsed seconds since timestamp
120 lfd f3,_COMM_PAGE_10_TO_6(0) // get 10**6
121 fctiwz f6,f5 // convert to integer
122 stfd f6,rzSeconds(r1) // store integer seconds into red zone
123 stw r9,rzSeconds(r1) // prepare to reload as floating pt
124 lfd f6,rzSeconds(r1) // get seconds + 2**52
125 fsub f6,f6,f2 // f6 <- integral seconds
126 fsub f6,f5,f6 // f6 <- fractional part of elapsed seconds
127 fmul f6,f6,f3 // f6 <- fractional elapsed useconds
128 fctiwz f6,f6 // convert useconds to integer
129 stfd f6,rzUSeconds(r1) // store useconds into red zone
131 lwz r5,rzSeconds+4(r1) // r5 <- seconds since timestamp
132 lwz r6,rzUSeconds+4(r1) // r6 <- useconds since timestamp
133 add r7,r7,r5 // add elapsed seconds to timestamp seconds
134 add r8,r8,r6 // ditto useconds
136 cmplw r8,r12 // r8 >= USEC_PER_SEC ?
138 addi r7,r7,1 // add 1 to secs
139 sub r8,r8,r12 // subtract USEC_PER_SEC from usecs
141 stw r7,0(r3) // store secs//usecs into user's timeval
143 li r3,0 // return success
145 3: // too long since last timestamp or this code is disabled
146 li r3,1 // return bad status so our caller will make syscall
149 COMMPAGE_DESCRIPTOR(gettimeofday_32,_COMM_PAGE_GETTIMEOFDAY,0,k64Bit,kCommPageSYNC)
152 // *********************************
153 // * G E T T I M E O F D A Y _ 6 4 *
154 // *********************************
156 gettimeofday_64: // int gettimeofday_64(timeval *tp);
158 ld r6,_COMM_PAGE_TIMEBASE(0) // r6 = TBR at timestamp
159 ld r8,_COMM_PAGE_TIMESTAMP(0) // r8 = timestamp (seconds,useconds)
160 lfd f1,_COMM_PAGE_SEC_PER_TICK(0)
161 mftb r10 // r10 = get current timebase
162 lwsync // create a barrier if MP (patched to NOP if UP)
163 ld r11,_COMM_PAGE_TIMEBASE(0) // then get data a 2nd time
164 ld r12,_COMM_PAGE_TIMESTAMP(0)
165 cmpdi cr1,r6,0 // is the timestamp disabled?
166 cmpld cr6,r6,r11 // did we read a consistent set?
168 beq-- cr1,3f // exit if timestamp disabled
169 crand cr6_eq,cr7_eq,cr6_eq
170 sub r11,r10,r6 // compute elapsed ticks from timestamp
171 bne-- cr6,0b // loop until we have a consistent set of data
173 srdi. r0,r11,35 // has it been more than 2**35 ticks since last timestamp?
174 std r11,rzTicks(r1) // put ticks in redzone where we can "lfd" it
175 bne-- 3f // timestamp too old, so reprime
177 lfd f3,rzTicks(r1) // get elapsed ticks since timestamp (fixed pt)
178 fcfid f4,f3 // float the tick count
179 fmul f5,f4,f1 // f5 <- elapsed seconds since timestamp
180 lfd f3,_COMM_PAGE_10_TO_6(0) // get 10**6
181 fctidz f6,f5 // convert integer seconds to fixed pt
182 stfd f6,rzSeconds(r1) // save fixed pt integer seconds in red zone
183 fcfid f6,f6 // float the integer seconds
184 fsub f6,f5,f6 // f6 <- fractional part of elapsed seconds
185 fmul f6,f6,f3 // f6 <- fractional elapsed useconds
186 fctidz f6,f6 // convert useconds to fixed pt integer
187 stfd f6,rzUSeconds(r1) // store useconds into red zone
189 lis r12,hi16(USEC_PER_SEC) // r12 <- 10**6
190 srdi r7,r8,32 // extract seconds from doubleword timestamp
191 lwz r5,rzSeconds+4(r1) // r5 <- seconds since timestamp
192 ori r12,r12,lo16(USEC_PER_SEC)
193 lwz r6,rzUSeconds+4(r1) // r6 <- useconds since timestamp
194 add r7,r7,r5 // add elapsed seconds to timestamp seconds
195 add r8,r8,r6 // ditto useconds
197 cmplw r8,r12 // r8 >= USEC_PER_SEC ?
199 addi r7,r7,1 // add 1 to secs
200 sub r8,r8,r12 // subtract USEC_PER_SEC from usecs
202 stw r7,0(r3) // store secs//usecs into user's timeval
204 li r3,0 // return success
206 3: // too long since last timestamp or this code is disabled
207 li r3,1 // return bad status so our caller will make syscall
210 COMMPAGE_DESCRIPTOR(gettimeofday_64,_COMM_PAGE_GETTIMEOFDAY,k64Bit,0,kCommPageSYNC)