public inbox for qemu-devel@nongnu.org
 help / color / mirror / Atom feed
* [PATCH v3 0/5] linux-user/riscv: add vector state to signal context
@ 2026-03-21 14:13 Nicholas Piggin
  2026-03-21 14:13 ` [PATCH v3 1/5] tests/tcg/riscv64: Add a user signal handling test Nicholas Piggin
                   ` (5 more replies)
  0 siblings, 6 replies; 13+ messages in thread
From: Nicholas Piggin @ 2026-03-21 14:13 UTC (permalink / raw)
  To: qemu-riscv
  Cc: Nicholas Piggin, qemu-devel, Laurent Vivier, Pierrick Bouvier,
	Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, Richard Henderson,
	Joel Stanley

Changes in v3:
- Fixed up a bunch of good review comments that Richard made against
  the v1 series here https://lore.kernel.org/all/06afcbdc-28e2-4d13-81d4-26fd257ed8a3@linaro.org/#r
  in particular:
  - Restoring vl/vtype state using CPU accessors rather than stuffing the
    values into the registers directly.
  - Fix ABI types.
  - Fix some confusion with host/guest pointer mapping.
  - Fix error handling to SIGSEGV.
  - Style fixes.
- Fixed build issue without riscv intrinsics header.
- Add some more test cases.

Changes in v2:
- Fix 32-bit compile
- Moved sizeof_rt_sigframe change to its correct patch (patch 2 is the
  one that extends the size of rt_sigframe, patch 3 is adding new
  extended state to the frame which is added dynamically).

This series adds vector state to the linux-user signal handler,
and adds a basic signal handling test case. As a sanity check, I
also verified the signal handling test works in the same way when
run under a real Linux kernel.

Thanks,
Nick

Nicholas Piggin (5):
  tests/tcg/riscv64: Add a user signal handling test
  linux-user/riscv: Allow restore_sigcontext to return error
  linux-user/riscv: Add extended state to sigcontext
  linux-user/riscv: Add vector state to signal context
  tests/tcg/riscv64: Add vector state to signal test

 linux-user/riscv/signal.c                | 281 +++++++++++-
 linux-user/riscv/vdso-asmoffset.h        |   4 +-
 target/riscv/cpu.h                       |   4 +
 target/riscv/csr.c                       |   7 +-
 target/riscv/vector_helper.c             |  19 +-
 tests/tcg/riscv64/Makefile.target        |   5 +
 tests/tcg/riscv64/test-signal-handling.c | 517 +++++++++++++++++++++++
 7 files changed, 807 insertions(+), 30 deletions(-)
 create mode 100644 tests/tcg/riscv64/test-signal-handling.c

-- 
2.51.0



^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v3 1/5] tests/tcg/riscv64: Add a user signal handling test
  2026-03-21 14:13 [PATCH v3 0/5] linux-user/riscv: add vector state to signal context Nicholas Piggin
@ 2026-03-21 14:13 ` Nicholas Piggin
  2026-03-25  3:07   ` Alistair Francis
  2026-03-21 14:13 ` [PATCH v3 2/5] linux-user/riscv: Allow restore_sigcontext to return error Nicholas Piggin
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 13+ messages in thread
From: Nicholas Piggin @ 2026-03-21 14:13 UTC (permalink / raw)
  To: qemu-riscv
  Cc: Nicholas Piggin, qemu-devel, Laurent Vivier, Pierrick Bouvier,
	Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, Richard Henderson,
	Joel Stanley

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



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v3 2/5] linux-user/riscv: Allow restore_sigcontext to return error
  2026-03-21 14:13 [PATCH v3 0/5] linux-user/riscv: add vector state to signal context Nicholas Piggin
  2026-03-21 14:13 ` [PATCH v3 1/5] tests/tcg/riscv64: Add a user signal handling test Nicholas Piggin
@ 2026-03-21 14:13 ` Nicholas Piggin
  2026-03-25  3:07   ` Alistair Francis
  2026-03-21 14:13 ` [PATCH v3 3/5] linux-user/riscv: Add extended state to sigcontext Nicholas Piggin
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 13+ messages in thread
From: Nicholas Piggin @ 2026-03-21 14:13 UTC (permalink / raw)
  To: qemu-riscv
  Cc: Nicholas Piggin, qemu-devel, Laurent Vivier, Pierrick Bouvier,
	Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, Richard Henderson,
	Joel Stanley

Linux can return error from restore_sigcontext in some conditions, which
forces a SIGSEGV. Plumb through this error handling which will be used
by the next change.

Suggested-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 linux-user/riscv/signal.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/linux-user/riscv/signal.c b/linux-user/riscv/signal.c
index 22b1b8149f..ece276f85f 100644
--- a/linux-user/riscv/signal.c
+++ b/linux-user/riscv/signal.c
@@ -145,7 +145,7 @@ badframe:
     force_sig(TARGET_SIGSEGV);
 }
 
-static void restore_sigcontext(CPURISCVState *env, struct target_sigcontext *sc)
+static bool restore_sigcontext(CPURISCVState *env, struct target_sigcontext *sc)
 {
     int i;
 
@@ -161,9 +161,11 @@ static void restore_sigcontext(CPURISCVState *env, struct target_sigcontext *sc)
     uint32_t fcsr;
     __get_user(fcsr, &sc->fcsr);
     riscv_csr_write(env, CSR_FCSR, fcsr);
+
+    return true;
 }
 
-static void restore_ucontext(CPURISCVState *env, struct target_ucontext *uc)
+static bool restore_ucontext(CPURISCVState *env, struct target_ucontext *uc)
 {
     sigset_t blocked;
     target_sigset_t target_set;
@@ -177,7 +179,7 @@ static void restore_ucontext(CPURISCVState *env, struct target_ucontext *uc)
     target_to_host_sigset_internal(&blocked, &target_set);
     set_sigmask(&blocked);
 
-    restore_sigcontext(env, &uc->uc_mcontext);
+    return restore_sigcontext(env, &uc->uc_mcontext);
 }
 
 long do_rt_sigreturn(CPURISCVState *env)
@@ -191,7 +193,10 @@ long do_rt_sigreturn(CPURISCVState *env)
         goto badframe;
     }
 
-    restore_ucontext(env, &frame->uc);
+    if (!restore_ucontext(env, &frame->uc)) {
+        goto badframe;
+    }
+
     target_restore_altstack(&frame->uc.uc_stack, env);
 
     unlock_user_struct(frame, frame_addr, 0);
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v3 3/5] linux-user/riscv: Add extended state to sigcontext
  2026-03-21 14:13 [PATCH v3 0/5] linux-user/riscv: add vector state to signal context Nicholas Piggin
  2026-03-21 14:13 ` [PATCH v3 1/5] tests/tcg/riscv64: Add a user signal handling test Nicholas Piggin
  2026-03-21 14:13 ` [PATCH v3 2/5] linux-user/riscv: Allow restore_sigcontext to return error Nicholas Piggin
@ 2026-03-21 14:13 ` Nicholas Piggin
  2026-03-25  3:14   ` Alistair Francis
  2026-03-21 14:13 ` [PATCH v3 4/5] linux-user/riscv: Add vector state to signal context Nicholas Piggin
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 13+ messages in thread
From: Nicholas Piggin @ 2026-03-21 14:13 UTC (permalink / raw)
  To: qemu-riscv
  Cc: Nicholas Piggin, qemu-devel, Laurent Vivier, Pierrick Bouvier,
	Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, Richard Henderson,
	Joel Stanley

Linux/riscv has extended the sigcontext with padding and an
extended state structure that can save various optional
features like vector in a flexible format. Update the
linux-user signal handling to this new structure.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 linux-user/riscv/signal.c         | 93 +++++++++++++++++++++++++++----
 linux-user/riscv/vdso-asmoffset.h |  4 +-
 2 files changed, 85 insertions(+), 12 deletions(-)

diff --git a/linux-user/riscv/signal.c b/linux-user/riscv/signal.c
index ece276f85f..e20b9ac177 100644
--- a/linux-user/riscv/signal.c
+++ b/linux-user/riscv/signal.c
@@ -31,14 +31,43 @@
 
    The code below is qemu re-implementation of arch/riscv/kernel/signal.c */
 
