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