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