Skip to content

Commit

Permalink
xtensa: implement task_user_regset_view
Browse files Browse the repository at this point in the history
- define struct user_pt_regs in the arch/xtensa/include/uapi/asm/ptrace.h
  with the same layout as xtensa_gregset_t; make xtensa_gregset_t a
  typedef;
- define REGSET_GPR regset, implement register get and set functions;
- define task_user_regset_view function and expose REGSET_GPR.

Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
  • Loading branch information
Max Filippov committed Dec 17, 2018
1 parent 3aee3e2 commit 06fbac8
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 80 deletions.
14 changes: 1 addition & 13 deletions arch/xtensa/include/asm/elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,7 @@

typedef unsigned long elf_greg_t;

typedef struct {
elf_greg_t pc;
elf_greg_t ps;
elf_greg_t lbeg;
elf_greg_t lend;
elf_greg_t lcount;
elf_greg_t sar;
elf_greg_t windowstart;
elf_greg_t windowbase;
elf_greg_t threadptr;
elf_greg_t reserved[7+48];
elf_greg_t a[64];
} xtensa_gregset_t;
typedef struct user_pt_regs xtensa_gregset_t;

#define ELF_NGREG (sizeof(xtensa_gregset_t) / sizeof(elf_greg_t))

Expand Down
20 changes: 19 additions & 1 deletion arch/xtensa/include/uapi/asm/ptrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#ifndef _UAPI_XTENSA_PTRACE_H
#define _UAPI_XTENSA_PTRACE_H

#include <linux/types.h>

/* Registers used by strace */

#define REG_A_BASE 0x0000
Expand All @@ -36,5 +38,21 @@
#define PTRACE_GETHBPREGS 20
#define PTRACE_SETHBPREGS 21


#ifndef __ASSEMBLY__

struct user_pt_regs {
__u32 pc;
__u32 ps;
__u32 lbeg;
__u32 lend;
__u32 lcount;
__u32 sar;
__u32 windowstart;
__u32 windowbase;
__u32 threadptr;
__u32 reserved[7 + 48];
__u32 a[64];
};

#endif
#endif /* _UAPI_XTENSA_PTRACE_H */
175 changes: 109 additions & 66 deletions arch/xtensa/kernel/ptrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <linux/mm.h>
#include <linux/perf_event.h>
#include <linux/ptrace.h>
#include <linux/regset.h>
#include <linux/sched.h>
#include <linux/sched/task_stack.h>
#include <linux/security.h>
Expand All @@ -32,6 +33,110 @@
#include <asm/pgtable.h>
#include <asm/ptrace.h>

static int gpr_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);
struct user_pt_regs newregs = {
.pc = regs->pc,
.ps = regs->ps & ~(1 << PS_EXCM_BIT),
.lbeg = regs->lbeg,
.lend = regs->lend,
.lcount = regs->lcount,
.sar = regs->sar,
.threadptr = regs->threadptr,
.windowbase = regs->windowbase,
.windowstart = regs->windowstart,
};

memcpy(newregs.a,
regs->areg + XCHAL_NUM_AREGS - regs->windowbase * 4,
regs->windowbase * 16);
memcpy(newregs.a + regs->windowbase * 4,
regs->areg,
(WSBITS - regs->windowbase) * 16);

return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
&newregs, 0, -1);
}

static int gpr_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 user_pt_regs newregs = {0};
struct pt_regs *regs;
const u32 ps_mask = PS_CALLINC_MASK | PS_OWB_MASK;

ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &newregs, 0, -1);
if (ret)
return ret;

if (newregs.windowbase >= XCHAL_NUM_AREGS / 4)
return -EINVAL;

regs = task_pt_regs(target);
regs->pc = newregs.pc;
regs->ps = (regs->ps & ~ps_mask) | (newregs.ps & ps_mask);
regs->lbeg = newregs.lbeg;
regs->lend = newregs.lend;
regs->lcount = newregs.lcount;
regs->sar = newregs.sar;
regs->threadptr = newregs.threadptr;

if (newregs.windowbase != regs->windowbase ||
newregs.windowstart != regs->windowstart) {
u32 rotws, wmask;

rotws = (((newregs.windowstart |
(newregs.windowstart << WSBITS)) >>
newregs.windowbase) &
((1 << WSBITS) - 1)) & ~1;
wmask = ((rotws ? WSBITS + 1 - ffs(rotws) : 0) << 4) |
(rotws & 0xF) | 1;
regs->windowbase = newregs.windowbase;
regs->windowstart = newregs.windowstart;
regs->wmask = wmask;
}