-struct target_sigcontext {
+struct target_gp_state {
     abi_long pc;
     abi_long gpr[31]; /* x0 is not present, so all offsets must be -1 */
+};
+
+struct target_fp_state {
     uint64_t fpr[32];
     uint32_t fcsr;
+};
+
+/* The Magic number for signal context frame header. */
+#define END_MAGIC       0x0
+
+/* The size of END signal context header. */
+#define END_HDR_SIZE    0x0
+
+struct target_ctx_hdr {
+    uint32_t magic;
+    uint32_t size;
+};
+
+struct target_extra_ext_header {
+    uint32_t __padding[129] __attribute__((aligned(16)));
+    uint32_t reserved;
+    struct target_ctx_hdr hdr;
+};
+
+struct target_sigcontext {
+    struct target_gp_state sc_regs;
+    union {
+        struct target_fp_state sc_fpregs;
+        struct target_extra_ext_header sc_extdesc;
+    };
 }; /* cf. riscv-linux:arch/riscv/include/uapi/asm/ptrace.h */
 
-QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, fpr) != offsetof_freg0);
+QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, sc_fpregs.fpr) !=
+                  offsetof_freg0);
 
 struct target_ucontext {
     abi_ulong uc_flags;
@@ -79,19 +108,26 @@ static abi_ulong get_sigframe(struct target_sigaction *ka,
 
 static void setup_sigcontext(struct target_sigcontext *sc, CPURISCVState *env)
 {
+    struct target_ctx_hdr *hdr;
     int i;
 
-    __put_user(env->pc, &sc->pc);
+    __put_user(env->pc, &sc->sc_regs.pc);
 
     for (i = 1; i < 32; i++) {
-        __put_user(env->gpr[i], &sc->gpr[i - 1]);
+        __put_user(env->gpr[i], &sc->sc_regs.gpr[i - 1]);
     }
     for (i = 0; i < 32; i++) {
-        __put_user(env->fpr[i], &sc->fpr[i]);
+        __put_user(env->fpr[i], &sc->sc_fpregs.fpr[i]);
     }
 
     uint32_t fcsr = riscv_csr_read(env, CSR_FCSR);
-    __put_user(fcsr, &sc->fcsr);
+    __put_user(fcsr, &sc->sc_fpregs.fcsr);
+
+    __put_user(0, &sc->sc_extdesc.reserved);
+
+    hdr = &sc->sc_extdesc.hdr;
+    __put_user(END_MAGIC, &hdr->magic);
+    __put_user(END_HDR_SIZE, &hdr->size);
 }
 
 static void setup_ucontext(struct target_ucontext *uc,
@@ -147,21 +183,58 @@ badframe:
 
 static bool restore_sigcontext(CPURISCVState *env, struct target_sigcontext *sc)
 {
+    struct target_ctx_hdr *hdr;
+    uint32_t rsv, magic, size;
     int i;
 
-    __get_user(env->pc, &sc->pc);
+    __get_user(env->pc, &sc->sc_regs.pc);
 
     for (i = 1; i < 32; ++i) {
-        __get_user(env->gpr[i], &sc->gpr[i - 1]);
+        __get_user(env->gpr[i], &sc->sc_regs.gpr[i - 1]);
     }
     for (i = 0; i < 32; ++i) {
-        __get_user(env->fpr[i], &sc->fpr[i]);
+        __get_user(env->fpr[i], &sc->sc_fpregs.fpr[i]);
     }
 
     uint32_t fcsr;
-    __get_user(fcsr, &sc->fcsr);
+    __get_user(fcsr, &sc->sc_fpregs.fcsr);
     riscv_csr_write(env, CSR_FCSR, fcsr);
 
+    hdr = &sc->sc_extdesc.hdr;
+    __get_user(rsv, &sc->sc_extdesc.reserved);
+    if (rsv != 0) {
+        qemu_log_mask(LOG_GUEST_ERROR, "signal: sigcontext reserved field is "
+                                       "non-zero\n");
+        return false;
+    }
+
+    __get_user(magic, &hdr->magic);
+    while (magic != END_MAGIC) {
+        switch (magic) {
+        default:
+            qemu_log_mask(LOG_GUEST_ERROR, "signal: unknown extended state in "
+                                           "sigcontext, magic=0x%08x\n", magic);
+            return false;
+        }
+
+        __get_user(size, &hdr->size);
+        if (size == 0) {
+            qemu_log_mask(LOG_GUEST_ERROR, "signal: extended state in "
+                                           "sigcontext has size 0\n");
+            return false;
+        }
+
+        hdr = (void *)hdr + size;
+        __get_user(magic, &hdr->magic);
+    }
+
+    __get_user(size, &hdr->size);
+    if (size != END_HDR_SIZE) {
+        qemu_log_mask(LOG_GUEST_ERROR, "signal: extended state end header has "
+                                       "size=%u (should be 0)\n", size);
+        return false;
+    }
+
     return true;
 }
 
diff --git a/linux-user/riscv/vdso-asmoffset.h b/linux-user/riscv/vdso-asmoffset.h
index 123902ef61..92e8ac10ab 100644
--- a/linux-user/riscv/vdso-asmoffset.h
+++ b/linux-user/riscv/vdso-asmoffset.h
@@ -1,9 +1,9 @@
 #ifdef TARGET_ABI32
-# define sizeof_rt_sigframe     0x2b0
+# define sizeof_rt_sigframe     0x3b0
 # define offsetof_uc_mcontext   0x120
 # define offsetof_freg0         0x80
 #else
-# define sizeof_rt_sigframe     0x340
+# define sizeof_rt_sigframe     0x440
 # define offsetof_uc_mcontext   0x130
 # define offsetof_freg0         0x100
 #endif
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v3 4/5] linux-user/riscv: Add vector state to signal context
  2026-03-21 14:13 [PATCH v3 0/5] linux-user/riscv: add vector state to signal context Nicholas Piggin
                   ` (2 preceding siblings ...)
  2026-03-21 14:13 ` [PATCH v3 3/5] linux-user/riscv: Add extended state to sigcontext Nicholas Piggin
@ 2026-03-21 14:13 ` Nicholas Piggin
  2026-03-25  3:45   ` Alistair Francis
  2026-03-21 14:13 ` [PATCH v3 5/5] tests/tcg/riscv64: Add vector state to signal test Nicholas Piggin
  2026-03-25  4:45 ` [PATCH v3 0/5] linux-user/riscv: add vector state to signal context Alistair Francis
  5 siblings, 1 reply; 13+ messages in thread
From: Nicholas Piggin @ 2026-03-21 14:13 UTC (permalink / raw)
  To: qemu-riscv
  Cc: Nicholas Piggin, qemu-devel, Laurent Vivier, Pierrick Bouvier,
	Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, Richard Henderson,
	Joel Stanley

This enables vector state to be saved and restored across signals.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 linux-user/riscv/signal.c    | 175 +++++++++++++++++++++++++++++++++--
 target/riscv/cpu.h           |   4 +
 target/riscv/csr.c           |   7 +-
 target/riscv/vector_helper.c |  19 +++-
 4 files changed, 191 insertions(+), 14 deletions(-)

diff --git a/linux-user/riscv/signal.c b/linux-user/riscv/signal.c
index e20b9ac177..2e1a1a5027 100644
--- a/linux-user/riscv/signal.c
+++ b/linux-user/riscv/signal.c
@@ -41,7 +41,17 @@ struct target_fp_state {
     uint32_t fcsr;
 };
 
+struct target_v_ext_state {
+    abi_ulong vstart;
+    abi_ulong vl;
+    abi_ulong vtype;
+    abi_ulong vcsr;
+    abi_ulong vlenb;
+    abi_ptr   datap;
+};
+
 /* The Magic number for signal context frame header. */
+#define RISCV_V_MAGIC   0x53465457
 #define END_MAGIC       0x0
 
 /* The size of END signal context header. */
@@ -106,6 +116,130 @@ static abi_ulong get_sigframe(struct target_sigaction *ka,
     return sp;
 }
 
+static unsigned int get_v_state_hdr_size(CPURISCVState *env)
+{
+    return sizeof(struct target_ctx_hdr) +
+           sizeof(struct target_v_ext_state);
+}
+
+static unsigned int get_v_state_data_size(CPURISCVState *env)
+{
+    RISCVCPU *cpu = env_archcpu(env);
+    return cpu->cfg.vlenb * 32;
+}
+
+static struct target_ctx_hdr *save_v_state(CPURISCVState *env,
+                                           struct target_ctx_hdr *hdr)
+{
+    RISCVCPU *cpu = env_archcpu(env);
+    target_ulong vlenb = cpu->cfg.vlenb;
+    uint32_t riscv_v_sc_size = get_v_state_hdr_size(env) +
+                               get_v_state_data_size(env);
+    struct target_v_ext_state *vs;
+    abi_ulong vcsr;
+    abi_ptr datap;
+    void *host_datap;
+
+#ifdef CONFIG_DEBUG_REMAP
+    /*
+     * The host pointers are derived from lock_user, not g2h, so
+     * h2g can not be used when CONFIG_DEBUG_REMAP=y.
+     */
+    qemu_log_mask(LOG_UNIMP, "signal: sigcontext can not save V state "
+                             "when CONFIG_DEBUG_REMAP=y\n");
+    return hdr;
+#endif
+
+    vs = (struct target_v_ext_state *)(hdr + 1);
+    vcsr = riscv_csr_read(env, CSR_VCSR);
+    host_datap = (vs + 1);
+    datap = h2g(host_datap);
+
+    __put_user(RISCV_V_MAGIC, &hdr->magic);
+    __put_user(riscv_v_sc_size, &hdr->size);
+
+    __put_user(env->vstart, &vs->vstart);
+    __put_user(env->vl, &vs->vl);
+    __put_user(env->vtype, &vs->vtype);
+    __put_user(vcsr, &vs->vcsr);
+    __put_user(vlenb, &vs->vlenb);
+    __put_user(datap, &vs->datap);
+
+    for (int i = 0; i < 32; i++) {
+        for (int j = 0; j < vlenb; j += 8) {
+            size_t idx = (i * vlenb + j);
+            __put_user(env->vreg[idx / 8],
+                       (uint64_t *)(host_datap + idx));
+        }
+    }
+
+    return (void *)hdr + riscv_v_sc_size;
+}
+
+static bool restore_v_state(CPURISCVState *env,
+                            struct target_ctx_hdr *hdr)
+{
+    RISCVCPU *cpu = env_archcpu(env);
+    target_ulong vlenb;
+    target_ulong vcsr, vl, vtype, vstart;
+    struct target_v_ext_state *vs;
+    uint32_t size;
+    abi_ptr datap;
+    void *host_datap;
+
+    if (!riscv_has_ext(env, RVV)) {
+        qemu_log_mask(LOG_GUEST_ERROR, "signal: sigcontext has V state but "
+                                       "CPU does not support V extension\n");
+        return false;
+    }
+
+    __get_user(size, &hdr->size);
+    if (size < get_v_state_hdr_size(env)) {
+        qemu_log_mask(LOG_GUEST_ERROR, "signal: sigcontext V state header "
+                                       "size is too small (%u)\n", size);
+        return false;
+    }
+
+    vs = (struct target_v_ext_state *)(hdr + 1);
+
+    __get_user(vstart, &vs->vstart);
+    __get_user(vl, &vs->vl);
+    __get_user(vtype, &vs->vtype);
+    __get_user(vcsr, &vs->vcsr);
+
+    riscv_cpu_set_vstart(env, vstart);
+    riscv_cpu_vsetvl(env, vl, vtype, 0);
+    riscv_csr_write(env, CSR_VCSR, vcsr);
+
+    __get_user(vlenb, &vs->vlenb);
+
+    if (vlenb != cpu->cfg.vlenb) {
+        qemu_log_mask(LOG_GUEST_ERROR, "signal: sigcontext has invalid "
+                                       "vlenb\n");
+        return false;
+    }
+
+    __get_user(datap, &vs->datap);
+
+    host_datap = lock_user(VERIFY_READ, datap, vlenb * 32, true);
+    if (!host_datap) {
+        qemu_log_mask(LOG_GUEST_ERROR, "signal: sigcontext has V state but "
+                                       "datap pointer is invalid\n");
+        return false;
+    }
+
+    for (int i = 0; i < 32; i++) {
+        for (int j = 0; j < vlenb; j += 8) {
+            size_t idx = (i * vlenb + j);
+            __get_user(env->vreg[idx / 8],
+                       (uint64_t *)(host_datap + idx));
+        }
+    }
+    unlock_user(host_datap, datap, 0);
+
+    return true;
+}
+
 static void setup_sigcontext(struct target_sigcontext *sc, CPURISCVState *env)
 {
     struct target_ctx_hdr *hdr;
@@ -126,6 +260,9 @@ static void setup_sigcontext(struct target_sigcontext *sc, CPURISCVState *env)
     __put_user(0, &sc->sc_extdesc.reserved);
 
     hdr = &sc->sc_extdesc.hdr;
+    if (riscv_has_ext(env, RVV)) {
+        hdr = save_v_state(env, hdr);
+    }
     __put_user(END_MAGIC, &hdr->magic);
     __put_user(END_HDR_SIZE, &hdr->size);
 }
@@ -152,17 +289,24 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
 {
     abi_ulong frame_addr;
     struct target_rt_sigframe *frame;
+    size_t frame_size = sizeof(*frame);
 
-    frame_addr = get_sigframe(ka, env, sizeof(*frame));
+    if (riscv_has_ext(env, RVV)) {
+        frame_size += get_v_state_hdr_size(env) +
+                      get_v_state_data_size(env);
+    }
+
+    frame_addr = get_sigframe(ka, env, frame_size);
     trace_user_setup_rt_frame(env, frame_addr);
 
-    if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
+    frame = lock_user(VERIFY_WRITE, frame_addr, frame_size, 0);
+    if (!frame) {
         goto badframe;
     }
 
     setup_ucontext(&frame->uc, env, set);
     frame->info = *info;
-    unlock_user_struct(frame, frame_addr, 1);
+    unlock_user(frame, frame_addr, frame_size);
 
     env->pc = ka->_sa_handler;
     env->gpr[xSP] = frame_addr;
@@ -174,7 +318,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
     return;
 
 badframe:
-    unlock_user_struct(frame, frame_addr, 1);
+    unlock_user(frame, frame_addr, frame_size);
     if (sig == TARGET_SIGSEGV) {
         ka->_sa_handler = TARGET_SIG_DFL;
     }
@@ -211,6 +355,11 @@ static bool restore_sigcontext(CPURISCVState *env, struct target_sigcontext *sc)
     __get_user(magic, &hdr->magic);
     while (magic != END_MAGIC) {
         switch (magic) {
+        case RISCV_V_MAGIC:
+            if (!restore_v_state(env, hdr)) {
+                return false;
+            }
+            break;
         default:
             qemu_log_mask(LOG_GUEST_ERROR, "signal: unknown extended state in "
                                            "sigcontext, magic=0x%08x\n", magic);
@@ -258,11 +407,23 @@ static bool restore_ucontext(CPURISCVState *env, struct target_ucontext *uc)
 long do_rt_sigreturn(CPURISCVState *env)
 {
     struct target_rt_sigframe *frame;
+    size_t frame_size = sizeof(*frame);
     abi_ulong frame_addr;
 
+    if (riscv_has_ext(env, RVV)) {
+        /*
+         * userspace may have set up a discontiguous V state data area,
+         * so need to map that region separately once the address is
+         * known, from datap.
+         */
+        frame_size += get_v_state_hdr_size(env);
+    }
+
     frame_addr = env->gpr[xSP];
     trace_user_do_sigreturn(env, frame_addr);
-    if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
+
+    frame = lock_user(VERIFY_READ, frame_addr, frame_size, 1);
+    if (!frame) {
         goto badframe;
     }
 
@@ -272,11 +433,11 @@ long do_rt_sigreturn(CPURISCVState *env)
 
     target_restore_altstack(&frame->uc.uc_stack, env);
 
-    unlock_user_struct(frame, frame_addr, 0);
+    unlock_user(frame, frame_addr, 0);
     return -QEMU_ESIGRETURN;
 
 badframe:
-    unlock_user_struct(frame, frame_addr, 0);
+    unlock_user(frame, frame_addr, 0);
     force_sig(TARGET_SIGSEGV);
     return 0;
 }
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 35d1f6362c..e1eca79197 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -668,6 +668,10 @@ G_NORETURN void riscv_raise_exception(CPURISCVState *env,
 target_ulong riscv_cpu_get_fflags(CPURISCVState *env);
 void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong);
 
+void riscv_cpu_set_vstart(CPURISCVState *env, target_ulong val);
+target_ulong riscv_cpu_vsetvl(CPURISCVState *env, target_ulong s1,
+                              target_ulong s2, target_ulong x0);
+
 #ifndef CONFIG_USER_ONLY
 void cpu_set_exception_base(int vp_index, target_ulong address);
 #endif
diff --git a/target/riscv/csr.c b/target/riscv/csr.c
index 5064483917..8a6fd11fb5 100644
--- a/target/riscv/csr.c
+++ b/target/riscv/csr.c
@@ -991,11 +991,8 @@ static RISCVException write_vstart(CPURISCVState *env, int csrno,
 #if !defined(CONFIG_USER_ONLY)
     env->mstatus |= MSTATUS_VS;
 #endif
-    /*
-     * The vstart CSR is defined to have only enough writable bits
-     * to hold the largest element index, i.e. lg2(VLEN) bits.
-     */
-    env->vstart = val & ~(~0ULL << ctzl(riscv_cpu_cfg(env)->vlenb << 3));
+    riscv_cpu_set_vstart(env, val);
+
     return RISCV_EXCP_NONE;
 }
 
diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c
index caa8dd9c12..bceefe019b 100644
--- a/target/riscv/vector_helper.c
+++ b/target/riscv/vector_helper.c
@@ -33,8 +33,17 @@
 #include "vector_internals.h"
 #include <math.h>
 
-target_ulong HELPER(vsetvl)(CPURISCVState *env, target_ulong s1,
-                            target_ulong s2, target_ulong x0)
+void riscv_cpu_set_vstart(CPURISCVState *env, target_ulong val)
+{
+    /*
+     * The vstart CSR is defined to have only enough writable bits
+     * to hold the largest element index, i.e. lg2(VLEN) bits.
+     */
+    env->vstart = val & ~(~0ULL << ctzl(riscv_cpu_cfg(env)->vlenb << 3));
+}
+
+target_ulong riscv_cpu_vsetvl(CPURISCVState *env, target_ulong s1,
+                              target_ulong s2, target_ulong x0)
 {
     int vlmax, vl;
     RISCVCPU *cpu = env_archcpu(env);
@@ -99,6 +108,12 @@ target_ulong HELPER(vsetvl)(CPURISCVState *env, target_ulong s1,
     return vl;
 }
 
+target_ulong HELPER(vsetvl)(CPURISCVState *env, target_ulong s1,
+                            target_ulong s2, target_ulong x0)
+{
+    return riscv_cpu_vsetvl(env, s1, s2, x0);
+}
+
 /*
  * Get the maximum number of elements can be operated.
  *
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v3 5/5] tests/tcg/riscv64: Add vector state to signal test
  2026-03-21 14:13 [PATCH v3 0/5] linux-user/riscv: add vector state to signal context Nicholas Piggin
                   ` (3 preceding siblings ...)
  2026-03-21 14:13 ` [PATCH v3 4/5] linux-user/riscv: Add vector state to signal context Nicholas Piggin
@ 2026-03-21 14:13 ` Nicholas Piggin
  2026-03-25  3:49   ` Alistair Francis
  2026-03-25  4:45 ` [PATCH v3 0/5] linux-user/riscv: add vector state to signal context Alistair Francis
  5 siblings, 1 reply; 13+ messages in thread
From: Nicholas Piggin @ 2026-03-21 14:13 UTC (permalink / raw)
  To: qemu-riscv
  Cc: Nicholas Piggin, qemu-devel, Laurent Vivier, Pierrick Bouvier,
	Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, Richard Henderson,
	Joel Stanley

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 tests/tcg/riscv64/Makefile.target        |   4 +-
 tests/tcg/riscv64/test-signal-handling.c | 226 ++++++++++++++++++++++-
 2 files changed, 222 insertions(+), 8 deletions(-)

diff --git a/tests/tcg/riscv64/Makefile.target b/tests/tcg/riscv64/Makefile.target
index f318891396..86b6889a3d 100644
--- a/tests/tcg/riscv64/Makefile.target
+++ b/tests/tcg/riscv64/Makefile.target
@@ -21,5 +21,5 @@ 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
+test-signal-handling: CFLAGS += -march=rv64gcv
+run-test-signal-handling: QEMU_OPTS += -cpu rv64,v=on
diff --git a/tests/tcg/riscv64/test-signal-handling.c b/tests/tcg/riscv64/test-signal-handling.c
index c202503382..b71fa6ee87 100644
--- a/tests/tcg/riscv64/test-signal-handling.c
+++ b/tests/tcg/riscv64/test-signal-handling.c
@@ -19,10 +19,27 @@
 #include <execinfo.h>
 #include <unistd.h>
 #include <assert.h>
+#include <sys/auxv.h>
+#include <elf.h>
 #include <sys/mman.h>
 #include <ucontext.h>
 #include <asm/sigcontext.h>
 
+#ifdef __riscv_v_intrinsic
+#include <riscv_vector.h>
+#else
+static inline unsigned long __riscv_vlenb(void)
+{
+    unsigned long vlenb;
+    __asm__ __volatile__ ("csrr %0, vlenb" : "=r" (vlenb));
+    return vlenb;
+}
+#endif
+
+#ifndef COMPAT_HWCAP_ISA_V
+#define COMPAT_HWCAP_ISA_V (1 << ('V' - 'A'))
+#endif
+
 /*
  * This horrible hack seems to be required when including
  * signal.h and asm/sigcontext.h, to prevent sigcontext
@@ -41,6 +58,10 @@ static uint64_t *signal_gvalues;
 static double *initial_fvalues;
 static double *final_fvalues;
 static double *signal_fvalues;
+static size_t vlenb;
+static uint8_t *initial_vvalues;
+static uint8_t *final_vvalues;
+static uint8_t *signal_vvalues;
 
 extern unsigned long unimp_addr[];
 
@@ -64,6 +85,8 @@ static void ILL_handler(int signo, siginfo_t *info, void *context)
 {
     ucontext_t *uc = context;
     struct sigcontext *sc = (struct sigcontext *)&uc->uc_mcontext;
+    struct __riscv_ctx_hdr *sc_ext = &sc->sc_extdesc.hdr;
+    bool found_v = false;
 
     got_signal = true;
 
@@ -82,12 +105,48 @@ static void ILL_handler(int signo, siginfo_t *info, void *context)
     }
     /* Test sc->sc_fpregs.d.fcsr ? */
 
+    assert(sc->sc_extdesc.reserved == 0);
+    while (sc_ext->magic != END_MAGIC) {
+        assert(sc_ext->size != 0);
+
+        if (sc_ext->magic == RISCV_V_MAGIC) {
+            struct __sc_riscv_v_state *sc_v_state =
+                        (struct __sc_riscv_v_state *)(sc_ext + 1);
+            struct __riscv_v_ext_state *v_state = &sc_v_state->v_state;
+
+            found_v = true;
+
+            assert(getauxval(AT_HWCAP) & COMPAT_HWCAP_ISA_V);
+
+            assert(v_state->vlenb == vlenb);
+            assert(v_state->vtype == 0xc0); /* vma, vta */
+            assert(v_state->vl == vlenb);
+            assert(v_state->vstart == 0);
+            assert(v_state->vcsr == 0);
+
+            uint64_t *vregs = v_state->datap;
+            for (int i = 0; i < 32; i++) {
+                for (int j = 0; j < vlenb; j += 8) {
+                    size_t idx = (i * vlenb + j) / 8;
+                    ((uint64_t *)signal_vvalues)[idx] = vregs[idx];
+                }
+            }
+        }
+
+        sc_ext = (void *)sc_ext + sc_ext->size;
+    }
+
+    assert(sc_ext->size == 0);
+    if (getauxval(AT_HWCAP) & COMPAT_HWCAP_ISA_V) {
+        assert(found_v);
+    }
+
     sc->sc_regs.pc += 4;
 }
 
 static void init_test(void)
 {
-    int i;
+    int i, j;
 
     callchain_root = find_callchain_root();
 
@@ -107,6 +166,19 @@ static void init_test(void)
     memset(final_fvalues, 0, 8 * 32);
     signal_fvalues = malloc(8 * 32);
     memset(signal_fvalues, 0, 8 * 32);
+
+    vlenb = __riscv_vlenb();
+    initial_vvalues = malloc(vlenb * 32);
+    memset(initial_vvalues, 0, vlenb * 32);
+    for (i = 0; i < 32 ; i++) {
+        for (j = 0; j < vlenb; j++) {
+            initial_vvalues[i * vlenb + j] = i * vlenb + j;
+        }
+    }
+    final_vvalues = malloc(vlenb * 32);
+    memset(final_vvalues, 0, vlenb * 32);
+    signal_vvalues = malloc(vlenb * 32);
+    memset(signal_vvalues, 0, vlenb * 32);
 }
 
 static void run_test(void)
@@ -179,6 +251,72 @@ static void run_test(void)
     "fld    f29, 0xe8(t0)\n\t"
     "fld    f30, 0xf0(t0)\n\t"
     "fld    f31, 0xf8(t0)\n\t"
+    /* Load initial values into vector registers */
+    "mv    t0, %[initial_vvalues]\n\t"
+    "vsetvli x0,%[vlenb],e8,m1,ta,ma\n\t"
+    "vle8.v    v0, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v1, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v2, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v3, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v4, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v5, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v6, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v7, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v8, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v9, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v10, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v11, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v12, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v13, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v14, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v15, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v16, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v17, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v18, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v19, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v20, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v21, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v22, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v23, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v24, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v25, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v26, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v27, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v28, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v29, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v30, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vle8.v    v31, (t0)\n\t"
     /* Trigger the SIGILL */
 ".global unimp_addr\n\t"
 "unimp_addr:\n\t"
@@ -251,19 +389,93 @@ static void run_test(void)
     "fsd    f29, 0xe8(t0)\n\t"
     "fsd    f30, 0xf0(t0)\n\t"
     "fsd    f31, 0xf8(t0)\n\t"
+    /* Save final values from vector registers */
+    "mv    t0, %[final_vvalues]\n\t"
+    "vse8.v    v0, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v1, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v2, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v3, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v4, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v5, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v6, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v7, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v8, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v9, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v10, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v11, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v12, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v13, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v14, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v15, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v16, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v17, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v18, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v19, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v20, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v21, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v22, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v23, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v24, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v25, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v26, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v27, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v28, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v29, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v30, (t0)\n\t"
+    "add    t0, t0, %[vlenb]\n\t"
+    "vse8.v    v31, (t0)\n\t"
     : "=m" (initial_gvalues),
       "=m" (final_gvalues),
-      "=m" (final_fvalues)
-    : "m" (initial_fvalues),
+      "=m" (final_fvalues),
+      "=m" (final_vvalues)
+    : [vlenb] "r" (vlenb),
+      "m" (initial_fvalues),
+      "m" (initial_vvalues),
       [initial_gvalues] "r" (initial_gvalues),
       [initial_fvalues] "r" (initial_fvalues),
+      [initial_vvalues] "r" (initial_vvalues),
       [final_gvalues] "r" (final_gvalues),
-      [final_fvalues] "r" (final_fvalues)
+      [final_fvalues] "r" (final_fvalues),
+      [final_vvalues] "r" (final_vvalues)
     : "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");
+      "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31",
+      "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
+      "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15",
+      "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23",
+      "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31");
 
     assert(got_signal);
 
@@ -272,7 +484,7 @@ static void run_test(void)
      * and is not a simple equality.
      */
     assert(initial_gvalues[4] == (unsigned long)initial_gvalues);
-    assert(signal_gvalues[4] == (unsigned long)initial_fvalues);
+    assert(signal_gvalues[4] == (unsigned long)initial_vvalues + 31 * vlenb);
     assert(final_gvalues[4] == (unsigned long)final_gvalues);
     initial_gvalues[4] = final_gvalues[4] = signal_gvalues[4] = 0;
 
@@ -284,6 +496,8 @@ static void run_test(void)
     assert(!memcmp(initial_gvalues, signal_gvalues, 8 * 31));
     assert(!memcmp(initial_fvalues, final_fvalues, 8 * 32));
     assert(!memcmp(initial_fvalues, signal_fvalues, 8 * 32));
+    assert(!memcmp(initial_vvalues, signal_vvalues, vlenb * 32));
+    assert(!memcmp(initial_vvalues, final_vvalues, vlenb * 32));
 }
 
 int main(void)
-- 
2.51.0



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 1/5] tests/tcg/riscv64: Add a user signal handling test
  2026-03-21 14:13 ` [PATCH v3 1/5] tests/tcg/riscv64: Add a user signal handling test Nicholas Piggin
@ 2026-03-25  3:07   ` Alistair Francis
  0 siblings, 0 replies; 13+ messages in thread
From: Alistair Francis @ 2026-03-25  3:07 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: qemu-riscv, qemu-devel, Laurent Vivier, Pierrick Bouvier,
	Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, Richard Henderson,
	Joel Stanley

On Sun, Mar 22, 2026 at 12:16 AM Nicholas Piggin <npiggin@gmail.com> wrote:
>
> Add a few basic signal handling tests for user emulation.
>
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>

Reviewed-by: Alistair Francis <alistair.francis@wdc.com>

Alistair

> ---
>  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
>
>


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 2/5] linux-user/riscv: Allow restore_sigcontext to return error
  2026-03-21 14:13 ` [PATCH v3 2/5] linux-user/riscv: Allow restore_sigcontext to return error Nicholas Piggin
@ 2026-03-25  3:07   ` Alistair Francis
  0 siblings, 0 replies; 13+ messages in thread
