• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2012 Tilera Corporation. All Rights Reserved.
3  *
4  *   This program is free software; you can redistribute it and/or
5  *   modify it under the terms of the GNU General Public License
6  *   as published by the Free Software Foundation, version 2.
7  *
8  *   This program is distributed in the hope that it will be useful, but
9  *   WITHOUT ANY WARRANTY; without even the implied warranty of
10  *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
11  *   NON INFRINGEMENT.  See the GNU General Public License for
12  *   more details.
13  */
14 
15 #define VDSO_BUILD  /* avoid some shift warnings for -m32 in <asm/page.h> */
16 #include <linux/time.h>
17 #include <asm/timex.h>
18 #include <asm/unistd.h>
19 #include <asm/vdso.h>
20 
21 #if CHIP_HAS_SPLIT_CYCLE()
get_cycles_inline(void)22 static inline cycles_t get_cycles_inline(void)
23 {
24 	unsigned int high = __insn_mfspr(SPR_CYCLE_HIGH);
25 	unsigned int low = __insn_mfspr(SPR_CYCLE_LOW);
26 	unsigned int high2 = __insn_mfspr(SPR_CYCLE_HIGH);
27 
28 	while (unlikely(high != high2)) {
29 		low = __insn_mfspr(SPR_CYCLE_LOW);
30 		high = high2;
31 		high2 = __insn_mfspr(SPR_CYCLE_HIGH);
32 	}
33 
34 	return (((cycles_t)high) << 32) | low;
35 }
36 #define get_cycles get_cycles_inline
37 #endif
38 
39 struct syscall_return_value {
40 	long value;
41 	long error;
42 };
43 
44 /*
45  * Find out the vDSO data page address in the process address space.
46  */
get_datapage(void)47 inline unsigned long get_datapage(void)
48 {
49 	unsigned long ret;
50 
51 	/* vdso data page located in the 2nd vDSO page. */
52 	asm volatile ("lnk %0" : "=r"(ret));
53 	ret &= ~(PAGE_SIZE - 1);
54 	ret += PAGE_SIZE;
55 
56 	return ret;
57 }
58 
vgetsns(struct vdso_data * vdso)59 static inline u64 vgetsns(struct vdso_data *vdso)
60 {
61 	return ((get_cycles() - vdso->cycle_last) & vdso->mask) * vdso->mult;
62 }
63 
do_realtime(struct vdso_data * vdso,struct timespec * ts)64 static inline int do_realtime(struct vdso_data *vdso, struct timespec *ts)
65 {
66 	unsigned count;
67 	u64 ns;
68 
69 	do {
70 		count = raw_read_seqcount_begin(&vdso->tb_seq);
71 		ts->tv_sec = vdso->wall_time_sec;
72 		ns = vdso->wall_time_snsec;
73 		ns += vgetsns(vdso);
74 		ns >>= vdso->shift;
75 	} while (unlikely(read_seqcount_retry(&vdso->tb_seq, count)));
76 
77 	ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
78 	ts->tv_nsec = ns;
79 
80 	return 0;
81 }
82 
do_monotonic(struct vdso_data * vdso,struct timespec * ts)83 static inline int do_monotonic(struct vdso_data *vdso, struct timespec *ts)
84 {
85 	unsigned count;
86 	u64 ns;
87 
88 	do {
89 		count = raw_read_seqcount_begin(&vdso->tb_seq);
90 		ts->tv_sec = vdso->monotonic_time_sec;
91 		ns = vdso->monotonic_time_snsec;
92 		ns += vgetsns(vdso);
93 		ns >>= vdso->shift;
94 	} while (unlikely(read_seqcount_retry(&vdso->tb_seq, count)));
95 
96 	ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
97 	ts->tv_nsec = ns;
98 
99 	return 0;
100 }
101 
do_realtime_coarse(struct vdso_data * vdso,struct timespec * ts)102 static inline int do_realtime_coarse(struct vdso_data *vdso,
103 				     struct timespec *ts)
104 {
105 	unsigned count;
106 
107 	do {
108 		count = raw_read_seqcount_begin(&vdso->tb_seq);
109 		ts->tv_sec = vdso->wall_time_coarse_sec;
110 		ts->tv_nsec = vdso->wall_time_coarse_nsec;
111 	} while (unlikely(read_seqcount_retry(&vdso->tb_seq, count)));
112 
113 	return 0;
114 }
115 
do_monotonic_coarse(struct vdso_data * vdso,struct timespec * ts)116 static inline int do_monotonic_coarse(struct vdso_data *vdso,
117 				      struct timespec *ts)
118 {
119 	unsigned count;
120 
121 	do {
122 		count = raw_read_seqcount_begin(&vdso->tb_seq);
123 		ts->tv_sec = vdso->monotonic_time_coarse_sec;
124 		ts->tv_nsec = vdso->monotonic_time_coarse_nsec;
125 	} while (unlikely(read_seqcount_retry(&vdso->tb_seq, count)));
126 
127 	return 0;
128 }
129 
__vdso_gettimeofday(struct timeval * tv,struct timezone * tz)130 struct syscall_return_value __vdso_gettimeofday(struct timeval *tv,
131 						struct timezone *tz)
132 {
133 	struct syscall_return_value ret = { 0, 0 };
134 	unsigned count;
135 	struct vdso_data *vdso = (struct vdso_data *)get_datapage();
136 
137 	/* The use of the timezone is obsolete, normally tz is NULL. */
138 	if (unlikely(tz != NULL)) {
139 		do {
140 			count = raw_read_seqcount_begin(&vdso->tz_seq);
141 			tz->tz_minuteswest = vdso->tz_minuteswest;
142 			tz->tz_dsttime = vdso->tz_dsttime;
143 		} while (unlikely(read_seqcount_retry(&vdso->tz_seq, count)));
144 	}
145 
146 	if (unlikely(tv == NULL))
147 		return ret;
148 
149 	do_realtime(vdso, (struct timespec *)tv);
150 	tv->tv_usec /= 1000;
151 
152 	return ret;
153 }
154 
155 int gettimeofday(struct timeval *tv, struct timezone *tz)
156 	__attribute__((weak, alias("__vdso_gettimeofday")));
157 
vdso_fallback_gettime(long clock,struct timespec * ts)158 static struct syscall_return_value vdso_fallback_gettime(long clock,
159 							 struct timespec *ts)
160 {
161 	struct syscall_return_value ret;
162 	__asm__ __volatile__ (
163 		"swint1"
164 		: "=R00" (ret.value), "=R01" (ret.error)
165 		: "R10" (__NR_clock_gettime), "R00" (clock), "R01" (ts)
166 		: "r2", "r3", "r4", "r5", "r6", "r7",
167 		"r8",  "r9", "r11", "r12", "r13", "r14", "r15",
168 		"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
169 		"r24", "r25", "r26", "r27", "r28", "r29", "memory");
170 	return ret;
171 }
172 
__vdso_clock_gettime(clockid_t clock,struct timespec * ts)173 struct syscall_return_value __vdso_clock_gettime(clockid_t clock,
174 						 struct timespec *ts)
175 {
176 	struct vdso_data *vdso = (struct vdso_data *)get_datapage();
177 	struct syscall_return_value ret = { 0, 0 };
178 
179 	switch (clock) {
180 	case CLOCK_REALTIME:
181 		do_realtime(vdso, ts);
182 		return ret;
183 	case CLOCK_MONOTONIC:
184 		do_monotonic(vdso, ts);
185 		return ret;
186 	case CLOCK_REALTIME_COARSE:
187 		do_realtime_coarse(vdso, ts);
188 		return ret;
189 	case CLOCK_MONOTONIC_COARSE:
190 		do_monotonic_coarse(vdso, ts);
191 		return ret;
192 	default:
193 		return vdso_fallback_gettime(clock, ts);
194 	}
195 }
196 
197 int clock_gettime(clockid_t clock, struct timespec *ts)
198 	__attribute__((weak, alias("__vdso_clock_gettime")));
199