1 /**
2  * \file
3  * \brief  time stamp counter related functions
4  *
5  * \author  Frank Mehnert <fm3@os.inf.tu-dresden.de>
6  * \ingroup l4util_tsc
7  */
8 
9 /*
10  * (c) 2003-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
11  *               Alexander Warg <warg@os.inf.tu-dresden.de>,
12  *               Frank Mehnert <fm3@os.inf.tu-dresden.de>,
13  *               Jork Löser <jork@os.inf.tu-dresden.de>,
14  *               Martin Pohlack <mp26@os.inf.tu-dresden.de>
15  *     economic rights: Technische Universität Dresden (Germany)
16  * This file is part of TUD:OS and distributed under the terms of the
17  * GNU Lesser General Public License 2.1.
18  * Please see the COPYING-LGPL-2.1 file for details.
19  */
20 
21 #ifndef __l4_rdtsc_h
22 #define __l4_rdtsc_h
23 
24 /**
25  * \defgroup l4util_tsc Timestamp Counter
26  * \ingroup l4util_api
27  */
28 
29 #include <l4/sys/compiler.h>
30 #include <l4/sys/l4int.h>
31 #include <l4/sys/kip.h>
32 
33 EXTERN_C_BEGIN
34 
35 /* interface */
36 /**
37  * \addtogroup l4util_tsc
38  */
39 /*@{*/
40 
41 #define L4_TSC_INIT_AUTO             0 ///< Automatic init
42 #define L4_TSC_INIT_KERNEL           1 ///< Initialized by kernel
43 #define L4_TSC_INIT_CALIBRATE        2 ///< Initialized by user-level
44 
45 extern l4_uint32_t l4_scaler_tsc_to_ns;
46 extern l4_uint32_t l4_scaler_tsc_to_us;
47 extern l4_uint32_t l4_scaler_ns_to_tsc;
48 extern l4_uint32_t l4_scaler_tsc_linux;
49 
50 /**
51  * \brief Read current value of CPU-internal time stamp counter.
52  * \return 64-bit time stamp
53  */
54 L4_INLINE l4_cpu_time_t
55 l4_rdtsc (void);
56 
57 /**
58  * \brief Read the lest significant 32 bit of the TSC.
59  *
60  * Useful for smaller differences, needs less cycles.
61  */
62 L4_INLINE
63 l4_uint32_t l4_rdtsc_32(void);
64 
65 /**
66  * \brief Return current value of CPU-internal performance measurement counter.
67  * \param  ecx          ECX value for the rdpmc instruction. For details see
68  *                      the Intel IA-32 Architectures Software Developer's
69  *                      Manual.
70  * \return 64-bit PMC */
71 L4_INLINE l4_uint64_t
72 l4_rdpmc (int ecx);
73 
74 /**
75  * \brief Return the least significant 32 bit of a performance counter.
76  *
77  * Useful for smaller differences, needs less cycles.
78  */
79 L4_INLINE
80 l4_uint32_t l4_rdpmc_32(int ecx);
81 
82 /** Convert time stamp to ns value.
83  * \param tsc time value in CPU ticks
84  * \return time value in ns
85  */
86 L4_INLINE l4_uint64_t
87 l4_tsc_to_ns (l4_cpu_time_t tsc);
88 
89 /** Convert time stamp into micro seconds value.
90  * \param tsc time value in CPU ticks
91  * \return time value in micro seconds
92  */
93 L4_INLINE l4_uint64_t
94 l4_tsc_to_us (l4_cpu_time_t tsc);
95 
96 /** Convert time stamp to s.ns value.
97  * \param tsc time value in CPU ticks
98  * \retval s seconds
99  * \retval ns nano seconds
100  */
101 L4_INLINE void
102 l4_tsc_to_s_and_ns (l4_cpu_time_t tsc, l4_uint32_t *s, l4_uint32_t *ns);
103 
104 /**
105  * \brief Convert nano seconds into CPU ticks.
106  * \param ns nano seconds
107  * \return CPU ticks
108  */
109 L4_INLINE l4_cpu_time_t
110 l4_ns_to_tsc (l4_uint64_t ns);
111 
112 /**
113  * \brief Wait busy for a small amount of time.
114  * \param ns nano seconds to wait
115  * \attention Not intended for any use!
116  */
117 L4_INLINE void
118 l4_busy_wait_ns (l4_uint64_t ns);
119 
120 /**
121  * \brief Wait busy for a small amount of time.
122  * \param us micro seconds to wait
123  * \attention Not intendet for any use!
124  */
125 L4_INLINE void
126 l4_busy_wait_us (l4_uint64_t us);
127 
128 EXTERN_C_BEGIN
129 
130 /**
131  * \brief Calibrate scalers for time stamp calculations.
132  *
133  * Determine some scalers to be able to convert between real time and CPU
134  * ticks. This test uses channel 0 of the PIT (i8254) or the kernel KIP,
135  * depending on availability.
136  * Just calls l4_tsc_init(L4_TSC_INIT_AUTO).
137  */
138 L4_INLINE l4_uint32_t
139 l4_calibrate_tsc (l4_kernel_info_t *kip);
140 
141 /**
142  * \brief Initialize scaler for TSC calibrations.
143  *
144  * Initialize the scalers needed by l4_tsc_to_ns()/l4_ns_to_tsc() and so on.
145  * Current versions of Fiasco export these scalers from kernel into userland.
146  * The programmer may decide whether he allows to use these scalers or if an
147  * calibration should be performed.
148  * \param   constraint   programmers constraint:
149  *                       - #L4_TSC_INIT_AUTO if the kernel exports the scalers
150  *                         then use them. If not, perform calibration using
151  *                         channel 0 of the PIT (i8254). The latter case may
152  *                         lead into short (unpredictable) periods where
153  *                         interrupts are disabled.
154  *                       - #L4_TSC_INIT_KERNEL depend on retrieving the scalers
155  *                         from kernel. If the scalers are not available,
156  *                         return 0.
157  *                       - #L4_TSC_INIT_CALIBRATE Ignore possible scalers
158  *                         exported by the scaler, instead insist on
159  *                         calibration using the PIT.
160  * \param kip            KIP pointer
161  * \return 0 on error (no scalers exported by kernel, calibrating failed ...)
162  *         otherwise returns (2^32 / (tsc per µsec)). This value has the
163  *         same semantics as the value returned by the calibrate_delay_loop()
164  *         function of the Linux kernel.
165  */
166 L4_CV l4_uint32_t
167 l4_tsc_init (int constraint, l4_kernel_info_t *kip);
168 
169 /**
170  * \brief Get CPU frequency in Hz
171  * \return frequency in Hz
172  */
173 L4_CV l4_uint32_t
174 l4_get_hz (void);
175 
176 /*@}*/
177 
178 EXTERN_C_END
179 
180 /* implementaion */
181 
182 L4_INLINE l4_uint32_t
l4_calibrate_tsc(l4_kernel_info_t * kip)183 l4_calibrate_tsc (l4_kernel_info_t *kip)
184 {
185   return l4_tsc_init(L4_TSC_INIT_AUTO, kip);
186 }
187 
188 L4_INLINE l4_cpu_time_t
l4_rdtsc(void)189 l4_rdtsc (void)
190 {
191     l4_cpu_time_t v;
192 
193     __asm__ __volatile__
194 	("				\n\t"
195 	 ".byte 0x0f, 0x31		\n\t"
196 	/*"rdtsc\n\t"*/
197 	:
198 	"=A" (v)
199 	: /* no inputs */
200 	);
201 
202     return v;
203 }
204 
205 /* the same, but only 32 bit. Useful for smaller differences,
206    needs less cycles. */
207 L4_INLINE
l4_rdtsc_32(void)208 l4_uint32_t l4_rdtsc_32(void)
209 {
210   l4_uint32_t x;
211 
212   __asm__ __volatile__ (
213        ".byte 0x0f, 0x31\n\t"	// rdtsc
214        : "=a" (x)
215        :
216        : "edx");
217 
218   return x;
219 }
220 
221 L4_INLINE l4_uint64_t
l4_rdpmc(int ecx)222 l4_rdpmc (int ecx)
223 {
224     l4_cpu_time_t v;
225 
226     __asm__ __volatile__ (
227 	 "rdpmc				\n\t"
228 	:
229 	"=A" (v)
230 	: "c" (ecx)
231 	);
232 
233     return v;
234 }
235 
236 /* the same, but only 32 bit. Useful for smaller differences,
237    needs less cycles. */
238 L4_INLINE
l4_rdpmc_32(int ecx)239 l4_uint32_t l4_rdpmc_32(int ecx)
240 {
241   l4_uint32_t x;
242 
243   __asm__ __volatile__ (
244        "rdpmc				\n\t"
245        : "=a" (x)
246        : "c" (ecx)
247        : "edx");
248 
249   return x;
250 }
251 
252 L4_INLINE l4_uint64_t
l4_tsc_to_ns(l4_cpu_time_t tsc)253 l4_tsc_to_ns (l4_cpu_time_t tsc)
254 {
255     l4_uint32_t dummy;
256     l4_uint64_t ns;
257     __asm__
258 	("				\n\t"
259 	 "movl  %%edx, %%ecx		\n\t"
260 	 "mull	%3			\n\t"
261 	 "movl	%%ecx, %%eax		\n\t"
262 	 "movl	%%edx, %%ecx		\n\t"
263 	 "mull	%3			\n\t"
264 	 "addl	%%ecx, %%eax		\n\t"
265 	 "adcl	$0, %%edx		\n\t"
266 	 "shld	$5, %%eax, %%edx	\n\t"
267 	 "shll	$5, %%eax		\n\t"
268 	:"=A" (ns),
269 	 "=&c" (dummy)
270 	:"0" (tsc),
271 	 "g" (l4_scaler_tsc_to_ns)
272 	);
273     return ns;
274 }
275 
276 L4_INLINE l4_uint64_t
l4_tsc_to_us(l4_cpu_time_t tsc)277 l4_tsc_to_us (l4_cpu_time_t tsc)
278 {
279     l4_uint32_t dummy;
280     l4_uint64_t us;
281     __asm__
282 	("				\n\t"
283 	 "movl  %%edx, %%ecx		\n\t"
284 	 "mull	%3			\n\t"
285 	 "movl	%%ecx, %%eax		\n\t"
286 	 "movl	%%edx, %%ecx		\n\t"
287 	 "mull	%3			\n\t"
288 	 "addl	%%ecx, %%eax		\n\t"
289 	 "adcl	$0, %%edx		\n\t"
290 	:"=A" (us),
291 	 "=&c" (dummy)
292 	:"0" (tsc),
293 	 "g" (l4_scaler_tsc_to_us)
294 	);
295     return us;
296 }
297 
298 L4_INLINE void
l4_tsc_to_s_and_ns(l4_cpu_time_t tsc,l4_uint32_t * s,l4_uint32_t * ns)299 l4_tsc_to_s_and_ns (l4_cpu_time_t tsc, l4_uint32_t *s, l4_uint32_t *ns)
300 {
301     l4_uint32_t dummy;
302     __asm__
303 	("				\n\t"
304 	 "movl  %%edx, %%ecx		\n\t"
305 	 "mull	%4			\n\t"
306 	 "movl	%%ecx, %%eax		\n\t"
307 	 "movl	%%edx, %%ecx		\n\t"
308 	 "mull	%4			\n\t"
309 	 "addl	%%ecx, %%eax		\n\t"
310 	 "adcl	$0, %%edx		\n\t"
311 	 "movl  $1000000000, %%ecx	\n\t"
312 	 "shld	$5, %%eax, %%edx	\n\t"
313 	 "shll	$5, %%eax		\n\t"
314 	 "divl  %%ecx			\n\t"
315 	:"=a" (*s), "=d" (*ns), "=&c" (dummy)
316 	: "A" (tsc), "g" (l4_scaler_tsc_to_ns)
317 	);
318 }
319 
320 L4_INLINE l4_cpu_time_t
l4_ns_to_tsc(l4_uint64_t ns)321 l4_ns_to_tsc (l4_uint64_t ns)
322 {
323     l4_uint32_t dummy;
324     l4_cpu_time_t tsc;
325     __asm__
326 	("				\n\t"
327 	 "movl  %%edx, %%ecx		\n\t"
328 	 "mull	%3			\n\t"
329 	 "movl	%%ecx, %%eax		\n\t"
330 	 "movl	%%edx, %%ecx		\n\t"
331 	 "mull	%3			\n\t"
332 	 "addl	%%ecx, %%eax		\n\t"
333 	 "adcl	$0, %%edx		\n\t"
334 	 "shld	$5, %%eax, %%edx	\n\t"
335 	 "shll	$5, %%eax		\n\t"
336 	:"=A" (tsc),
337 	 "=&c" (dummy)
338 	:"0" (ns),
339 	 "g" (l4_scaler_ns_to_tsc)
340 	);
341     return tsc;
342 }
343 
344 L4_INLINE void
l4_busy_wait_ns(l4_uint64_t ns)345 l4_busy_wait_ns (l4_uint64_t ns)
346 {
347   l4_cpu_time_t stop = l4_rdtsc();
348   stop += l4_ns_to_tsc(ns);
349 
350   while (l4_rdtsc() < stop)
351     ;
352 }
353 
354 L4_INLINE void
l4_busy_wait_us(l4_uint64_t us)355 l4_busy_wait_us (l4_uint64_t us)
356 {
357   l4_cpu_time_t stop = l4_rdtsc ();
358   stop += l4_ns_to_tsc(us*1000ULL);
359 
360   while (l4_rdtsc() < stop)
361     ;
362 }
363 
364 EXTERN_C_END
365 
366 #endif /* __l4_rdtsc_h */
367 
368