* At this time Linux/MIPS64 only supports syscall tracing, even for 32-bit
* binaries.
*/
-#include <linux/config.h>
#include <linux/compiler.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
-#include <linux/audit.h>
#include <linux/smp.h>
-#include <linux/smp_lock.h>
#include <linux/user.h>
#include <linux/security.h>
-#include <linux/signal.h>
+#include <linux/audit.h>
+#include <linux/seccomp.h>
#include <asm/byteorder.h>
#include <asm/cpu.h>
*/
void ptrace_disable(struct task_struct *child)
{
- /* Nothing to do.. */
+ /* Don't load the watchpoint registers for the ex-child. */
+ clear_tsk_thread_flag(child, TIF_LOAD_WATCH);
}
/*
* for 32-bit kernels and for 32-bit processes on a 64-bit kernel.
* Registers are sign extended to fill the available space.
*/
-int ptrace_getregs (struct task_struct *child, __s64 __user *data)
+int ptrace_getregs(struct task_struct *child, __s64 __user *data)
{
struct pt_regs *regs;
int i;
regs = task_pt_regs(child);
for (i = 0; i < 32; i++)
- __put_user (regs->regs[i], data + i);
- __put_user (regs->lo, data + EF_LO - EF_R0);
- __put_user (regs->hi, data + EF_HI - EF_R0);
- __put_user (regs->cp0_epc, data + EF_CP0_EPC - EF_R0);
- __put_user (regs->cp0_badvaddr, data + EF_CP0_BADVADDR - EF_R0);
- __put_user (regs->cp0_status, data + EF_CP0_STATUS - EF_R0);
- __put_user (regs->cp0_cause, data + EF_CP0_CAUSE - EF_R0);
+ __put_user((long)regs->regs[i], data + i);
+ __put_user((long)regs->lo, data + EF_LO - EF_R0);
+ __put_user((long)regs->hi, data + EF_HI - EF_R0);
+ __put_user((long)regs->cp0_epc, data + EF_CP0_EPC - EF_R0);
+ __put_user((long)regs->cp0_badvaddr, data + EF_CP0_BADVADDR - EF_R0);
+ __put_user((long)regs->cp0_status, data + EF_CP0_STATUS - EF_R0);
+ __put_user((long)regs->cp0_cause, data + EF_CP0_CAUSE - EF_R0);
return 0;
}
* the 64-bit format. On a 32-bit kernel only the lower order half
* (according to endianness) will be used.
*/
-int ptrace_setregs (struct task_struct *child, __s64 __user *data)
+int ptrace_setregs(struct task_struct *child, __s64 __user *data)
{
struct pt_regs *regs;
int i;
regs = task_pt_regs(child);
for (i = 0; i < 32; i++)
- __get_user (regs->regs[i], data + i);
- __get_user (regs->lo, data + EF_LO - EF_R0);
- __get_user (regs->hi, data + EF_HI - EF_R0);
- __get_user (regs->cp0_epc, data + EF_CP0_EPC - EF_R0);
+ __get_user(regs->regs[i], data + i);
+ __get_user(regs->lo, data + EF_LO - EF_R0);
+ __get_user(regs->hi, data + EF_HI - EF_R0);
+ __get_user(regs->cp0_epc, data + EF_CP0_EPC - EF_R0);
/* badvaddr, status, and cause may not be written. */
return 0;
}
-int ptrace_getfpregs (struct task_struct *child, __u32 __user *data)
+int ptrace_getfpregs(struct task_struct *child, __u32 __user *data)
{
int i;
+ unsigned int tmp;
if (!access_ok(VERIFY_WRITE, data, 33 * 8))
return -EIO;
if (tsk_used_math(child)) {
fpureg_t *fregs = get_fpu_regs(child);
for (i = 0; i < 32; i++)
- __put_user (fregs[i], i + (__u64 __user *) data);
+ __put_user(fregs[i], i + (__u64 __user *) data);
} else {
for (i = 0; i < 32; i++)
- __put_user ((__u64) -1, i + (__u64 __user *) data);
+ __put_user((__u64) -1, i + (__u64 __user *) data);
}
- if (cpu_has_fpu) {
- unsigned int flags, tmp;
+ __put_user(child->thread.fpu.fcr31, data + 64);
- __put_user (child->thread.fpu.hard.fcr31, data + 64);
+ preempt_disable();
+ if (cpu_has_fpu) {
+ unsigned int flags;
- preempt_disable();
if (cpu_has_mipsmt) {
unsigned int vpflags = dvpe();
flags = read_c0_status();
__asm__ __volatile__("cfc1\t%0,$0" : "=r" (tmp));
write_c0_status(flags);
}
- preempt_enable();
- __put_user (tmp, data + 65);
} else {
- __put_user (child->thread.fpu.soft.fcr31, data + 64);
- __put_user ((__u32) 0, data + 65);
+ tmp = 0;
}
+ preempt_enable();
+ __put_user(tmp, data + 65);
return 0;
}
-int ptrace_setfpregs (struct task_struct *child, __u32 __user *data)
+int ptrace_setfpregs(struct task_struct *child, __u32 __user *data)
{
fpureg_t *fregs;
int i;
fregs = get_fpu_regs(child);
for (i = 0; i < 32; i++)
- __get_user (fregs[i], i + (__u64 __user *) data);
+ __get_user(fregs[i], i + (__u64 __user *) data);
- if (cpu_has_fpu)
- __get_user (child->thread.fpu.hard.fcr31, data + 64);
- else
- __get_user (child->thread.fpu.soft.fcr31, data + 64);
+ __get_user(child->thread.fpu.fcr31, data + 64);
/* FIR may not be written. */
return 0;
}
+int ptrace_get_watch_regs(struct task_struct *child,
+ struct pt_watch_regs __user *addr)
+{
+ enum pt_watch_style style;
+ int i;
+
+ if (!cpu_has_watch || current_cpu_data.watch_reg_use_cnt == 0)
+ return -EIO;
+ if (!access_ok(VERIFY_WRITE, addr, sizeof(struct pt_watch_regs)))
+ return -EIO;
+
+#ifdef CONFIG_32BIT
+ style = pt_watch_style_mips32;
+#define WATCH_STYLE mips32
+#else
+ style = pt_watch_style_mips64;
+#define WATCH_STYLE mips64
+#endif
+
+ __put_user(style, &addr->style);
+ __put_user(current_cpu_data.watch_reg_use_cnt,
+ &addr->WATCH_STYLE.num_valid);
+ for (i = 0; i < current_cpu_data.watch_reg_use_cnt; i++) {
+ __put_user(child->thread.watch.mips3264.watchlo[i],
+ &addr->WATCH_STYLE.watchlo[i]);
+ __put_user(child->thread.watch.mips3264.watchhi[i] & 0xfff,
+ &addr->WATCH_STYLE.watchhi[i]);
+ __put_user(current_cpu_data.watch_reg_masks[i],
+ &addr->WATCH_STYLE.watch_masks[i]);
+ }
+ for (; i < 8; i++) {
+ __put_user(0, &addr->WATCH_STYLE.watchlo[i]);
+ __put_user(0, &addr->WATCH_STYLE.watchhi[i]);
+ __put_user(0, &addr->WATCH_STYLE.watch_masks[i]);
+ }
+
+ return 0;
+}
+
+int ptrace_set_watch_regs(struct task_struct *child,
+ struct pt_watch_regs __user *addr)
+{
+ int i;
+ int watch_active = 0;
+ unsigned long lt[NUM_WATCH_REGS];
+ u16 ht[NUM_WATCH_REGS];
+
+ if (!cpu_has_watch || current_cpu_data.watch_reg_use_cnt == 0)
+ return -EIO;
+ if (!access_ok(VERIFY_READ, addr, sizeof(struct pt_watch_regs)))
+ return -EIO;
+ /* Check the values. */
+ for (i = 0; i < current_cpu_data.watch_reg_use_cnt; i++) {
+ __get_user(lt[i], &addr->WATCH_STYLE.watchlo[i]);
+#ifdef CONFIG_32BIT
+ if (lt[i] & __UA_LIMIT)
+ return -EINVAL;
+#else
+ if (test_tsk_thread_flag(child, TIF_32BIT_ADDR)) {
+ if (lt[i] & 0xffffffff80000000UL)
+ return -EINVAL;
+ } else {
+ if (lt[i] & __UA_LIMIT)
+ return -EINVAL;
+ }
+#endif
+ __get_user(ht[i], &addr->WATCH_STYLE.watchhi[i]);
+ if (ht[i] & ~0xff8)
+ return -EINVAL;
+ }
+ /* Install them. */
+ for (i = 0; i < current_cpu_data.watch_reg_use_cnt; i++) {
+ if (lt[i] & 7)
+ watch_active = 1;
+ child->thread.watch.mips3264.watchlo[i] = lt[i];
+ /* Set the G bit. */
+ child->thread.watch.mips3264.watchhi[i] = ht[i];
+ }
+
+ if (watch_active)
+ set_tsk_thread_flag(child, TIF_LOAD_WATCH);
+ else
+ clear_tsk_thread_flag(child, TIF_LOAD_WATCH);
+
+ return 0;
+}
+
long arch_ptrace(struct task_struct *child, long request, long addr, long data)
{
int ret;
switch (request) {
/* when I and D space are separate, these will need to be fixed. */
case PTRACE_PEEKTEXT: /* read word at location addr. */
- case PTRACE_PEEKDATA: {
- unsigned long tmp;
- int copied;
-
- copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
- ret = -EIO;
- if (copied != sizeof(tmp))
- break;
- ret = put_user(tmp,(unsigned long __user *) data);
+ case PTRACE_PEEKDATA:
+ ret = generic_ptrace_peekdata(child, addr, data);
break;
- }
/* Read the word at location addr in the USER area. */
case PTRACE_PEEKUSR: {
case MMLO:
tmp = regs->lo;
break;
+#ifdef CONFIG_CPU_HAS_SMARTMIPS
+ case ACX:
+ tmp = regs->acx;
+ break;
+#endif
case FPC_CSR:
- if (cpu_has_fpu)
- tmp = child->thread.fpu.hard.fcr31;
- else
- tmp = child->thread.fpu.soft.fcr31;
+ tmp = child->thread.fpu.fcr31;
break;
case FPC_EIR: { /* implementation / version register */
unsigned int flags;
#ifdef CONFIG_MIPS_MT_SMTC
- unsigned int irqflags;
+ unsigned long irqflags;
unsigned int mtflags;
#endif /* CONFIG_MIPS_MT_SMTC */
- if (!cpu_has_fpu)
+ preempt_disable();
+ if (!cpu_has_fpu) {
+ preempt_enable();
break;
+ }
#ifdef CONFIG_MIPS_MT_SMTC
/* Read-modify-write of Status must be atomic */
local_irq_save(irqflags);
mtflags = dmt();
#endif /* CONFIG_MIPS_MT_SMTC */
-
- preempt_disable();
if (cpu_has_mipsmt) {
unsigned int vpflags = dvpe();
flags = read_c0_status();
/* when I and D space are separate, this will have to be fixed. */
case PTRACE_POKETEXT: /* write the word at location addr. */
case PTRACE_POKEDATA:
- ret = 0;
- if (access_process_vm(child, addr, &data, sizeof(data), 1)
- == sizeof(data))
- break;
- ret = -EIO;
+ ret = generic_ptrace_pokedata(child, addr, data);
break;
case PTRACE_POKEUSR: {
if (!tsk_used_math(child)) {
/* FP not yet used */
- memset(&child->thread.fpu.hard, ~0,
- sizeof(child->thread.fpu.hard));
- child->thread.fpu.hard.fcr31 = 0;
+ memset(&child->thread.fpu, ~0,
+ sizeof(child->thread.fpu));
+ child->thread.fpu.fcr31 = 0;
}
#ifdef CONFIG_32BIT
/*
case MMLO:
regs->lo = data;
break;
+#ifdef CONFIG_CPU_HAS_SMARTMIPS
+ case ACX:
+ regs->acx = data;
+ break;
+#endif
case FPC_CSR:
- if (cpu_has_fpu)
- child->thread.fpu.hard.fcr31 = data;
- else
- child->thread.fpu.soft.fcr31 = data;
+ child->thread.fpu.fcr31 = data;
break;
case DSP_BASE ... DSP_BASE + 5: {
dspreg_t *dregs;
}
case PTRACE_GETREGS:
- ret = ptrace_getregs (child, (__u64 __user *) data);
+ ret = ptrace_getregs(child, (__s64 __user *) data);
break;
case PTRACE_SETREGS:
- ret = ptrace_setregs (child, (__u64 __user *) data);
+ ret = ptrace_setregs(child, (__s64 __user *) data);
break;
case PTRACE_GETFPREGS:
- ret = ptrace_getfpregs (child, (__u32 __user *) data);
+ ret = ptrace_getfpregs(child, (__u32 __user *) data);
break;
case PTRACE_SETFPREGS:
- ret = ptrace_setfpregs (child, (__u32 __user *) data);
+ ret = ptrace_setfpregs(child, (__u32 __user *) data);
break;
- case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */
- case PTRACE_CONT: { /* restart after signal. */
- ret = -EIO;
- if (!valid_signal(data))
- break;
- if (request == PTRACE_SYSCALL) {
- set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- }
- else {
- clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
- }
- child->exit_code = data;
- wake_up_process(child);
- ret = 0;
- break;
- }
-
- /*
- * make the child exit. Best I can do is send it a sigkill.
- * perhaps it should be put in the status that it wants to
- * exit.
- */
- case PTRACE_KILL:
- ret = 0;
- if (child->exit_state == EXIT_ZOMBIE) /* already dead */
- break;
- child->exit_code = SIGKILL;
- wake_up_process(child);
+ case PTRACE_GET_THREAD_AREA:
+ ret = put_user(task_thread_info(child)->tp_value,
+ (unsigned long __user *) data);
break;
- case PTRACE_DETACH: /* detach a process that was attached. */
- ret = ptrace_detach(child, data);
+ case PTRACE_GET_WATCH_REGS:
+ ret = ptrace_get_watch_regs(child,
+ (struct pt_watch_regs __user *) addr);
break;
- case PTRACE_GET_THREAD_AREA:
- ret = put_user(task_thread_info(child)->tp_value,
- (unsigned long __user *) data);
+ case PTRACE_SET_WATCH_REGS:
+ ret = ptrace_set_watch_regs(child,
+ (struct pt_watch_regs __user *) addr);
break;
default:
*/
asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit)
{
+ /* do the secure computing check first */
+ if (!entryexit)
+ secure_computing(regs->regs[2]);
+
if (unlikely(current->audit_context) && entryexit)
audit_syscall_exit(AUDITSC_RESULT(regs->regs[2]),
regs->regs[2]);
if (!(current->ptrace & PT_PTRACED))
goto out;
+
if (!test_thread_flag(TIF_SYSCALL_TRACE))
goto out;
send_sig(current->exit_code, current, 1);
current->exit_code = 0;
}
- out:
+
+out:
if (unlikely(current->audit_context) && !entryexit)
audit_syscall_entry(audit_arch(), regs->regs[2],
regs->regs[4], regs->regs[5],