blob: 66c6d91c158ed7a1ea3acf07cb626b07b304267f [file] [log] [blame]
Guillaume Chatelet439d3712018-02-01 10:03:09 +01001// Copyright 2017 Google Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "cpuinfo_arm.h"
16
17#include "internal/bit_utils.h"
18#include "internal/filesystem.h"
19#include "internal/hwcaps.h"
Guillaume Chatelet439d3712018-02-01 10:03:09 +010020#include "internal/stack_line_reader.h"
21#include "internal/string_view.h"
Guillaume Chateletdfdac6a2019-01-17 18:00:21 +010022#include "internal/unix_features_aggregator.h"
Guillaume Chatelet439d3712018-02-01 10:03:09 +010023
Dr.-Ing. Patrick Siegl22c05ed2019-06-29 22:02:23 +020024#include <assert.h>
Guillaume Chatelet439d3712018-02-01 10:03:09 +010025#include <ctype.h>
26
Dr.-Ing. Patrick Sieglbfd109b2019-06-26 12:56:52 +020027DECLARE_SETTER(ArmFeatures, swp)
Dr.-Ing. Patrick Siegl6482bad2019-06-18 12:53:08 +020028DECLARE_SETTER(ArmFeatures, half)
29DECLARE_SETTER(ArmFeatures, thumb)
Dr.-Ing. Patrick Sieglbfd109b2019-06-26 12:56:52 +020030DECLARE_SETTER(ArmFeatures, _26bit)
Dr.-Ing. Patrick Siegl6482bad2019-06-18 12:53:08 +020031DECLARE_SETTER(ArmFeatures, fastmult)
Dr.-Ing. Patrick Sieglbfd109b2019-06-26 12:56:52 +020032DECLARE_SETTER(ArmFeatures, fpa)
Guillaume Chatelet439d3712018-02-01 10:03:09 +010033DECLARE_SETTER(ArmFeatures, vfp)
Dr.-Ing. Patrick Siegl6482bad2019-06-18 12:53:08 +020034DECLARE_SETTER(ArmFeatures, edsp)
35DECLARE_SETTER(ArmFeatures, java)
Guillaume Chatelet439d3712018-02-01 10:03:09 +010036DECLARE_SETTER(ArmFeatures, iwmmxt)
Dr.-Ing. Patrick Sieglbfd109b2019-06-26 12:56:52 +020037DECLARE_SETTER(ArmFeatures, crunch)
38DECLARE_SETTER(ArmFeatures, thumbee)
Guillaume Chatelet439d3712018-02-01 10:03:09 +010039DECLARE_SETTER(ArmFeatures, neon)
40DECLARE_SETTER(ArmFeatures, vfpv3)
41DECLARE_SETTER(ArmFeatures, vfpv3d16)
Dr.-Ing. Patrick Siegl6482bad2019-06-18 12:53:08 +020042DECLARE_SETTER(ArmFeatures, tls)
Guillaume Chatelet439d3712018-02-01 10:03:09 +010043DECLARE_SETTER(ArmFeatures, vfpv4)
44DECLARE_SETTER(ArmFeatures, idiva)
45DECLARE_SETTER(ArmFeatures, idivt)
Dr.-Ing. Patrick Sieglbfd109b2019-06-26 12:56:52 +020046DECLARE_SETTER(ArmFeatures, vfpd32)
47DECLARE_SETTER(ArmFeatures, lpae)
48DECLARE_SETTER(ArmFeatures, evtstrm)
Guillaume Chatelet439d3712018-02-01 10:03:09 +010049DECLARE_SETTER(ArmFeatures, aes)
50DECLARE_SETTER(ArmFeatures, pmull)
51DECLARE_SETTER(ArmFeatures, sha1)
52DECLARE_SETTER(ArmFeatures, sha2)
53DECLARE_SETTER(ArmFeatures, crc32)
54
55static const CapabilityConfig kConfigs[] = {
Dr.-Ing. Patrick Siegl22c05ed2019-06-29 22:02:23 +020056 [ARM_SWP] = {{ARM_HWCAP_SWP, 0}, "swp", &set_swp}, //
57 [ARM_HALF] = {{ARM_HWCAP_HALF, 0}, "half", &set_half}, //
58 [ARM_THUMB] = {{ARM_HWCAP_THUMB, 0}, "thumb", &set_thumb}, //
59 [ARM_26BIT] = {{ARM_HWCAP_26BIT, 0}, "26bit", &set__26bit}, //
60 [ARM_FASTMULT] = {{ARM_HWCAP_FAST_MULT, 0}, "fastmult", &set_fastmult}, //
61 [ARM_FPA] = {{ARM_HWCAP_FPA, 0}, "fpa", &set_fpa}, //
62 [ARM_VFP] = {{ARM_HWCAP_VFP, 0}, "vfp", &set_vfp}, //
63 [ARM_EDSP] = {{ARM_HWCAP_EDSP, 0}, "edsp", &set_edsp}, //
64 [ARM_JAVA] = {{ARM_HWCAP_JAVA, 0}, "java", &set_java}, //
65 [ARM_IWMMXT] = {{ARM_HWCAP_IWMMXT, 0}, "iwmmxt", &set_iwmmxt}, //
66 [ARM_CRUNCH] = {{ARM_HWCAP_CRUNCH, 0}, "crunch", &set_crunch}, //
67 [ARM_THUMBEE] = {{ARM_HWCAP_THUMBEE, 0}, "thumbee", &set_thumbee}, //
68 [ARM_NEON] = {{ARM_HWCAP_NEON, 0}, "neon", &set_neon}, //
69 [ARM_VFPV3] = {{ARM_HWCAP_VFPV3, 0}, "vfpv3", &set_vfpv3}, //
70 [ARM_VFPV3D16] = {{ARM_HWCAP_VFPV3D16, 0}, "vfpv3d16", &set_vfpv3d16}, //
71 [ARM_TLS] = {{ARM_HWCAP_TLS, 0}, "tls", &set_tls}, //
72 [ARM_VFPV4] = {{ARM_HWCAP_VFPV4, 0}, "vfpv4", &set_vfpv4}, //
73 [ARM_IDIVA] = {{ARM_HWCAP_IDIVA, 0}, "idiva", &set_idiva}, //
74 [ARM_IDIVT] = {{ARM_HWCAP_IDIVT, 0}, "idivt", &set_idivt}, //
75 [ARM_VFPD32] = {{ARM_HWCAP_VFPD32, 0}, "vfpd32", &set_vfpd32}, //
76 [ARM_LPAE] = {{ARM_HWCAP_LPAE, 0}, "lpae", &set_lpae}, //
77 [ARM_EVTSTRM] = {{ARM_HWCAP_EVTSTRM, 0}, "evtstrm", &set_evtstrm}, //
78 [ARM_AES] = {{0, ARM_HWCAP2_AES}, "aes", &set_aes}, //
79 [ARM_PMULL] = {{0, ARM_HWCAP2_PMULL}, "pmull", &set_pmull}, //
80 [ARM_SHA1] = {{0, ARM_HWCAP2_SHA1}, "sha1", &set_sha1}, //
81 [ARM_SHA2] = {{0, ARM_HWCAP2_SHA2}, "sha2", &set_sha2}, //
82 [ARM_CRC32] = {{0, ARM_HWCAP2_CRC32}, "crc32", &set_crc32}, //
Guillaume Chatelet439d3712018-02-01 10:03:09 +010083};
84
85static const size_t kConfigsSize = sizeof(kConfigs) / sizeof(CapabilityConfig);
86
87typedef struct {
88 bool processor_reports_armv6;
89 bool hardware_reports_goldfish;
90} ProcCpuInfoData;
91
92static int IndexOfNonDigit(StringView str) {
93 size_t index = 0;
Arvid Gerstmanna1ffdcb2018-04-26 10:31:03 +020094 while (str.size && isdigit(CpuFeatures_StringView_Front(str))) {
95 str = CpuFeatures_StringView_PopFront(str, 1);
Guillaume Chatelet439d3712018-02-01 10:03:09 +010096 ++index;
97 }
98 return index;
99}
100
101static bool HandleArmLine(const LineResult result, ArmInfo* const info,
102 ProcCpuInfoData* const proc_info) {
103 StringView line = result.line;
104 StringView key, value;
Arvid Gerstmanna1ffdcb2018-04-26 10:31:03 +0200105 if (CpuFeatures_StringView_GetAttributeKeyValue(line, &key, &value)) {
106 if (CpuFeatures_StringView_IsEquals(key, str("Features"))) {
107 CpuFeatures_SetFromFlags(kConfigsSize, kConfigs, value, &info->features);
108 } else if (CpuFeatures_StringView_IsEquals(key, str("CPU implementer"))) {
109 info->implementer = CpuFeatures_StringView_ParsePositiveNumber(value);
110 } else if (CpuFeatures_StringView_IsEquals(key, str("CPU variant"))) {
111 info->variant = CpuFeatures_StringView_ParsePositiveNumber(value);
112 } else if (CpuFeatures_StringView_IsEquals(key, str("CPU part"))) {
113 info->part = CpuFeatures_StringView_ParsePositiveNumber(value);
114 } else if (CpuFeatures_StringView_IsEquals(key, str("CPU revision"))) {
115 info->revision = CpuFeatures_StringView_ParsePositiveNumber(value);
116 } else if (CpuFeatures_StringView_IsEquals(key, str("CPU architecture"))) {
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100117 // CPU architecture is a number that may be followed by letters. e.g.
118 // "6TEJ", "7".
Arvid Gerstmannd9689912018-05-04 09:32:17 +0200119 const StringView digits =
120 CpuFeatures_StringView_KeepFront(value, IndexOfNonDigit(value));
Arvid Gerstmanna1ffdcb2018-04-26 10:31:03 +0200121 info->architecture = CpuFeatures_StringView_ParsePositiveNumber(digits);
Dr.-Ing. Patrick Siegl18342782019-06-25 17:58:31 +0200122 } else if (CpuFeatures_StringView_IsEquals(key, str("Processor"))
123 || CpuFeatures_StringView_IsEquals(key, str("model name")) ) {
124 // Android reports this in a non-Linux standard "Processor" but sometimes
125 // also in "model name", Linux reports it only in "model name"
126 // see RaspberryPiZero (Linux) vs InvalidArmv7 (Android) test-cases
Arvid Gerstmannd9689912018-05-04 09:32:17 +0200127 proc_info->processor_reports_armv6 =
128 CpuFeatures_StringView_IndexOf(value, str("(v6l)")) >= 0;
Arvid Gerstmanna1ffdcb2018-04-26 10:31:03 +0200129 } else if (CpuFeatures_StringView_IsEquals(key, str("Hardware"))) {
Arvid Gerstmannd9689912018-05-04 09:32:17 +0200130 proc_info->hardware_reports_goldfish =
131 CpuFeatures_StringView_IsEquals(value, str("Goldfish"));
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100132 }
133 }
134 return !result.eof;
135}
136
Guillaume Chatelet918553a2019-01-17 15:28:04 +0100137uint32_t GetArmCpuId(const ArmInfo* const info) {
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100138 return (ExtractBitRange(info->implementer, 7, 0) << 24) |
139 (ExtractBitRange(info->variant, 3, 0) << 20) |
140 (ExtractBitRange(info->part, 11, 0) << 4) |
141 (ExtractBitRange(info->revision, 3, 0) << 0);
142}
143
144static void FixErrors(ArmInfo* const info,
145 ProcCpuInfoData* const proc_cpu_info_data) {
146 // Fixing Samsung kernel reporting invalid cpu architecture.
147 // http://code.google.com/p/android/issues/detail?id=10812
148 if (proc_cpu_info_data->processor_reports_armv6 && info->architecture >= 7) {
149 info->architecture = 6;
150 }
151
152 // Handle kernel configuration bugs that prevent the correct reporting of CPU
153 // features.
Guillaume Chatelet918553a2019-01-17 15:28:04 +0100154 switch (GetArmCpuId(info)) {
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100155 case 0x4100C080:
156 // Special case: The emulator-specific Android 4.2 kernel fails to report
157 // support for the 32-bit ARM IDIV instruction. Technically, this is a
158 // feature of the virtual CPU implemented by the emulator. Note that it
159 // could also support Thumb IDIV in the future, and this will have to be
160 // slightly updated.
161 if (info->architecture >= 7 &&
162 proc_cpu_info_data->hardware_reports_goldfish) {
163 info->features.idiva = true;
164 }
165 break;
166 case 0x511004D0:
167 // https://crbug.com/341598.
168 info->features.neon = false;
169 break;
170 case 0x510006F2:
171 case 0x510006F3:
172 // The Nexus 4 (Qualcomm Krait) kernel configuration forgets to report
173 // IDIV support.
174 info->features.idiva = true;
175 info->features.idivt = true;
176 break;
177 }
178
179 // Propagate cpu features.
180 if (info->features.vfpv4) info->features.vfpv3 = true;
181 if (info->features.neon) info->features.vfpv3 = true;
182 if (info->features.vfpv3) info->features.vfp = true;
183}
184
185static void FillProcCpuInfoData(ArmInfo* const info,
186 ProcCpuInfoData* proc_cpu_info_data) {
Arvid Gerstmanna1ffdcb2018-04-26 10:31:03 +0200187 const int fd = CpuFeatures_OpenFile("/proc/cpuinfo");
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100188 if (fd >= 0) {
189 StackLineReader reader;
190 StackLineReader_Initialize(&reader, fd);
191 for (;;) {
192 if (!HandleArmLine(StackLineReader_NextLine(&reader), info,
193 proc_cpu_info_data)) {
194 break;
195 }
196 }
Arvid Gerstmanna1ffdcb2018-04-26 10:31:03 +0200197 CpuFeatures_CloseFile(fd);
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100198 }
199}
200
201static const ArmInfo kEmptyArmInfo;
202
203static const ProcCpuInfoData kEmptyProcCpuInfoData;
204
205ArmInfo GetArmInfo(void) {
206 // capabilities are fetched from both getauxval and /proc/cpuinfo so we can
207 // have some information if the executable is sandboxed (aka no access to
208 // /proc/cpuinfo).
209 ArmInfo info = kEmptyArmInfo;
210 ProcCpuInfoData proc_cpu_info_data = kEmptyProcCpuInfoData;
211
212 FillProcCpuInfoData(&info, &proc_cpu_info_data);
Arvid Gerstmannd9689912018-05-04 09:32:17 +0200213 CpuFeatures_OverrideFromHwCaps(kConfigsSize, kConfigs,
214 CpuFeatures_GetHardwareCapabilities(),
215 &info.features);
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100216
217 FixErrors(&info, &proc_cpu_info_data);
218
219 return info;
220}
221
222////////////////////////////////////////////////////////////////////////////////
223// Introspection functions
224
225int GetArmFeaturesEnumValue(const ArmFeatures* features,
226 ArmFeaturesEnum value) {
227 switch (value) {
Dr.-Ing. Patrick Sieglbfd109b2019-06-26 12:56:52 +0200228 case ARM_SWP:
229 return features->swp;
Dr.-Ing. Patrick Siegl6482bad2019-06-18 12:53:08 +0200230 case ARM_HALF:
231 return features->half;
232 case ARM_THUMB:
233 return features->thumb;
Dr.-Ing. Patrick Sieglbfd109b2019-06-26 12:56:52 +0200234 case ARM_26BIT:
235 return features->_26bit;
Dr.-Ing. Patrick Siegl6482bad2019-06-18 12:53:08 +0200236 case ARM_FASTMULT:
237 return features->fastmult;
Dr.-Ing. Patrick Sieglbfd109b2019-06-26 12:56:52 +0200238 case ARM_FPA:
239 return features->fpa;
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100240 case ARM_VFP:
241 return features->vfp;
Dr.-Ing. Patrick Siegl6482bad2019-06-18 12:53:08 +0200242 case ARM_EDSP:
243 return features->edsp;
244 case ARM_JAVA:
245 return features->java;
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100246 case ARM_IWMMXT:
247 return features->iwmmxt;
Dr.-Ing. Patrick Sieglbfd109b2019-06-26 12:56:52 +0200248 case ARM_CRUNCH:
249 return features->crunch;
250 case ARM_THUMBEE:
251 return features->thumbee;
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100252 case ARM_NEON:
253 return features->neon;
254 case ARM_VFPV3:
255 return features->vfpv3;
256 case ARM_VFPV3D16:
257 return features->vfpv3d16;
Dr.-Ing. Patrick Siegl6482bad2019-06-18 12:53:08 +0200258 case ARM_TLS:
259 return features->tls;
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100260 case ARM_VFPV4:
261 return features->vfpv4;
262 case ARM_IDIVA:
263 return features->idiva;
264 case ARM_IDIVT:
265 return features->idivt;
Dr.-Ing. Patrick Sieglbfd109b2019-06-26 12:56:52 +0200266 case ARM_VFPD32:
267 return features->vfpd32;
268 case ARM_LPAE:
269 return features->lpae;
270 case ARM_EVTSTRM:
271 return features->evtstrm;
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100272 case ARM_AES:
273 return features->aes;
274 case ARM_PMULL:
275 return features->pmull;
276 case ARM_SHA1:
277 return features->sha1;
278 case ARM_SHA2:
279 return features->sha2;
280 case ARM_CRC32:
281 return features->crc32;
282 case ARM_LAST_:
283 break;
284 }
285 return false;
286}
287
288const char* GetArmFeaturesEnumName(ArmFeaturesEnum value) {
Dr.-Ing. Patrick Siegl22c05ed2019-06-29 22:02:23 +0200289 if(value >= kConfigsSize)
290 return "unknown feature";
291 return kConfigs[value].proc_cpuinfo_flag;
Guillaume Chatelet439d3712018-02-01 10:03:09 +0100292}