From: Alistair Francis @ 2026-03-25  3:07 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: qemu-riscv, qemu-devel, Laurent Vivier, Pierrick Bouvier,
	Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, Richard Henderson,
	Joel Stanley

On Sun, Mar 22, 2026 at 12:16 AM Nicholas Piggin <npiggin@gmail.com> wrote:
>
> Linux can return error from restore_sigcontext in some conditions, which
> forces a SIGSEGV. Plumb through this error handling which will be used
> by the next change.
>
> Suggested-by: Richard Henderson <richard.henderson@linaro.org>
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>

Reviewed-by: Alistair Francis <alistair.francis@wdc.com>

Alistair

> ---
>  linux-user/riscv/signal.c | 13 +++++++++----
>  1 file changed, 9 insertions(+), 4 deletions(-)
>
> diff --git a/linux-user/riscv/signal.c b/linux-user/riscv/signal.c
> index 22b1b8149f..ece276f85f 100644
> --- a/linux-user/riscv/signal.c
> +++ b/linux-user/riscv/signal.c
> @@ -145,7 +145,7 @@ badframe:
>      force_sig(TARGET_SIGSEGV);
>  }
>
> -static void restore_sigcontext(CPURISCVState *env, struct target_sigcontext *sc)
> +static bool restore_sigcontext(CPURISCVState *env, struct target_sigcontext *sc)
>  {
>      int i;
>
> @@ -161,9 +161,11 @@ static void restore_sigcontext(CPURISCVState *env, struct target_sigcontext *sc)
>      uint32_t fcsr;
>      __get_user(fcsr, &sc->fcsr);
>      riscv_csr_write(env, CSR_FCSR, fcsr);
> +
> +    return true;
>  }
>
> -static void restore_ucontext(CPURISCVState *env, struct target_ucontext *uc)
> +static bool restore_ucontext(CPURISCVState *env, struct target_ucontext *uc)
>  {
>      sigset_t blocked;
>      target_sigset_t target_set;
> @@ -177,7 +179,7 @@ static void restore_ucontext(CPURISCVState *env, struct target_ucontext *uc)
>      target_to_host_sigset_internal(&blocked, &target_set);
>      set_sigmask(&blocked);
>
> -    restore_sigcontext(env, &uc->uc_mcontext);
> +    return restore_sigcontext(env, &uc->uc_mcontext);
>  }
>
>  long do_rt_sigreturn(CPURISCVState *env)
> @@ -191,7 +193,10 @@ long do_rt_sigreturn(CPURISCVState *env)
>          goto badframe;
>      }
>
> -    restore_ucontext(env, &frame->uc);
> +    if (!restore_ucontext(env, &frame->uc)) {
> +        goto badframe;
> +    }
> +
>      target_restore_altstack(&frame->uc.uc_stack, env);
>
>      unlock_user_struct(frame, frame_addr, 0);
> --
> 2.51.0
>
>


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 3/5] linux-user/riscv: Add extended state to sigcontext
  2026-03-21 14:13 ` [PATCH v3 3/5] linux-user/riscv: Add extended state to sigcontext Nicholas Piggin
@ 2026-03-25  3:14   ` Alistair Francis
  0 siblings, 0 replies; 13+ messages in thread
