xref: /openbmc/qemu/util/cpuinfo-riscv.c (revision 05caa062)
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 "host/cpuinfo.h"
8 
9 #ifdef CONFIG_ASM_HWPROBE_H
10 #include <asm/hwprobe.h>
11 #include <sys/syscall.h>
12 #endif
13 
14 unsigned cpuinfo;
15 static volatile sig_atomic_t got_sigill;
16 
17 static void sigill_handler(int signo, siginfo_t *si, void *data)
18 {
19     /* Skip the faulty instruction */
20     ucontext_t *uc = (ucontext_t *)data;
21 
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
29 
30     got_sigill = 1;
31 }
32 
33 /* Called both as constructor and (possibly) via other constructors. */
34 unsigned __attribute__((constructor)) cpuinfo_init(void)
35 {
36     unsigned left = CPUINFO_ZBA | CPUINFO_ZBB | CPUINFO_ZICOND;
37     unsigned info = cpuinfo;
38 
39     if (info) {
40         return info;
41     }
42 
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;
54 
55 #ifdef CONFIG_ASM_HWPROBE_H
56     if (left) {
57         /*
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.
61          */
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
72         }
73     }
74 #endif /* CONFIG_ASM_HWPROBE_H */
75 
76     if (left) {
77         struct sigaction sa_old, sa_new;
78 
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);
83 
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;
91         }
92 
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;
100         }
101 
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;
109         }
110 
111         sigaction(SIGILL, &sa_old, NULL);
112         assert(left == 0);
113     }
114 
115     info |= CPUINFO_ALWAYS;
116     cpuinfo = info;
117     return info;
118 }
119