timing.c 13.3 KB
Newer Older
1 2 3
/*
 *  Portable interface to the CPU cycle counter
 *
4
 *  Copyright (C) 2006-2014, ARM Limited, All Rights Reserved
5
 *
6
 *  This file is part of mbed TLS (https://tls.mbed.org)
7
 *
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

23
#if !defined(MBEDTLS_CONFIG_FILE)
24
#include "mbedtls/config.h"
25
#else
26
#include MBEDTLS_CONFIG_FILE
27
#endif
28

29
#if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_PLATFORM_C)
30
#include "mbedtls/platform.h"
31 32
#else
#include <stdio.h>
33
#define mbedtls_printf     printf
34 35
#endif

36
#if defined(MBEDTLS_TIMING_C)
37

38
#include "mbedtls/timing.h"
39

40 41
#if !defined(MBEDTLS_TIMING_ALT)

42 43 44 45
#ifndef asm
#define asm __asm
#endif

46
#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32)
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68

#include <windows.h>
#include <winbase.h>

struct _hr_time
{
    LARGE_INTEGER start;
};

#else

#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#include <signal.h>
#include <time.h>

struct _hr_time
{
    struct timeval start;
};

69
#endif /* _WIN32 && !EFIX64 && !EFI32 */
70

71
#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&  \
72
    ( defined(_MSC_VER) && defined(_M_IX86) ) || defined(__WATCOMC__)
73

74
#define HAVE_HARDCLOCK
75

76
unsigned long mbedtls_timing_hardclock( void )
77 78 79 80 81 82
{
    unsigned long tsc;
    __asm   rdtsc
    __asm   mov  [tsc], eax
    return( tsc );
}
83
#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
84
          ( _MSC_VER && _M_IX86 ) || __WATCOMC__ */
85

86
/* some versions of mingw-64 have 32-bit longs even on x84_64 */
87
#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&  \
88 89
    defined(__GNUC__) && ( defined(__i386__) || (                       \
    ( defined(__amd64__) || defined( __x86_64__) ) && __SIZEOF_LONG__ == 4 ) )
90

91
#define HAVE_HARDCLOCK
92

93
unsigned long mbedtls_timing_hardclock( void )
94
{
95
    unsigned long lo, hi;
96
    asm volatile( "rdtsc" : "=a" (lo), "=d" (hi) );
97
    return( lo );
98
}
99
#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
100
          __GNUC__ && __i386__ */
101

102
#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&  \
103
    defined(__GNUC__) && ( defined(__amd64__) || defined(__x86_64__) )
104

105
#define HAVE_HARDCLOCK
106

107
unsigned long mbedtls_timing_hardclock( void )
108 109
{
    unsigned long lo, hi;
110
    asm volatile( "rdtsc" : "=a" (lo), "=d" (hi) );
111
    return( lo | ( hi << 32 ) );
112
}
113
#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
114
          __GNUC__ && ( __amd64__ || __x86_64__ ) */
115

116
#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&  \
117
    defined(__GNUC__) && ( defined(__powerpc__) || defined(__ppc__) )
118

119
#define HAVE_HARDCLOCK
120

121
unsigned long mbedtls_timing_hardclock( void )
122 123 124 125 126
{
    unsigned long tbl, tbu0, tbu1;

    do
    {
127 128 129
        asm volatile( "mftbu %0" : "=r" (tbu0) );
        asm volatile( "mftb  %0" : "=r" (tbl ) );
        asm volatile( "mftbu %0" : "=r" (tbu1) );
130 131 132 133 134
    }
    while( tbu0 != tbu1 );

    return( tbl );
}
135
#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
136
          __GNUC__ && ( __powerpc__ || __ppc__ ) */
137

138
#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&  \
139
    defined(__GNUC__) && defined(__sparc64__)
140

141 142
#if defined(__OpenBSD__)
#warning OpenBSD does not allow access to tick register using software version instead
143
#else
144
#define HAVE_HARDCLOCK
145

146
unsigned long mbedtls_timing_hardclock( void )
147 148
{
    unsigned long tick;
149
    asm volatile( "rdpr %%tick, %0;" : "=&r" (tick) );
150 151
    return( tick );
}
152
#endif /* __OpenBSD__ */
153
#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
154
          __GNUC__ && __sparc64__ */
