]>
Commit | Line | Data |
---|---|---|
700d8687 OM |
1 | /* |
2 | * Portable interface to the CPU cycle counter | |
3 | * | |
4 | * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved | |
5 | * SPDX-License-Identifier: GPL-2.0 | |
6 | * | |
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. | |
11 | * | |
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. | |
16 | * | |
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. | |
20 | * | |
21 | * This file is part of mbed TLS (https://tls.mbed.org) | |
22 | */ | |
23 | ||
24 | #if !defined(MBEDTLS_CONFIG_FILE) | |
25 | #include "mbedtls/config.h" | |
26 | #else | |
27 | #include MBEDTLS_CONFIG_FILE | |
28 | #endif | |
29 | ||
30 | #if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_PLATFORM_C) | |
31 | #include "mbedtls/platform.h" | |
32 | #else | |
33 | #include <stdio.h> | |
34 | #define mbedtls_printf printf | |
35 | #endif | |
36 | ||
37 | #if defined(MBEDTLS_TIMING_C) | |
38 | ||
39 | #include "mbedtls/timing.h" | |
40 | ||
41 | #if !defined(MBEDTLS_TIMING_ALT) | |
42 | ||
43 | #if !defined(unix) && !defined(__unix__) && !defined(__unix) && \ | |
44 | !defined(__APPLE__) && !defined(_WIN32) && !defined(__QNXNTO__) && \ | |
45 | !defined(__HAIKU__) | |
46 | #error "This module only works on Unix and Windows, see MBEDTLS_TIMING_C in config.h" | |
47 | #endif | |
48 | ||
49 | #ifndef asm | |
50 | #define asm __asm | |
51 | #endif | |
52 | ||
53 | #if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) | |
54 | ||
55 | #include <windows.h> | |
56 | #include <winbase.h> | |
57 | ||
58 | struct _hr_time | |
59 | { | |
60 | LARGE_INTEGER start; | |
61 | }; | |
62 | ||
63 | #else | |
64 | ||
65 | #include <unistd.h> | |
66 | #include <sys/types.h> | |
67 | #include <sys/time.h> | |
68 | #include <signal.h> | |
69 | #include <time.h> | |
70 | ||
71 | struct _hr_time | |
72 | { | |
73 | struct timeval start; | |
74 | }; | |
75 | ||
76 | #endif /* _WIN32 && !EFIX64 && !EFI32 */ | |
77 | ||
78 | #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ | |
79 | ( defined(_MSC_VER) && defined(_M_IX86) ) || defined(__WATCOMC__) | |
80 | ||
81 | #define HAVE_HARDCLOCK | |
82 | ||
83 | unsigned long mbedtls_timing_hardclock( void ) | |
84 | { | |
85 | unsigned long tsc; | |
86 | __asm rdtsc | |
87 | __asm mov [tsc], eax | |
88 | return( tsc ); | |
89 | } | |
90 | #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && | |
91 | ( _MSC_VER && _M_IX86 ) || __WATCOMC__ */ | |
92 | ||
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 ) ) | |
97 | ||
98 | #define HAVE_HARDCLOCK | |
99 | ||
100 | unsigned long mbedtls_timing_hardclock( void ) | |
101 | { | |
102 | unsigned long lo, hi; | |
103 | asm volatile( "rdtsc" : "=a" (lo), "=d" (hi) ); | |
104 | return( lo ); | |
105 | } | |
106 | #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && | |
107 | __GNUC__ && __i386__ */ | |
108 | ||
109 | #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ | |
110 | defined(__GNUC__) && ( defined(__amd64__) || defined(__x86_64__) ) | |
111 | ||
112 | #define HAVE_HARDCLOCK | |
113 | ||
114 | unsigned long mbedtls_timing_hardclock( void ) | |
115 | { | |
116 | unsigned long lo, hi; | |
117 | asm volatile( "rdtsc" : "=a" (lo), "=d" (hi) ); | |
118 | return( lo | ( hi << 32 ) ); | |
119 | } | |
120 | #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && | |
121 | __GNUC__ && ( __amd64__ || __x86_64__ ) */ | |
122 | ||
123 | #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ | |
124 | defined(__GNUC__) && ( defined(__powerpc__) || defined(__ppc__) ) | |
125 | ||
126 | #define HAVE_HARDCLOCK | |
127 | ||
128 | unsigned long mbedtls_timing_hardclock( void ) | |
129 | { | |
130 | unsigned long tbl, tbu0, tbu1; | |
131 | ||
132 | do | |
133 | { | |
134 | asm volatile( "mftbu %0" : "=r" (tbu0) ); | |
135 | asm volatile( "mftb %0" : "=r" (tbl ) ); | |
136 | asm volatile( "mftbu %0" : "=r" (tbu1) ); | |
137 | } | |
138 | while( tbu0 != tbu1 ); | |
139 | ||
140 | return( tbl ); | |
141 | } | |
142 | #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && | |
143 | __GNUC__ && ( __powerpc__ || __ppc__ ) */ | |
144 | ||
145 | #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ | |
146 | defined(__GNUC__) && defined(__sparc64__) | |
147 | ||
148 | #if defined(__OpenBSD__) | |
149 | #warning OpenBSD does not allow access to tick register using software version instead | |
150 | #else | |
151 | #define HAVE_HARDCLOCK | |
152 | ||
153 | unsigned long mbedtls_timing_hardclock( void ) | |
154 | { | |
155 | unsigned long tick; | |
156 | asm volatile( "rdpr %%tick, %0;" : "=&r" (tick) ); | |
157 | return( tick ); | |
158 | } | |
159 | #endif /* __OpenBSD__ */ | |
160 | #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && | |
161 | __GNUC__ && __sparc64__ */ | |
162 | ||
163 | #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ | |
164 | defined(__GNUC__) && defined(__sparc__) && !defined(__sparc64__) | |
165 | ||
166 | #define HAVE_HARDCLOCK | |
167 | ||
168 | unsigned long mbedtls_timing_hardclock( void ) | |
169 | { | |
170 | unsigned long tick; | |
171 | asm volatile( ".byte 0x83, 0x41, 0x00, 0x00" ); | |
172 | asm volatile( "mov %%g1, %0" : "=r" (tick) ); | |
173 | return( tick ); | |
174 | } | |
175 | #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && | |
176 | __GNUC__ && __sparc__ && !__sparc64__ */ | |
177 | ||
178 | #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ | |
179 | defined(__GNUC__) && defined(__alpha__) | |
180 | ||
181 | #define HAVE_HARDCLOCK | |
182 | ||
183 | unsigned long mbedtls_timing_hardclock( void ) | |
184 | { | |
185 | unsigned long cc; | |
186 | asm volatile( "rpcc %0" : "=r" (cc) ); | |
187 | return( cc & 0xFFFFFFFF ); | |
188 | } | |
189 | #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && | |
190 | __GNUC__ && __alpha__ */ | |
191 | ||
192 | #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ | |
193 | defined(__GNUC__) && defined(__ia64__) | |
194 | ||
195 | #define HAVE_HARDCLOCK | |
196 | ||
197 | unsigned long mbedtls_timing_hardclock( void ) | |
198 | { | |
199 | unsigned long itc; | |
200 | asm volatile( "mov %0 = ar.itc" : "=r" (itc) ); | |
201 | return( itc ); | |
202 | } | |
203 | #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && | |
204 | __GNUC__ && __ia64__ */ | |
205 | ||
206 | #if !defined(HAVE_HARDCLOCK) && defined(_MSC_VER) && \ | |
207 | !defined(EFIX64) && !defined(EFI32) | |
208 | ||
209 | #define HAVE_HARDCLOCK | |
210 | ||
211 | unsigned long mbedtls_timing_hardclock( void ) | |
212 | { | |
213 | LARGE_INTEGER offset; | |
214 | ||
215 | QueryPerformanceCounter( &offset ); | |
216 | ||
217 | return( (unsigned long)( offset.QuadPart ) ); | |
218 | } | |
219 | #endif /* !HAVE_HARDCLOCK && _MSC_VER && !EFIX64 && !EFI32 */ | |
220 | ||
221 | #if !defined(HAVE_HARDCLOCK) | |
222 | ||
223 | #define HAVE_HARDCLOCK | |
224 | ||
225 | static int hardclock_init = 0; | |
226 | static struct timeval tv_init; | |
227 | ||
228 | unsigned long mbedtls_timing_hardclock( void ) | |
229 | { | |
230 | struct timeval tv_cur; | |
231 | ||
232 | if( hardclock_init == 0 ) | |
233 | { | |
234 | #ifdef __MINGW32__ | |
235 | mingw_gettimeofday( &tv_init, NULL ); | |
236 | #else | |
237 | gettimeofday( &tv_init, NULL ); | |
238 | #endif | |
239 | hardclock_init = 1; | |
240 | } | |
241 | ||
242 | #ifdef __MINGW32__ | |
243 | mingw_gettimeofday( &tv_cur, NULL ); | |
244 | #else | |
245 | gettimeofday( &tv_cur, NULL ); | |
246 | #endif | |
247 | return( ( tv_cur.tv_sec - tv_init.tv_sec ) * 1000000 | |
248 | + ( tv_cur.tv_usec - tv_init.tv_usec ) ); | |
249 | } | |
250 | #endif /* !HAVE_HARDCLOCK */ | |
251 | ||
252 | volatile int mbedtls_timing_alarmed = 0; | |
253 | ||
254 | #if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) | |
255 | ||
256 | unsigned long mbedtls_timing_get_timer( struct mbedtls_timing_hr_time *val, int reset ) | |
257 | { | |
258 | struct _hr_time *t = (struct _hr_time *) val; | |
259 | ||
260 | if( reset ) | |
261 | { | |
262 | QueryPerformanceCounter( &t->start ); | |
263 | return( 0 ); | |
264 | } | |
265 | else | |
266 | { | |
267 | unsigned long delta; | |
268 | LARGE_INTEGER now, hfreq; | |
269 | QueryPerformanceCounter( &now ); | |
270 | QueryPerformanceFrequency( &hfreq ); | |
271 | delta = (unsigned long)( ( now.QuadPart - t->start.QuadPart ) * 1000ul | |
272 | / hfreq.QuadPart ); | |
273 | return( delta ); | |
274 | } | |
275 | } | |
276 | ||
277 | /* It's OK to use a global because alarm() is supposed to be global anyway */ | |
278 | static DWORD alarmMs; | |
279 | ||
280 | static DWORD WINAPI TimerProc( LPVOID TimerContext ) | |
281 | { | |
282 | ((void) TimerContext); | |
283 | Sleep( alarmMs ); | |
284 | mbedtls_timing_alarmed = 1; | |
285 | return( TRUE ); | |
286 | } | |
287 | ||
288 | void mbedtls_set_alarm( int seconds ) | |
289 | { | |
290 | DWORD ThreadId; | |
291 | ||
292 | if( seconds == 0 ) | |
293 | { | |
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; | |
297 | return; | |
298 | } | |
299 | ||
300 | mbedtls_timing_alarmed = 0; | |
301 | alarmMs = seconds * 1000; | |
302 | CloseHandle( CreateThread( NULL, 0, TimerProc, NULL, 0, &ThreadId ) ); | |
303 | } | |
304 | ||
305 | #else /* _WIN32 && !EFIX64 && !EFI32 */ | |
306 | ||
307 | unsigned long mbedtls_timing_get_timer( struct mbedtls_timing_hr_time *val, int reset ) | |
308 | { | |
309 | struct _hr_time *t = (struct _hr_time *) val; | |
310 | ||
311 | if( reset ) | |
312 | { | |
313 | gettimeofday( &t->start, NULL ); | |
314 | return( 0 ); | |
315 | } | |
316 | else | |
317 | { | |
318 | unsigned long delta; | |
319 | struct timeval now; | |
320 | gettimeofday( &now, NULL ); | |
321 | delta = ( now.tv_sec - t->start.tv_sec ) * 1000ul | |
322 | + ( now.tv_usec - t->start.tv_usec ) / 1000; | |
323 | return( delta ); | |
324 | } | |
325 | } | |
326 | ||
327 | static void sighandler( int signum ) | |
328 | { | |
329 | mbedtls_timing_alarmed = 1; | |
330 | signal( signum, sighandler ); | |
331 | } | |
332 | ||
333 | void mbedtls_set_alarm( int seconds ) | |
334 | { | |
335 | mbedtls_timing_alarmed = 0; | |
336 | signal( SIGALRM, sighandler ); | |
337 | alarm( seconds ); | |
338 | if( seconds == 0 ) | |
339 | { | |
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; | |
343 | } | |
344 | } | |
345 | ||
346 | #endif /* _WIN32 && !EFIX64 && !EFI32 */ | |
347 | ||
348 | /* | |
349 | * Set delays to watch | |
350 | */ | |
351 | void mbedtls_timing_set_delay( void *data, uint32_t int_ms, uint32_t fin_ms ) | |
352 | { | |
353 | mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data; | |
354 | ||
355 | ctx->int_ms = int_ms; | |
356 | ctx->fin_ms = fin_ms; | |
357 | ||
358 | if( fin_ms != 0 ) | |
359 | (void) mbedtls_timing_get_timer( &ctx->timer, 1 ); | |
360 | } | |
361 | ||
362 | /* | |
363 | * Get number of delays expired | |
364 | */ | |
365 | int mbedtls_timing_get_delay( void *data ) | |
366 | { | |
367 | mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data; | |
368 | unsigned long elapsed_ms; | |
369 | ||
370 | if( ctx->fin_ms == 0 ) | |
371 | return( -1 ); | |
372 | ||
373 | elapsed_ms = mbedtls_timing_get_timer( &ctx->timer, 0 ); | |
374 | ||
375 | if( elapsed_ms >= ctx->fin_ms ) | |
376 | return( 2 ); | |
377 | ||
378 | if( elapsed_ms >= ctx->int_ms ) | |
379 | return( 1 ); | |
380 | ||
381 | return( 0 ); | |
382 | } | |
383 | ||
384 | #endif /* !MBEDTLS_TIMING_ALT */ | |
385 | ||
386 | #if defined(MBEDTLS_SELF_TEST) | |
387 | ||
388 | /* | |
389 | * Busy-waits for the given number of milliseconds. | |
390 | * Used for testing mbedtls_timing_hardclock. | |
391 | */ | |
392 | static void busy_msleep( unsigned long msec ) | |
393 | { | |
394 | struct mbedtls_timing_hr_time hires; | |
395 | unsigned long i = 0; /* for busy-waiting */ | |
396 | volatile unsigned long j; /* to prevent optimisation */ | |
397 | ||
398 | (void) mbedtls_timing_get_timer( &hires, 1 ); | |
399 | ||
400 | while( mbedtls_timing_get_timer( &hires, 0 ) < msec ) | |
401 | i++; | |
402 | ||
403 | j = i; | |
404 | (void) j; | |
405 | } | |
406 | ||
407 | #define FAIL do \ | |
408 | { \ | |
409 | if( verbose != 0 ) \ | |
410 | { \ | |
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 ) ); \ | |
419 | } \ | |
420 | return( 1 ); \ | |
421 | } while( 0 ) | |
422 | ||
423 | /* | |
424 | * Checkup routine | |
425 | * | |
426 | * Warning: this is work in progress, some tests may not be reliable enough | |
427 | * yet! False positives may happen. | |
428 | */ | |
429 | int mbedtls_timing_self_test( int verbose ) | |
430 | { | |
431 | unsigned long cycles = 0, ratio = 0; | |
432 | unsigned long millisecs = 0, secs = 0; | |
433 | int hardfail = 0; | |
434 | struct mbedtls_timing_hr_time hires; | |
435 | uint32_t a = 0, b = 0; | |
436 | mbedtls_timing_delay_context ctx; | |
437 | ||
438 | if( verbose != 0 ) | |
439 | mbedtls_printf( " TIMING tests note: will take some time!\n" ); | |
440 | ||
441 | if( verbose != 0 ) | |
442 | mbedtls_printf( " TIMING test #1 (set_alarm / get_timer): " ); | |
443 | ||
444 | { | |
445 | secs = 1; | |
446 | ||
447 | (void) mbedtls_timing_get_timer( &hires, 1 ); | |
448 | ||
449 | mbedtls_set_alarm( (int) secs ); | |
450 | while( !mbedtls_timing_alarmed ) | |
451 | ; | |
452 | ||
453 | millisecs = mbedtls_timing_get_timer( &hires, 0 ); | |
454 | ||
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 ) | |
458 | FAIL; | |
459 | } | |
460 | ||
461 | if( verbose != 0 ) | |
462 | mbedtls_printf( "passed\n" ); | |
463 | ||
464 | if( verbose != 0 ) | |
465 | mbedtls_printf( " TIMING test #2 (set/get_delay ): " ); | |
466 | ||
467 | { | |
468 | a = 800; | |
469 | b = 400; | |
470 | mbedtls_timing_set_delay( &ctx, a, a + b ); /* T = 0 */ | |
471 | ||
472 | busy_msleep( a - a / 4 ); /* T = a - a/4 */ | |
473 | if( mbedtls_timing_get_delay( &ctx ) != 0 ) | |
474 | FAIL; | |
475 | ||
476 | busy_msleep( a / 4 + b / 4 ); /* T = a + b/4 */ | |
477 | if( mbedtls_timing_get_delay( &ctx ) != 1 ) | |
478 | FAIL; | |
479 | ||
480 | busy_msleep( b ); /* T = a + b + b/4 */ | |
481 | if( mbedtls_timing_get_delay( &ctx ) != 2 ) | |
482 | FAIL; | |
483 | } | |
484 | ||
485 | mbedtls_timing_set_delay( &ctx, 0, 0 ); | |
486 | busy_msleep( 200 ); | |
487 | if( mbedtls_timing_get_delay( &ctx ) != -1 ) | |
488 | FAIL; | |
489 | ||
490 | if( verbose != 0 ) | |
491 | mbedtls_printf( "passed\n" ); | |
492 | ||
493 | if( verbose != 0 ) | |
494 | mbedtls_printf( " TIMING test #3 (hardclock / get_timer): " ); | |
495 | ||
496 | /* | |
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. | |
500 | */ | |
501 | ||
502 | hard_test: | |
503 | if( hardfail > 1 ) | |
504 | { | |
505 | if( verbose != 0 ) | |
506 | mbedtls_printf( "failed (ignored)\n" ); | |
507 | ||
508 | goto hard_test_done; | |
509 | } | |
510 | ||
511 | /* Get a reference ratio cycles/ms */ | |
512 | millisecs = 1; | |
513 | cycles = mbedtls_timing_hardclock(); | |
514 | busy_msleep( millisecs ); | |
515 | cycles = mbedtls_timing_hardclock() - cycles; | |
516 | ratio = cycles / millisecs; | |
517 | ||
518 | /* Check that the ratio is mostly constant */ | |
519 | for( millisecs = 2; millisecs <= 4; millisecs++ ) | |
520 | { | |
521 | cycles = mbedtls_timing_hardclock(); | |
522 | busy_msleep( millisecs ); | |
523 | cycles = mbedtls_timing_hardclock() - cycles; | |
524 | ||
525 | /* Allow variation up to 20% */ | |
526 | if( cycles / millisecs < ratio - ratio / 5 || | |
527 | cycles / millisecs > ratio + ratio / 5 ) | |
528 | { | |
529 | hardfail++; | |
530 | goto hard_test; | |
531 | } | |
532 | } | |
533 | ||
534 | if( verbose != 0 ) | |
535 | mbedtls_printf( "passed\n" ); | |
536 | ||
537 | hard_test_done: | |
538 | ||
539 | if( verbose != 0 ) | |
540 | mbedtls_printf( "\n" ); | |
541 | ||
542 | return( 0 ); | |
543 | } | |
544 | ||
545 | #endif /* MBEDTLS_SELF_TEST */ | |
546 | ||
547 | #endif /* MBEDTLS_TIMING_C */ |