|
|
|
@ -1,6 +1,6 @@ |
|
|
|
|
/* ptrace.c: Sparc process tracing support.
|
|
|
|
|
* |
|
|
|
|
* Copyright (C) 1996 David S. Miller (davem@caipfs.rutgers.edu) |
|
|
|
|
* Copyright (C) 1996, 2008 David S. Miller (davem@davemloft.net) |
|
|
|
|
* Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) |
|
|
|
|
* |
|
|
|
|
* Based upon code written by Ross Biro, Linus Torvalds, Bob Manson, |
|
|
|
@ -22,6 +22,9 @@ |
|
|
|
|
#include <linux/seccomp.h> |
|
|
|
|
#include <linux/audit.h> |
|
|
|
|
#include <linux/signal.h> |
|
|
|
|
#include <linux/regset.h> |
|
|
|
|
#include <linux/compat.h> |
|
|
|
|
#include <linux/elf.h> |
|
|
|
|
|
|
|
|
|
#include <asm/asi.h> |
|
|
|
|
#include <asm/pgtable.h> |
|
|
|
@ -167,6 +170,583 @@ void flush_ptrace_access(struct vm_area_struct *vma, struct page *page, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
enum sparc_regset { |
|
|
|
|
REGSET_GENERAL, |
|
|
|
|
REGSET_FP, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static int genregs64_get(struct task_struct *target, |
|
|
|
|
const struct user_regset *regset, |
|
|
|
|
unsigned int pos, unsigned int count, |
|
|
|
|
void *kbuf, void __user *ubuf) |
|
|
|
|
{ |
|
|
|
|
const struct pt_regs *regs = task_pt_regs(target); |
|
|
|
|
int ret; |
|
|
|
|
|
|
|
|
|
if (target == current) |
|
|
|
|
flushw_user(); |
|
|
|
|
|
|
|
|
|
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
regs->u_regs, |
|
|
|
|
0, 16 * sizeof(u64)); |
|
|
|
|
if (!ret) { |
|
|
|
|
unsigned long __user *reg_window = (unsigned long __user *) |
|
|
|
|
(regs->u_regs[UREG_I6] + STACK_BIAS); |
|
|
|
|
unsigned long window[16]; |
|
|
|
|
|
|
|
|
|
if (copy_from_user(window, reg_window, sizeof(window))) |
|
|
|
|
return -EFAULT; |
|
|
|
|
|
|
|
|
|
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
window, |
|
|
|
|
16 * sizeof(u64), |
|
|
|
|
32 * sizeof(u64)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!ret) { |
|
|
|
|
/* TSTATE, TPC, TNPC */ |
|
|
|
|
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
®s->tstate, |
|
|
|
|
32 * sizeof(u64), |
|
|
|
|
35 * sizeof(u64)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!ret) { |
|
|
|
|
unsigned long y = regs->y; |
|
|
|
|
|
|
|
|
|
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
&y, |
|
|
|
|
35 * sizeof(u64), |
|
|
|
|
36 * sizeof(u64)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!ret) |
|
|
|
|
ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
36 * sizeof(u64), -1); |
|
|
|
|
|
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int genregs64_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); |
|
|
|
|
int ret; |
|
|
|
|
|
|
|
|
|
if (target == current) |
|
|
|
|
flushw_user(); |
|
|
|
|
|
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
regs->u_regs, |
|
|
|
|
0, 16 * sizeof(u64)); |
|
|
|
|
if (!ret && count > 0) { |
|
|
|
|
unsigned long __user *reg_window = (unsigned long __user *) |
|
|
|
|
(regs->u_regs[UREG_I6] + STACK_BIAS); |
|
|
|
|
unsigned long window[16]; |
|
|
|
|
|
|
|
|
|
if (copy_from_user(window, reg_window, sizeof(window))) |
|
|
|
|
return -EFAULT; |
|
|
|
|
|
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
window, |
|
|
|
|
16 * sizeof(u64), |
|
|
|
|
32 * sizeof(u64)); |
|
|
|
|
if (!ret && |
|
|
|
|
copy_to_user(reg_window, window, sizeof(window))) |
|
|
|
|
return -EFAULT; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!ret && count > 0) { |
|
|
|
|
unsigned long tstate; |
|
|
|
|
|
|
|
|
|
/* TSTATE */ |
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
&tstate, |
|
|
|
|
32 * sizeof(u64), |
|
|
|
|
33 * sizeof(u64)); |
|
|
|
|
if (!ret) { |
|
|
|
|
/* Only the condition codes can be modified
|
|
|
|
|
* in the %tstate register. |
|
|
|
|
*/ |
|
|
|
|
tstate &= (TSTATE_ICC | TSTATE_XCC); |
|
|
|
|
regs->tstate &= ~(TSTATE_ICC | TSTATE_XCC); |
|
|
|
|
regs->tstate |= tstate; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!ret) { |
|
|
|
|
/* TPC, TNPC */ |
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
®s->tpc, |
|
|
|
|
33 * sizeof(u64), |
|
|
|
|
35 * sizeof(u64)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!ret) { |
|
|
|
|
unsigned long y; |
|
|
|
|
|
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
&y, |
|
|
|
|
35 * sizeof(u64), |
|
|
|
|
36 * sizeof(u64)); |
|
|
|
|
if (!ret) |
|
|
|
|
regs->y = y; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!ret) |
|
|
|
|
ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
36 * sizeof(u64), -1); |
|
|
|
|
|
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int fpregs64_get(struct task_struct *target, |
|
|
|
|
const struct user_regset *regset, |
|
|
|
|
unsigned int pos, unsigned int count, |
|
|
|
|
void *kbuf, void __user *ubuf) |
|
|
|
|
{ |
|
|
|
|
const unsigned long *fpregs = task_thread_info(target)->fpregs; |
|
|
|
|
unsigned long fprs, fsr, gsr; |
|
|
|
|
int ret; |
|
|
|
|
|
|
|
|
|
if (target == current) |
|
|
|
|
save_and_clear_fpu(); |
|
|
|
|
|
|
|
|
|
fprs = task_thread_info(target)->fpsaved[0]; |
|
|
|
|
|
|
|
|
|
if (fprs & FPRS_DL) |
|
|
|
|
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
fpregs, |
|
|
|
|
0, 16 * sizeof(u64)); |
|
|
|
|
else |
|
|
|
|
ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
0, |
|
|
|
|
16 * sizeof(u64)); |
|
|
|
|
|
|
|
|
|
if (!ret) { |
|
|
|
|
if (fprs & FPRS_DU) |
|
|
|
|
ret = user_regset_copyout(&pos, &count, |
|
|
|
|
&kbuf, &ubuf, |
|
|
|
|
fpregs + 16, |
|
|
|
|
16 * sizeof(u64), |
|
|
|
|
32 * sizeof(u64)); |
|
|
|
|
else |
|
|
|
|
ret = user_regset_copyout_zero(&pos, &count, |
|
|
|
|
&kbuf, &ubuf, |
|
|
|
|
16 * sizeof(u64), |
|
|
|
|
32 * sizeof(u64)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (fprs & FPRS_FEF) { |
|
|
|
|
fsr = task_thread_info(target)->xfsr[0]; |
|
|
|
|
gsr = task_thread_info(target)->gsr[0]; |
|
|
|
|
} else { |
|
|
|
|
fsr = gsr = 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!ret) |
|
|
|
|
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
&fsr, |
|
|
|
|
32 * sizeof(u64), |
|
|
|
|
33 * sizeof(u64)); |
|
|
|
|
if (!ret) |
|
|
|
|
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
&gsr, |
|
|
|
|
33 * sizeof(u64), |
|
|
|
|
34 * sizeof(u64)); |
|
|
|
|
if (!ret) |
|
|
|
|
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
&fprs, |
|
|
|
|
34 * sizeof(u64), |
|
|
|
|
35 * sizeof(u64)); |
|
|
|
|
|
|
|
|
|
if (!ret) |
|
|
|
|
ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
35 * sizeof(u64), -1); |
|
|
|
|
|
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int fpregs64_set(struct task_struct *target, |
|
|
|
|
const struct user_regset *regset, |
|
|
|
|
unsigned int pos, unsigned int count, |
|
|
|
|
const void *kbuf, const void __user *ubuf) |
|
|
|
|
{ |
|
|
|
|
unsigned long *fpregs = task_thread_info(target)->fpregs; |
|
|
|
|
unsigned long fprs; |
|
|
|
|
int ret; |
|
|
|
|
|
|
|
|
|
if (target == current) |
|
|
|
|
save_and_clear_fpu(); |
|
|
|
|
|
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
fpregs, |
|
|
|
|
0, 32 * sizeof(u64)); |
|
|
|
|
if (!ret) |
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
task_thread_info(target)->xfsr, |
|
|
|
|
32 * sizeof(u64), |
|
|
|
|
33 * sizeof(u64)); |
|
|
|
|
if (!ret) |
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
task_thread_info(target)->gsr, |
|
|
|
|
33 * sizeof(u64), |
|
|
|
|
34 * sizeof(u64)); |
|
|
|
|
|
|
|
|
|
fprs = task_thread_info(target)->fpsaved[0]; |
|
|
|
|
if (!ret && count > 0) { |
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
&fprs, |
|
|
|
|
34 * sizeof(u64), |
|
|
|
|
35 * sizeof(u64)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fprs |= (FPRS_FEF | FPRS_DL | FPRS_DU); |
|
|
|
|
task_thread_info(target)->fpsaved[0] = fprs; |
|
|
|
|
|
|
|
|
|
if (!ret) |
|
|
|
|
ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
35 * sizeof(u64), -1); |
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static const struct user_regset sparc64_regsets[] = { |
|
|
|
|
/* Format is:
|
|
|
|
|
* G0 --> G7 |
|
|
|
|
* O0 --> O7 |
|
|
|
|
* L0 --> L7 |
|
|
|
|
* I0 --> I7 |
|
|
|
|
* TSTATE, TPC, TNPC, Y |
|
|
|
|
*/ |
|
|
|
|
[REGSET_GENERAL] = { |
|
|
|
|
.core_note_type = NT_PRSTATUS, |
|
|
|
|
.n = 36 * sizeof(u64), |
|
|
|
|
.size = sizeof(u64), .align = sizeof(u64), |
|
|
|
|
.get = genregs64_get, .set = genregs64_set |
|
|
|
|
}, |
|
|
|
|
/* Format is:
|
|
|
|
|
* F0 --> F63 |
|
|
|
|
* FSR |
|
|
|
|
* GSR |
|
|
|
|
* FPRS |
|
|
|
|
*/ |
|
|
|
|
[REGSET_FP] = { |
|
|
|
|
.core_note_type = NT_PRFPREG, |
|
|
|
|
.n = 35 * sizeof(u64), |
|
|
|
|
.size = sizeof(u64), .align = sizeof(u64), |
|
|
|
|
.get = fpregs64_get, .set = fpregs64_set |
|
|
|
|
}, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static const struct user_regset_view user_sparc64_view = { |
|
|
|
|
.name = "sparc64", .e_machine = EM_SPARCV9, |
|
|
|
|
.regsets = sparc64_regsets, .n = ARRAY_SIZE(sparc64_regsets) |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static int genregs32_get(struct task_struct *target, |
|
|
|
|
const struct user_regset *regset, |
|
|
|
|
unsigned int pos, unsigned int count, |
|
|
|
|
void *kbuf, void __user *ubuf) |
|
|
|
|
{ |
|
|
|
|
const struct pt_regs *regs = task_pt_regs(target); |
|
|
|
|
compat_ulong_t __user *reg_window; |
|
|
|
|
compat_ulong_t *k = kbuf; |
|
|
|
|
compat_ulong_t __user *u = ubuf; |
|
|
|
|
compat_ulong_t reg; |
|
|
|
|
|
|
|
|
|
if (target == current) |
|
|
|
|
flushw_user(); |
|
|
|
|
|
|
|
|
|
pos /= sizeof(reg); |
|
|
|
|
count /= sizeof(reg); |
|
|
|
|
|
|
|
|
|
if (kbuf) { |
|
|
|
|
for (; count > 0 && pos < 16; count--) |
|
|
|
|
*k++ = regs->u_regs[pos++]; |
|
|
|
|
|
|
|
|
|
reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; |
|
|
|
|
for (; count > 0 && pos < 32; count--) { |
|
|
|
|
if (get_user(*k++, ®_window[pos++])) |
|
|
|
|
return -EFAULT; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
for (; count > 0 && pos < 16; count--) { |
|
|
|
|
if (put_user((compat_ulong_t) regs->u_regs[pos++], u++)) |
|
|
|
|
return -EFAULT; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; |
|
|
|
|
for (; count > 0 && pos < 32; count--) { |
|
|
|
|
if (get_user(reg, ®_window[pos++]) || |
|
|
|
|
put_user(reg, u++)) |
|
|
|
|
return -EFAULT; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
while (count > 0) { |
|
|
|
|
switch (pos) { |
|
|
|
|
case 32: /* PSR */ |
|
|
|
|
reg = tstate_to_psr(regs->tstate); |
|
|
|
|
break; |
|
|
|
|
case 33: /* PC */ |
|
|
|
|
reg = regs->tpc; |
|
|
|
|
break; |
|
|
|
|
case 34: /* NPC */ |
|
|
|
|
reg = regs->tnpc; |
|
|
|
|
break; |
|
|
|
|
case 35: /* Y */ |
|
|
|
|
reg = regs->y; |
|
|
|
|
break; |
|
|
|
|
case 36: /* WIM */ |
|
|
|
|
case 37: /* TBR */ |
|
|
|
|
reg = 0; |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
goto finish; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (kbuf) |
|
|
|
|
*k++ = reg; |
|
|
|
|
else if (put_user(reg, u++)) |
|
|
|
|
return -EFAULT; |
|
|
|
|
pos++; |
|
|
|
|
count--; |
|
|
|
|
} |
|
|
|
|
finish: |
|
|
|
|
pos *= sizeof(reg); |
|
|
|
|
count *= sizeof(reg); |
|
|
|
|
|
|
|
|
|
return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
38 * sizeof(reg), -1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int genregs32_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); |
|
|
|
|
compat_ulong_t __user *reg_window; |
|
|
|
|
const compat_ulong_t *k = kbuf; |
|
|
|
|
const compat_ulong_t __user *u = ubuf; |
|
|
|
|
compat_ulong_t reg; |
|
|
|
|
|
|
|
|
|
if (target == current) |
|
|
|
|
flushw_user(); |
|
|
|
|
|
|
|
|
|
pos /= sizeof(reg); |
|
|
|
|
count /= sizeof(reg); |
|
|
|
|
|
|
|
|
|
if (kbuf) { |
|
|
|
|
for (; count > 0 && pos < 16; count--) |
|
|
|
|
regs->u_regs[pos++] = *k++; |
|
|
|
|
|
|
|
|
|
reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; |
|
|
|
|
for (; count > 0 && pos < 32; count--) { |
|
|
|
|
if (put_user(*k++, ®_window[pos++])) |
|
|
|
|
return -EFAULT; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
for (; count > 0 && pos < 16; count--) { |
|
|
|
|
if (get_user(reg, u++)) |
|
|
|
|
return -EFAULT; |
|
|
|
|
regs->u_regs[pos++] = reg; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
reg_window = (compat_ulong_t __user *) regs->u_regs[UREG_I6]; |
|
|
|
|
for (; count > 0 && pos < 32; count--) { |
|
|
|
|
if (get_user(reg, u++) || |
|
|
|
|
put_user(reg, ®_window[pos++])) |
|
|
|
|
return -EFAULT; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
while (count > 0) { |
|
|
|
|
unsigned long tstate; |
|
|
|
|
|
|
|
|
|
if (kbuf) |
|
|
|
|
reg = *k++; |
|
|
|
|
else if (get_user(reg, u++)) |
|
|
|
|
return -EFAULT; |
|
|
|
|
|
|
|
|
|
switch (pos) { |
|
|
|
|
case 32: /* PSR */ |
|
|
|
|
tstate = regs->tstate; |
|
|
|
|
tstate &= ~(TSTATE_ICC | TSTATE_XCC); |
|
|
|
|
tstate |= psr_to_tstate_icc(reg); |
|
|
|
|
regs->tstate = tstate; |
|
|
|
|
break; |
|
|
|
|
case 33: /* PC */ |
|
|
|
|
regs->tpc = reg; |
|
|
|
|
break; |
|
|
|
|
case 34: /* NPC */ |
|
|
|
|
regs->tnpc = reg; |
|
|
|
|
break; |
|
|
|
|
case 35: /* Y */ |
|
|
|
|
regs->y = reg; |
|
|
|
|
break; |
|
|
|
|
case 36: /* WIM */ |
|
|
|
|
case 37: /* TBR */ |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
goto finish; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pos++; |
|
|
|
|
count--; |
|
|
|
|
} |
|
|
|
|
finish: |
|
|
|
|
pos *= sizeof(reg); |
|
|
|
|
count *= sizeof(reg); |
|
|
|
|
|
|
|
|
|
return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
38 * sizeof(reg), -1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int fpregs32_get(struct task_struct *target, |
|
|
|
|
const struct user_regset *regset, |
|
|
|
|
unsigned int pos, unsigned int count, |
|
|
|
|
void *kbuf, void __user *ubuf) |
|
|
|
|
{ |
|
|
|
|
const unsigned long *fpregs = task_thread_info(target)->fpregs; |
|
|
|
|
compat_ulong_t enabled; |
|
|
|
|
unsigned long fprs; |
|
|
|
|
compat_ulong_t fsr; |
|
|
|
|
int ret = 0; |
|
|
|
|
|
|
|
|
|
if (target == current) |
|
|
|
|
save_and_clear_fpu(); |
|
|
|
|
|
|
|
|
|
fprs = task_thread_info(target)->fpsaved[0]; |
|
|
|
|
if (fprs & FPRS_FEF) { |
|
|
|
|
fsr = task_thread_info(target)->xfsr[0]; |
|
|
|
|
enabled = 1; |
|
|
|
|
} else { |
|
|
|
|
fsr = 0; |
|
|
|
|
enabled = 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
fpregs, |
|
|
|
|
0, 32 * sizeof(u32)); |
|
|
|
|
|
|
|
|
|
if (!ret) |
|
|
|
|
ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
32 * sizeof(u32), |
|
|
|
|
33 * sizeof(u32)); |
|
|
|
|
if (!ret) |
|
|
|
|
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
&fsr, |
|
|
|
|
33 * sizeof(u32), |
|
|
|
|
34 * sizeof(u32)); |
|
|
|
|
|
|
|
|
|
if (!ret) { |
|
|
|
|
compat_ulong_t val; |
|
|
|
|
|
|
|
|
|
val = (enabled << 8) | (8 << 16); |
|
|
|
|
ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
&val, |
|
|
|
|
34 * sizeof(u32), |
|
|
|
|
35 * sizeof(u32)); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!ret) |
|
|
|
|
ret = user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
35 * sizeof(u32), -1); |
|
|
|
|
|
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int fpregs32_set(struct task_struct *target, |
|
|
|
|
const struct user_regset *regset, |
|
|
|
|
unsigned int pos, unsigned int count, |
|
|
|
|
const void *kbuf, const void __user *ubuf) |
|
|
|
|
{ |
|
|
|
|
unsigned long *fpregs = task_thread_info(target)->fpregs; |
|
|
|
|
unsigned long fprs; |
|
|
|
|
int ret; |
|
|
|
|
|
|
|
|
|
if (target == current) |
|
|
|
|
save_and_clear_fpu(); |
|
|
|
|
|
|
|
|
|
fprs = task_thread_info(target)->fpsaved[0]; |
|
|
|
|
|
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
fpregs, |
|
|
|
|
0, 32 * sizeof(u32)); |
|
|
|
|
if (!ret) |
|
|
|
|
user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
32 * sizeof(u32), |
|
|
|
|
33 * sizeof(u32)); |
|
|
|
|
if (!ret && count > 0) { |
|
|
|
|
compat_ulong_t fsr; |
|
|
|
|
unsigned long val; |
|
|
|
|
|
|
|
|
|
ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
&fsr, |
|
|
|
|
33 * sizeof(u32), |
|
|
|
|
34 * sizeof(u32)); |
|
|
|
|
if (!ret) { |
|
|
|
|
val = task_thread_info(target)->xfsr[0]; |
|
|
|
|
val &= 0xffffffff00000000UL; |
|
|
|
|
val |= fsr; |
|
|
|
|
task_thread_info(target)->xfsr[0] = val; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fprs |= (FPRS_FEF | FPRS_DL); |
|
|
|
|
task_thread_info(target)->fpsaved[0] = fprs; |
|
|
|
|
|
|
|
|
|
if (!ret) |
|
|
|
|
ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, |
|
|
|
|
34 * sizeof(u32), -1); |
|
|
|
|
return ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static const struct user_regset sparc32_regsets[] = { |
|
|
|
|
/* Format is:
|
|
|
|
|
* G0 --> G7 |
|
|
|
|
* O0 --> O7 |
|
|
|
|
* L0 --> L7 |
|
|
|
|
* I0 --> I7 |
|
|
|
|
* PSR, PC, nPC, Y, WIM, TBR |
|
|
|
|
*/ |
|
|
|
|
[REGSET_GENERAL] = { |
|
|
|
|
.core_note_type = NT_PRSTATUS, |
|
|
|
|
.n = 38 * sizeof(u32), |
|
|
|
|
.size = sizeof(u32), .align = sizeof(u32), |
|
|
|
|
.get = genregs32_get, .set = genregs32_set |
|
|
|
|
}, |
|
|
|
|
/* Format is:
|
|
|
|
|
* F0 --> F31 |
|
|
|
|
* empty 32-bit word |
|
|
|
|
* FSR (32--bit word) |
|
|
|
|
* FPU QUEUE COUNT (8-bit char) |
|
|
|
|
* FPU QUEUE ENTRYSIZE (8-bit char) |
|
|
|
|
* FPU ENABLED (8-bit char) |
|
|
|
|
* empty 8-bit char |
|
|
|
|
* FPU QUEUE (64 32-bit ints) |
|
|
|
|
*/ |
|
|
|
|
[REGSET_FP] = { |
|
|
|
|
.core_note_type = NT_PRFPREG, |
|
|
|
|
.n = 99 * sizeof(u32), |
|
|
|
|
.size = sizeof(u32), .align = sizeof(u32), |
|
|
|
|
.get = fpregs32_get, .set = fpregs32_set |
|
|
|
|
}, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
static const struct user_regset_view user_sparc32_view = { |
|
|
|
|
.name = "sparc", .e_machine = EM_SPARC, |
|
|
|
|
.regsets = sparc32_regsets, .n = ARRAY_SIZE(sparc32_regsets) |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const struct user_regset_view *task_user_regset_view(struct task_struct *task) |
|
|
|
|
{ |
|
|
|
|
if (test_tsk_thread_flag(task, TIF_32BIT)) |
|
|
|
|
return &user_sparc32_view; |
|
|
|
|
return &user_sparc64_view; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
asmlinkage void do_ptrace(struct pt_regs *regs) |
|
|
|
|
{ |
|
|
|
|
int request = regs->u_regs[UREG_I0]; |
|
|
|
|