ppccap.c revision 296341
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <setjmp.h>
5#include <signal.h>
6#include <unistd.h>
7#include <crypto.h>
8#include <openssl/bn.h>
9
10#define PPC_FPU64       (1<<0)
11#define PPC_ALTIVEC     (1<<1)
12
13static int OPENSSL_ppccap_P = 0;
14
15static sigset_t all_masked;
16
17#ifdef OPENSSL_BN_ASM_MONT
18int bn_mul_mont(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
19                const BN_ULONG *np, const BN_ULONG *n0, int num)
20{
21    int bn_mul_mont_fpu64(BN_ULONG *rp, const BN_ULONG *ap,
22                          const BN_ULONG *bp, const BN_ULONG *np,
23                          const BN_ULONG *n0, int num);
24    int bn_mul_mont_int(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
25                        const BN_ULONG *np, const BN_ULONG *n0, int num);
26
27    if (sizeof(size_t) == 4) {
28# if (defined(__APPLE__) && defined(__MACH__))
29        if (num >= 8 && (num & 3) == 0 && (OPENSSL_ppccap_P & PPC_FPU64))
30            return bn_mul_mont_fpu64(rp, ap, bp, np, n0, num);
31# else
32        /*
33         * boundary of 32 was experimentally determined on Linux 2.6.22,
34         * might have to be adjusted on AIX...
35         */
36        if (num >= 32 && (num & 3) == 0 && (OPENSSL_ppccap_P & PPC_FPU64)) {
37            sigset_t oset;
38            int ret;
39
40            sigprocmask(SIG_SETMASK, &all_masked, &oset);
41            ret = bn_mul_mont_fpu64(rp, ap, bp, np, n0, num);
42            sigprocmask(SIG_SETMASK, &oset, NULL);
43
44            return ret;
45        }
46# endif
47    } else if ((OPENSSL_ppccap_P & PPC_FPU64))
48        /*
49         * this is a "must" on POWER6, but run-time detection is not
50         * implemented yet...
51         */
52        return bn_mul_mont_fpu64(rp, ap, bp, np, n0, num);
53
54    return bn_mul_mont_int(rp, ap, bp, np, n0, num);
55}
56#endif
57
58static sigjmp_buf ill_jmp;
59static void ill_handler(int sig)
60{
61    siglongjmp(ill_jmp, sig);
62}
63
64void OPENSSL_ppc64_probe(void);
65void OPENSSL_altivec_probe(void);
66
67void OPENSSL_cpuid_setup(void)
68{
69    char *e;
70    struct sigaction ill_oact, ill_act;
71    sigset_t oset;
72    static int trigger = 0;
73
74    if (trigger)
75        return;
76    trigger = 1;
77
78    sigfillset(&all_masked);
79    sigdelset(&all_masked, SIGILL);
80    sigdelset(&all_masked, SIGTRAP);
81#ifdef SIGEMT
82    sigdelset(&all_masked, SIGEMT);
83#endif
84    sigdelset(&all_masked, SIGFPE);
85    sigdelset(&all_masked, SIGBUS);
86    sigdelset(&all_masked, SIGSEGV);
87
88    if ((e = getenv("OPENSSL_ppccap"))) {
89        OPENSSL_ppccap_P = strtoul(e, NULL, 0);
90        return;
91    }
92
93    OPENSSL_ppccap_P = 0;
94
95#if defined(_AIX)
96    if (sizeof(size_t) == 4
97# if defined(_SC_AIX_KERNEL_BITMODE)
98        && sysconf(_SC_AIX_KERNEL_BITMODE) != 64
99# endif
100        )
101        return;
102#endif
103
104    memset(&ill_act, 0, sizeof(ill_act));
105    ill_act.sa_handler = ill_handler;
106    ill_act.sa_mask = all_masked;
107
108    sigprocmask(SIG_SETMASK, &ill_act.sa_mask, &oset);
109    sigaction(SIGILL, &ill_act, &ill_oact);
110
111    if (sizeof(size_t) == 4) {
112        if (sigsetjmp(ill_jmp, 1) == 0) {
113            OPENSSL_ppc64_probe();
114            OPENSSL_ppccap_P |= PPC_FPU64;
115        }
116    } else {
117        /*
118         * Wanted code detecting POWER6 CPU and setting PPC_FPU64
119         */
120    }
121
122    if (sigsetjmp(ill_jmp, 1) == 0) {
123        OPENSSL_altivec_probe();
124        OPENSSL_ppccap_P |= PPC_ALTIVEC;
125    }
126
127    sigaction(SIGILL, &ill_oact, NULL);
128    sigprocmask(SIG_SETMASK, &oset, NULL);
129}
130