1 /*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "instruction_set_features_arm.h"
18
19 #if defined(ART_TARGET_ANDROID) && defined(__arm__)
20 #include <asm/hwcap.h>
21 #include <sys/auxv.h>
22 #endif
23
24 #include "signal.h"
25
26 #include <fstream>
27
28 #include <android-base/logging.h>
29 #include <android-base/stringprintf.h>
30 #include <android-base/strings.h>
31
32 #if defined(__arm__)
33 extern "C" bool artCheckForArmSdivInstruction();
34 extern "C" bool artCheckForArmv8AInstructions();
35 #endif
36
37 namespace art {
38
39 using android::base::StringPrintf;
40
FromVariant(const std::string & variant,std::string * error_msg)41 ArmFeaturesUniquePtr ArmInstructionSetFeatures::FromVariant(
42 const std::string& variant, std::string* error_msg) {
43 static const char* arm_variants_with_armv8a[] = {
44 "cortex-a32",
45 "cortex-a35",
46 "cortex-a53",
47 "cortex-a53.a57",
48 "cortex-a53.a72",
49 "cortex-a55",
50 "cortex-a57",
51 "cortex-a72",
52 "cortex-a73",
53 "cortex-a75",
54 "cortex-a76",
55 "exynos-m1",
56 "kryo",
57 "kryo385",
58 };
59 bool has_armv8a = FindVariantInArray(arm_variants_with_armv8a,
60 arraysize(arm_variants_with_armv8a),
61 variant);
62
63 // Look for variants that have divide support.
64 static const char* arm_variants_with_div[] = {
65 "cortex-a7",
66 "cortex-a12",
67 "cortex-a15",
68 "cortex-a17",
69 "krait",
70 };
71 bool has_div = has_armv8a || FindVariantInArray(arm_variants_with_div,
72 arraysize(arm_variants_with_div),
73 variant);
74
75 // Look for variants that have LPAE support.
76 static const char* arm_variants_with_lpae[] = {
77 "cortex-a7",
78 "cortex-a12",
79 "cortex-a15",
80 "cortex-a17",
81 "krait",
82 };
83 bool has_atomic_ldrd_strd = has_armv8a || FindVariantInArray(arm_variants_with_lpae,
84 arraysize(arm_variants_with_lpae),
85 variant);
86
87 if (has_armv8a == false && has_div == false && has_atomic_ldrd_strd == false) {
88 static const char* arm_variants_with_default_features[] = {
89 "cortex-a5",
90 "cortex-a8",
91 "cortex-a9",
92 "cortex-a9-mp",
93 "default",
94 "generic"
95 };
96 if (!FindVariantInArray(arm_variants_with_default_features,
97 arraysize(arm_variants_with_default_features),
98 variant)) {
99 *error_msg = StringPrintf("Attempt to use unsupported ARM variant: %s", variant.c_str());
100 return nullptr;
101 } else {
102 // Warn if we use the default features.
103 LOG(WARNING) << "Using default instruction set features for ARM CPU variant (" << variant
104 << ") using conservative defaults";
105 }
106 }
107 return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(has_div,
108 has_atomic_ldrd_strd,
109 has_armv8a));
110 }
111
FromBitmap(uint32_t bitmap)112 ArmFeaturesUniquePtr ArmInstructionSetFeatures::FromBitmap(uint32_t bitmap) {
113 bool has_div = (bitmap & kDivBitfield) != 0;
114 bool has_atomic_ldrd_strd = (bitmap & kAtomicLdrdStrdBitfield) != 0;
115 bool has_armv8a = (bitmap & kARMv8A) != 0;
116 return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(has_div,
117 has_atomic_ldrd_strd,
118 has_armv8a));
119 }
120
FromCppDefines()121 ArmFeaturesUniquePtr ArmInstructionSetFeatures::FromCppDefines() {
122 // Note: This will not work for now since we still build the 32-bit as __ARCH_ARM_7A__.
123 #if defined(__ARM_ARCH_8A__)
124 const bool has_armv8a = true;
125 #else
126 const bool has_armv8a = false;
127 #endif
128 #if defined (__ARM_ARCH_8A__) || defined(__ARM_ARCH_EXT_IDIV__)
129 const bool has_div = true;
130 #else
131 const bool has_div = false;
132 #endif
133 #if defined (__ARM_ARCH_8A__) || defined(__ARM_FEATURE_LPAE)
134 const bool has_atomic_ldrd_strd = true;
135 #else
136 const bool has_atomic_ldrd_strd = false;
137 #endif
138 return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(has_div,
139 has_atomic_ldrd_strd,
140 has_armv8a));
141 }
142
FromCpuInfo()143 ArmFeaturesUniquePtr ArmInstructionSetFeatures::FromCpuInfo() {
144 // Look in /proc/cpuinfo for features we need. Only use this when we can guarantee that
145 // the kernel puts the appropriate feature flags in here. Sometimes it doesn't.
146 bool has_atomic_ldrd_strd = false;
147 bool has_div = false;
148 bool has_armv8a = false;
149
150 std::ifstream in("/proc/cpuinfo");
151 if (!in.fail()) {
152 while (!in.eof()) {
153 std::string line;
154 std::getline(in, line);
155 if (!in.eof()) {
156 LOG(INFO) << "cpuinfo line: " << line;
157 if (line.find("Features") != std::string::npos) {
158 LOG(INFO) << "found features";
159 if (line.find("idivt") != std::string::npos) {
160 // We always expect both ARM and Thumb divide instructions to be available or not
161 // available.
162 CHECK_NE(line.find("idiva"), std::string::npos);
163 has_div = true;
164 }
165 if (line.find("lpae") != std::string::npos) {
166 has_atomic_ldrd_strd = true;
167 }
168 }
169 if (line.find("architecture") != std::string::npos
170 && line.find(": 8") != std::string::npos) {
171 LOG(INFO) << "found architecture ARMv8";
172 // Android is only run on A cores, so ARMv8 implies ARMv8-A.
173 has_armv8a = true;
174 // ARMv8 CPUs have LPAE and div support.
175 has_div = true;
176 has_atomic_ldrd_strd = true;
177 }
178 }
179 }
180 in.close();
181 } else {
182 LOG(ERROR) << "Failed to open /proc/cpuinfo";
183 }
184 return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(has_div,
185 has_atomic_ldrd_strd,
186 has_armv8a));
187 }
188
FromHwcap()189 ArmFeaturesUniquePtr ArmInstructionSetFeatures::FromHwcap() {
190 bool has_div = false;
191 bool has_atomic_ldrd_strd = false;
192 bool has_armv8a = false;
193
194 #if defined(ART_TARGET_ANDROID) && defined(__arm__)
195 uint64_t hwcaps = getauxval(AT_HWCAP);
196 LOG(INFO) << "hwcaps=" << hwcaps;
197 if ((hwcaps & HWCAP_IDIVT) != 0) {
198 // We always expect both ARM and Thumb divide instructions to be available or not
199 // available.
200 CHECK_NE(hwcaps & HWCAP_IDIVA, 0U);
201 has_div = true;
202 }
203 if ((hwcaps & HWCAP_LPAE) != 0) {
204 has_atomic_ldrd_strd = true;
205 }
206 // TODO: Fix this once FPMISC makes it upstream.
207 // For now we detect if we run on an ARMv8 CPU by looking for CRC32 and SHA1
208 // (only available on ARMv8 CPUs).
209 if ((hwcaps & HWCAP2_CRC32) != 0 && (hwcaps & HWCAP2_SHA1) != 0) {
210 has_armv8a = true;
211 }
212 #endif
213
214 return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(has_div,
215 has_atomic_ldrd_strd,
216 has_armv8a));
217 }
218
219 // A signal handler called by a fault for an illegal instruction. We record the fact in r0
220 // and then increment the PC in the signal context to return to the next instruction. We know the
221 // instruction is 4 bytes long.
bad_instr_handle(int signo ATTRIBUTE_UNUSED,siginfo_t * si ATTRIBUTE_UNUSED,void * data)222 static void bad_instr_handle(int signo ATTRIBUTE_UNUSED,
223 siginfo_t* si ATTRIBUTE_UNUSED,
224 void* data) {
225 #if defined(__arm__)
226 struct ucontext *uc = (struct ucontext *)data;
227 struct sigcontext *sc = &uc->uc_mcontext;
228 sc->arm_r0 = 0; // Set R0 to #0 to signal error.
229 sc->arm_pc += 4; // Skip offending instruction.
230 #else
231 UNUSED(data);
232 #endif
233 }
234
FromAssembly()235 ArmFeaturesUniquePtr ArmInstructionSetFeatures::FromAssembly() {
236 // See if have a sdiv instruction. Register a signal handler and try to execute an sdiv
237 // instruction. If we get a SIGILL then it's not supported.
238 struct sigaction sa, osa;
239 sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO;
240 sa.sa_sigaction = bad_instr_handle;
241 sigemptyset(&sa.sa_mask);
242 sigaction(SIGILL, &sa, &osa);
243
244 bool has_div = false;
245 bool has_armv8a = false;
246 #if defined(__arm__)
247 if (artCheckForArmSdivInstruction()) {
248 has_div = true;
249 }
250 if (artCheckForArmv8AInstructions()) {
251 has_armv8a = true;
252 }
253 #endif
254
255 // Restore the signal handler.
256 sigaction(SIGILL, &osa, nullptr);
257
258 // Use compile time features to "detect" LPAE support.
259 // TODO: write an assembly LPAE support test.
260 #if defined(__ARM_FEATURE_LPAE)
261 const bool has_atomic_ldrd_strd = true;
262 #else
263 const bool has_atomic_ldrd_strd = false;
264 #endif
265 return ArmFeaturesUniquePtr(new ArmInstructionSetFeatures(has_div,
266 has_atomic_ldrd_strd,
267 has_armv8a));
268 }
269
Equals(const InstructionSetFeatures * other) const270 bool ArmInstructionSetFeatures::Equals(const InstructionSetFeatures* other) const {
271 if (InstructionSet::kArm != other->GetInstructionSet()) {
272 return false;
273 }
274 const ArmInstructionSetFeatures* other_as_arm = other->AsArmInstructionSetFeatures();
275 return has_div_ == other_as_arm->has_div_
276 && has_atomic_ldrd_strd_ == other_as_arm->has_atomic_ldrd_strd_
277 && has_armv8a_ == other_as_arm->has_armv8a_;
278 }
279
HasAtLeast(const InstructionSetFeatures * other) const280 bool ArmInstructionSetFeatures::HasAtLeast(const InstructionSetFeatures* other) const {
281 if (InstructionSet::kArm != other->GetInstructionSet()) {
282 return false;
283 }
284 const ArmInstructionSetFeatures* other_as_arm = other->AsArmInstructionSetFeatures();
285 return (has_div_ || !other_as_arm->has_div_)
286 && (has_atomic_ldrd_strd_ || !other_as_arm->has_atomic_ldrd_strd_)
287 && (has_armv8a_ || !other_as_arm->has_armv8a_);
288 }
289
AsBitmap() const290 uint32_t ArmInstructionSetFeatures::AsBitmap() const {
291 return (has_div_ ? kDivBitfield : 0)
292 | (has_atomic_ldrd_strd_ ? kAtomicLdrdStrdBitfield : 0)
293 | (has_armv8a_ ? kARMv8A : 0);
294 }
295
GetFeatureString() const296 std::string ArmInstructionSetFeatures::GetFeatureString() const {
297 std::string result;
298 if (has_div_) {
299 result += "div";
300 } else {
301 result += "-div";
302 }
303 if (has_atomic_ldrd_strd_) {
304 result += ",atomic_ldrd_strd";
305 } else {
306 result += ",-atomic_ldrd_strd";
307 }
308 if (has_armv8a_) {
309 result += ",armv8a";
310 } else {
311 result += ",-armv8a";
312 }
313 return result;
314 }
315
316 std::unique_ptr<const InstructionSetFeatures>
AddFeaturesFromSplitString(const std::vector<std::string> & features,std::string * error_msg) const317 ArmInstructionSetFeatures::AddFeaturesFromSplitString(
318 const std::vector<std::string>& features, std::string* error_msg) const {
319 bool has_atomic_ldrd_strd = has_atomic_ldrd_strd_;
320 bool has_div = has_div_;
321 bool has_armv8a = has_armv8a_;
322 for (const std::string& feature : features) {
323 DCHECK_EQ(android::base::Trim(feature), feature)
324 << "Feature name is not trimmed: '" << feature << "'";
325 if (feature == "div") {
326 has_div = true;
327 } else if (feature == "-div") {
328 has_div = false;
329 } else if (feature == "atomic_ldrd_strd") {
330 has_atomic_ldrd_strd = true;
331 } else if (feature == "-atomic_ldrd_strd") {
332 has_atomic_ldrd_strd = false;
333 } else if (feature == "armv8a") {
334 has_armv8a = true;
335 } else if (feature == "-armv8a") {
336 has_armv8a = false;
337 } else {
338 *error_msg = StringPrintf("Unknown instruction set feature: '%s'", feature.c_str());
339 return nullptr;
340 }
341 }
342 return std::unique_ptr<const InstructionSetFeatures>(
343 new ArmInstructionSetFeatures(has_div, has_atomic_ldrd_strd, has_armv8a));
344 }
345
346 } // namespace art
347