xref: /qemu/util/cpuinfo-riscv.c (revision 70ce076fa6dff60585c229a4b641b13e64bf03cf)
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
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