summaryrefslogtreecommitdiff
path: root/lib/libcrypto/arch/aarch64/arm64cap.c
blob: 9d75daba0baba425cfc3a8d0a00ab2a2c07cb688 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/* $OpenBSD: arm64cap.c,v 1.2 2022/03/25 17:42:07 robert Exp $ */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <setjmp.h>
#include <signal.h>
#include <openssl/crypto.h>

#if defined(__OpenBSD__)
#include <sys/sysctl.h>
#include <machine/cpu.h>	/* CPU_ID_AA64ISAR0 */
#endif

#include "arm64_arch.h"

/* ID_AA64ISAR0_EL1 required for OPENSSL_cpuid_setup */
#define	ID_AA64ISAR0_AES_SHIFT		4
#define	ID_AA64ISAR0_AES_MASK		(0xf << ID_AA64ISAR0_AES_SHIFT)
#define	ID_AA64ISAR0_AES(x)		((x) & ID_AA64ISAR0_AES_MASK)
#define	 ID_AA64ISAR0_AES_BASE		(0x1 << ID_AA64ISAR0_AES_SHIFT)
#define	 ID_AA64ISAR0_AES_PMULL		(0x2 << ID_AA64ISAR0_AES_SHIFT)
#define	ID_AA64ISAR0_SHA1_SHIFT		8
#define	ID_AA64ISAR0_SHA1_MASK		(0xf << ID_AA64ISAR0_SHA1_SHIFT)
#define	ID_AA64ISAR0_SHA1(x)		((x) & ID_AA64ISAR0_SHA1_MASK)
#define	 ID_AA64ISAR0_SHA1_BASE		(0x1 << ID_AA64ISAR0_SHA1_SHIFT)
#define	ID_AA64ISAR0_SHA2_SHIFT		12
#define	ID_AA64ISAR0_SHA2_MASK		(0xf << ID_AA64ISAR0_SHA2_SHIFT)
#define	ID_AA64ISAR0_SHA2(x)		((x) & ID_AA64ISAR0_SHA2_MASK)
#define	 ID_AA64ISAR0_SHA2_BASE		(0x1 << ID_AA64ISAR0_SHA2_SHIFT)

unsigned int OPENSSL_armcap_P;

#if defined(__GNUC__) && __GNUC__ >= 2
void OPENSSL_cpuid_setup(void) __attribute__((constructor));
#endif

#if defined(CPU_ID_AA64ISAR0)
void
OPENSSL_cpuid_setup(void)
{
	int isar0_mib[] = { CTL_MACHDEP, CPU_ID_AA64ISAR0 };
	size_t len = sizeof(uint64_t);
	uint64_t cpu_id = 0;

	if (OPENSSL_armcap_P != 0)
		return;

	if (sysctl(isar0_mib, 2, &cpu_id, &len, NULL, 0) < 0)
		return;

	OPENSSL_armcap_P |= ARMV7_NEON;

	if (ID_AA64ISAR0_AES(cpu_id) >= ID_AA64ISAR0_AES_BASE)
		OPENSSL_armcap_P |= ARMV8_AES;

	if (ID_AA64ISAR0_AES(cpu_id) >= ID_AA64ISAR0_AES_PMULL)
		OPENSSL_armcap_P |= ARMV8_PMULL;

	if (ID_AA64ISAR0_SHA1(cpu_id) >= ID_AA64ISAR0_SHA1_BASE)
		OPENSSL_armcap_P |= ARMV8_SHA1;

	if (ID_AA64ISAR0_SHA2(cpu_id) >= ID_AA64ISAR0_SHA2_BASE)
		OPENSSL_armcap_P |= ARMV8_SHA256;
}
#else
#if __ARM_ARCH__ >= 7
static sigset_t all_masked;

static sigjmp_buf ill_jmp;
	static void ill_handler (int sig) { siglongjmp(ill_jmp, sig);
}

/*
 * Following subroutines could have been inlined, but it's not all
 * ARM compilers support inline assembler...
 */
void _armv7_neon_probe(void);
void _armv8_aes_probe(void);
void _armv8_sha1_probe(void);
void _armv8_sha256_probe(void);
void _armv8_pmull_probe(void);
#endif

void
OPENSSL_cpuid_setup(void)
{
#if __ARM_ARCH__ >= 7
	struct sigaction	ill_oact, ill_act;
	sigset_t		oset;
#endif
	static int trigger = 0;

	if (trigger)
		return;
	trigger = 1;

	OPENSSL_armcap_P = 0;

#if __ARM_ARCH__ >= 7
	sigfillset(&all_masked);
	sigdelset(&all_masked, SIGILL);
	sigdelset(&all_masked, SIGTRAP);
	sigdelset(&all_masked, SIGFPE);
	sigdelset(&all_masked, SIGBUS);
	sigdelset(&all_masked, SIGSEGV);

	memset(&ill_act, 0, sizeof(ill_act));
	ill_act.sa_handler = ill_handler;
	ill_act.sa_mask = all_masked;

	sigprocmask(SIG_SETMASK, &ill_act.sa_mask, &oset);
	sigaction(SIGILL, &ill_act, &ill_oact);

	if (sigsetjmp(ill_jmp, 1) == 0) {
		_armv7_neon_probe();
		OPENSSL_armcap_P |= ARMV7_NEON;
		if (sigsetjmp(ill_jmp, 1) == 0) {
			_armv8_pmull_probe();
			OPENSSL_armcap_P |= ARMV8_PMULL | ARMV8_AES;
		} else if (sigsetjmp(ill_jmp, 1) == 0) {
			_armv8_aes_probe();
			OPENSSL_armcap_P |= ARMV8_AES;
		}
		if (sigsetjmp(ill_jmp, 1) == 0) {
			_armv8_sha1_probe();
			OPENSSL_armcap_P |= ARMV8_SHA1;
		}
		if (sigsetjmp(ill_jmp, 1) == 0) {
			_armv8_sha256_probe();
			OPENSSL_armcap_P |= ARMV8_SHA256;
		}
	}

	sigaction (SIGILL, &ill_oact, NULL);
	sigprocmask(SIG_SETMASK, &oset, NULL);
#endif
}
#endif