ppccap.c revision 325335
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#if defined(__linux) || defined(_AIX)
8# include <sys/utsname.h>
9#endif
10#if defined(__APPLE__) && defined(__MACH__)
11# include <sys/types.h>
12# include <sys/sysctl.h>
13#endif
14#include <openssl/crypto.h>
15#include <openssl/bn.h>
16
17#include "ppc_arch.h"
18
19unsigned int OPENSSL_ppccap_P = 0;
20
21static sigset_t all_masked;
22
23#ifdef OPENSSL_BN_ASM_MONT
24int bn_mul_mont(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
25                const BN_ULONG *np, const BN_ULONG *n0, int num)
26{
27    int bn_mul_mont_fpu64(BN_ULONG *rp, const BN_ULONG *ap,
28                          const BN_ULONG *bp, const BN_ULONG *np,
29                          const BN_ULONG *n0, int num);
30    int bn_mul_mont_int(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
31                        const BN_ULONG *np, const BN_ULONG *n0, int num);
32
33    if (sizeof(size_t) == 4) {
34# if 1 || (defined(__APPLE__) && defined(__MACH__))
35        if (num >= 8 && (num & 3) == 0 && (OPENSSL_ppccap_P & PPC_FPU64))
36            return bn_mul_mont_fpu64(rp, ap, bp, np, n0, num);
37# else
38        /*
39         * boundary of 32 was experimentally determined on Linux 2.6.22,
40         * might have to be adjusted on AIX...
41         */
42        if (num >= 32 && (num & 3) == 0 && (OPENSSL_ppccap_P & PPC_FPU64)) {
43            sigset_t oset;
44            int ret;
45
46            sigprocmask(SIG_SETMASK, &all_masked, &oset);
47            ret = bn_mul_mont_fpu64(rp, ap, bp, np, n0, num);
48            sigprocmask(SIG_SETMASK, &oset, NULL);
49
50            return ret;
51        }
52# endif
53    } else if ((OPENSSL_ppccap_P & PPC_FPU64))
54        /*
55         * this is a "must" on POWER6, but run-time detection is not
56         * implemented yet...
57         */
58        return bn_mul_mont_fpu64(rp, ap, bp, np, n0, num);
59
60    return bn_mul_mont_int(rp, ap, bp, np, n0, num);
61}
62#endif
63
64void sha256_block_p8(void *ctx, const void *inp, size_t len);
65void sha256_block_ppc(void *ctx, const void *inp, size_t len);
66void sha256_block_data_order(void *ctx, const void *inp, size_t len)
67{
68    OPENSSL_ppccap_P & PPC_CRYPTO207 ? sha256_block_p8(ctx, inp, len) :
69        sha256_block_ppc(ctx, inp, len);
70}
71
72void sha512_block_p8(void *ctx, const void *inp, size_t len);
73void sha512_block_ppc(void *ctx, const void *inp, size_t len);
74void sha512_block_data_order(void *ctx, const void *inp, size_t len)
75{
76    OPENSSL_ppccap_P & PPC_CRYPTO207 ? sha512_block_p8(ctx, inp, len) :
77        sha512_block_ppc(ctx, inp, len);
78}
79
80static sigjmp_buf ill_jmp;
81static void ill_handler(int sig)
82{
83    siglongjmp(ill_jmp, sig);
84}
85
86void OPENSSL_ppc64_probe(void);
87void OPENSSL_altivec_probe(void);
88void OPENSSL_crypto207_probe(void);
89
90void OPENSSL_cpuid_setup(void)
91{
92    char *e;
93    struct sigaction ill_oact, ill_act;
94    sigset_t oset;
95    static int trigger = 0;
96
97    if (trigger)
98        return;
99    trigger = 1;
100
101    sigfillset(&all_masked);
102    sigdelset(&all_masked, SIGILL);
103    sigdelset(&all_masked, SIGTRAP);
104#ifdef SIGEMT
105    sigdelset(&all_masked, SIGEMT);
106#endif
107    sigdelset(&all_masked, SIGFPE);
108    sigdelset(&all_masked, SIGBUS);
109    sigdelset(&all_masked, SIGSEGV);
110
111    if ((e = getenv("OPENSSL_ppccap"))) {
112        OPENSSL_ppccap_P = strtoul(e, NULL, 0);
113        return;
114    }
115
116    OPENSSL_ppccap_P = 0;
117
118#if defined(_AIX)
119    if (sizeof(size_t) == 4) {
120        struct utsname uts;
121# if defined(_SC_AIX_KERNEL_BITMODE)
122        if (sysconf(_SC_AIX_KERNEL_BITMODE) != 64)
123            return;
124# endif
125        if (uname(&uts) != 0 || atoi(uts.version) < 6)
126            return;
127    }
128#endif
129
130#if defined(__APPLE__) && defined(__MACH__)
131    {
132        int val;
133        size_t len = sizeof(val);
134
135        if (sysctlbyname("hw.optional.64bitops", &val, &len, NULL, 0) == 0) {
136            if (val)
137                OPENSSL_ppccap_P |= PPC_FPU64;
138        }
139
140        len = sizeof(val);
141        if (sysctlbyname("hw.optional.altivec", &val, &len, NULL, 0) == 0) {
142            if (val)
143                OPENSSL_ppccap_P |= PPC_ALTIVEC;
144        }
145
146        return;
147    }
148#endif
149
150    memset(&ill_act, 0, sizeof(ill_act));
151    ill_act.sa_handler = ill_handler;
152    ill_act.sa_mask = all_masked;
153
154    sigprocmask(SIG_SETMASK, &ill_act.sa_mask, &oset);
155    sigaction(SIGILL, &ill_act, &ill_oact);
156
157    if (sizeof(size_t) == 4) {
158#ifdef __linux
159        struct utsname uts;
160        if (uname(&uts) == 0 && strcmp(uts.machine, "ppc64") == 0)
161#endif
162            if (sigsetjmp(ill_jmp, 1) == 0) {
163                OPENSSL_ppc64_probe();
164                OPENSSL_ppccap_P |= PPC_FPU64;
165            }
166    } else {
167        /*
168         * Wanted code detecting POWER6 CPU and setting PPC_FPU64
169         */
170    }
171
172    if (sigsetjmp(ill_jmp, 1) == 0) {
173        OPENSSL_altivec_probe();
174        OPENSSL_ppccap_P |= PPC_ALTIVEC;
175        if (sigsetjmp(ill_jmp, 1) == 0) {
176            OPENSSL_crypto207_probe();
177            OPENSSL_ppccap_P |= PPC_CRYPTO207;
178        }
179    }
180
181    sigaction(SIGILL, &ill_oact, NULL);
182    sigprocmask(SIG_SETMASK, &oset, NULL);
183}
184