1/*
2 * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
3 * Licensed under the GPL
4 */
5
6#include <linux/audit.h>
7#include <linux/ptrace.h>
8#include <linux/sched.h>
9#include <linux/tracehook.h>
10#include <asm/uaccess.h>
11
12void user_enable_single_step(struct task_struct *child)
13{
14	child->ptrace |= PT_DTRACE;
15	child->thread.singlestep_syscall = 0;
16
17#ifdef SUBARCH_SET_SINGLESTEPPING
18	SUBARCH_SET_SINGLESTEPPING(child, 1);
19#endif
20}
21
22void user_disable_single_step(struct task_struct *child)
23{
24	child->ptrace &= ~PT_DTRACE;
25	child->thread.singlestep_syscall = 0;
26
27#ifdef SUBARCH_SET_SINGLESTEPPING
28	SUBARCH_SET_SINGLESTEPPING(child, 0);
29#endif
30}
31
32/*
33 * Called by kernel/ptrace.c when detaching..
34 */
35void ptrace_disable(struct task_struct *child)
36{
37	user_disable_single_step(child);
38}
39
40extern int peek_user(struct task_struct * child, long addr, long data);
41extern int poke_user(struct task_struct * child, long addr, long data);
42
43long arch_ptrace(struct task_struct *child, long request,
44		 unsigned long addr, unsigned long data)
45{
46	int i, ret;
47	unsigned long __user *p = (void __user *)data;
48	void __user *vp = p;
49
50	switch (request) {
51	/* read the word at location addr in the USER area. */
52	case PTRACE_PEEKUSR:
53		ret = peek_user(child, addr, data);
54		break;
55
56	/* write the word at location addr in the USER area */
57	case PTRACE_POKEUSR:
58		ret = poke_user(child, addr, data);
59		break;
60
61	case PTRACE_SYSEMU:
62	case PTRACE_SYSEMU_SINGLESTEP:
63		ret = -EIO;
64		break;
65
66#ifdef PTRACE_GETREGS
67	case PTRACE_GETREGS: { /* Get all gp regs from the child. */
68		if (!access_ok(VERIFY_WRITE, p, MAX_REG_OFFSET)) {
69			ret = -EIO;
70			break;
71		}
72		for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) {
73			__put_user(getreg(child, i), p);
74			p++;
75		}
76		ret = 0;
77		break;
78	}
79#endif
80#ifdef PTRACE_SETREGS
81	case PTRACE_SETREGS: { /* Set all gp regs in the child. */
82		unsigned long tmp = 0;
83		if (!access_ok(VERIFY_READ, p, MAX_REG_OFFSET)) {
84			ret = -EIO;
85			break;
86		}
87		for ( i = 0; i < MAX_REG_OFFSET; i += sizeof(long) ) {
88			__get_user(tmp, p);
89			putreg(child, i, tmp);
90			p++;
91		}
92		ret = 0;
93		break;
94	}
95#endif
96	case PTRACE_GET_THREAD_AREA:
97		ret = ptrace_get_thread_area(child, addr, vp);
98		break;
99
100	case PTRACE_SET_THREAD_AREA:
101		ret = ptrace_set_thread_area(child, addr, vp);
102		break;
103
104	default:
105		ret = ptrace_request(child, request, addr, data);
106		if (ret == -EIO)
107			ret = subarch_ptrace(child, request, addr, data);
108		break;
109	}
110
111	return ret;
112}
113
114static void send_sigtrap(struct task_struct *tsk, struct uml_pt_regs *regs,
115		  int error_code)
116{
117	struct siginfo info;
118
119	memset(&info, 0, sizeof(info));
120	info.si_signo = SIGTRAP;
121	info.si_code = TRAP_BRKPT;
122
123	/* User-mode eip? */
124	info.si_addr = UPT_IS_USER(regs) ? (void __user *) UPT_IP(regs) : NULL;
125
126	/* Send us the fake SIGTRAP */
127	force_sig_info(SIGTRAP, &info, tsk);
128}
129
130/*
131 * XXX Check PT_DTRACE vs TIF_SINGLESTEP for singlestepping check and
132 * PT_PTRACED vs TIF_SYSCALL_TRACE for syscall tracing check
133 */
134void syscall_trace_enter(struct pt_regs *regs)
135{
136	audit_syscall_entry(UPT_SYSCALL_NR(&regs->regs),
137			    UPT_SYSCALL_ARG1(&regs->regs),
138			    UPT_SYSCALL_ARG2(&regs->regs),
139			    UPT_SYSCALL_ARG3(&regs->regs),
140			    UPT_SYSCALL_ARG4(&regs->regs));
141
142	if (!test_thread_flag(TIF_SYSCALL_TRACE))
143		return;
144
145	tracehook_report_syscall_entry(regs);
146}
147
148void syscall_trace_leave(struct pt_regs *regs)
149{
150	int ptraced = current->ptrace;
151
152	audit_syscall_exit(regs);
153
154	/* Fake a debug trap */
155	if (ptraced & PT_DTRACE)
156		send_sigtrap(current, &regs->regs, 0);
157
158	if (!test_thread_flag(TIF_SYSCALL_TRACE))
159		return;
160
161	tracehook_report_syscall_exit(regs, 0);
162	/* force do_signal() --> is_syscall() */
163	if (ptraced & PT_PTRACED)
164		set_thread_flag(TIF_SIGPENDING);
165}
166