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