1 /*
2 * SPDX-License-Identifier: GPL-2.0-or-later
3 * Host specific cpu identification for RISC-V.
4 */
5
6 #include "qemu/osdep.h"
7 #include "qemu/host-utils.h"
8 #include "host/cpuinfo.h"
9
10 #ifdef CONFIG_ASM_HWPROBE_H
11 #include <asm/hwprobe.h>
12 #include <sys/syscall.h>
13 #include <asm/unistd.h>
14 #endif
15
16 unsigned cpuinfo;
17 unsigned riscv_lg2_vlenb;
18 static volatile sig_atomic_t got_sigill;
19
sigill_handler(int signo,siginfo_t * si,void * data)20 static void sigill_handler(int signo, siginfo_t *si, void *data)
21 {
22 /* Skip the faulty instruction */
23 ucontext_t *uc = (ucontext_t *)data;
24
25 #ifdef __linux__
26 uc->uc_mcontext.__gregs[REG_PC] += 4;
27 #elif defined(__OpenBSD__)
28 uc->sc_sepc += 4;
29 #else
30 # error Unsupported OS
31 #endif
32
33 got_sigill = 1;
34 }
35
36 /* Called both as constructor and (possibly) via other constructors. */
cpuinfo_init(void)37 unsigned __attribute__((constructor)) cpuinfo_init(void)
38 {
39 unsigned left = CPUINFO_ZBA | CPUINFO_ZBB | CPUINFO_ZBS
40 | CPUINFO_ZICOND | CPUINFO_ZVE64X;
41 unsigned info = cpuinfo;
42
43 if (info) {
44 return info;
45 }
46
47 /* Test for compile-time settings. */
48 #if defined(__riscv_arch_test) && defined(__riscv_zba)
49 info |= CPUINFO_ZBA;
50 #endif
51 #if defined(__riscv_arch_test) && defined(__riscv_zbb)
52 info |= CPUINFO_ZBB;
53 #endif
54 #if defined(__riscv_arch_test) && defined(__riscv_zbs)
55 info |= CPUINFO_ZBS;
56 #endif
57 #if defined(__riscv_arch_test) && defined(__riscv_zicond)
58 info |= CPUINFO_ZICOND;
59 #endif
60 #if defined(__riscv_arch_test) && \
61 (defined(__riscv_vector) || defined(__riscv_zve64x))
62 info |= CPUINFO_ZVE64X;
63 #endif
64 left &= ~info;
65
66 #ifdef CONFIG_ASM_HWPROBE_H
67 if (left) {
68 /*
69 * TODO: glibc 2.40 will introduce <sys/hwprobe.h>, which
70 * provides __riscv_hwprobe and __riscv_hwprobe_one,
71 * which is a slightly cleaner interface.
72 */
73 struct riscv_hwprobe pair = { .key = RISCV_HWPROBE_KEY_IMA_EXT_0 };
74 if (syscall(__NR_riscv_hwprobe, &pair, 1, 0, NULL, 0) == 0
75 && pair.key >= 0) {
76 info |= pair.value & RISCV_HWPROBE_EXT_ZBA ? CPUINFO_ZBA : 0;
77 info |= pair.value & RISCV_HWPROBE_EXT_ZBB ? CPUINFO_ZBB : 0;
78 info |= pair.value & RISCV_HWPROBE_EXT_ZBS ? CPUINFO_ZBS : 0;
79 left &= ~(CPUINFO_ZBA | CPUINFO_ZBB | CPUINFO_ZBS);
80 #ifdef RISCV_HWPROBE_EXT_ZICOND
81 info |= pair.value & RISCV_HWPROBE_EXT_ZICOND ? CPUINFO_ZICOND : 0;
82 left &= ~CPUINFO_ZICOND;
83 #endif
84 /* For rv64, V is Zve64d, a superset of Zve64x. */
85 info |= pair.value & RISCV_HWPROBE_IMA_V ? CPUINFO_ZVE64X : 0;
86 #ifdef RISCV_HWPROBE_EXT_ZVE64X
87 info |= pair.value & RISCV_HWPROBE_EXT_ZVE64X ? CPUINFO_ZVE64X : 0;
88 #endif
89 }
90 }
91 #endif /* CONFIG_ASM_HWPROBE_H */
92
93 /*
94 * We only detect support for vectors with hwprobe. All kernels with
95 * support for vectors in userspace also support the hwprobe syscall.
96 */
97 left &= ~CPUINFO_ZVE64X;
98
99 if (left) {
100 struct sigaction sa_old, sa_new;
101
102 memset(&sa_new, 0, sizeof(sa_new));
103 sa_new.sa_flags = SA_SIGINFO;
104 sa_new.sa_sigaction = sigill_handler;
105 sigaction(SIGILL, &sa_new, &sa_old);
106
107 if (left & CPUINFO_ZBA) {
108 /* Probe for Zba: add.uw zero,zero,zero. */
109 got_sigill = 0;
110 asm volatile(".insn r 0x3b, 0, 0x04, zero, zero, zero"
111 : : : "memory");
112 info |= got_sigill ? 0 : CPUINFO_ZBA;
113 left &= ~CPUINFO_ZBA;
114 }
115
116 if (left & CPUINFO_ZBB) {
117 /* Probe for Zbb: andn zero,zero,zero. */
118 got_sigill = 0;
119 asm volatile(".insn r 0x33, 7, 0x20, zero, zero, zero"
120 : : : "memory");
121 info |= got_sigill ? 0 : CPUINFO_ZBB;
122 left &= ~CPUINFO_ZBB;
123 }
124
125 if (left & CPUINFO_ZBS) {
126 /* Probe for Zbs: bext zero,zero,zero. */
127 got_sigill = 0;
128 asm volatile(".insn r 0x33, 5, 0x24, zero, zero, zero"
129 : : : "memory");
130 info |= got_sigill ? 0 : CPUINFO_ZBS;
131 left &= ~CPUINFO_ZBS;
132 }
133
134 if (left & CPUINFO_ZICOND) {
135 /* Probe for Zicond: czero.eqz zero,zero,zero. */
136 got_sigill = 0;
137 asm volatile(".insn r 0x33, 5, 0x07, zero, zero, zero"
138 : : : "memory");
139 info |= got_sigill ? 0 : CPUINFO_ZICOND;
140 left &= ~CPUINFO_ZICOND;
141 }
142
143 sigaction(SIGILL, &sa_old, NULL);
144 assert(left == 0);
145 }
146
147 if (info & CPUINFO_ZVE64X) {
148 /*
149 * We are guaranteed by RVV-1.0 that VLEN is a power of 2.
150 * We are guaranteed by Zve64x that VLEN >= 64, and that
151 * EEW of {8,16,32,64} are supported.
152 */
153 unsigned long vlenb;
154 /* csrr %0, vlenb */
155 asm volatile(".insn i 0x73, 0x2, %0, zero, -990" : "=r"(vlenb));
156 assert(vlenb >= 8);
157 assert(is_power_of_2(vlenb));
158 /* Cache VLEN in a convenient form. */
159 riscv_lg2_vlenb = ctz32(vlenb);
160 }
161
162 info |= CPUINFO_ALWAYS;
163 cpuinfo = info;
164 return info;
165 }
166