Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
8774 | rgimad | 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 |
||
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 |
||
56 | #include |
||
57 | |||
58 | struct _hr_time |
||
59 | { |
||
60 | LARGE_INTEGER start; |
||
61 | }; |
||
62 | |||
63 | #else |
||
64 | |||
65 | #include |
||
66 | #include |
||
67 | #include |
||
68 | #include |
||
69 | #include |
||
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 | gettimeofday( &tv_init, NULL ); |
||
235 | hardclock_init = 1; |
||
236 | } |
||
237 | |||
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 ) ); |
||
241 | } |
||
242 | #endif /* !HAVE_HARDCLOCK */ |
||
243 | |||
244 | volatile int mbedtls_timing_alarmed = 0; |
||
245 | |||
246 | #if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) |
||
247 | |||
248 | unsigned long mbedtls_timing_get_timer( struct mbedtls_timing_hr_time *val, int reset ) |
||
249 | { |
||
250 | struct _hr_time *t = (struct _hr_time *) val; |
||
251 | |||
252 | if( reset ) |
||
253 | { |
||
254 | QueryPerformanceCounter( &t->start ); |
||
255 | return( 0 ); |
||
256 | } |
||
257 | else |
||
258 | { |
||
259 | unsigned long delta; |
||
260 | LARGE_INTEGER now, hfreq; |
||
261 | QueryPerformanceCounter( &now ); |
||
262 | QueryPerformanceFrequency( &hfreq ); |
||
263 | delta = (unsigned long)( ( now.QuadPart - t->start.QuadPart ) * 1000ul |
||
264 | / hfreq.QuadPart ); |
||
265 | return( delta ); |
||
266 | } |
||
267 | } |
||
268 | |||
269 | /* It's OK to use a global because alarm() is supposed to be global anyway */ |
||
270 | static DWORD alarmMs; |
||
271 | |||
272 | static void TimerProc( void *TimerContext ) |
||
273 | { |
||
274 | (void) TimerContext; |
||
275 | Sleep( alarmMs ); |
||
276 | mbedtls_timing_alarmed = 1; |
||
277 | /* _endthread will be called implicitly on return |
||
278 | * That ensures execution of thread funcition's epilogue */ |
||
279 | } |
||
280 | |||
281 | void mbedtls_set_alarm( int seconds ) |
||
282 | { |
||
283 | if( seconds == 0 ) |
||
284 | { |
||
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; |
||
288 | return; |
||
289 | } |
||
290 | |||
291 | mbedtls_timing_alarmed = 0; |
||
292 | alarmMs = seconds * 1000; |
||
293 | (void) _beginthread( TimerProc, 0, NULL ); |
||
294 | } |
||
295 | |||
296 | #else /* _WIN32 && !EFIX64 && !EFI32 */ |
||
297 | |||
298 | unsigned long mbedtls_timing_get_timer( struct mbedtls_timing_hr_time *val, int reset ) |
||
299 | { |
||
300 | struct _hr_time *t = (struct _hr_time *) val; |
||
301 | |||
302 | if( reset ) |
||
303 | { |
||
304 | gettimeofday( &t->start, NULL ); |
||
305 | return( 0 ); |
||
306 | } |
||
307 | else |
||
308 | { |
||
309 | unsigned long delta; |
||
310 | struct timeval now; |
||
311 | gettimeofday( &now, NULL ); |
||
312 | delta = ( now.tv_sec - t->start.tv_sec ) * 1000ul |
||
313 | + ( now.tv_usec - t->start.tv_usec ) / 1000; |
||
314 | return( delta ); |
||
315 | } |
||
316 | } |
||
317 | |||
318 | static void sighandler( int signum ) |
||
319 | { |
||
320 | mbedtls_timing_alarmed = 1; |
||
321 | signal( signum, sighandler ); |
||
322 | } |
||
323 | |||
324 | void mbedtls_set_alarm( int seconds ) |
||
325 | { |
||
326 | mbedtls_timing_alarmed = 0; |
||
327 | signal( SIGALRM, sighandler ); |
||
328 | //!!!!rgimad |
||
329 | //alarm( seconds ); |
||
330 | if( seconds == 0 ) |
||
331 | { |
||
332 | /* alarm(0) cancelled any previous pending alarm, but the |
||
333 | handler won't fire, so raise the flag straight away. */ |
||
334 | mbedtls_timing_alarmed = 1; |
||
335 | } |
||
336 | } |
||
337 | |||
338 | #endif /* _WIN32 && !EFIX64 && !EFI32 */ |
||
339 | |||
340 | /* |
||
341 | * Set delays to watch |
||
342 | */ |
||
343 | void mbedtls_timing_set_delay( void *data, uint32_t int_ms, uint32_t fin_ms ) |
||
344 | { |
||
345 | mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data; |
||
346 | |||
347 | ctx->int_ms = int_ms; |
||
348 | ctx->fin_ms = fin_ms; |
||
349 | |||
350 | if( fin_ms != 0 ) |
||
351 | (void) mbedtls_timing_get_timer( &ctx->timer, 1 ); |
||
352 | } |
||
353 | |||
354 | /* |
||
355 | * Get number of delays expired |
||
356 | */ |
||
357 | int mbedtls_timing_get_delay( void *data ) |
||
358 | { |
||
359 | mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data; |
||
360 | unsigned long elapsed_ms; |
||
361 | |||
362 | if( ctx->fin_ms == 0 ) |
||
363 | return( -1 ); |
||
364 | |||
365 | elapsed_ms = mbedtls_timing_get_timer( &ctx->timer, 0 ); |
||
366 | |||
367 | if( elapsed_ms >= ctx->fin_ms ) |
||
368 | return( 2 ); |
||
369 | |||
370 | if( elapsed_ms >= ctx->int_ms ) |
||
371 | return( 1 ); |
||
372 | |||
373 | return( 0 ); |
||
374 | } |
||
375 | |||
376 | #endif /* !MBEDTLS_TIMING_ALT */ |
||
377 | |||
378 | #if defined(MBEDTLS_SELF_TEST) |
||
379 | |||
380 | /* |
||
381 | * Busy-waits for the given number of milliseconds. |
||
382 | * Used for testing mbedtls_timing_hardclock. |
||
383 | */ |
||
384 | static void busy_msleep( unsigned long msec ) |
||
385 | { |
||
386 | struct mbedtls_timing_hr_time hires; |
||
387 | unsigned long i = 0; /* for busy-waiting */ |
||
388 | volatile unsigned long j; /* to prevent optimisation */ |
||
389 | |||
390 | (void) mbedtls_timing_get_timer( &hires, 1 ); |
||
391 | |||
392 | while( mbedtls_timing_get_timer( &hires, 0 ) < msec ) |
||
393 | i++; |
||
394 | |||
395 | j = i; |
||
396 | (void) j; |
||
397 | } |
||
398 | |||
399 | #define FAIL do \ |
||
400 | { \ |
||
401 | if( verbose != 0 ) \ |
||
402 | { \ |
||
403 | mbedtls_printf( "failed at line %d\n", __LINE__ ); \ |
||
404 | mbedtls_printf( " cycles=%lu ratio=%lu millisecs=%lu secs=%lu hardfail=%d a=%lu b=%lu\n", \ |
||
405 | cycles, ratio, millisecs, secs, hardfail, \ |
||
406 | (unsigned long) a, (unsigned long) b ); \ |
||
407 | mbedtls_printf( " elapsed(hires)=%lu elapsed(ctx)=%lu status(ctx)=%d\n", \ |
||
408 | mbedtls_timing_get_timer( &hires, 0 ), \ |
||
409 | mbedtls_timing_get_timer( &ctx.timer, 0 ), \ |
||
410 | mbedtls_timing_get_delay( &ctx ) ); \ |
||
411 | } \ |
||
412 | return( 1 ); \ |
||
413 | } while( 0 ) |
||
414 | |||
415 | /* |
||
416 | * Checkup routine |
||
417 | * |
||
418 | * Warning: this is work in progress, some tests may not be reliable enough |
||
419 | * yet! False positives may happen. |
||
420 | */ |
||
421 | int mbedtls_timing_self_test( int verbose ) |
||
422 | { |
||
423 | unsigned long cycles = 0, ratio = 0; |
||
424 | unsigned long millisecs = 0, secs = 0; |
||
425 | int hardfail = 0; |
||
426 | struct mbedtls_timing_hr_time hires; |
||
427 | uint32_t a = 0, b = 0; |
||
428 | mbedtls_timing_delay_context ctx; |
||
429 | |||
430 | if( verbose != 0 ) |
||
431 | mbedtls_printf( " TIMING tests note: will take some time!\n" ); |
||
432 | |||
433 | if( verbose != 0 ) |
||
434 | mbedtls_printf( " TIMING test #1 (set_alarm / get_timer): " ); |
||
435 | |||
436 | { |
||
437 | secs = 1; |
||
438 | |||
439 | (void) mbedtls_timing_get_timer( &hires, 1 ); |
||
440 | |||
441 | mbedtls_set_alarm( (int) secs ); |
||
442 | while( !mbedtls_timing_alarmed ) |
||
443 | ; |
||
444 | |||
445 | millisecs = mbedtls_timing_get_timer( &hires, 0 ); |
||
446 | |||
447 | /* For some reason on Windows it looks like alarm has an extra delay |
||
448 | * (maybe related to creating a new thread). Allow some room here. */ |
||
449 | if( millisecs < 800 * secs || millisecs > 1200 * secs + 300 ) |
||
450 | FAIL; |
||
451 | } |
||
452 | |||
453 | if( verbose != 0 ) |
||
454 | mbedtls_printf( "passed\n" ); |
||
455 | |||
456 | if( verbose != 0 ) |
||
457 | mbedtls_printf( " TIMING test #2 (set/get_delay ): " ); |
||
458 | |||
459 | { |
||
460 | a = 800; |
||
461 | b = 400; |
||
462 | mbedtls_timing_set_delay( &ctx, a, a + b ); /* T = 0 */ |
||
463 | |||
464 | busy_msleep( a - a / 4 ); /* T = a - a/4 */ |
||
465 | if( mbedtls_timing_get_delay( &ctx ) != 0 ) |
||
466 | FAIL; |
||
467 | |||
468 | busy_msleep( a / 4 + b / 4 ); /* T = a + b/4 */ |
||
469 | if( mbedtls_timing_get_delay( &ctx ) != 1 ) |
||
470 | FAIL; |
||
471 | |||
472 | busy_msleep( b ); /* T = a + b + b/4 */ |
||
473 | if( mbedtls_timing_get_delay( &ctx ) != 2 ) |
||
474 | FAIL; |
||
475 | } |
||
476 | |||
477 | mbedtls_timing_set_delay( &ctx, 0, 0 ); |
||
478 | busy_msleep( 200 ); |
||
479 | if( mbedtls_timing_get_delay( &ctx ) != -1 ) |
||
480 | FAIL; |
||
481 | |||
482 | if( verbose != 0 ) |
||
483 | mbedtls_printf( "passed\n" ); |
||
484 | |||
485 | if( verbose != 0 ) |
||
486 | mbedtls_printf( " TIMING test #3 (hardclock / get_timer): " ); |
||
487 | |||
488 | /* |
||
489 | * Allow one failure for possible counter wrapping. |
||
490 | * On a 4Ghz 32-bit machine the cycle counter wraps about once per second; |
||
491 | * since the whole test is about 10ms, it shouldn't happen twice in a row. |
||
492 | */ |
||
493 | |||
494 | hard_test: |
||
495 | if( hardfail > 1 ) |
||
496 | { |
||
497 | if( verbose != 0 ) |
||
498 | mbedtls_printf( "failed (ignored)\n" ); |
||
499 | |||
500 | goto hard_test_done; |
||
501 | } |
||
502 | |||
503 | /* Get a reference ratio cycles/ms */ |
||
504 | millisecs = 1; |
||
505 | cycles = mbedtls_timing_hardclock(); |
||
506 | busy_msleep( millisecs ); |
||
507 | cycles = mbedtls_timing_hardclock() - cycles; |
||
508 | ratio = cycles / millisecs; |
||
509 | |||
510 | /* Check that the ratio is mostly constant */ |
||
511 | for( millisecs = 2; millisecs <= 4; millisecs++ ) |
||
512 | { |
||
513 | cycles = mbedtls_timing_hardclock(); |
||
514 | busy_msleep( millisecs ); |
||
515 | cycles = mbedtls_timing_hardclock() - cycles; |
||
516 | |||
517 | /* Allow variation up to 20% */ |
||
518 | if( cycles / millisecs < ratio - ratio / 5 || |
||
519 | cycles / millisecs > ratio + ratio / 5 ) |
||
520 | { |
||
521 | hardfail++; |
||
522 | goto hard_test; |
||
523 | } |
||
524 | } |
||
525 | |||
526 | if( verbose != 0 ) |
||
527 | mbedtls_printf( "passed\n" ); |
||
528 | |||
529 | hard_test_done: |
||
530 | |||
531 | if( verbose != 0 ) |
||
532 | mbedtls_printf( "\n" ); |
||
533 | |||
534 | return( 0 ); |
||
535 | } |
||
536 | |||
537 | #endif /* MBEDTLS_SELF_TEST */ |
||
538 | |||
539 | #endif /* MBEDTLS_TIMING_C */>=>>>><> |