master
c 178 lines 5.49 KB
Raw
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
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. */
37 unsigned __attribute__((constructor)) cpuinfo_init(void)
38 {
39 unsigned left = CPUINFO_ZBA | CPUINFO_ZBB | CPUINFO_ZBS | CPUINFO_ZBKB
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 #if defined(__riscv_arch_test) && defined(__riscv_zbkb)
65 info |= CPUINFO_ZBKB;
66 #endif
67 left &= ~info;
68
69 #ifdef CONFIG_ASM_HWPROBE_H
70 if (left) {
71 /*
72 * TODO: glibc 2.40 will introduce <sys/hwprobe.h>, which
73 * provides __riscv_hwprobe and __riscv_hwprobe_one,
74 * which is a slightly cleaner interface.
75 */
76 struct riscv_hwprobe pair = { .key = RISCV_HWPROBE_KEY_IMA_EXT_0 };
77 if (syscall(__NR_riscv_hwprobe, &pair, 1, 0, NULL, 0) == 0
78 && pair.key >= 0) {
79 info |= pair.value & RISCV_HWPROBE_EXT_ZBA ? CPUINFO_ZBA : 0;
80 info |= pair.value & RISCV_HWPROBE_EXT_ZBB ? CPUINFO_ZBB : 0;
81 info |= pair.value & RISCV_HWPROBE_EXT_ZBS ? CPUINFO_ZBS : 0;
82 info |= pair.value & RISCV_HWPROBE_EXT_ZBKB ? CPUINFO_ZBKB : 0;
83 left &= ~(CPUINFO_ZBA | CPUINFO_ZBB | CPUINFO_ZBS | CPUINFO_ZBKB);
84 #ifdef RISCV_HWPROBE_EXT_ZICOND
85 info |= pair.value & RISCV_HWPROBE_EXT_ZICOND ? CPUINFO_ZICOND : 0;
86 left &= ~CPUINFO_ZICOND;
87 #endif
88 /* For rv64, V is Zve64d, a superset of Zve64x. */
89 info |= pair.value & RISCV_HWPROBE_IMA_V ? CPUINFO_ZVE64X : 0;
90 #ifdef RISCV_HWPROBE_EXT_ZVE64X
91 info |= pair.value & RISCV_HWPROBE_EXT_ZVE64X ? CPUINFO_ZVE64X : 0;
92 #endif
93 }
94 }
95 #endif /* CONFIG_ASM_HWPROBE_H */
96
97 /*
98 * We only detect support for vectors with hwprobe. All kernels with
99 * support for vectors in userspace also support the hwprobe syscall.
100 */
101 left &= ~CPUINFO_ZVE64X;
102
103 if (left) {
104 struct sigaction sa_old, sa_new;
105
106 memset(&sa_new, 0, sizeof(sa_new));
107 sa_new.sa_flags = SA_SIGINFO;
108 sa_new.sa_sigaction = sigill_handler;
109 sigaction(SIGILL, &sa_new, &sa_old);
110
111 if (left & CPUINFO_ZBA) {
112 /* Probe for Zba: add.uw zero,zero,zero. */
113 got_sigill = 0;
114 asm volatile(".insn r 0x3b, 0, 0x04, zero, zero, zero"
115 : : : "memory");
116 info |= got_sigill ? 0 : CPUINFO_ZBA;
117 left &= ~CPUINFO_ZBA;
118 }
119
120 if (left & CPUINFO_ZBB) {
121 /* Probe for Zbb: andn zero,zero,zero. */
122 got_sigill = 0;
123 asm volatile(".insn r 0x33, 7, 0x20, zero, zero, zero"
124 : : : "memory");
125 info |= got_sigill ? 0 : CPUINFO_ZBB;
126 left &= ~CPUINFO_ZBB;
127 }
128
129 if (left & CPUINFO_ZBS) {
130 /* Probe for Zbs: bext zero,zero,zero. */
131 got_sigill = 0;
132 asm volatile(".insn r 0x33, 5, 0x24, zero, zero, zero"
133 : : : "memory");
134 info |= got_sigill ? 0 : CPUINFO_ZBS;
135 left &= ~CPUINFO_ZBS;
136 }
137
138 if (left & CPUINFO_ZBKB) {
139 /* Probe for Zbkb: brev8 zero,zero. */
140 got_sigill = 0;
141 asm volatile(".insn i 0x13, 5, zero, zero, 0x687"
142 : : : "memory");
143 info |= got_sigill ? 0 : CPUINFO_ZBKB;
144 left &= ~CPUINFO_ZBKB;
145 }
146
147 if (left & CPUINFO_ZICOND) {
148 /* Probe for Zicond: czero.eqz zero,zero,zero. */
149 got_sigill = 0;
150 asm volatile(".insn r 0x33, 5, 0x07, zero, zero, zero"
151 : : : "memory");
152 info |= got_sigill ? 0 : CPUINFO_ZICOND;
153 left &= ~CPUINFO_ZICOND;
154 }
155
156 sigaction(SIGILL, &sa_old, NULL);
157 assert(left == 0);
158 }
159
160 if (info & CPUINFO_ZVE64X) {
161 /*
162 * We are guaranteed by RVV-1.0 that VLEN is a power of 2.
163 * We are guaranteed by Zve64x that VLEN >= 64, and that
164 * EEW of {8,16,32,64} are supported.
165 */
166 unsigned long vlenb;
167 /* csrr %0, vlenb */
168 asm volatile(".insn i 0x73, 0x2, %0, zero, -990" : "=r"(vlenb));
169 assert(vlenb >= 8);
170 assert(is_power_of_2(vlenb));
171 /* Cache VLEN in a convenient form. */
172 riscv_lg2_vlenb = ctz32(vlenb);
173 }
174
175 info |= CPUINFO_ALWAYS;
176 cpuinfo = info;
177 return info;
178 }