SPARCv9 assembler pack: refine CPU detection on Linux, fix for "unaligned
[openssl.git] / crypto / sparcv9cap.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <setjmp.h>
5 #include <signal.h>
6 #include <sys/time.h>
7 #include <openssl/bn.h>
8
9 #define SPARCV9_TICK_PRIVILEGED (1<<0)
10 #define SPARCV9_PREFER_FPU      (1<<1)
11 #define SPARCV9_VIS1            (1<<2)
12 #define SPARCV9_VIS2            (1<<3)  /* reserved */
13 #define SPARCV9_FMADD           (1<<4)  /* reserved for SPARC64 V */
14
15 static int OPENSSL_sparcv9cap_P=SPARCV9_TICK_PRIVILEGED;
16
17 int bn_mul_mont(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp, const BN_ULONG *np,const BN_ULONG *n0, int num)
18         {
19         int bn_mul_mont_fpu(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp, const BN_ULONG *np,const BN_ULONG *n0, int num);
20         int bn_mul_mont_int(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp, const BN_ULONG *np,const BN_ULONG *n0, int num);
21
22         if ((OPENSSL_sparcv9cap_P&(SPARCV9_PREFER_FPU|SPARCV9_VIS1)) ==
23                 (SPARCV9_PREFER_FPU|SPARCV9_VIS1))
24                 return bn_mul_mont_fpu(rp,ap,bp,np,n0,num);
25         else
26                 return bn_mul_mont_int(rp,ap,bp,np,n0,num);
27         }
28
29 unsigned long   _sparcv9_rdtick(void);
30 unsigned long   _sparcv9_rdwrasi(unsigned long);
31 void            _sparcv9_vis1_probe(void);
32
33 unsigned long OPENSSL_rdtsc(void)
34         {
35         if (OPENSSL_sparcv9cap_P&SPARCV9_TICK_PRIVILEGED)
36 #if defined(__sun) && defined(__SVR4)
37                 return gethrtime();
38 #else
39                 return 0;
40 #endif
41         else
42                 return _sparcv9_rdtick();
43         }
44
45 #if defined(__sun) && defined(__SVR4)
46
47 #include <dlfcn.h>
48 #include <libdevinfo.h>
49 #include <sys/systeminfo.h>
50
51 typedef di_node_t (*di_init_t)(const char *,uint_t);
52 typedef void      (*di_fini_t)(di_node_t);
53 typedef char *    (*di_node_name_t)(di_node_t);
54 typedef int       (*di_walk_node_t)(di_node_t,uint_t,di_node_name_t,int (*)(di_node_t,di_node_name_t));
55
56 #define DLLINK(h,name) (name=(name##_t)dlsym((h),#name))
57
58 static int walk_nodename(di_node_t node, di_node_name_t di_node_name)
59         {
60         char *name = (*di_node_name)(node);
61
62         /* This is expected to catch all UltraSPARC flavors prior T1 */
63         if (!strcmp (name,"SUNW,UltraSPARC") ||
64             !strncmp(name,"SUNW,UltraSPARC-I",17))  /* covers II,III,IV */
65                 {
66                 OPENSSL_sparcv9cap_P |= SPARCV9_PREFER_FPU|SPARCV9_VIS1;
67
68                 /* %tick is privileged only on UltraSPARC-I/II, but not IIe */
69                 if (name[14]!='\0' && name[17]!='\0' && name[18]!='\0')
70                         OPENSSL_sparcv9cap_P &= ~SPARCV9_TICK_PRIVILEGED;
71
72                 return DI_WALK_TERMINATE;
73                 }
74         /* This is expected to catch remaining UltraSPARCs, such as T1 */
75         else if (!strncmp(name,"SUNW,UltraSPARC",15))
76                 {
77                 OPENSSL_sparcv9cap_P &= ~SPARCV9_TICK_PRIVILEGED;
78
79                 return DI_WALK_TERMINATE;
80                 }
81
82         return DI_WALK_CONTINUE;
83         }
84
85 void OPENSSL_cpuid_setup(void)
86         {
87         void *h;
88         char *e,si[256];
89         static int trigger=0;
90
91         if (trigger) return;
92         trigger=1;
93
94         if ((e=getenv("OPENSSL_sparcv9cap")))
95                 {
96                 OPENSSL_sparcv9cap_P=strtoul(e,NULL,0);
97                 return;
98                 }
99
100         if (sysinfo(SI_MACHINE,si,sizeof(si))>0)
101                 {
102                 if (strcmp(si,"sun4v"))
103                         /* FPU is preferred for all CPUs, but US-T1/2 */
104                         OPENSSL_sparcv9cap_P |= SPARCV9_PREFER_FPU;
105                 }
106
107         if (sysinfo(SI_ISALIST,si,sizeof(si))>0)
108                 {
109                 if (strstr(si,"+vis"))
110                         OPENSSL_sparcv9cap_P |= SPARCV9_VIS1;
111                 if (strstr(si,"+vis2"))
112                         {
113                         OPENSSL_sparcv9cap_P |= SPARCV9_VIS2;
114                         OPENSSL_sparcv9cap_P &= ~SPARCV9_TICK_PRIVILEGED;
115                         return;
116                         }
117                 }
118
119         if ((h = dlopen("libdevinfo.so.1",RTLD_LAZY))) do
120                 {
121                 di_init_t       di_init;
122                 di_fini_t       di_fini;
123                 di_walk_node_t  di_walk_node;
124                 di_node_name_t  di_node_name;
125                 di_node_t       root_node;
126
127                 if (!DLLINK(h,di_init))         break;
128                 if (!DLLINK(h,di_fini))         break;
129                 if (!DLLINK(h,di_walk_node))    break;
130                 if (!DLLINK(h,di_node_name))    break;
131
132                 if ((root_node = (*di_init)("/",DINFOSUBTREE))!=DI_NODE_NIL)
133                         {
134                         (*di_walk_node)(root_node,DI_WALK_SIBFIRST,
135                                         di_node_name,walk_nodename);
136                         (*di_fini)(root_node);
137                         }
138                 } while(0);
139
140         if (h) dlclose(h);
141         }
142
143 #else
144
145 static sigjmp_buf common_jmp;
146 static void common_handler(int sig) { siglongjmp(common_jmp,sig); }
147
148 void OPENSSL_cpuid_setup(void)
149         {
150         char *e;
151         struct sigaction        common_act,ill_oact,bus_oact;
152         sigset_t                all_masked,oset;
153         unsigned long           oasi;
154         int                     sig;
155  
156         if ((e=getenv("OPENSSL_sparcv9cap")))
157                 {
158                 OPENSSL_sparcv9cap_P=strtoul(e,NULL,0);
159                 return;
160                 }
161
162         /* For now we assume that the rest supports UltraSPARC-I* only */
163         OPENSSL_sparcv9cap_P |= SPARCV9_PREFER_FPU|SPARCV9_VIS1;
164
165         sigfillset(&all_masked);
166         sigdelset(&all_masked,SIGILL);
167         sigdelset(&all_masked,SIGTRAP);
168 #ifdef SIGEMT
169         sigdelset(&all_masked,SIGEMT);
170 #endif
171         sigdelset(&all_masked,SIGFPE);
172         sigdelset(&all_masked,SIGBUS);
173         sigdelset(&all_masked,SIGSEGV);
174         sigprocmask(SIG_SETMASK,&all_masked,&oset);
175
176         memset(&common_act,0,sizeof(common_act));
177         common_act.sa_handler = common_handler;
178         common_act.sa_mask    = all_masked;
179
180         sigaction(SIGILL,&common_act,&ill_oact);
181         sigaction(SIGBUS,&common_act,&bus_oact);/* T1 fails 16-bit ldda */
182         oasi = _sparcv9_rdwrasi(0xD2);          /* ASI_FL16_P */
183         if ((sig=sigsetjmp(common_jmp,0)) == 0)
184                 {
185                 _sparcv9_vis1_probe();
186                 OPENSSL_sparcv9cap_P |= SPARCV9_VIS1;
187                 }
188         else if (sig == SIGBUS)                 /* T1 fails 16-bit ldda */
189                 {
190                 OPENSSL_sparcv9cap_P &= ~SPARCV9_PREFER_FPU;
191                 }
192         else
193                 {
194                 OPENSSL_sparcv9cap_P &= ~SPARCV9_VIS1;
195                 }
196         _sparcv9_rdwrasi(oasi);
197         sigaction(SIGBUS,&bus_oact,NULL);
198         sigaction(SIGILL,&ill_oact,NULL);
199
200         sigaction(SIGILL,&common_act,&ill_oact);
201         if (sigsetjmp(common_jmp,0) == 0)
202                 {
203                 _sparcv9_rdtick();
204                 OPENSSL_sparcv9cap_P &= ~SPARCV9_TICK_PRIVILEGED;
205                 }
206         else
207                 {
208                 OPENSSL_sparcv9cap_P |= SPARCV9_TICK_PRIVILEGED;
209                 }
210         sigaction(SIGILL,&ill_oact,NULL);
211
212         sigprocmask(SIG_SETMASK,&oset,NULL);
213         }
214
215 #endif