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(__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