blob: b5f85719a04162a5a28a283bb49e4d839ced6981 [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 Rogers6f3dbba2014-10-14 17:41:57 -070019#include <fstream>
20
21#include "base/casts.h"
22#include "base/stringprintf.h"
23#include "utils.h"
24
Ian Rogers8afeb852014-04-02 14:55:49 -070025namespace art {
26
Narayan Kamath11d9f062014-04-23 20:24:57 +010027const char* GetInstructionSetString(const InstructionSet isa) {
28 switch (isa) {
29 case kArm:
30 case kThumb2:
31 return "arm";
32 case kArm64:
33 return "arm64";
34 case kX86:
35 return "x86";
36 case kX86_64:
37 return "x86_64";
38 case kMips:
39 return "mips";
40 case kNone:
41 return "none";
42 default:
43 LOG(FATAL) << "Unknown ISA " << isa;
Ian Rogers6f3dbba2014-10-14 17:41:57 -070044 UNREACHABLE();
Narayan Kamath11d9f062014-04-23 20:24:57 +010045 }
46}
47
48InstructionSet GetInstructionSetFromString(const char* isa_str) {
49 CHECK(isa_str != nullptr);
50
Andreas Gampe20c89302014-08-19 17:28:06 -070051 if (strcmp("arm", isa_str) == 0) {
Narayan Kamath11d9f062014-04-23 20:24:57 +010052 return kArm;
Andreas Gampe20c89302014-08-19 17:28:06 -070053 } else if (strcmp("arm64", isa_str) == 0) {
Narayan Kamath11d9f062014-04-23 20:24:57 +010054 return kArm64;
Andreas Gampe20c89302014-08-19 17:28:06 -070055 } else if (strcmp("x86", isa_str) == 0) {
Narayan Kamath11d9f062014-04-23 20:24:57 +010056 return kX86;
Andreas Gampe20c89302014-08-19 17:28:06 -070057 } else if (strcmp("x86_64", isa_str) == 0) {
Narayan Kamath11d9f062014-04-23 20:24:57 +010058 return kX86_64;
Andreas Gampe20c89302014-08-19 17:28:06 -070059 } else if (strcmp("mips", isa_str) == 0) {
Narayan Kamath11d9f062014-04-23 20:24:57 +010060 return kMips;
Narayan Kamath11d9f062014-04-23 20:24:57 +010061 }
62
Narayan Kamath11d9f062014-04-23 20:24:57 +010063 return kNone;
64}
65
Andreas Gampeaf13ad92014-04-11 12:07:48 -070066size_t GetInstructionSetAlignment(InstructionSet isa) {
67 switch (isa) {
68 case kArm:
69 // Fall-through.
70 case kThumb2:
71 return kArmAlignment;
72 case kArm64:
73 return kArm64Alignment;
74 case kX86:
75 // Fall-through.
76 case kX86_64:
77 return kX86Alignment;
78 case kMips:
79 return kMipsAlignment;
80 case kNone:
81 LOG(FATAL) << "ISA kNone does not have alignment.";
82 return 0;
83 default:
84 LOG(FATAL) << "Unknown ISA " << isa;
85 return 0;
86 }
87}
88
Andreas Gampe7ea6f792014-07-14 16:21:44 -070089
90static constexpr size_t kDefaultStackOverflowReservedBytes = 16 * KB;
91static constexpr size_t kMipsStackOverflowReservedBytes = kDefaultStackOverflowReservedBytes;
92
Dave Allison648d7112014-07-25 16:15:27 -070093static constexpr size_t kArmStackOverflowReservedBytes = 8 * KB;
94static constexpr size_t kArm64StackOverflowReservedBytes = 8 * KB;
95static constexpr size_t kX86StackOverflowReservedBytes = 8 * KB;
96static constexpr size_t kX86_64StackOverflowReservedBytes = 8 * KB;
Andreas Gampe7ea6f792014-07-14 16:21:44 -070097
98size_t GetStackOverflowReservedBytes(InstructionSet isa) {
99 switch (isa) {
100 case kArm: // Intentional fall-through.
101 case kThumb2:
102 return kArmStackOverflowReservedBytes;
103
104 case kArm64:
105 return kArm64StackOverflowReservedBytes;
106
107 case kMips:
108 return kMipsStackOverflowReservedBytes;
109
110 case kX86:
111 return kX86StackOverflowReservedBytes;
112
113 case kX86_64:
114 return kX86_64StackOverflowReservedBytes;
115
116 case kNone:
117 LOG(FATAL) << "kNone has no stack overflow size";
118 return 0;
119
120 default:
121 LOG(FATAL) << "Unknown instruction set" << isa;
122 return 0;
123 }
124}
125
Ian Rogers6f3dbba2014-10-14 17:41:57 -0700126const InstructionSetFeatures* InstructionSetFeatures::FromVariant(InstructionSet isa,
127 const std::string& variant,
128 std::string* error_msg) {
129 const InstructionSetFeatures* result;
130 switch (isa) {
131 case kArm:
132 case kThumb2:
133 result = ArmInstructionSetFeatures::FromVariant(variant, error_msg);
134 break;
135 default:
136 result = UnknownInstructionSetFeatures::Unknown(isa);
137 break;
Ian Rogers8afeb852014-04-02 14:55:49 -0700138 }
Ian Rogers6f3dbba2014-10-14 17:41:57 -0700139 CHECK_EQ(result == nullptr, error_msg->size() != 0);
140 return result;
141}
142
143const InstructionSetFeatures* InstructionSetFeatures::FromFeatureString(InstructionSet isa,
144 const std::string& feature_list,
145 std::string* error_msg) {
146 const InstructionSetFeatures* result;
147 switch (isa) {
148 case kArm:
149 case kThumb2:
150 result = ArmInstructionSetFeatures::FromFeatureString(feature_list, error_msg);
151 break;
152 default:
153 result = UnknownInstructionSetFeatures::Unknown(isa);
154 break;
155 }
156 // TODO: warn if feature_list doesn't agree with result's GetFeatureList().
157 CHECK_EQ(result == nullptr, error_msg->size() != 0);
158 return result;
159}
160
161const InstructionSetFeatures* InstructionSetFeatures::FromBitmap(InstructionSet isa,
162 uint32_t bitmap) {
163 const InstructionSetFeatures* result;
164 switch (isa) {
165 case kArm:
166 case kThumb2:
167 result = ArmInstructionSetFeatures::FromBitmap(bitmap);
168 break;
169 default:
170 result = UnknownInstructionSetFeatures::Unknown(isa);
171 break;
172 }
173 CHECK_EQ(bitmap, result->AsBitmap());
174 return result;
175}
176
177const InstructionSetFeatures* InstructionSetFeatures::FromCppDefines() {
178 const InstructionSetFeatures* result;
179 switch (kRuntimeISA) {
180 case kArm:
181 case kThumb2:
182 result = ArmInstructionSetFeatures::FromCppDefines();
183 break;
184 default:
185 result = UnknownInstructionSetFeatures::Unknown(kRuntimeISA);
186 break;
Ian Rogers8afeb852014-04-02 14:55:49 -0700187 }
188 return result;
189}
190
Ian Rogers6f3dbba2014-10-14 17:41:57 -0700191
192const InstructionSetFeatures* InstructionSetFeatures::FromCpuInfo() {
193 const InstructionSetFeatures* result;
194 switch (kRuntimeISA) {
195 case kArm:
196 case kThumb2:
197 result = ArmInstructionSetFeatures::FromCpuInfo();
198 break;
199 default:
200 result = UnknownInstructionSetFeatures::Unknown(kRuntimeISA);
201 break;
202 }
203 return result;
204}
205
206const InstructionSetFeatures* InstructionSetFeatures::FromHwcap() {
207 const InstructionSetFeatures* result;
208 switch (kRuntimeISA) {
209 case kArm:
210 case kThumb2:
211 result = ArmInstructionSetFeatures::FromHwcap();
212 break;
213 default:
214 result = UnknownInstructionSetFeatures::Unknown(kRuntimeISA);
215 break;
216 }
217 return result;
218}
219
220const InstructionSetFeatures* InstructionSetFeatures::FromAssembly() {
221 const InstructionSetFeatures* result;
222 switch (kRuntimeISA) {
223 case kArm:
224 case kThumb2:
225 result = ArmInstructionSetFeatures::FromAssembly();
226 break;
227 default:
228 result = UnknownInstructionSetFeatures::Unknown(kRuntimeISA);
229 break;
230 }
231 return result;
232}
233
234const ArmInstructionSetFeatures* InstructionSetFeatures::AsArmInstructionSetFeatures() const {
235 DCHECK_EQ(kArm, GetInstructionSet());
236 return down_cast<const ArmInstructionSetFeatures*>(this);
237}
238
239std::ostream& operator<<(std::ostream& os, const InstructionSetFeatures& rhs) {
240 os << "ISA: " << rhs.GetInstructionSet() << " Feature string: " << rhs.GetFeatureString();
241 return os;
242}
243
244const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromFeatureString(
245 const std::string& feature_list, std::string* error_msg) {
246 std::vector<std::string> features;
247 Split(feature_list, ',', &features);
248 bool has_lpae = false;
249 bool has_div = false;
250 for (auto i = features.begin(); i != features.end(); i++) {
251 std::string feature = Trim(*i);
252 if (feature == "default" || feature == "none") {
253 // Nothing to do.
254 } else if (feature == "div") {
255 has_div = true;
256 } else if (feature == "nodiv") {
257 has_div = false;
258 } else if (feature == "lpae") {
259 has_lpae = true;
260 } else if (feature == "nolpae") {
261 has_lpae = false;
262 } else {
263 *error_msg = StringPrintf("Unknown instruction set feature: '%s'", feature.c_str());
264 return nullptr;
265 }
266 }
267 return new ArmInstructionSetFeatures(has_lpae, has_div);
268}
269
270const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromVariant(
271 const std::string& variant, std::string* error_msg) {
272 // Look for variants that have divide support.
273 bool has_div = false;
274 {
275 static const char* arm_variants_with_div[] = {
276 "cortex-a7", "cortex-a12", "cortex-a15", "cortex-a17", "cortex-a53", "cortex-a57",
277 "cortex-m3", "cortex-m4", "cortex-r4", "cortex-r5",
278 "cyclone", "denver", "krait", "swift"
279 };
280 for (const char* div_variant : arm_variants_with_div) {
281 if (variant == div_variant) {
282 has_div = true;
283 break;
284 }
285 }
286 }
287 // Look for variants that have LPAE support.
288 bool has_lpae = false;
289 {
290 static const char* arm_variants_with_lpae[] = {
291 "cortex-a7", "cortex-a15", "krait", "denver"
292 };
293 for (const char* lpae_variant : arm_variants_with_lpae) {
294 if (variant == lpae_variant) {
295 has_lpae = true;
296 break;
297 }
298 }
299 }
300 if (has_div == false && has_lpae == false) {
301 // Avoid unsupported variants.
302 static const char* unsupported_arm_variants[] = {
303 // ARM processors that aren't ARMv7 compatible aren't supported.
304 "arm2", "arm250", "arm3", "arm6", "arm60", "arm600", "arm610", "arm620",
305 "cortex-m0", "cortex-m0plus", "cortex-m1",
306 "fa526", "fa626", "fa606te", "fa626te", "fmp626", "fa726te",
307 "iwmmxt", "iwmmxt2",
308 "strongarm", "strongarm110", "strongarm1100", "strongarm1110",
309 "xscale"
310 };
311 for (const char* us_variant : unsupported_arm_variants) {
312 if (variant == us_variant) {
313 *error_msg = StringPrintf("Attempt to use unsupported ARM variant: %s", us_variant);
314 return nullptr;
315 }
316 }
317 // Warn if the variant is unknown.
318 // TODO: some of the variants below may have feature support, but that support is currently
319 // unknown so we'll choose conservative (sub-optimal) defaults without warning.
320 // TODO: some of the architectures may not support all features required by ART and should be
321 // moved to unsupported_arm_variants[] above.
322 static const char* arm_variants_without_known_features[] = {
323 "arm7", "arm7m", "arm7d", "arm7dm", "arm7di", "arm7dmi", "arm70", "arm700", "arm700i",
324 "arm710", "arm710c", "arm7100", "arm720", "arm7500", "arm7500fe", "arm7tdmi", "arm7tdmi-s",
325 "arm710t", "arm720t", "arm740t",
326 "arm8", "arm810",
327 "arm9", "arm9e", "arm920", "arm920t", "arm922t", "arm946e-s", "arm966e-s", "arm968e-s",
328 "arm926ej-s", "arm940t", "arm9tdmi",
329 "arm10tdmi", "arm1020t", "arm1026ej-s", "arm10e", "arm1020e", "arm1022e",
330 "arm1136j-s", "arm1136jf-s",
331 "arm1156t2-s", "arm1156t2f-s", "arm1176jz-s", "arm1176jzf-s",
332 "cortex-a5", "cortex-a8", "cortex-a9", "cortex-a9-mp", "cortex-r4f",
333 "marvell-pj4", "mpcore", "mpcorenovfp"
334 };
335 bool found = false;
336 for (const char* ff_variant : arm_variants_without_known_features) {
337 if (variant == ff_variant) {
338 found = true;
339 break;
340 }
341 }
342 if (!found) {
343 LOG(WARNING) << "Unknown instruction set features for ARM CPU variant (" << variant
344 << ") using conservative defaults";
345 }
346 }
347 return new ArmInstructionSetFeatures(has_lpae, has_div);
348}
349
350const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromBitmap(uint32_t bitmap) {
351 bool has_lpae = (bitmap & kLpaeBitfield) != 0;
352 bool has_div = (bitmap & kDivBitfield) != 0;
353 return new ArmInstructionSetFeatures(has_lpae, has_div);
354}
355
356const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromCppDefines() {
357#if defined(__ARM_ARCH_EXT_IDIV__)
358 bool has_div = true;
359#else
360 bool has_div = false;
361#endif
362#if defined(__ARM_FEATURE_LPAE)
363 bool has_lpae = true;
364#else
365 bool has_lpae = false;
366#endif
367 return new ArmInstructionSetFeatures(has_lpae, has_div);
368}
369
370const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromCpuInfo() {
371 // Look in /proc/cpuinfo for features we need. Only use this when we can guarantee that
372 // the kernel puts the appropriate feature flags in here. Sometimes it doesn't.
373 bool has_lpae = false;
374 bool has_div = false;
375
376 std::ifstream in("/proc/cpuinfo");
377 if (!in.fail()) {
378 while (!in.eof()) {
379 std::string line;
380 std::getline(in, line);
381 if (!in.eof()) {
382 LOG(INFO) << "cpuinfo line: " << line;
383 if (line.find("Features") != std::string::npos) {
384 LOG(INFO) << "found features";
385 if (line.find("idivt") != std::string::npos) {
386 // We always expect both ARM and Thumb divide instructions to be available or not
387 // available.
388 CHECK_NE(line.find("idiva"), std::string::npos);
389 has_div = true;
390 }
391 if (line.find("lpae") != std::string::npos) {
392 has_lpae = true;
393 }
394 }
395 }
396 }
397 in.close();
398 } else {
399 LOG(INFO) << "Failed to open /proc/cpuinfo";
400 }
401 return new ArmInstructionSetFeatures(has_lpae, has_div);
402}
403
404#if defined(HAVE_ANDROID_OS) && defined(__arm__)
405#include <sys/auxv.h>
406#include <asm/hwcap.h>
407#endif
408
409const ArmInstructionSetFeatures* ArmInstructionSetFeatures::FromHwcap() {
410 bool has_lpae = false;
411 bool has_div = false;
412
413#if defined(HAVE_ANDROID_OS) && defined(__arm__)
414 uint64_t hwcaps = getauxval(AT_HWCAP);
415 LOG(INFO) << "hwcaps=" << hwcaps;
416 if ((hwcaps & HWCAP_IDIVT) != 0) {
417 // We always expect both ARM and Thumb divide instructions to be available or not
418 // available.
419 CHECK_NE(hwcaps & HWCAP_IDIVA, 0U);
420 has_div = true;
421 }
422 if ((hwcaps & HWCAP_LPAE) != 0) {
423 has_lpae = true;
424 }
425#endif
426
427 return new ArmInstructionSetFeatures(has_lpae, has_div);
428}
429
430// A signal handler called by a fault for an illegal instruction. We record the fact in r0
431// and then increment the PC in the signal context to return to the next instruction. We know the
432// instruction is an sdiv (4 bytes long).
433static void bad_divide_inst_handle(int signo, siginfo *si, void *data) {
434 UNUSED(signo);
435 UNUSED(si);
436#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