RISC-V: Add ptrace support for vectors
authorAndy Chiu <andy.chiu@sifive.com>
Fri, 25 Aug 2023 05:02:46 +0000 (05:02 +0000)
committerPalmer Dabbelt <palmer@rivosinc.com>
Fri, 1 Sep 2023 20:05:38 +0000 (13:05 -0700)
This patch add back the ptrace support with the following fix:
 - Define NT_RISCV_CSR and re-number NT_RISCV_VECTOR to prevent
   conflicting with gdb's NT_RISCV_CSR.
 - Use struct __riscv_v_regset_state to handle ptrace requests

Since gdb does not directly include the note description header in
Linux and has already defined NT_RISCV_CSR as 0x900, we decide to
sync with gdb and renumber NT_RISCV_VECTOR to solve and prevent future
conflicts.

Fixes: 0c59922c769a ("riscv: Add ptrace vector support")
Signed-off-by: Andy Chiu <andy.chiu@sifive.com>
Link: https://lore.kernel.org/r/20230825050248.32681-1-andy.chiu@sifive.com
[Palmer: Drop the unused "size" variable in riscv_vr_set().]
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
arch/riscv/include/uapi/asm/ptrace.h
arch/riscv/kernel/ptrace.c
include/uapi/linux/elf.h

index 2838001..575e95b 100644 (file)
@@ -103,13 +103,18 @@ struct __riscv_v_ext_state {
         * In signal handler, datap will be set a correct user stack offset
         * and vector registers will be copied to the address of datap
         * pointer.
-        *
-        * In ptrace syscall, datap will be set to zero and the vector
-        * registers will be copied to the address right after this
-        * structure.
         */
 };
 
+struct __riscv_v_regset_state {
+       unsigned long vstart;
+       unsigned long vl;
+       unsigned long vtype;
+       unsigned long vcsr;
+       unsigned long vlenb;
+       char vreg[];
+};
+
 /*
  * According to spec: The number of bits in a single vector register,
  * VLEN >= ELEN, which must be a power of 2, and must be no greater than
index 487303e..2afe460 100644 (file)
@@ -25,6 +25,9 @@ enum riscv_regset {
 #ifdef CONFIG_FPU
        REGSET_F,
 #endif
+#ifdef CONFIG_RISCV_ISA_V
+       REGSET_V,
+#endif
 };
 
 static int riscv_gpr_get(struct task_struct *target,
@@ -81,6 +84,71 @@ static int riscv_fpr_set(struct task_struct *target,
 }
 #endif
 
+#ifdef CONFIG_RISCV_ISA_V
+static int riscv_vr_get(struct task_struct *target,
+                       const struct user_regset *regset,
+                       struct membuf to)
+{
+       struct __riscv_v_ext_state *vstate = &target->thread.vstate;
+       struct __riscv_v_regset_state ptrace_vstate;
+
+       if (!riscv_v_vstate_query(task_pt_regs(target)))
+               return -EINVAL;
+
+       /*
+        * Ensure the vector registers have been saved to the memory before
+        * copying them to membuf.
+        */
+       if (target == current)
+               riscv_v_vstate_save(current, task_pt_regs(current));
+
+       ptrace_vstate.vstart = vstate->vstart;
+       ptrace_vstate.vl = vstate->vl;
+       ptrace_vstate.vtype = vstate->vtype;
+       ptrace_vstate.vcsr = vstate->vcsr;
+       ptrace_vstate.vlenb = vstate->vlenb;
+
+       /* Copy vector header from vstate. */
+       membuf_write(&to, &ptrace_vstate, sizeof(struct __riscv_v_regset_state));
+
+       /* Copy all the vector registers from vstate. */
+       return membuf_write(&to, vstate->datap, riscv_v_vsize);
+}
+
+static int riscv_vr_set(struct task_struct *target,
+                       const struct user_regset *regset,
+                       unsigned int pos, unsigned int count,
+                       const void *kbuf, const void __user *ubuf)
+{
+       int ret;
+       struct __riscv_v_ext_state *vstate = &target->thread.vstate;
+       struct __riscv_v_regset_state ptrace_vstate;
+
+       if (!riscv_v_vstate_query(task_pt_regs(target)))
+               return -EINVAL;
+
+       /* Copy rest of the vstate except datap */
+       ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ptrace_vstate, 0,
+                                sizeof(struct __riscv_v_regset_state));
+       if (unlikely(ret))
+               return ret;
+
+       if (vstate->vlenb != ptrace_vstate.vlenb)
+               return -EINVAL;
+
+       vstate->vstart = ptrace_vstate.vstart;
+       vstate->vl = ptrace_vstate.vl;
+       vstate->vtype = ptrace_vstate.vtype;
+       vstate->vcsr = ptrace_vstate.vcsr;
+
+       /* Copy all the vector registers. */
+       pos = 0;
+       ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, vstate->datap,
+                                0, riscv_v_vsize);
+       return ret;
+}
+#endif
+
 static const struct user_regset riscv_user_regset[] = {
        [REGSET_X] = {
                .core_note_type = NT_PRSTATUS,
@@ -100,6 +168,17 @@ static const struct user_regset riscv_user_regset[] = {
                .set = riscv_fpr_set,
        },
 #endif
+#ifdef CONFIG_RISCV_ISA_V
+       [REGSET_V] = {
+               .core_note_type = NT_RISCV_VECTOR,
+               .align = 16,
+               .n = ((32 * RISCV_MAX_VLENB) +
+                     sizeof(struct __riscv_v_regset_state)) / sizeof(__u32),
+               .size = sizeof(__u32),
+               .regset_get = riscv_vr_get,
+               .set = riscv_vr_set,
+       },
+#endif
 };
 
 static const struct user_regset_view riscv_user_native_view = {
index e0e1591..20e285f 100644 (file)
@@ -443,6 +443,8 @@ typedef struct elf64_shdr {
 #define NT_MIPS_DSP    0x800           /* MIPS DSP ASE registers */
 #define NT_MIPS_FP_MODE        0x801           /* MIPS floating-point mode */
 #define NT_MIPS_MSA    0x802           /* MIPS SIMD registers */
+#define NT_RISCV_CSR   0x900           /* RISC-V Control and Status Registers */
+#define NT_RISCV_VECTOR        0x901           /* RISC-V vector registers */
 #define NT_LOONGARCH_CPUCFG    0xa00   /* LoongArch CPU config registers */
 #define NT_LOONGARCH_CSR       0xa01   /* LoongArch control and status registers */
 #define NT_LOONGARCH_LSX       0xa02   /* LoongArch Loongson SIMD Extension registers */