MIPS: Add DSP ASE regset support
authorMaciej W. Rozycki <macro@mips.com>
Tue, 15 May 2018 22:34:28 +0000 (23:34 +0100)
committerPaul Burton <paul.burton@mips.com>
Thu, 19 Jul 2018 20:47:51 +0000 (13:47 -0700)
Define an NT_MIPS_DSP core file note type and implement a corresponding
regset holding the DSP ASE register context, following the layout of the
`mips_dsp_state' structure, except for the DSPControl register stored as
a 64-bit rather than 32-bit quantity in a 64-bit note.

The lack of DSP ASE register saving to core files can be considered a
design flaw with commit e50c0a8fa60d ("Support the MIPS32 / MIPS64 DSP
ASE."), leading to an incomplete state being saved.  Consequently no DSP
ASE regset has been created with commit 7aeb753b5353 ("MIPS: Implement
task_user_regset_view."), when regset support was added to the MIPS
port.

Additionally there is no way for ptrace(2) to correctly access the DSP
accumulator registers in n32 processes with the existing interfaces.
This is due to 32-bit truncation of data passed with PTRACE_PEEKUSR and
PTRACE_POKEUSR requests, which cannot be avoided owing to how the data
types for ptrace(3) have been defined.  This new NT_MIPS_DSP regset
fills the missing interface gap.

[paul.burton@mips.com:
  - Change NT_MIPS_DSP to 0x800 to avoid conflict with NT_VMCOREDD
    introduced by commit 2724273e8fd0 ("vmcore: add API to collect
    hardware dump in second kernel").
  - Drop stable tag. Whilst I agree the lack of this functionality can
    be considered a flaw in earlier DSP ASE support, it's still new
    functionality which doesn't meet up to the requirements set out in
    Documentation/process/stable-kernel-rules.rst.]

Signed-off-by: Maciej W. Rozycki <macro@mips.com>
Signed-off-by: Paul Burton <paul.burton@mips.com>
References: 7aeb753b5353 ("MIPS: Implement task_user_regset_view.")
Patchwork: https://patchwork.linux-mips.org/patch/19330/
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: James Hogan <jhogan@kernel.org>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: linux-fsdevel@vger.kernel.org
Cc: linux-mips@linux-mips.org
Cc: linux-kernel@vger.kernel.org
arch/mips/kernel/ptrace.c
include/uapi/linux/elf.h

index 8c8d42823bdaf1f5cd5565e3d6f31a2fc784ff7d..a536271ba08458f3c03c7f49bd2372a01058841e 100644 (file)
@@ -41,6 +41,7 @@
 #include <asm/mipsmtregs.h>
 #include <asm/pgtable.h>
 #include <asm/page.h>
+#include <asm/processor.h>
 #include <asm/syscall.h>
 #include <linux/uaccess.h>
 #include <asm/bootinfo.h>
@@ -589,9 +590,179 @@ static int fpr_set(struct task_struct *target,
        return err;
 }
 
+#if defined(CONFIG_32BIT) || defined(CONFIG_MIPS32_O32)
+
+/*
+ * Copy the DSP context to the supplied 32-bit NT_MIPS_DSP buffer.
+ */
+static int dsp32_get(struct task_struct *target,
+                    const struct user_regset *regset,
+                    unsigned int pos, unsigned int count,
+                    void *kbuf, void __user *ubuf)
+{
+       unsigned int start, num_regs, i;
+       u32 dspregs[NUM_DSP_REGS + 1];
+
+       BUG_ON(count % sizeof(u32));
+
+       if (!cpu_has_dsp)
+               return -EIO;
+
+       start = pos / sizeof(u32);
+       num_regs = count / sizeof(u32);
+
+       if (start + num_regs > NUM_DSP_REGS + 1)
+               return -EIO;
+
+       for (i = start; i < num_regs; i++)
+               switch (i) {
+               case 0 ... NUM_DSP_REGS - 1:
+                       dspregs[i] = target->thread.dsp.dspr[i];
+                       break;
+               case NUM_DSP_REGS:
+                       dspregs[i] = target->thread.dsp.dspcontrol;
+                       break;
+               }
+       return user_regset_copyout(&pos, &count, &kbuf, &ubuf, dspregs, 0,
+                                  sizeof(dspregs));
+}
+
+/*
+ * Copy the supplied 32-bit NT_MIPS_DSP buffer to the DSP context.
+ */
+static int dsp32_set(struct task_struct *target,
+                    const struct user_regset *regset,
+                    unsigned int pos, unsigned int count,
+                    const void *kbuf, const void __user *ubuf)
+{
+       unsigned int start, num_regs, i;
+       u32 dspregs[NUM_DSP_REGS + 1];
+       int err;
+
+       BUG_ON(count % sizeof(u32));
+
+       if (!cpu_has_dsp)
+               return -EIO;
+
+       start = pos / sizeof(u32);
+       num_regs = count / sizeof(u32);
+
+       if (start + num_regs > NUM_DSP_REGS + 1)
+               return -EIO;
+
+       err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, dspregs, 0,
+                                sizeof(dspregs));
+       if (err)
+               return err;
+
+       for (i = start; i < num_regs; i++)
+               switch (i) {
+               case 0 ... NUM_DSP_REGS - 1:
+                       target->thread.dsp.dspr[i] = (s32)dspregs[i];
+                       break;
+               case NUM_DSP_REGS:
+                       target->thread.dsp.dspcontrol = (s32)dspregs[i];
+                       break;
+               }
+
+       return 0;
+}
+
+#endif /* CONFIG_32BIT || CONFIG_MIPS32_O32 */
+
+#ifdef CONFIG_64BIT
+
+/*
+ * Copy the DSP context to the supplied 64-bit NT_MIPS_DSP buffer.
+ */
+static int dsp64_get(struct task_struct *target,
+                    const struct user_regset *regset,
+                    unsigned int pos, unsigned int count,
+                    void *kbuf, void __user *ubuf)
+{
+       unsigned int start, num_regs, i;
+       u64 dspregs[NUM_DSP_REGS + 1];
+
+       BUG_ON(count % sizeof(u64));
+
+       if (!cpu_has_dsp)
+               return -EIO;
+
+       start = pos / sizeof(u64);
+       num_regs = count / sizeof(u64);
+
+       if (start + num_regs > NUM_DSP_REGS + 1)
+               return -EIO;
+
+       for (i = start; i < num_regs; i++)
+               switch (i) {
+               case 0 ... NUM_DSP_REGS - 1:
+                       dspregs[i] = target->thread.dsp.dspr[i];
+                       break;
+               case NUM_DSP_REGS:
+                       dspregs[i] = target->thread.dsp.dspcontrol;
+                       break;
+               }
+       return user_regset_copyout(&pos, &count, &kbuf, &ubuf, dspregs, 0,
+                                  sizeof(dspregs));
+}
+
+/*
+ * Copy the supplied 64-bit NT_MIPS_DSP buffer to the DSP context.
+ */
+static int dsp64_set(struct task_struct *target,
+                    const struct user_regset *regset,
+                    unsigned int pos, unsigned int count,
+                    const void *kbuf, const void __user *ubuf)
+{
+       unsigned int start, num_regs, i;
+       u64 dspregs[NUM_DSP_REGS + 1];
+       int err;
+
+       BUG_ON(count % sizeof(u64));
+
+       if (!cpu_has_dsp)
+               return -EIO;
+
+       start = pos / sizeof(u64);
+       num_regs = count / sizeof(u64);
+
+       if (start + num_regs > NUM_DSP_REGS + 1)
+               return -EIO;
+
+       err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, dspregs, 0,
+                                sizeof(dspregs));
+       if (err)
+               return err;
+
+       for (i = start; i < num_regs; i++)
+               switch (i) {
+               case 0 ... NUM_DSP_REGS - 1:
+                       target->thread.dsp.dspr[i] = dspregs[i];
+                       break;
+               case NUM_DSP_REGS:
+                       target->thread.dsp.dspcontrol = dspregs[i];
+                       break;
+               }
+
+       return 0;
+}
+
+#endif /* CONFIG_64BIT */
+
+/*
+ * Determine whether the DSP context is present.
+ */
+static int dsp_active(struct task_struct *target,
+                     const struct user_regset *regset)
+{
+       return cpu_has_dsp ? NUM_DSP_REGS + 1 : -ENODEV;
+}
+
 enum mips_regset {
        REGSET_GPR,
        REGSET_FPR,
+       REGSET_DSP,
 };
 
 struct pt_regs_offset {
@@ -697,6 +868,15 @@ static const struct user_regset mips_regsets[] = {
                .get            = fpr_get,
                .set            = fpr_set,
        },
+       [REGSET_DSP] = {
+               .core_note_type = NT_MIPS_DSP,
+               .n              = NUM_DSP_REGS + 1,
+               .size           = sizeof(u32),
+               .align          = sizeof(u32),
+               .get            = dsp32_get,
+               .set            = dsp32_set,
+               .active         = dsp_active,
+       },
 };
 
 static const struct user_regset_view user_mips_view = {
@@ -728,6 +908,15 @@ static const struct user_regset mips64_regsets[] = {
                .get            = fpr_get,
                .set            = fpr_set,
        },
+       [REGSET_DSP] = {
+               .core_note_type = NT_MIPS_DSP,
+               .n              = NUM_DSP_REGS + 1,
+               .size           = sizeof(u64),
+               .align          = sizeof(u64),
+               .get            = dsp64_get,
+               .set            = dsp64_set,
+               .active         = dsp_active,
+       },
 };
 
 static const struct user_regset_view user_mips64_view = {
index 4e12c423b9fe9352df063f14c447858cf6e774e9..e326c99b38816bfb5c6562fb658126ce5d972838 100644 (file)
@@ -422,6 +422,7 @@ typedef struct elf64_shdr {
 #define NT_ARM_SVE     0x405           /* ARM Scalable Vector Extension registers */
 #define NT_ARC_V2      0x600           /* ARCv2 accumulator/extra registers */
 #define NT_VMCOREDD    0x700           /* Vmcore Device Dump Note */
+#define NT_MIPS_DSP    0x800           /* MIPS DSP ASE registers */
 
 /* Note header in a PT_NOTE section */
 typedef struct elf32_note {