From: Alistair Francis @ 2026-03-25  3:14 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: qemu-riscv, qemu-devel, Laurent Vivier, Pierrick Bouvier,
	Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, Richard Henderson,
	Joel Stanley

On Sun, Mar 22, 2026 at 12:16 AM Nicholas Piggin <npiggin@gmail.com> wrote:
>
> Linux/riscv has extended the sigcontext with padding and an
> extended state structure that can save various optional
> features like vector in a flexible format. Update the
> linux-user signal handling to this new structure.
>
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>  linux-user/riscv/signal.c         | 93 +++++++++++++++++++++++++++----
>  linux-user/riscv/vdso-asmoffset.h |  4 +-
>  2 files changed, 85 insertions(+), 12 deletions(-)
>
> diff --git a/linux-user/riscv/signal.c b/linux-user/riscv/signal.c
> index ece276f85f..e20b9ac177 100644
> --- a/linux-user/riscv/signal.c
> +++ b/linux-user/riscv/signal.c
> @@ -31,14 +31,43 @@
>
>     The code below is qemu re-implementation of arch/riscv/kernel/signal.c */
>
> -struct target_sigcontext {
> +struct target_gp_state {
>      abi_long pc;
>      abi_long gpr[31]; /* x0 is not present, so all offsets must be -1 */
> +};
> +
> +struct target_fp_state {
>      uint64_t fpr[32];
>      uint32_t fcsr;
> +};
> +
> +/* The Magic number for signal context frame header. */
> +#define END_MAGIC       0x0
> +
> +/* The size of END signal context header. */
> +#define END_HDR_SIZE    0x0
> +
> +struct target_ctx_hdr {
> +    uint32_t magic;
> +    uint32_t size;
> +};
> +
> +struct target_extra_ext_header {
> +    uint32_t __padding[129] __attribute__((aligned(16)));
> +    uint32_t reserved;
> +    struct target_ctx_hdr hdr;
> +};
> +
> +struct target_sigcontext {
> +    struct target_gp_state sc_regs;
> +    union {
> +        struct target_fp_state sc_fpregs;
> +        struct target_extra_ext_header sc_extdesc;
> +    };
>  }; /* cf. riscv-linux:arch/riscv/include/uapi/asm/ptrace.h */

I would argue that arch/riscv/include/uapi/asm/sigcontext.h is a
better pointer, but both work

Reviewed-by: Alistair Francis <alistair.francis@wdc.com>

Alistair

>
> -QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, fpr) != offsetof_freg0);
> +QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, sc_fpregs.fpr) !=
> +                  offsetof_freg0);
>
>  struct target_ucontext {
>      abi_ulong uc_flags;
> @@ -79,19 +108,26 @@ static abi_ulong get_sigframe(struct target_sigaction *ka,
>
>  static void setup_sigcontext(struct target_sigcontext *sc, CPURISCVState *env)
>  {
> +    struct target_ctx_hdr *hdr;
>      int i;
>
> -    __put_user(env->pc, &sc->pc);
> +    __put_user(env->pc, &sc->sc_regs.pc);
>
>      for (i = 1; i < 32; i++) {
> -        __put_user(env->gpr[i], &sc->gpr[i - 1]);
> +        __put_user(env->gpr[i], &sc->sc_regs.gpr[i - 1]);
>      }
>      for (i = 0; i < 32; i++) {
> -        __put_user(env->fpr[i], &sc->fpr[i]);
> +        __put_user(env->fpr[i], &sc->sc_fpregs.fpr[i]);
>      }
>
>      uint32_t fcsr = riscv_csr_read(env, CSR_FCSR);
> -    __put_user(fcsr, &sc->fcsr);
> +    __put_user(fcsr, &sc->sc_fpregs.fcsr);
> +
> +    __put_user(0, &sc->sc_extdesc.reserved);
> +
> +    hdr = &sc->sc_extdesc.hdr;
> +    __put_user(END_MAGIC, &hdr->magic);
> +    __put_user(END_HDR_SIZE, &hdr->size);
>  }
>
>  static void setup_ucontext(struct target_ucontext *uc,
> @@ -147,21 +183,58 @@ badframe:
>
>  static bool restore_sigcontext(CPURISCVState *env, struct target_sigcontext *sc)
>  {
> +    struct target_ctx_hdr *hdr;
> +    uint32_t rsv, magic, size;
>      int i;
>
> -    __get_user(env->pc, &sc->pc);
> +    __get_user(env->pc, &sc->sc_regs.pc);
>
>      for (i = 1; i < 32; ++i) {
> -        __get_user(env->gpr[i], &sc->gpr[i - 1]);
> +        __get_user(env->gpr[i], &sc->sc_regs.gpr[i - 1]);
>      }
>      for (i = 0; i < 32; ++i) {
> -        __get_user(env->fpr[i], &sc->fpr[i]);
> +        __get_user(env->fpr[i], &sc->sc_fpregs.fpr[i]);
>      }
>
>      uint32_t fcsr;
> -    __get_user(fcsr, &sc->fcsr);
> +    __get_user(fcsr, &sc->sc_fpregs.fcsr);
>      riscv_csr_write(env, CSR_FCSR, fcsr);
>
> +    hdr = &sc->sc_extdesc.hdr;
> +    __get_user(rsv, &sc->sc_extdesc.reserved);
> +    if (rsv != 0) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "signal: sigcontext reserved field is "
> +                                       "non-zero\n");
> +        return false;
> +    }
> +
> +    __get_user(magic, &hdr->magic);
> +    while (magic != END_MAGIC) {
> +        switch (magic) {
> +        default:
> +            qemu_log_mask(LOG_GUEST_ERROR, "signal: unknown extended state in "
> +                                           "sigcontext, magic=0x%08x\n", magic);
> +            return false;
> +        }
> +
> +        __get_user(size, &hdr->size);
> +        if (size == 0) {
> +            qemu_log_mask(LOG_GUEST_ERROR, "signal: extended state in "
> +                                           "sigcontext has size 0\n");
> +            return false;
> +        }
> +
> +        hdr = (void *)hdr + size;
> +        __get_user(magic, &hdr->magic);
> +    }
> +
> +    __get_user(size, &hdr->size);
> +    if (size != END_HDR_SIZE) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "signal: extended state end header has "
> +                                       "size=%u (should be 0)\n", size);
> +        return false;
> +    }
> +
>      return true;
>  }
>
> diff --git a/linux-user/riscv/vdso-asmoffset.h b/linux-user/riscv/vdso-asmoffset.h
> index 123902ef61..92e8ac10ab 100644
> --- a/linux-user/riscv/vdso-asmoffset.h
> +++ b/linux-user/riscv/vdso-asmoffset.h
> @@ -1,9 +1,9 @@
>  #ifdef TARGET_ABI32
> -# define sizeof_rt_sigframe     0x2b0
> +# define sizeof_rt_sigframe     0x3b0
>  # define offsetof_uc_mcontext   0x120
>  # define offsetof_freg0         0x80
>  #else
> -# define sizeof_rt_sigframe     0x340
> +# define sizeof_rt_sigframe     0x440
>  # define offsetof_uc_mcontext   0x130
>  # define offsetof_freg0         0x100
>  #endif
> --
> 2.51.0
>
>


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 4/5] linux-user/riscv: Add vector state to signal context
  2026-03-21 14:13 ` [PATCH v3 4/5] linux-user/riscv: Add vector state to signal context Nicholas Piggin
@ 2026-03-25  3:45   ` Alistair Francis
  0 siblings, 0 replies; 13+ messages in thread
