aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/asmlib/physseed64.asm
blob: b30fc26712981ed1644a255c6fcf8b2e848a0a8f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
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
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
%include "defs.asm"

;*************************  physseed64.asm  **********************************
; Author:           Agner Fog
; Date created:     2010-08-03
; Last modified:    2013-09-13
; Source URL:       www.agner.org/optimize
; Project:          asmlib.zip
; C++ prototype:
; extern "C" int PhysicalSeed(int seeds[], int NumSeeds);
;
; Description:
; Generates a non-deterministic random seed from a physical random number generator 
; which is available on some processors. 
; Uses the time stamp counter (which is less random) if no physical random number
; generator is available.
; The code is not optimized for speed because it is typically called only once.
;
; Parameters:
; int seeds[]       An array which will be filled with random numbers
; int NumSeeds      Indicates the desired number of 32-bit random numbers
;
; Return value:     0   Failure. No suitable instruction available (processor older than Pentium)
;                   1   No physical random number generator. Used time stamp counter instead
;                   2   Success. VIA physical random number generator used
;                   3   Success. Intel physical random number generator used
;                   4   Success. Intel physical seed generator used
; 
; The return value will indicate the availability of a physical random number generator
; even if NumSeeds = 0.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

default rel

%define NUM_TRIES   20                 ; max number of tries for rdseed and rdrand instructions

%define TESTING     0                  ; 1 for test only

global PhysicalSeed

; Direct entries to CPU-specific versions
global PhysicalSeedNone: function
global PhysicalSeedRDTSC: function
global PhysicalSeedVIA: function
global PhysicalSeedRDRand: function
global PhysicalSeedRDSeed function

; ***************************************************************************
; Define registers used for function parameters, used in 64-bit mode only
; ***************************************************************************
 
%IFDEF WINDOWS
  %define par1     rcx
  %define par2     rdx
  %define par3     r8
  %define par1d    ecx
  %define par2d    edx
  %define par3d    r8d
%ENDIF
  
%IFDEF UNIX
  %define par1     rdi
  %define par2     rsi
  %define par3     rdx
  %define par1d    edi
  %define par2d    esi
  %define par3d    edx
%ENDIF 


SECTION .text  align=16

%IFDEF WINDOWS
global PhysicalSeedD@8                 ; DLL version
PhysicalSeedD@8:
%ENDIF

PhysicalSeed:
        jmp     [PhysicalSeedDispatch] ; Go to appropriate version, depending on instructions available


PhysicalSeedRDSeed:
        push    rbx
        test    par2d, par2d           ; NumSeeds
        jz      S300 
        js      S900
        mov     par3d, par2d           ; NumSeeds
        shr     par3d, 1
        jz      S150
        ; do 64 bits at a time
S100:   mov     ebx, NUM_TRIES
S110:   ; rdseed rax
%if     TESTING
        mov     eax, par3d
        stc
%ELSE
        db 48h, 0Fh, 0C7h, 0F8h        ; rdseed rax
%ENDIF
        jc      S120
        ; failed. try again
        dec     ebx
        jz      S900
        jmp     S110
S120:   mov     [par1], rax
        add     par1, 8
        dec     par3d
        jnz     S100                   ; loop 64 bits
S150:
        and     par2d, 1
        jz      S300
        ; an odd 32 bit remains
S200:   mov     ebx, NUM_TRIES
S210:   ; rdseed rax
%if     TESTING
        mov     eax, par3d
        stc
%ELSE
        db 0Fh, 0C7h, 0F8h             ; rdseed eax
%ENDIF
        jc      S220
        ; failed. try again
        dec     ebx
        jz      S900
        jmp     S210
S220:   mov     [par1], eax
S300:   mov     eax, 4                 ; return value
        pop     rbx
        ret
S900:   ; failure 
        xor     eax, eax               ; return 0
        pop     rbx
        ret
                

PhysicalSeedRDRand:
        push    rbx
        test    par2d, par2d           ; NumSeeds
        jz      R300
        js      R900         
        mov     par3d, par2d           ; NumSeeds
        shr     par3d, 1               ; NumSeeds/2
        jz      R150
        ; do 64 bits at a time
R100:   mov     ebx, NUM_TRIES
R110:   ; rdrand rax
%if     TESTING
        mov     eax, par3d
        stc
%ELSE
        db 48h, 0Fh, 0C7h, 0F0h        ; rdrand rax
%ENDIF
        jc      R120
        ; failed. try again
        dec     ebx
        jz      R900
        jmp     R110
R120:   mov     [par1], rax
        add     par1, 8
        dec     par3d
        jnz     R100                   ; loop 64 bits
R150:
        and     par2d, 1
        jz      R300
        ; an odd 32 bit remains
R200:   mov     ebx, NUM_TRIES
R210:   ; rdrand eax
%if     TESTING
        mov     eax, par3d
        stc
%ELSE
        db 0Fh, 0C7h, 0F0h             ; rdrand eax
%ENDIF
        jc      R220
        ; failed. try again
        dec     ebx
        jz      R900
        jmp     R210
R220:   mov     [par1], eax
R300:   mov     eax, 4                 ; return value
        pop     rbx
        ret
R900:   ; failure 
        xor     eax, eax               ; return 0
        pop     rbx
        ret


PhysicalSeedVIA:
;       VIA XSTORE  supported
        push    rbx
%IFDEF WINDOWS
        push    rsi
        push    rdi
        mov     rdi, rcx               ; seeds
        mov     esi, edx               ; NumSeeds
%ENDIF        
        mov     ecx, esi               ; NumSeeds
        and     ecx, -2                ; round down to nearest even
        jz      T200                   ; NumSeeds <= 1
        ; make an even number of random dwords
        shl     ecx, 2                 ; number of bytes (divisible by 8)
        mov     edx, 3                 ; quality factor
