Home | History | Annotate | Download | only in arm
      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