Commit c23b3d1a authored by Alex Smith's avatar Alex Smith Committed by Ralf Baechle

MIPS: ptrace: Change GP regset to use correct core dump register layout

Commit 6a9c001b ("MIPS: Switch ELF core dumper to use regsets.")
switched the core dumper to use regsets, however the GP regset code
simply makes a direct copy of the kernel's pt_regs, which does not
match the original core dump register layout as defined in asm/reg.h.
Furthermore, the definition of pt_regs can vary with certain Kconfig
variables, therefore the GP regset can never be relied upon to return
registers in the same layout.

Therefore, this patch changes the GP regset to match the original core
dump layout. The layout differs for 32- and 64-bit processes, so
separate implementations of the get/set functions are added for the
32- and 64-bit regsets.
Signed-off-by: default avatarAlex Smith <alex@alex-smith.me.uk>
Cc: <stable@vger.kernel.org> # v3.13+
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/7452/Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent bcec7c8d
...@@ -246,36 +246,160 @@ int ptrace_set_watch_regs(struct task_struct *child, ...@@ -246,36 +246,160 @@ int ptrace_set_watch_regs(struct task_struct *child,
/* regset get/set implementations */ /* regset get/set implementations */
static int gpr_get(struct task_struct *target, #if defined(CONFIG_32BIT) || defined(CONFIG_MIPS32_O32)
static int gpr32_get(struct task_struct *target,
const struct user_regset *regset, const struct user_regset *regset,
unsigned int pos, unsigned int count, unsigned int pos, unsigned int count,
void *kbuf, void __user *ubuf) void *kbuf, void __user *ubuf)
{ {
struct pt_regs *regs = task_pt_regs(target); struct pt_regs *regs = task_pt_regs(target);
u32 uregs[ELF_NGREG] = {};
unsigned i;
return user_regset_copyout(&pos, &count, &kbuf, &ubuf, for (i = MIPS32_EF_R1; i <= MIPS32_EF_R31; i++) {
regs, 0, sizeof(*regs)); /* k0/k1 are copied as zero. */
if (i == MIPS32_EF_R26 || i == MIPS32_EF_R27)
continue;
uregs[i] = regs->regs[i - MIPS32_EF_R0];
}
uregs[MIPS32_EF_LO] = regs->lo;
uregs[MIPS32_EF_HI] = regs->hi;
uregs[MIPS32_EF_CP0_EPC] = regs->cp0_epc;
uregs[MIPS32_EF_CP0_BADVADDR] = regs->cp0_badvaddr;
uregs[MIPS32_EF_CP0_STATUS] = regs->cp0_status;
uregs[MIPS32_EF_CP0_CAUSE] = regs->cp0_cause;
return user_regset_copyout(&pos, &count, &kbuf, &ubuf, uregs, 0,
sizeof(uregs));
} }
static int gpr_set(struct task_struct *target, static int gpr32_set(struct task_struct *target,
const struct user_regset *regset, const struct user_regset *regset,
unsigned int pos, unsigned int count, unsigned int pos, unsigned int count,
const void *kbuf, const void __user *ubuf) const void *kbuf, const void __user *ubuf)
{ {
struct pt_regs newregs; struct pt_regs *regs = task_pt_regs(target);
int ret; u32 uregs[ELF_NGREG];
unsigned start, num_regs, i;
int err;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, start = pos / sizeof(u32);
&newregs, num_regs = count / sizeof(u32);
0, sizeof(newregs));
if (ret) if (start + num_regs > ELF_NGREG)
return ret; return -EIO;
err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, uregs, 0,
sizeof(uregs));
if (err)
return err;
for (i = start; i < num_regs; i++) {
/*
* Cast all values to signed here so that if this is a 64-bit
* kernel, the supplied 32-bit values will be sign extended.
*/
switch (i) {
case MIPS32_EF_R1 ... MIPS32_EF_R25:
/* k0/k1 are ignored. */
case MIPS32_EF_R28 ... MIPS32_EF_R31:
regs->regs[i - MIPS32_EF_R0] = (s32)uregs[i];
break;
case MIPS32_EF_LO:
regs->lo = (s32)uregs[i];
break;
case MIPS32_EF_HI:
regs->hi = (s32)uregs[i];
break;
case MIPS32_EF_CP0_EPC:
regs->cp0_epc = (s32)uregs[i];
break;
}
}
return 0;
}
#endif /* CONFIG_32BIT || CONFIG_MIPS32_O32 */
#ifdef CONFIG_64BIT
static int gpr64_get(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
void *kbuf, void __user *ubuf)
{
struct pt_regs *regs = task_pt_regs(target);
u64 uregs[ELF_NGREG] = {};
unsigned i;
*task_pt_regs(target) = newregs; for (i = MIPS64_EF_R1; i <= MIPS64_EF_R31; i++) {
/* k0/k1 are copied as zero. */
if (i == MIPS64_EF_R26 || i == MIPS64_EF_R27)
continue;
uregs[i] = regs->regs[i - MIPS64_EF_R0];
}
uregs[MIPS64_EF_LO] = regs->lo;
uregs[MIPS64_EF_HI] = regs->hi;
uregs[MIPS64_EF_CP0_EPC] = regs->cp0_epc;
uregs[MIPS64_EF_CP0_BADVADDR] = regs->cp0_badvaddr;
uregs[MIPS64_EF_CP0_STATUS] = regs->cp0_status;
uregs[MIPS64_EF_CP0_CAUSE] = regs->cp0_cause;
return user_regset_copyout(&pos, &count, &kbuf, &ubuf, uregs, 0,
sizeof(uregs));
}
static int gpr64_set(struct task_struct *target,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
const void *kbuf, const void __user *ubuf)
{
struct pt_regs *regs = task_pt_regs(target);
u64 uregs[ELF_NGREG];
unsigned start, num_regs, i;
int err;
start = pos / sizeof(u64);
num_regs = count / sizeof(u64);
if (start + num_regs > ELF_NGREG)
return -EIO;
err = user_regset_copyin(&pos, &count, &kbuf, &ubuf, uregs, 0,
sizeof(uregs));
if (err)
return err;
for (i = start; i < num_regs; i++) {
switch (i) {
case MIPS64_EF_R1 ... MIPS64_EF_R25:
/* k0/k1 are ignored. */
case MIPS64_EF_R28 ... MIPS64_EF_R31:
regs->regs[i - MIPS64_EF_R0] = uregs[i];
break;
case MIPS64_EF_LO:
regs->lo = uregs[i];
break;
case MIPS64_EF_HI:
regs->hi = uregs[i];
break;
case MIPS64_EF_CP0_EPC:
regs->cp0_epc = uregs[i];
break;
}
}
return 0; return 0;
} }
#endif /* CONFIG_64BIT */
static int fpr_get(struct task_struct *target, static int fpr_get(struct task_struct *target,
const struct user_regset *regset, const struct user_regset *regset,
unsigned int pos, unsigned int count, unsigned int pos, unsigned int count,
...@@ -337,14 +461,16 @@ enum mips_regset { ...@@ -337,14 +461,16 @@ enum mips_regset {
REGSET_FPR, REGSET_FPR,
}; };
#if defined(CONFIG_32BIT) || defined(CONFIG_MIPS32_O32)
static const struct user_regset mips_regsets[] = { static const struct user_regset mips_regsets[] = {
[REGSET_GPR] = { [REGSET_GPR] = {
.core_note_type = NT_PRSTATUS, .core_note_type = NT_PRSTATUS,
.n = ELF_NGREG, .n = ELF_NGREG,
.size = sizeof(unsigned int), .size = sizeof(unsigned int),
.align = sizeof(unsigned int), .align = sizeof(unsigned int),
.get = gpr_get, .get = gpr32_get,
.set = gpr_set, .set = gpr32_set,
}, },
[REGSET_FPR] = { [REGSET_FPR] = {
.core_note_type = NT_PRFPREG, .core_note_type = NT_PRFPREG,
...@@ -364,14 +490,18 @@ static const struct user_regset_view user_mips_view = { ...@@ -364,14 +490,18 @@ static const struct user_regset_view user_mips_view = {
.n = ARRAY_SIZE(mips_regsets), .n = ARRAY_SIZE(mips_regsets),
}; };
#endif /* CONFIG_32BIT || CONFIG_MIPS32_O32 */
#ifdef CONFIG_64BIT
static const struct user_regset mips64_regsets[] = { static const struct user_regset mips64_regsets[] = {
[REGSET_GPR] = { [REGSET_GPR] = {
.core_note_type = NT_PRSTATUS, .core_note_type = NT_PRSTATUS,
.n = ELF_NGREG, .n = ELF_NGREG,
.size = sizeof(unsigned long), .size = sizeof(unsigned long),
.align = sizeof(unsigned long), .align = sizeof(unsigned long),
.get = gpr_get, .get = gpr64_get,
.set = gpr_set, .set = gpr64_set,
}, },
[REGSET_FPR] = { [REGSET_FPR] = {
.core_note_type = NT_PRFPREG, .core_note_type = NT_PRFPREG,
...@@ -384,25 +514,26 @@ static const struct user_regset mips64_regsets[] = { ...@@ -384,25 +514,26 @@ static const struct user_regset mips64_regsets[] = {
}; };
static const struct user_regset_view user_mips64_view = { static const struct user_regset_view user_mips64_view = {
.name = "mips", .name = "mips64",
.e_machine = ELF_ARCH, .e_machine = ELF_ARCH,
.ei_osabi = ELF_OSABI, .ei_osabi = ELF_OSABI,
.regsets = mips64_regsets, .regsets = mips64_regsets,
.n = ARRAY_SIZE(mips_regsets), .n = ARRAY_SIZE(mips64_regsets),
}; };
#endif /* CONFIG_64BIT */
const struct user_regset_view *task_user_regset_view(struct task_struct *task) const struct user_regset_view *task_user_regset_view(struct task_struct *task)
{ {
#ifdef CONFIG_32BIT #ifdef CONFIG_32BIT
return &user_mips_view; return &user_mips_view;
#endif #else
#ifdef CONFIG_MIPS32_O32 #ifdef CONFIG_MIPS32_O32
if (test_tsk_thread_flag(task, TIF_32BIT_REGS)) if (test_tsk_thread_flag(task, TIF_32BIT_REGS))
return &user_mips_view; return &user_mips_view;
#endif #endif
return &user_mips64_view; return &user_mips64_view;
#endif
} }
long arch_ptrace(struct task_struct *child, long request, long arch_ptrace(struct task_struct *child, long request,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment