aboutsummaryrefslogtreecommitdiff
path: root/src/arm/linux/aarch32-isa.c
blob: df68aa147a4783f1aa6e5d1b839c863acf82e40c (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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
#include <stdint.h>

#if CPUINFO_MOCK
	#include <cpuinfo-mock.h>
#endif
#include <arm/linux/api.h>
#include <arm/linux/cp.h>
#include <arm/midr.h>
#include <cpuinfo/log.h>


#if CPUINFO_MOCK
	uint32_t cpuinfo_arm_fpsid = 0;
	uint32_t cpuinfo_arm_mvfr0 = 0;
	uint32_t cpuinfo_arm_wcid = 0;

	void cpuinfo_set_fpsid(uint32_t fpsid) {
		cpuinfo_arm_fpsid = fpsid;
	}

	void cpuinfo_set_wcid(uint32_t wcid) {
		cpuinfo_arm_wcid = wcid;
	}
#endif


void cpuinfo_arm_linux_decode_isa_from_proc_cpuinfo(
	uint32_t features,
	uint32_t features2,
	uint32_t midr,
	uint32_t architecture_version,
	uint32_t architecture_flags,
	const struct cpuinfo_arm_chipset chipset[restrict static 1],
	struct cpuinfo_arm_isa isa[restrict static 1])
{
	if (architecture_version >= 8) {
		/*
		 * ARMv7 code running on ARMv8: IDIV, VFP, NEON are always supported,
		 * but may be not reported in /proc/cpuinfo features.
		 */
		isa->armv5e  = true;
		isa->armv6   = true;
		isa->armv6k  = true;
		isa->armv7   = true;
		isa->armv7mp = true;
		isa->armv8   = true;
		isa->thumb  = true;
		isa->thumb2 = true;
		isa->idiv = true;
		isa->vfpv3 = true;
		isa->d32 = true;
		isa->fp16 = true;
		isa->fma = true;
		isa->neon = true;

		/*
		 * NEON FP16 compute extension and VQRDMLAH/VQRDMLSH instructions are not indicated in /proc/cpuinfo.
		 * Use a MIDR-based heuristic to whitelist processors known to support it:
		 * - Processors with Cortex-A55 cores
		 * - Processors with Cortex-A65 cores
		 * - Processors with Cortex-A75 cores
		 * - Processors with Cortex-A76 cores
		 * - Processors with Cortex-A77 cores
		 * - Processors with Exynos M4 cores
		 * - Processors with Exynos M5 cores
		 * - Neoverse N1 cores
		 */
		if (chipset->series == cpuinfo_arm_chipset_series_samsung_exynos && chipset->model == 9810) {
			/* Only little cores of Exynos 9810 support FP16 & RDM */
			cpuinfo_log_warning("FP16 arithmetics and RDM disabled: only little cores in Exynos 9810 support these extensions");
		} else {
			switch (midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) {
				case UINT32_C(0x4100D050): /* Cortex-A55 */
				case UINT32_C(0x4100D060): /* Cortex-A65 */
				case UINT32_C(0x4100D0B0): /* Cortex-A76 */
				case UINT32_C(0x4100D0C0): /* Neoverse N1 */
				case UINT32_C(0x4100D0D0): /* Cortex-A77 */
				case UINT32_C(0x4100D0E0): /* Cortex-A76AE */
				case UINT32_C(0x4800D400): /* Cortex-A76 (HiSilicon) */
				case UINT32_C(0x51008020): /* Kryo 385 Gold (Cortex-A75) */
				case UINT32_C(0x51008030): /* Kryo 385 Silver (Cortex-A55) */
				case UINT32_C(0x51008040): /* Kryo 485 Gold (Cortex-A76) */
				case UINT32_C(0x51008050): /* Kryo 485 Silver (Cortex-A55) */
				case UINT32_C(0x53000030): /* Exynos M4 */
				case UINT32_C(0x53000040): /* Exynos M5 */
					isa->fp16arith = true;
					isa->rdm = true;
					break;
			}
		}

		/*
		 * NEON VDOT instructions are not indicated in /proc/cpuinfo.
		 * Use a MIDR-based heuristic to whitelist processors known to support it.
		 */
		switch (midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) {
			case UINT32_C(0x4100D0B0): /* Cortex-A76 */
			case UINT32_C(0x4100D0D0): /* Cortex-A77 */
			case UINT32_C(0x4100D0E0): /* Cortex-A76AE */
			case UINT32_C(0x4800D400): /* Cortex-A76 (HiSilicon) */
			case UINT32_C(0x51008040): /* Kryo 485 Gold (Cortex-A76) */
			case UINT32_C(0x51008050): /* Kryo 485 Silver (Cortex-A55) */
			case UINT32_C(0x53000030): /* Exynos-M4 */
			case UINT32_C(0x53000040): /* Exynos-M5 */
				isa->dot = true;
				break;
			case UINT32_C(0x4100D050): /* Cortex A55: revision 1 or later only */
				isa->dot = !!(midr_get_variant(midr) >= 1);
				break;
			case UINT32_C(0x4100D0A0): /* Cortex A75: revision 2 or later only */
				isa->dot = !!(midr_get_variant(midr) >= 2);
				break;
		}
	} else {
		/* ARMv7 or lower: use feature flags to detect optional features */

		/*
		 * ARM11 (ARM 1136/1156/1176/11 MPCore) processors can report v7 architecture
		 * even though they support only ARMv6 instruction set.
		 */
		if (architecture_version == 7 && midr_is_arm11(midr)) {
			cpuinfo_log_warning("kernel-reported architecture ARMv7 ignored due to mismatch with processor microarchitecture (ARM11)");
			architecture_version = 6;
		}

		if (architecture_version < 7) {
			const uint32_t armv7_features_mask = CPUINFO_ARM_LINUX_FEATURE_VFPV3 | CPUINFO_ARM_LINUX_FEATURE_VFPV3D16 | CPUINFO_ARM_LINUX_FEATURE_VFPD32 |
				CPUINFO_ARM_LINUX_FEATURE_VFPV4 | CPUINFO_ARM_LINUX_FEATURE_NEON | CPUINFO_ARM_LINUX_FEATURE_IDIVT | CPUINFO_ARM_LINUX_FEATURE_IDIVA;
			if (features & armv7_features_mask) {
				architecture_version = 7;
			}
		}
		if ((architecture_version >= 6) || (features & CPUINFO_ARM_LINUX_FEATURE_EDSP) || (architecture_flags & CPUINFO_ARM_LINUX_ARCH_E)) {
			isa->armv5e = true;
		}
		if (architecture_version >= 6) {
			isa->armv6 = true;
		}
		if (architecture_version >= 7) {
			isa->armv6k = true;
			isa->armv7 = true;

			/*
			 * ARMv7 MP extension (PLDW instruction) is not indicated in /proc/cpuinfo.
			 * Use heuristic list of supporting processors:
			 * - Processors supporting UDIV/SDIV instructions ("idiva" + "idivt" features in /proc/cpuinfo)
			 * - Cortex-A5
			 * - Cortex-A9
			 * - Dual-Core Scorpion
			 * - Krait (supports UDIV/SDIV, but kernels may not report it in /proc/cpuinfo)
			 *
			 * TODO: check single-core Qualcomm Scorpion.
			 */
			switch (midr & (CPUINFO_ARM_MIDR_IMPLEMENTER_MASK | CPUINFO_ARM_MIDR_PART_MASK)) {
				case UINT32_C(0x4100C050): /* Cortex-A5 */
				case UINT32_C(0x4100C090): /* Cortex-A9 */
				case UINT32_C(0x510002D0): /* Scorpion (dual-core) */
				case UINT32_C(0x510004D0): /* Krait (dual-core) */
				case UINT32_C(0x510006F0): /* Krait (quad-core) */
					isa->armv7mp = true;
					break;
				default:
					/* In practice IDIV instruction implies ARMv7+MP ISA */
					isa->armv7mp = (features & CPUINFO_ARM_LINUX_FEATURE_IDIV) == CPUINFO_ARM_LINUX_FEATURE_IDIV;
					break;
			}
		}

		if (features & CPUINFO_ARM_LINUX_FEATURE_IWMMXT) {
			const uint32_t wcid = read_wcid();
			cpuinfo_log_debug("WCID = 0x%08"PRIx32, wcid);
			const uint32_t coprocessor_type = (wcid >> 8) & UINT32_C(0xFF);
			if (coprocessor_type >= 0x10) {
				isa->wmmx = true;
				if (coprocessor_type >= 0x20) {
					isa->wmmx2 = true;
				}
			} else {
				cpuinfo_log_warning("WMMX ISA disabled: OS reported iwmmxt feature, "
					"but WCID coprocessor type 0x%"PRIx32" indicates no WMMX support",
					coprocessor_type);
			}
		}

		if ((features & CPUINFO_ARM_LINUX_FEATURE_THUMB) || (architecture_flags & CPUINFO_ARM_LINUX_ARCH_T)) {
			isa->thumb = true;

			/*
			 * There is no separate feature flag for Thumb 2.
			 * All ARMv7 processors and ARM 1156 support Thumb 2.
			 */
			if (architecture_version >= 7 || midr_is_arm1156(midr)) {
				isa->thumb2 = true;
			}
		}
		if (features & CPUINFO_ARM_LINUX_FEATURE_THUMBEE) {
			isa->thumbee = true;
		}
		if ((features & CPUINFO_ARM_LINUX_FEATURE_JAVA) || (architecture_flags & CPUINFO_ARM_LINUX_ARCH_J)) {
			isa->jazelle = true;
		}

		/* Qualcomm Krait may have buggy kernel configuration that doesn't report IDIV */
		if ((features & CPUINFO_ARM_LINUX_FEATURE_IDIV) == CPUINFO_ARM_LINUX_FEATURE_IDIV || midr_is_krait(midr)) {
			isa->idiv = true;
		}

		const uint32_t vfp_mask = \
			CPUINFO_ARM_LINUX_FEATURE_VFP | CPUINFO_ARM_LINUX_FEATURE_VFPV3 | CPUINFO_ARM_LINUX_FEATURE_VFPV3D16 | \
			CPUINFO_ARM_LINUX_FEATURE_VFPD32 | CPUINFO_ARM_LINUX_FEATURE_VFPV4 | CPUINFO_ARM_LINUX_FEATURE_NEON;
		if (features & vfp_mask) {
			const uint32_t vfpv3_mask = CPUINFO_ARM_LINUX_FEATURE_VFPV3 | CPUINFO_ARM_LINUX_FEATURE_VFPV3D16 | \
				CPUINFO_ARM_LINUX_FEATURE_VFPD32 | CPUINFO_ARM_LINUX_FEATURE_VFPV4 | CPUINFO_ARM_LINUX_FEATURE_NEON;
			if ((architecture_version >= 7) || (features & vfpv3_mask)) {
				isa->vfpv3 = true;

				const uint32_t d32_mask = CPUINFO_ARM_LINUX_FEATURE_VFPD32 | CPUINFO_ARM_LINUX_FEATURE_NEON;
				if (features & d32_mask) {
					isa->d32 = true;
				}
			} else {
				#if defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_8A__) || defined(__ARM_ARCH) && (__ARM_ARCH >= 7)
					isa->vfpv3 = true;
				#else
					const uint32_t fpsid = read_fpsid();
					cpuinfo_log_debug("FPSID = 0x%08"PRIx32, fpsid);
					const uint32_t subarchitecture = (fpsid >> 16) & UINT32_C(0x7F);
					if (subarchitecture >= 0x01) {
						isa->vfpv2 = true;
					}
				#endif
			}
		}
		if (features & CPUINFO_ARM_LINUX_FEATURE_NEON) {
			isa->neon = true;
		}

		/*
		 * There is no separate feature flag for FP16 support.
		 * VFPv4 implies VFPv3-FP16 support (and in practice, NEON-HP as well).
		 * Additionally, ARM Cortex-A9 and Qualcomm Scorpion support FP16.
		 */
		if ((features & CPUINFO_ARM_LINUX_FEATURE_VFPV4) || midr_is_cortex_a9(midr) || midr_is_scorpion(midr)) {
			isa->fp16 = true;
		}

		if (features & CPUINFO_ARM_LINUX_FEATURE_VFPV4) {
			isa->fma = true;
		}
	}

	if (features2 & CPUINFO_ARM_LINUX_FEATURE2_AES) {
		isa->aes = true;
	}
	if (features2 & CPUINFO_ARM_LINUX_FEATURE2_PMULL) {
		isa->pmull = true;
	}
	if (features2 & CPUINFO_ARM_LINUX_FEATURE2_SHA1) {
		isa->sha1 = true;
	}
	if (features2 & CPUINFO_ARM_LINUX_FEATURE2_SHA2) {
		isa->sha2 = true;
	}
	if (features2 & CPUINFO_ARM_LINUX_FEATURE2_CRC32) {
		isa->crc32 = true;
	}
}