| 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 | } |