1 // SPDX-License-Identifier: Apache-2.0 2 /* 3 * Portable interface to the CPU cycle counter 4 * 5 * Copyright (C) 2006-2015, ARM Limited, All Rights Reserved 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); you may 8 * not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 15 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 * This file is part of mbed TLS (https://tls.mbed.org) 20 */ 21 22 #if !defined(MBEDTLS_CONFIG_FILE) 23 #include "mbedtls/config.h" 24 #else 25 #include MBEDTLS_CONFIG_FILE 26 #endif 27 28 #if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_PLATFORM_C) 29 #include "mbedtls/platform.h" 30 #else 31 #include <stdio.h> 32 #define mbedtls_printf printf 33 #endif 34 35 #if defined(MBEDTLS_TIMING_C) 36 37 #include "mbedtls/timing.h" 38 39 #if !defined(MBEDTLS_TIMING_ALT) 40 41 #if !defined(unix) && !defined(__unix__) && !defined(__unix) && \ 42 !defined(__APPLE__) && !defined(_WIN32) && !defined(__QNXNTO__) && \ 43 !defined(__HAIKU__) 44 #error "This module only works on Unix and Windows, see MBEDTLS_TIMING_C in config.h" 45 #endif 46 47 #ifndef asm 48 #define asm __asm 49 #endif 50 51 #if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) 52 53 #include <windows.h> 54 #include <process.h> 55 56 struct _hr_time 57 { 58 LARGE_INTEGER start; 59 }; 60 61 #else 62 63 #include <unistd.h> 64 #include <sys/types.h> 65 #include <sys/time.h> 66 #include <signal.h> 67 #include <time.h> 68 69 struct _hr_time 70 { 71 struct timeval start; 72 }; 73 74 #endif /* _WIN32 && !EFIX64 && !EFI32 */ 75 76 #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ 77 ( defined(_MSC_VER) && defined(_M_IX86) ) || defined(__WATCOMC__) 78 79 #define HAVE_HARDCLOCK 80 81 unsigned long mbedtls_timing_hardclock( void ) 82 { 83 unsigned long tsc; 84 __asm rdtsc 85 __asm mov [tsc], eax 86 return( tsc ); 87 } 88 #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && 89 ( _MSC_VER && _M_IX86 ) || __WATCOMC__ */ 90 91 /* some versions of mingw-64 have 32-bit longs even on x84_64 */ 92 #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ 93 defined(__GNUC__) && ( defined(__i386__) || ( \ 94 ( defined(__amd64__) || defined( __x86_64__) ) && __SIZEOF_LONG__ == 4 ) ) 95 96 #define HAVE_HARDCLOCK 97 98 unsigned long mbedtls_timing_hardclock( void ) 99 { 100 unsigned long lo, hi; 101 asm volatile( "rdtsc" : "=a" (lo), "=d" (hi) ); 102 return( lo ); 103 } 104 #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && 105 __GNUC__ && __i386__ */ 106 107 #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ 108 defined(__GNUC__) && ( defined(__amd64__) || defined(__x86_64__) ) 109 110 #define HAVE_HARDCLOCK 111 112 unsigned long mbedtls_timing_hardclock( void ) 113 { 114 unsigned long lo, hi; 115 asm volatile( "rdtsc" : "=a" (lo), "=d" (hi) ); 116 return( lo | ( hi << 32 ) ); 117 } 118 #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && 119 __GNUC__ && ( __amd64__ || __x86_64__ ) */ 120 121 #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ 122 defined(__GNUC__) && ( defined(__powerpc__) || defined(__ppc__) ) 123 124 #define HAVE_HARDCLOCK 125 126 unsigned long mbedtls_timing_hardclock( void ) 127 { 128 unsigned long tbl, tbu0, tbu1; 129 130 do 131 { 132 asm volatile( "mftbu %0" : "=r" (tbu0) ); 133 asm volatile( "mftb %0" : "=r" (tbl ) ); 134 asm volatile( "mftbu %0" : "=r" (tbu1) ); 135 } 136 while( tbu0 != tbu1 ); 137 138 return( tbl ); 139 } 140 #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && 141 __GNUC__ && ( __powerpc__ || __ppc__ ) */ 142 143 #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ 144 defined(__GNUC__) && defined(__sparc64__) 145 146 #if defined(__OpenBSD__) 147 #warning OpenBSD does not allow access to tick register using software version instead 148 #else 149 #define HAVE_HARDCLOCK 150 151 unsigned long mbedtls_timing_hardclock( void ) 152 { 153 unsigned long tick; 154 asm volatile( "rdpr %%tick, %0;" : "=&r" (tick) ); 155 return( tick ); 156 } 157 #endif /* __OpenBSD__ */ 158 #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && 159 __GNUC__ && __sparc64__ */ 160 161 #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ 162 defined(__GNUC__) && defined(__sparc__) && !defined(__sparc64__) 163 164 #define HAVE_HARDCLOCK 165 166 unsigned long mbedtls_timing_hardclock( void ) 167 { 168 unsigned long tick; 169 asm volatile( ".byte 0x83, 0x41, 0x00, 0x00" ); 170 asm volatile( "mov %%g1, %0" : "=r" (tick) ); 171 return( tick ); 172 } 173 #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && 174 __GNUC__ && __sparc__ && !__sparc64__ */ 175 176 #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ 177 defined(__GNUC__) && defined(__alpha__) 178 179 #define HAVE_HARDCLOCK 180 181 unsigned long mbedtls_timing_hardclock( void ) 182 { 183 unsigned long cc; 184 asm volatile( "rpcc %0" : "=r" (cc) ); 185 return( cc & 0xFFFFFFFF ); 186 } 187 #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && 188 __GNUC__ && __alpha__ */ 189 190 #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) && \ 191 defined(__GNUC__) && defined(__ia64__) 192 193 #define HAVE_HARDCLOCK 194 195 unsigned long mbedtls_timing_hardclock( void ) 196 { 197 unsigned long itc; 198 asm volatile( "mov %0 = ar.itc" : "=r" (itc) ); 199 return( itc ); 200 } 201 #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM && 202 __GNUC__ && __ia64__ */ 203 204 #if !defined(HAVE_HARDCLOCK) && defined(_MSC_VER) && \ 205 !defined(EFIX64) && !defined(EFI32) 206 207 #define HAVE_HARDCLOCK 208 209 unsigned long mbedtls_timing_hardclock( void ) 210 { 211 LARGE_INTEGER offset; 212 213 QueryPerformanceCounter( &offset ); 214 215 return( (unsigned long)( offset.QuadPart ) ); 216 } 217 #endif /* !HAVE_HARDCLOCK && _MSC_VER && !EFIX64 && !EFI32 */ 218 219 #if !defined(HAVE_HARDCLOCK) 220 221 #define HAVE_HARDCLOCK 222 223 static int hardclock_init = 0; 224 static struct timeval tv_init; 225 226 unsigned long mbedtls_timing_hardclock( void ) 227 { 228 struct timeval tv_cur; 229 230 if( hardclock_init == 0 ) 231 { 232 gettimeofday( &tv_init, NULL ); 233 hardclock_init = 1; 234 } 235 236 gettimeofday( &tv_cur, NULL ); 237 return( ( tv_cur.tv_sec - tv_init.tv_sec ) * 1000000 238 + ( tv_cur.tv_usec - tv_init.tv_usec ) ); 239 } 240 #endif /* !HAVE_HARDCLOCK */ 241 242 volatile int mbedtls_timing_alarmed = 0; 243 244 #if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32) 245 246 unsigned long mbedtls_timing_get_timer( struct mbedtls_timing_hr_time *val, int reset ) 247 { 248 struct _hr_time *t = (struct _hr_time *) val; 249 250 if( reset ) 251 { 252 QueryPerformanceCounter( &t->start ); 253 return( 0 ); 254 } 255 else 256 { 257 unsigned long delta; 258 LARGE_INTEGER now, hfreq; 259 QueryPerformanceCounter( &now ); 260 QueryPerformanceFrequency( &hfreq ); 261 delta = (unsigned long)( ( now.QuadPart - t->start.QuadPart ) * 1000ul 262 / hfreq.QuadPart ); 263 return( delta ); 264 } 265 } 266 267 /* It's OK to use a global because alarm() is supposed to be global anyway */ 268 static DWORD alarmMs; 269 270 static void TimerProc( void *TimerContext ) 271 { 272 (void) TimerContext; 273 Sleep( alarmMs ); 274 mbedtls_timing_alarmed = 1; 275 /* _endthread will be called implicitly on return 276 * That ensures execution of thread funcition's epilogue */ 277 } 278 279 void mbedtls_set_alarm( int seconds ) 280 { 281 if( seconds == 0 ) 282 { 283 /* No need to create a thread for this simple case. 284 * Also, this shorcut is more reliable at least on MinGW32 */ 285 mbedtls_timing_alarmed = 1; 286 return; 287 } 288 289 mbedtls_timing_alarmed = 0; 290 alarmMs = seconds * 1000; 291 (void) _beginthread( TimerProc, 0, NULL ); 292 } 293 294 #else /* _WIN32 && !EFIX64 && !EFI32 */ 295 296 unsigned long mbedtls_timing_get_timer( struct mbedtls_timing_hr_time *val, int reset ) 297 { 298 struct _hr_time *t = (struct _hr_time *) val; 299 300 if( reset ) 301 { 302 gettimeofday( &t->start, NULL ); 303 return( 0 ); 304 } 305 else 306 { 307 unsigned long delta; 308 struct timeval now; 309 gettimeofday( &now, NULL ); 310 delta = ( now.tv_sec - t->start.tv_sec ) * 1000ul 311 + ( now.tv_usec - t->start.tv_usec ) / 1000; 312 return( delta ); 313 } 314 } 315 316 static void sighandler( int signum ) 317 { 318 mbedtls_timing_alarmed = 1; 319 signal( signum, sighandler ); 320 } 321 322 void mbedtls_set_alarm( int seconds ) 323 { 324 mbedtls_timing_alarmed = 0; 325 signal( SIGALRM, sighandler ); 326 alarm( seconds ); 327 if( seconds == 0 ) 328 { 329 /* alarm(0) cancelled any previous pending alarm, but the 330 handler won't fire, so raise the flag straight away. */ 331 mbedtls_timing_alarmed = 1; 332 } 333 } 334 335 #endif /* _WIN32 && !EFIX64 && !EFI32 */ 336 337 /* 338 * Set delays to watch 339 */ 340 void mbedtls_timing_set_delay( void *data, uint32_t int_ms, uint32_t fin_ms ) 341 { 342 mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data; 343 344 ctx->int_ms = int_ms; 345 ctx->fin_ms = fin_ms; 346 347 if( fin_ms != 0 ) 348 (void) mbedtls_timing_get_timer( &ctx->timer, 1 ); 349 } 350 351 /* 352 * Get number of delays expired 353 */ 354 int mbedtls_timing_get_delay( void *data ) 355 { 356 mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data; 357 unsigned long elapsed_ms; 358 359 if( ctx->fin_ms == 0 ) 360 return( -1 ); 361 362 elapsed_ms = mbedtls_timing_get_timer( &ctx->timer, 0 ); 363 364 if( elapsed_ms >= ctx->fin_ms ) 365 return( 2 ); 366 367 if( elapsed_ms >= ctx->int_ms ) 368 return( 1 ); 369 370 return( 0 ); 371 } 372 373 #endif /* !MBEDTLS_TIMING_ALT */ 374 375 #if defined(MBEDTLS_SELF_TEST) 376 377 /* 378 * Busy-waits for the given number of milliseconds. 379 * Used for testing mbedtls_timing_hardclock. 380 */ 381 static void busy_msleep( unsigned long msec ) 382 { 383 struct mbedtls_timing_hr_time hires; 384 unsigned long i = 0; /* for busy-waiting */ 385 volatile unsigned long j; /* to prevent optimisation */ 386 387 (void) mbedtls_timing_get_timer( &hires, 1 ); 388 389 while( mbedtls_timing_get_timer( &hires, 0 ) < msec ) 390 i++; 391 392 j = i; 393 (void) j; 394 } 395 396 #define FAIL do \ 397 { \ 398 if( verbose != 0 ) \ 399 { \ 400 mbedtls_printf( "failed at line %d\n", __LINE__ ); \ 401 mbedtls_printf( " cycles=%lu ratio=%lu millisecs=%lu secs=%lu hardfail=%d a=%lu b=%lu\n", \ 402 cycles, ratio, millisecs, secs, hardfail, \ 403 (unsigned long) a, (unsigned long) b ); \ 404 mbedtls_printf( " elapsed(hires)=%lu elapsed(ctx)=%lu status(ctx)=%d\n", \ 405 mbedtls_timing_get_timer( &hires, 0 ), \ 406 mbedtls_timing_get_timer( &ctx.timer, 0 ), \ 407 mbedtls_timing_get_delay( &ctx ) ); \ 408 } \ 409 return( 1 ); \ 410 } while( 0 ) 411 412 /* 413 * Checkup routine 414 * 415 * Warning: this is work in progress, some tests may not be reliable enough 416 * yet! False positives may happen. 417 */ 418 int mbedtls_timing_self_test( int verbose ) 419 { 420 unsigned long cycles = 0, ratio = 0; 421 unsigned long millisecs = 0, secs = 0; 422 int hardfail = 0; 423 struct mbedtls_timing_hr_time hires; 424 uint32_t a = 0, b = 0; 425 mbedtls_timing_delay_context ctx; 426 427 if( verbose != 0 ) 428 mbedtls_printf( " TIMING tests note: will take some time!\n" ); 429 430 if( verbose != 0 ) 431 mbedtls_printf( " TIMING test #1 (set_alarm / get_timer): " ); 432 433 { 434 secs = 1; 435 436 (void) mbedtls_timing_get_timer( &hires, 1 ); 437 438 mbedtls_set_alarm( (int) secs ); 439 while( !mbedtls_timing_alarmed ) 440 ; 441 442 millisecs = mbedtls_timing_get_timer( &hires, 0 ); 443 444 /* For some reason on Windows it looks like alarm has an extra delay 445 * (maybe related to creating a new thread). Allow some room here. */ 446 if( millisecs < 800 * secs || millisecs > 1200 * secs + 300 ) 447 FAIL; 448 } 449 450 if( verbose != 0 ) 451 mbedtls_printf( "passed\n" ); 452 453 if( verbose != 0 ) 454 mbedtls_printf( " TIMING test #2 (set/get_delay ): " ); 455 456 { 457 a = 800; 458 b = 400; 459 mbedtls_timing_set_delay( &ctx, a, a + b ); /* T = 0 */ 460 461 busy_msleep( a - a / 4 ); /* T = a - a/4 */ 462 if( mbedtls_timing_get_delay( &ctx ) != 0 ) 463 FAIL; 464 465 busy_msleep( a / 4 + b / 4 ); /* T = a + b/4 */ 466 if( mbedtls_timing_get_delay( &ctx ) != 1 ) 467 FAIL; 468 469 busy_msleep( b ); /* T = a + b + b/4 */ 470 if( mbedtls_timing_get_delay( &ctx ) != 2 ) 471 FAIL; 472 } 473 474 mbedtls_timing_set_delay( &ctx, 0, 0 ); 475 busy_msleep( 200 ); 476 if( mbedtls_timing_get_delay( &ctx ) != -1 ) 477 FAIL; 478 479 if( verbose != 0 ) 480 mbedtls_printf( "passed\n" ); 481 482 if( verbose != 0 ) 483 mbedtls_printf( " TIMING test #3 (hardclock / get_timer): " ); 484 485 /* 486 * Allow one failure for possible counter wrapping. 487 * On a 4Ghz 32-bit machine the cycle counter wraps about once per second; 488 * since the whole test is about 10ms, it shouldn't happen twice in a row. 489 */ 490 491 hard_test: 492 if( hardfail > 1 ) 493 { 494 if( verbose != 0 ) 495 mbedtls_printf( "failed (ignored)\n" ); 496 497 goto hard_test_done; 498 } 499 500 /* Get a reference ratio cycles/ms */ 501 millisecs = 1; 502 cycles = mbedtls_timing_hardclock(); 503 busy_msleep( millisecs ); 504 cycles = mbedtls_timing_hardclock() - cycles; 505 ratio = cycles / millisecs; 506 507 /* Check that the ratio is mostly constant */ 508 for( millisecs = 2; millisecs <= 4; millisecs++ ) 509 { 510 cycles = mbedtls_timing_hardclock(); 511 busy_msleep( millisecs ); 512 cycles = mbedtls_timing_hardclock() - cycles; 513 514 /* Allow variation up to 20% */ 515 if( cycles / millisecs < ratio - ratio / 5 || 516 cycles / millisecs > ratio + ratio / 5 ) 517 { 518 hardfail++; 519 goto hard_test; 520 } 521 } 522 523 if( verbose != 0 ) 524 mbedtls_printf( "passed\n" ); 525 526 hard_test_done: 527 528 if( verbose != 0 ) 529 mbedtls_printf( "\n" ); 530 531 return( 0 ); 532 } 533 534 #endif /* MBEDTLS_SELF_TEST */ 535 536 #endif /* MBEDTLS_TIMING_C */ 537