• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
FromVariant(const std::string & variant,std::string * error_msg)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 
FromBitmap(uint32_t bitmap)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 
FromCppDefines()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 
FromCpuInfo()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 
FromHwcap()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).
bad_divide_inst_handle(int signo ATTRIBUTE_UNUSED,siginfo_t * si ATTRIBUTE_UNUSED,void * data)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 
FromAssembly()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 
Equals(const InstructionSetFeatures * other) const231 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 
AsBitmap() const241 uint32_t ArmInstructionSetFeatures::AsBitmap() const {
242   return (IsSmp() ? kSmpBitfield : 0) |
243       (has_div_ ? kDivBitfield : 0) |
244       (has_atomic_ldrd_strd_ ? kAtomicLdrdStrdBitfield : 0);
245 }
246 
GetFeatureString() const247 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 
AddFeaturesFromSplitString(const bool smp,const std::vector<std::string> & features,std::string * error_msg) const267 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