2 * Copyright (C) 2024 Mikulas Patocka
4 * This file is part of Ajla.
6 * Ajla is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software
8 * Foundation, either version 3 of the License, or (at your option) any later
11 * Ajla is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along with
16 * Ajla. If not, see <https://www.gnu.org/licenses/>.
29 #ifdef DEBUG_CRASH_HANDLER
30 void *u_data_trap_lookup(void *ptr
);
31 void *c_data_trap_lookup(void *ptr
);
32 static void dump_registers(int sig
, ucontext_t
*uc
)
35 #if defined(ARCH_IA64)
36 debug("%s at %lx", sig
== SIGSEGV
? "sigsegv" : sig
== SIGBUS
? "sigbus" : "sigill", uc
->uc_mcontext
.sc_ip
);
37 for (i
= 0; i
< 32; i
+= 2) {
38 debug("gr_%02x = %016lx gr_%02x = %016lx", i
, uc
->uc_mcontext
.sc_gr
[i
], i
+ 1, uc
->uc_mcontext
.sc_gr
[i
+ 1]);
40 /*for (i = 0; i < 32; i += 2) {
41 debug("gr_%02x = %016lx gr_%02x = %016lx", 32 + i, ptr[i - 16], 32 + i + 1, ptr[i + 1 - 16]);
43 for (i
= 0; i
< 8; i
+= 2) {
44 debug("br_%02x = %016lx br_%02x = %016lx", i
, uc
->uc_mcontext
.sc_br
[i
], i
+ 1, uc
->uc_mcontext
.sc_br
[i
+ 1]);
47 #if defined(ARCH_MIPS)
48 debug("%s at %llx", sig
== SIGSEGV
? "sigsegv" : sig
== SIGBUS
? "sigbus" : "sigill", uc
->uc_mcontext
.pc
);
49 for (i
= 0; i
< 32; i
++)
50 debug("gpr_%d = %llx", i
, uc
->uc_mcontext
.gregs
[i
]);
51 call(data_trap_lookup
)(num_to_ptr(uc
->uc_mcontext
.pc
));
53 #if defined(ARCH_POWER)
54 debug("%s at %lx", sig
== SIGSEGV
? "sigsegv" : sig
== SIGBUS
? "sigbus" : "sigill", uc
->uc_mcontext
.regs
->nip
);
55 for (i
= 0; i
< 32; i
++)
56 debug("gpr_%d = %lx", i
, uc
->uc_mcontext
.regs
->gpr
[i
]);
58 #if defined(ARCH_S390)
59 debug("%s at %lx", sig
== SIGSEGV
? "sigsegv" : sig
== SIGBUS
? "sigbus" : "sigill", uc
->uc_mcontext
.psw
.addr
);
60 for (i
= 0; i
< 16; i
++)
61 debug("gpr_%d = %lx", i
, uc
->uc_mcontext
.gregs
[i
]);
64 debug("%s at %x", sig
== SIGSEGV
? "sigsegv" : sig
== SIGBUS
? "sigbus" : "sigill", uc
->uc_mcontext
.pc
);
65 for (i
= 0; i
< 16; i
++)
66 debug("gpr_%d = %x", i
, uc
->uc_mcontext
.gregs
[i
]);
67 debug("pr = %x", uc
->uc_mcontext
.pr
);
68 debug("sr = %x", uc
->uc_mcontext
.sr
);
69 debug("gbr = %x", uc
->uc_mcontext
.gbr
);
70 debug("mach = %x", uc
->uc_mcontext
.mach
);
71 debug("macl = %x", uc
->uc_mcontext
.macl
);
74 static void crash(int sig
, siginfo_t attr_unused
*siginfo
, void *ucontext
)
76 dump_registers(sig
, ucontext
);
81 static const char * const cpu_feature_names
[] = {
88 cpu_feature_mask_t cpu_feature_flags
= 0;
89 static bool detection_failed
;
95 static uint32_t amask
= 0;
96 static void alpha_read_amask(void)
100 uint64_t (*fn
)(void);
103 str_add_hex(&c
, &cs
, "ffff1f20200ce0470180fa6b");
104 fn
= os_code_map(cast_ptr(uint8_t *, c
), cs
, NULL
);
106 os_code_unmap(fn
, cs
);
116 #ifdef HAVE_SYS_AUXV_H
117 #include <sys/auxv.h>
120 static uint32_t elf_hwcap
;
121 static unsigned arm_arch
;
123 static char *proc_get_field(const char *data
, const char *field
)
126 if (!strncmp(data
, field
, strlen(field
))) {
127 const char *colon
, *newline
;
128 colon
= strchr(data
, ':');
129 newline
= strchr(data
, '\n');
131 newline
= strchr(data
, 0);
132 if (!colon
|| colon
> newline
)
135 while (*colon
== ' ' || *colon
== '\t')
137 return str_dup(colon
, newline
- colon
, NULL
);
139 data
= strchr(data
, '\n');
146 static void arm_read_caps(void)
154 arm_arch
= ARM_VERSION
;
155 #if defined(HAVE_GETAUXVAL) && defined(AT_PLATFORM) && !defined(UNUSUAL)
156 c
= getauxval(AT_PLATFORM
);
158 const char *p
= (const char *)c
;
159 if (!strcmp(p
, "aarch64")) {
164 goto no_aux_platform
;
165 if (p
[1] < '1' || p
[1] > '9')
166 goto no_aux_platform
;
167 arm_arch
= atoi(&p
[1]);
172 if (!os_read_file("/proc/cpuinfo", &data
, &l
, &sink
))
173 array_init(char, &data
, &l
);
174 array_add(char, &data
, &l
, 0);
175 arch
= proc_get_field(data
, "CPU architecture");
179 char *proc
= proc_get_field(data
, "Processor");
180 if (!proc
) proc
= proc_get_field(data
, "model name");
182 if (strstr(proc
, "(v6l)"))
193 #ifdef ARM_VERSION_UNKNOWN
194 detection_failed
= true;
198 goto got_arch
; /* avoid warning */
200 #if defined(HAVE_GETAUXVAL) && defined(AT_HWCAP) && !defined(UNUSUAL)
201 c
= getauxval(AT_HWCAP
);
207 if (!os_read_file("/proc/self/auxv", &data
, &l
, &sink
))
208 array_init(char, &data
, &l
);
209 for (i
= 0; i
+ sizeof(unsigned long) * 2 > i
&& i
+ sizeof(unsigned long) * 2 <= l
; i
+= sizeof(unsigned long) * 2) {
210 unsigned long tag
= *(unsigned long *)(data
+ i
);
211 unsigned long value
= *(unsigned long *)(data
+ i
+ sizeof(unsigned long));
218 detection_failed
= true;
221 goto got_hwcap
; /* avoid warning */
224 /*debug("arm arch %u, caps %x", arm_arch, elf_hwcap);*/
232 static uint64_t cpuid_4
= 0;
233 static void ia64_read_cpuid(void)
235 void * volatile desc
[2];
239 str_add_hex(&c
, &cs
, "0a4000401704000000020000000004001d000000010000000002008008008400");
240 desc
[0] = os_code_map(cast_ptr(uint8_t *, c
), cs
, NULL
);
242 cpuid_4
= ((uint64_t (*)(uint64_t))desc
)(4);
243 os_code_unmap(desc
[0], cs
);
249 #ifdef ARCH_LOONGARCH64
251 static uint32_t cpucfg_1
= 0;
252 static void loongarch_read_cpucfg(void)
256 uint64_t (*fn
)(uint64_t);
258 str_add_hex(&c
, &cs
, "846c00002000004c");
259 fn
= os_code_map(cast_ptr(uint8_t *, c
), cs
, NULL
);
261 os_code_unmap(fn
, cs
);
271 static bool parisc_detect_20(void)
273 #if defined(__hpux) && defined(_SC_KERNEL_BITS)
274 return sysconf(_SC_KERNEL_BITS
) == 64;
278 return !strcasecmp(un
.machine
, "parisc64");
288 static void sigill(int attr_unused sig
, siginfo_t attr_unused
*siginfo
, void *ucontext
)
290 ucontext_t
*uc
= ucontext
;
291 uc
->uc_mcontext
.regs
->nip
+= 4;
292 uc
->uc_mcontext
.regs
->gpr
[3] = 0;
295 static bool trap_insn(const char *hex
)
299 size_t attr_unused i
;
302 if (unlikely(!OS_SUPPORTS_TRAPS
)) {
303 detection_failed
= true;
307 str_add_hex(&c
, &cs
, "38600001");
308 str_add_hex(&c
, &cs
, hex
);
309 str_add_hex(&c
, &cs
, "4e800020");
310 #if defined(C_LITTLE_ENDIAN)
311 for (i
= 0; i
< cs
; i
+= 4) {
313 t
= c
[i
]; c
[i
] = c
[i
+ 3]; c
[i
+ 3] = t
;
314 t
= c
[i
+ 1]; c
[i
+ 1] = c
[i
+ 2]; c
[i
+ 2] = t
;
317 fn
= os_code_map(cast_ptr(uint8_t *, c
), cs
, NULL
);
320 volatile uintptr_t desc
[3];
321 desc
[0] = ptr_to_num(fn
);
324 ret
= ((int (*)(void))desc
)();
329 os_code_unmap(fn
, cs
);
330 /*debug("trap: %d", ret);*/
340 static void sigill(int attr_unused sig
, siginfo_t attr_unused
*siginfo
, void *ucontext
)
342 ucontext_t
*uc
= ucontext
;
343 uc
->uc_mcontext
.__gregs
[REG_PC
] += 4;
344 uc
->uc_mcontext
.__gregs
[REG_A0
] = 0;
347 static bool trap_insn(const char *hex
)
353 if (unlikely(!OS_SUPPORTS_TRAPS
)) {
354 detection_failed
= true;
358 str_add_hex(&c
, &cs
, "0545");
359 str_add_hex(&c
, &cs
, hex
);
360 str_add_hex(&c
, &cs
, "8280");
361 fn
= os_code_map(cast_ptr(uint8_t *, c
), cs
, NULL
);
363 os_code_unmap(fn
, cs
);
364 /*debug("trap: %d", ret);*/
374 static uint64_t s390_facilities
[4];
376 static void s390_sigill(int attr_unused sig
, siginfo_t attr_unused
*siginfo
, void *ucontext
)
378 ucontext_t
*uc
= ucontext
;
379 uc
->uc_mcontext
.psw
.addr
+= 4;
380 detection_failed
= true;
383 static void s390_stfle(void)
387 void (*fn
)(uint64_t *);
388 memset(&s390_facilities
, 0, sizeof s390_facilities
);
389 if (unlikely(!OS_SUPPORTS_TRAPS
)) {
390 detection_failed
= true;
394 str_add_hex(&c
, &cs
, "a7090003b2b0200007fe");
395 fn
= os_code_map(cast_ptr(uint8_t *, c
), cs
, NULL
);
396 os_signal_trap(SIGILL
, s390_sigill
);
398 os_signal_untrap(SIGILL
);
399 os_code_unmap(fn
, cs
);
401 debug("facilities0: %016llx", (unsigned long long)s390_facilities
[0]);
402 debug("facilities1: %016llx", (unsigned long long)s390_facilities
[1]);
403 debug("facilities2: %016llx", (unsigned long long)s390_facilities
[2]);
404 debug("facilities3: %016llx", (unsigned long long)s390_facilities
[3]);
408 static bool test_facility(unsigned f
)
410 return (s390_facilities
[f
>> 6] >> (~f
& 63)) & 1;
423 static void riscv64_syscall(void)
426 memset(riscv_hwp
, 0, sizeof riscv_hwp
);
427 riscv_hwp
[0].key
= 4;
428 riscv_hwp
[1].key
= 5;
430 EINTR_LOOP(r
, syscall(258, riscv_hwp
, n_array_elements(riscv_hwp
), 0, NULL
, 0));
435 riscv_hwp
[0].key
= -1;
436 riscv_hwp
[1].key
= -1;
445 static bool sparc_detect_9(void)
449 return !strcasecmp(un
.machine
, "sparc64");
458 #if !defined(ARCH_X86_32) || defined(__i686__) || defined(__athlon__) || defined(__SSE__)
459 #define test_eflags_bits() do { } while (0)
460 #define eflags_bits ((1U << 18) | (1U << 21))
462 static uint32_t eflags_bits
= 0;
463 static void test_eflags_bits(void)
467 uint32_t (*fn
)(void);
470 str_add_hex(&c
, &cs
, "b8000024009c330424509d9c583304249dc3");
471 fn
= os_code_map(cast_ptr(uint8_t *, c
), cs
, NULL
);
472 os_block_signals(&set
);
474 os_unblock_signals(&set
);
475 os_code_unmap(fn
, cs
);
479 static uint32_t cpuid_0
[4];
480 static uint32_t cpuid_1
[4];
481 static uint32_t cpuid_7
[4];
482 static uint32_t cpuid_8_0
[4];
483 static uint32_t cpuid_8_1
[4];
485 static void do_cpuid(void)
489 void (*cpuid
)(uint32_t level
, uint32_t sublevel
, uint32_t *result
);
491 memset(cpuid_0
, 0, sizeof cpuid_0
);
492 memset(cpuid_1
, 0, sizeof cpuid_1
);
493 memset(cpuid_7
, 0, sizeof cpuid_7
);
494 memset(cpuid_8_0
, 0, sizeof cpuid_8_0
);
495 memset(cpuid_8_0
, 1, sizeof cpuid_8_1
);
496 if (unlikely(!(eflags_bits
& (1U << 21))))
500 #if defined(ARCH_X86_32)
501 str_add_hex(&c
, &cs
, "53568b44240c8b4c24100fa28b7424148906895e04894e0889560c5e5bc3");
502 #elif defined(ARCH_X86_64) && defined(ARCH_X86_WIN_ABI)
503 str_add_hex(&c
, &cs
, "5389c889d10fa241890041895804418948084189500c5bc3");
504 #elif defined(ARCH_X86_64)
505 str_add_hex(&c
, &cs
, "5389f889f14889d60fa28906895e04894e0889560c5bc3");
506 #elif defined(ARCH_X86_X32)
507 str_add_hex(&c
, &cs
, "5389f889f189d60fa28906895e04894e0889560c5bc3");
511 cpuid
= os_code_map(cast_ptr(uint8_t *, c
), cs
, NULL
);
513 cpuid(0, 0, cpuid_0
);
514 if (likely(cpuid_0
[0] >= 1))
515 cpuid(1, 0, cpuid_1
);
516 if (likely(cpuid_0
[0] >= 7))
517 cpuid(7, 0, cpuid_7
);
518 cpuid(0x80000000, 0, cpuid_8_0
);
519 if (likely((cpuid_8_0
[0] & 0xffff0000) == 0x80000000)) {
520 if (likely(cpuid_8_0
[0] >= 0x80000001))
521 cpuid(0x80000001, 0, cpuid_8_1
);
524 os_code_unmap(cpuid
, cs
);
527 static bool test_fxsave(void)
529 #if defined(ARCH_X86_32)
531 unsigned char space
[1024 + 15] = ""; /* avoid warning */
532 unsigned char *mem
= space
+ (-ptr_to_num(space
) & 15);
536 void (*fn
)(unsigned char *ptr
);
542 str_add_hex(&c
, &cs
, "8b4424040fae00fe80a00000000fae080fae8000020000c3");
543 fn
= os_code_map(cast_ptr(uint8_t *, c
), cs
, NULL
);
545 os_code_unmap(fn
, cs
);
546 supported
= mem
[160] == mem
[160 + 512];
553 static bool test_xcr0(unsigned mask
)
557 uint32_t (*fn
)(void);
560 str_add_hex(&c
, &cs
, "31c90f01d0c3");
561 fn
= os_code_map(cast_ptr(uint8_t *, c
), cs
, NULL
);
563 os_code_unmap(fn
, cs
);
564 return (res
& mask
) == mask
;
571 attr_noinline
void asm_setup_thread(void)
573 #if defined(INLINE_ASM_GCC_X86)
578 __asm__
volatile("fstcw %0" : "=m"(fpcw
));
579 __asm__
volatile("stmxcsr %0" : "=m"(mxcsr
));
580 debug("fpcw: %x, mxcsr: %x", fpcw
, mxcsr
);
584 unsigned short fpcw
= 0x37f;
585 __asm__
volatile("fldcw %0" : : "m"(fpcw
));
587 #if defined(HAVE_X86_ASSEMBLER_SSE)
588 if (likely(cpu_test_feature(CPU_FEATURE_sse
))) {
589 unsigned mxcsr
= 0x1f80;
590 __asm__
volatile("ldmxcsr %0" : : "m"(mxcsr
));
594 #if defined(INLINE_ASM_GCC_ARM)
595 __asm__
volatile (ARM_ASM_PREFIX
"vmsr fpscr, %0" : : "r"(0));
597 #if defined(INLINE_ASM_GCC_ARM64)
598 __asm__
volatile (ARM_ASM_PREFIX
"msr fpcr, %0" : : "r"(0UL));
603 static const struct {
606 cpu_feature_mask_t mask
;
607 } code_alttable
[] = {
608 #define EMIT_ALTTABLE(orig, alt, flags) { orig, alt, flags },
612 #define code_alttable_n (n_array_elements(code_alttable) - 1)
614 code_t
code_alt(code_t code
)
618 binary_search(size_t, code_alttable_n
, s
, false, code_alttable
[s
].orig
< code
, break);
620 for (; s
< code_alttable_n
+ uzero
&& code_alttable
[s
].orig
== code
; s
++) {
621 if ((cpu_feature_flags
& code_alttable
[s
].mask
) == code_alttable
[s
].mask
)
622 ret
= code_alttable
[s
].alt
;
624 /*if (code != ret) debug("code alt: %x -> %x", code, ret);*/
629 static attr_noinline
void verify_alttable(void)
632 /*debug("Alttable size: %x", (unsigned)code_alttable_n);*/
633 for (i
= 0; i
< (int)code_alttable_n
- 1; i
++) {
634 /*debug("Alttable: %x -> %x", code_alttable[i].orig, code_alttable[i].alt);*/
635 if (unlikely(code_alttable
[i
].orig
> code_alttable
[i
+ 1].orig
))
636 internal(file_line
, "verify_alttable: code_alttable is not sorted: 0x%x > 0x%x @ %d", (unsigned)code_alttable
[i
].orig
, (unsigned)code_alttable
[i
+ 1].orig
, i
);
637 /*code_alt(code_alttable[i].orig);*/
641 #define verify_alttable() do { } while (0)
647 bool trap_sigill
= false;
648 uint32_t missing_features
;
652 detection_failed
= false;
662 #ifdef ARCH_LOONGARCH64
663 loongarch_read_cpucfg();
673 if (riscv_hwp
[0].key
< 0)
681 #if defined(ARCH_POWER) || defined(ARCH_RISCV64)
682 os_signal_trap(SIGILL
, sigill
);
685 #define ASM_INC_DYNAMIC
687 #undef ASM_INC_DYNAMIC
689 #if defined(ARCH_POWER) || defined(ARCH_RISCV64)
690 os_signal_untrap(SIGILL
);
693 if (unlikely(detection_failed
))
694 cpu_feature_flags
|= cpu_feature_static_flags
;
695 missing_features
= cpu_feature_static_flags
& ~cpu_feature_flags
;
696 if (unlikely(missing_features
!= 0)) {
701 str_init(&error
, &error_l
);
702 str_add_string(&error
, &error_l
, "CPU doesn't have the following features: ");
703 for (first
= 1, f
= 0; missing_features
; missing_features
>>= 1, f
++) {
704 if (missing_features
& 1) {
706 str_add_string(&error
, &error_l
, ", ");
708 str_add_string(&error
, &error_l
, cpu_feature_names
[f
]);
711 str_finish(&error
, &error_l
);
716 char *str
= getenv("CPU_FLAGS");
719 cpu_feature_flags
= strtoul(str
, &e
, 16);
721 fatal("invalid CPU_FLAGS");
726 if (getenv("PRINT_FLAGS")) {
727 debug("static flags: %x", cpu_feature_static_flags
);
728 debug("dynamic flags: %x", cpu_feature_flags
);
733 #ifdef DEBUG_CRASH_HANDLER
734 os_signal_trap(SIGSEGV
, crash
);
735 os_signal_trap(SIGBUS
, crash
);
736 os_signal_trap(SIGILL
, crash
);
742 #ifdef DEBUG_CRASH_HANDLER
743 os_signal_untrap(SIGSEGV
);
744 os_signal_untrap(SIGBUS
);
745 os_signal_untrap(SIGILL
);