Merge remote-tracking branch 'qemu-project/master'
[qemu/ar7.git] / util / cpuinfo-riscv.c
blob497ce126800c09a7c555851194e5537cf2018e8e
1 /*
2 * SPDX-License-Identifier: GPL-2.0-or-later
3 * Host specific cpu identification for RISC-V.
4 */
6 #include "qemu/osdep.h"
7 #include "host/cpuinfo.h"
9 #ifdef CONFIG_ASM_HWPROBE_H
10 #include <asm/hwprobe.h>
11 #include <sys/syscall.h>
12 #endif
14 unsigned cpuinfo;
15 static volatile sig_atomic_t got_sigill;
17 static void sigill_handler(int signo, siginfo_t *si, void *data)
19 /* Skip the faulty instruction */
20 ucontext_t *uc = (ucontext_t *)data;
22 #ifdef __linux__
23 uc->uc_mcontext.__gregs[REG_PC] += 4;
24 #elif defined(__OpenBSD__)
25 uc->sc_sepc += 4;
26 #else
27 # error Unsupported OS
28 #endif
30 got_sigill = 1;
33 /* Called both as constructor and (possibly) via other constructors. */
34 unsigned __attribute__((constructor)) cpuinfo_init(void)
36 unsigned left = CPUINFO_ZBA | CPUINFO_ZBB | CPUINFO_ZICOND;
37 unsigned info = cpuinfo;
39 if (info) {
40 return info;
43 /* Test for compile-time settings. */
44 #if defined(__riscv_arch_test) && defined(__riscv_zba)
45 info |= CPUINFO_ZBA;
46 #endif
47 #if defined(__riscv_arch_test) && defined(__riscv_zbb)
48 info |= CPUINFO_ZBB;
49 #endif
50 #if defined(__riscv_arch_test) && defined(__riscv_zicond)
51 info |= CPUINFO_ZICOND;
52 #endif
53 left &= ~info;
55 #ifdef CONFIG_ASM_HWPROBE_H
56 if (left) {
58 * TODO: glibc 2.40 will introduce <sys/hwprobe.h>, which
59 * provides __riscv_hwprobe and __riscv_hwprobe_one,
60 * which is a slightly cleaner interface.
62 struct riscv_hwprobe pair = { .key = RISCV_HWPROBE_KEY_IMA_EXT_0 };
63 if (syscall(__NR_riscv_hwprobe, &pair, 1, 0, NULL, 0) == 0
64 && pair.key >= 0) {
65 info |= pair.value & RISCV_HWPROBE_EXT_ZBA ? CPUINFO_ZBA : 0;
66 info |= pair.value & RISCV_HWPROBE_EXT_ZBB ? CPUINFO_ZBB : 0;
67 left &= ~(CPUINFO_ZBA | CPUINFO_ZBB);
68 #ifdef RISCV_HWPROBE_EXT_ZICOND
69 info |= pair.value & RISCV_HWPROBE_EXT_ZICOND ? CPUINFO_ZICOND : 0;
70 left &= ~CPUINFO_ZICOND;
71 #endif
74 #endif /* CONFIG_ASM_HWPROBE_H */
76 if (left) {
77 struct sigaction sa_old, sa_new;
79 memset(&sa_new, 0, sizeof(sa_new));
80 sa_new.sa_flags = SA_SIGINFO;
81 sa_new.sa_sigaction = sigill_handler;
82 sigaction(SIGILL, &sa_new, &sa_old);
84 if (left & CPUINFO_ZBA) {
85 /* Probe for Zba: add.uw zero,zero,zero. */
86 got_sigill = 0;
87 asm volatile(".insn r 0x3b, 0, 0x04, zero, zero, zero"
88 : : : "memory");
89 info |= got_sigill ? 0 : CPUINFO_ZBA;
90 left &= ~CPUINFO_ZBA;
93 if (left & CPUINFO_ZBB) {
94 /* Probe for Zbb: andn zero,zero,zero. */
95 got_sigill = 0;
96 asm volatile(".insn r 0x33, 7, 0x20, zero, zero, zero"
97 : : : "memory");
98 info |= got_sigill ? 0 : CPUINFO_ZBB;
99 left &= ~CPUINFO_ZBB;
102 if (left & CPUINFO_ZICOND) {
103 /* Probe for Zicond: czero.eqz zero,zero,zero. */
104 got_sigill = 0;
105 asm volatile(".insn r 0x33, 5, 0x07, zero, zero, zero"
106 : : : "memory");
107 info |= got_sigill ? 0 : CPUINFO_ZICOND;
108 left &= ~CPUINFO_ZICOND;
111 sigaction(SIGILL, &sa_old, NULL);
112 assert(left == 0);
115 info |= CPUINFO_ALWAYS;
116 cpuinfo = info;
117 return info;