155

156
#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&  \
157 158
    defined(__GNUC__) && defined(__sparc__) && !defined(__sparc64__)

159
#define HAVE_HARDCLOCK
160

161
unsigned long mbedtls_timing_hardclock( void )
162 163
{
    unsigned long tick;
164 165
    asm volatile( ".byte 0x83, 0x41, 0x00, 0x00" );
    asm volatile( "mov   %%g1, %0" : "=r" (tick) );
166 167
    return( tick );
}
168
#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
169
          __GNUC__ && __sparc__ && !__sparc64__ */
170

171
#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&      \
172 173
    defined(__GNUC__) && defined(__alpha__)

174
#define HAVE_HARDCLOCK
175

176
unsigned long mbedtls_timing_hardclock( void )
177 178
{
    unsigned long cc;
179
    asm volatile( "rpcc %0" : "=r" (cc) );
180 181
    return( cc & 0xFFFFFFFF );
}
182
#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
183
          __GNUC__ && __alpha__ */
184

185
#if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&      \
186 187
    defined(__GNUC__) && defined(__ia64__)

188
#define HAVE_HARDCLOCK
189

190
unsigned long mbedtls_timing_hardclock( void )
191 192
{
    unsigned long itc;
193
    asm volatile( "mov %0 = ar.itc" : "=r" (itc) );
194 195
    return( itc );
}
196
#endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
197
          __GNUC__ && __ia64__ */
198

199
#if !defined(HAVE_HARDCLOCK) && defined(_MSC_VER) && \
200
    !defined(EFIX64) && !defined(EFI32)
201

202
#define HAVE_HARDCLOCK
203

204
unsigned long mbedtls_timing_hardclock( void )
205 206
{
    LARGE_INTEGER offset;
207

208
    QueryPerformanceCounter( &offset );
209

210
    return( (unsigned long)( offset.QuadPart ) );
211
}
212
#endif /* !HAVE_HARDCLOCK && _MSC_VER && !EFIX64 && !EFI32 */
213

214
#if !defined(HAVE_HARDCLOCK)
215

216
#define HAVE_HARDCLOCK
217 218 219 220

static int hardclock_init = 0;
static struct timeval tv_init;

221
unsigned long mbedtls_timing_hardclock( void )
222 223 224 225 226 227 228 229 230 231 232 233 234
{
    struct timeval tv_cur;

    if( hardclock_init == 0 )
    {
        gettimeofday( &tv_init, NULL );
        hardclock_init = 1;
    }

    gettimeofday( &tv_cur, NULL );
    return( ( tv_cur.tv_sec  - tv_init.tv_sec  ) * 1000000
          + ( tv_cur.tv_usec - tv_init.tv_usec ) );
}
235
#endif /* !HAVE_HARDCLOCK */
236

237
volatile int mbedtls_timing_alarmed = 0;
238

239
#if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32)
240

241
unsigned long mbedtls_timing_get_timer( struct mbedtls_timing_hr_time *val, int reset )
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
{
    unsigned long delta;
    LARGE_INTEGER offset, hfreq;
    struct _hr_time *t = (struct _hr_time *) val;

    QueryPerformanceCounter(  &offset );
    QueryPerformanceFrequency( &hfreq );

    delta = (unsigned long)( ( 1000 *
        ( offset.QuadPart - t->start.QuadPart ) ) /
           hfreq.QuadPart );

    if( reset )
        QueryPerformanceCounter( &t->start );

    return( delta );
}

260 261 262
/* It's OK to use a global because alarm() is supposed to be global anyway */
static DWORD alarmMs;

263
static DWORD WINAPI TimerProc( LPVOID TimerContext )
264
{
265 266
    ((void) TimerContext);
    Sleep( alarmMs );
267
    mbedtls_timing_alarmed = 1;
268 269 270
    return( TRUE );
}

271
void mbedtls_set_alarm( int seconds )
272
{
273 274
    DWORD ThreadId;

275
    mbedtls_timing_alarmed = 0;
276 277
    alarmMs = seconds * 1000;
    CloseHandle( CreateThread( NULL, 0, TimerProc, NULL, 0, &ThreadId ) );
278 279
}

280
#else /* _WIN32 && !EFIX64 && !EFI32 */
281

