diff options
Diffstat (limited to 'arch/ppc64/kernel/vdso32/gettimeofday.S')
-rw-r--r-- | arch/ppc64/kernel/vdso32/gettimeofday.S | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/arch/ppc64/kernel/vdso32/gettimeofday.S b/arch/ppc64/kernel/vdso32/gettimeofday.S new file mode 100644 index 000000000000..ca7f415195c4 --- /dev/null +++ b/arch/ppc64/kernel/vdso32/gettimeofday.S | |||
@@ -0,0 +1,139 @@ | |||
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), IBM Corp. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * as published by the Free Software Foundation; either version | ||
10 | * 2 of the License, or (at your option) any later version. | ||
11 | */ | ||
12 | #include <linux/config.h> | ||
13 | #include <asm/processor.h> | ||
14 | #include <asm/ppc_asm.h> | ||
15 | #include <asm/vdso.h> | ||
16 | #include <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 | */ | ||
26 | V_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 | bl __do_get_xsec@local /* get xsec from tb & kernel */ | ||
36 | bne- 2f /* out of line -> do syscall */ | ||
37 | |||
38 | /* seconds are xsec >> 20 */ | ||
39 | rlwinm r5,r4,12,20,31 | ||
40 | rlwimi r5,r3,12,0,19 | ||
41 | stw r5,TVAL32_TV_SEC(r10) | ||
42 | |||
43 | /* get remaining xsec and convert to usec. we scale | ||
44 | * up remaining xsec by 12 bits and get the top 32 bits | ||
45 | * of the multiplication | ||
46 | */ | ||
47 | rlwinm r5,r4,12,0,19 | ||
48 | lis r6,1000000@h | ||
49 | ori r6,r6,1000000@l | ||
50 | mulhwu r5,r5,r6 | ||
51 | stw r5,TVAL32_TV_USEC(r10) | ||
52 | |||
53 | cmpli cr0,r11,0 /* check if tz is NULL */ | ||
54 | beq 1f | ||
55 | lwz r4,CFG_TZ_MINUTEWEST(r9)/* fill tz */ | ||
56 | lwz r5,CFG_TZ_DSTTIME(r9) | ||
57 | stw r4,TZONE_TZ_MINWEST(r11) | ||
58 | stw r5,TZONE_TZ_DSTTIME(r11) | ||
59 | |||
60 | 1: mtlr r12 | ||
61 | blr | ||
62 | |||
63 | 2: mr r3,r10 | ||
64 | mr r4,r11 | ||
65 | li r0,__NR_gettimeofday | ||
66 | sc | ||
67 | b 1b | ||
68 | .cfi_endproc | ||
69 | V_FUNCTION_END(__kernel_gettimeofday) | ||
70 | |||
71 | /* | ||
72 | * This is the core of gettimeofday(), it returns the xsec | ||
73 | * value in r3 & r4 and expects the datapage ptr (non clobbered) | ||
74 | * in r9. clobbers r0,r4,r5,r6,r7,r8 | ||
75 | */ | ||
76 | __do_get_xsec: | ||
77 | .cfi_startproc | ||
78 | /* Check for update count & load values. We use the low | ||
79 | * order 32 bits of the update count | ||
80 | */ | ||
81 | 1: lwz r8,(CFG_TB_UPDATE_COUNT+4)(r9) | ||
82 | andi. r0,r8,1 /* pending update ? loop */ | ||
83 | bne- 1b | ||
84 | xor r0,r8,r8 /* create dependency */ | ||
85 | add r9,r9,r0 | ||
86 | |||
87 | /* Load orig stamp (offset to TB) */ | ||
88 | lwz r5,CFG_TB_ORIG_STAMP(r9) | ||
89 | lwz r6,(CFG_TB_ORIG_STAMP+4)(r9) | ||
90 | |||
91 | /* Get a stable TB value */ | ||
92 | 2: mftbu r3 | ||
93 | mftbl r4 | ||
94 | mftbu r0 | ||
95 | cmpl cr0,r3,r0 | ||
96 | bne- 2b | ||
97 | |||
98 | /* Substract tb orig stamp. If the high part is non-zero, we jump to the | ||
99 | * slow path which call the syscall. If it's ok, then we have our 32 bits | ||
100 | * tb_ticks value in r7 | ||
101 | */ | ||
102 | subfc r7,r6,r4 | ||
103 | subfe. r0,r5,r3 | ||
104 | bne- 3f | ||
105 | |||
106 | /* Load scale factor & do multiplication */ | ||
107 | lwz r5,CFG_TB_TO_XS(r9) /* load values */ | ||
108 | lwz r6,(CFG_TB_TO_XS+4)(r9) | ||
109 | mulhwu r4,r7,r5 | ||
110 | mulhwu r6,r7,r6 | ||
111 | mullw r6,r7,r5 | ||
112 | addc r6,r6,r0 | ||
113 | |||
114 | /* At this point, we have the scaled xsec value in r4 + XER:CA | ||
115 | * we load & add the stamp since epoch | ||
116 | */ | ||
117 | lwz r5,CFG_STAMP_XSEC(r9) | ||
118 | lwz r6,(CFG_STAMP_XSEC+4)(r9) | ||
119 | adde r4,r4,r6 | ||
120 | addze r3,r5 | ||
121 | |||
122 | /* We now have our result in r3,r4. We create a fake dependency | ||
123 | * on that result and re-check the counter | ||
124 | */ | ||
125 | xor r0,r4,r4 | ||
126 | add r9,r9,r0 | ||
127 | lwz r0,(CFG_TB_UPDATE_COUNT+4)(r9) | ||
128 | cmpl cr0,r8,r0 /* check if updated */ | ||
129 | bne- 1b | ||
130 | |||
131 | /* Warning ! The caller expects CR:EQ to be set to indicate a | ||
132 | * successful calculation (so it won't fallback to the syscall | ||
133 | * method). We have overriden that CR bit in the counter check, | ||
134 | * but fortunately, the loop exit condition _is_ CR:EQ set, so | ||
135 | * we can exit safely here. If you change this code, be careful | ||
136 | * of that side effect. | ||
137 | */ | ||
138 | 3: blr | ||
139 | .cfi_endproc | ||