/* * SPDX-License-Identifier: GPL-2.0-or-later * Host specific cpu identification for RISC-V. */ #include "qemu/osdep.h" #include "qemu/host-utils.h" #include "host/cpuinfo.h" #ifdef CONFIG_ASM_HWPROBE_H #include #include #include #endif unsigned cpuinfo; unsigned riscv_lg2_vlenb; static volatile sig_atomic_t got_sigill; static void sigill_handler(int signo, siginfo_t *si, void *data) { /* Skip the faulty instruction */ ucontext_t *uc = (ucontext_t *)data; #ifdef __linux__ uc->uc_mcontext.__gregs[REG_PC] += 4; #elif defined(__OpenBSD__) uc->sc_sepc += 4; #else # error Unsupported OS #endif got_sigill = 1; } /* Called both as constructor and (possibly) via other constructors. */ unsigned __attribute__((constructor)) cpuinfo_init(void) { unsigned left = CPUINFO_ZBA | CPUINFO_ZBB | CPUINFO_ZICOND | CPUINFO_ZVE64X; unsigned info = cpuinfo; if (info) { return info; } /* Test for compile-time settings. */ #if defined(__riscv_arch_test) && defined(__riscv_zba) info |= CPUINFO_ZBA; #endif #if defined(__riscv_arch_test) && defined(__riscv_zbb) info |= CPUINFO_ZBB; #endif #if defined(__riscv_arch_test) && defined(__riscv_zicond) info |= CPUINFO_ZICOND; #endif #if defined(__riscv_arch_test) && \ (defined(__riscv_vector) || defined(__riscv_zve64x)) info |= CPUINFO_ZVE64X; #endif left &= ~info; #ifdef CONFIG_ASM_HWPROBE_H if (left) { /* * TODO: glibc 2.40 will introduce , which * provides __riscv_hwprobe and __riscv_hwprobe_one, * which is a slightly cleaner interface. */ struct riscv_hwprobe pair = { .key = RISCV_HWPROBE_KEY_IMA_EXT_0 }; if (syscall(__NR_riscv_hwprobe, &pair, 1, 0, NULL, 0) == 0 && pair.key >= 0) { info |= pair.value & RISCV_HWPROBE_EXT_ZBA ? CPUINFO_ZBA : 0; info |= pair.value & RISCV_HWPROBE_EXT_ZBB ? CPUINFO_ZBB : 0; left &= ~(CPUINFO_ZBA | CPUINFO_ZBB); #ifdef RISCV_HWPROBE_EXT_ZICOND info |= pair.value & RISCV_HWPROBE_EXT_ZICOND ? CPUINFO_ZICOND : 0; left &= ~CPUINFO_ZICOND; #endif /* For rv64, V is Zve64d, a superset of Zve64x. */ info |= pair.value & RISCV_HWPROBE_IMA_V ? CPUINFO_ZVE64X : 0; #ifdef RISCV_HWPROBE_EXT_ZVE64X info |= pair.value & RISCV_HWPROBE_EXT_ZVE64X ? CPUINFO_ZVE64X : 0; #endif } } #endif /* CONFIG_ASM_HWPROBE_H */ /* * We only detect support for vectors with hwprobe. All kernels with * support for vectors in userspace also support the hwprobe syscall. */ left &= ~CPUINFO_ZVE64X; if (left) { struct sigaction sa_old, sa_new; memset(&sa_new, 0, sizeof(sa_new)); sa_new.sa_flags = SA_SIGINFO; sa_new.sa_sigaction = sigill_handler; sigaction(SIGILL, &sa_new, &sa_old); if (left & CPUINFO_ZBA) { /* Probe for Zba: add.uw zero,zero,zero. */ got_sigill = 0; asm volatile(".insn r 0x3b, 0, 0x04, zero, zero, zero" : : : "memory"); info |= got_sigill ? 0 : CPUINFO_ZBA; left &= ~CPUINFO_ZBA; } if (left & CPUINFO_ZBB) { /* Probe for Zbb: andn zero,zero,zero. */ got_sigill = 0; asm volatile(".insn r 0x33, 7, 0x20, zero, zero, zero" : : : "memory"); info |= got_sigill ? 0 : CPUINFO_ZBB; left &= ~CPUINFO_ZBB; } if (left & CPUINFO_ZICOND) { /* Probe for Zicond: czero.eqz zero,zero,zero. */ got_sigill = 0; asm volatile(".insn r 0x33, 5, 0x07, zero, zero, zero" : : : "memory"); info |= got_sigill ? 0 : CPUINFO_ZICOND; left &= ~CPUINFO_ZICOND; } sigaction(SIGILL, &sa_old, NULL); assert(left == 0); } if (info & CPUINFO_ZVE64X) { /* * We are guaranteed by RVV-1.0 that VLEN is a power of 2. * We are guaranteed by Zve64x that VLEN >= 64, and that * EEW of {8,16,32,64} are supported. */ unsigned long vlenb; /* csrr %0, vlenb */ asm volatile(".insn i 0x73, 0x2, %0, zero, -990" : "=r"(vlenb)); assert(vlenb >= 8); assert(is_power_of_2(vlenb)); /* Cache VLEN in a convenient form. */ riscv_lg2_vlenb = ctz32(vlenb); } info |= CPUINFO_ALWAYS; cpuinfo = info; return info; }