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.
Dependents: TYBLE16_simple_data_logger TYBLE16_MP3_Air
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 <process.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 void TimerProc( void *TimerContext ) 00271 { 00272 (void) TimerContext; 00273 Sleep( alarmMs ); 00274 mbedtls_timing_alarmed = 1; 00275 /* _endthread will be called implicitly on return 00276 * That ensures execution of thread funcition's epilogue */ 00277 } 00278 00279 void mbedtls_set_alarm( int seconds ) 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 (void) _beginthread( TimerProc, 0, NULL ); 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 13:55:00 by
