summary refs log tree commit diff stats
path: root/util/cpuinfo-riscv.c
diff options
context:
space:
mode:
Diffstat (limited to 'util/cpuinfo-riscv.c')
-rw-r--r--util/cpuinfo-riscv.c85
1 files changed, 85 insertions, 0 deletions
diff --git a/util/cpuinfo-riscv.c b/util/cpuinfo-riscv.c
new file mode 100644
index 0000000000..1236ee5fa4
--- /dev/null
+++ b/util/cpuinfo-riscv.c
@@ -0,0 +1,85 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * Host specific cpu identification for RISC-V.
+ */
+
+#include "qemu/osdep.h"
+#include "host/cpuinfo.h"
+
+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;
+    uc->uc_mcontext.__gregs[REG_PC] += 4;
+
+    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;
+
+    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;
+}