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