113ce5681Schristos #include <stdio.h>
213ce5681Schristos #include <stdlib.h>
313ce5681Schristos #include <string.h>
413ce5681Schristos #include <setjmp.h>
513ce5681Schristos #include <signal.h>
621bfa713Schristos #include <unistd.h>
713ce5681Schristos #include <crypto.h>
813ce5681Schristos #include <openssl/bn.h>
913ce5681Schristos 
1013ce5681Schristos #define PPC_FPU64       (1<<0)
1113ce5681Schristos #define PPC_ALTIVEC     (1<<1)
1213ce5681Schristos 
1313ce5681Schristos static int OPENSSL_ppccap_P = 0;
1413ce5681Schristos 
1513ce5681Schristos static sigset_t all_masked;
1613ce5681Schristos 
1713ce5681Schristos #ifdef OPENSSL_BN_ASM_MONT
18*d572d25fSspz int bn_mul_mont(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
19*d572d25fSspz                 const BN_ULONG *np, const BN_ULONG *n0, int num)
2013ce5681Schristos {
21*d572d25fSspz     int bn_mul_mont_fpu64(BN_ULONG *rp, const BN_ULONG *ap,
22*d572d25fSspz                           const BN_ULONG *bp, const BN_ULONG *np,
23*d572d25fSspz                           const BN_ULONG *n0, int num);
24*d572d25fSspz     int bn_mul_mont_int(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp,
25*d572d25fSspz                         const BN_ULONG *np, const BN_ULONG *n0, int num);
2613ce5681Schristos 
27*d572d25fSspz     if (sizeof(size_t) == 4) {
2813ce5681Schristos # if (defined(__APPLE__) && defined(__MACH__))
2913ce5681Schristos         if (num >= 8 && (num & 3) == 0 && (OPENSSL_ppccap_P & PPC_FPU64))
3013ce5681Schristos             return bn_mul_mont_fpu64(rp, ap, bp, np, n0, num);
3113ce5681Schristos # else
32*d572d25fSspz         /*
33*d572d25fSspz          * boundary of 32 was experimentally determined on Linux 2.6.22,
34*d572d25fSspz          * might have to be adjusted on AIX...
35*d572d25fSspz          */
36*d572d25fSspz         if (num >= 32 && (num & 3) == 0 && (OPENSSL_ppccap_P & PPC_FPU64)) {
3713ce5681Schristos             sigset_t oset;
3813ce5681Schristos             int ret;
3913ce5681Schristos 
4013ce5681Schristos             sigprocmask(SIG_SETMASK, &all_masked, &oset);
4113ce5681Schristos             ret = bn_mul_mont_fpu64(rp, ap, bp, np, n0, num);
4213ce5681Schristos             sigprocmask(SIG_SETMASK, &oset, NULL);
4313ce5681Schristos 
4413ce5681Schristos             return ret;
4513ce5681Schristos         }
4613ce5681Schristos # endif
47*d572d25fSspz     } else if ((OPENSSL_ppccap_P & PPC_FPU64))
48*d572d25fSspz         /*
49*d572d25fSspz          * this is a "must" on POWER6, but run-time detection is not
50*d572d25fSspz          * implemented yet...
51*d572d25fSspz          */
5213ce5681Schristos         return bn_mul_mont_fpu64(rp, ap, bp, np, n0, num);
5313ce5681Schristos 
5413ce5681Schristos     return bn_mul_mont_int(rp, ap, bp, np, n0, num);
5513ce5681Schristos }
5613ce5681Schristos #endif
5713ce5681Schristos 
5813ce5681Schristos static sigjmp_buf ill_jmp;
59*d572d25fSspz static void ill_handler(int sig)
60*d572d25fSspz {
61*d572d25fSspz     siglongjmp(ill_jmp, sig);
62*d572d25fSspz }
6313ce5681Schristos 
6413ce5681Schristos void OPENSSL_ppc64_probe(void);
6521bfa713Schristos void OPENSSL_altivec_probe(void);
6613ce5681Schristos 
6713ce5681Schristos void OPENSSL_cpuid_setup(void)
6813ce5681Schristos {
6913ce5681Schristos     char *e;
7013ce5681Schristos     struct sigaction ill_oact, ill_act;
7113ce5681Schristos     sigset_t oset;
7213ce5681Schristos     static int trigger = 0;
7313ce5681Schristos 
74*d572d25fSspz     if (trigger)
75*d572d25fSspz         return;
7613ce5681Schristos     trigger = 1;
7713ce5681Schristos 
7813ce5681Schristos     sigfillset(&all_masked);
7913ce5681Schristos     sigdelset(&all_masked, SIGILL);
8013ce5681Schristos     sigdelset(&all_masked, SIGTRAP);
8113ce5681Schristos #ifdef SIGEMT
8213ce5681Schristos     sigdelset(&all_masked, SIGEMT);
8313ce5681Schristos #endif
8413ce5681Schristos     sigdelset(&all_masked, SIGFPE);
8513ce5681Schristos     sigdelset(&all_masked, SIGBUS);
8613ce5681Schristos     sigdelset(&all_masked, SIGSEGV);
8713ce5681Schristos 
88*d572d25fSspz     if ((e = getenv("OPENSSL_ppccap"))) {
8913ce5681Schristos         OPENSSL_ppccap_P = strtoul(e, NULL, 0);
9013ce5681Schristos         return;
9113ce5681Schristos     }
9213ce5681Schristos 
9313ce5681Schristos     OPENSSL_ppccap_P = 0;
9413ce5681Schristos 
9521bfa713Schristos #if defined(_AIX)
9621bfa713Schristos     if (sizeof(size_t) == 4
9721bfa713Schristos # if defined(_SC_AIX_KERNEL_BITMODE)
9821bfa713Schristos         && sysconf(_SC_AIX_KERNEL_BITMODE) != 64
9921bfa713Schristos # endif
10021bfa713Schristos         )
10121bfa713Schristos         return;
10221bfa713Schristos #endif
10321bfa713Schristos 
10413ce5681Schristos     memset(&ill_act, 0, sizeof(ill_act));
10513ce5681Schristos     ill_act.sa_handler = ill_handler;
10613ce5681Schristos     ill_act.sa_mask = all_masked;
10713ce5681Schristos 
10813ce5681Schristos     sigprocmask(SIG_SETMASK, &ill_act.sa_mask, &oset);
10913ce5681Schristos     sigaction(SIGILL, &ill_act, &ill_oact);
11013ce5681Schristos 
111*d572d25fSspz     if (sizeof(size_t) == 4) {
112*d572d25fSspz         if (sigsetjmp(ill_jmp, 1) == 0) {
11313ce5681Schristos             OPENSSL_ppc64_probe();
11413ce5681Schristos             OPENSSL_ppccap_P |= PPC_FPU64;
11513ce5681Schristos         }
116*d572d25fSspz     } else {
11713ce5681Schristos         /*
11813ce5681Schristos          * Wanted code detecting POWER6 CPU and setting PPC_FPU64
11913ce5681Schristos          */
12013ce5681Schristos     }
12113ce5681Schristos 
122*d572d25fSspz     if (sigsetjmp(ill_jmp, 1) == 0) {
12313ce5681Schristos         OPENSSL_altivec_probe();
12413ce5681Schristos         OPENSSL_ppccap_P |= PPC_ALTIVEC;
12513ce5681Schristos     }
12613ce5681Schristos 
12713ce5681Schristos     sigaction(SIGILL, &ill_oact, NULL);
12813ce5681Schristos     sigprocmask(SIG_SETMASK, &oset, NULL);
12913ce5681Schristos }
130