1/* 2 * Userland implementation of gettimeofday() for 32 bits processes in a 3 * ppc64 kernel for use in the vDSO 4 * 5 * Copyright (C) 2004 Benjamin Herrenschmuidt (benh@kernel.crashing.org, 6 * IBM Corp. 7 * 8 * This program is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License 10 * as published by the Free Software Foundation; either version 11 * 2 of the License, or (at your option) any later version. 12 */ 13#include <asm/processor.h> 14#include <asm/ppc_asm.h> 15#include <asm/vdso.h> 16#include <asm/asm-offsets.h> 17#include <asm/unistd.h> 18 19 .text 20/* 21 * Exact prototype of gettimeofday 22 * 23 * int __kernel_gettimeofday(struct timeval *tv, struct timezone *tz); 24 * 25 */ 26V_FUNCTION_BEGIN(__kernel_gettimeofday) 27 .cfi_startproc 28 mflr r12 29 .cfi_register lr,r12 30 31 mr r10,r3 /* r10 saves tv */ 32 mr r11,r4 /* r11 saves tz */ 33 bl __get_datapage@local /* get data page */ 34 mr r9, r3 /* datapage ptr in r9 */ 35 cmplwi r10,0 /* check if tv is NULL */ 36 beq 3f 37 bl __do_get_xsec@local /* get xsec from tb & kernel */ 38 bne- 2f /* out of line -> do syscall */ 39 40 /* seconds are xsec >> 20 */ 41 rlwinm r5,r4,12,20,31 42 rlwimi r5,r3,12,0,19 43 stw r5,TVAL32_TV_SEC(r10) 44 45 /* get remaining xsec and convert to usec. we scale 46 * up remaining xsec by 12 bits and get the top 32 bits 47 * of the multiplication 48 */ 49 rlwinm r5,r4,12,0,19 50 lis r6,1000000@h 51 ori r6,r6,1000000@l 52 mulhwu r5,r5,r6 53 stw r5,TVAL32_TV_USEC(r10) 54 553: cmplwi r11,0 /* check if tz is NULL */ 56 beq 1f 57 lwz r4,CFG_TZ_MINUTEWEST(r9)/* fill tz */ 58 lwz r5,CFG_TZ_DSTTIME(r9) 59 stw r4,TZONE_TZ_MINWEST(r11) 60 stw r5,TZONE_TZ_DSTTIME(r11) 61 621: mtlr r12 63 crclr cr0*4+so 64 li r3,0 65 blr 66 672: 68 mtlr r12 69 mr r3,r10 70 mr r4,r11 71 li r0,__NR_gettimeofday 72 sc 73 blr 74 .cfi_endproc 75V_FUNCTION_END(__kernel_gettimeofday) 76 77/* 78 * Exact prototype of clock_gettime() 79 * 80 * int __kernel_clock_gettime(clockid_t clock_id, struct timespec *tp); 81 * 82 */ 83V_FUNCTION_BEGIN(__kernel_clock_gettime) 84 .cfi_startproc 85 /* Check for supported clock IDs */ 86 cmpli cr0,r3,CLOCK_REALTIME 87 cmpli cr1,r3,CLOCK_MONOTONIC 88 cror cr0*4+eq,cr0*4+eq,cr1*4+eq 89 bne cr0,99f 90 91 mflr r12 /* r12 saves lr */ 92 .cfi_register lr,r12 93 mr r10,r3 /* r10 saves id */ 94 mr r11,r4 /* r11 saves tp */ 95 bl __get_datapage@local /* get data page */ 96 mr r9,r3 /* datapage ptr in r9 */ 97 beq cr1,50f /* if monotonic -> jump there */ 98 99 /* 100 * CLOCK_REALTIME 101 */ 102 103 bl __do_get_xsec@local /* get xsec from tb & kernel */ 104 bne- 98f /* out of line -> do syscall */ 105 106 /* seconds are xsec >> 20 */ 107 rlwinm r5,r4,12,20,31 108 rlwimi r5,r3,12,0,19 109 stw r5,TSPC32_TV_SEC(r11) 110 111 /* get remaining xsec and convert to nsec. we scale 112 * up remaining xsec by 12 bits and get the top 32 bits 113 * of the multiplication, then we multiply by 1000 114 */ 115 rlwinm r5,r4,12,0,19 116 lis r6,1000000@h 117 ori r6,r6,1000000@l 118 mulhwu r5,r5,r6 119 mulli r5,r5,1000 120 stw r5,TSPC32_TV_NSEC(r11) 121 mtlr r12 122 crclr cr0*4+so 123 li r3,0 124 blr 125 126 /* 127 * CLOCK_MONOTONIC 128 */ 129 13050: bl __do_get_xsec@local /* get xsec from tb & kernel */ 131 bne- 98f /* out of line -> do syscall */ 132 133 /* seconds are xsec >> 20 */ 134 rlwinm r6,r4,12,20,31 135 rlwimi r6,r3,12,0,19 136 137 /* get remaining xsec and convert to nsec. we scale 138 * up remaining xsec by 12 bits and get the top 32 bits 139 * of the multiplication, then we multiply by 1000 140 */ 141 rlwinm r7,r4,12,0,19 142 lis r5,1000000@h 143 ori r5,r5,1000000@l 144 mulhwu r7,r7,r5 145 mulli r7,r7,1000 146 147 /* now we must fixup using wall to monotonic. We need to snapshot 148 * that value and do the counter trick again. Fortunately, we still 149 * have the counter value in r8 that was returned by __do_get_xsec. 150 * At this point, r6,r7 contain our sec/nsec values, r3,r4 and r5 151 * can be used 152 */ 153 154 lwz r3,WTOM_CLOCK_SEC(r9) 155 lwz r4,WTOM_CLOCK_NSEC(r9) 156 157 /* We now have our result in r3,r4. We create a fake dependency 158 * on that result and re-check the counter 159 */ 160 or r5,r4,r3 161 xor r0,r5,r5 162 add r9,r9,r0 163#ifdef CONFIG_PPC64 164 lwz r0,(CFG_TB_UPDATE_COUNT+4)(r9) 165#else 166 lwz r0,(CFG_TB_UPDATE_COUNT)(r9) 167#endif 168 cmpl cr0,r8,r0 /* check if updated */ 169 bne- 50b 170 171 /* Calculate and store result. Note that this mimmics the C code, 172 * which may cause funny results if nsec goes negative... is that 173 * possible at all ? 174 */ 175 add r3,r3,r6 176 add r4,r4,r7 177 lis r5,NSEC_PER_SEC@h 178 ori r5,r5,NSEC_PER_SEC@l 179 cmpl cr0,r4,r5 180 cmpli cr1,r4,0 181 blt 1f 182 subf r4,r5,r4 183 addi r3,r3,1 1841: bge cr1,1f 185 addi r3,r3,-1 186 add r4,r4,r5 1871: stw r3,TSPC32_TV_SEC(r11) 188 stw r4,TSPC32_TV_NSEC(r11) 189 190 mtlr r12 191 crclr cr0*4+so 192 li r3,0 193 blr 194 195 /* 196 * syscall fallback 197 */ 19898: 199 mtlr r12 200 mr r3,r10 201 mr r4,r11 20299: 203 li r0,__NR_clock_gettime 204 sc 205 blr 206 .cfi_endproc 207V_FUNCTION_END(__kernel_clock_gettime) 208 209 210/* 211 * Exact prototype of clock_getres() 212 * 213 * int __kernel_clock_getres(clockid_t clock_id, struct timespec *res); 214 * 215 */ 216V_FUNCTION_BEGIN(__kernel_clock_getres) 217 .cfi_startproc 218 /* Check for supported clock IDs */ 219 cmpwi cr0,r3,CLOCK_REALTIME 220 cmpwi cr1,r3,CLOCK_MONOTONIC 221 cror cr0*4+eq,cr0*4+eq,cr1*4+eq 222 bne cr0,99f 223 224 li r3,0 225 cmpli cr0,r4,0 226 crclr cr0*4+so 227 beqlr 228 lis r5,CLOCK_REALTIME_RES@h 229 ori r5,r5,CLOCK_REALTIME_RES@l 230 stw r3,TSPC32_TV_SEC(r4) 231 stw r5,TSPC32_TV_NSEC(r4) 232 blr 233 234 /* 235 * syscall fallback 236 */ 23799: 238 li r0,__NR_clock_getres 239 sc 240 blr 241 .cfi_endproc 242V_FUNCTION_END(__kernel_clock_getres) 243 244 245/* 246 * This is the core of gettimeofday() & friends, it returns the xsec 247 * value in r3 & r4 and expects the datapage ptr (non clobbered) 248 * in r9. clobbers r0,r4,r5,r6,r7,r8. 249 * When returning, r8 contains the counter value that can be reused 250 * by the monotonic clock implementation 251 */ 252__do_get_xsec: 253 .cfi_startproc 254 /* Check for update count & load values. We use the low 255 * order 32 bits of the update count 256 */ 257#ifdef CONFIG_PPC64 2581: lwz r8,(CFG_TB_UPDATE_COUNT+4)(r9) 259#else 2601: lwz r8,(CFG_TB_UPDATE_COUNT)(r9) 261#endif 262 andi. r0,r8,1 /* pending update ? loop */ 263 bne- 1b 264 xor r0,r8,r8 /* create dependency */ 265 add r9,r9,r0 266 267 /* Load orig stamp (offset to TB) */ 268 lwz r5,CFG_TB_ORIG_STAMP(r9) 269 lwz r6,(CFG_TB_ORIG_STAMP+4)(r9) 270 271 /* Get a stable TB value */ 2722: mftbu r3 273 mftbl r4 274 mftbu r0 275 cmpl cr0,r3,r0 276 bne- 2b 277 278 /* Substract tb orig stamp. If the high part is non-zero, we jump to 279 * the slow path which call the syscall. 280 * If it's ok, then we have our 32 bits tb_ticks value in r7 281 */ 282 subfc r7,r6,r4 283 subfe. r0,r5,r3 284 bne- 3f 285 286 /* Load scale factor & do multiplication */ 287 lwz r5,CFG_TB_TO_XS(r9) /* load values */ 288 lwz r6,(CFG_TB_TO_XS+4)(r9) 289 mulhwu r4,r7,r5 290 mulhwu r6,r7,r6 291 mullw r0,r7,r5 292 addc r6,r6,r0 293 294 /* At this point, we have the scaled xsec value in r4 + XER:CA 295 * we load & add the stamp since epoch 296 */ 297 lwz r5,CFG_STAMP_XSEC(r9) 298 lwz r6,(CFG_STAMP_XSEC+4)(r9) 299 adde r4,r4,r6 300 addze r3,r5 301 302 /* We now have our result in r3,r4. We create a fake dependency 303 * on that result and re-check the counter 304 */ 305 or r6,r4,r3 306 xor r0,r6,r6 307 add r9,r9,r0 308#ifdef CONFIG_PPC64 309 lwz r0,(CFG_TB_UPDATE_COUNT+4)(r9) 310#else 311 lwz r0,(CFG_TB_UPDATE_COUNT)(r9) 312#endif 313 cmpl cr0,r8,r0 /* check if updated */ 314 bne- 1b 315 316 /* Warning ! The caller expects CR:EQ to be set to indicate a 317 * successful calculation (so it won't fallback to the syscall 318 * method). We have overriden that CR bit in the counter check, 319 * but fortunately, the loop exit condition _is_ CR:EQ set, so 320 * we can exit safely here. If you change this code, be careful 321 * of that side effect. 322 */ 3233: blr 324 .cfi_endproc 325