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