1/* $OpenBSD: sparcv9cap.c,v 1.7 2014/06/20 21:00:46 deraadt Exp $ */
2#include <stdio.h>
3#include <stdlib.h>
4#include <string.h>
5#include <setjmp.h>
6#include <signal.h>
7#include <sys/time.h>
8#include <openssl/bn.h>
9
10#define SPARCV9_PREFER_FPU	(1<<1)
11#define SPARCV9_VIS1		(1<<2)
12#define SPARCV9_VIS2		(1<<3)	/* reserved */
13#define SPARCV9_FMADD		(1<<4)	/* reserved for SPARC64 V */
14
15static int OPENSSL_sparcv9cap_P = 0;
16
17int
18bn_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_fpu(BN_ULONG *rp, const BN_ULONG *ap, const BN_ULONG *bp, const BN_ULONG *np, const BN_ULONG *n0, int num);
22	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);
23
24	if (num >= 8 && !(num & 1) &&
25	    (OPENSSL_sparcv9cap_P & (SPARCV9_PREFER_FPU|SPARCV9_VIS1)) ==
26	    (SPARCV9_PREFER_FPU|SPARCV9_VIS1))
27		return bn_mul_mont_fpu(rp, ap, bp, np, n0, num);
28	else
29		return bn_mul_mont_int(rp, ap, bp, np, n0, num);
30}
31
32void		_sparcv9_vis1_probe(void);
33unsigned long	_sparcv9_vis1_instrument(void);
34void		_sparcv9_vis2_probe(void);
35void		_sparcv9_fmadd_probe(void);
36
37static sigjmp_buf common_jmp;
38static void
39common_handler(int sig)
40{
41	siglongjmp(common_jmp, sig);
42}
43
44void
45OPENSSL_cpuid_setup(void)
46{
47	char *e;
48	struct sigaction	common_act, ill_oact, bus_oact;
49	sigset_t		all_masked, oset;
50	static int trigger = 0;
51
52	if (trigger)
53		return;
54	trigger = 1;
55
56	/* Initial value, fits UltraSPARC-I&II... */
57	OPENSSL_sparcv9cap_P = SPARCV9_PREFER_FPU;
58
59	sigfillset(&all_masked);
60	sigdelset(&all_masked, SIGILL);
61	sigdelset(&all_masked, SIGTRAP);
62#ifdef SIGEMT
63	sigdelset(&all_masked, SIGEMT);
64#endif
65	sigdelset(&all_masked, SIGFPE);
66	sigdelset(&all_masked, SIGBUS);
67	sigdelset(&all_masked, SIGSEGV);
68	sigprocmask(SIG_SETMASK, &all_masked, &oset);
69
70	memset(&common_act, 0, sizeof(common_act));
71	common_act.sa_handler = common_handler;
72	common_act.sa_mask = all_masked;
73
74	sigaction(SIGILL, &common_act, &ill_oact);
75	sigaction(SIGBUS,&common_act,&bus_oact);/* T1 fails 16-bit ldda [on Linux] */
76
77	if (sigsetjmp(common_jmp, 1) == 0) {
78		_sparcv9_vis1_probe();
79		OPENSSL_sparcv9cap_P |= SPARCV9_VIS1;
80		/* detect UltraSPARC-Tx, see sparccpud.S for details... */
81		if (_sparcv9_vis1_instrument() >= 12)
82			OPENSSL_sparcv9cap_P &= ~(SPARCV9_VIS1|SPARCV9_PREFER_FPU);
83		else {
84			_sparcv9_vis2_probe();
85			OPENSSL_sparcv9cap_P |= SPARCV9_VIS2;
86		}
87	}
88
89	if (sigsetjmp(common_jmp, 1) == 0) {
90		_sparcv9_fmadd_probe();
91		OPENSSL_sparcv9cap_P |= SPARCV9_FMADD;
92	}
93
94	sigaction(SIGBUS, &bus_oact, NULL);
95	sigaction(SIGILL, &ill_oact, NULL);
96
97	sigprocmask(SIG_SETMASK, &oset, NULL);
98}
99