1/* 2 * Userland implementation of gettimeofday() for 64 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 r11,r3 /* r11 holds tv */ 32 mr r10,r4 /* r10 holds tz */ 33 bl V_LOCAL_FUNC(__get_datapage) /* get data page */ 34 cmpldi r11,0 /* check if tv is NULL */ 35 beq 2f 36 lis r7,1000000@ha /* load up USEC_PER_SEC */ 37 addi r7,r7,1000000@l 38 bl V_LOCAL_FUNC(__do_get_tspec) /* get sec/us from tb & kernel */ 39 std r4,TVAL64_TV_SEC(r11) /* store sec in tv */ 40 std r5,TVAL64_TV_USEC(r11) /* store usec in tv */ 412: cmpldi r10,0 /* check if tz is NULL */ 42 beq 1f 43 lwz r4,CFG_TZ_MINUTEWEST(r3)/* fill tz */ 44 lwz r5,CFG_TZ_DSTTIME(r3) 45 stw r4,TZONE_TZ_MINWEST(r10) 46 stw r5,TZONE_TZ_DSTTIME(r10) 471: mtlr r12 48 crclr cr0*4+so 49 li r3,0 /* always success */ 50 blr 51 .cfi_endproc 52V_FUNCTION_END(__kernel_gettimeofday) 53 54 55/* 56 * Exact prototype of clock_gettime() 57 * 58 * int __kernel_clock_gettime(clockid_t clock_id, struct timespec *tp); 59 * 60 */ 61V_FUNCTION_BEGIN(__kernel_clock_gettime) 62 .cfi_startproc 63 /* Check for supported clock IDs */ 64 cmpwi cr0,r3,CLOCK_REALTIME 65 cmpwi cr1,r3,CLOCK_MONOTONIC 66 cror cr0*4+eq,cr0*4+eq,cr1*4+eq 67 bne cr0,99f 68 69 mflr r12 /* r12 saves lr */ 70 .cfi_register lr,r12 71 mr r11,r4 /* r11 saves tp */ 72 bl V_LOCAL_FUNC(__get_datapage) /* get data page */ 73 lis r7,NSEC_PER_SEC@h /* want nanoseconds */ 74 ori r7,r7,NSEC_PER_SEC@l 7550: bl V_LOCAL_FUNC(__do_get_tspec) /* get time from tb & kernel */ 76 bne cr1,80f /* if not monotonic, all done */ 77 78 /* 79 * CLOCK_MONOTONIC 80 */ 81 82 /* now we must fixup using wall to monotonic. We need to snapshot 83 * that value and do the counter trick again. Fortunately, we still 84 * have the counter value in r8 that was returned by __do_get_tspec. 85 * At this point, r4,r5 contain our sec/nsec values. 86 */ 87 88 lwa r6,WTOM_CLOCK_SEC(r3) 89 lwa r9,WTOM_CLOCK_NSEC(r3) 90 91 /* We now have our result in r6,r9. We create a fake dependency 92 * on that result and re-check the counter 93 */ 94 or r0,r6,r9 95 xor r0,r0,r0 96 add r3,r3,r0 97 ld r0,CFG_TB_UPDATE_COUNT(r3) 98 cmpld cr0,r0,r8 /* check if updated */ 99 bne- 50b 100 101 /* Add wall->monotonic offset and check for overflow or underflow. 102 */ 103 add r4,r4,r6 104 add r5,r5,r9 105 cmpd cr0,r5,r7 106 cmpdi cr1,r5,0 107 blt 1f 108 subf r5,r7,r5 109 addi r4,r4,1 1101: bge cr1,80f 111 addi r4,r4,-1 112 add r5,r5,r7 113 11480: std r4,TSPC64_TV_SEC(r11) 115 std r5,TSPC64_TV_NSEC(r11) 116 117 mtlr r12 118 crclr cr0*4+so 119 li r3,0 120 blr 121 122 /* 123 * syscall fallback 124 */ 12599: 126 li r0,__NR_clock_gettime 127 sc 128 blr 129 .cfi_endproc 130V_FUNCTION_END(__kernel_clock_gettime) 131 132 133/* 134 * Exact prototype of clock_getres() 135 * 136 * int __kernel_clock_getres(clockid_t clock_id, struct timespec *res); 137 * 138 */ 139V_FUNCTION_BEGIN(__kernel_clock_getres) 140 .cfi_startproc 141 /* Check for supported clock IDs */ 142 cmpwi cr0,r3,CLOCK_REALTIME 143 cmpwi cr1,r3,CLOCK_MONOTONIC 144 cror cr0*4+eq,cr0*4+eq,cr1*4+eq 145 bne cr0,99f 146 147 li r3,0 148 cmpli cr0,r4,0 149 crclr cr0*4+so 150 beqlr 151 lis r5,CLOCK_REALTIME_RES@h 152 ori r5,r5,CLOCK_REALTIME_RES@l 153 std r3,TSPC64_TV_SEC(r4) 154 std r5,TSPC64_TV_NSEC(r4) 155 blr 156 157 /* 158 * syscall fallback 159 */ 16099: 161 li r0,__NR_clock_getres 162 sc 163 blr 164 .cfi_endproc 165V_FUNCTION_END(__kernel_clock_getres) 166 167 168/* 169 * This is the core of clock_gettime() and gettimeofday(), 170 * it returns the current time in r4 (seconds) and r5. 171 * On entry, r7 gives the resolution of r5, either USEC_PER_SEC 172 * or NSEC_PER_SEC, giving r5 in microseconds or nanoseconds. 173 * It expects the datapage ptr in r3 and doesn't clobber it. 174 * It clobbers r0, r6 and r9. 175 * On return, r8 contains the counter value that can be reused. 176 * This clobbers cr0 but not any other cr field. 177 */ 178V_FUNCTION_BEGIN(__do_get_tspec) 179 .cfi_startproc 180 /* check for update count & load values */ 1811: ld r8,CFG_TB_UPDATE_COUNT(r3) 182 andi. r0,r8,1 /* pending update ? loop */ 183 bne- 1b 184 xor r0,r8,r8 /* create dependency */ 185 add r3,r3,r0 186 187 MFTB(r6) 188 ld r9,CFG_TB_ORIG_STAMP(r3) 189 subf r6,r9,r6 190 191 /* Scale result */ 192 ld r5,CFG_TB_TO_XS(r3) 193 sldi r6,r6,12 /* compute time since stamp_xtime */ 194 mulhdu r6,r6,r5 /* in units of 2^-32 seconds */ 195 196 /* Add stamp since epoch */ 197 ld r4,STAMP_XTIME+TSPC64_TV_SEC(r3) 198 lwz r5,STAMP_SEC_FRAC(r3) 199 or r0,r4,r5 200 or r0,r0,r6 201 xor r0,r0,r0 202 add r3,r3,r0 203 ld r0,CFG_TB_UPDATE_COUNT(r3) 204 cmpld r0,r8 /* check if updated */ 205 bne- 1b /* reload if so */ 206 207 /* convert to seconds & nanoseconds and add to stamp */ 208 add r6,r6,r5 /* add on fractional seconds of xtime */ 209 mulhwu r5,r6,r7 /* compute micro or nanoseconds and */ 210 srdi r6,r6,32 /* seconds since stamp_xtime */ 211 clrldi r5,r5,32 212 add r4,r4,r6 213 blr 214 .cfi_endproc 215V_FUNCTION_END(__do_get_tspec) 216