From: Alistair Francis @ 2026-03-25  3:45 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: qemu-riscv, qemu-devel, Laurent Vivier, Pierrick Bouvier,
	Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, Richard Henderson,
	Joel Stanley

On Sun, Mar 22, 2026 at 12:16 AM Nicholas Piggin <npiggin@gmail.com> wrote:
>
> This enables vector state to be saved and restored across signals.
>
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>

Reviewed-by: Alistair Francis <alistair.francis@wdc.com>

Alistair

> ---
>  linux-user/riscv/signal.c    | 175 +++++++++++++++++++++++++++++++++--
>  target/riscv/cpu.h           |   4 +
>  target/riscv/csr.c           |   7 +-
>  target/riscv/vector_helper.c |  19 +++-
>  4 files changed, 191 insertions(+), 14 deletions(-)
>
> diff --git a/linux-user/riscv/signal.c b/linux-user/riscv/signal.c
> index e20b9ac177..2e1a1a5027 100644
> --- a/linux-user/riscv/signal.c
> +++ b/linux-user/riscv/signal.c
> @@ -41,7 +41,17 @@ struct target_fp_state {
>      uint32_t fcsr;
>  };
>
> +struct target_v_ext_state {
> +    abi_ulong vstart;
> +    abi_ulong vl;
> +    abi_ulong vtype;
> +    abi_ulong vcsr;
> +    abi_ulong vlenb;
> +    abi_ptr   datap;
> +};
> +
>  /* The Magic number for signal context frame header. */
> +#define RISCV_V_MAGIC   0x53465457
>  #define END_MAGIC       0x0
>
>  /* The size of END signal context header. */
> @@ -106,6 +116,130 @@ static abi_ulong get_sigframe(struct target_sigaction *ka,
>      return sp;
>  }
>
> +static unsigned int get_v_state_hdr_size(CPURISCVState *env)
> +{
> +    return sizeof(struct target_ctx_hdr) +
> +           sizeof(struct target_v_ext_state);
> +}
> +
> +static unsigned int get_v_state_data_size(CPURISCVState *env)
> +{
> +    RISCVCPU *cpu = env_archcpu(env);
> +    return cpu->cfg.vlenb * 32;
> +}
> +
> +static struct target_ctx_hdr *save_v_state(CPURISCVState *env,
> +                                           struct target_ctx_hdr *hdr)
> +{
> +    RISCVCPU *cpu = env_archcpu(env);
> +    target_ulong vlenb = cpu->cfg.vlenb;
> +    uint32_t riscv_v_sc_size = get_v_state_hdr_size(env) +
> +                               get_v_state_data_size(env);
> +    struct target_v_ext_state *vs;
> +    abi_ulong vcsr;
> +    abi_ptr datap;
> +    void *host_datap;
> +
> +#ifdef CONFIG_DEBUG_REMAP
> +    /*
> +     * The host pointers are derived from lock_user, not g2h, so
> +     * h2g can not be used when CONFIG_DEBUG_REMAP=y.
> +     */
> +    qemu_log_mask(LOG_UNIMP, "signal: sigcontext can not save V state "
> +                             "when CONFIG_DEBUG_REMAP=y\n");
> +    return hdr;
> +#endif
> +
> +    vs = (struct target_v_ext_state *)(hdr + 1);
> +    vcsr = riscv_csr_read(env, CSR_VCSR);
> +    host_datap = (vs + 1);
> +    datap = h2g(host_datap);
> +
> +    __put_user(RISCV_V_MAGIC, &hdr->magic);
> +    __put_user(riscv_v_sc_size, &hdr->size);
> +
> +    __put_user(env->vstart, &vs->vstart);
> +    __put_user(env->vl, &vs->vl);
> +    __put_user(env->vtype, &vs->vtype);
> +    __put_user(vcsr, &vs->vcsr);
> +    __put_user(vlenb, &vs->vlenb);
> +    __put_user(datap, &vs->datap);
> +
> +    for (int i = 0; i < 32; i++) {
> +        for (int j = 0; j < vlenb; j += 8) {
> +            size_t idx = (i * vlenb + j);
> +            __put_user(env->vreg[idx / 8],
> +                       (uint64_t *)(host_datap + idx));
> +        }
> +    }
> +
> +    return (void *)hdr + riscv_v_sc_size;
> +}
> +
> +static bool restore_v_state(CPURISCVState *env,
> +                            struct target_ctx_hdr *hdr)
> +{
> +    RISCVCPU *cpu = env_archcpu(env);
> +    target_ulong vlenb;
> +    target_ulong vcsr, vl, vtype, vstart;
> +    struct target_v_ext_state *vs;
> +    uint32_t size;
> +    abi_ptr datap;
> +    void *host_datap;
> +
> +    if (!riscv_has_ext(env, RVV)) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "signal: sigcontext has V state but "
> +                                       "CPU does not support V extension\n");
> +        return false;
> +    }
> +
> +    __get_user(size, &hdr->size);
> +    if (size < get_v_state_hdr_size(env)) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "signal: sigcontext V state header "
> +                                       "size is too small (%u)\n", size);
> +        return false;
> +    }
> +
> +    vs = (struct target_v_ext_state *)(hdr + 1);
> +
> +    __get_user(vstart, &vs->vstart);
> +    __get_user(vl, &vs->vl);
> +    __get_user(vtype, &vs->vtype);
> +    __get_user(vcsr, &vs->vcsr);
> +
> +    riscv_cpu_set_vstart(env, vstart);
> +    riscv_cpu_vsetvl(env, vl, vtype, 0);
> +    riscv_csr_write(env, CSR_VCSR, vcsr);
> +
> +    __get_user(vlenb, &vs->vlenb);
> +
> +    if (vlenb != cpu->cfg.vlenb) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "signal: sigcontext has invalid "
> +                                       "vlenb\n");
> +        return false;
> +    }
> +
> +    __get_user(datap, &vs->datap);
> +
> +    host_datap = lock_user(VERIFY_READ, datap, vlenb * 32, true);
> +    if (!host_datap) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "signal: sigcontext has V state but "
> +                                       "datap pointer is invalid\n");
> +        return false;
> +    }
> +
> +    for (int i = 0; i < 32; i++) {
> +        for (int j = 0; j < vlenb; j += 8) {
> +            size_t idx = (i * vlenb + j);
> +            __get_user(env->vreg[idx / 8],
> +                       (uint64_t *)(host_datap + idx));
> +        }
> +    }
> +    unlock_user(host_datap, datap, 0);
> +
> +    return true;
> +}
> +
>  static void setup_sigcontext(struct target_sigcontext *sc, CPURISCVState *env)
>  {
>      struct target_ctx_hdr *hdr;
> @@ -126,6 +260,9 @@ static void setup_sigcontext(struct target_sigcontext *sc, CPURISCVState *env)
>      __put_user(0, &sc->sc_extdesc.reserved);
>
>      hdr = &sc->sc_extdesc.hdr;
> +    if (riscv_has_ext(env, RVV)) {
> +        hdr = save_v_state(env, hdr);
> +    }
>      __put_user(END_MAGIC, &hdr->magic);
>      __put_user(END_HDR_SIZE, &hdr->size);
>  }
> @@ -152,17 +289,24 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
>  {
>      abi_ulong frame_addr;
>      struct target_rt_sigframe *frame;
> +    size_t frame_size = sizeof(*frame);
>
> -    frame_addr = get_sigframe(ka, env, sizeof(*frame));
> +    if (riscv_has_ext(env, RVV)) {
> +        frame_size += get_v_state_hdr_size(env) +
> +                      get_v_state_data_size(env);
> +    }
> +
> +    frame_addr = get_sigframe(ka, env, frame_size);
>      trace_user_setup_rt_frame(env, frame_addr);
>
> -    if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
> +    frame = lock_user(VERIFY_WRITE, frame_addr, frame_size, 0);
> +    if (!frame) {
>          goto badframe;
>      }
>
>      setup_ucontext(&frame->uc, env, set);
>      frame->info = *info;
> -    unlock_user_struct(frame, frame_addr, 1);
> +    unlock_user(frame, frame_addr, frame_size);
>
>      env->pc = ka->_sa_handler;
>      env->gpr[xSP] = frame_addr;
> @@ -174,7 +318,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
>      return;
>
>  badframe:
> -    unlock_user_struct(frame, frame_addr, 1);
> +    unlock_user(frame, frame_addr, frame_size);
>      if (sig == TARGET_SIGSEGV) {
>          ka->_sa_handler = TARGET_SIG_DFL;
>      }
> @@ -211,6 +355,11 @@ static bool restore_sigcontext(CPURISCVState *env, struct target_sigcontext *sc)
>      __get_user(magic, &hdr->magic);
>      while (magic != END_MAGIC) {
>          switch (magic) {
> +        case RISCV_V_MAGIC:
> +            if (!restore_v_state(env, hdr)) {
> +                return false;
> +            }
> +            break;
>          default:
>              qemu_log_mask(LOG_GUEST_ERROR, "signal: unknown extended state in "
>                                             "sigcontext, magic=0x%08x\n", magic);
> @@ -258,11 +407,23 @@ static bool restore_ucontext(CPURISCVState *env, struct target_ucontext *uc)
>  long do_rt_sigreturn(CPURISCVState *env)
>  {
>      struct target_rt_sigframe *frame;
> +    size_t frame_size = sizeof(*frame);
>      abi_ulong frame_addr;
>
> +    if (riscv_has_ext(env, RVV)) {
> +        /*
> +         * userspace may have set up a discontiguous V state data area,
> +         * so need to map that region separately once the address is
> +         * known, from datap.
> +         */
> +        frame_size += get_v_state_hdr_size(env);
> +    }
> +
>      frame_addr = env->gpr[xSP];
>      trace_user_do_sigreturn(env, frame_addr);
> -    if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
> +
> +    frame = lock_user(VERIFY_READ, frame_addr, frame_size, 1);
> +    if (!frame) {
>          goto badframe;
>      }
>
> @@ -272,11 +433,11 @@ long do_rt_sigreturn(CPURISCVState *env)
>
>      target_restore_altstack(&frame->uc.uc_stack, env);
>
> -    unlock_user_struct(frame, frame_addr, 0);
> +    unlock_user(frame, frame_addr, 0);
>      return -QEMU_ESIGRETURN;
>
>  badframe:
> -    unlock_user_struct(frame, frame_addr, 0);
> +    unlock_user(frame, frame_addr, 0);
>      force_sig(TARGET_SIGSEGV);
>      return 0;
>  }
> diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
> index 35d1f6362c..e1eca79197 100644
> --- a/target/riscv/cpu.h
> +++ b/target/riscv/cpu.h
> @@ -668,6 +668,10 @@ G_NORETURN void riscv_raise_exception(CPURISCVState *env,
>  target_ulong riscv_cpu_get_fflags(CPURISCVState *env);
>  void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong);
>
> +void riscv_cpu_set_vstart(CPURISCVState *env, target_ulong val);
> +target_ulong riscv_cpu_vsetvl(CPURISCVState *env, target_ulong s1,
> +                              target_ulong s2, target_ulong x0);
> +
>  #ifndef CONFIG_USER_ONLY
>  void cpu_set_exception_base(int vp_index, target_ulong address);
>  #endif
> diff --git a/target/riscv/csr.c b/target/riscv/csr.c
> index 5064483917..8a6fd11fb5 100644
> --- a/target/riscv/csr.c
> +++ b/target/riscv/csr.c
> @@ -991,11 +991,8 @@ static RISCVException write_vstart(CPURISCVState *env, int csrno,
>  #if !defined(CONFIG_USER_ONLY)
>      env->mstatus |= MSTATUS_VS;
>  #endif
> -    /*
> -     * The vstart CSR is defined to have only enough writable bits
> -     * to hold the largest element index, i.e. lg2(VLEN) bits.
> -     */
> -    env->vstart = val & ~(~0ULL << ctzl(riscv_cpu_cfg(env)->vlenb << 3));
> +    riscv_cpu_set_vstart(env, val);
> +
>      return RISCV_EXCP_NONE;
>  }
>
> diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c
> index caa8dd9c12..bceefe019b 100644
> --- a/target/riscv/vector_helper.c
> +++ b/target/riscv/vector_helper.c
> @@ -33,8 +33,17 @@
>  #include "vector_internals.h"
>  #include <math.h>
>
> -target_ulong HELPER(vsetvl)(CPURISCVState *env, target_ulong s1,
> -                            target_ulong s2, target_ulong x0)
> +void riscv_cpu_set_vstart(CPURISCVState *env, target_ulong val)
> +{
> +    /*
> +     * The vstart CSR is defined to have only enough writable bits
> +     * to hold the largest element index, i.e. lg2(VLEN) bits.
> +     */
> +    env->vstart = val & ~(~0ULL << ctzl(riscv_cpu_cfg(env)->vlenb << 3));
> +}
> +
> +target_ulong riscv_cpu_vsetvl(CPURISCVState *env, target_ulong s1,
> +                              target_ulong s2, target_ulong x0)
>  {
>      int vlmax, vl;
>      RISCVCPU *cpu = env_archcpu(env);
> @@ -99,6 +108,12 @@ target_ulong HELPER(vsetvl)(CPURISCVState *env, target_ulong s1,
>      return vl;
>  }
>
> +target_ulong HELPER(vsetvl)(CPURISCVState *env, target_ulong s1,
> +                            target_ulong s2, target_ulong x0)
> +{
> +    return riscv_cpu_vsetvl(env, s1, s2, x0);
> +}
> +
>  /*
>   * Get the maximum number of elements can be operated.
>   *
> --
> 2.51.0
>
>


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 5/5] tests/tcg/riscv64: Add vector state to signal test
  2026-03-21 14:13 ` [PATCH v3 5/5] tests/tcg/riscv64: Add vector state to signal test Nicholas Piggin
@ 2026-03-25  3:49   ` Alistair Francis
  0 siblings, 0 replies; 13+ messages in thread
From: Alistair Francis @ 2026-03-25  3:49 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: qemu-riscv, qemu-devel, Laurent Vivier, Pierrick Bouvier,
	Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, Richard Henderson,
	Joel Stanley

On Sun, Mar 22, 2026 at 12:16 AM Nicholas Piggin <npiggin@gmail.com> wrote:
>
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>

Reviewed-by: Alistair Francis <alistair.francis@wdc.com>

Alistair

> ---
>  tests/tcg/riscv64/Makefile.target        |   4 +-
>  tests/tcg/riscv64/test-signal-handling.c | 226 ++++++++++++++++++++++-
>  2 files changed, 222 insertions(+), 8 deletions(-)
>
> diff --git a/tests/tcg/riscv64/Makefile.target b/tests/tcg/riscv64/Makefile.target
> index f318891396..86b6889a3d 100644
> --- a/tests/tcg/riscv64/Makefile.target
> +++ b/tests/tcg/riscv64/Makefile.target
> @@ -21,5 +21,5 @@ 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
> +test-signal-handling: CFLAGS += -march=rv64gcv
> +run-test-signal-handling: QEMU_OPTS += -cpu rv64,v=on
> diff --git a/tests/tcg/riscv64/test-signal-handling.c b/tests/tcg/riscv64/test-signal-handling.c
> index c202503382..b71fa6ee87 100644
> --- a/tests/tcg/riscv64/test-signal-handling.c
> +++ b/tests/tcg/riscv64/test-signal-handling.c
> @@ -19,10 +19,27 @@
>  #include <execinfo.h>
>  #include <unistd.h>
>  #include <assert.h>
> +#include <sys/auxv.h>
> +#include <elf.h>
>  #include <sys/mman.h>
>  #include <ucontext.h>
>  #include <asm/sigcontext.h>
>
> +#ifdef __riscv_v_intrinsic
> +#include <riscv_vector.h>
> +#else
> +static inline unsigned long __riscv_vlenb(void)
> +{
> +    unsigned long vlenb;
> +    __asm__ __volatile__ ("csrr %0, vlenb" : "=r" (vlenb));
> +    return vlenb;
> +}
> +#endif
> +
> +#ifndef COMPAT_HWCAP_ISA_V
> +#define COMPAT_HWCAP_ISA_V (1 << ('V' - 'A'))
> +#endif
> +
>  /*
>   * This horrible hack seems to be required when including
>   * signal.h and asm/sigcontext.h, to prevent sigcontext
> @@ -41,6 +58,10 @@ static uint64_t *signal_gvalues;
>  static double *initial_fvalues;
>  static double *final_fvalues;
>  static double *signal_fvalues;
> +static size_t vlenb;
> +static uint8_t *initial_vvalues;
> +static uint8_t *final_vvalues;
> +static uint8_t *signal_vvalues;
>
>  extern unsigned long unimp_addr[];
>
> @@ -64,6 +85,8 @@ static void ILL_handler(int signo, siginfo_t *info, void *context)
>  {
>      ucontext_t *uc = context;
>      struct sigcontext *sc = (struct sigcontext *)&uc->uc_mcontext;
> +    struct __riscv_ctx_hdr *sc_ext = &sc->sc_extdesc.hdr;
> +    bool found_v = false;
>
>      got_signal = true;
>
> @@ -82,12 +105,48 @@ static void ILL_handler(int signo, siginfo_t *info, void *context)
>      }
>      /* Test sc->sc_fpregs.d.fcsr ? */
>
> +    assert(sc->sc_extdesc.reserved == 0);
> +    while (sc_ext->magic != END_MAGIC) {
> +        assert(sc_ext->size != 0);
> +
> +        if (sc_ext->magic == RISCV_V_MAGIC) {
> +            struct __sc_riscv_v_state *sc_v_state =
> +                        (struct __sc_riscv_v_state *)(sc_ext + 1);
> +            struct __riscv_v_ext_state *v_state = &sc_v_state->v_state;
> +
> +            found_v = true;
> +
> +            assert(getauxval(AT_HWCAP) & COMPAT_HWCAP_ISA_V);
> +
> +            assert(v_state->vlenb == vlenb);
> +            assert(v_state->vtype == 0xc0); /* vma, vta */
> +            assert(v_state->vl == vlenb);
> +            assert(v_state->vstart == 0);
> +            assert(v_state->vcsr == 0);
> +
> +            uint64_t *vregs = v_state->datap;
> +            for (int i = 0; i < 32; i++) {
> +                for (int j = 0; j < vlenb; j += 8) {
> +                    size_t idx = (i * vlenb + j) / 8;
> +                    ((uint64_t *)signal_vvalues)[idx] = vregs[idx];
> +                }
> +            }
> +        }
> +
> +        sc_ext = (void *)sc_ext + sc_ext->size;
> +    }
> +
> +    assert(sc_ext->size == 0);
> +    if (getauxval(AT_HWCAP) & COMPAT_HWCAP_ISA_V) {
> +        assert(found_v);
> +    }
> +
>      sc->sc_regs.pc += 4;
>  }
>
>  static void init_test(void)
>  {
> -    int i;
> +    int i, j;
>
>      callchain_root = find_callchain_root();
>
> @@ -107,6 +166,19 @@ static void init_test(void)
>      memset(final_fvalues, 0, 8 * 32);
>      signal_fvalues = malloc(8 * 32);
>      memset(signal_fvalues, 0, 8 * 32);
> +
> +    vlenb = __riscv_vlenb();
> +    initial_vvalues = malloc(vlenb * 32);
> +    memset(initial_vvalues, 0, vlenb * 32);
> +    for (i = 0; i < 32 ; i++) {
> +        for (j = 0; j < vlenb; j++) {
> +            initial_vvalues[i * vlenb + j] = i * vlenb + j;
> +        }
> +    }
> +    final_vvalues = malloc(vlenb * 32);
> +    memset(final_vvalues, 0, vlenb * 32);
> +    signal_vvalues = malloc(vlenb * 32);
> +    memset(signal_vvalues, 0, vlenb * 32);
>  }
>
>  static void run_test(void)
> @@ -179,6 +251,72 @@ static void run_test(void)
>      "fld    f29, 0xe8(t0)\n\t"
>      "fld    f30, 0xf0(t0)\n\t"
>      "fld    f31, 0xf8(t0)\n\t"
> +    /* Load initial values into vector registers */
> +    "mv    t0, %[initial_vvalues]\n\t"
> +    "vsetvli x0,%[vlenb],e8,m1,ta,ma\n\t"
> +    "vle8.v    v0, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v1, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v2, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v3, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v4, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v5, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v6, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v7, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v8, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v9, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v10, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v11, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v12, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v13, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v14, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v15, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v16, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v17, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v18, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v19, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v20, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v21, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v22, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v23, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v24, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v25, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v26, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v27, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v28, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v29, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v30, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vle8.v    v31, (t0)\n\t"
>      /* Trigger the SIGILL */
>  ".global unimp_addr\n\t"
>  "unimp_addr:\n\t"
> @@ -251,19 +389,93 @@ static void run_test(void)
>      "fsd    f29, 0xe8(t0)\n\t"
>      "fsd    f30, 0xf0(t0)\n\t"
>      "fsd    f31, 0xf8(t0)\n\t"
> +    /* Save final values from vector registers */
> +    "mv    t0, %[final_vvalues]\n\t"
> +    "vse8.v    v0, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v1, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v2, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v3, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v4, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v5, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v6, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v7, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v8, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v9, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v10, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v11, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v12, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v13, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v14, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v15, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v16, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v17, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v18, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v19, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v20, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v21, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v22, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v23, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v24, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v25, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v26, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v27, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v28, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v29, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v30, (t0)\n\t"
> +    "add    t0, t0, %[vlenb]\n\t"
> +    "vse8.v    v31, (t0)\n\t"
>      : "=m" (initial_gvalues),
>        "=m" (final_gvalues),
> -      "=m" (final_fvalues)
> -    : "m" (initial_fvalues),
> +      "=m" (final_fvalues),
> +      "=m" (final_vvalues)
> +    : [vlenb] "r" (vlenb),
> +      "m" (initial_fvalues),
> +      "m" (initial_vvalues),
>        [initial_gvalues] "r" (initial_gvalues),
>        [initial_fvalues] "r" (initial_fvalues),
> +      [initial_vvalues] "r" (initial_vvalues),
>        [final_gvalues] "r" (final_gvalues),
> -      [final_fvalues] "r" (final_fvalues)
> +      [final_fvalues] "r" (final_fvalues),
> +      [final_vvalues] "r" (final_vvalues)
>      : "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");
> +      "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31",
> +      "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
> +      "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15",
> +      "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23",
> +      "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31");
>
>      assert(got_signal);
>
> @@ -272,7 +484,7 @@ static void run_test(void)
>       * and is not a simple equality.
>       */
>      assert(initial_gvalues[4] == (unsigned long)initial_gvalues);
> -    assert(signal_gvalues[4] == (unsigned long)initial_fvalues);
> +    assert(signal_gvalues[4] == (unsigned long)initial_vvalues + 31 * vlenb);
>      assert(final_gvalues[4] == (unsigned long)final_gvalues);
>      initial_gvalues[4] = final_gvalues[4] = signal_gvalues[4] = 0;
>
> @@ -284,6 +496,8 @@ static void run_test(void)
>      assert(!memcmp(initial_gvalues, signal_gvalues, 8 * 31));
>      assert(!memcmp(initial_fvalues, final_fvalues, 8 * 32));
>      assert(!memcmp(initial_fvalues, signal_fvalues, 8 * 32));
> +    assert(!memcmp(initial_vvalues, signal_vvalues, vlenb * 32));
> +    assert(!memcmp(initial_vvalues, final_vvalues, vlenb * 32));
>  }
>
>  int main(void)
> --
> 2.51.0
>
>


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 0/5] linux-user/riscv: add vector state to signal context
  2026-03-21 14:13 [PATCH v3 0/5] linux-user/riscv: add vector state to signal context Nicholas Piggin
                   ` (4 preceding siblings ...)
  2026-03-21 14:13 ` [PATCH v3 5/5] tests/tcg/riscv64: Add vector state to signal test Nicholas Piggin
