Break out DllMain from crypto/cryptlib.c and use it in shared libs only
[openssl.git] / crypto / x86cpuid.pl
1 #!/usr/bin/env perl
2
3 $0 =~ m/(.*[\/\\])[^\/\\]+$/; $dir=$1;
4 push(@INC, "${dir}perlasm", "perlasm");
5 require "x86asm.pl";
6
7 $output = pop;
8 open OUT,">$output";
9 *STDOUT=*OUT;
10
11 &asm_init($ARGV[0],"x86cpuid");
12
13 for (@ARGV) { $sse2=1 if (/-DOPENSSL_IA32_SSE2/); }
14
15 &function_begin("OPENSSL_ia32_cpuid");
16         &xor    ("edx","edx");
17         &pushf  ();
18         &pop    ("eax");
19         &mov    ("ecx","eax");
20         &xor    ("eax",1<<21);
21         &push   ("eax");
22         &popf   ();
23         &pushf  ();
24         &pop    ("eax");
25         &xor    ("ecx","eax");
26         &xor    ("eax","eax");
27         &bt     ("ecx",21);
28         &jnc    (&label("nocpuid"));
29         &mov    ("esi",&wparam(0));
30         &mov    (&DWP(8,"esi"),"eax");  # clear 3rd word
31         &cpuid  ();
32         &mov    ("edi","eax");          # max value for standard query level
33
34         &xor    ("eax","eax");
35         &cmp    ("ebx",0x756e6547);     # "Genu"
36         &setne  (&LB("eax"));
37         &mov    ("ebp","eax");
38         &cmp    ("edx",0x49656e69);     # "ineI"
39         &setne  (&LB("eax"));
40         &or     ("ebp","eax");
41         &cmp    ("ecx",0x6c65746e);     # "ntel"
42         &setne  (&LB("eax"));
43         &or     ("ebp","eax");          # 0 indicates Intel CPU
44         &jz     (&label("intel"));
45
46         &cmp    ("ebx",0x68747541);     # "Auth"
47         &setne  (&LB("eax"));
48         &mov    ("esi","eax");
49         &cmp    ("edx",0x69746E65);     # "enti"
50         &setne  (&LB("eax"));
51         &or     ("esi","eax");
52         &cmp    ("ecx",0x444D4163);     # "cAMD"
53         &setne  (&LB("eax"));
54         &or     ("esi","eax");          # 0 indicates AMD CPU
55         &jnz    (&label("intel"));
56
57         # AMD specific
58         &mov    ("eax",0x80000000);
59         &cpuid  ();
60         &cmp    ("eax",0x80000001);
61         &jb     (&label("intel"));
62         &mov    ("esi","eax");
63         &mov    ("eax",0x80000001);
64         &cpuid  ();
65         &or     ("ebp","ecx");
66         &and    ("ebp",1<<11|1);        # isolate XOP bit
67         &cmp    ("esi",0x80000008);
68         &jb     (&label("intel"));
69
70         &mov    ("eax",0x80000008);
71         &cpuid  ();
72         &movz   ("esi",&LB("ecx"));     # number of cores - 1
73         &inc    ("esi");                # number of cores
74
75         &mov    ("eax",1);
76         &xor    ("ecx","ecx");
77         &cpuid  ();
78         &bt     ("edx",28);
79         &jnc    (&label("generic"));
80         &shr    ("ebx",16);
81         &and    ("ebx",0xff);
82         &cmp    ("ebx","esi");
83         &ja     (&label("generic"));
84         &and    ("edx",0xefffffff);     # clear hyper-threading bit
85         &jmp    (&label("generic"));
86         
87 &set_label("intel");
88         &cmp    ("edi",7);
89         &jb     (&label("cacheinfo"));
90
91         &mov    ("esi",&wparam(0));
92         &mov    ("eax",7);
93         &xor    ("ecx","ecx");
94         &cpuid  ();
95         &mov    (&DWP(8,"esi"),"ebx");
96
97 &set_label("cacheinfo");
98         &cmp    ("edi",4);
99         &mov    ("edi",-1);
100         &jb     (&label("nocacheinfo"));
101
102         &mov    ("eax",4);
103         &mov    ("ecx",0);              # query L1D
104         &cpuid  ();
105         &mov    ("edi","eax");
106         &shr    ("edi",14);
107         &and    ("edi",0xfff);          # number of cores -1 per L1D
108
109 &set_label("nocacheinfo");
110         &mov    ("eax",1);
111         &xor    ("ecx","ecx");
112         &cpuid  ();
113         &and    ("edx",0xbfefffff);     # force reserved bits #20, #30 to 0
114         &cmp    ("ebp",0);
115         &jne    (&label("notintel"));
116         &or     ("edx",1<<30);          # set reserved bit#30 on Intel CPUs
117         &and    (&HB("eax"),15);        # familiy ID
118         &cmp    (&HB("eax"),15);        # P4?
119         &jne    (&label("notintel"));
120         &or     ("edx",1<<20);          # set reserved bit#20 to engage RC4_CHAR
121 &set_label("notintel");
122         &bt     ("edx",28);             # test hyper-threading bit
123         &jnc    (&label("generic"));
124         &and    ("edx",0xefffffff);
125         &cmp    ("edi",0);
126         &je     (&label("generic"));
127
128         &or     ("edx",0x10000000);
129         &shr    ("ebx",16);
130         &cmp    (&LB("ebx"),1);
131         &ja     (&label("generic"));
132         &and    ("edx",0xefffffff);     # clear hyper-threading bit if not
133
134 &set_label("generic");
135         &and    ("ebp",1<<11);          # isolate AMD XOP flag
136         &and    ("ecx",0xfffff7ff);     # force 11th bit to 0
137         &mov    ("esi","edx");
138         &or     ("ebp","ecx");          # merge AMD XOP flag
139
140         &bt     ("ecx",27);             # check OSXSAVE bit
141         &jnc    (&label("clear_avx"));
142         &xor    ("ecx","ecx");
143         &data_byte(0x0f,0x01,0xd0);     # xgetbv
144         &and    ("eax",6);
145         &cmp    ("eax",6);
146         &je     (&label("done"));
147         &cmp    ("eax",2);
148         &je     (&label("clear_avx"));
149 &set_label("clear_xmm");
150         &and    ("ebp",0xfdfffffd);     # clear AESNI and PCLMULQDQ bits
151         &and    ("esi",0xfeffffff);     # clear FXSR
152 &set_label("clear_avx");
153         &and    ("ebp",0xefffe7ff);     # clear AVX, FMA and AMD XOP bits
154         &mov    ("edi",&wparam(0));
155         &and    (&DWP(8,"edi"),0xffffffdf);     # clear AVX2
156 &set_label("done");
157         &mov    ("eax","esi");
158         &mov    ("edx","ebp");
159 &set_label("nocpuid");
160 &function_end("OPENSSL_ia32_cpuid");
161
162 &external_label("OPENSSL_ia32cap_P");
163
164 &function_begin_B("OPENSSL_rdtsc","EXTRN\t_OPENSSL_ia32cap_P:DWORD");
165         &xor    ("eax","eax");
166         &xor    ("edx","edx");
167         &picmeup("ecx","OPENSSL_ia32cap_P");
168         &bt     (&DWP(0,"ecx"),4);
169         &jnc    (&label("notsc"));
170         &rdtsc  ();
171 &set_label("notsc");
172         &ret    ();
173 &function_end_B("OPENSSL_rdtsc");
174
175 # This works in Ring 0 only [read DJGPP+MS-DOS+privileged DPMI host],
176 # but it's safe to call it on any [supported] 32-bit platform...
177 # Just check for [non-]zero return value...
178 &function_begin_B("OPENSSL_instrument_halt","EXTRN\t_OPENSSL_ia32cap_P:DWORD");
179         &picmeup("ecx","OPENSSL_ia32cap_P");
180         &bt     (&DWP(0,"ecx"),4);
181         &jnc    (&label("nohalt"));     # no TSC
182
183         &data_word(0x9058900e);         # push %cs; pop %eax
184         &and    ("eax",3);
185         &jnz    (&label("nohalt"));     # not enough privileges
186
187         &pushf  ();
188         &pop    ("eax");
189         &bt     ("eax",9);
190         &jnc    (&label("nohalt"));     # interrupts are disabled
191
192         &rdtsc  ();
193         &push   ("edx");
194         &push   ("eax");
195         &halt   ();
196         &rdtsc  ();
197
198         &sub    ("eax",&DWP(0,"esp"));
199         &sbb    ("edx",&DWP(4,"esp"));
200         &add    ("esp",8);
201         &ret    ();
202
203 &set_label("nohalt");
204         &xor    ("eax","eax");
205         &xor    ("edx","edx");
206         &ret    ();
207 &function_end_B("OPENSSL_instrument_halt");
208
209 # Essentially there is only one use for this function. Under DJGPP:
210 #
211 #       #include <go32.h>
212 #       ...
213 #       i=OPENSSL_far_spin(_dos_ds,0x46c);
214 #       ...
215 # to obtain the number of spins till closest timer interrupt.
216
217 &function_begin_B("OPENSSL_far_spin");
218         &pushf  ();
219         &pop    ("eax");
220         &bt     ("eax",9);
221         &jnc    (&label("nospin"));     # interrupts are disabled
222
223         &mov    ("eax",&DWP(4,"esp"));
224         &mov    ("ecx",&DWP(8,"esp"));
225         &data_word (0x90d88e1e);        # push %ds, mov %eax,%ds
226         &xor    ("eax","eax");
227         &mov    ("edx",&DWP(0,"ecx"));
228         &jmp    (&label("spin"));
229
230         &align  (16);
231 &set_label("spin");
232         &inc    ("eax");
233         &cmp    ("edx",&DWP(0,"ecx"));
234         &je     (&label("spin"));
235
236         &data_word (0x1f909090);        # pop   %ds
237         &ret    ();
238
239 &set_label("nospin");
240         &xor    ("eax","eax");
241         &xor    ("edx","edx");
242         &ret    ();
243 &function_end_B("OPENSSL_far_spin");
244
245 &function_begin_B("OPENSSL_wipe_cpu","EXTRN\t_OPENSSL_ia32cap_P:DWORD");
246         &xor    ("eax","eax");
247         &xor    ("edx","edx");
248         &picmeup("ecx","OPENSSL_ia32cap_P");
249         &mov    ("ecx",&DWP(0,"ecx"));
250         &bt     (&DWP(0,"ecx"),1);
251         &jnc    (&label("no_x87"));
252         if ($sse2) {
253                 &and    ("ecx",1<<26|1<<24);    # check SSE2 and FXSR bits
254                 &cmp    ("ecx",1<<26|1<<24);
255                 &jne    (&label("no_sse2"));
256                 &pxor   ("xmm0","xmm0");
257                 &pxor   ("xmm1","xmm1");
258                 &pxor   ("xmm2","xmm2");
259                 &pxor   ("xmm3","xmm3");
260                 &pxor   ("xmm4","xmm4");
261                 &pxor   ("xmm5","xmm5");
262                 &pxor   ("xmm6","xmm6");
263                 &pxor   ("xmm7","xmm7");
264         &set_label("no_sse2");
265         }
266         # just a bunch of fldz to zap the fp/mm bank followed by finit...
267         &data_word(0xeed9eed9,0xeed9eed9,0xeed9eed9,0xeed9eed9,0x90e3db9b);
268 &set_label("no_x87");
269         &lea    ("eax",&DWP(4,"esp"));
270         &ret    ();
271 &function_end_B("OPENSSL_wipe_cpu");
272
273 &function_begin_B("OPENSSL_atomic_add");
274         &mov    ("edx",&DWP(4,"esp"));  # fetch the pointer, 1st arg
275         &mov    ("ecx",&DWP(8,"esp"));  # fetch the increment, 2nd arg
276         &push   ("ebx");
277         &nop    ();
278         &mov    ("eax",&DWP(0,"edx"));
279 &set_label("spin");
280         &lea    ("ebx",&DWP(0,"eax","ecx"));
281         &nop    ();
282         &data_word(0x1ab10ff0); # lock; cmpxchg %ebx,(%edx)     # %eax is envolved and is always reloaded
283         &jne    (&label("spin"));
284         &mov    ("eax","ebx");  # OpenSSL expects the new value
285         &pop    ("ebx");
286         &ret    ();
287 &function_end_B("OPENSSL_atomic_add");
288
289 # This function can become handy under Win32 in situations when
290 # we don't know which calling convention, __stdcall or __cdecl(*),
291 # indirect callee is using. In C it can be deployed as
292 #
293 #ifdef OPENSSL_CPUID_OBJ
294 #       type OPENSSL_indirect_call(void *f,...);
295 #       ...
296 #       OPENSSL_indirect_call(func,[up to $max arguments]);
297 #endif
298 #
299 # (*)   it's designed to work even for __fastcall if number of
300 #       arguments is 1 or 2!
301 &function_begin_B("OPENSSL_indirect_call");
302         {
303         my ($max,$i)=(7,);      # $max has to be chosen as 4*n-1
304                                 # in order to preserve eventual
305                                 # stack alignment
306         &push   ("ebp");
307         &mov    ("ebp","esp");
308         &sub    ("esp",$max*4);
309         &mov    ("ecx",&DWP(12,"ebp"));
310         &mov    (&DWP(0,"esp"),"ecx");
311         &mov    ("edx",&DWP(16,"ebp"));
312         &mov    (&DWP(4,"esp"),"edx");
313         for($i=2;$i<$max;$i++)
314                 {
315                 # Some copies will be redundant/bogus...
316                 &mov    ("eax",&DWP(12+$i*4,"ebp"));
317                 &mov    (&DWP(0+$i*4,"esp"),"eax");
318                 }
319         &call_ptr       (&DWP(8,"ebp"));# make the call...
320         &mov    ("esp","ebp");  # ... and just restore the stack pointer
321                                 # without paying attention to what we called,
322                                 # (__cdecl *func) or (__stdcall *one).
323         &pop    ("ebp");
324         &ret    ();
325         }
326 &function_end_B("OPENSSL_indirect_call");
327
328 &function_begin_B("OPENSSL_cleanse");
329         &mov    ("edx",&wparam(0));
330         &mov    ("ecx",&wparam(1));
331         &xor    ("eax","eax");
332         &cmp    ("ecx",7);
333         &jae    (&label("lot"));
334         &cmp    ("ecx",0);
335         &je     (&label("ret"));
336 &set_label("little");
337         &mov    (&BP(0,"edx"),"al");
338         &sub    ("ecx",1);
339         &lea    ("edx",&DWP(1,"edx"));
340         &jnz    (&label("little"));
341 &set_label("ret");
342         &ret    ();
343
344 &set_label("lot",16);
345         &test   ("edx",3);
346         &jz     (&label("aligned"));
347         &mov    (&BP(0,"edx"),"al");
348         &lea    ("ecx",&DWP(-1,"ecx"));
349         &lea    ("edx",&DWP(1,"edx"));
350         &jmp    (&label("lot"));
351 &set_label("aligned");
352         &mov    (&DWP(0,"edx"),"eax");
353         &lea    ("ecx",&DWP(-4,"ecx"));
354         &test   ("ecx",-4);
355         &lea    ("edx",&DWP(4,"edx"));
356         &jnz    (&label("aligned"));
357         &cmp    ("ecx",0);
358         &jne    (&label("little"));
359         &ret    ();
360 &function_end_B("OPENSSL_cleanse");
361
362 {
363 my $lasttick = "esi";
364 my $lastdiff = "ebx";
365 my $out = "edi";
366 my $cnt = "ecx";
367 my $max = "ebp";
368
369 &function_begin("OPENSSL_instrument_bus");
370     &mov        ("eax",0);
371     if ($sse2) {
372         &picmeup("edx","OPENSSL_ia32cap_P");
373         &bt     (&DWP(0,"edx"),4);
374         &jnc    (&label("nogo"));       # no TSC
375         &bt     (&DWP(0,"edx"),19);
376         &jnc    (&label("nogo"));       # no CLFLUSH
377
378         &mov    ($out,&wparam(0));      # load arguments
379         &mov    ($cnt,&wparam(1));
380
381         # collect 1st tick
382         &rdtsc  ();
383         &mov    ($lasttick,"eax");      # lasttick = tick
384         &mov    ($lastdiff,0);          # lastdiff = 0
385         &clflush(&DWP(0,$out));
386         &data_byte(0xf0);               # lock
387         &add    (&DWP(0,$out),$lastdiff);
388         &jmp    (&label("loop"));
389
390 &set_label("loop",16);
391         &rdtsc  ();
392         &mov    ("edx","eax");          # put aside tick (yes, I neglect edx)
393         &sub    ("eax",$lasttick);      # diff
394         &mov    ($lasttick,"edx");      # lasttick = tick
395         &mov    ($lastdiff,"eax");      # lastdiff = diff
396         &clflush(&DWP(0,$out));
397         &data_byte(0xf0);               # lock
398         &add    (&DWP(0,$out),"eax");   # accumulate diff
399         &lea    ($out,&DWP(4,$out));    # ++$out
400         &sub    ($cnt,1);               # --$cnt
401         &jnz    (&label("loop"));
402
403         &mov    ("eax",&wparam(1));
404 &set_label("nogo");
405     }
406 &function_end("OPENSSL_instrument_bus");
407
408 &function_begin("OPENSSL_instrument_bus2");
409     &mov        ("eax",0);
410     if ($sse2) {
411         &picmeup("edx","OPENSSL_ia32cap_P");
412         &bt     (&DWP(0,"edx"),4);
413         &jnc    (&label("nogo"));       # no TSC
414         &bt     (&DWP(0,"edx"),19);
415         &jnc    (&label("nogo"));       # no CLFLUSH
416
417         &mov    ($out,&wparam(0));      # load arguments
418         &mov    ($cnt,&wparam(1));
419         &mov    ($max,&wparam(2));
420
421         &rdtsc  ();                     # collect 1st tick
422         &mov    ($lasttick,"eax");      # lasttick = tick
423         &mov    ($lastdiff,0);          # lastdiff = 0
424
425         &clflush(&DWP(0,$out));
426         &data_byte(0xf0);               # lock
427         &add    (&DWP(0,$out),$lastdiff);
428
429         &rdtsc  ();                     # collect 1st diff
430         &mov    ("edx","eax");          # put aside tick (yes, I neglect edx)
431         &sub    ("eax",$lasttick);      # diff
432         &mov    ($lasttick,"edx");      # lasttick = tick
433         &mov    ($lastdiff,"eax");      # lastdiff = diff
434         &jmp    (&label("loop2"));
435
436 &set_label("loop2",16);
437         &clflush(&DWP(0,$out));
438         &data_byte(0xf0);               # lock
439         &add    (&DWP(0,$out),"eax");   # accumulate diff
440
441         &sub    ($max,1);
442         &jz     (&label("done2"));
443
444         &rdtsc  ();
445         &mov    ("edx","eax");          # put aside tick (yes, I neglect edx)
446         &sub    ("eax",$lasttick);      # diff
447         &mov    ($lasttick,"edx");      # lasttick = tick
448         &cmp    ("eax",$lastdiff);
449         &mov    ($lastdiff,"eax");      # lastdiff = diff
450         &mov    ("edx",0);
451         &setne  ("dl");
452         &sub    ($cnt,"edx");           # conditional --$cnt
453         &lea    ($out,&DWP(0,$out,"edx",4));    # conditional ++$out
454         &jnz    (&label("loop2"));
455
456 &set_label("done2");
457         &mov    ("eax",&wparam(1));
458         &sub    ("eax",$cnt);
459 &set_label("nogo");
460     }
461 &function_end("OPENSSL_instrument_bus2");
462 }
463
464 &function_begin_B("OPENSSL_ia32_rdrand");
465         &mov    ("ecx",8);
466 &set_label("loop");
467         &rdrand ("eax");
468         &jc     (&label("break"));
469         &loop   (&label("loop"));
470 &set_label("break");
471         &cmp    ("eax",0);
472         &cmove  ("eax","ecx");
473         &ret    ();
474 &function_end_B("OPENSSL_ia32_rdrand");
475
476 &function_begin_B("OPENSSL_ia32_rdseed");
477         &mov    ("ecx",8);
478 &set_label("loop");
479         &rdseed ("eax");
480         &jc     (&label("break"));
481         &loop   (&label("loop"));
482 &set_label("break");
483         &cmp    ("eax",0);
484         &cmove  ("eax","ecx");
485         &ret    ();
486 &function_end_B("OPENSSL_ia32_rdseed");
487
488 &initseg("OPENSSL_cpuid_setup");
489
490 &hidden("OPENSSL_cpuid_setup");
491 &hidden("OPENSSL_ia32cap_P");
492
493 &asm_finish();
494
495 close STDOUT;