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