@ 2026-03-25  4:45 ` Alistair Francis
  2026-03-26  6:22   ` Nicholas Piggin
  5 siblings, 1 reply; 13+ messages in thread
From: Alistair Francis @ 2026-03-25  4:45 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: qemu-riscv, qemu-devel, Laurent Vivier, Pierrick Bouvier,
	Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, Richard Henderson,
	Joel Stanley

On Sun, Mar 22, 2026 at 12:16 AM Nicholas Piggin <npiggin@gmail.com> wrote:
>
> Changes in v3:
> - Fixed up a bunch of good review comments that Richard made against
>   the v1 series here https://lore.kernel.org/all/06afcbdc-28e2-4d13-81d4-26fd257ed8a3@linaro.org/#r
>   in particular:
>   - Restoring vl/vtype state using CPU accessors rather than stuffing the
>     values into the registers directly.
>   - Fix ABI types.
>   - Fix some confusion with host/guest pointer mapping.
>   - Fix error handling to SIGSEGV.
>   - Style fixes.
> - Fixed build issue without riscv intrinsics header.
> - Add some more test cases.
>
> Changes in v2:
> - Fix 32-bit compile
> - Moved sizeof_rt_sigframe change to its correct patch (patch 2 is the
>   one that extends the size of rt_sigframe, patch 3 is adding new
>   extended state to the frame which is added dynamically).
>
> This series adds vector state to the linux-user signal handler,
> and adds a basic signal handling test case. As a sanity check, I
> also verified the signal handling test works in the same way when
> run under a real Linux kernel.
>
> Thanks,
> Nick
>
> Nicholas Piggin (5):
>   tests/tcg/riscv64: Add a user signal handling test
>   linux-user/riscv: Allow restore_sigcontext to return error
>   linux-user/riscv: Add extended state to sigcontext
>   linux-user/riscv: Add vector state to signal context
>   tests/tcg/riscv64: Add vector state to signal test

