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 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 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 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 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 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. 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 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 267 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 277 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 288 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 294 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> 315 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