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