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