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.
Fork of mbedtls by
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 unsigned long delta; 00248 LARGE_INTEGER offset, hfreq; 00249 struct _hr_time *t = (struct _hr_time *) val; 00250 00251 QueryPerformanceCounter( &offset ); 00252 QueryPerformanceFrequency( &hfreq ); 00253 00254 delta = (unsigned long)( ( 1000 * 00255 ( offset.QuadPart - t->start.QuadPart ) ) / 00256 hfreq.QuadPart ); 00257 00258 if( reset ) 00259 QueryPerformanceCounter( &t->start ); 00260 00261 return( delta ); 00262 } 00263 00264 /* It's OK to use a global because alarm() is supposed to be global anyway */ 00265 static DWORD alarmMs; 00266 00267 static DWORD WINAPI TimerProc( LPVOID TimerContext ) 00268 { 00269 ((void) TimerContext); 00270 Sleep( alarmMs ); 00271 mbedtls_timing_alarmed = 1; 00272 return( TRUE ); 00273 } 00274 00275 void mbedtls_set_alarm( int seconds ) 00276 { 00277 DWORD ThreadId; 00278 00279 mbedtls_timing_alarmed = 0; 00280 alarmMs = seconds * 1000; 00281 CloseHandle( CreateThread( NULL, 0, TimerProc, NULL, 0, &ThreadId ) ); 00282 } 00283 00284 #else /* _WIN32 && !EFIX64 && !EFI32 */ 00285 00286 unsigned long mbedtls_timing_get_timer( struct mbedtls_timing_hr_time *val, int reset ) 00287 { 00288 unsigned long delta; 00289 struct timeval offset; 00290 struct _hr_time *t = (struct _hr_time *) val; 00291 00292 gettimeofday( &offset, NULL ); 00293 00294 if( reset ) 00295 { 00296 t->start.tv_sec = offset.tv_sec; 00297 t->start.tv_usec = offset.tv_usec; 00298 return( 0 ); 00299 } 00300 00301 delta = ( offset.tv_sec - t->start.tv_sec ) * 1000 00302 + ( offset.tv_usec - t->start.tv_usec ) / 1000; 00303 00304 return( delta ); 00305 } 00306 00307 static void sighandler( int signum ) 00308 { 00309 mbedtls_timing_alarmed = 1; 00310 signal( signum, sighandler ); 00311 } 00312 00313 void mbedtls_set_alarm( int seconds ) 00314 { 00315 mbedtls_timing_alarmed = 0; 00316 signal( SIGALRM, sighandler ); 00317 alarm( seconds ); 00318 } 00319 00320 #endif /* _WIN32 && !EFIX64 && !EFI32 */ 00321 00322 /* 00323 * Set delays to watch 00324 */ 00325 void mbedtls_timing_set_delay( void *data, uint32_t int_ms, uint32_t fin_ms ) 00326 { 00327 mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data; 00328 00329 ctx->int_ms = int_ms; 00330 ctx->fin_ms = fin_ms; 00331 00332 if( fin_ms != 0 ) 00333 (void) mbedtls_timing_get_timer( &ctx->timer, 1 ); 00334 } 00335 00336 /* 00337 * Get number of delays expired 00338 */ 00339 int mbedtls_timing_get_delay( void *data ) 00340 { 00341 mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data; 00342 unsigned long elapsed_ms; 00343 00344 if( ctx->fin_ms == 0 ) 00345 return( -1 ); 00346 00347 elapsed_ms = mbedtls_timing_get_timer( &ctx->timer, 0 ); 00348 00349 if( elapsed_ms >= ctx->fin_ms ) 00350 return( 2 ); 00351 00352 if( elapsed_ms >= ctx->int_ms ) 00353 return( 1 ); 00354 00355 return( 0 ); 00356 } 00357 00358 #endif /* !MBEDTLS_TIMING_ALT */ 00359 00360 #if defined(MBEDTLS_SELF_TEST) 00361 00362 /* 00363 * Busy-waits for the given number of milliseconds. 00364 * Used for testing mbedtls_timing_hardclock. 00365 */ 00366 static void busy_msleep( unsigned long msec ) 00367 { 00368 struct mbedtls_timing_hr_time hires; 00369 unsigned long i = 0; /* for busy-waiting */ 00370 volatile unsigned long j; /* to prevent optimisation */ 00371 00372 (void) mbedtls_timing_get_timer( &hires, 1 ); 00373 00374 while( mbedtls_timing_get_timer( &hires, 0 ) < msec ) 00375 i++; 00376 00377 j = i; 00378 (void) j; 00379 } 00380 00381 #define FAIL do \ 00382 { \ 00383 if( verbose != 0 ) \ 00384 mbedtls_printf( "failed\n" ); \ 00385 \ 00386 return( 1 ); \ 00387 } while( 0 ) 00388 00389 /* 00390 * Checkup routine 00391 * 00392 * Warning: this is work in progress, some tests may not be reliable enough 00393 * yet! False positives may happen. 00394 */ 00395 int mbedtls_timing_self_test( int verbose ) 00396 { 00397 unsigned long cycles, ratio; 00398 unsigned long millisecs, secs; 00399 int hardfail; 00400 struct mbedtls_timing_hr_time hires; 00401 uint32_t a, b; 00402 mbedtls_timing_delay_context ctx; 00403 00404 if( verbose != 0 ) 00405 mbedtls_printf( " TIMING tests note: will take some time!\n" ); 00406 00407 00408 if( verbose != 0 ) 00409 mbedtls_printf( " TIMING test #1 (set_alarm / get_timer): " ); 00410 00411 for( secs = 1; secs <= 3; secs++ ) 00412 { 00413 (void) mbedtls_timing_get_timer( &hires, 1 ); 00414 00415 mbedtls_set_alarm( (int) secs ); 00416 while( !mbedtls_timing_alarmed ) 00417 ; 00418 00419 millisecs = mbedtls_timing_get_timer( &hires, 0 ); 00420 00421 /* For some reason on Windows it looks like alarm has an extra delay 00422 * (maybe related to creating a new thread). Allow some room here. */ 00423 if( millisecs < 800 * secs || millisecs > 1200 * secs + 300 ) 00424 { 00425 if( verbose != 0 ) 00426 mbedtls_printf( "failed\n" ); 00427 00428 return( 1 ); 00429 } 00430 } 00431 00432 if( verbose != 0 ) 00433 mbedtls_printf( "passed\n" ); 00434 00435 if( verbose != 0 ) 00436 mbedtls_printf( " TIMING test #2 (set/get_delay ): " ); 00437 00438 for( a = 200; a <= 400; a += 200 ) 00439 { 00440 for( b = 200; b <= 400; b += 200 ) 00441 { 00442 mbedtls_timing_set_delay( &ctx, a, a + b ); 00443 00444 busy_msleep( a - a / 8 ); 00445 if( mbedtls_timing_get_delay( &ctx ) != 0 ) 00446 FAIL; 00447 00448 busy_msleep( a / 4 ); 00449 if( mbedtls_timing_get_delay( &ctx ) != 1 ) 00450 FAIL; 00451 00452 busy_msleep( b - a / 8 - b / 8 ); 00453 if( mbedtls_timing_get_delay( &ctx ) != 1 ) 00454 FAIL; 00455 00456 busy_msleep( b / 4 ); 00457 if( mbedtls_timing_get_delay( &ctx ) != 2 ) 00458 FAIL; 00459 } 00460 } 00461 00462 mbedtls_timing_set_delay( &ctx, 0, 0 ); 00463 busy_msleep( 200 ); 00464 if( mbedtls_timing_get_delay( &ctx ) != -1 ) 00465 FAIL; 00466 00467 if( verbose != 0 ) 00468 mbedtls_printf( "passed\n" ); 00469 00470 if( verbose != 0 ) 00471 mbedtls_printf( " TIMING test #3 (hardclock / get_timer): " ); 00472 00473 /* 00474 * Allow one failure for possible counter wrapping. 00475 * On a 4Ghz 32-bit machine the cycle counter wraps about once per second; 00476 * since the whole test is about 10ms, it shouldn't happen twice in a row. 00477 */ 00478 hardfail = 0; 00479 00480 hard_test: 00481 if( hardfail > 1 ) 00482 { 00483 if( verbose != 0 ) 00484 mbedtls_printf( "failed (ignored)\n" ); 00485 00486 goto hard_test_done; 00487 } 00488 00489 /* Get a reference ratio cycles/ms */ 00490 millisecs = 1; 00491 cycles = mbedtls_timing_hardclock(); 00492 busy_msleep( millisecs ); 00493 cycles = mbedtls_timing_hardclock() - cycles; 00494 ratio = cycles / millisecs; 00495 00496 /* Check that the ratio is mostly constant */ 00497 for( millisecs = 2; millisecs <= 4; millisecs++ ) 00498 { 00499 cycles = mbedtls_timing_hardclock(); 00500 busy_msleep( millisecs ); 00501 cycles = mbedtls_timing_hardclock() - cycles; 00502 00503 /* Allow variation up to 20% */ 00504 if( cycles / millisecs < ratio - ratio / 5 || 00505 cycles / millisecs > ratio + ratio / 5 ) 00506 { 00507 hardfail++; 00508 goto hard_test; 00509 } 00510 } 00511 00512 if( verbose != 0 ) 00513 mbedtls_printf( "passed\n" ); 00514 00515 hard_test_done: 00516 00517 if( verbose != 0 ) 00518 mbedtls_printf( "\n" ); 00519 00520 return( 0 ); 00521 } 00522 00523 #endif /* MBEDTLS_SELF_TEST */ 00524 00525 #endif /* MBEDTLS_TIMING_C */
Generated on Tue Jul 12 2022 17:25:43 by
