/* ptrace.c: Sparc process tracing support.
*
* 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,
* and David Mosberger.
*
* Added Linux support -miguel (weird, eh?, the original code was meant
* to emulate SunOS).
*/
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/smp.h>
#include <linux/security.h>
#include <linux/seccomp.h>
#include <linux/audit.h>
#include <linux/signal.h>
#include <linux/regset.h>
#include <linux/tracehook.h>
#include <trace/syscall.h>
#include <linux/compat.h>
#include <linux/elf.h>
#include <linux/context_tracking.h>
#include <asm/asi.h>
#include <asm/pgtable.h>
#include <asm/uaccess.h>
#include <asm/psrcompat.h>
#include <asm/visasm.h>
#include <asm/spitfire.h>
#include <asm/page.h>
#include <asm/cpudata.h>
#include <asm/cacheflush.h>
#define CREATE_TRACE_POINTS
#include <trace/events/syscalls.h>
#include "entry.h"
/* #define ALLOW_INIT_TRACING */
/*
* Called by kernel/ptrace.c when detaching..
*
* Make sure single step bits etc are not set.
*/
void ptrace_disable(struct task_struct *child)
{
/* 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);
if (tlb_type == hypervisor)
return;
preempt_disable();
#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 ^ (unsigned long) kaddr) & (1UL << 13)) {
unsigned long start = __pa(kaddr);
unsigned long end = start + len;
unsigned long dcache_line_size;
dcache_line_size = local_cpu_data().dcache_line_size;
if (tlb_type == spitfire) {
for (; start < end; start += dcache_line_size)
spitfire_put_dcache_tag(start & 0x3fe0, 0x0);
} else {
start &= ~(dcache_line_size - 1);
for (; start < end; start += dcache_line_size)
__asm__ __volatile__(
"stxa %%g0, [%0] %1\n\t"
"membar #Sync"
: /* no outputs */
: "r" (start),
"i" (ASI_DCACHE_INVALIDATE));
}
}
#endif
if (write && tlb_type == spitfire) {
unsigned long start = (unsigned long) kaddr;
unsigned long end = start + len;
unsigned long icache_line_size;
icache_line_size = local_cpu_data().icache_line_size;
for (; start < end; start += icache_line_size)
flushi(start);
}
preempt_enable();
}
EXPORT_SYMBOL_GPL(flush_ptrace_access);
static int get_from_target(struct task_struct *target, unsigned long uaddr,
void *kbuf, int len)
{
if (target == current) {
if (copy_from_user(kbuf, (void __user *) uaddr, len))
return -EFAULT;
} else {
int len2 = access_process_vm(target, uaddr, kbuf, len, 0);
if (len2 != len)
return -EFAULT;
}
return 0;
}
static int set_to_target(struct task_struct *target, unsigned long uaddr,
void *kbuf, int len)
{
if (target ==