%if     TESTING
        mov     eax, 1
        rep stosb
%ELSE        
        db 0F3H, 00FH, 0A7H, 0C0H      ; rep xstore instuction
%ENDIF
T200:        
        test    esi, 1
        jz      T300
        ; NumSeeds is odd. Make 8 bytes in temporary buffer and store 4 of the bytes
        mov     rbx, rdi               ; current output pointer
        mov     ecx, 4                 ; Will generate 4 or 8 bytes, depending on CPU
        mov     edx, 3                 ; quality factor
        push    rcx                    ; make temporary space on stack
        mov     rdi, rsp               ; point to buffer on stack
%if     TESTING
        mov     eax, 1
        rep stosb
%ELSE        
        db 0F3H, 00FH, 0A7H, 0C0H      ; rep xstore instuction
%ENDIF
        pop     rax
        mov     [rbx], eax             ; store the last 4 bytes
T300:
        mov     eax, 2                 ; return value        
%IFDEF WINDOWS
        pop     rdi
        pop     rsi
%ENDIF  
        pop     rbx      
        ret        


PhysicalSeedRDTSC:
%IFDEF WINDOWS
        push    rbx
        push    rcx
        push    rdx
        xor     eax, eax
        cpuid                          ; serialize
        rdtsc                          ; get time stamp counter
        pop     rbx                    ; numseeds
        pop     rcx                    ; seeds
        test    ebx, ebx
        jz      U300                   ; zero seeds
        js      U900                   ; failure
        mov     [rcx], eax             ; store time stamp counter as seeds[0]
        add     rcx, 4
        dec     ebx
        jz      U300
        mov     [rcx], edx             ; store upper part of time stamp counter as seeds[1]
        add     rcx, 4
        dec     ebx
        jz      U300
        xor     eax, eax
U100:   mov     [rcx], eax             ; store 0 for the rest
        add     rcx, 4
        dec     ebx
        jnz     U100
U300:   mov     eax, 1                 ; return value        
        pop     rbx
        ret
U900:   ; failure         
        xor     eax, eax               ; return 0
        pop     rbx
        ret
        
%ELSE   ; UNIX

        push    rbx
        xor     eax, eax
        cpuid                          ; serialize
        rdtsc                          ; get time stamp counter
        test    esi, esi               ; numseeds
        jz      U300                   ; zero seeds
        js      U900                   ; failure
        mov     [rdi], eax             ; store time stamp counter as seeds[0]
        add     rdi, 4
        dec     esi
        jz      U300
        mov     [rdi], edx             ; store upper part of time stamp counter as seeds[1]
        add     rdi, 4
        dec     esi
        jz      U300
        xor     eax, eax
U100:   mov     [rdi], eax             ; store 0 for the rest
        add     rdi, 4
        dec     esi
        jnz     U100
U300:   mov     eax, 1                 ; return value        
        pop     rbx
        ret
U900:   ; failure         
        xor     eax, eax               ; return 0
        pop     rbx
        ret 

%ENDIF  


PhysicalSeedNone:                      ; no possible generation
        xor     eax, eax
        test    par2d, par2d           ; numseeds
        jz      N200
N100:   mov     [par1], eax
        add     par1, 4
        dec     par2d
        jnz     N100
N200:   ret                            ; return 0


PhysicalSeedDispatcher:
        push    rbx
%IFDEF WINDOWS
        push    rcx
        push    rdx
%ENDIF
        ; test if RDSEED supported
        xor     eax, eax
        cpuid
        cmp     eax, 7
        jb      P200                   ; RDSEED not supported
        mov     eax, 7
        xor     ecx, ecx
        cpuid
        bt      ebx, 18
       ; jc      USE_RDSEED             ; not tested yet!!

P200:   ; test if RDRAND supported
        mov     eax, 1
        cpuid
        bt      ecx, 30
        jc      USE_RDRAND

        ; test if VIA xstore instruction supported
        mov     eax, 0C0000000H
        push    rax
        cpuid
        pop     rbx
        cmp     eax, ebx
        jna     P300                   ; not a VIA processor
        lea     eax, [rbx+1]
        cpuid
        bt      edx, 3
        jc      VIA_METHOD

P300:   ; test if RDTSC supported
        mov     eax, 1
        cpuid
        bt      edx, 4
        jc      USE_RDTSC              ; XSTORE instruction not supported or not enabled
        
FAILURE: ; No useful instruction supported
        lea     rax, [PhysicalSeedNone]
        jmp     P800

USE_RDRAND:     ; Use RDRAND instruction        
        lea     rax, [PhysicalSeedRDRand]
        jmp     P800

USE_RDSEED:     ; Use RDSEED instruction (not tested yet)
        lea     rax, [PhysicalSeedRDSeed]
        jmp     P800

VIA_METHOD:     ; Use VIA xstore instructions   
        lea     rax, [PhysicalSeedVIA]
        jmp     P800
        
USE_RDTSC:
        lea     rax, [PhysicalSeedRDTSC]
        ;jmp     P800
        
P800:   mov     [PhysicalSeedDispatch], rax
%IFDEF WINDOWS
        pop     rdx
        pop     rcx
%ENDIF
        pop     rbx
        jmp     rax                    ; continue in dispatched version
        

; -----------------------------------------------------------------
;  Data section for dispatcher
; -----------------------------------------------------------------

SECTION .data

; Pointer to appropriate versions. Initially point to dispatcher
PhysicalSeedDispatch  DQ PhysicalSeedDispatcher

%IFDEF POSITIONINDEPENDENT
; Fix potential problem in Mac linker
        DD      0, 0
%ENDIF