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) && !defined(__QNXNTO__) && \
46 #error "This module only works on Unix and Windows, see MBEDTLS_TIMING_C in config.h"
53 #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 )
235 mingw_gettimeofday( &tv_init
, NULL
);
237 gettimeofday( &tv_init
, NULL
);
243 mingw_gettimeofday( &tv_cur
, NULL
);
245 gettimeofday( &tv_cur
, NULL
);
247 return( ( tv_cur
.tv_sec
- tv_init
.tv_sec
) * 1000000
248 + ( tv_cur
.tv_usec
- tv_init
.tv_usec
) );
250 #endif /* !HAVE_HARDCLOCK */
252 volatile int mbedtls_timing_alarmed
= 0;
254 #if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32)
256 unsigned long mbedtls_timing_get_timer( struct mbedtls_timing_hr_time
*val
, int reset
)
258 struct _hr_time
*t
= (struct _hr_time
*) val
;
262 QueryPerformanceCounter( &t
->start
);
268 LARGE_INTEGER now
, hfreq
;
269 QueryPerformanceCounter( &now
);
270 QueryPerformanceFrequency( &hfreq
);
271 delta
= (unsigned long)( ( now
.QuadPart
- t
->start
.QuadPart
) * 1000ul
277 /* It's OK to use a global because alarm() is supposed to be global anyway */
278 static DWORD alarmMs
;
280 static DWORD WINAPI
TimerProc( LPVOID TimerContext
)
282 ((void) TimerContext
);
284 mbedtls_timing_alarmed
= 1;
288 void mbedtls_set_alarm( int seconds
)
294 /* No need to create a thread for this simple case.
295 * Also, this shorcut is more reliable at least on MinGW32 */
296 mbedtls_timing_alarmed
= 1;
300 mbedtls_timing_alarmed
= 0;
301 alarmMs
= seconds
* 1000;
302 CloseHandle( CreateThread( NULL
, 0, TimerProc
, NULL
, 0, &ThreadId
) );
305 #else /* _WIN32 && !EFIX64 && !EFI32 */
307 unsigned long mbedtls_timing_get_timer( struct mbedtls_timing_hr_time
*val
, int reset
)
309 struct _hr_time
*t
= (struct _hr_time
*) val
;
313 gettimeofday( &t
->start
, NULL
);
320 gettimeofday( &now
, NULL
);
321 delta
= ( now
.tv_sec
- t
->start
.tv_sec
) * 1000ul
322 + ( now
.tv_usec
- t
->start
.tv_usec
) / 1000;
327 static void sighandler( int signum
)
329 mbedtls_timing_alarmed
= 1;
330 signal( signum
, sighandler
);
333 void mbedtls_set_alarm( int seconds
)
335 mbedtls_timing_alarmed
= 0;
336 signal( SIGALRM
, sighandler
);
340 /* alarm(0) cancelled any previous pending alarm, but the
341 handler won't fire, so raise the flag straight away. */
342 mbedtls_timing_alarmed
= 1;
346 #endif /* _WIN32 && !EFIX64 && !EFI32 */
349 * Set delays to watch
351 void mbedtls_timing_set_delay( void *data
, uint32_t int_ms
, uint32_t fin_ms
)
353 mbedtls_timing_delay_context
*ctx
= (mbedtls_timing_delay_context
*) data
;
355 ctx
->int_ms
= int_ms
;
356 ctx
->fin_ms
= fin_ms
;
359 (void) mbedtls_timing_get_timer( &ctx
->timer
, 1 );
363 * Get number of delays expired
365 int mbedtls_timing_get_delay( void *data
)
367 mbedtls_timing_delay_context
*ctx
= (mbedtls_timing_delay_context
*) data
;
368 unsigned long elapsed_ms
;
370 if( ctx
->fin_ms
== 0 )
373 elapsed_ms
= mbedtls_timing_get_timer( &ctx
->timer
, 0 );
375 if( elapsed_ms
>= ctx
->fin_ms
)
378 if( elapsed_ms
>= ctx
->int_ms
)
384 #endif /* !MBEDTLS_TIMING_ALT */
386 #if defined(MBEDTLS_SELF_TEST)
389 * Busy-waits for the given number of milliseconds.
390 * Used for testing mbedtls_timing_hardclock.
392 static void busy_msleep( unsigned long msec
)
394 struct mbedtls_timing_hr_time hires
;
395 unsigned long i
= 0; /* for busy-waiting */
396 volatile unsigned long j
; /* to prevent optimisation */
398 (void) mbedtls_timing_get_timer( &hires
, 1 );
400 while( mbedtls_timing_get_timer( &hires
, 0 ) < msec
)
411 mbedtls_printf( "failed at line %d\n", __LINE__ ); \
412 mbedtls_printf( " cycles=%lu ratio=%lu millisecs=%lu secs=%lu hardfail=%d a=%lu b=%lu\n", \
413 cycles, ratio, millisecs, secs, hardfail, \
414 (unsigned long) a, (unsigned long) b ); \
415 mbedtls_printf( " elapsed(hires)=%lu elapsed(ctx)=%lu status(ctx)=%d\n", \
416 mbedtls_timing_get_timer( &hires, 0 ), \
417 mbedtls_timing_get_timer( &ctx.timer, 0 ), \
418 mbedtls_timing_get_delay( &ctx ) ); \
426 * Warning: this is work in progress, some tests may not be reliable enough
427 * yet! False positives may happen.
429 int mbedtls_timing_self_test( int verbose
)
431 unsigned long cycles
= 0, ratio
= 0;
432 unsigned long millisecs
= 0, secs
= 0;
434 struct mbedtls_timing_hr_time hires
;
435 uint32_t a
= 0, b
= 0;
436 mbedtls_timing_delay_context ctx
;
439 mbedtls_printf( " TIMING tests note: will take some time!\n" );
442 mbedtls_printf( " TIMING test #1 (set_alarm / get_timer): " );
447 (void) mbedtls_timing_get_timer( &hires
, 1 );
449 mbedtls_set_alarm( (int) secs
);
450 while( !mbedtls_timing_alarmed
)
453 millisecs
= mbedtls_timing_get_timer( &hires
, 0 );
455 /* For some reason on Windows it looks like alarm has an extra delay
456 * (maybe related to creating a new thread). Allow some room here. */
457 if( millisecs
< 800 * secs
|| millisecs
> 1200 * secs
+ 300 )
462 mbedtls_printf( "passed\n" );
465 mbedtls_printf( " TIMING test #2 (set/get_delay ): " );
470 mbedtls_timing_set_delay( &ctx
, a
, a
+ b
); /* T = 0 */
472 busy_msleep( a
- a
/ 4 ); /* T = a - a/4 */
473 if( mbedtls_timing_get_delay( &ctx
) != 0 )
476 busy_msleep( a
/ 4 + b
/ 4 ); /* T = a + b/4 */
477 if( mbedtls_timing_get_delay( &ctx
) != 1 )
480 busy_msleep( b
); /* T = a + b + b/4 */
481 if( mbedtls_timing_get_delay( &ctx
) != 2 )
485 mbedtls_timing_set_delay( &ctx
, 0, 0 );
487 if( mbedtls_timing_get_delay( &ctx
) != -1 )
491 mbedtls_printf( "passed\n" );
494 mbedtls_printf( " TIMING test #3 (hardclock / get_timer): " );
497 * Allow one failure for possible counter wrapping.
498 * On a 4Ghz 32-bit machine the cycle counter wraps about once per second;
499 * since the whole test is about 10ms, it shouldn't happen twice in a row.
506 mbedtls_printf( "failed (ignored)\n" );
511 /* Get a reference ratio cycles/ms */
513 cycles
= mbedtls_timing_hardclock();
514 busy_msleep( millisecs
);
515 cycles
= mbedtls_timing_hardclock() - cycles
;
516 ratio
= cycles
/ millisecs
;
518 /* Check that the ratio is mostly constant */
519 for( millisecs
= 2; millisecs
<= 4; millisecs
++ )
521 cycles
= mbedtls_timing_hardclock();
522 busy_msleep( millisecs
);
523 cycles
= mbedtls_timing_hardclock() - cycles
;
525 /* Allow variation up to 20% */
526 if( cycles
/ millisecs
< ratio
- ratio
/ 5 ||
527 cycles
/ millisecs
> ratio
+ ratio
/ 5 )
535 mbedtls_printf( "passed\n" );
540 mbedtls_printf( "\n" );
545 #endif /* MBEDTLS_SELF_TEST */
547 #endif /* MBEDTLS_TIMING_C */