 e57173085a
			
		
	
	
		e57173085a
		
	
	
	
	
		
			
			With recent linux kernels, there is a syscall to probe for various ISA extensions. These bits were phased in over several kernel releases, so we still require checks for symbol availability. Acked-by: Alistair Francis <alistair.francis@wdc.com> Reviewed-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
		
			
				
	
	
		
			119 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			119 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * SPDX-License-Identifier: GPL-2.0-or-later
 | |
|  * Host specific cpu identification for RISC-V.
 | |
|  */
 | |
| 
 | |
| #include "qemu/osdep.h"
 | |
| #include "host/cpuinfo.h"
 | |
| 
 | |
| #ifdef CONFIG_ASM_HWPROBE_H
 | |
| #include <asm/hwprobe.h>
 | |
| #include <sys/syscall.h>
 | |
| #endif
 | |
| 
 | |
| unsigned cpuinfo;
 | |
| 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;
 | |
|     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
 | |
|     left &= ~info;
 | |
| 
 | |
| #ifdef CONFIG_ASM_HWPROBE_H
 | |
|     if (left) {
 | |
|         /*
 | |
|          * TODO: glibc 2.40 will introduce <sys/hwprobe.h>, 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
 | |
|         }
 | |
|     }
 | |
| #endif /* CONFIG_ASM_HWPROBE_H */
 | |
| 
 | |
|     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);
 | |
|     }
 | |
| 
 | |
|     info |= CPUINFO_ALWAYS;
 | |
|     cpuinfo = info;
 | |
|     return info;
 | |
| }
 |