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