Commit 50d11478 authored by Kyle McMartin's avatar Kyle McMartin

parisc: first pass of new-style ptrace for parisc

This is going to be super nasty to gdb if we ever try to wire up
PTRACE_(GET|SET){,FP}REGS since the format is different between
coredump and pt_regs.
Signed-off-by: default avatarKyle McMartin <kyle@mcmartin.ca>
parent feb8fa81
...@@ -329,6 +329,7 @@ struct pt_regs; /* forward declaration... */ ...@@ -329,6 +329,7 @@ struct pt_regs; /* forward declaration... */
#define ELF_PLAT_INIT(_r, load_addr) _r->gr[23] = 0 #define ELF_PLAT_INIT(_r, load_addr) _r->gr[23] = 0
#define USE_ELF_CORE_DUMP #define USE_ELF_CORE_DUMP
#define CORE_DUMP_USE_REGSET
#define ELF_EXEC_PAGESIZE 4096 #define ELF_EXEC_PAGESIZE 4096
/* This is the location that an ET_DYN program is loaded if exec'ed. Typical /* This is the location that an ET_DYN program is loaded if exec'ed. Typical
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
* Copyright (C) 2000 Matthew Wilcox <matthew@wil.cx> * Copyright (C) 2000 Matthew Wilcox <matthew@wil.cx>
* Copyright (C) 2000 David Huggins-Daines <dhd@debian.org> * Copyright (C) 2000 David Huggins-Daines <dhd@debian.org>
* Copyright (C) 2008 Helge Deller <deller@gmx.de> * Copyright (C) 2008 Helge Deller <deller@gmx.de>
* Copyright (C) 2009 Kyle McMartin <kyle@redhat.com>
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -19,6 +20,8 @@ ...@@ -19,6 +20,8 @@
#include <linux/security.h> #include <linux/security.h>
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/signal.h> #include <linux/signal.h>
#include <linux/regset.h>
#include <linux/elf.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
...@@ -109,6 +112,363 @@ void user_enable_block_step(struct task_struct *task) ...@@ -109,6 +112,363 @@ void user_enable_block_step(struct task_struct *task)
pa_psw(task)->l = 0; pa_psw(task)->l = 0;
} }
/* extra regs not saved in pt_regs, written when ejecting core */
static inline void fill_specials(unsigned long *regs)
{
int i = 0;
#define SAVE_CR(cr) regs[i++] = mfctl(cr)
SAVE_CR(22); SAVE_CR( 0);
SAVE_CR(24); SAVE_CR(25);
SAVE_CR(26); SAVE_CR(27);
SAVE_CR(28); SAVE_CR(29);
SAVE_CR(30); SAVE_CR(31);
SAVE_CR( 8); SAVE_CR( 9);
SAVE_CR(12); SAVE_CR(13);
SAVE_CR(10); SAVE_CR(15);
#undef SAVE_CR
}
/* save thread state in regset. this does extra work compared to the gr_set
* function since we save extra magic in coredumps, that we don't let
* userspace play with. (protection registers and the like.)
*/
static int gr_get(struct task_struct *tsk, const struct user_regset *regset,
unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf)
{
const struct pt_regs *regs = task_pt_regs(tsk);
unsigned long cr[16];
unsigned long *kbuf_reg = kbuf, *ubuf_reg = ubuf; /* register view */
int ret;
/* 32 gprs, %r0 ... %r31 */
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &regs->gr[0],
0, 32 * sizeof(unsigned long));
if (ret)
goto out;
/* %sr0 ... %sr7 */
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &regs->sr[0],
0, 8 * sizeof(unsigned long));
if (ret)
goto out;
/* extra magic stuff we need for coredumps
* sadly we can't just chunk through pt_regs... sigh.
*/
#define SAVE_REG(r) ({ \
if (count <= 0) \
goto out; \
if (kbuf) \
*kbuf_reg++ = (r); \
else \
ret = __put_user((r), ubuf_reg++); \
if (ret < 0) \
goto out; \
++pos, --count; \
})
SAVE_REG(regs->iaoq[0]); SAVE_REG(regs->iaoq[1]);
SAVE_REG(regs->iasq[0]); SAVE_REG(regs->iasq[1]);
SAVE_REG(regs->sar); SAVE_REG(regs->iir);
SAVE_REG(regs->isr); SAVE_REG(regs->ior);
#undef SAVE_REG
ubuf = ubuf_reg, kbuf = kbuf_reg;
fill_specials(cr);
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &cr,
0, ARRAY_SIZE(cr));
out:
return ret;
}
/* fill in struct pt_regs from a regset. */
static int gr_set(struct task_struct *tsk, 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(tsk);
const unsigned long *ubuf_reg = ubuf, *kbuf_reg = kbuf;
int ret;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &regs->gr[0],
offsetof(struct pt_regs, gr[0]), 32 * sizeof(long));
if (ret || count <= 0)
goto out;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &regs->sr[0],
0, 8 * sizeof(long));
if (ret || count <= 0)
goto out;
/* additional magics (iaoq, iasq, ior, etc.) */
#define RESTORE_REG(r) ({ \
if (count <= 0) \
goto out; \
if (kbuf) \
(r) = *kbuf_reg++; \
else { \
ret = __get_user((r), ubuf_reg++); \
if (ret < 0) \
goto out; \
} \
++pos, --count; \
})
RESTORE_REG(regs->iaoq[0]); RESTORE_REG(regs->iaoq[1]);
RESTORE_REG(regs->iasq[0]); RESTORE_REG(regs->iasq[1]);
RESTORE_REG(regs->sar); RESTORE_REG(regs->iir);
RESTORE_REG(regs->isr); RESTORE_REG(regs->ior);
#undef RESTORE_REG
ubuf = ubuf_reg, kbuf = kbuf_reg;
out:
return ret;
}
/* thankfully our floating point is sensible. */
static int fr_get(struct task_struct *tsk, const struct user_regset *regset,
unsigned int pos, unsigned int count, void *kbuf, void __user *ubuf)
{
struct pt_regs *regs = task_pt_regs(tsk);
int ret;
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &regs->fr[0],
0, ELF_NFPREG * sizeof(double));
return ret;
}
static int fr_set(struct task_struct *tsk, 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(tsk);
int ret;
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &regs->fr[0],
0, ELF_NFPREG * sizeof(double));
return ret;
}
enum pa_regset {
REGSET_GR,
REGSET_FR,
};
static const struct user_regset pa_regsets[] = {
[REGSET_GR] = {
.core_note_type = NT_PRSTATUS,
.n = ELF_NGREG,
.size = sizeof(long),
.align = sizeof(long),
.get = gr_get,
.set = gr_set,
},
[REGSET_FR] = {
.core_note_type = NT_PRFPREG,
.n = ELF_NFPREG,
.size = sizeof(double),
.align = sizeof(double),
.get = fr_get,
.set = fr_set,
},
};
static const struct user_regset_view user_parisc_native_view = {
.name = "parisc",
.e_machine = EM_PARISC,
.ei_osabi = ELFOSABI_LINUX,
.regsets = pa_regsets,
.n = ARRAY_SIZE(pa_regsets),
};
#ifdef CONFIG_COMPAT
/* unmitigated bullshit abounds */
static int gr_get_compat(struct task_struct *tsk,
const struct user_regset *regset,
unsigned int pos, unsigned int count,
void *_kbuf, void __user *_ubuf)
{
struct pt_regs *regs = task_pt_regs(tsk);
compat_ulong_t *kbuf = _kbuf;
compat_ulong_t __user *ubuf = _ubuf;
compat_ulong_t reg;
unsigned long cr[16];
int i, ret = 0;
pos /= sizeof(reg);
count /= sizeof(reg);
/* gprs */
if (kbuf)
for (i = 0; count > 0 && i < 32; count--, pos++)
*kbuf++ = (compat_ulong_t)(regs->gr[i++]);
else
for (i = 0; count > 0 && i < 32; count--, pos++) {
ret = __put_user((compat_ulong_t)regs->gr[i++], ubuf++);
if (ret < 0)
goto out;
}
/* space registers */
if (kbuf)
for (i = 0; count > 0 && i < 8; count--, pos++)
*kbuf++ = (compat_ulong_t)(regs->sr[i++]);
else
for (i = 0; count > 0 && i < 8; count--, pos++) {
ret = __put_user((compat_ulong_t)regs->sr[i++], ubuf++);
if (ret < 0)
goto out;
}
/* all the other bollocks we need in our coredump */
#define SAVE_REG(r) ({ \
if (count <= 0) \
goto out; \
if (kbuf) \
*kbuf++ = (compat_ulong_t)((r)); \
else \
ret = __put_user((compat_ulong_t)(r), ubuf++); \
if (ret < 0) \
goto out; \
++pos, --count; \
})
SAVE_REG(regs->iaoq[0]); SAVE_REG(regs->iaoq[1]);
SAVE_REG(regs->iasq[0]); SAVE_REG(regs->iasq[1]);
SAVE_REG(regs->sar); SAVE_REG(regs->iir);
SAVE_REG(regs->isr); SAVE_REG(regs->ior);
fill_specials(cr);
for (i = 0; i < 16; count--, pos++)
SAVE_REG(cr[i++]);
#undef SAVE_REG
/* that should bring us up to 64*sizeof(ulong_t || compat_ulong_t) */
_kbuf = kbuf;
_ubuf = ubuf;
pos *= sizeof(reg);
count *= sizeof(reg);
ret = user_regset_copyout_zero(&pos, &count, &_kbuf, &_ubuf,
64 * sizeof(reg), -1);
out:
return ret;
}
/* thankfully we get to avoid the %cr saving we do in _get, since
* ptrace doesn't need to touch it.
*/
static int gr_set_compat(struct task_struct *tsk,
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(tsk);
const compat_ulong_t *kbuf = _kbuf;
const compat_ulong_t __user *ubuf = _ubuf;
compat_ulong_t reg;
int ret = 0;
/* if i can't smoke and swear, i'm *fucked* */
pos /= sizeof(reg);
count /= sizeof(reg);
/* 32 gprs @ 4-bytes a piece */
if (kbuf)
for (; count > 0 && pos < 32; --count)
regs->gr[pos++] = *kbuf++;
else
for (; count > 0 && pos < 32; --count) {
ret = __get_user(reg, ubuf++);
if (ret < 0)
goto out;
regs->gr[pos++] = reg;
}
/* 8 space registers */
if (kbuf)
for (; count > 0 && pos < 8; --count)
regs->sr[pos++] = *kbuf++;
else
for (; count > 0 && pos < 8; --count) {
ret = __get_user(reg, ubuf++);
if (ret < 0)
goto out;
regs->sr[pos++] = reg;
}
/* additional magics (iaoq, iasq, ior, etc.) */
#define RESTORE_REG(r) ({ \
if (count <= 0) \
goto out; \
if (kbuf) \
(r) = *kbuf++; \
else { \
ret = __get_user(reg, ubuf++); \
if (ret < 0) \
goto out; \
(r) = reg; \
} \
++pos, --count; \
})
RESTORE_REG(regs->iaoq[0]); RESTORE_REG(regs->iaoq[1]);
RESTORE_REG(regs->iasq[0]); RESTORE_REG(regs->iasq[1]);
RESTORE_REG(regs->sar); RESTORE_REG(regs->iir);
RESTORE_REG(regs->isr); RESTORE_REG(regs->ior);
#undef RESTORE_REG
/* update our position */
_kbuf = kbuf;
_ubuf = ubuf;
out:
return ret;
}
static const struct user_regset pa_regsets_compat[] = {
[REGSET_GR] = {
.core_note_type = NT_PRSTATUS,
.n = ELF_NGREG,
.size = sizeof(compat_long_t),
.align = sizeof(compat_long_t),
.get = gr_get_compat,
.set = gr_set_compat,
},
/* no need, fpr are fortunately always 64-bit */
[REGSET_FR] = {
.core_note_type = NT_PRFPREG,
.n = ELF_NFPREG,
.size = sizeof(double),
.align = sizeof(double),
.get = fr_get,
.set = fr_set,
},
};
static const struct user_regset_view user_parisc_compat_view = {
.name = "parisc",
.e_machine = EM_PARISC,
.ei_osabi = ELFOSABI_LINUX,
.regsets = pa_regsets_compat,
.n = ARRAY_SIZE(pa_regsets_compat),
};
#endif /* CONFIG_COMPAT */
const struct user_regset_view *task_user_regset_view(struct task_struct *t)
{
#ifdef CONFIG_COMPAT
if (__is_compat_task(t))
return &user_parisc_compat_view;
#endif
return &user_parisc_native_view;
}
long arch_ptrace(struct task_struct *child, long request, long addr, long data) long arch_ptrace(struct task_struct *child, long request, long addr, long data)
{ {
unsigned long tmp; unsigned long tmp;
......
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