timing.c 12.7 KB
Newer Older
1 2 3
/*
 *  Portable interface to the CPU cycle counter
 *
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
4
 *  Copyright (C) 2006-2014, ARM Limited, All Rights Reserved
Paul Bakker's avatar
Paul Bakker committed
5
 *
6
 *  This file is part of mbed TLS (https://tls.mbed.org)
Paul Bakker's avatar
Paul Bakker committed
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.
 */

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
23
#if !defined(MBEDTLS_CONFIG_FILE)
24
#include "mbedtls/config.h"
25
#else
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
26
#include MBEDTLS_CONFIG_FILE
27
#endif
28

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
29
#if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_PLATFORM_C)
30
#include "mbedtls/platform.h"
31 32
#else
#include <stdio.h>
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
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

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
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

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
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

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
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

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
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

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
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

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
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

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
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

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
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

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
204
unsigned long mbedtls_timing_hardclock( void )
205 206
{
    LARGE_INTEGER offset;
207

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
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;

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
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

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
237
volatile int mbedtls_timing_alarmed = 0;
238

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

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
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 )
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
264
{
265 266
    ((void) TimerContext);
    Sleep( alarmMs );
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
267
    mbedtls_timing_alarmed = 1;
268 269 270
    return( TRUE );
}

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
271
void mbedtls_set_alarm( int seconds )
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
272
{
273 274
    DWORD ThreadId;

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
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

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
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 )
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
304
{
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
305
    mbedtls_timing_alarmed = 1;
306 307 308
    signal( signum, sighandler );
}

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
309
void mbedtls_set_alarm( int seconds )
310
{
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
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 */

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
356
#if defined(MBEDTLS_SELF_TEST)
357

358 359
/*
 * Busy-waits for the given number of milliseconds.
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
360
 * Used for testing mbedtls_timing_hardclock.
361 362 363
 */
static void busy_msleep( unsigned long msec )
{
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
364
    struct mbedtls_timing_hr_time hires;
365 366 367
    unsigned long i = 0; /* for busy-waiting */
    volatile unsigned long j; /* to prevent optimisation */

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
368
    (void) mbedtls_timing_get_timer( &hires, 1 );
369

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
370
    while( mbedtls_timing_get_timer( &hires, 0 ) < msec )
371 372 373 374 375 376
        i++;

    j = i;
    (void) j;
}

377 378 379 380 381 382 383 384
#define FAIL    do                      \
{                                       \
    if( verbose != 0 )                  \
        mbedtls_printf( "failed\n" );   \
                                        \
    return( 1 );                        \
} while( 0 )

385 386
/*
 * Checkup routine
387 388 389
 *
 * Warning: this is work in progress, some tests may not be reliable enough
 * yet! False positives may happen.
390
 */
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
391
int mbedtls_timing_self_test( int verbose )
392 393 394 395
{
    unsigned long cycles, ratio;
    unsigned long millisecs, secs;
    int hardfail;
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
396
    struct mbedtls_timing_hr_time hires;
397 398
    uint32_t a, b;
    mbedtls_timing_delay_context ctx;
399

400
    if( verbose != 0 )
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
401
        mbedtls_printf( "  TIMING tests note: will take some time!\n" );
402 403 404


    if( verbose != 0 )
405
        mbedtls_printf( "  TIMING test #1 (set_alarm / get_timer): " );
406 407 408

    for( secs = 1; secs <= 3; secs++ )
    {
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
409
        (void) mbedtls_timing_get_timer( &hires, 1 );
410

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
411 412
        mbedtls_set_alarm( (int) secs );
        while( !mbedtls_timing_alarmed )
413 414
            ;

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
415
        millisecs = mbedtls_timing_get_timer( &hires, 0 );
416 417 418 419

        if( millisecs < 900 * secs || millisecs > 1100 * secs )
        {
            if( verbose != 0 )
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
420
                mbedtls_printf( "failed\n" );
421 422 423 424 425 426

            return( 1 );
        }
    }

    if( verbose != 0 )
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
427
        mbedtls_printf( "passed\n" );
428 429

    if( verbose != 0 )
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
        mbedtls_printf( "  TIMING test #2 (set/get_delay        ): " );

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

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

            busy_msleep( a / 5 );
            if( mbedtls_timing_get_delay( &ctx ) != 1 )
                FAIL;

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

            busy_msleep( b / 5 );
            if( mbedtls_timing_get_delay( &ctx ) != 2 )
                FAIL;
        }
    }

    mbedtls_timing_set_delay( &ctx, 0, 0 );
    busy_msleep( 200 );
    if( mbedtls_timing_get_delay( &ctx ) != -1 )
        FAIL;

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

    if( verbose != 0 )
        mbedtls_printf( "  TIMING test #3 (hardclock / get_timer): " );
466 467 468 469 470 471 472 473 474 475 476 477

    /*
     * 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 )
478
            mbedtls_printf( "failed (ignored)\n" );
479

480
        goto hard_test_done;
481 482 483
    }

    /* Get a reference ratio cycles/ms */
484
    millisecs = 1;
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
485
    cycles = mbedtls_timing_hardclock();
486
    busy_msleep( millisecs );
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
487
    cycles = mbedtls_timing_hardclock() - cycles;
488
    ratio = cycles / millisecs;
489

490
    /* Check that the ratio is mostly constant */
491 492
    for( millisecs = 2; millisecs <= 4; millisecs++ )
    {
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
493
        cycles = mbedtls_timing_hardclock();
494
        busy_msleep( millisecs );
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
495
        cycles = mbedtls_timing_hardclock() - cycles;
496 497 498 499 500 501 502 503 504 505 506

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

    if( verbose != 0 )
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
507
        mbedtls_printf( "passed\n" );
508

509
hard_test_done:
510

511
    if( verbose != 0 )
Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
512
        mbedtls_printf( "\n" );
513

514 515 516
    return( 0 );
}

Manuel Pégourié-Gonnard's avatar
Manuel Pégourié-Gonnard committed
517
#endif /* MBEDTLS_SELF_TEST */
518

519
#endif /* MBEDTLS_TIMING_C */