]> git.saurik.com Git - apple/xnu.git/blob - osfmk/ppc/commpage/gettimeofday.s
xnu-792.tar.gz
[apple/xnu.git] / osfmk / ppc / commpage / gettimeofday.s
1 /*
2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23 #define ASSEMBLER
24 #include <sys/appleapiopts.h>
25 #include <ppc/asm.h> // EXT, LEXT
26 #include <machine/cpu_capabilities.h>
27 #include <machine/commpage.h>
28
29 #define USEC_PER_SEC 1000000
30
31
32 /* The red zone is used to move data between GPRs and FPRs: */
33
34 #define rzTicks -8 // elapsed ticks since timestamp (double)
35 #define rzSeconds -16 // seconds since timestamp (double)
36 #define rzUSeconds -24 // useconds since timestamp (double)
37
38
39 .text
40 .align 2
41
42
43 // *********************************
44 // * G E T T I M E O F D A Y _ 3 2 *
45 // *********************************
46 //
47 // This is a subroutine of gettimeofday.c that gets the seconds and microseconds
48 // in user mode, usually without having to make a system call. We do not deal with
49 // the timezone. The kernel maintains the following values in the comm page:
50 //
51 // _COMM_PAGE_TIMESTAMP = a BSD-style pair of uint_32's for seconds and microseconds
52 //
53 // _COMM_PAGE_TIMEBASE = the timebase at which the timestamp was valid
54 //
55 // _COMM_PAGE_SEC_PER_TICK = multiply timebase ticks by this to get seconds (double)
56 //
57 // _COMM_PAGE_2_TO_52 = double precision constant 2**52
58 //
59 // _COMM_PAGE_10_TO_6 = double precision constant 10**6
60 //
61 // We have to be careful to read these values atomically. The kernel updates them
62 // asynchronously to account for drift or time changes (eg, ntp.) We adopt the
63 // convention that (timebase==0) means the timestamp is invalid, in which case we
64 // return a bad status so our caller can make the system call.
65 //
66 // r3 = ptr to user's timeval structure (should not be null)
67
68 gettimeofday_32: // int gettimeofday(timeval *tp);
69 0:
70 lwz r5,_COMM_PAGE_TIMEBASE+0(0) // r5,r6 = TBR at timestamp
71 lwz r6,_COMM_PAGE_TIMEBASE+4(0)
72 lwz r7,_COMM_PAGE_TIMESTAMP+0(0) // r7 = timestamp seconds
73 lwz r8,_COMM_PAGE_TIMESTAMP+4(0) // r8 = timestamp microseconds
74 lfd f1,_COMM_PAGE_SEC_PER_TICK(0)
75 1:
76 mftbu r10 // r10,r11 = current timebase
77 mftb r11
78 mftbu r12
79 cmplw r10,r12
80 bne- 1b
81 or. r0,r5,r6 // timebase 0? (ie, is timestamp invalid?)
82
83 sync // create a barrier (patched to NOP if UP)
84
85 lwz r0,_COMM_PAGE_TIMEBASE+0(0) // then load data a 2nd time
86 lwz r12,_COMM_PAGE_TIMEBASE+4(0)
87 lwz r2,_COMM_PAGE_TIMESTAMP+0(0)
88 lwz r9,_COMM_PAGE_TIMESTAMP+4(0)
89 cmplw cr6,r5,r0 // did we read a consistent set?
90 cmplw cr7,r6,r12
91 beq- 3f // timestamp is disabled so return bad status
92 cmplw cr1,r2,r7
93 cmplw cr5,r9,r8
94 crand cr0_eq,cr6_eq,cr7_eq
95 crand cr1_eq,cr1_eq,cr5_eq
96 crand cr0_eq,cr0_eq,cr1_eq
97 bne- 0b // loop until we have a consistent set of data
98
99 subfc r11,r6,r11 // compute ticks since timestamp
100 lwz r9,_COMM_PAGE_2_TO_52(0) // get exponent for (2**52)
101 subfe r10,r5,r10 // complete 64-bit subtract
102 lfd f2,_COMM_PAGE_2_TO_52(0) // f3 <- (2**52)
103 srwi. r0,r10,2 // if more than 2**34 ticks have elapsed...
104 stw r11,rzTicks+4(r1) // store elapsed ticks into red zone
105 or r10,r10,r9 // convert long-long in (r10,r11) into double
106 bne- 3f // ...call kernel to reprime timestamp
107
108 stw r10,rzTicks(r1) // complete double
109 lis r12,hi16(USEC_PER_SEC)
110 ori r12,r12,lo16(USEC_PER_SEC)
111
112 lfd f3,rzTicks(r1) // get elapsed ticks since timestamp + 2**52
113 fsub f4,f3,f2 // subtract 2**52 and normalize
114 fmul f5,f4,f1 // f5 <- elapsed seconds since timestamp
115 lfd f3,_COMM_PAGE_10_TO_6(0) // get 10**6
116 fctiwz f6,f5 // convert to integer
117 stfd f6,rzSeconds(r1) // store integer seconds into red zone
118 stw r9,rzSeconds(r1) // prepare to reload as floating pt
119 lfd f6,rzSeconds(r1) // get seconds + 2**52
120 fsub f6,f6,f2 // f6 <- integral seconds
121 fsub f6,f5,f6 // f6 <- fractional part of elapsed seconds
122 fmul f6,f6,f3 // f6 <- fractional elapsed useconds
123 fctiwz f6,f6 // convert useconds to integer
124 stfd f6,rzUSeconds(r1) // store useconds into red zone
125
126 lwz r5,rzSeconds+4(r1) // r5 <- seconds since timestamp
127 lwz r6,rzUSeconds+4(r1) // r6 <- useconds since timestamp
128 add r7,r7,r5 // add elapsed seconds to timestamp seconds
129 add r8,r8,r6 // ditto useconds
130
131 cmplw r8,r12 // r8 >= USEC_PER_SEC ?
132 blt 2f // no
133 addi r7,r7,1 // add 1 to secs
134 sub r8,r8,r12 // subtract USEC_PER_SEC from usecs
135 2:
136 stw r7,0(r3) // store secs//usecs into user's timeval
137 stw r8,4(r3)
138 li r3,0 // return success
139 blr
140 3: // too long since last timestamp or this code is disabled
141 li r3,1 // return bad status so our caller will make syscall
142 blr
143
144 COMMPAGE_DESCRIPTOR(gettimeofday_32,_COMM_PAGE_GETTIMEOFDAY,0,k64Bit,kCommPageSYNC+kCommPage32)
145
146
147 // ***************************************
148 // * G E T T I M E O F D A Y _ G 5 _ 3 2 *
149 // ***************************************
150 //
151 // This routine is called in 32-bit mode on 64-bit processors. A timeval is a struct of
152 // a long seconds and int useconds, so it's size depends on mode.
153
154 gettimeofday_g5_32: // int gettimeofday(timeval *tp);
155 0:
156 ld r6,_COMM_PAGE_TIMEBASE(0) // r6 = TBR at timestamp
157 ld r8,_COMM_PAGE_TIMESTAMP(0) // r8 = timestamp (seconds,useconds)
158 lfd f1,_COMM_PAGE_SEC_PER_TICK(0)
159 mftb r10 // r10 = get current timebase
160 lwsync // create a barrier if MP (patched to NOP if UP)
161 ld r11,_COMM_PAGE_TIMEBASE(0) // then get data a 2nd time
162 ld r12,_COMM_PAGE_TIMESTAMP(0)
163 cmpdi cr1,r6,0 // is the timestamp disabled?
164 cmpld cr6,r6,r11 // did we read a consistent set?
165 cmpld cr7,r8,r12
166 beq-- cr1,3f // exit if timestamp disabled
167 crand cr6_eq,cr7_eq,cr6_eq
168 sub r11,r10,r6 // compute elapsed ticks from timestamp
169 bne-- cr6,0b // loop until we have a consistent set of data
170
171 srdi. r0,r11,35 // has it been more than 2**35 ticks since last timestamp?
172 std r11,rzTicks(r1) // put ticks in redzone where we can "lfd" it
173 bne-- 3f // timestamp too old, so reprime
174
175 lfd f3,rzTicks(r1) // get elapsed ticks since timestamp (fixed pt)
176 fcfid f4,f3 // float the tick count
177 fmul f5,f4,f1 // f5 <- elapsed seconds since timestamp
178 lfd f3,_COMM_PAGE_10_TO_6(0) // get 10**6
179 fctidz f6,f5 // convert integer seconds to fixed pt
180 stfd f6,rzSeconds(r1) // save fixed pt integer seconds in red zone
181 fcfid f6,f6 // float the integer seconds
182 fsub f6,f5,f6 // f6 <- fractional part of elapsed seconds
183 fmul f6,f6,f3 // f6 <- fractional elapsed useconds
184 fctidz f6,f6 // convert useconds to fixed pt integer
185 stfd f6,rzUSeconds(r1) // store useconds into red zone
186
187 lis r12,hi16(USEC_PER_SEC) // r12 <- 10**6
188 srdi r7,r8,32 // extract seconds from doubleword timestamp
189 lwz r5,rzSeconds+4(r1) // r5 <- seconds since timestamp
190 ori r12,r12,lo16(USEC_PER_SEC)
191 lwz r6,rzUSeconds+4(r1) // r6 <- useconds since timestamp
192 add r7,r7,r5 // add elapsed seconds to timestamp seconds
193 add r8,r8,r6 // ditto useconds
194
195 cmplw r8,r12 // r8 >= USEC_PER_SEC ?
196 blt 2f // no
197 addi r7,r7,1 // add 1 to secs
198 sub r8,r8,r12 // subtract USEC_PER_SEC from usecs
199 2:
200 stw r7,0(r3) // store secs//usecs into user's timeval
201 stw r8,4(r3)
202 li r3,0 // return success
203 blr
204 3: // too long since last timestamp or this code is disabled
205 li r3,1 // return bad status so our caller will make syscall
206 blr
207
208 COMMPAGE_DESCRIPTOR(gettimeofday_g5_32,_COMM_PAGE_GETTIMEOFDAY,k64Bit,0,kCommPageSYNC+kCommPage32)
209
210
211 // ***************************************
212 // * G E T T I M E O F D A Y _ G 5 _ 6 4 *
213 // ***************************************
214 //
215 // This routine is called in 64-bit mode on 64-bit processors. A timeval is a struct of
216 // a long seconds and int useconds, so it's size depends on mode.
217
218 gettimeofday_g5_64: // int gettimeofday(timeval *tp);
219 0:
220 ld r6,_COMM_PAGE_TIMEBASE(0) // r6 = TBR at timestamp
221 ld r8,_COMM_PAGE_TIMESTAMP(0) // r8 = timestamp (seconds,useconds)
222 lfd f1,_COMM_PAGE_SEC_PER_TICK(0)
223 mftb r10 // r10 = get current timebase
224 lwsync // create a barrier if MP (patched to NOP if UP)
225 ld r11,_COMM_PAGE_TIMEBASE(0) // then get data a 2nd time
226 ld r12,_COMM_PAGE_TIMESTAMP(0)
227 cmpdi cr1,r6,0 // is the timestamp disabled?
228 cmpld cr6,r6,r11 // did we read a consistent set?
229 cmpld cr7,r8,r12
230 beq-- cr1,3f // exit if timestamp disabled
231 crand cr6_eq,cr7_eq,cr6_eq
232 sub r11,r10,r6 // compute elapsed ticks from timestamp
233 bne-- cr6,0b // loop until we have a consistent set of data
234
235 srdi. r0,r11,35 // has it been more than 2**35 ticks since last timestamp?
236 std r11,rzTicks(r1) // put ticks in redzone where we can "lfd" it
237 bne-- 3f // timestamp too old, so reprime
238
239 lfd f3,rzTicks(r1) // get elapsed ticks since timestamp (fixed pt)
240 fcfid f4,f3 // float the tick count
241 fmul f5,f4,f1 // f5 <- elapsed seconds since timestamp
242 lfd f3,_COMM_PAGE_10_TO_6(0) // get 10**6
243 fctidz f6,f5 // convert integer seconds to fixed pt
244 stfd f6,rzSeconds(r1) // save fixed pt integer seconds in red zone
245 fcfid f6,f6 // float the integer seconds
246 fsub f6,f5,f6 // f6 <- fractional part of elapsed seconds
247 fmul f6,f6,f3 // f6 <- fractional elapsed useconds
248 fctidz f6,f6 // convert useconds to fixed pt integer
249 stfd f6,rzUSeconds(r1) // store useconds into red zone
250
251 lis r12,hi16(USEC_PER_SEC) // r12 <- 10**6
252 srdi r7,r8,32 // extract seconds from doubleword timestamp
253 lwz r5,rzSeconds+4(r1) // r5 <- seconds since timestamp
254 ori r12,r12,lo16(USEC_PER_SEC)
255 lwz r6,rzUSeconds+4(r1) // r6 <- useconds since timestamp
256 add r7,r7,r5 // add elapsed seconds to timestamp seconds
257 add r8,r8,r6 // ditto useconds
258
259 cmplw r8,r12 // r8 >= USEC_PER_SEC ?
260 blt 2f // no
261 addi r7,r7,1 // add 1 to secs
262 sub r8,r8,r12 // subtract USEC_PER_SEC from usecs
263 2:
264 std r7,0(r3) // store secs//usecs into user's timeval
265 stw r8,8(r3)
266 li r3,0 // return success
267 blr
268 3: // too long since last timestamp or this code is disabled
269 li r3,1 // return bad status so our caller will make syscall
270 blr
271
272 COMMPAGE_DESCRIPTOR(gettimeofday_g5_64,_COMM_PAGE_GETTIMEOFDAY,k64Bit,0,kCommPageSYNC+kCommPage64)
273
274