282
unsigned long mbedtls_timing_get_timer( struct mbedtls_timing_hr_time *val, int reset )
283 284 285 286 287 288 289 290 291 292 293
{
    unsigned long delta;
    struct timeval offset;
    struct _hr_time *t = (struct _hr_time *) val;

    gettimeofday( &offset, NULL );

    if( reset )
    {
        t->start.tv_sec  = offset.tv_sec;
        t->start.tv_usec = offset.tv_usec;
294
        return( 0 );
295 296
    }

297 298 299
    delta = ( offset.tv_sec  - t->start.tv_sec  ) * 1000
          + ( offset.tv_usec - t->start.tv_usec ) / 1000;

300 301 302 303
    return( delta );
}

static void sighandler( int signum )
304
{
305
    mbedtls_timing_alarmed = 1;
306 307 308
    signal( signum, sighandler );
}

309
void mbedtls_set_alarm( int seconds )
310
{
311
    mbedtls_timing_alarmed = 0;
312 313 314 315
    signal( SIGALRM, sighandler );
    alarm( seconds );
}

316
#endif /* _WIN32 && !EFIX64 && !EFI32 */
317

318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
/*
 * Set delays to watch
 */
void mbedtls_timing_set_delay( void *data, uint32_t int_ms, uint32_t fin_ms )
{
    mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data;

    ctx->int_ms = int_ms;
    ctx->fin_ms = fin_ms;

    if( fin_ms != 0 )
        (void) mbedtls_timing_get_timer( &ctx->timer, 1 );
}

/*
 * Get number of delays expired
 */
int mbedtls_timing_get_delay( void *data )
{
    mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data;
    unsigned long elapsed_ms;

    if( ctx->fin_ms == 0 )
        return( -1 );

    elapsed_ms = mbedtls_timing_get_timer( &ctx->timer, 0 );

    if( elapsed_ms >= ctx->fin_ms )
        return( 2 );

    if( elapsed_ms >= ctx->int_ms )
        return( 1 );

    return( 0 );
}

354 355
#endif /* !MBEDTLS_TIMING_ALT */

356
#if defined(MBEDTLS_SELF_TEST)
357

358
/* To test mbedtls_net_usleep against our functions */
359
#if defined(MBEDTLS_NET_C)
360
#include "mbedtls/net.h"
361 362
#endif

363 364
/*
 * Busy-waits for the given number of milliseconds.
365
 * Used for testing mbedtls_timing_hardclock.
366 367 368
 */
static void busy_msleep( unsigned long msec )
{
369
    struct mbedtls_timing_hr_time hires;
370 371 372
    unsigned long i = 0; /* for busy-waiting */
    volatile unsigned long j; /* to prevent optimisation */

373
    (void) mbedtls_timing_get_timer( &hires, 1 );
374

375
    while( mbedtls_timing_get_timer( &hires, 0 ) < msec )
376 377 378 379 380 381
        i++;

    j = i;
    (void) j;
}

382 383 384 385 386 387 388 389
#define FAIL    do                      \
{                                       \
    if( verbose != 0 )                  \
        mbedtls_printf( "failed\n" );   \
                                        \
    return( 1 );                        \
} while( 0 )

390 391
/*
 * Checkup routine
392 393 394
 *
 * Warning: this is work in progress, some tests may not be reliable enough
 * yet! False positives may happen.
395
 */
