#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/security.h>
+#include <linux/signal.h>
#include <asm/asi.h>
#include <asm/pgtable.h>
}
static inline void
-pt_succ_return_linux(struct pt_regs *regs, unsigned long value, long *addr)
+pt_succ_return_linux(struct pt_regs *regs, unsigned long value, void __user *addr)
{
if (test_thread_flag(TIF_32BIT)) {
- if (put_user(value, (unsigned int *)addr))
- return pt_error_return(regs, EFAULT);
+ if (put_user(value, (unsigned int __user *) addr)) {
+ pt_error_return(regs, EFAULT);
+ return;
+ }
} else {
- if (put_user(value, addr))
- return pt_error_return(regs, EFAULT);
+ if (put_user(value, (long __user *) addr)) {
+ pt_error_return(regs, EFAULT);
+ return;
+ }
}
regs->u_regs[UREG_I0] = 0;
regs->tstate &= ~(TSTATE_ICARRY | TSTATE_XCARRY);
}
static void
-pt_os_succ_return (struct pt_regs *regs, unsigned long val, long *addr)
+pt_os_succ_return (struct pt_regs *regs, unsigned long val, void __user *addr)
{
if (current->personality == PER_SUNOS)
pt_succ_return (regs, val);
/* nothing to do */
}
+/* To get the necessary page struct, access_process_vm() first calls
+ * get_user_pages(). This has done a flush_dcache_page() on the
+ * accessed page. Then our caller (copy_{to,from}_user_page()) did
+ * to memcpy to read/write the data from that page.
+ *
+ * Now, the only thing we have to do is:
+ * 1) flush the D-cache if it's possible than an illegal alias
+ * has been created
+ * 2) flush the I-cache if this is pre-cheetah and we did a write
+ */
+void flush_ptrace_access(struct vm_area_struct *vma, struct page *page,
+ unsigned long uaddr, void *kaddr,
+ unsigned long len, int write)
+{
+ BUG_ON(len > PAGE_SIZE);
+
+#ifdef DCACHE_ALIASING_POSSIBLE
+ /* If bit 13 of the kernel address we used to access the
+ * user page is the same as the virtual address that page
+ * is mapped to in the user's address space, we can skip the
+ * D-cache flush.
+ */
+ if ((uaddr ^ kaddr) & (1UL << 13)) {
+ unsigned long start = __pa(kaddr);
+ unsigned long end = start + len;
+
+ if (tlb_type == spitfire) {
+ for (; start < end; start += 32)
+ spitfire_put_dcache_tag(va & 0x3fe0, 0x0);
+ } else {
+ for (; start < end; start += 32)
+ __asm__ __volatile__(
+ "stxa %%g0, [%0] %1\n\t"
+ "membar #Sync"
+ : /* no outputs */
+ : "r" (va),
+ "i" (ASI_DCACHE_INVALIDATE));
+ }
+ }
+#endif
+ if (write && tlb_type == spitfire) {
+ unsigned long start = (unsigned long) kaddr;
+ unsigned long end = start + len;
+
+ for (; start < end; start += 32)
+ flushi(start);
+ }
+}
+
asmlinkage void do_ptrace(struct pt_regs *regs)
{
int request = regs->u_regs[UREG_I0];
pt_error_return(regs, ESRCH);
goto out;
}
+ if (!vx_check(vx_task_xid(child), VX_WATCH|VX_IDENT)) {
+ pt_error_return(regs, ESRCH);
+ goto out_tsk;
+ }
if ((current->personality == PER_SUNOS && request == PTRACE_SUNATTACH)
|| (current->personality != PER_SUNOS && request == PTRACE_ATTACH)) {
if (res < 0)
pt_error_return(regs, -res);
else
- pt_os_succ_return(regs, tmp64, (long *) data);
- goto flush_and_out;
+ pt_os_succ_return(regs, tmp64, (void __user *) data);
+ goto out_tsk;
}
case PTRACE_POKETEXT: /* write the word at location addr. */
pt_error_return(regs, -res);
else
pt_succ_return(regs, res);
- goto flush_and_out;
+ goto out_tsk;
}
case PTRACE_GETREGS: {
- struct pt_regs32 *pregs = (struct pt_regs32 *) addr;
+ struct pt_regs32 __user *pregs =
+ (struct pt_regs32 __user *) addr;
struct pt_regs *cregs = child->thread_info->kregs;
int rval;
}
case PTRACE_GETREGS64: {
- struct pt_regs *pregs = (struct pt_regs *) addr;
+ struct pt_regs __user *pregs = (struct pt_regs __user *) addr;
struct pt_regs *cregs = child->thread_info->kregs;
unsigned long tpc = cregs->tpc;
int rval;
}
case PTRACE_SETREGS: {
- struct pt_regs32 *pregs = (struct pt_regs32 *) addr;
+ struct pt_regs32 __user *pregs =
+ (struct pt_regs32 __user *) addr;
struct pt_regs *cregs = child->thread_info->kregs;
unsigned int psr, pc, npc, y;
int i;
}
case PTRACE_SETREGS64: {
- struct pt_regs *pregs = (struct pt_regs *) addr;
+ struct pt_regs __user *pregs = (struct pt_regs __user *) addr;
struct pt_regs *cregs = child->thread_info->kregs;
unsigned long tstate, tpc, tnpc, y;
int i;
unsigned int insnaddr;
unsigned int insn;
} fpq[16];
- } *fps = (struct fps *) addr;
+ };
+ struct fps __user *fps = (struct fps __user *) addr;
unsigned long *fpregs = child->thread_info->fpregs;
if (copy_to_user(&fps->regs[0], fpregs,
struct fps {
unsigned int regs[64];
unsigned long fsr;
- } *fps = (struct fps *) addr;
+ };
+ struct fps __user *fps = (struct fps __user *) addr;
unsigned long *fpregs = child->thread_info->fpregs;
if (copy_to_user(&fps->regs[0], fpregs,
unsigned int insnaddr;
unsigned int insn;
} fpq[16];
- } *fps = (struct fps *) addr;
+ };
+ struct fps __user *fps = (struct fps __user *) addr;
unsigned long *fpregs = child->thread_info->fpregs;
unsigned fsr;
struct fps {
unsigned int regs[64];
unsigned long fsr;
- } *fps = (struct fps *) addr;
+ };
+ struct fps __user *fps = (struct fps __user *) addr;
unsigned long *fpregs = child->thread_info->fpregs;
if (copy_from_user(fpregs, &fps->regs[0],
case PTRACE_READTEXT:
case PTRACE_READDATA: {
int res = ptrace_readdata(child, addr,
- (void *)addr2, data);
+ (char __user *)addr2, data);
if (res == data) {
pt_succ_return(regs, 0);
- goto flush_and_out;
+ goto out_tsk;
}
if (res >= 0)
res = -EIO;
pt_error_return(regs, -res);
- goto flush_and_out;
+ goto out_tsk;
}
case PTRACE_WRITETEXT:
case PTRACE_WRITEDATA: {
- int res = ptrace_writedata(child, (void *) addr2,
+ int res = ptrace_writedata(child, (char __user *) addr2,
addr, data);
if (res == data) {
pt_succ_return(regs, 0);
- goto flush_and_out;
+ goto out_tsk;
}
if (res >= 0)
res = -EIO;
pt_error_return(regs, -res);
- goto flush_and_out;
+ goto out_tsk;
}
case PTRACE_SYSCALL: /* continue and stop at (return from) syscall */
addr = 1;
case PTRACE_CONT: { /* restart after signal. */
- if (data > _NSIG) {
+ if (!valid_signal(data)) {
pt_error_return(regs, EIO);
goto out_tsk;
}
- if (addr != 1) {
- unsigned long pc_mask = ~0UL;
-
- if ((child->thread_info->flags & _TIF_32BIT) != 0)
- pc_mask = 0xffffffff;
-
- if (addr & 3) {
- pt_error_return(regs, EINVAL);
- goto out_tsk;
- }
-#ifdef DEBUG_PTRACE
- printk ("Original: %016lx %016lx\n",
- child->thread_info->kregs->tpc,
- child->thread_info->kregs->tnpc);
- printk ("Continuing with %016lx %016lx\n", addr, addr+4);
-#endif
- child->thread_info->kregs->tpc = (addr & pc_mask);
- child->thread_info->kregs->tnpc = ((addr + 4) & pc_mask);
- }
if (request == PTRACE_SYSCALL) {
set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
* exit.
*/
case PTRACE_KILL: {
- if (child->state == TASK_ZOMBIE) { /* already dead */
+ if (child->exit_state == EXIT_ZOMBIE) { /* already dead */
pt_succ_return(regs, 0);
goto out_tsk;
}
goto out_tsk;
}
}
-flush_and_out:
- {
- unsigned long va;
-
- if (tlb_type == cheetah || tlb_type == cheetah_plus) {
- for (va = 0; va < (1 << 16); va += (1 << 5))
- spitfire_put_dcache_tag(va, 0x0);
- /* No need to mess with I-cache on Cheetah. */
- } else {
- for (va = 0; va < L1DCACHE_SIZE; va += 32)
- spitfire_put_dcache_tag(va, 0x0);
- if (request == PTRACE_PEEKTEXT ||
- request == PTRACE_POKETEXT ||
- request == PTRACE_READTEXT ||
- request == PTRACE_WRITETEXT) {
- for (va = 0; va < (PAGE_SIZE << 1); va += 32)
- spitfire_put_icache_tag(va, 0x0);
- __asm__ __volatile__("flush %g6");
- }
- }
- }
out_tsk:
if (child)
put_task_struct(child);
return;
if (!(current->ptrace & PT_PTRACED))
return;
- current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
- ? 0x80 : 0);
- current->state = TASK_STOPPED;
- notify_parent(current, SIGCHLD);
- schedule();
+ ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
+ ? 0x80 : 0));
/*
* this isn't the same as continuing with a signal, but it will do