2 * Portable interface to the CPU cycle counter
4 * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
5 * SPDX-License-Identifier: GPL-2.0
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 * This file is part of mbed TLS (https://tls.mbed.org)
24 #if !defined(MBEDTLS_CONFIG_FILE)
25 #include "mbedtls/config.h"
27 #include MBEDTLS_CONFIG_FILE
30 #if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_PLATFORM_C)
31 #include "mbedtls/platform.h"
34 #define mbedtls_printf printf
37 #if defined(MBEDTLS_TIMING_C)
39 #include "mbedtls/timing.h"
41 #if !defined(MBEDTLS_TIMING_ALT)
43 #if !defined(unix) && !defined(__unix__) && !defined(__unix) && \
44 !defined(__APPLE__) && !defined(_WIN32)
45 #error "This module only works on Unix and Windows, see MBEDTLS_TIMING_C in config.h"
52 #if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32)
66 #include <sys/types.h>
76 #endif /* _WIN32 && !EFIX64 && !EFI32 */
78 #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \
79 ( defined(_MSC_VER) && defined(_M_IX86) ) || defined(__WATCOMC__)
81 #define HAVE_HARDCLOCK
83 unsigned long mbedtls_timing_hardclock( void )
90 #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
91 ( _MSC_VER && _M_IX86 ) || __WATCOMC__ */
93 /* some versions of mingw-64 have 32-bit longs even on x84_64 */
94 #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \
95 defined(__GNUC__) && ( defined(__i386__) || ( \
96 ( defined(__amd64__) || defined( __x86_64__) ) && __SIZEOF_LONG__ == 4 ) )
98 #define HAVE_HARDCLOCK
100 unsigned long mbedtls_timing_hardclock( void )
102 unsigned long lo
, hi
;
103 asm volatile( "rdtsc" : "=a" (lo
), "=d" (hi
) );
106 #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
107 __GNUC__ && __i386__ */
109 #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \
110 defined(__GNUC__) && ( defined(__amd64__) || defined(__x86_64__) )
112 #define HAVE_HARDCLOCK
114 unsigned long mbedtls_timing_hardclock( void )
116 unsigned long lo
, hi
;
117 asm volatile( "rdtsc" : "=a" (lo
), "=d" (hi
) );
118 return( lo
| ( hi
<< 32 ) );
120 #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
121 __GNUC__ && ( __amd64__ || __x86_64__ ) */
123 #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \
124 defined(__GNUC__) && ( defined(__powerpc__) || defined(__ppc__) )
126 #define HAVE_HARDCLOCK
128 unsigned long mbedtls_timing_hardclock( void )
130 unsigned long tbl
, tbu0
, tbu1
;
134 asm volatile( "mftbu %0" : "=r" (tbu0
) );
135 asm volatile( "mftb %0" : "=r" (tbl
) );
136 asm volatile( "mftbu %0" : "=r" (tbu1
) );
138 while( tbu0
!= tbu1
);
142 #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
143 __GNUC__ && ( __powerpc__ || __ppc__ ) */
145 #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \
146 defined(__GNUC__) && defined(__sparc64__)
148 #if defined(__OpenBSD__)
149 #warning OpenBSD does not allow access to tick register using software version instead
151 #define HAVE_HARDCLOCK
153 unsigned long mbedtls_timing_hardclock( void )
156 asm volatile( "rdpr %%tick, %0;" : "=&r" (tick
) );
159 #endif /* __OpenBSD__ */
160 #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
161 __GNUC__ && __sparc64__ */
163 #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \
164 defined(__GNUC__) && defined(__sparc__) && !defined(__sparc64__)
166 #define HAVE_HARDCLOCK
168 unsigned long mbedtls_timing_hardclock( void )
171 asm volatile( ".byte 0x83, 0x41, 0x00, 0x00" );
172 asm volatile( "mov %%g1, %0" : "=r" (tick
) );
175 #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
176 __GNUC__ && __sparc__ && !__sparc64__ */
178 #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \
179 defined(__GNUC__) && defined(__alpha__)
181 #define HAVE_HARDCLOCK
183 unsigned long mbedtls_timing_hardclock( void )
186 asm volatile( "rpcc %0" : "=r" (cc
) );
187 return( cc
& 0xFFFFFFFF );
189 #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
190 __GNUC__ && __alpha__ */
192 #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \
193 defined(__GNUC__) && defined(__ia64__)
195 #define HAVE_HARDCLOCK
197 unsigned long mbedtls_timing_hardclock( void )
200 asm volatile( "mov %0 = ar.itc" : "=r" (itc
) );
203 #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
204 __GNUC__ && __ia64__ */
206 #if !defined(HAVE_HARDCLOCK) && defined(_MSC_VER) && \
207 !defined(EFIX64) && !defined(EFI32)
209 #define HAVE_HARDCLOCK
211 unsigned long mbedtls_timing_hardclock( void )
213 LARGE_INTEGER offset
;
215 QueryPerformanceCounter( &offset
);
217 return( (unsigned long)( offset
.QuadPart
) );
219 #endif /* !HAVE_HARDCLOCK && _MSC_VER && !EFIX64 && !EFI32 */
221 #if !defined(HAVE_HARDCLOCK)
223 #define HAVE_HARDCLOCK
225 static int hardclock_init
= 0;
226 static struct timeval tv_init
;
228 unsigned long mbedtls_timing_hardclock( void )
230 struct timeval tv_cur
;
232 if( hardclock_init
== 0 )
234 gettimeofday( &tv_init
, NULL
);
238 gettimeofday( &tv_cur
, NULL
);
239 return( ( tv_cur
.tv_sec
- tv_init
.tv_sec
) * 1000000
240 + ( tv_cur
.tv_usec
- tv_init
.tv_usec
) );
242 #endif /* !HAVE_HARDCLOCK */
244 volatile int mbedtls_timing_alarmed
= 0;
246 #if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32)
248 unsigned long mbedtls_timing_get_timer( struct mbedtls_timing_hr_time
*val
, int reset
)
250 struct _hr_time
*t
= (struct _hr_time
*) val
;
254 QueryPerformanceCounter( &t
->start
);
260 LARGE_INTEGER now
, hfreq
;
261 QueryPerformanceCounter( &now
);
262 QueryPerformanceFrequency( &hfreq
);
263 delta
= (unsigned long)( ( now
.QuadPart
- t
->start
.QuadPart
) * 1000ul
269 /* It's OK to use a global because alarm() is supposed to be global anyway */
270 static DWORD alarmMs
;
272 static void TimerProc( void *TimerContext
)
276 mbedtls_timing_alarmed
= 1;
277 /* _endthread will be called implicitly on return
278 * That ensures execution of thread funcition's epilogue */
281 void mbedtls_set_alarm( int seconds
)
285 /* No need to create a thread for this simple case.
286 * Also, this shorcut is more reliable at least on MinGW32 */
287 mbedtls_timing_alarmed
= 1;
291 mbedtls_timing_alarmed
= 0;
292 alarmMs
= seconds
* 1000;
293 (void) _beginthread( TimerProc
, 0, NULL
);
296 #else /* _WIN32 && !EFIX64 && !EFI32 */
298 unsigned long mbedtls_timing_get_timer( struct mbedtls_timing_hr_time
*val
, int reset
)
300 struct _hr_time
*t
= (struct _hr_time
*) val
;
304 gettimeofday( &t
->start
, NULL
);
311 gettimeofday( &now
, NULL
);
312 delta
= ( now
.tv_sec
- t
->start
.tv_sec
) * 1000ul
313 + ( now
.tv_usec
- t
->start
.tv_usec
) / 1000;
318 static void sighandler( int signum
)
320 mbedtls_timing_alarmed
= 1;
321 signal( signum
, sighandler
);
324 void mbedtls_set_alarm( int seconds
)
326 mbedtls_timing_alarmed
= 0;
327 signal( SIGALRM
, sighandler
);
331 /* alarm(0) cancelled any previous pending alarm, but the
332 handler won't fire, so raise the flag straight away. */
333 mbedtls_timing_alarmed
= 1;
337 #endif /* _WIN32 && !EFIX64 && !EFI32 */
340 * Set delays to watch
342 void mbedtls_timing_set_delay( void *data
, uint32_t int_ms
, uint32_t fin_ms
)
344 mbedtls_timing_delay_context
*ctx
= (mbedtls_timing_delay_context
*) data
;
346 ctx
->int_ms
= int_ms
;
347 ctx
->fin_ms
= fin_ms
;
350 (void) mbedtls_timing_get_timer( &ctx
->timer
, 1 );
354 * Get number of delays expired
356 int mbedtls_timing_get_delay( void *data
)
358 mbedtls_timing_delay_context
*ctx
= (mbedtls_timing_delay_context
*) data
;
359 unsigned long elapsed_ms
;
361 if( ctx
->fin_ms
== 0 )
364 elapsed_ms
= mbedtls_timing_get_timer( &ctx
->timer
, 0 );
366 if( elapsed_ms
>= ctx
->fin_ms
)
369 if( elapsed_ms
>= ctx
->int_ms
)
375 #endif /* !MBEDTLS_TIMING_ALT */
377 #if defined(MBEDTLS_SELF_TEST)
380 * Busy-waits for the given number of milliseconds.
381 * Used for testing mbedtls_timing_hardclock.
383 static void busy_msleep( unsigned long msec
)
385 struct mbedtls_timing_hr_time hires
;
386 unsigned long i
= 0; /* for busy-waiting */
387 volatile unsigned long j
; /* to prevent optimisation */
389 (void) mbedtls_timing_get_timer( &hires
, 1 );
391 while( mbedtls_timing_get_timer( &hires
, 0 ) < msec
)
402 mbedtls_printf( "failed at line %d\n", __LINE__ ); \
403 mbedtls_printf( " cycles=%lu ratio=%lu millisecs=%lu secs=%lu hardfail=%d a=%lu b=%lu\n", \
404 cycles, ratio, millisecs, secs, hardfail, \
405 (unsigned long) a, (unsigned long) b ); \
406 mbedtls_printf( " elapsed(hires)=%lu elapsed(ctx)=%lu status(ctx)=%d\n", \
407 mbedtls_timing_get_timer( &hires, 0 ), \
408 mbedtls_timing_get_timer( &ctx.timer, 0 ), \
409 mbedtls_timing_get_delay( &ctx ) ); \
417 * Warning: this is work in progress, some tests may not be reliable enough
418 * yet! False positives may happen.
420 int mbedtls_timing_self_test( int verbose
)
422 unsigned long cycles
= 0, ratio
= 0;
423 unsigned long millisecs
= 0, secs
= 0;
425 struct mbedtls_timing_hr_time hires
;
426 uint32_t a
= 0, b
= 0;
427 mbedtls_timing_delay_context ctx
;
430 mbedtls_printf( " TIMING tests note: will take some time!\n" );
433 mbedtls_printf( " TIMING test #1 (set_alarm / get_timer): " );
438 (void) mbedtls_timing_get_timer( &hires
, 1 );
440 mbedtls_set_alarm( (int) secs
);
441 while( !mbedtls_timing_alarmed
)
444 millisecs
= mbedtls_timing_get_timer( &hires
, 0 );
446 /* For some reason on Windows it looks like alarm has an extra delay
447 * (maybe related to creating a new thread). Allow some room here. */
448 if( millisecs
< 800 * secs
|| millisecs
> 1200 * secs
+ 300 )
453 mbedtls_printf( "passed\n" );
456 mbedtls_printf( " TIMING test #2 (set/get_delay ): " );
461 mbedtls_timing_set_delay( &ctx
, a
, a
+ b
); /* T = 0 */
463 busy_msleep( a
- a
/ 4 ); /* T = a - a/4 */
464 if( mbedtls_timing_get_delay( &ctx
) != 0 )
467 busy_msleep( a
/ 4 + b
/ 4 ); /* T = a + b/4 */
468 if( mbedtls_timing_get_delay( &ctx
) != 1 )
471 busy_msleep( b
); /* T = a + b + b/4 */
472 if( mbedtls_timing_get_delay( &ctx
) != 2 )
476 mbedtls_timing_set_delay( &ctx
, 0, 0 );
478 if( mbedtls_timing_get_delay( &ctx
) != -1 )
482 mbedtls_printf( "passed\n" );
485 mbedtls_printf( " TIMING test #3 (hardclock / get_timer): " );
488 * Allow one failure for possible counter wrapping.
489 * On a 4Ghz 32-bit machine the cycle counter wraps about once per second;
490 * since the whole test is about 10ms, it shouldn't happen twice in a row.
497 mbedtls_printf( "failed (ignored)\n" );
502 /* Get a reference ratio cycles/ms */
504 cycles
= mbedtls_timing_hardclock();
505 busy_msleep( millisecs
);
506 cycles
= mbedtls_timing_hardclock() - cycles
;
507 ratio
= cycles
/ millisecs
;
509 /* Check that the ratio is mostly constant */
510 for( millisecs
= 2; millisecs
<= 4; millisecs
++ )
512 cycles
= mbedtls_timing_hardclock();
513 busy_msleep( millisecs
);
514 cycles
= mbedtls_timing_hardclock() - cycles
;
516 /* Allow variation up to 20% */
517 if( cycles
/ millisecs
< ratio
- ratio
/ 5 ||
518 cycles
/ millisecs
> ratio
+ ratio
/ 5 )
526 mbedtls_printf( "passed\n" );
531 mbedtls_printf( "\n" );
536 #endif /* MBEDTLS_SELF_TEST */
538 #endif /* MBEDTLS_TIMING_C */