Can you rebase this on the latest master branch?

Alistair

>
>  linux-user/riscv/signal.c                | 281 +++++++++++-
>  linux-user/riscv/vdso-asmoffset.h        |   4 +-
>  target/riscv/cpu.h                       |   4 +
>  target/riscv/csr.c                       |   7 +-
>  target/riscv/vector_helper.c             |  19 +-
>  tests/tcg/riscv64/Makefile.target        |   5 +
>  tests/tcg/riscv64/test-signal-handling.c | 517 +++++++++++++++++++++++
>  7 files changed, 807 insertions(+), 30 deletions(-)
>  create mode 100644 tests/tcg/riscv64/test-signal-handling.c
>
> --
> 2.51.0
>
>


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 0/5] linux-user/riscv: add vector state to signal context
  2026-03-25  4:45 ` [PATCH v3 0/5] linux-user/riscv: add vector state to signal context Alistair Francis
@ 2026-03-26  6:22   ` Nicholas Piggin
  0 siblings, 0 replies; 13+ messages in thread
From: Nicholas Piggin @ 2026-03-26  6:22 UTC (permalink / raw)
  To: Alistair Francis
  Cc: qemu-riscv, qemu-devel, Laurent Vivier, Pierrick Bouvier,
	Palmer Dabbelt, Alistair Francis, Weiwei Li,
	Daniel Henrique Barboza, Liu Zhiwei, Richard Henderson,
	Joel Stanley

