1238384Sjkim#include <stdio.h>
2238384Sjkim#include <stdlib.h>
3238384Sjkim#include <string.h>
4238384Sjkim#include <setjmp.h>
5238384Sjkim#include <signal.h>
6238384Sjkim#include <sys/time.h>
7238384Sjkim#include <openssl/bn.h>
8238384Sjkim
9280304Sjkim#define SPARCV9_TICK_PRIVILEGED (1<<0)
10280304Sjkim#define SPARCV9_PREFER_FPU      (1<<1)
11280304Sjkim#define SPARCV9_VIS1            (1<<2)
12280304Sjkim#define SPARCV9_VIS2            (1<<3) /* reserved */
13280304Sjkim#define SPARCV9_FMADD           (1<<4) /* reserved for SPARC64 V */
14238384Sjkim
15280304Sjkimstatic int OPENSSL_sparcv9cap_P = SPARCV9_TICK_PRIVILEGED;
16238384Sjkim
17280304Sjkimint bn_mul_mont(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
18280304Sjkim                const BN_ULONG *np, const BN_ULONG *n0, int num)
19280304Sjkim{
20280304Sjkim    int bn_mul_mont_fpu(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
21280304Sjkim                        const BN_ULONG *np, const BN_ULONG *n0, int num);
22280304Sjkim    int bn_mul_mont_int(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
23280304Sjkim                        const BN_ULONG *np, const BN_ULONG *n0, int num);
24238384Sjkim
25280304Sjkim    if (num >= 8 && !(num & 1) &&
26280304Sjkim        (OPENSSL_sparcv9cap_P & (SPARCV9_PREFER_FPU | SPARCV9_VIS1)) ==
27280304Sjkim        (SPARCV9_PREFER_FPU | SPARCV9_VIS1))
28280304Sjkim        return bn_mul_mont_fpu(rp, ap, bp, np, n0, num);
29280304Sjkim    else
30280304Sjkim        return bn_mul_mont_int(rp, ap, bp, np, n0, num);
31280304Sjkim}
32238384Sjkim
33280304Sjkimunsigned long _sparcv9_rdtick(void);
34280304Sjkimvoid _sparcv9_vis1_probe(void);
35280304Sjkimunsigned long _sparcv9_vis1_instrument(void);
36280304Sjkimvoid _sparcv9_vis2_probe(void);
37280304Sjkimvoid _sparcv9_fmadd_probe(void);
38238384Sjkim
39238384Sjkimunsigned long OPENSSL_rdtsc(void)
40280304Sjkim{
41280304Sjkim    if (OPENSSL_sparcv9cap_P & SPARCV9_TICK_PRIVILEGED)
42238384Sjkim#if defined(__sun) && defined(__SVR4)
43280304Sjkim        return gethrtime();
44238384Sjkim#else
45280304Sjkim        return 0;
46238384Sjkim#endif
47280304Sjkim    else
48280304Sjkim        return _sparcv9_rdtick();
49280304Sjkim}
50238384Sjkim
51238384Sjkim#if 0 && defined(__sun) && defined(__SVR4)
52280304Sjkim/*
53280304Sjkim * This code path is disabled, because of incompatibility of libdevinfo.so.1
54280304Sjkim * and libmalloc.so.1 (see below for details)
55238384Sjkim */
56280304Sjkim# include <malloc.h>
57280304Sjkim# include <dlfcn.h>
58280304Sjkim# include <libdevinfo.h>
59280304Sjkim# include <sys/systeminfo.h>
60238384Sjkim
61280304Sjkimtypedef di_node_t(*di_init_t) (const char *, uint_t);
62280304Sjkimtypedef void (*di_fini_t) (di_node_t);
63280304Sjkimtypedef char *(*di_node_name_t) (di_node_t);
64280304Sjkimtypedef int (*di_walk_node_t) (di_node_t, uint_t, di_node_name_t,
65280304Sjkim                               int (*)(di_node_t, di_node_name_t));
66238384Sjkim
67280304Sjkim# define DLLINK(h,name) (name=(name##_t)dlsym((h),#name))
68238384Sjkim
69238384Sjkimstatic int walk_nodename(di_node_t node, di_node_name_t di_node_name)
70280304Sjkim{
71280304Sjkim    char *name = (*di_node_name) (node);
72238384Sjkim
73280304Sjkim    /* This is expected to catch all UltraSPARC flavors prior T1 */
74280304Sjkim    if (!strcmp(name, "SUNW,UltraSPARC") ||
75280304Sjkim        /* covers II,III,IV */
76280304Sjkim        !strncmp(name, "SUNW,UltraSPARC-I", 17)) {
77280304Sjkim        OPENSSL_sparcv9cap_P |= SPARCV9_PREFER_FPU | SPARCV9_VIS1;
78238384Sjkim
79280304Sjkim        /* %tick is privileged only on UltraSPARC-I/II, but not IIe */
80280304Sjkim        if (name[14] != '\0' && name[17] != '\0' && name[18] != '\0')
81280304Sjkim            OPENSSL_sparcv9cap_P &= ~SPARCV9_TICK_PRIVILEGED;
82238384Sjkim
83280304Sjkim        return DI_WALK_TERMINATE;
84280304Sjkim    }
85280304Sjkim    /* This is expected to catch remaining UltraSPARCs, such as T1 */
86280304Sjkim    else if (!strncmp(name, "SUNW,UltraSPARC", 15)) {
87280304Sjkim        OPENSSL_sparcv9cap_P &= ~SPARCV9_TICK_PRIVILEGED;
88238384Sjkim
89280304Sjkim        return DI_WALK_TERMINATE;
90280304Sjkim    }
91238384Sjkim
92280304Sjkim    return DI_WALK_CONTINUE;
93280304Sjkim}
94238384Sjkim
95238384Sjkimvoid OPENSSL_cpuid_setup(void)
96280304Sjkim{
97280304Sjkim    void *h;
98280304Sjkim    char *e, si[256];
99280304Sjkim    static int trigger = 0;
100238384Sjkim
101280304Sjkim    if (trigger)
102280304Sjkim        return;
103280304Sjkim    trigger = 1;
104238384Sjkim
105280304Sjkim    if ((e = getenv("OPENSSL_sparcv9cap"))) {
106280304Sjkim        OPENSSL_sparcv9cap_P = strtoul(e, NULL, 0);
107280304Sjkim        return;
108280304Sjkim    }
109238384Sjkim
110280304Sjkim    if (sysinfo(SI_MACHINE, si, sizeof(si)) > 0) {
111280304Sjkim        if (strcmp(si, "sun4v"))
112280304Sjkim            /* FPU is preferred for all CPUs, but US-T1/2 */
113280304Sjkim            OPENSSL_sparcv9cap_P |= SPARCV9_PREFER_FPU;
114280304Sjkim    }
115238384Sjkim
116280304Sjkim    if (sysinfo(SI_ISALIST, si, sizeof(si)) > 0) {
117280304Sjkim        if (strstr(si, "+vis"))
118280304Sjkim            OPENSSL_sparcv9cap_P |= SPARCV9_VIS1;
119280304Sjkim        if (strstr(si, "+vis2")) {
120280304Sjkim            OPENSSL_sparcv9cap_P |= SPARCV9_VIS2;
121280304Sjkim            OPENSSL_sparcv9cap_P &= ~SPARCV9_TICK_PRIVILEGED;
122280304Sjkim            return;
123280304Sjkim        }
124280304Sjkim    }
125280304Sjkim# ifdef M_KEEP
126280304Sjkim    /*
127280304Sjkim     * Solaris libdevinfo.so.1 is effectively incomatible with
128280304Sjkim     * libmalloc.so.1. Specifically, if application is linked with
129280304Sjkim     * -lmalloc, it crashes upon startup with SIGSEGV in
130280304Sjkim     * free(3LIBMALLOC) called by di_fini. Prior call to
131280304Sjkim     * mallopt(M_KEEP,0) somehow helps... But not always...
132280304Sjkim     */
133280304Sjkim    if ((h = dlopen(NULL, RTLD_LAZY))) {
134280304Sjkim        union {
135280304Sjkim            void *p;
136280304Sjkim            int (*f) (int, int);
137280304Sjkim        } sym;
138280304Sjkim        if ((sym.p = dlsym(h, "mallopt")))
139280304Sjkim            (*sym.f) (M_KEEP, 0);
140280304Sjkim        dlclose(h);
141280304Sjkim    }
142280304Sjkim# endif
143280304Sjkim    if ((h = dlopen("libdevinfo.so.1", RTLD_LAZY)))
144280304Sjkim        do {
145280304Sjkim            di_init_t di_init;
146280304Sjkim            di_fini_t di_fini;
147280304Sjkim            di_walk_node_t di_walk_node;
148280304Sjkim            di_node_name_t di_node_name;
149280304Sjkim            di_node_t root_node;
150238384Sjkim
151280304Sjkim            if (!DLLINK(h, di_init))
152280304Sjkim                break;
153280304Sjkim            if (!DLLINK(h, di_fini))
154280304Sjkim                break;
155280304Sjkim            if (!DLLINK(h, di_walk_node))
156280304Sjkim                break;
157280304Sjkim            if (!DLLINK(h, di_node_name))
158280304Sjkim                break;
159238384Sjkim
160280304Sjkim            if ((root_node = (*di_init) ("/", DINFOSUBTREE)) != DI_NODE_NIL) {
161280304Sjkim                (*di_walk_node) (root_node, DI_WALK_SIBFIRST,
162280304Sjkim                                 di_node_name, walk_nodename);
163280304Sjkim                (*di_fini) (root_node);
164280304Sjkim            }
165280304Sjkim        } while (0);
166238384Sjkim
167280304Sjkim    if (h)
168280304Sjkim        dlclose(h);
169280304Sjkim}
170238384Sjkim
171238384Sjkim#else
172238384Sjkim
173238384Sjkimstatic sigjmp_buf common_jmp;
174280304Sjkimstatic void common_handler(int sig)
175280304Sjkim{
176280304Sjkim    siglongjmp(common_jmp, sig);
177280304Sjkim}
178238384Sjkim
179238384Sjkimvoid OPENSSL_cpuid_setup(void)
180280304Sjkim{
181280304Sjkim    char *e;
182280304Sjkim    struct sigaction common_act, ill_oact, bus_oact;
183280304Sjkim    sigset_t all_masked, oset;
184280304Sjkim    static int trigger = 0;
185238384Sjkim
186280304Sjkim    if (trigger)
187280304Sjkim        return;
188280304Sjkim    trigger = 1;
189238384Sjkim
190280304Sjkim    if ((e = getenv("OPENSSL_sparcv9cap"))) {
191280304Sjkim        OPENSSL_sparcv9cap_P = strtoul(e, NULL, 0);
192280304Sjkim        return;
193280304Sjkim    }
194238384Sjkim
195280304Sjkim    /* Initial value, fits UltraSPARC-I&II... */
196280304Sjkim    OPENSSL_sparcv9cap_P = SPARCV9_PREFER_FPU | SPARCV9_TICK_PRIVILEGED;
197238384Sjkim
198280304Sjkim    sigfillset(&all_masked);
199280304Sjkim    sigdelset(&all_masked, SIGILL);
200280304Sjkim    sigdelset(&all_masked, SIGTRAP);
201280304Sjkim# ifdef SIGEMT
202280304Sjkim    sigdelset(&all_masked, SIGEMT);
203280304Sjkim# endif
204280304Sjkim    sigdelset(&all_masked, SIGFPE);
205280304Sjkim    sigdelset(&all_masked, SIGBUS);
206280304Sjkim    sigdelset(&all_masked, SIGSEGV);
207280304Sjkim    sigprocmask(SIG_SETMASK, &all_masked, &oset);
208238384Sjkim
209280304Sjkim    memset(&common_act, 0, sizeof(common_act));
210280304Sjkim    common_act.sa_handler = common_handler;
211280304Sjkim    common_act.sa_mask = all_masked;
212238384Sjkim
213280304Sjkim    sigaction(SIGILL, &common_act, &ill_oact);
214280304Sjkim    sigaction(SIGBUS, &common_act, &bus_oact); /* T1 fails 16-bit ldda [on
215280304Sjkim                                                * Linux] */
216238384Sjkim
217280304Sjkim    if (sigsetjmp(common_jmp, 1) == 0) {
218280304Sjkim        _sparcv9_rdtick();
219280304Sjkim        OPENSSL_sparcv9cap_P &= ~SPARCV9_TICK_PRIVILEGED;
220280304Sjkim    }
221238384Sjkim
222280304Sjkim    if (sigsetjmp(common_jmp, 1) == 0) {
223280304Sjkim        _sparcv9_vis1_probe();
224280304Sjkim        OPENSSL_sparcv9cap_P |= SPARCV9_VIS1;
225280304Sjkim        /* detect UltraSPARC-Tx, see sparccpud.S for details... */
226280304Sjkim        if (_sparcv9_vis1_instrument() >= 12)
227280304Sjkim            OPENSSL_sparcv9cap_P &= ~(SPARCV9_VIS1 | SPARCV9_PREFER_FPU);
228280304Sjkim        else {
229280304Sjkim            _sparcv9_vis2_probe();
230280304Sjkim            OPENSSL_sparcv9cap_P |= SPARCV9_VIS2;
231280304Sjkim        }
232280304Sjkim    }
233238384Sjkim
234280304Sjkim    if (sigsetjmp(common_jmp, 1) == 0) {
235280304Sjkim        _sparcv9_fmadd_probe();
236280304Sjkim        OPENSSL_sparcv9cap_P |= SPARCV9_FMADD;
237280304Sjkim    }
238238384Sjkim
239280304Sjkim    sigaction(SIGBUS, &bus_oact, NULL);
240280304Sjkim    sigaction(SIGILL, &ill_oact, NULL);
241238384Sjkim
242280304Sjkim    sigprocmask(SIG_SETMASK, &oset, NULL);
243280304Sjkim}
244280304Sjkim
245238384Sjkim#endif
246