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