[PATCH v3 1/5] tests/tcg/riscv64: Add a user signal handling test

Nicholas Piggin posted 5 patches 1 day ago
[PATCH v3 1/5] tests/tcg/riscv64: Add a user signal handling test
Posted by Nicholas Piggin 1 day ago
Add a few basic signal handling tests for user emulation.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 tests/tcg/riscv64/Makefile.target        |   5 +
 tests/tcg/riscv64/test-signal-handling.c | 303 +++++++++++++++++++++++
 2 files changed, 308 insertions(+)
 create mode 100644 tests/tcg/riscv64/test-signal-handling.c

diff --git a/tests/tcg/riscv64/Makefile.target b/tests/tcg/riscv64/Makefile.target
index 4da5b9a3b3..f318891396 100644
--- a/tests/tcg/riscv64/Makefile.target
+++ b/tests/tcg/riscv64/Makefile.target
@@ -18,3 +18,8 @@ TESTS += test-fcvtmod
 test-fcvtmod: CFLAGS += -march=rv64imafdc
 test-fcvtmod: LDFLAGS += -static
 run-test-fcvtmod: QEMU_OPTS += -cpu rv64,d=true,zfa=true
+
+# Test signal handling.
+TESTS += test-signal-handling
+test-signal-handling: CFLAGS += -march=rv64gc
+run-test-signal-handling: QEMU_OPTS += -cpu rv64
diff --git a/tests/tcg/riscv64/test-signal-handling.c b/tests/tcg/riscv64/test-signal-handling.c
new file mode 100644
index 0000000000..c202503382
--- /dev/null
+++ b/tests/tcg/riscv64/test-signal-handling.c
@@ -0,0 +1,303 @@
+/*
+ * Test for linux-user signal handling.
+ *
+ * This ensures that integer and fp register values are
+ * saved as expected in the sigcontext, created by a SIGILL.
+ *
+ * TODO: Register restore is not explicitly verified, except
+ * for advancing pc, and the restoring of registers that were
+ * clobbered by the compiler in the signal handler.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <execinfo.h>
+#include <unistd.h>
+#include <assert.h>
+#include <sys/mman.h>
+#include <ucontext.h>
+#include <asm/sigcontext.h>
+
+/*
+ * This horrible hack seems to be required when including
+ * signal.h and asm/sigcontext.h, to prevent sigcontext
+ * redefinition by bits/sigcontext.h :(
+ *
+ * bits/sigcontext.h does not have the extended state or
+ * RISCV_V_MAGIC, etc. It could have just been introduced
+ * as a new type.
+ */
+#define _BITS_SIGCONTEXT_H 1
+#include <signal.h>
+
+static uint64_t *initial_gvalues;
+static uint64_t *final_gvalues;
+static uint64_t *signal_gvalues;
+static double *initial_fvalues;
+static double *final_fvalues;
+static double *signal_fvalues;
+
+extern unsigned long unimp_addr[];
+
+static bool got_signal = false;
+
+#define BT_BUF_SIZE 100
+
+static void *find_callchain_root(void)
+{
+   int nptrs;
+   void *buffer[BT_BUF_SIZE];
+
+   nptrs = backtrace(buffer, BT_BUF_SIZE);
+
+   return buffer[nptrs - 1];
+}
+
+static void *callchain_root;
+
+static void ILL_handler(int signo, siginfo_t *info, void *context)
+{
+    ucontext_t *uc = context;
+    struct sigcontext *sc = (struct sigcontext *)&uc->uc_mcontext;
+
+    got_signal = true;
+
+    assert(unimp_addr == info->si_addr);
+    assert(sc->sc_regs.pc == (unsigned long)info->si_addr);
+
+    /* Ensure stack unwind through the signal frame is not broken */
+    assert(callchain_root == find_callchain_root());
+
+    for (int i = 0; i < 31; i++) {
+        ((uint64_t *)signal_gvalues)[i] = ((unsigned long *)&sc->sc_regs.ra)[i];
+    }
+
+    for (int i = 0; i < 32; i++) {
+        ((uint64_t *)signal_fvalues)[i] = sc->sc_fpregs.d.f[i];
+    }
+    /* Test sc->sc_fpregs.d.fcsr ? */
+
+    sc->sc_regs.pc += 4;
+}
+
+static void init_test(void)
+{
+    int i;
+
+    callchain_root = find_callchain_root();
+
+    initial_gvalues = malloc(8 * 31);
+    memset(initial_gvalues, 0, 8 * 31);
+    final_gvalues = malloc(8 * 31);
+    memset(final_gvalues, 0, 8 * 31);
+    signal_gvalues = malloc(8 * 31);
+    memset(signal_gvalues, 0, 8 * 31);
+
+    initial_fvalues = malloc(8 * 32);
+    memset(initial_fvalues, 0, 8 * 32);
+    for (i = 0; i < 32 ; i++) {
+        initial_fvalues[i] = 3.142 * (i + 1);
+    }
+    final_fvalues = malloc(8 * 32);
+    memset(final_fvalues, 0, 8 * 32);
+    signal_fvalues = malloc(8 * 32);
+    memset(signal_fvalues, 0, 8 * 32);
+}
+
+static void run_test(void)
+{
+    asm volatile(
+    /* Save initial values from gp registers */
+    "mv    t0, %[initial_gvalues]\n\t"
+    "sd    x1, 0x0(t0)\n\t"
+    "sd    x2, 0x8(t0)\n\t"
+    "sd    x3, 0x10(t0)\n\t"
+    "sd    x4, 0x18(t0)\n\t"
+    "sd    x5, 0x20(t0)\n\t"
+    "sd    x6, 0x28(t0)\n\t"
+    "sd    x7, 0x30(t0)\n\t"
+    "sd    x8, 0x38(t0)\n\t"
+    "sd    x9, 0x40(t0)\n\t"
+    "sd    x10, 0x48(t0)\n\t"
+    "sd    x11, 0x50(t0)\n\t"
+    "sd    x12, 0x58(t0)\n\t"
+    "sd    x13, 0x60(t0)\n\t"
+    "sd    x14, 0x68(t0)\n\t"
+    "sd    x15, 0x70(t0)\n\t"
+    "sd    x16, 0x78(t0)\n\t"
+    "sd    x17, 0x80(t0)\n\t"
+    "sd    x18, 0x88(t0)\n\t"
+    "sd    x19, 0x90(t0)\n\t"
+    "sd    x20, 0x98(t0)\n\t"
+    "sd    x21, 0xa0(t0)\n\t"
+    "sd    x22, 0xa8(t0)\n\t"
+    "sd    x23, 0xb0(t0)\n\t"
+    "sd    x24, 0xb8(t0)\n\t"
+    "sd    x25, 0xc0(t0)\n\t"
+    "sd    x26, 0xc8(t0)\n\t"
+    "sd    x27, 0xd0(t0)\n\t"
+    "sd    x28, 0xd8(t0)\n\t"
+    "sd    x29, 0xe0(t0)\n\t"
+    "sd    x30, 0xe8(t0)\n\t"
+    "sd    x31, 0xf0(t0)\n\t"
+    /* Load initial values into float registers */
+    "mv    t0, %[initial_fvalues]\n\t"
+    "fld    f0, 0x0(t0)\n\t"
+    "fld    f1, 0x8(t0)\n\t"
+    "fld    f2, 0x10(t0)\n\t"
+    "fld    f3, 0x18(t0)\n\t"
+    "fld    f4, 0x20(t0)\n\t"
+    "fld    f5, 0x28(t0)\n\t"
+    "fld    f6, 0x30(t0)\n\t"
+    "fld    f7, 0x38(t0)\n\t"
+    "fld    f8, 0x40(t0)\n\t"
+    "fld    f9, 0x48(t0)\n\t"
+    "fld    f10, 0x50(t0)\n\t"
+    "fld    f11, 0x58(t0)\n\t"
+    "fld    f12, 0x60(t0)\n\t"
+    "fld    f13, 0x68(t0)\n\t"
+    "fld    f14, 0x70(t0)\n\t"
+    "fld    f15, 0x78(t0)\n\t"
+    "fld    f16, 0x80(t0)\n\t"
+    "fld    f17, 0x88(t0)\n\t"
+    "fld    f18, 0x90(t0)\n\t"
+    "fld    f19, 0x98(t0)\n\t"
+    "fld    f20, 0xa0(t0)\n\t"
+    "fld    f21, 0xa8(t0)\n\t"
+    "fld    f22, 0xb0(t0)\n\t"
+    "fld    f23, 0xb8(t0)\n\t"
+    "fld    f24, 0xc0(t0)\n\t"
+    "fld    f25, 0xc8(t0)\n\t"
+    "fld    f26, 0xd0(t0)\n\t"
+    "fld    f27, 0xd8(t0)\n\t"
+    "fld    f28, 0xe0(t0)\n\t"
+    "fld    f29, 0xe8(t0)\n\t"
+    "fld    f30, 0xf0(t0)\n\t"
+    "fld    f31, 0xf8(t0)\n\t"
+    /* Trigger the SIGILL */
+".global unimp_addr\n\t"
+"unimp_addr:\n\t"
+    "unimp\n\t"
+    "nop\n\t"
+    /* Save final values from gp registers */
+    "mv    t0, %[final_gvalues]\n\t"
+    "sd    x1, 0x0(t0)\n\t"
+    "sd    x2, 0x8(t0)\n\t"
+    "sd    x3, 0x10(t0)\n\t"
+    "sd    x4, 0x18(t0)\n\t"
+    "sd    x5, 0x20(t0)\n\t"
+    "sd    x6, 0x28(t0)\n\t"
+    "sd    x7, 0x30(t0)\n\t"
+    "sd    x8, 0x38(t0)\n\t"
+    "sd    x9, 0x40(t0)\n\t"
+    "sd    x10, 0x48(t0)\n\t"
+    "sd    x11, 0x50(t0)\n\t"
+    "sd    x12, 0x58(t0)\n\t"
+    "sd    x13, 0x60(t0)\n\t"
+    "sd    x14, 0x68(t0)\n\t"
+    "sd    x15, 0x70(t0)\n\t"
+    "sd    x16, 0x78(t0)\n\t"
+    "sd    x17, 0x80(t0)\n\t"
+    "sd    x18, 0x88(t0)\n\t"
+    "sd    x19, 0x90(t0)\n\t"
+    "sd    x20, 0x98(t0)\n\t"
+    "sd    x21, 0xa0(t0)\n\t"
+    "sd    x22, 0xa8(t0)\n\t"
+    "sd    x23, 0xb0(t0)\n\t"
+    "sd    x24, 0xb8(t0)\n\t"
+    "sd    x25, 0xc0(t0)\n\t"
+    "sd    x26, 0xc8(t0)\n\t"
+    "sd    x27, 0xd0(t0)\n\t"
+    "sd    x28, 0xd8(t0)\n\t"
+    "sd    x29, 0xe0(t0)\n\t"
+    "sd    x30, 0xe8(t0)\n\t"
+    "sd    x31, 0xf0(t0)\n\t"
+    /* Save final values from float registers */
+    "mv    t0, %[final_fvalues]\n\t"
+    "fsd    f0, 0x0(t0)\n\t"
+    "fsd    f1, 0x8(t0)\n\t"
+    "fsd    f2, 0x10(t0)\n\t"
+    "fsd    f3, 0x18(t0)\n\t"
+    "fsd    f4, 0x20(t0)\n\t"
+    "fsd    f5, 0x28(t0)\n\t"
+    "fsd    f6, 0x30(t0)\n\t"
+    "fsd    f7, 0x38(t0)\n\t"
+    "fsd    f8, 0x40(t0)\n\t"
+    "fsd    f9, 0x48(t0)\n\t"
+    "fsd    f10, 0x50(t0)\n\t"
+    "fsd    f11, 0x58(t0)\n\t"
+    "fsd    f12, 0x60(t0)\n\t"
+    "fsd    f13, 0x68(t0)\n\t"
+    "fsd    f14, 0x70(t0)\n\t"
+    "fsd    f15, 0x78(t0)\n\t"
+    "fsd    f16, 0x80(t0)\n\t"
+    "fsd    f17, 0x88(t0)\n\t"
+    "fsd    f18, 0x90(t0)\n\t"
+    "fsd    f19, 0x98(t0)\n\t"
+    "fsd    f20, 0xa0(t0)\n\t"
+    "fsd    f21, 0xa8(t0)\n\t"
+    "fsd    f22, 0xb0(t0)\n\t"
+    "fsd    f23, 0xb8(t0)\n\t"
+    "fsd    f24, 0xc0(t0)\n\t"
+    "fsd    f25, 0xc8(t0)\n\t"
+    "fsd    f26, 0xd0(t0)\n\t"
+    "fsd    f27, 0xd8(t0)\n\t"
+    "fsd    f28, 0xe0(t0)\n\t"
+    "fsd    f29, 0xe8(t0)\n\t"
+    "fsd    f30, 0xf0(t0)\n\t"
+    "fsd    f31, 0xf8(t0)\n\t"
+    : "=m" (initial_gvalues),
+      "=m" (final_gvalues),
+      "=m" (final_fvalues)
+    : "m" (initial_fvalues),
+      [initial_gvalues] "r" (initial_gvalues),
+      [initial_fvalues] "r" (initial_fvalues),
+      [final_gvalues] "r" (final_gvalues),
+      [final_fvalues] "r" (final_fvalues)
+    : "t0",
+      "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
+      "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",
+      "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
+      "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31");
+
+    assert(got_signal);
+
+    /*
+     * x4 / t0 is used in the asm so it has to be handled specially
+     * and is not a simple equality.
+     */
+    assert(initial_gvalues[4] == (unsigned long)initial_gvalues);
+    assert(signal_gvalues[4] == (unsigned long)initial_fvalues);
+    assert(final_gvalues[4] == (unsigned long)final_gvalues);
+    initial_gvalues[4] = final_gvalues[4] = signal_gvalues[4] = 0;
+
+    /*
+     * Ensure registers match before, inside, and after signal
+     * handler.
+     */
+    assert(!memcmp(initial_gvalues, final_gvalues, 8 * 31));
+    assert(!memcmp(initial_gvalues, signal_gvalues, 8 * 31));
+    assert(!memcmp(initial_fvalues, final_fvalues, 8 * 32));
+    assert(!memcmp(initial_fvalues, signal_fvalues, 8 * 32));
+}
+
+int main(void)
+{
+    struct sigaction act = { 0 };
+
+    act.sa_flags = SA_SIGINFO;
+    act.sa_sigaction = &ILL_handler;
+    if (sigaction(SIGILL, &act, NULL) == -1) {
+        perror("sigaction");
+        exit(EXIT_FAILURE);
+    }
+
+    init_test();
+
+    run_test();
+}
-- 
2.51.0