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(__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 "base/stringprintf.h" 28 #include "utils.h" // For Trim. 29 30 #if defined(__arm__) 31 extern "C" bool artCheckForArmSdivInstruction(); 32 #endif 33 34 namespace art { 35 36 const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromVariant( 37 const std::string& variant, std::string* error_msg) { 38 // Assume all ARM processors are SMP. 39 // TODO: set the SMP support based on variant. 40 const bool smp = true; 41 42 // Look for variants that have divide support. 43 static const char* arm_variants_with_div[] = { 44 "cortex-a7", "cortex-a12", "cortex-a15", "cortex-a17", "cortex-a53", "cortex-a57", 45 "cortex-a53.a57", "cortex-m3", "cortex-m4", "cortex-r4", "cortex-r5", 46 "cyclone", "denver", "krait", "swift" }; 47 48 bool has_div = FindVariantInArray(arm_variants_with_div, arraysize(arm_variants_with_div), 49 variant); 50 51 // Look for variants that have LPAE support. 52 static const char* arm_variants_with_lpae[] = { 53 "cortex-a7", "cortex-a15", "krait", "denver", "cortex-a53", "cortex-a57", "cortex-a53.a57" 54 }; 55 bool has_lpae = FindVariantInArray(arm_variants_with_lpae, arraysize(arm_variants_with_lpae), 56 variant); 57 58 if (has_div == false && has_lpae == false) { 59 // Avoid unsupported variants. 60 static const char* unsupported_arm_variants[] = { 61 // ARM processors that aren't ARMv7 compatible aren't supported. 62 "arm2", "arm250", "arm3", "arm6", "arm60", "arm600", "arm610", "arm620", 63 "cortex-m0", "cortex-m0plus", "cortex-m1", 64 "fa526", "fa626", "fa606te", "fa626te", "fmp626", "fa726te", 65 "iwmmxt", "iwmmxt2", 66 "strongarm", "strongarm110", "strongarm1100", "strongarm1110", 67 "xscale" 68 }; 69 if (FindVariantInArray(unsupported_arm_variants, arraysize(unsupported_arm_variants), 70 variant)) { 71 *error_msg = StringPrintf("Attempt to use unsupported ARM variant: %s", variant.c_str()); 72 return nullptr; 73 } 74 // Warn if the variant is unknown. 75 // TODO: some of the variants below may have feature support, but that support is currently 76 // unknown so we'll choose conservative (sub-optimal) defaults without warning. 77 // TODO: some of the architectures may not support all features required by ART and should be 78 // moved to unsupported_arm_variants[] above. 79 static const char* arm_variants_without_known_features[] = { 80 "default", 81 "arm7", "arm7m", "arm7d", "arm7dm", "arm7di", "arm7dmi", "arm70", "arm700", "arm700i", 82 "arm710", "arm710c", "arm7100", "arm720", "arm7500", "arm7500fe", "arm7tdmi", "arm7tdmi-s", 83 "arm710t", "arm720t", "arm740t", 84 "arm8", "arm810", 85 "arm9", "arm9e", "arm920", "arm920t", "arm922t", "arm946e-s", "arm966e-s", "arm968e-s", 86 "arm926ej-s", "arm940t", "arm9tdmi", 87 "arm10tdmi", "arm1020t", "arm1026ej-s", "arm10e", "arm1020e", "arm1022e", 88 "arm1136j-s", "arm1136jf-s", 89 "arm1156t2-s", "arm1156t2f-s", "arm1176jz-s", "arm1176jzf-s", 90 "cortex-a5", "cortex-a8", "cortex-a9", "cortex-a9-mp", "cortex-r4f", 91 "marvell-pj4", "mpcore", "mpcorenovfp" 92 }; 93 if (!FindVariantInArray(arm_variants_without_known_features, 94 arraysize(arm_variants_without_known_features), 95 variant)) { 96 LOG(WARNING) << "Unknown instruction set features for ARM CPU variant (" << variant 97 << ") using conservative defaults"; 98 } 99 } 100 return new ArmInstructionSetFeatures(smp, has_div, has_lpae); 101 } 102 103 const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromBitmap(uint32_t bitmap) { 104 bool smp = (bitmap & kSmpBitfield) != 0; 105 bool has_div = (bitmap & kDivBitfield) != 0; 106 bool has_atomic_ldrd_strd = (bitmap & kAtomicLdrdStrdBitfield) != 0; 107 return new ArmInstructionSetFeatures(smp, has_div, has_atomic_ldrd_strd); 108 } 109 110 const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromCppDefines() { 111 const bool smp = true; 112 #if defined(__ARM_ARCH_EXT_IDIV__) 113 const bool has_div = true; 114 #else 115 const bool has_div = false; 116 #endif 117 #if defined(__ARM_FEATURE_LPAE) 118 const bool has_lpae = true; 119 #else 120 const bool has_lpae = false; 121 #endif 122 return new ArmInstructionSetFeatures(smp, has_div, has_lpae); 123 } 124 125 const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromCpuInfo() { 126 // Look in /proc/cpuinfo for features we need. Only use this when we can guarantee that 127 // the kernel puts the appropriate feature flags in here. Sometimes it doesn't. 128 bool smp = false; 129 bool has_lpae = false; 130 bool has_div = false; 131 132 std::ifstream in("/proc/cpuinfo"); 133 if (!in.fail()) { 134 while (!in.eof()) { 135 std::string line; 136 std::getline(in, line); 137 if (!in.eof()) { 138 LOG(INFO) << "cpuinfo line: " << line; 139 if (line.find("Features") != std::string::npos) { 140 LOG(INFO) << "found features"; 141 if (line.find("idivt") != std::string::npos) { 142 // We always expect both ARM and Thumb divide instructions to be available or not 143 // available. 144 CHECK_NE(line.find("idiva"), std::string::npos); 145 has_div = true; 146 } 147 if (line.find("lpae") != std::string::npos) { 148 has_lpae = true; 149 } 150 } else if (line.find("processor") != std::string::npos && 151 line.find(": 1") != std::string::npos) { 152 smp = true; 153 } 154 } 155 } 156 in.close(); 157 } else { 158 LOG(ERROR) << "Failed to open /proc/cpuinfo"; 159 } 160 return new ArmInstructionSetFeatures(smp, has_div, has_lpae); 161 } 162 163 const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromHwcap() { 164 bool smp = sysconf(_SC_NPROCESSORS_CONF) > 1; 165 166 bool has_div = false; 167 bool has_lpae = false; 168 169 #if defined(__ANDROID__) && defined(__arm__) 170 uint64_t hwcaps = getauxval(AT_HWCAP); 171 LOG(INFO) << "hwcaps=" << hwcaps; 172 if ((hwcaps & HWCAP_IDIVT) != 0) { 173 // We always expect both ARM and Thumb divide instructions to be available or not 174 // available. 175 CHECK_NE(hwcaps & HWCAP_IDIVA, 0U); 176 has_div = true; 177 } 178 if ((hwcaps & HWCAP_LPAE) != 0) { 179 has_lpae = true; 180 } 181 #endif 182 183 return new ArmInstructionSetFeatures(smp, has_div, has_lpae); 184 } 185 186 // A signal handler called by a fault for an illegal instruction. We record the fact in r0 187 // and then increment the PC in the signal context to return to the next instruction. We know the 188 // instruction is an sdiv (4 bytes long). 189 static void bad_divide_inst_handle(int signo ATTRIBUTE_UNUSED, siginfo_t* si ATTRIBUTE_UNUSED, 190 void* data) { 191 #if defined(__arm__) 192 struct ucontext *uc = (struct ucontext *)data; 193 struct sigcontext *sc = &uc->uc_mcontext; 194 sc->arm_r0 = 0; // Set R0 to #0 to signal error. 195 sc->arm_pc += 4; // Skip offending instruction. 196 #else 197 UNUSED(data); 198 #endif 199 } 200 201 const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromAssembly() { 202 const bool smp = true; 203 204 // See if have a sdiv instruction. Register a signal handler and try to execute an sdiv 205 // instruction. If we get a SIGILL then it's not supported. 206 struct sigaction sa, osa; 207 sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO; 208 sa.sa_sigaction = bad_divide_inst_handle; 209 sigaction(SIGILL, &sa, &osa); 210 211 bool has_div = false; 212 #if defined(__arm__) 213 if (artCheckForArmSdivInstruction()) { 214 has_div = true; 215 } 216 #endif 217 218 // Restore the signal handler. 219 sigaction(SIGILL, &osa, nullptr); 220 221 // Use compile time features to "detect" LPAE support. 222 // TODO: write an assembly LPAE support test. 223 #if defined(__ARM_FEATURE_LPAE) 224 const bool has_lpae = true; 225 #else 226 const bool has_lpae = false; 227 #endif 228 return new ArmInstructionSetFeatures(smp, has_div, has_lpae); 229 } 230 231 bool ArmInstructionSetFeatures::Equals(const InstructionSetFeatures* other) const { 232 if (kArm != other->GetInstructionSet()) { 233 return false; 234 } 235 const ArmInstructionSetFeatures* other_as_arm = other->AsArmInstructionSetFeatures(); 236 return IsSmp() == other_as_arm->IsSmp() && 237 has_div_ == other_as_arm->has_div_ && 238 has_atomic_ldrd_strd_ == other_as_arm->has_atomic_ldrd_strd_; 239 } 240 241 uint32_t ArmInstructionSetFeatures::AsBitmap() const { 242 return (IsSmp() ? kSmpBitfield : 0) | 243 (has_div_ ? kDivBitfield : 0) | 244 (has_atomic_ldrd_strd_ ? kAtomicLdrdStrdBitfield : 0); 245 } 246 247 std::string ArmInstructionSetFeatures::GetFeatureString() const { 248 std::string result; 249 if (IsSmp()) { 250 result += "smp"; 251 } else { 252 result += "-smp"; 253 } 254 if (has_div_) { 255 result += ",div"; 256 } else { 257 result += ",-div"; 258 } 259 if (has_atomic_ldrd_strd_) { 260 result += ",atomic_ldrd_strd"; 261 } else { 262 result += ",-atomic_ldrd_strd"; 263 } 264 return result; 265 } 266 267 const InstructionSetFeatures* ArmInstructionSetFeatures::AddFeaturesFromSplitString( 268 const bool smp, const std::vector<std::string>& features, std::string* error_msg) const { 269 bool has_atomic_ldrd_strd = has_atomic_ldrd_strd_; 270 bool has_div = has_div_; 271 for (auto i = features.begin(); i != features.end(); i++) { 272 std::string feature = Trim(*i); 273 if (feature == "div") { 274 has_div = true; 275 } else if (feature == "-div") { 276 has_div = false; 277 } else if (feature == "atomic_ldrd_strd") { 278 has_atomic_ldrd_strd = true; 279 } else if (feature == "-atomic_ldrd_strd") { 280 has_atomic_ldrd_strd = false; 281 } else { 282 *error_msg = StringPrintf("Unknown instruction set feature: '%s'", feature.c_str()); 283 return nullptr; 284 } 285 } 286 return new ArmInstructionSetFeatures(smp, has_div, has_atomic_ldrd_strd); 287 } 288 289 } // namespace art 290