xref: /optee_os/lib/libmbedtls/mbedtls/library/timing.c (revision c6672fdcd95b9a895eb5b4191f8ba3483a34a442)
1*c6672fdcSEdison Ai // SPDX-License-Identifier: Apache-2.0
2817466cbSJens Wiklander /*
3817466cbSJens Wiklander  *  Portable interface to the CPU cycle counter
4817466cbSJens Wiklander  *
5817466cbSJens Wiklander  *  Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
6817466cbSJens Wiklander  *
7817466cbSJens Wiklander  *  Licensed under the Apache License, Version 2.0 (the "License"); you may
8817466cbSJens Wiklander  *  not use this file except in compliance with the License.
9817466cbSJens Wiklander  *  You may obtain a copy of the License at
10817466cbSJens Wiklander  *
11817466cbSJens Wiklander  *  http://www.apache.org/licenses/LICENSE-2.0
12817466cbSJens Wiklander  *
13817466cbSJens Wiklander  *  Unless required by applicable law or agreed to in writing, software
14817466cbSJens Wiklander  *  distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15817466cbSJens Wiklander  *  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16817466cbSJens Wiklander  *  See the License for the specific language governing permissions and
17817466cbSJens Wiklander  *  limitations under the License.
18817466cbSJens Wiklander  *
19817466cbSJens Wiklander  *  This file is part of mbed TLS (https://tls.mbed.org)
20817466cbSJens Wiklander  */
21817466cbSJens Wiklander 
22817466cbSJens Wiklander #if !defined(MBEDTLS_CONFIG_FILE)
23817466cbSJens Wiklander #include "mbedtls/config.h"
24817466cbSJens Wiklander #else
25817466cbSJens Wiklander #include MBEDTLS_CONFIG_FILE
26817466cbSJens Wiklander #endif
27817466cbSJens Wiklander 
28817466cbSJens Wiklander #if defined(MBEDTLS_SELF_TEST) && defined(MBEDTLS_PLATFORM_C)
29817466cbSJens Wiklander #include "mbedtls/platform.h"
30817466cbSJens Wiklander #else
31817466cbSJens Wiklander #include <stdio.h>
32817466cbSJens Wiklander #define mbedtls_printf     printf
33817466cbSJens Wiklander #endif
34817466cbSJens Wiklander 
35817466cbSJens Wiklander #if defined(MBEDTLS_TIMING_C)
36817466cbSJens Wiklander 
37817466cbSJens Wiklander #include "mbedtls/timing.h"
38817466cbSJens Wiklander 
39817466cbSJens Wiklander #if !defined(MBEDTLS_TIMING_ALT)
40817466cbSJens Wiklander 
41817466cbSJens Wiklander #if !defined(unix) && !defined(__unix__) && !defined(__unix) && \
42817466cbSJens Wiklander     !defined(__APPLE__) && !defined(_WIN32)
43817466cbSJens Wiklander #error "This module only works on Unix and Windows, see MBEDTLS_TIMING_C in config.h"
44817466cbSJens Wiklander #endif
45817466cbSJens Wiklander 
46817466cbSJens Wiklander #ifndef asm
47817466cbSJens Wiklander #define asm __asm
48817466cbSJens Wiklander #endif
49817466cbSJens Wiklander 
50817466cbSJens Wiklander #if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32)
51817466cbSJens Wiklander 
52817466cbSJens Wiklander #include <windows.h>
53817466cbSJens Wiklander #include <winbase.h>
54817466cbSJens Wiklander 
55817466cbSJens Wiklander struct _hr_time
56817466cbSJens Wiklander {
57817466cbSJens Wiklander     LARGE_INTEGER start;
58817466cbSJens Wiklander };
59817466cbSJens Wiklander 
60817466cbSJens Wiklander #else
61817466cbSJens Wiklander 
62817466cbSJens Wiklander #include <unistd.h>
63817466cbSJens Wiklander #include <sys/types.h>
64817466cbSJens Wiklander #include <sys/time.h>
65817466cbSJens Wiklander #include <signal.h>
66817466cbSJens Wiklander #include <time.h>
67817466cbSJens Wiklander 
68817466cbSJens Wiklander struct _hr_time
69817466cbSJens Wiklander {
70817466cbSJens Wiklander     struct timeval start;
71817466cbSJens Wiklander };
72817466cbSJens Wiklander 
73817466cbSJens Wiklander #endif /* _WIN32 && !EFIX64 && !EFI32 */
74817466cbSJens Wiklander 
75817466cbSJens Wiklander #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&  \
76817466cbSJens Wiklander     ( defined(_MSC_VER) && defined(_M_IX86) ) || defined(__WATCOMC__)
77817466cbSJens Wiklander 
78817466cbSJens Wiklander #define HAVE_HARDCLOCK
79817466cbSJens Wiklander 
80817466cbSJens Wiklander unsigned long mbedtls_timing_hardclock( void )
81817466cbSJens Wiklander {
82817466cbSJens Wiklander     unsigned long tsc;
83817466cbSJens Wiklander     __asm   rdtsc
84817466cbSJens Wiklander     __asm   mov  [tsc], eax
85817466cbSJens Wiklander     return( tsc );
86817466cbSJens Wiklander }
87817466cbSJens Wiklander #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
88817466cbSJens Wiklander           ( _MSC_VER && _M_IX86 ) || __WATCOMC__ */
89817466cbSJens Wiklander 
90817466cbSJens Wiklander /* some versions of mingw-64 have 32-bit longs even on x84_64 */
91817466cbSJens Wiklander #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&  \
92817466cbSJens Wiklander     defined(__GNUC__) && ( defined(__i386__) || (                       \
93817466cbSJens Wiklander     ( defined(__amd64__) || defined( __x86_64__) ) && __SIZEOF_LONG__ == 4 ) )
94817466cbSJens Wiklander 
95817466cbSJens Wiklander #define HAVE_HARDCLOCK
96817466cbSJens Wiklander 
97817466cbSJens Wiklander unsigned long mbedtls_timing_hardclock( void )
98817466cbSJens Wiklander {
99817466cbSJens Wiklander     unsigned long lo, hi;
100817466cbSJens Wiklander     asm volatile( "rdtsc" : "=a" (lo), "=d" (hi) );
101817466cbSJens Wiklander     return( lo );
102817466cbSJens Wiklander }
103817466cbSJens Wiklander #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
104817466cbSJens Wiklander           __GNUC__ && __i386__ */
105817466cbSJens Wiklander 
106817466cbSJens Wiklander #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&  \
107817466cbSJens Wiklander     defined(__GNUC__) && ( defined(__amd64__) || defined(__x86_64__) )
108817466cbSJens Wiklander 
109817466cbSJens Wiklander #define HAVE_HARDCLOCK
110817466cbSJens Wiklander 
111817466cbSJens Wiklander unsigned long mbedtls_timing_hardclock( void )
112817466cbSJens Wiklander {
113817466cbSJens Wiklander     unsigned long lo, hi;
114817466cbSJens Wiklander     asm volatile( "rdtsc" : "=a" (lo), "=d" (hi) );
115817466cbSJens Wiklander     return( lo | ( hi << 32 ) );
116817466cbSJens Wiklander }
117817466cbSJens Wiklander #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
118817466cbSJens Wiklander           __GNUC__ && ( __amd64__ || __x86_64__ ) */
119817466cbSJens Wiklander 
120817466cbSJens Wiklander #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&  \
121817466cbSJens Wiklander     defined(__GNUC__) && ( defined(__powerpc__) || defined(__ppc__) )
122817466cbSJens Wiklander 
123817466cbSJens Wiklander #define HAVE_HARDCLOCK
124817466cbSJens Wiklander 
125817466cbSJens Wiklander unsigned long mbedtls_timing_hardclock( void )
126817466cbSJens Wiklander {
127817466cbSJens Wiklander     unsigned long tbl, tbu0, tbu1;
128817466cbSJens Wiklander 
129817466cbSJens Wiklander     do
130817466cbSJens Wiklander     {
131817466cbSJens Wiklander         asm volatile( "mftbu %0" : "=r" (tbu0) );
132817466cbSJens Wiklander         asm volatile( "mftb  %0" : "=r" (tbl ) );
133817466cbSJens Wiklander         asm volatile( "mftbu %0" : "=r" (tbu1) );
134817466cbSJens Wiklander     }
135817466cbSJens Wiklander     while( tbu0 != tbu1 );
136817466cbSJens Wiklander 
137817466cbSJens Wiklander     return( tbl );
138817466cbSJens Wiklander }
139817466cbSJens Wiklander #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
140817466cbSJens Wiklander           __GNUC__ && ( __powerpc__ || __ppc__ ) */
141817466cbSJens Wiklander 
142817466cbSJens Wiklander #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&  \
143817466cbSJens Wiklander     defined(__GNUC__) && defined(__sparc64__)
144817466cbSJens Wiklander 
145817466cbSJens Wiklander #if defined(__OpenBSD__)
146817466cbSJens Wiklander #warning OpenBSD does not allow access to tick register using software version instead
147817466cbSJens Wiklander #else
148817466cbSJens Wiklander #define HAVE_HARDCLOCK
149817466cbSJens Wiklander 
150817466cbSJens Wiklander unsigned long mbedtls_timing_hardclock( void )
151817466cbSJens Wiklander {
152817466cbSJens Wiklander     unsigned long tick;
153817466cbSJens Wiklander     asm volatile( "rdpr %%tick, %0;" : "=&r" (tick) );
154817466cbSJens Wiklander     return( tick );
155817466cbSJens Wiklander }
156817466cbSJens Wiklander #endif /* __OpenBSD__ */
157817466cbSJens Wiklander #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
158817466cbSJens Wiklander           __GNUC__ && __sparc64__ */
159817466cbSJens Wiklander 
160817466cbSJens Wiklander #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&  \
161817466cbSJens Wiklander     defined(__GNUC__) && defined(__sparc__) && !defined(__sparc64__)
162817466cbSJens Wiklander 
163817466cbSJens Wiklander #define HAVE_HARDCLOCK
164817466cbSJens Wiklander 
165817466cbSJens Wiklander unsigned long mbedtls_timing_hardclock( void )
166817466cbSJens Wiklander {
167817466cbSJens Wiklander     unsigned long tick;
168817466cbSJens Wiklander     asm volatile( ".byte 0x83, 0x41, 0x00, 0x00" );
169817466cbSJens Wiklander     asm volatile( "mov   %%g1, %0" : "=r" (tick) );
170817466cbSJens Wiklander     return( tick );
171817466cbSJens Wiklander }
172817466cbSJens Wiklander #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
173817466cbSJens Wiklander           __GNUC__ && __sparc__ && !__sparc64__ */
174817466cbSJens Wiklander 
175817466cbSJens Wiklander #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&      \
176817466cbSJens Wiklander     defined(__GNUC__) && defined(__alpha__)
177817466cbSJens Wiklander 
178817466cbSJens Wiklander #define HAVE_HARDCLOCK
179817466cbSJens Wiklander 
180817466cbSJens Wiklander unsigned long mbedtls_timing_hardclock( void )
181817466cbSJens Wiklander {
182817466cbSJens Wiklander     unsigned long cc;
183817466cbSJens Wiklander     asm volatile( "rpcc %0" : "=r" (cc) );
184817466cbSJens Wiklander     return( cc & 0xFFFFFFFF );
185817466cbSJens Wiklander }
186817466cbSJens Wiklander #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
187817466cbSJens Wiklander           __GNUC__ && __alpha__ */
188817466cbSJens Wiklander 
189817466cbSJens Wiklander #if !defined(HAVE_HARDCLOCK) && defined(MBEDTLS_HAVE_ASM) &&      \
190817466cbSJens Wiklander     defined(__GNUC__) && defined(__ia64__)
191817466cbSJens Wiklander 
192817466cbSJens Wiklander #define HAVE_HARDCLOCK
193817466cbSJens Wiklander 
194817466cbSJens Wiklander unsigned long mbedtls_timing_hardclock( void )
195817466cbSJens Wiklander {
196817466cbSJens Wiklander     unsigned long itc;
197817466cbSJens Wiklander     asm volatile( "mov %0 = ar.itc" : "=r" (itc) );
198817466cbSJens Wiklander     return( itc );
199817466cbSJens Wiklander }
200817466cbSJens Wiklander #endif /* !HAVE_HARDCLOCK && MBEDTLS_HAVE_ASM &&
201817466cbSJens Wiklander           __GNUC__ && __ia64__ */
202817466cbSJens Wiklander 
203817466cbSJens Wiklander #if !defined(HAVE_HARDCLOCK) && defined(_MSC_VER) && \
204817466cbSJens Wiklander     !defined(EFIX64) && !defined(EFI32)
205817466cbSJens Wiklander 
206817466cbSJens Wiklander #define HAVE_HARDCLOCK
207817466cbSJens Wiklander 
208817466cbSJens Wiklander unsigned long mbedtls_timing_hardclock( void )
209817466cbSJens Wiklander {
210817466cbSJens Wiklander     LARGE_INTEGER offset;
211817466cbSJens Wiklander 
212817466cbSJens Wiklander     QueryPerformanceCounter( &offset );
213817466cbSJens Wiklander 
214817466cbSJens Wiklander     return( (unsigned long)( offset.QuadPart ) );
215817466cbSJens Wiklander }
216817466cbSJens Wiklander #endif /* !HAVE_HARDCLOCK && _MSC_VER && !EFIX64 && !EFI32 */
217817466cbSJens Wiklander 
218817466cbSJens Wiklander #if !defined(HAVE_HARDCLOCK)
219817466cbSJens Wiklander 
220817466cbSJens Wiklander #define HAVE_HARDCLOCK
221817466cbSJens Wiklander 
222817466cbSJens Wiklander static int hardclock_init = 0;
223817466cbSJens Wiklander static struct timeval tv_init;
224817466cbSJens Wiklander 
225817466cbSJens Wiklander unsigned long mbedtls_timing_hardclock( void )
226817466cbSJens Wiklander {
227817466cbSJens Wiklander     struct timeval tv_cur;
228817466cbSJens Wiklander 
229817466cbSJens Wiklander     if( hardclock_init == 0 )
230817466cbSJens Wiklander     {
231817466cbSJens Wiklander         gettimeofday( &tv_init, NULL );
232817466cbSJens Wiklander         hardclock_init = 1;
233817466cbSJens Wiklander     }
234817466cbSJens Wiklander 
235817466cbSJens Wiklander     gettimeofday( &tv_cur, NULL );
236817466cbSJens Wiklander     return( ( tv_cur.tv_sec  - tv_init.tv_sec  ) * 1000000
237817466cbSJens Wiklander           + ( tv_cur.tv_usec - tv_init.tv_usec ) );
238817466cbSJens Wiklander }
239817466cbSJens Wiklander #endif /* !HAVE_HARDCLOCK */
240817466cbSJens Wiklander 
241817466cbSJens Wiklander volatile int mbedtls_timing_alarmed = 0;
242817466cbSJens Wiklander 
243817466cbSJens Wiklander #if defined(_WIN32) && !defined(EFIX64) && !defined(EFI32)
244817466cbSJens Wiklander 
245817466cbSJens Wiklander unsigned long mbedtls_timing_get_timer( struct mbedtls_timing_hr_time *val, int reset )
246817466cbSJens Wiklander {
247817466cbSJens Wiklander     unsigned long delta;
248817466cbSJens Wiklander     LARGE_INTEGER offset, hfreq;
249817466cbSJens Wiklander     struct _hr_time *t = (struct _hr_time *) val;
250817466cbSJens Wiklander 
251817466cbSJens Wiklander     QueryPerformanceCounter(  &offset );
252817466cbSJens Wiklander     QueryPerformanceFrequency( &hfreq );
253817466cbSJens Wiklander 
254817466cbSJens Wiklander     delta = (unsigned long)( ( 1000 *
255817466cbSJens Wiklander         ( offset.QuadPart - t->start.QuadPart ) ) /
256817466cbSJens Wiklander            hfreq.QuadPart );
257817466cbSJens Wiklander 
258817466cbSJens Wiklander     if( reset )
259817466cbSJens Wiklander         QueryPerformanceCounter( &t->start );
260817466cbSJens Wiklander 
261817466cbSJens Wiklander     return( delta );
262817466cbSJens Wiklander }
263817466cbSJens Wiklander 
264817466cbSJens Wiklander /* It's OK to use a global because alarm() is supposed to be global anyway */
265817466cbSJens Wiklander static DWORD alarmMs;
266817466cbSJens Wiklander 
267817466cbSJens Wiklander static DWORD WINAPI TimerProc( LPVOID TimerContext )
268817466cbSJens Wiklander {
269817466cbSJens Wiklander     ((void) TimerContext);
270817466cbSJens Wiklander     Sleep( alarmMs );
271817466cbSJens Wiklander     mbedtls_timing_alarmed = 1;
272817466cbSJens Wiklander     return( TRUE );
273817466cbSJens Wiklander }
274817466cbSJens Wiklander 
275817466cbSJens Wiklander void mbedtls_set_alarm( int seconds )
276817466cbSJens Wiklander {
277817466cbSJens Wiklander     DWORD ThreadId;
278817466cbSJens Wiklander 
279817466cbSJens Wiklander     mbedtls_timing_alarmed = 0;
280817466cbSJens Wiklander     alarmMs = seconds * 1000;
281817466cbSJens Wiklander     CloseHandle( CreateThread( NULL, 0, TimerProc, NULL, 0, &ThreadId ) );
282817466cbSJens Wiklander }
283817466cbSJens Wiklander 
284817466cbSJens Wiklander #else /* _WIN32 && !EFIX64 && !EFI32 */
285817466cbSJens Wiklander 
286817466cbSJens Wiklander unsigned long mbedtls_timing_get_timer( struct mbedtls_timing_hr_time *val, int reset )
287817466cbSJens Wiklander {
288817466cbSJens Wiklander     unsigned long delta;
289817466cbSJens Wiklander     struct timeval offset;
290817466cbSJens Wiklander     struct _hr_time *t = (struct _hr_time *) val;
291817466cbSJens Wiklander 
292817466cbSJens Wiklander     gettimeofday( &offset, NULL );
293817466cbSJens Wiklander 
294817466cbSJens Wiklander     if( reset )
295817466cbSJens Wiklander     {
296817466cbSJens Wiklander         t->start.tv_sec  = offset.tv_sec;
297817466cbSJens Wiklander         t->start.tv_usec = offset.tv_usec;
298817466cbSJens Wiklander         return( 0 );
299817466cbSJens Wiklander     }
300817466cbSJens Wiklander 
301817466cbSJens Wiklander     delta = ( offset.tv_sec  - t->start.tv_sec  ) * 1000
302817466cbSJens Wiklander           + ( offset.tv_usec - t->start.tv_usec ) / 1000;
303817466cbSJens Wiklander 
304817466cbSJens Wiklander     return( delta );
305817466cbSJens Wiklander }
306817466cbSJens Wiklander 
307817466cbSJens Wiklander static void sighandler( int signum )
308817466cbSJens Wiklander {
309817466cbSJens Wiklander     mbedtls_timing_alarmed = 1;
310817466cbSJens Wiklander     signal( signum, sighandler );
311817466cbSJens Wiklander }
312817466cbSJens Wiklander 
313817466cbSJens Wiklander void mbedtls_set_alarm( int seconds )
314817466cbSJens Wiklander {
315817466cbSJens Wiklander     mbedtls_timing_alarmed = 0;
316817466cbSJens Wiklander     signal( SIGALRM, sighandler );
317817466cbSJens Wiklander     alarm( seconds );
318817466cbSJens Wiklander }
319817466cbSJens Wiklander 
320817466cbSJens Wiklander #endif /* _WIN32 && !EFIX64 && !EFI32 */
321817466cbSJens Wiklander 
322817466cbSJens Wiklander /*
323817466cbSJens Wiklander  * Set delays to watch
324817466cbSJens Wiklander  */
325817466cbSJens Wiklander void mbedtls_timing_set_delay( void *data, uint32_t int_ms, uint32_t fin_ms )
326817466cbSJens Wiklander {
327817466cbSJens Wiklander     mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data;
328817466cbSJens Wiklander 
329817466cbSJens Wiklander     ctx->int_ms = int_ms;
330817466cbSJens Wiklander     ctx->fin_ms = fin_ms;
331817466cbSJens Wiklander 
332817466cbSJens Wiklander     if( fin_ms != 0 )
333817466cbSJens Wiklander         (void) mbedtls_timing_get_timer( &ctx->timer, 1 );
334817466cbSJens Wiklander }
335817466cbSJens Wiklander 
336817466cbSJens Wiklander /*
337817466cbSJens Wiklander  * Get number of delays expired
338817466cbSJens Wiklander  */
339817466cbSJens Wiklander int mbedtls_timing_get_delay( void *data )
340817466cbSJens Wiklander {
341817466cbSJens Wiklander     mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data;
342817466cbSJens Wiklander     unsigned long elapsed_ms;
343817466cbSJens Wiklander 
344817466cbSJens Wiklander     if( ctx->fin_ms == 0 )
345817466cbSJens Wiklander         return( -1 );
346817466cbSJens Wiklander 
347817466cbSJens Wiklander     elapsed_ms = mbedtls_timing_get_timer( &ctx->timer, 0 );
348817466cbSJens Wiklander 
349817466cbSJens Wiklander     if( elapsed_ms >= ctx->fin_ms )
350817466cbSJens Wiklander         return( 2 );
351817466cbSJens Wiklander 
352817466cbSJens Wiklander     if( elapsed_ms >= ctx->int_ms )
353817466cbSJens Wiklander         return( 1 );
354817466cbSJens Wiklander 
355817466cbSJens Wiklander     return( 0 );
356817466cbSJens Wiklander }
357817466cbSJens Wiklander 
358817466cbSJens Wiklander #endif /* !MBEDTLS_TIMING_ALT */
359817466cbSJens Wiklander 
360817466cbSJens Wiklander #if defined(MBEDTLS_SELF_TEST)
361817466cbSJens Wiklander 
362817466cbSJens Wiklander /*
363817466cbSJens Wiklander  * Busy-waits for the given number of milliseconds.
364817466cbSJens Wiklander  * Used for testing mbedtls_timing_hardclock.
365817466cbSJens Wiklander  */
366817466cbSJens Wiklander static void busy_msleep( unsigned long msec )
367817466cbSJens Wiklander {
368817466cbSJens Wiklander     struct mbedtls_timing_hr_time hires;
369817466cbSJens Wiklander     unsigned long i = 0; /* for busy-waiting */
370817466cbSJens Wiklander     volatile unsigned long j; /* to prevent optimisation */
371817466cbSJens Wiklander 
372817466cbSJens Wiklander     (void) mbedtls_timing_get_timer( &hires, 1 );
373817466cbSJens Wiklander 
374817466cbSJens Wiklander     while( mbedtls_timing_get_timer( &hires, 0 ) < msec )
375817466cbSJens Wiklander         i++;
376817466cbSJens Wiklander 
377817466cbSJens Wiklander     j = i;
378817466cbSJens Wiklander     (void) j;
379817466cbSJens Wiklander }
380817466cbSJens Wiklander 
381817466cbSJens Wiklander #define FAIL    do                      \
382817466cbSJens Wiklander {                                       \
383817466cbSJens Wiklander     if( verbose != 0 )                  \
384817466cbSJens Wiklander         mbedtls_printf( "failed\n" );   \
385817466cbSJens Wiklander                                         \
386817466cbSJens Wiklander     return( 1 );                        \
387817466cbSJens Wiklander } while( 0 )
388817466cbSJens Wiklander 
389817466cbSJens Wiklander /*
390817466cbSJens Wiklander  * Checkup routine
391817466cbSJens Wiklander  *
392817466cbSJens Wiklander  * Warning: this is work in progress, some tests may not be reliable enough
393817466cbSJens Wiklander  * yet! False positives may happen.
394817466cbSJens Wiklander  */
395817466cbSJens Wiklander int mbedtls_timing_self_test( int verbose )
396817466cbSJens Wiklander {
397817466cbSJens Wiklander     unsigned long cycles, ratio;
398817466cbSJens Wiklander     unsigned long millisecs, secs;
399817466cbSJens Wiklander     int hardfail;
400817466cbSJens Wiklander     struct mbedtls_timing_hr_time hires;
401817466cbSJens Wiklander     uint32_t a, b;
402817466cbSJens Wiklander     mbedtls_timing_delay_context ctx;
403817466cbSJens Wiklander 
404817466cbSJens Wiklander     if( verbose != 0 )
405817466cbSJens Wiklander         mbedtls_printf( "  TIMING tests note: will take some time!\n" );
406817466cbSJens Wiklander 
407817466cbSJens Wiklander 
408817466cbSJens Wiklander     if( verbose != 0 )
409817466cbSJens Wiklander         mbedtls_printf( "  TIMING test #1 (set_alarm / get_timer): " );
410817466cbSJens Wiklander 
411817466cbSJens Wiklander     for( secs = 1; secs <= 3; secs++ )
412817466cbSJens Wiklander     {
413817466cbSJens Wiklander         (void) mbedtls_timing_get_timer( &hires, 1 );
414817466cbSJens Wiklander 
415817466cbSJens Wiklander         mbedtls_set_alarm( (int) secs );
416817466cbSJens Wiklander         while( !mbedtls_timing_alarmed )
417817466cbSJens Wiklander             ;
418817466cbSJens Wiklander 
419817466cbSJens Wiklander         millisecs = mbedtls_timing_get_timer( &hires, 0 );
420817466cbSJens Wiklander 
421817466cbSJens Wiklander         /* For some reason on Windows it looks like alarm has an extra delay
422817466cbSJens Wiklander          * (maybe related to creating a new thread). Allow some room here. */
423817466cbSJens Wiklander         if( millisecs < 800 * secs || millisecs > 1200 * secs + 300 )
424817466cbSJens Wiklander         {
425817466cbSJens Wiklander             if( verbose != 0 )
426817466cbSJens Wiklander                 mbedtls_printf( "failed\n" );
427817466cbSJens Wiklander 
428817466cbSJens Wiklander             return( 1 );
429817466cbSJens Wiklander         }
430817466cbSJens Wiklander     }
431817466cbSJens Wiklander 
432817466cbSJens Wiklander     if( verbose != 0 )
433817466cbSJens Wiklander         mbedtls_printf( "passed\n" );
434817466cbSJens Wiklander 
435817466cbSJens Wiklander     if( verbose != 0 )
436817466cbSJens Wiklander         mbedtls_printf( "  TIMING test #2 (set/get_delay        ): " );
437817466cbSJens Wiklander 
438817466cbSJens Wiklander     for( a = 200; a <= 400; a += 200 )
439817466cbSJens Wiklander     {
440817466cbSJens Wiklander         for( b = 200; b <= 400; b += 200 )
441817466cbSJens Wiklander         {
442817466cbSJens Wiklander             mbedtls_timing_set_delay( &ctx, a, a + b );
443817466cbSJens Wiklander 
444817466cbSJens Wiklander             busy_msleep( a - a / 8 );
445817466cbSJens Wiklander             if( mbedtls_timing_get_delay( &ctx ) != 0 )
446817466cbSJens Wiklander                 FAIL;
447817466cbSJens Wiklander 
448817466cbSJens Wiklander             busy_msleep( a / 4 );
449817466cbSJens Wiklander             if( mbedtls_timing_get_delay( &ctx ) != 1 )
450817466cbSJens Wiklander                 FAIL;
451817466cbSJens Wiklander 
452817466cbSJens Wiklander             busy_msleep( b - a / 8 - b / 8 );
453817466cbSJens Wiklander             if( mbedtls_timing_get_delay( &ctx ) != 1 )
454817466cbSJens Wiklander                 FAIL;
455817466cbSJens Wiklander 
456817466cbSJens Wiklander             busy_msleep( b / 4 );
457817466cbSJens Wiklander             if( mbedtls_timing_get_delay( &ctx ) != 2 )
458817466cbSJens Wiklander                 FAIL;
459817466cbSJens Wiklander         }
460817466cbSJens Wiklander     }
461817466cbSJens Wiklander 
462817466cbSJens Wiklander     mbedtls_timing_set_delay( &ctx, 0, 0 );
463817466cbSJens Wiklander     busy_msleep( 200 );
464817466cbSJens Wiklander     if( mbedtls_timing_get_delay( &ctx ) != -1 )
465817466cbSJens Wiklander         FAIL;
466817466cbSJens Wiklander 
467817466cbSJens Wiklander     if( verbose != 0 )
468817466cbSJens Wiklander         mbedtls_printf( "passed\n" );
469817466cbSJens Wiklander 
470817466cbSJens Wiklander     if( verbose != 0 )
471817466cbSJens Wiklander         mbedtls_printf( "  TIMING test #3 (hardclock / get_timer): " );
472817466cbSJens Wiklander 
473817466cbSJens Wiklander     /*
474817466cbSJens Wiklander      * Allow one failure for possible counter wrapping.
475817466cbSJens Wiklander      * On a 4Ghz 32-bit machine the cycle counter wraps about once per second;
476817466cbSJens Wiklander      * since the whole test is about 10ms, it shouldn't happen twice in a row.
477817466cbSJens Wiklander      */
478817466cbSJens Wiklander     hardfail = 0;
479817466cbSJens Wiklander 
480817466cbSJens Wiklander hard_test:
481817466cbSJens Wiklander     if( hardfail > 1 )
482817466cbSJens Wiklander     {
483817466cbSJens Wiklander         if( verbose != 0 )
484817466cbSJens Wiklander             mbedtls_printf( "failed (ignored)\n" );
485817466cbSJens Wiklander 
486817466cbSJens Wiklander         goto hard_test_done;
487817466cbSJens Wiklander     }
488817466cbSJens Wiklander 
489817466cbSJens Wiklander     /* Get a reference ratio cycles/ms */
490817466cbSJens Wiklander     millisecs = 1;
491817466cbSJens Wiklander     cycles = mbedtls_timing_hardclock();
492817466cbSJens Wiklander     busy_msleep( millisecs );
493817466cbSJens Wiklander     cycles = mbedtls_timing_hardclock() - cycles;
494817466cbSJens Wiklander     ratio = cycles / millisecs;
495817466cbSJens Wiklander 
496817466cbSJens Wiklander     /* Check that the ratio is mostly constant */
497817466cbSJens Wiklander     for( millisecs = 2; millisecs <= 4; millisecs++ )
498817466cbSJens Wiklander     {
499817466cbSJens Wiklander         cycles = mbedtls_timing_hardclock();
500817466cbSJens Wiklander         busy_msleep( millisecs );
501817466cbSJens Wiklander         cycles = mbedtls_timing_hardclock() - cycles;
502817466cbSJens Wiklander 
503817466cbSJens Wiklander         /* Allow variation up to 20% */
504817466cbSJens Wiklander         if( cycles / millisecs < ratio - ratio / 5 ||
505817466cbSJens Wiklander             cycles / millisecs > ratio + ratio / 5 )
506817466cbSJens Wiklander         {
507817466cbSJens Wiklander             hardfail++;
508817466cbSJens Wiklander             goto hard_test;
509817466cbSJens Wiklander         }
510817466cbSJens Wiklander     }
511817466cbSJens Wiklander 
512817466cbSJens Wiklander     if( verbose != 0 )
513817466cbSJens Wiklander         mbedtls_printf( "passed\n" );
514817466cbSJens Wiklander 
515817466cbSJens Wiklander hard_test_done:
516817466cbSJens Wiklander 
517817466cbSJens Wiklander     if( verbose != 0 )
518817466cbSJens Wiklander         mbedtls_printf( "\n" );
519817466cbSJens Wiklander 
520817466cbSJens Wiklander     return( 0 );
521817466cbSJens Wiklander }
522817466cbSJens Wiklander 
523817466cbSJens Wiklander #endif /* MBEDTLS_SELF_TEST */
524817466cbSJens Wiklander 
525817466cbSJens Wiklander #endif /* MBEDTLS_TIMING_C */
526