Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
timing.c
00001 /* 00002 * Portable interface to the CPU cycle counter 00003 * 00004 * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved 00005 * SPDX-License-Identifier: Apache-2.0 00006 * 00007 * Licensed under the Apache License, Version 2.0 (the "License"); you may 00008 * not use this file except in compliance with the License. 00009 * You may obtain a copy of the License at 00010 * 00011 * http://www.apache.org/licenses/LICENSE-2.0 00012 * 00013 * Unless required by applicable law or agreed to in writing, software 00014 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 00015 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00016 * See the License for the specific language governing permissions and 00017 * limitations under the License. 00018 * 00019 * This file is part of mbed TLS (https://tls.mbed.org) 00020 */ 00021 00022 #if !defined(MBEDTLS_CONFIG_FILE) 00023 #include "mbedtls/config.h" 00024 #else 00025 #include MBEDTLS_CONFIG_FILE 00026 #endif 00027 00028 #if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_PLATFORM_C) 00029 #include "mbedtls/platform.h" 00030 #else 00031 #include <stdio.h> 00032 #define mbedtls_printf printf 00033 #endif 00034 00035 #if defined(MBEDTLS_TIMING_C) 00036 00037 #include "mbedtls/timing.h" 00038 00039 #if !defined(MBEDTLS_TIMING_ALT) 00040 00041 #if !defined(unix) && !defined(__unix__) && !defined(__unix) && \ 00042 !defined(__APPLE__) && !defined(_WIN32) 00043 #error "This module only works on Unix and Windows, see MBEDTLS_TIMING_C in config.h" 00044 #endif 00045 00046 #ifndef asm 00047 #define asm __asm 00048 #endif 00049 00050 #if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) 00051 00052 #include <windows.h> 00053 #include <winbase.h> 00054 00055 struct _hr_time 00056 { 00057 LARGE_INTEGER start; 00058 }; 00059 00060 #else 00061 00062 #include <unistd.h> 00063 #include <sys/types.h> 00064 #include <sys/time.h> 00065 #include <signal.h> 00066 #include <time.h> 00067 00068 struct _hr_time 00069 { 00070 struct timeval start; 00071 }; 00072 00073 #endif /* _WIN32 && !EFIX64 && !EFI32 */ 00074 00075 #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ 00076 ( defined(_MSC_VER) && defined(_M_IX86) ) || defined(__WATCOMC__) 00077 00078 #define HAVE_HARDCLOCK 00079 00080 unsigned long mbedtls_timing_hardclock( void ) 00081 { 00082 unsigned long tsc; 00083 __asm rdtsc 00084 __asm mov [tsc], eax 00085 return( tsc ); 00086 } 00087 #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && 00088 ( _MSC_VER && _M_IX86 ) || __WATCOMC__ */ 00089 00090 /* some versions of mingw-64 have 32-bit longs even on x84_64 */ 00091 #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ 00092 defined(__GNUC__) && ( defined(__i386__) || ( \ 00093 ( defined(__amd64__) || defined( __x86_64__) ) && __SIZEOF_LONG__ == 4 ) ) 00094 00095 #define HAVE_HARDCLOCK 00096 00097 unsigned long mbedtls_timing_hardclock( void ) 00098 { 00099 unsigned long lo, hi; 00100 asm volatile( "rdtsc" : "=a" (lo), "=d" (hi) ); 00101 return( lo ); 00102 } 00103 #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && 00104 __GNUC__ && __i386__ */ 00105 00106 #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ 00107 defined(__GNUC__) && ( defined(__amd64__) || defined(__x86_64__) ) 00108 00109 #define HAVE_HARDCLOCK 00110 00111 unsigned long mbedtls_timing_hardclock( void ) 00112 { 00113 unsigned long lo, hi; 00114 asm volatile( "rdtsc" : "=a" (lo), "=d" (hi) ); 00115 return( lo | ( hi << 32 ) ); 00116 } 00117 #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && 00118 __GNUC__ && ( __amd64__ || __x86_64__ ) */ 00119 00120 #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ 00121 defined(__GNUC__) && ( defined(__powerpc__) || defined(__ppc__) ) 00122 00123 #define HAVE_HARDCLOCK 00124 00125 unsigned long mbedtls_timing_hardclock( void ) 00126 { 00127 unsigned long tbl, tbu0, tbu1; 00128 00129 do 00130 { 00131 asm volatile( "mftbu %0" : "=r" (tbu0) ); 00132 asm volatile( "mftb %0" : "=r" (tbl ) ); 00133 asm volatile( "mftbu %0" : "=r" (tbu1) ); 00134 } 00135 while( tbu0 != tbu1 ); 00136 00137 return( tbl ); 00138 } 00139 #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && 00140 __GNUC__ && ( __powerpc__ || __ppc__ ) */ 00141 00142 #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ 00143 defined(__GNUC__) && defined(__sparc64__) 00144 00145 #if defined(__OpenBSD__) 00146 #warning OpenBSD does not allow access to tick register using software version instead 00147 #else 00148 #define HAVE_HARDCLOCK 00149 00150 unsigned long mbedtls_timing_hardclock( void ) 00151 { 00152 unsigned long tick; 00153 asm volatile( "rdpr %%tick, %0;" : "=&r" (tick) ); 00154 return( tick ); 00155 } 00156 #endif /* __OpenBSD__ */ 00157 #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && 00158 __GNUC__ && __sparc64__ */ 00159 00160 #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ 00161 defined(__GNUC__) && defined(__sparc__) && !defined(__sparc64__) 00162 00163 #define HAVE_HARDCLOCK 00164 00165 unsigned long mbedtls_timing_hardclock( void ) 00166 { 00167 unsigned long tick; 00168 asm volatile( ".byte 0x83, 0x41, 0x00, 0x00" ); 00169 asm volatile( "mov %%g1, %0" : "=r" (tick) ); 00170 return( tick ); 00171 } 00172 #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && 00173 __GNUC__ && __sparc__ && !__sparc64__ */ 00174 00175 #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ 00176 defined(__GNUC__) && defined(__alpha__) 00177 00178 #define HAVE_HARDCLOCK 00179 00180 unsigned long mbedtls_timing_hardclock( void ) 00181 { 00182 unsigned long cc; 00183 asm volatile( "rpcc %0" : "=r" (cc) ); 00184 return( cc & 0xFFFFFFFF ); 00185 } 00186 #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && 00187 __GNUC__ && __alpha__ */ 00188 00189 #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ 00190 defined(__GNUC__) && defined(__ia64__) 00191 00192 #define HAVE_HARDCLOCK 00193 00194 unsigned long mbedtls_timing_hardclock( void ) 00195 { 00196 unsigned long itc; 00197 asm volatile( "mov %0 = ar.itc" : "=r" (itc) ); 00198 return( itc ); 00199 } 00200 #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && 00201 __GNUC__ && __ia64__ */ 00202 00203 #if !defined(HAVE_HARDCLOCK) && defined(_MSC_VER) && \ 00204 !defined(EFIX64) && !defined(EFI32) 00205 00206 #define HAVE_HARDCLOCK 00207 00208 unsigned long mbedtls_timing_hardclock( void ) 00209 { 00210 LARGE_INTEGER offset; 00211 00212 QueryPerformanceCounter( &offset ); 00213 00214 return( (unsigned long)( offset.QuadPart ) ); 00215 } 00216 #endif /* !HAVE_HARDCLOCK && _MSC_VER && !EFIX64 && !EFI32 */ 00217 00218 #if !defined(HAVE_HARDCLOCK) 00219 00220 #define HAVE_HARDCLOCK 00221 00222 static int hardclock_init = 0; 00223 static struct timeval tv_init; 00224 00225 unsigned long mbedtls_timing_hardclock( void ) 00226 { 00227 struct timeval tv_cur; 00228 00229 if( hardclock_init == 0 ) 00230 { 00231 gettimeofday( &tv_init, NULL ); 00232 hardclock_init = 1; 00233 } 00234 00235 gettimeofday( &tv_cur, NULL ); 00236 return( ( tv_cur.tv_sec - tv_init.tv_sec ) * 1000000 00237 + ( tv_cur.tv_usec - tv_init.tv_usec ) ); 00238 } 00239 #endif /* !HAVE_HARDCLOCK */ 00240 00241 volatile int mbedtls_timing_alarmed = 0; 00242 00243 #if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) 00244 00245 unsigned long mbedtls_timing_get_timer( struct mbedtls_timing_hr_time *val, int reset ) 00246 { 00247 struct _hr_time *t = (struct _hr_time *) val; 00248 00249 if( reset ) 00250 { 00251 QueryPerformanceCounter( &t->start ); 00252 return( 0 ); 00253 } 00254 else 00255 { 00256 unsigned long delta; 00257 LARGE_INTEGER now, hfreq; 00258 QueryPerformanceCounter( &now ); 00259 QueryPerformanceFrequency( &hfreq ); 00260 delta = (unsigned long)( ( now.QuadPart - t->start.QuadPart ) * 1000ul 00261 / hfreq.QuadPart ); 00262 return( delta ); 00263 } 00264 } 00265 00266 /* It's OK to use a global because alarm() is supposed to be global anyway */ 00267 static DWORD alarmMs; 00268 00269 static DWORD WINAPI TimerProc( LPVOID TimerContext ) 00270 { 00271 ((void) TimerContext); 00272 Sleep( alarmMs ); 00273 mbedtls_timing_alarmed = 1; 00274 return( TRUE ); 00275 } 00276 00277 void mbedtls_set_alarm( int seconds ) 00278 { 00279 DWORD ThreadId; 00280 00281 if( seconds == 0 ) 00282 { 00283 /* No need to create a thread for this simple case. 00284 * Also, this shorcut is more reliable at least on MinGW32 */ 00285 mbedtls_timing_alarmed = 1; 00286 return; 00287 } 00288 00289 mbedtls_timing_alarmed = 0; 00290 alarmMs = seconds * 1000; 00291 CloseHandle( CreateThread( NULL, 0, TimerProc, NULL, 0, &ThreadId ) ); 00292 } 00293 00294 #else /* _WIN32 && !EFIX64 && !EFI32 */ 00295 00296 unsigned long mbedtls_timing_get_timer( struct mbedtls_timing_hr_time *val, int reset ) 00297 { 00298 struct _hr_time *t = (struct _hr_time *) val; 00299 00300 if( reset ) 00301 { 00302 gettimeofday( &t->start, NULL ); 00303 return( 0 ); 00304 } 00305 else 00306 { 00307 unsigned long delta; 00308 struct timeval now; 00309 gettimeofday( &now, NULL ); 00310 delta = ( now.tv_sec - t->start.tv_sec ) * 1000ul 00311 + ( now.tv_usec - t->start.tv_usec ) / 1000; 00312 return( delta ); 00313 } 00314 } 00315 00316 static void sighandler( int signum ) 00317 { 00318 mbedtls_timing_alarmed = 1; 00319 signal( signum, sighandler ); 00320 } 00321 00322 void mbedtls_set_alarm( int seconds ) 00323 { 00324 mbedtls_timing_alarmed = 0; 00325 signal( SIGALRM, sighandler ); 00326 alarm( seconds ); 00327 if( seconds == 0 ) 00328 { 00329 /* alarm(0) cancelled any previous pending alarm, but the 00330 handler won't fire, so raise the flag straight away. */ 00331 mbedtls_timing_alarmed = 1; 00332 } 00333 } 00334 00335 #endif /* _WIN32 && !EFIX64 && !EFI32 */ 00336 00337 /* 00338 * Set delays to watch 00339 */ 00340 void mbedtls_timing_set_delay( void *data, uint32_t int_ms, uint32_t fin_ms ) 00341 { 00342 mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data; 00343 00344 ctx->int_ms = int_ms; 00345 ctx->fin_ms = fin_ms; 00346 00347 if( fin_ms != 0 ) 00348 (void) mbedtls_timing_get_timer( &ctx->timer, 1 ); 00349 } 00350 00351 /* 00352 * Get number of delays expired 00353 */ 00354 int mbedtls_timing_get_delay( void *data ) 00355 { 00356 mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data; 00357 unsigned long elapsed_ms; 00358 00359 if( ctx->fin_ms == 0 ) 00360 return( -1 ); 00361 00362 elapsed_ms = mbedtls_timing_get_timer( &ctx->timer, 0 ); 00363 00364 if( elapsed_ms >= ctx->fin_ms ) 00365 return( 2 ); 00366 00367 if( elapsed_ms >= ctx->int_ms ) 00368 return( 1 ); 00369 00370 return( 0 ); 00371 } 00372 00373 #endif /* !MBEDTLS_TIMING_ALT */ 00374 00375 #if defined(MBEDTLS_SELF_TEST) 00376 00377 /* 00378 * Busy-waits for the given number of milliseconds. 00379 * Used for testing mbedtls_timing_hardclock. 00380 */ 00381 static void busy_msleep( unsigned long msec ) 00382 { 00383 struct mbedtls_timing_hr_time hires; 00384 unsigned long i = 0; /* for busy-waiting */ 00385 volatile unsigned long j; /* to prevent optimisation */ 00386 00387 (void) mbedtls_timing_get_timer( &hires, 1 ); 00388 00389 while( mbedtls_timing_get_timer( &hires, 0 ) < msec ) 00390 i++; 00391 00392 j = i; 00393 (void) j; 00394 } 00395 00396 #define FAIL do \ 00397 { \ 00398 if( verbose != 0 ) \ 00399 { \ 00400 mbedtls_printf( "failed at line %d\n", __LINE__ ); \ 00401 mbedtls_printf( " cycles=%lu ratio=%lu millisecs=%lu secs=%lu hardfail=%d a=%lu b=%lu\n", \ 00402 cycles, ratio, millisecs, secs, hardfail, \ 00403 (unsigned long) a, (unsigned long) b ); \ 00404 mbedtls_printf( " elapsed(hires)=%lu elapsed(ctx)=%lu status(ctx)=%d\n", \ 00405 mbedtls_timing_get_timer( &hires, 0 ), \ 00406 mbedtls_timing_get_timer( &ctx.timer, 0 ), \ 00407 mbedtls_timing_get_delay( &ctx ) ); \ 00408 } \ 00409 return( 1 ); \ 00410 } while( 0 ) 00411 00412 /* 00413 * Checkup routine 00414 * 00415 * Warning: this is work in progress, some tests may not be reliable enough 00416 * yet! False positives may happen. 00417 */ 00418 int mbedtls_timing_self_test( int verbose ) 00419 { 00420 unsigned long cycles = 0, ratio = 0; 00421 unsigned long millisecs = 0, secs = 0; 00422 int hardfail = 0; 00423 struct mbedtls_timing_hr_time hires; 00424 uint32_t a = 0, b = 0; 00425 mbedtls_timing_delay_context ctx; 00426 00427 if( verbose != 0 ) 00428 mbedtls_printf( " TIMING tests note: will take some time!\n" ); 00429 00430 if( verbose != 0 ) 00431 mbedtls_printf( " TIMING test #1 (set_alarm / get_timer): " ); 00432 00433 { 00434 secs = 1; 00435 00436 (void) mbedtls_timing_get_timer( &hires, 1 ); 00437 00438 mbedtls_set_alarm( (int) secs ); 00439 while( !mbedtls_timing_alarmed ) 00440 ; 00441 00442 millisecs = mbedtls_timing_get_timer( &hires, 0 ); 00443 00444 /* For some reason on Windows it looks like alarm has an extra delay 00445 * (maybe related to creating a new thread). Allow some room here. */ 00446 if( millisecs < 800 * secs || millisecs > 1200 * secs + 300 ) 00447 FAIL; 00448 } 00449 00450 if( verbose != 0 ) 00451 mbedtls_printf( "passed\n" ); 00452 00453 if( verbose != 0 ) 00454 mbedtls_printf( " TIMING test #2 (set/get_delay ): " ); 00455 00456 { 00457 a = 800; 00458 b = 400; 00459 mbedtls_timing_set_delay( &ctx, a, a + b ); /* T = 0 */ 00460 00461 busy_msleep( a - a / 4 ); /* T = a - a/4 */ 00462 if( mbedtls_timing_get_delay( &ctx ) != 0 ) 00463 FAIL; 00464 00465 busy_msleep( a / 4 + b / 4 ); /* T = a + b/4 */ 00466 if( mbedtls_timing_get_delay( &ctx ) != 1 ) 00467 FAIL; 00468 00469 busy_msleep( b ); /* T = a + b + b/4 */ 00470 if( mbedtls_timing_get_delay( &ctx ) != 2 ) 00471 FAIL; 00472 } 00473 00474 mbedtls_timing_set_delay( &ctx, 0, 0 ); 00475 busy_msleep( 200 ); 00476 if( mbedtls_timing_get_delay( &ctx ) != -1 ) 00477 FAIL; 00478 00479 if( verbose != 0 ) 00480 mbedtls_printf( "passed\n" ); 00481 00482 if( verbose != 0 ) 00483 mbedtls_printf( " TIMING test #3 (hardclock / get_timer): " ); 00484 00485 /* 00486 * Allow one failure for possible counter wrapping. 00487 * On a 4Ghz 32-bit machine the cycle counter wraps about once per second; 00488 * since the whole test is about 10ms, it shouldn't happen twice in a row. 00489 */ 00490 00491 hard_test: 00492 if( hardfail > 1 ) 00493 { 00494 if( verbose != 0 ) 00495 mbedtls_printf( "failed (ignored)\n" ); 00496 00497 goto hard_test_done; 00498 } 00499 00500 /* Get a reference ratio cycles/ms */ 00501 millisecs = 1; 00502 cycles = mbedtls_timing_hardclock(); 00503 busy_msleep( millisecs ); 00504 cycles = mbedtls_timing_hardclock() - cycles; 00505 ratio = cycles / millisecs; 00506 00507 /* Check that the ratio is mostly constant */ 00508 for( millisecs = 2; millisecs <= 4; millisecs++ ) 00509 { 00510 cycles = mbedtls_timing_hardclock(); 00511 busy_msleep( millisecs ); 00512 cycles = mbedtls_timing_hardclock() - cycles; 00513 00514 /* Allow variation up to 20% */ 00515 if( cycles / millisecs < ratio - ratio / 5 || 00516 cycles / millisecs > ratio + ratio / 5 ) 00517 { 00518 hardfail++; 00519 goto hard_test; 00520 } 00521 } 00522 00523 if( verbose != 0 ) 00524 mbedtls_printf( "passed\n" ); 00525 00526 hard_test_done: 00527 00528 if( verbose != 0 ) 00529 mbedtls_printf( "\n" ); 00530 00531 return( 0 ); 00532 } 00533 00534 #endif /* MBEDTLS_SELF_TEST */ 00535 00536 #endif /* MBEDTLS_TIMING_C */
Generated on Tue Jul 12 2022 14:25:04 by