memcpy(regs->areg + XCHAL_NUM_AREGS - newregs.windowbase * 4,
newregs.a, newregs.windowbase * 16);
memcpy(regs->areg, newregs.a + newregs.windowbase * 4,
(WSBITS - newregs.windowbase) * 16);

return 0;
}

enum xtensa_regset {
REGSET_GPR,
};

static const struct user_regset xtensa_regsets[] = {
[REGSET_GPR] = {
.core_note_type = NT_PRSTATUS,
.n = sizeof(struct user_pt_regs) / sizeof(u32),
.size = sizeof(u32),
.align = sizeof(u32),
.get = gpr_get,
.set = gpr_set
},
};

static const struct user_regset_view user_xtensa_view = {
.name = "xtensa",
.e_machine = EM_XTENSA,
.regsets = xtensa_regsets,
.n = ARRAY_SIZE(xtensa_regsets)
};

const struct user_regset_view *task_user_regset_view(struct task_struct *task)
{
return &user_xtensa_view;
}

void user_enable_single_step(struct task_struct *child)
{
Expand All @@ -54,76 +159,14 @@ void ptrace_disable(struct task_struct *child)

static int ptrace_getregs(struct task_struct *child, void __user *uregs)
{
struct pt_regs *regs = task_pt_regs(child);
xtensa_gregset_t __user *gregset = uregs;
unsigned long wb = regs->windowbase;
int i;

if (!access_ok(VERIFY_WRITE, uregs, sizeof(xtensa_gregset_t)))
return -EIO;

__put_user(regs->pc, &gregset->pc);
__put_user(regs->ps & ~(1 << PS_EXCM_BIT), &gregset->ps);
__put_user(regs->lbeg, &gregset->lbeg);
__put_user(regs->lend, &gregset->lend);
__put_user(regs->lcount, &gregset->lcount);
__put_user(regs->windowstart, &gregset->windowstart);
__put_user(regs->windowbase, &gregset->windowbase);
__put_user(regs->threadptr, &gregset->threadptr);

for (i = 0; i < XCHAL_NUM_AREGS; i++)
__put_user(regs->areg[i],
gregset->a + ((wb * 4 + i) % XCHAL_NUM_AREGS));

return 0;
return copy_regset_to_user(child, &user_xtensa_view, REGSET_GPR,
0, sizeof(xtensa_gregset_t), uregs);
}

static int ptrace_setregs(struct task_struct *child, void __user *uregs)
{
struct pt_regs *regs = task_pt_regs(child);
xtensa_gregset_t *gregset = uregs;
const unsigned long ps_mask = PS_CALLINC_MASK | PS_OWB_MASK;
unsigned long ps;
unsigned long wb, ws;

if (!access_ok(VERIFY_WRITE, uregs, sizeof(xtensa_gregset_t)))
return -EIO;

__get_user(regs->pc, &gregset->pc);
__get_user(ps, &gregset->ps);
__get_user(regs->lbeg, &gregset->lbeg);
__get_user(regs->lend, &gregset->lend);
__get_user(regs->lcount, &gregset->lcount);
__get_user(ws, &gregset->windowstart);
__get_user(wb, &gregset->windowbase);
__get_user(regs->threadptr, &gregset->threadptr);

regs->ps = (regs->ps & ~ps_mask) | (ps & ps_mask) | (1 << PS_EXCM_BIT);

if (wb >= XCHAL_NUM_AREGS / 4)
return -EFAULT;

if (wb != regs->windowbase || ws != regs->windowstart) {
unsigned long rotws, wmask;

rotws = (((ws | (ws << WSBITS)) >> wb) &
((1 << WSBITS) - 1)) & ~1;
wmask = ((rotws ? WSBITS + 1 - ffs(rotws) : 0) << 4) |
(rotws & 0xF) | 1;
regs->windowbase = wb;
regs->windowstart = ws;
regs->wmask = wmask;
}

if (wb != 0 && __copy_from_user(regs->areg + XCHAL_NUM_AREGS - wb * 4,
gregset->a, wb * 16))
return -EFAULT;

if (__copy_from_user(regs->areg, gregset->a + wb * 4,
(WSBITS - wb) * 16))
return -EFAULT;

return 0;
return copy_regset_from_user(child, &user_xtensa_view, REGSET_GPR,
0, sizeof(xtensa_gregset_t), uregs);
}


Expand Down

0 comments on commit 06fbac8

Please sign in to comment.