396
int mbedtls_timing_self_test( int verbose )
397 398 399 400
{
    unsigned long cycles, ratio;
    unsigned long millisecs, secs;
    int hardfail;
401
    struct mbedtls_timing_hr_time hires;
402 403
    uint32_t a, b;
    mbedtls_timing_delay_context ctx;
404

405
    if( verbose != 0 )
406
        mbedtls_printf( "  TIMING tests note: will take some time!\n" );
407 408 409


    if( verbose != 0 )
410
        mbedtls_printf( "  TIMING test #1 (set_alarm / get_timer): " );
411 412 413

    for( secs = 1; secs <= 3; secs++ )
    {
414
        (void) mbedtls_timing_get_timer( &hires, 1 );
415

416 417
        mbedtls_set_alarm( (int) secs );
        while( !mbedtls_timing_alarmed )
418 419
            ;

420
        millisecs = mbedtls_timing_get_timer( &hires, 0 );
421 422 423 424

        if( millisecs < 900 * secs || millisecs > 1100 * secs )
        {
            if( verbose != 0 )
425
                mbedtls_printf( "failed\n" );
426 427 428 429 430 431

            return( 1 );
        }
    }

    if( verbose != 0 )
432
        mbedtls_printf( "passed\n" );
433 434

    if( verbose != 0 )
435
        mbedtls_printf( "  TIMING test #2 (hardclock / get_timer): " );
436 437 438 439 440 441 442 443 444 445 446 447

    /*
     * Allow one failure for possible counter wrapping.
     * On a 4Ghz 32-bit machine the cycle counter wraps about once per second;
     * since the whole test is about 10ms, it shouldn't happen twice in a row.
     */
    hardfail = 0;

hard_test:
    if( hardfail > 1 )
    {
        if( verbose != 0 )
448
            mbedtls_printf( "failed\n" );
449 450 451 452 453

        return( 1 );
    }

    /* Get a reference ratio cycles/ms */
454
    millisecs = 1;
455
    cycles = mbedtls_timing_hardclock();
456
    busy_msleep( millisecs );
457
    cycles = mbedtls_timing_hardclock() - cycles;
458
    ratio = cycles / millisecs;
459

460
    /* Check that the ratio is mostly constant */
461 462
    for( millisecs = 2; millisecs <= 4; millisecs++ )
    {
463
        cycles = mbedtls_timing_hardclock();
464
        busy_msleep( millisecs );
465
        cycles = mbedtls_timing_hardclock() - cycles;
466 467 468 469 470 471 472 473 474 475 476

        /* Allow variation up to 20% */
        if( cycles / millisecs < ratio - ratio / 5 ||
            cycles / millisecs > ratio + ratio / 5 )
        {
            hardfail++;
            goto hard_test;
        }
    }

    if( verbose != 0 )
477
        mbedtls_printf( "passed\n" );
478

479
    if( verbose != 0 )
480
        mbedtls_printf( "  TIMING test #3 (set/get_delay        ): " );
481 482 483 484 485 486 487

    for( a = 100; a <= 200; a += 100 )
    {
        for( b = 100; b <= 200; b += 100 )
        {
            mbedtls_timing_set_delay( &ctx, a, a + b );

488
            busy_msleep( a - a / 10 );
489 490 491
            if( mbedtls_timing_get_delay( &ctx ) != 0 )
                FAIL;

492
            busy_msleep( a / 5 );
493 494 495
            if( mbedtls_timing_get_delay( &ctx ) != 1 )
                FAIL;

496
            busy_msleep( b - a / 5 );
497 498 499
            if( mbedtls_timing_get_delay( &ctx ) != 1 )
                FAIL;

500
            busy_msleep( b / 5 );
501 502 503 504 505 506
            if( mbedtls_timing_get_delay( &ctx ) != 2 )
                FAIL;
        }
    }

    mbedtls_timing_set_delay( &ctx, 0, 0 );
507
    busy_msleep( 200 );
508 509 510 511 512 513
    if( mbedtls_timing_get_delay( &ctx ) != -1 )
        FAIL;

    if( verbose != 0 )
        mbedtls_printf( "passed\n" );

514
#if defined(MBEDTLS_NET_C)
515
    if( verbose != 0 )
516
        mbedtls_printf( "  TIMING test #4 (net_usleep/ get_timer): " );
517 518 519

    for( secs = 1; secs <= 3; secs++ )
    {
520
        (void) mbedtls_timing_get_timer( &hires, 1 );
521

522
        mbedtls_net_usleep( 500000 * secs );
523

524
        millisecs = mbedtls_timing_get_timer( &hires, 0 );
525 526 527 528

        if( millisecs < 450 * secs || millisecs > 550 * secs )
        {
            if( verbose != 0 )
529
                mbedtls_printf( "failed\n" );
530 531 532 533 534 535

            return( 1 );
        }
    }

    if( verbose != 0 )
536 537
        mbedtls_printf( "passed\n" );
#endif /* MBEDTLS_NET_C */
538

539
    if( verbose != 0 )
540
        mbedtls_printf( "\n" );
541

542 543 544
    return( 0 );
}

545
#endif /* MBEDTLS_SELF_TEST */
546

547
#endif /* MBEDTLS_TIMING_C */