On Wed, Mar 25, 2026 at 02:45:24PM +1000, Alistair Francis wrote:
> On Sun, Mar 22, 2026 at 12:16 AM Nicholas Piggin <npiggin@gmail.com> wrote:
> >
> > Changes in v3:
> > - Fixed up a bunch of good review comments that Richard made against
> >   the v1 series here https://lore.kernel.org/all/06afcbdc-28e2-4d13-81d4-26fd257ed8a3@linaro.org/#r
> >   in particular:
> >   - Restoring vl/vtype state using CPU accessors rather than stuffing the
> >     values into the registers directly.
> >   - Fix ABI types.
> >   - Fix some confusion with host/guest pointer mapping.
> >   - Fix error handling to SIGSEGV.
> >   - Style fixes.
> > - Fixed build issue without riscv intrinsics header.
> > - Add some more test cases.
> >
> > Changes in v2:
> > - Fix 32-bit compile
> > - Moved sizeof_rt_sigframe change to its correct patch (patch 2 is the
> >   one that extends the size of rt_sigframe, patch 3 is adding new
> >   extended state to the frame which is added dynamically).
> >
> > This series adds vector state to the linux-user signal handler,
> > and adds a basic signal handling test case. As a sanity check, I
> > also verified the signal handling test works in the same way when
> > run under a real Linux kernel.
> >
> > Thanks,
> > Nick
> >
> > Nicholas Piggin (5):
> >   tests/tcg/riscv64: Add a user signal handling test
> >   linux-user/riscv: Allow restore_sigcontext to return error
> >   linux-user/riscv: Add extended state to sigcontext
> >   linux-user/riscv: Add vector state to signal context
> >   tests/tcg/riscv64: Add vector state to signal test
> 
> Can you rebase this on the latest master branch?

Will do. I'll give a few more days then send out updates. Thanks
for the reviews.

Thanks,
Nick


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2026-03-26  6:23 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-21 14:13 [PATCH v3 0/5] linux-user/riscv: add vector state to signal context Nicholas Piggin
2026-03-21 14:13 ` [PATCH v3 1/5] tests/tcg/riscv64: Add a user signal handling test Nicholas Piggin
2026-03-25  3:07   ` Alistair Francis
2026-03-21 14:13 ` [PATCH v3 2/5] linux-user/riscv: Allow restore_sigcontext to return error Nicholas Piggin
2026-03-25  3:07   ` Alistair Francis
2026-03-21 14:13 ` [PATCH v3 3/5] linux-user/riscv: Add extended state to sigcontext Nicholas Piggin
2026-03-25  3:14   ` Alistair Francis
2026-03-21 14:13 ` [PATCH v3 4/5] linux-user/riscv: Add vector state to signal context Nicholas Piggin
2026-03-25  3:45   ` Alistair Francis
2026-03-21 14:13 ` [PATCH v3 5/5] tests/tcg/riscv64: Add vector state to signal test Nicholas Piggin
2026-03-25  3:49   ` Alistair Francis
2026-03-25  4:45 ` [PATCH v3 0/5] linux-user/riscv: add vector state to signal context Alistair Francis
2026-03-26  6:22   ` Nicholas Piggin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox