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