1238384Sjkim#include <stdio.h>
2238384Sjkim#include <stdlib.h>
3238384Sjkim#include <string.h>
4238384Sjkim#include <setjmp.h>
5238384Sjkim#include <signal.h>
6246772Sjkim#include <unistd.h>
7238384Sjkim#include <crypto.h>
8238384Sjkim#include <openssl/bn.h>
9238384Sjkim
10238384Sjkim#define PPC_FPU64	(1<<0)
11238384Sjkim#define PPC_ALTIVEC	(1<<1)
12238384Sjkim
13238384Sjkimstatic int OPENSSL_ppccap_P = 0;
14238384Sjkim
15238384Sjkimstatic sigset_t all_masked;
16238384Sjkim
17238384Sjkim#ifdef OPENSSL_BN_ASM_MONT
18238384Sjkimint bn_mul_mont(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp, const BN_ULONG *np, const BN_ULONG *n0, int num)
19238384Sjkim	{
20238384Sjkim	int bn_mul_mont_fpu64(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp, const BN_ULONG *np, const BN_ULONG *n0, int num);
21238384Sjkim	int bn_mul_mont_int(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp, const BN_ULONG *np, const BN_ULONG *n0, int num);
22238384Sjkim
23238384Sjkim	if (sizeof(size_t)==4)
24238384Sjkim		{
25238384Sjkim#if (defined(__APPLE__) && defined(__MACH__))
26238384Sjkim		if (num>=8 && (num&3)==0 && (OPENSSL_ppccap_P&PPC_FPU64))
27238384Sjkim			return bn_mul_mont_fpu64(rp,ap,bp,np,n0,num);
28238384Sjkim#else
29238384Sjkim		/* boundary of 32 was experimentally determined on
30238384Sjkim		   Linux 2.6.22, might have to be adjusted on AIX... */
31238384Sjkim		if (num>=32 && (num&3)==0 && (OPENSSL_ppccap_P&PPC_FPU64))
32238384Sjkim			{
33238384Sjkim			sigset_t oset;
34238384Sjkim			int ret;
35238384Sjkim
36238384Sjkim			sigprocmask(SIG_SETMASK,&all_masked,&oset);
37238384Sjkim			ret=bn_mul_mont_fpu64(rp,ap,bp,np,n0,num);
38238384Sjkim			sigprocmask(SIG_SETMASK,&oset,NULL);
39238384Sjkim
40238384Sjkim			return ret;
41238384Sjkim			}
42238384Sjkim#endif
43238384Sjkim		}
44238384Sjkim	else if ((OPENSSL_ppccap_P&PPC_FPU64))
45238384Sjkim		/* this is a "must" on POWER6, but run-time detection
46238384Sjkim		 * is not implemented yet... */
47238384Sjkim		return bn_mul_mont_fpu64(rp,ap,bp,np,n0,num);
48238384Sjkim
49238384Sjkim	return bn_mul_mont_int(rp,ap,bp,np,n0,num);
50238384Sjkim	}
51238384Sjkim#endif
52238384Sjkim
53238384Sjkimstatic sigjmp_buf ill_jmp;
54238384Sjkimstatic void ill_handler (int sig) { siglongjmp(ill_jmp,sig); }
55238384Sjkim
56238384Sjkimvoid OPENSSL_ppc64_probe(void);
57246772Sjkimvoid OPENSSL_altivec_probe(void);
58238384Sjkim
59238384Sjkimvoid OPENSSL_cpuid_setup(void)
60238384Sjkim	{
61238384Sjkim	char *e;
62238384Sjkim	struct sigaction	ill_oact,ill_act;
63238384Sjkim	sigset_t		oset;
64238384Sjkim	static int trigger=0;
65238384Sjkim
66238384Sjkim	if (trigger) return;
67238384Sjkim	trigger=1;
68238384Sjkim
69238384Sjkim	sigfillset(&all_masked);
70238384Sjkim	sigdelset(&all_masked,SIGILL);
71238384Sjkim	sigdelset(&all_masked,SIGTRAP);
72238384Sjkim#ifdef SIGEMT
73238384Sjkim	sigdelset(&all_masked,SIGEMT);
74238384Sjkim#endif
75238384Sjkim	sigdelset(&all_masked,SIGFPE);
76238384Sjkim	sigdelset(&all_masked,SIGBUS);
77238384Sjkim	sigdelset(&all_masked,SIGSEGV);
78238384Sjkim
79238384Sjkim	if ((e=getenv("OPENSSL_ppccap")))
80238384Sjkim		{
81238384Sjkim		OPENSSL_ppccap_P=strtoul(e,NULL,0);
82238384Sjkim		return;
83238384Sjkim		}
84238384Sjkim
85238384Sjkim	OPENSSL_ppccap_P = 0;
86238384Sjkim
87246772Sjkim#if defined(_AIX)
88246772Sjkim	if (sizeof(size_t)==4
89246772Sjkim# if defined(_SC_AIX_KERNEL_BITMODE)
90246772Sjkim	    && sysconf(_SC_AIX_KERNEL_BITMODE)!=64
91246772Sjkim# endif
92246772Sjkim	   )
93246772Sjkim		return;
94246772Sjkim#endif
95246772Sjkim
96238384Sjkim	memset(&ill_act,0,sizeof(ill_act));
97238384Sjkim	ill_act.sa_handler = ill_handler;
98238384Sjkim	ill_act.sa_mask    = all_masked;
99238384Sjkim
100238384Sjkim	sigprocmask(SIG_SETMASK,&ill_act.sa_mask,&oset);
101238384Sjkim	sigaction(SIGILL,&ill_act,&ill_oact);
102238384Sjkim
103238384Sjkim	if (sizeof(size_t)==4)
104238384Sjkim		{
105238384Sjkim		if (sigsetjmp(ill_jmp,1) == 0)
106238384Sjkim			{
107238384Sjkim			OPENSSL_ppc64_probe();
108238384Sjkim			OPENSSL_ppccap_P |= PPC_FPU64;
109238384Sjkim			}
110238384Sjkim		}
111238384Sjkim	else
112238384Sjkim		{
113238384Sjkim		/*
114238384Sjkim		 * Wanted code detecting POWER6 CPU and setting PPC_FPU64
115238384Sjkim		 */
116238384Sjkim		}
117238384Sjkim
118238384Sjkim	if (sigsetjmp(ill_jmp,1) == 0)
119238384Sjkim		{
120238384Sjkim		OPENSSL_altivec_probe();
121238384Sjkim		OPENSSL_ppccap_P |= PPC_ALTIVEC;
122238384Sjkim		}
123238384Sjkim
124238384Sjkim	sigaction (SIGILL,&ill_oact,NULL);
125238384Sjkim	sigprocmask(SIG_SETMASK,&oset,NULL);
126238384Sjkim	}
127