blob: e165a7565901c5943dfb803edd434defc52b9346 [file] [log] [blame]
Ian Rogers8afeb852014-04-02 14:55:49 -07001/*
2 * Copyright (C) 2011 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.h"
18
Ian Rogersad69bcd2014-10-16 23:05:10 -070019#include <signal.h>
Ian Rogers6f3dbba2014-10-14 17:41:57 -070020#include <fstream>
21
22#include "base/casts.h"
23#include "base/stringprintf.h"
24#include "utils.h"
25
Ian Rogers8afeb852014-04-02 14:55:49 -070026namespace art {
27
Narayan Kamath11d9f062014-04-23 20:24:57 +010028const char* GetInstructionSetString(const InstructionSet isa) {
29 switch (isa) {
30 case kArm:
31 case kThumb2:
32 return "arm";
33 case kArm64:
34 return "arm64";
35 case kX86:
36 return "x86";
37 case kX86_64:
38 return "x86_64";
39 case kMips:
40 return "mips";
41 case kNone:
42 return "none";
43 default:
44 LOG(FATAL) << "Unknown ISA " << isa;
Ian Rogers6f3dbba2014-10-14 17:41:57 -070045 UNREACHABLE();
Narayan Kamath11d9f062014-04-23 20:24:57 +010046 }
47}
48
49InstructionSet GetInstructionSetFromString(const char* isa_str) {
50 CHECK(isa_str != nullptr);
51
Andreas Gampe20c89302014-08-19 17:28:06 -070052 if (strcmp("arm", isa_str) == 0) {
Narayan Kamath11d9f062014-04-23 20:24:57 +010053 return kArm;
Andreas Gampe20c89302014-08-19 17:28:06 -070054 } else if (strcmp("arm64", isa_str) == 0) {
Narayan Kamath11d9f062014-04-23 20:24:57 +010055 return kArm64;
Andreas Gampe20c89302014-08-19 17:28:06 -070056 } else if (strcmp("x86", isa_str) == 0) {
Narayan Kamath11d9f062014-04-23 20:24:57 +010057 return kX86;
Andreas Gampe20c89302014-08-19 17:28:06 -070058 } else if (strcmp("x86_64", isa_str) == 0) {
Narayan Kamath11d9f062014-04-23 20:24:57 +010059 return kX86_64;
Andreas Gampe20c89302014-08-19 17:28:06 -070060 } else if (strcmp("mips", isa_str) == 0) {
Narayan Kamath11d9f062014-04-23 20:24:57 +010061 return kMips;
Narayan Kamath11d9f062014-04-23 20:24:57 +010062 }
63
Narayan Kamath11d9f062014-04-23 20:24:57 +010064 return kNone;
65}
66
Andreas Gampeaf13ad92014-04-11 12:07:48 -070067size_t GetInstructionSetAlignment(InstructionSet isa) {
68 switch (isa) {
69 case kArm:
70 // Fall-through.
71 case kThumb2:
72 return kArmAlignment;
73 case kArm64:
74 return kArm64Alignment;
75 case kX86:
76 // Fall-through.
77 case kX86_64:
78 return kX86Alignment;
79 case kMips:
80 return kMipsAlignment;
81 case kNone:
82 LOG(FATAL) << "ISA kNone does not have alignment.";
83 return 0;
84 default:
85 LOG(FATAL) << "Unknown ISA " << isa;
86 return 0;
87 }
88}
89
Andreas Gampe7ea6f792014-07-14 16:21:44 -070090
91static constexpr size_t kDefaultStackOverflowReservedBytes = 16 * KB;
92static constexpr size_t kMipsStackOverflowReservedBytes = kDefaultStackOverflowReservedBytes;
93
Dave Allison648d7112014-07-25 16:15:27 -070094static constexpr size_t kArmStackOverflowReservedBytes = 8 * KB;
95static constexpr size_t kArm64StackOverflowReservedBytes = 8 * KB;
96static constexpr size_t kX86StackOverflowReservedBytes = 8 * KB;
97static constexpr size_t kX86_64StackOverflowReservedBytes = 8 * KB;
Andreas Gampe7ea6f792014-07-14 16:21:44 -070098
99size_t GetStackOverflowReservedBytes(InstructionSet isa) {
100 switch (isa) {
101 case kArm: // Intentional fall-through.
102 case kThumb2:
103 return kArmStackOverflowReservedBytes;
104
105 case kArm64:
106 return kArm64StackOverflowReservedBytes;
107
108 case kMips:
109 return kMipsStackOverflowReservedBytes;
110
111 case kX86:
112 return kX86StackOverflowReservedBytes;
113
114 case kX86_64:
115 return kX86_64StackOverflowReservedBytes;
116
117 case kNone:
118 LOG(FATAL) << "kNone has no stack overflow size";
119 return 0;
120
121 default:
122 LOG(FATAL) << "Unknown instruction set" << isa;
123 return 0;
124 }
125}
126
Ian Rogers6f3dbba2014-10-14 17:41:57 -0700127const InstructionSetFeatures* InstructionSetFeatures::FromVariant(InstructionSet isa,
128 const std::string& variant,
129 std::string* error_msg) {
130 const InstructionSetFeatures* result;
131 switch (isa) {
132 case kArm:
133 case kThumb2:
134 result = ArmInstructionSetFeatures::FromVariant(variant, error_msg);
135 break;
136 default:
137 result = UnknownInstructionSetFeatures::Unknown(isa);
138 break;
Ian Rogers8afeb852014-04-02 14:55:49 -0700139 }
Ian Rogers6f3dbba2014-10-14 17:41:57 -0700140 CHECK_EQ(result == nullptr, error_msg->size() != 0);
141 return result;
142}
143
144const InstructionSetFeatures* InstructionSetFeatures::FromFeatureString(InstructionSet isa,
145 const std::string& feature_list,
146 std::string* error_msg) {
147 const InstructionSetFeatures* result;
148 switch (isa) {
149 case kArm:
150 case kThumb2:
151 result = ArmInstructionSetFeatures::FromFeatureString(feature_list, error_msg);
152 break;
153 default:
154 result = UnknownInstructionSetFeatures::Unknown(isa);
155 break;
156 }
157 // TODO: warn if feature_list doesn't agree with result's GetFeatureList().
158 CHECK_EQ(result == nullptr, error_msg->size() != 0);
159 return result;
160}
161
162const InstructionSetFeatures* InstructionSetFeatures::FromBitmap(InstructionSet isa,
163 uint32_t bitmap) {
164 const InstructionSetFeatures* result;
165 switch (isa) {
166 case kArm:
167 case kThumb2:
168 result = ArmInstructionSetFeatures::FromBitmap(bitmap);
169 break;
170 default:
171 result = UnknownInstructionSetFeatures::Unknown(isa);
172 break;
173 }
174 CHECK_EQ(bitmap, result->AsBitmap());
175 return result;
176}
177
178const InstructionSetFeatures* InstructionSetFeatures::FromCppDefines() {
179 const InstructionSetFeatures* result;
180 switch (kRuntimeISA) {
181 case kArm:
182 case kThumb2:
183 result = ArmInstructionSetFeatures::FromCppDefines();
184 break;
185 default:
186 result = UnknownInstructionSetFeatures::Unknown(kRuntimeISA);
187 break;
Ian Rogers8afeb852014-04-02 14:55:49 -0700188 }
189 return result;
190}
191
Ian Rogers6f3dbba2014-10-14 17:41:57 -0700192
193const InstructionSetFeatures* InstructionSetFeatures::FromCpuInfo() {
194 const InstructionSetFeatures* result;
195 switch (kRuntimeISA) {
196 case kArm:
197 case kThumb2:
198 result = ArmInstructionSetFeatures::FromCpuInfo();
199 break;
200 default:
201 result = UnknownInstructionSetFeatures::Unknown(kRuntimeISA);
202 break;
203 }
204 return result;
205}
206
207const InstructionSetFeatures* InstructionSetFeatures::FromHwcap() {
208 const InstructionSetFeatures* result;
209 switch (kRuntimeISA) {
210 case kArm:
211 case kThumb2:
212 result = ArmInstructionSetFeatures::FromHwcap();
213 break;
214 default:
215 result = UnknownInstructionSetFeatures::Unknown(kRuntimeISA);
216 break;
217 }
218 return result;
219}
220
221const InstructionSetFeatures* InstructionSetFeatures::FromAssembly() {
222 const InstructionSetFeatures* result;
223 switch (kRuntimeISA) {
224 case kArm:
225 case kThumb2:
226 result = ArmInstructionSetFeatures::FromAssembly();
227 break;
228 default:
229 result = UnknownInstructionSetFeatures::Unknown(kRuntimeISA);
230 break;
231 }
232 return result;
233}
234
235const ArmInstructionSetFeatures* InstructionSetFeatures::AsArmInstructionSetFeatures() const {
236 DCHECK_EQ(kArm, GetInstructionSet());
237 return down_cast<const ArmInstructionSetFeatures*>(this);
238}
239
240std::ostream& operator<<(std::ostream& os, const InstructionSetFeatures& rhs) {
241 os << "ISA: " << rhs.GetInstructionSet() << " Feature string: " << rhs.GetFeatureString();
242 return os;
243}
244
245const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromFeatureString(
246 const std::string& feature_list, std::string* error_msg) {
247 std::vector<std::string> features;
248 Split(feature_list, ',', &features);
249 bool has_lpae = false;
250 bool has_div = false;
251 for (auto i = features.begin(); i != features.end(); i++) {
252 std::string feature = Trim(*i);
253 if (feature == "default" || feature == "none") {
254 // Nothing to do.
255 } else if (feature == "div") {
256 has_div = true;
257 } else if (feature == "nodiv") {
258 has_div = false;
259 } else if (feature == "lpae") {
260 has_lpae = true;
261 } else if (feature == "nolpae") {
262 has_lpae = false;
263 } else {
264 *error_msg = StringPrintf("Unknown instruction set feature: '%s'", feature.c_str());
265 return nullptr;
266 }
267 }
268 return new ArmInstructionSetFeatures(has_lpae, has_div);
269}
270
271const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromVariant(
272 const std::string& variant, std::string* error_msg) {
273 // Look for variants that have divide support.
274 bool has_div = false;
275 {
276 static const char* arm_variants_with_div[] = {
277 "cortex-a7", "cortex-a12", "cortex-a15", "cortex-a17", "cortex-a53", "cortex-a57",
278 "cortex-m3", "cortex-m4", "cortex-r4", "cortex-r5",
279 "cyclone", "denver", "krait", "swift"
280 };
281 for (const char* div_variant : arm_variants_with_div) {
282 if (variant == div_variant) {
283 has_div = true;
284 break;
285 }
286 }
287 }
288 // Look for variants that have LPAE support.
289 bool has_lpae = false;
290 {
291 static const char* arm_variants_with_lpae[] = {
292 "cortex-a7", "cortex-a15", "krait", "denver"
293 };
294 for (const char* lpae_variant : arm_variants_with_lpae) {
295 if (variant == lpae_variant) {
296 has_lpae = true;
297 break;
298 }
299 }
300 }
301 if (has_div == false && has_lpae == false) {
302 // Avoid unsupported variants.
303 static const char* unsupported_arm_variants[] = {
304 // ARM processors that aren't ARMv7 compatible aren't supported.
305 "arm2", "arm250", "arm3", "arm6", "arm60", "arm600", "arm610", "arm620",
306 "cortex-m0", "cortex-m0plus", "cortex-m1",
307 "fa526", "fa626", "fa606te", "fa626te", "fmp626", "fa726te",
308 "iwmmxt", "iwmmxt2",
309 "strongarm", "strongarm110", "strongarm1100", "strongarm1110",
310 "xscale"
311 };
312 for (const char* us_variant : unsupported_arm_variants) {
313 if (variant == us_variant) {
314 *error_msg = StringPrintf("Attempt to use unsupported ARM variant: %s", us_variant);
315 return nullptr;
316 }
317 }
318 // Warn if the variant is unknown.
319 // TODO: some of the variants below may have feature support, but that support is currently
320 // unknown so we'll choose conservative (sub-optimal) defaults without warning.
321 // TODO: some of the architectures may not support all features required by ART and should be
322 // moved to unsupported_arm_variants[] above.
323 static const char* arm_variants_without_known_features[] = {
324 "arm7", "arm7m", "arm7d", "arm7dm", "arm7di", "arm7dmi", "arm70", "arm700", "arm700i",
325 "arm710", "arm710c", "arm7100", "arm720", "arm7500", "arm7500fe", "arm7tdmi", "arm7tdmi-s",
326 "arm710t", "arm720t", "arm740t",
327 "arm8", "arm810",
328 "arm9", "arm9e", "arm920", "arm920t", "arm922t", "arm946e-s", "arm966e-s", "arm968e-s",
329 "arm926ej-s", "arm940t", "arm9tdmi",
330 "arm10tdmi", "arm1020t", "arm1026ej-s", "arm10e", "arm1020e", "arm1022e",
331 "arm1136j-s", "arm1136jf-s",
332 "arm1156t2-s", "arm1156t2f-s", "arm1176jz-s", "arm1176jzf-s",
333 "cortex-a5", "cortex-a8", "cortex-a9", "cortex-a9-mp", "cortex-r4f",
334 "marvell-pj4", "mpcore", "mpcorenovfp"
335 };
336 bool found = false;
337 for (const char* ff_variant : arm_variants_without_known_features) {
338 if (variant == ff_variant) {
339 found = true;
340 break;
341 }
342 }
343 if (!found) {
344 LOG(WARNING) << "Unknown instruction set features for ARM CPU variant (" << variant
345 << ") using conservative defaults";
346 }
347 }
348 return new ArmInstructionSetFeatures(has_lpae, has_div);
349}
350
351const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromBitmap(uint32_t bitmap) {
352 bool has_lpae = (bitmap & kLpaeBitfield) != 0;
353 bool has_div = (bitmap & kDivBitfield) != 0;
354 return new ArmInstructionSetFeatures(has_lpae, has_div);
355}
356
357const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromCppDefines() {
358#if defined(__ARM_ARCH_EXT_IDIV__)
359 bool has_div = true;
360#else
361 bool has_div = false;
362#endif
363#if defined(__ARM_FEATURE_LPAE)
364 bool has_lpae = true;
365#else
366 bool has_lpae = false;
367#endif
368 return new ArmInstructionSetFeatures(has_lpae, has_div);
369}
370
371const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromCpuInfo() {
372 // Look in /proc/cpuinfo for features we need. Only use this when we can guarantee that
373 // the kernel puts the appropriate feature flags in here. Sometimes it doesn't.
374 bool has_lpae = false;
375 bool has_div = false;
376
377 std::ifstream in("/proc/cpuinfo");
378 if (!in.fail()) {
379 while (!in.eof()) {
380 std::string line;
381 std::getline(in, line);
382 if (!in.eof()) {
383 LOG(INFO) << "cpuinfo line: " << line;
384 if (line.find("Features") != std::string::npos) {
385 LOG(INFO) << "found features";
386 if (line.find("idivt") != std::string::npos) {
387 // We always expect both ARM and Thumb divide instructions to be available or not
388 // available.
389 CHECK_NE(line.find("idiva"), std::string::npos);
390 has_div = true;
391 }
392 if (line.find("lpae") != std::string::npos) {
393 has_lpae = true;
394 }
395 }
396 }
397 }
398 in.close();
399 } else {
400 LOG(INFO) << "Failed to open /proc/cpuinfo";
401 }
402 return new ArmInstructionSetFeatures(has_lpae, has_div);
403}
404
405#if defined(HAVE_ANDROID_OS) && defined(__arm__)
406#include <sys/auxv.h>
407#include <asm/hwcap.h>
408#endif
409
410const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromHwcap() {
411 bool has_lpae = false;
412 bool has_div = false;
413
414#if defined(HAVE_ANDROID_OS) && defined(__arm__)
415 uint64_t hwcaps = getauxval(AT_HWCAP);
416 LOG(INFO) << "hwcaps=" << hwcaps;
417 if ((hwcaps & HWCAP_IDIVT) != 0) {
418 // We always expect both ARM and Thumb divide instructions to be available or not
419 // available.
420 CHECK_NE(hwcaps & HWCAP_IDIVA, 0U);
421 has_div = true;
422 }
423 if ((hwcaps & HWCAP_LPAE) != 0) {
424 has_lpae = true;
425 }
426#endif
427
428 return new ArmInstructionSetFeatures(has_lpae, has_div);
429}
430
431// A signal handler called by a fault for an illegal instruction. We record the fact in r0
432// and then increment the PC in the signal context to return to the next instruction. We know the
433// instruction is an sdiv (4 bytes long).
Ian Rogers6a3c1fc2014-10-31 00:33:20 -0700434static void bad_divide_inst_handle(int signo ATTRIBUTE_UNUSED, siginfo_t* si ATTRIBUTE_UNUSED,
435 void* data) {
Ian Rogers6f3dbba2014-10-14 17:41:57 -0700436#if defined(__arm__)
437 struct ucontext *uc = (struct ucontext *)data;
438 struct sigcontext *sc = &uc->uc_mcontext;
439 sc->arm_r0 = 0; // Set R0 to #0 to signal error.
440 sc->arm_pc += 4; // Skip offending instruction.
441#else
442 UNUSED(data);
443#endif
444}
445
446#if defined(__arm__)
447extern "C" bool artCheckForARMSDIVInstruction();
448#endif
449
450const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromAssembly() {
451 // See if have a sdiv instruction. Register a signal handler and try to execute an sdiv
452 // instruction. If we get a SIGILL then it's not supported.
453 struct sigaction sa, osa;
454 sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO;
455 sa.sa_sigaction = bad_divide_inst_handle;
456 sigaction(SIGILL, &sa, &osa);
457
458 bool has_div = false;
459#if defined(__arm__)
460 if (artCheckForARMSDIVInstruction()) {
461 has_div = true;
462 }
463#endif
464
465 // Restore the signal handler.
466 sigaction(SIGILL, &osa, nullptr);
467
468 // Use compile time features to "detect" LPAE support.
469 // TODO: write an assembly LPAE support test.
470#if defined(__ARM_FEATURE_LPAE)
471 bool has_lpae = true;
472#else
473 bool has_lpae = false;
474#endif
475 return new ArmInstructionSetFeatures(has_lpae, has_div);
476}
477
478
479bool ArmInstructionSetFeatures::Equals(const InstructionSetFeatures* other) const {
480 if (kArm != other->GetInstructionSet()) {
481 return false;
482 }
483 const ArmInstructionSetFeatures* other_as_arm = other->AsArmInstructionSetFeatures();
484 return has_lpae_ == other_as_arm->has_lpae_ && has_div_ == other_as_arm->has_div_;
485}
486
487uint32_t ArmInstructionSetFeatures::AsBitmap() const {
488 return (has_lpae_ ? kLpaeBitfield : 0) | (has_div_ ? kDivBitfield : 0);
489}
490
491std::string ArmInstructionSetFeatures::GetFeatureString() const {
492 std::string result;
493 if (has_div_) {
494 result += ",div";
495 }
496 if (has_lpae_) {
497 result += ",lpae";
498 }
499 if (result.size() == 0) {
500 return "none";
501 } else {
502 // Strip leading comma.
503 return result.substr(1, result.size());
504 }
505}
506
Ian Rogers8afeb852014-04-02 14:55:49 -0700507} // namespace art