xref: /openbmc/qemu/util/cpuinfo-riscv.c (revision 2e1cacfb)
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 #include <asm/unistd.h>
13 #endif
14 
15 unsigned cpuinfo;
16 static volatile sig_atomic_t got_sigill;
17 
18 static void sigill_handler(int signo, siginfo_t *si, void *data)
19 {
20     /* Skip the faulty instruction */
21     ucontext_t *uc = (ucontext_t *)data;
22 
23 #ifdef __linux__
24     uc->uc_mcontext.__gregs[REG_PC] += 4;
25 #elif defined(__OpenBSD__)
26     uc->sc_sepc += 4;
27 #else
28 # error Unsupported OS
29 #endif
30 
31     got_sigill = 1;
32 }
33 
34 /* Called both as constructor and (possibly) via other constructors. */
35 unsigned __attribute__((constructor)) cpuinfo_init(void)
36 {
37     unsigned left = CPUINFO_ZBA | CPUINFO_ZBB | CPUINFO_ZICOND;
38     unsigned info = cpuinfo;
39 
40     if (info) {
41         return info;
42     }
43 
44     /* Test for compile-time settings. */
45 #if defined(__riscv_arch_test) && defined(__riscv_zba)
46     info |= CPUINFO_ZBA;
47 #endif
48 #if defined(__riscv_arch_test) && defined(__riscv_zbb)
49     info |= CPUINFO_ZBB;
50 #endif
51 #if defined(__riscv_arch_test) && defined(__riscv_zicond)
52     info |= CPUINFO_ZICOND;
53 #endif
54     left &= ~info;
55 
56 #ifdef CONFIG_ASM_HWPROBE_H
57     if (left) {
58         /*
59          * TODO: glibc 2.40 will introduce <sys/hwprobe.h>, which
60          * provides __riscv_hwprobe and __riscv_hwprobe_one,
61          * which is a slightly cleaner interface.
62          */
63         struct riscv_hwprobe pair = { .key = RISCV_HWPROBE_KEY_IMA_EXT_0 };
64         if (syscall(__NR_riscv_hwprobe, &pair, 1, 0, NULL, 0) == 0
65             && pair.key >= 0) {
66             info |= pair.value & RISCV_HWPROBE_EXT_ZBA ? CPUINFO_ZBA : 0;
67             info |= pair.value & RISCV_HWPROBE_EXT_ZBB ? CPUINFO_ZBB : 0;
68             left &= ~(CPUINFO_ZBA | CPUINFO_ZBB);
69 #ifdef RISCV_HWPROBE_EXT_ZICOND
70             info |= pair.value & RISCV_HWPROBE_EXT_ZICOND ? CPUINFO_ZICOND : 0;
71             left &= ~CPUINFO_ZICOND;
72 #endif
73         }
74     }
75 #endif /* CONFIG_ASM_HWPROBE_H */
76 
77     if (left) {
78         struct sigaction sa_old, sa_new;
79 
80         memset(&sa_new, 0, sizeof(sa_new));
81         sa_new.sa_flags = SA_SIGINFO;
82         sa_new.sa_sigaction = sigill_handler;
83         sigaction(SIGILL, &sa_new, &sa_old);
84 
85         if (left & CPUINFO_ZBA) {
86             /* Probe for Zba: add.uw zero,zero,zero. */
87             got_sigill = 0;
88             asm volatile(".insn r 0x3b, 0, 0x04, zero, zero, zero"
89                          : : : "memory");
90             info |= got_sigill ? 0 : CPUINFO_ZBA;
91             left &= ~CPUINFO_ZBA;
92         }
93 
94         if (left & CPUINFO_ZBB) {
95             /* Probe for Zbb: andn zero,zero,zero. */
96             got_sigill = 0;
97             asm volatile(".insn r 0x33, 7, 0x20, zero, zero, zero"
98                          : : : "memory");
99             info |= got_sigill ? 0 : CPUINFO_ZBB;
100             left &= ~CPUINFO_ZBB;
101         }
102 
103         if (left & CPUINFO_ZICOND) {
104             /* Probe for Zicond: czero.eqz zero,zero,zero. */
105             got_sigill = 0;
106             asm volatile(".insn r 0x33, 5, 0x07, zero, zero, zero"
107                          : : : "memory");
108             info |= got_sigill ? 0 : CPUINFO_ZICOND;
109             left &= ~CPUINFO_ZICOND;
110         }
111 
112         sigaction(SIGILL, &sa_old, NULL);
113         assert(left == 0);
114     }
115 
116     info |= CPUINFO_ALWAYS;
117     cpuinfo = info;
118     return info;
119 }
120