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