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
]);
59 debug("%s at %x", sig
== SIGSEGV
? "sigsegv" : sig
== SIGBUS
? "sigbus" : "sigill", uc
->uc_mcontext
.pc
);
60 for (i
= 0; i
< 16; i
++)
61 debug("gpr_%d = %x", i
, uc
->uc_mcontext
.gregs
[i
]);
62 debug("pr = %x", uc
->uc_mcontext
.pr
);
63 debug("sr = %x", uc
->uc_mcontext
.sr
);
64 debug("gbr = %x", uc
->uc_mcontext
.gbr
);
65 debug("mach = %x", uc
->uc_mcontext
.mach
);
66 debug("macl = %x", uc
->uc_mcontext
.macl
);
69 static void crash(int sig
, siginfo_t attr_unused
*siginfo
, void *ucontext
)
71 dump_registers(sig
, ucontext
);
76 static const char * const cpu_feature_names
[] = {
83 cpu_feature_mask_t cpu_feature_flags
= 0;
84 static bool detection_failed
;
90 static uint32_t amask
= 0;
91 static void alpha_read_amask(void)
98 str_add_hex(&c
, &cs
, "ffff1f20200ce0470180fa6b");
99 fn
= os_code_map(cast_ptr(uint8_t *, c
), cs
, NULL
);
101 os_code_unmap(fn
, cs
);
111 #ifdef HAVE_SYS_AUXV_H
112 #include <sys/auxv.h>
115 static uint32_t elf_hwcap
;
116 static unsigned arm_arch
;
118 static char *proc_get_field(const char *data
, const char *field
)
121 if (!strncmp(data
, field
, strlen(field
))) {
122 const char *colon
, *newline
;
123 colon
= strchr(data
, ':');
124 newline
= strchr(data
, '\n');
126 newline
= strchr(data
, 0);
127 if (!colon
|| colon
> newline
)
130 while (*colon
== ' ' || *colon
== '\t')
132 return str_dup(colon
, newline
- colon
, NULL
);
134 data
= strchr(data
, '\n');
141 static void arm_read_caps(void)
149 arm_arch
= ARM_VERSION
;
150 #if defined(HAVE_GETAUXVAL) && defined(AT_PLATFORM) && !defined(UNUSUAL)
151 c
= getauxval(AT_PLATFORM
);
153 const char *p
= (const char *)c
;
154 if (!strcmp(p
, "aarch64")) {
159 goto no_aux_platform
;
160 if (p
[1] < '1' || p
[1] > '9')
161 goto no_aux_platform
;
162 arm_arch
= atoi(&p
[1]);
167 if (!os_read_file("/proc/cpuinfo", &data
, &l
, &sink
))
168 array_init(char, &data
, &l
);
169 array_add(char, &data
, &l
, 0);
170 arch
= proc_get_field(data
, "CPU architecture");
174 char *proc
= proc_get_field(data
, "Processor");
175 if (!proc
) proc
= proc_get_field(data
, "model name");
177 if (strstr(proc
, "(v6l)"))
188 #ifdef ARM_VERSION_UNKNOWN
189 detection_failed
= true;
193 goto got_arch
; /* avoid warning */
195 #if defined(HAVE_GETAUXVAL) && defined(AT_HWCAP) && !defined(UNUSUAL)
196 c
= getauxval(AT_HWCAP
);
202 if (!os_read_file("/proc/self/auxv", &data
, &l
, &sink
))
203 array_init(char, &data
, &l
);
204 for (i
= 0; i
+ sizeof(unsigned long) * 2 > i
&& i
+ sizeof(unsigned long) * 2 <= l
; i
+= sizeof(unsigned long) * 2) {
205 unsigned long tag
= *(unsigned long *)(data
+ i
);
206 unsigned long value
= *(unsigned long *)(data
+ i
+ sizeof(unsigned long));
213 detection_failed
= true;
216 goto got_hwcap
; /* avoid warning */
219 /*debug("arm arch %u, caps %x", arm_arch, elf_hwcap);*/
227 static uint64_t cpuid_4
= 0;
228 static void ia64_read_cpuid(void)
230 void * volatile desc
[2];
234 str_add_hex(&c
, &cs
, "0a4000401704000000020000000004001d000000010000000002008008008400");
235 desc
[0] = os_code_map(cast_ptr(uint8_t *, c
), cs
, NULL
);
237 cpuid_4
= ((uint64_t (*)(uint64_t))desc
)(4);
238 os_code_unmap(desc
[0], cs
);
244 #ifdef ARCH_LOONGARCH64
246 static uint32_t cpucfg_1
= 0;
247 static void loongarch_read_cpucfg(void)
251 uint64_t (*fn
)(uint64_t);
253 str_add_hex(&c
, &cs
, "846c00002000004c");
254 fn
= os_code_map(cast_ptr(uint8_t *, c
), cs
, NULL
);
256 os_code_unmap(fn
, cs
);
266 static bool parisc_detect_20(void)
268 #if defined(__hpux) && defined(_SC_KERNEL_BITS)
269 return sysconf(_SC_KERNEL_BITS
) == 64;
273 return !strcasecmp(un
.machine
, "parisc64");
283 static void sigill(int attr_unused sig
, siginfo_t attr_unused
*siginfo
, void *ucontext
)
285 ucontext_t
*uc
= ucontext
;
286 uc
->uc_mcontext
.regs
->nip
+= 4;
287 uc
->uc_mcontext
.regs
->gpr
[3] = 0;
290 static bool trap_insn(const char *hex
)
294 size_t attr_unused i
;
297 if (unlikely(!OS_SUPPORTS_TRAPS
)) {
298 detection_failed
= true;
302 str_add_hex(&c
, &cs
, "38600001");
303 str_add_hex(&c
, &cs
, hex
);
304 str_add_hex(&c
, &cs
, "4e800020");
305 #if defined(C_LITTLE_ENDIAN)
306 for (i
= 0; i
< cs
; i
+= 4) {
308 t
= c
[i
]; c
[i
] = c
[i
+ 3]; c
[i
+ 3] = t
;
309 t
= c
[i
+ 1]; c
[i
+ 1] = c
[i
+ 2]; c
[i
+ 2] = t
;
312 fn
= os_code_map(cast_ptr(uint8_t *, c
), cs
, NULL
);
315 volatile uintptr_t desc
[3];
316 desc
[0] = ptr_to_num(fn
);
319 ret
= ((int (*)(void))desc
)();
324 os_code_unmap(fn
, cs
);
325 /*debug("trap: %d", ret);*/
335 static void sigill(int attr_unused sig
, siginfo_t attr_unused
*siginfo
, void *ucontext
)
337 ucontext_t
*uc
= ucontext
;
338 uc
->uc_mcontext
.__gregs
[REG_PC
] += 4;
339 uc
->uc_mcontext
.__gregs
[REG_A0
] = 0;
342 static bool trap_insn(const char *hex
)
348 if (unlikely(!OS_SUPPORTS_TRAPS
)) {
349 detection_failed
= true;
353 str_add_hex(&c
, &cs
, "0545");
354 str_add_hex(&c
, &cs
, hex
);
355 str_add_hex(&c
, &cs
, "8280");
356 fn
= os_code_map(cast_ptr(uint8_t *, c
), cs
, NULL
);
358 os_code_unmap(fn
, cs
);
359 /*debug("trap: %d", ret);*/
369 static uint64_t s390_facilities
[4];
371 static void s390_sigill(int attr_unused sig
, siginfo_t attr_unused
*siginfo
, void *ucontext
)
373 ucontext_t
*uc
= ucontext
;
374 uc
->uc_mcontext
.psw
.addr
+= 4;
375 detection_failed
= true;
378 static void s390_stfle(void)
382 void (*fn
)(uint64_t *);
383 memset(&s390_facilities
, 0, sizeof s390_facilities
);
384 if (unlikely(!OS_SUPPORTS_TRAPS
)) {
385 detection_failed
= true;
389 str_add_hex(&c
, &cs
, "a7090003b2b0200007fe");
390 fn
= os_code_map(cast_ptr(uint8_t *, c
), cs
, NULL
);
391 os_signal_trap(SIGILL
, s390_sigill
);
393 os_signal_untrap(SIGILL
);
394 os_code_unmap(fn
, cs
);
396 debug("facilities0: %016llx", (unsigned long long)s390_facilities
[0]);
397 debug("facilities1: %016llx", (unsigned long long)s390_facilities
[1]);
398 debug("facilities2: %016llx", (unsigned long long)s390_facilities
[2]);
399 debug("facilities3: %016llx", (unsigned long long)s390_facilities
[3]);
403 static bool test_facility(unsigned f
)
405 return (s390_facilities
[f
>> 6] >> (~f
& 63)) & 1;
418 static void riscv64_syscall(void)
421 memset(riscv_hwp
, 0, sizeof riscv_hwp
);
422 riscv_hwp
[0].key
= 4;
423 riscv_hwp
[1].key
= 5;
425 EINTR_LOOP(r
, syscall(258, riscv_hwp
, n_array_elements(riscv_hwp
), 0, NULL
, 0));
430 riscv_hwp
[0].key
= -1;
431 riscv_hwp
[1].key
= -1;
440 static bool sparc_detect_9(void)
444 return !strcasecmp(un
.machine
, "sparc64");
453 #if !defined(ARCH_X86_32) || defined(__i686__) || defined(__athlon__) || defined(__SSE__)
454 #define test_eflags_bits() do { } while (0)
455 #define eflags_bits ((1U << 18) | (1U << 21))
457 static uint32_t eflags_bits
= 0;
458 static void test_eflags_bits(void)
462 uint32_t (*fn
)(void);
465 str_add_hex(&c
, &cs
, "b8000024009c330424509d9c583304249dc3");
466 fn
= os_code_map(cast_ptr(uint8_t *, c
), cs
, NULL
);
467 os_block_signals(&set
);
469 os_unblock_signals(&set
);
470 os_code_unmap(fn
, cs
);
474 static uint32_t cpuid_0
[4];
475 static uint32_t cpuid_1
[4];
476 static uint32_t cpuid_7
[4];
477 static uint32_t cpuid_8_0
[4];
478 static uint32_t cpuid_8_1
[4];
480 static void do_cpuid(void)
484 void (*cpuid
)(uint32_t level
, uint32_t sublevel
, uint32_t *result
);
486 memset(cpuid_0
, 0, sizeof cpuid_0
);
487 memset(cpuid_1
, 0, sizeof cpuid_1
);
488 memset(cpuid_7
, 0, sizeof cpuid_7
);
489 memset(cpuid_8_0
, 0, sizeof cpuid_8_0
);
490 memset(cpuid_8_0
, 1, sizeof cpuid_8_1
);
491 if (unlikely(!(eflags_bits
& (1U << 21))))
495 #if defined(ARCH_X86_32)
496 str_add_hex(&c
, &cs
, "53568b44240c8b4c24100fa28b7424148906895e04894e0889560c5e5bc3");
497 #elif defined(ARCH_X86_64) && defined(ARCH_X86_WIN_ABI)
498 str_add_hex(&c
, &cs
, "5389c889d10fa241890041895804418948084189500c5bc3");
499 #elif defined(ARCH_X86_64)
500 str_add_hex(&c
, &cs
, "5389f889f14889d60fa28906895e04894e0889560c5bc3");
501 #elif defined(ARCH_X86_X32)
502 str_add_hex(&c
, &cs
, "5389f889f189d60fa28906895e04894e0889560c5bc3");
506 cpuid
= os_code_map(cast_ptr(uint8_t *, c
), cs
, NULL
);
508 cpuid(0, 0, cpuid_0
);
509 if (likely(cpuid_0
[0] >= 1))
510 cpuid(1, 0, cpuid_1
);
511 if (likely(cpuid_0
[0] >= 7))
512 cpuid(7, 0, cpuid_7
);
513 cpuid(0x80000000, 0, cpuid_8_0
);
514 if (likely((cpuid_8_0
[0] & 0xffff0000) == 0x80000000)) {
515 if (likely(cpuid_8_0
[0] >= 0x80000001))
516 cpuid(0x80000001, 0, cpuid_8_1
);
519 os_code_unmap(cpuid
, cs
);
522 static bool test_fxsave(void)
524 #if defined(ARCH_X86_32)
526 unsigned char space
[1024 + 15] = ""; /* avoid warning */
527 unsigned char *mem
= space
+ (-ptr_to_num(space
) & 15);
531 void (*fn
)(unsigned char *ptr
);
537 str_add_hex(&c
, &cs
, "8b4424040fae00fe80a00000000fae080fae8000020000c3");
538 fn
= os_code_map(cast_ptr(uint8_t *, c
), cs
, NULL
);
540 os_code_unmap(fn
, cs
);
541 supported
= mem
[160] == mem
[160 + 512];
548 static bool test_xcr0(unsigned mask
)
552 uint32_t (*fn
)(void);
555 str_add_hex(&c
, &cs
, "31c90f01d0c3");
556 fn
= os_code_map(cast_ptr(uint8_t *, c
), cs
, NULL
);
558 os_code_unmap(fn
, cs
);
559 return (res
& mask
) == mask
;
566 attr_noinline
void asm_setup_thread(void)
568 #if defined(INLINE_ASM_GCC_X86)
573 __asm__
volatile("fstcw %0" : "=m"(fpcw
));
574 __asm__
volatile("stmxcsr %0" : "=m"(mxcsr
));
575 debug("fpcw: %x, mxcsr: %x", fpcw
, mxcsr
);
579 unsigned short fpcw
= 0x37f;
580 __asm__
volatile("fldcw %0" : : "m"(fpcw
));
582 #if defined(HAVE_X86_ASSEMBLER_SSE)
583 if (likely(cpu_test_feature(CPU_FEATURE_sse
))) {
584 unsigned mxcsr
= 0x1f80;
585 __asm__
volatile("ldmxcsr %0" : : "m"(mxcsr
));
589 #if defined(INLINE_ASM_GCC_ARM)
590 __asm__
volatile (ARM_ASM_PREFIX
"vmsr fpscr, %0" : : "r"(0));
592 #if defined(INLINE_ASM_GCC_ARM64)
593 __asm__
volatile (ARM_ASM_PREFIX
"msr fpcr, %0" : : "r"(0UL));
598 static const struct {
601 cpu_feature_mask_t mask
;
602 } code_alttable
[] = {
603 #define EMIT_ALTTABLE(orig, alt, flags) { orig, alt, flags },
607 #define code_alttable_n (n_array_elements(code_alttable) - 1)
609 code_t
code_alt(code_t code
)
613 binary_search(size_t, code_alttable_n
, s
, false, code_alttable
[s
].orig
< code
, break);
615 for (; s
< code_alttable_n
+ uzero
&& code_alttable
[s
].orig
== code
; s
++) {
616 if ((cpu_feature_flags
& code_alttable
[s
].mask
) == code_alttable
[s
].mask
)
617 ret
= code_alttable
[s
].alt
;
619 /*if (code != ret) debug("code alt: %x -> %x", code, ret);*/
624 static attr_noinline
void verify_alttable(void)
627 /*debug("Alttable size: %x", (unsigned)code_alttable_n);*/
628 for (i
= 0; i
< (int)code_alttable_n
- 1; i
++) {
629 /*debug("Alttable: %x -> %x", code_alttable[i].orig, code_alttable[i].alt);*/
630 if (unlikely(code_alttable
[i
].orig
> code_alttable
[i
+ 1].orig
))
631 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
);
632 /*code_alt(code_alttable[i].orig);*/
636 #define verify_alttable() do { } while (0)
642 bool trap_sigill
= false;
643 uint32_t missing_features
;
647 detection_failed
= false;
657 #ifdef ARCH_LOONGARCH64
658 loongarch_read_cpucfg();
668 if (riscv_hwp
[0].key
< 0)
676 #if defined(ARCH_POWER) || defined(ARCH_RISCV64)
677 os_signal_trap(SIGILL
, sigill
);
680 #define ASM_INC_DYNAMIC
682 #undef ASM_INC_DYNAMIC
684 #if defined(ARCH_POWER) || defined(ARCH_RISCV64)
685 os_signal_untrap(SIGILL
);
688 if (unlikely(detection_failed
))
689 cpu_feature_flags
|= cpu_feature_static_flags
;
690 missing_features
= cpu_feature_static_flags
& ~cpu_feature_flags
;
691 if (unlikely(missing_features
!= 0)) {
696 str_init(&error
, &error_l
);
697 str_add_string(&error
, &error_l
, "CPU doesn't have the following features: ");
698 for (first
= 1, f
= 0; missing_features
; missing_features
>>= 1, f
++) {
699 if (missing_features
& 1) {
701 str_add_string(&error
, &error_l
, ", ");
703 str_add_string(&error
, &error_l
, cpu_feature_names
[f
]);
706 str_finish(&error
, &error_l
);
711 char *str
= getenv("CPU_FLAGS");
714 cpu_feature_flags
= strtoul(str
, &e
, 16);
716 fatal("invalid CPU_FLAGS");
721 if (getenv("PRINT_FLAGS")) {
722 debug("static flags: %x", cpu_feature_static_flags
);
723 debug("dynamic flags: %x", cpu_feature_flags
);
728 #ifdef DEBUG_CRASH_HANDLER
729 os_signal_trap(SIGSEGV
, crash
);
730 os_signal_trap(SIGBUS
, crash
);
731 os_signal_trap(SIGILL
, crash
);
737 #ifdef DEBUG_CRASH_HANDLER
738 os_signal_untrap(SIGSEGV
);
739 os_signal_untrap(SIGBUS
);
740 os_signal_untrap(SIGILL
);