]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Copyright (C) 2000-2003, Axis Communications AB. | |
3 | */ | |
4 | ||
5 | #include <linux/kernel.h> | |
6 | #include <linux/sched.h> | |
7 | #include <linux/mm.h> | |
8 | #include <linux/smp.h> | |
1da177e4 LT |
9 | #include <linux/errno.h> |
10 | #include <linux/ptrace.h> | |
11 | #include <linux/user.h> | |
7ed20e1a | 12 | #include <linux/signal.h> |
5d01e6ce | 13 | #include <linux/security.h> |
1da177e4 LT |
14 | |
15 | #include <asm/uaccess.h> | |
16 | #include <asm/page.h> | |
17 | #include <asm/pgtable.h> | |
18 | #include <asm/system.h> | |
19 | #include <asm/processor.h> | |
20 | ||
21 | /* | |
22 | * Determines which bits in DCCR the user has access to. | |
23 | * 1 = access, 0 = no access. | |
24 | */ | |
25 | #define DCCR_MASK 0x0000001f /* XNZVC */ | |
26 | ||
27 | /* | |
28 | * Get contents of register REGNO in task TASK. | |
29 | */ | |
30 | inline long get_reg(struct task_struct *task, unsigned int regno) | |
31 | { | |
32 | /* USP is a special case, it's not in the pt_regs struct but | |
33 | * in the tasks thread struct | |
34 | */ | |
35 | ||
36 | if (regno == PT_USP) | |
37 | return task->thread.usp; | |
38 | else if (regno < PT_MAX) | |
95ca0dc6 | 39 | return ((unsigned long *)task_pt_regs(task))[regno]; |
1da177e4 LT |
40 | else |
41 | return 0; | |
42 | } | |
43 | ||
44 | /* | |
45 | * Write contents of register REGNO in task TASK. | |
46 | */ | |
47 | inline int put_reg(struct task_struct *task, unsigned int regno, | |
48 | unsigned long data) | |
49 | { | |
50 | if (regno == PT_USP) | |
51 | task->thread.usp = data; | |
52 | else if (regno < PT_MAX) | |
95ca0dc6 | 53 | ((unsigned long *)task_pt_regs(task))[regno] = data; |
1da177e4 LT |
54 | else |
55 | return -1; | |
56 | return 0; | |
57 | } | |
58 | ||
59 | /* | |
60 | * Called by kernel/ptrace.c when detaching. | |
61 | * | |
62 | * Make sure the single step bit is not set. | |
63 | */ | |
64 | void | |
65 | ptrace_disable(struct task_struct *child) | |
66 | { | |
67 | /* Todo - pending singlesteps? */ | |
2afab729 | 68 | clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); |
1da177e4 LT |
69 | } |
70 | ||
71 | /* | |
72 | * Note that this implementation of ptrace behaves differently from vanilla | |
73 | * ptrace. Contrary to what the man page says, in the PTRACE_PEEKTEXT, | |
74 | * PTRACE_PEEKDATA, and PTRACE_PEEKUSER requests the data variable is not | |
75 | * ignored. Instead, the data variable is expected to point at a location | |
76 | * (in user space) where the result of the ptrace call is written (instead of | |
77 | * being returned). | |
78 | */ | |
9b05a69e NK |
79 | long arch_ptrace(struct task_struct *child, long request, |
80 | unsigned long addr, unsigned long data) | |
1da177e4 | 81 | { |
1da177e4 | 82 | int ret; |
475a4b81 | 83 | unsigned int regno = addr >> 2; |
1da177e4 LT |
84 | unsigned long __user *datap = (unsigned long __user *)data; |
85 | ||
1da177e4 LT |
86 | switch (request) { |
87 | /* Read word at location address. */ | |
88 | case PTRACE_PEEKTEXT: | |
76647323 AD |
89 | case PTRACE_PEEKDATA: |
90 | ret = generic_ptrace_peekdata(child, addr, data); | |
1da177e4 | 91 | break; |
1da177e4 LT |
92 | |
93 | /* Read the word at location address in the USER area. */ | |
94 | case PTRACE_PEEKUSR: { | |
95 | unsigned long tmp; | |
96 | ||
97 | ret = -EIO; | |
475a4b81 | 98 | if ((addr & 3) || regno > PT_MAX) |
1da177e4 LT |
99 | break; |
100 | ||
475a4b81 | 101 | tmp = get_reg(child, regno); |
1da177e4 LT |
102 | ret = put_user(tmp, datap); |
103 | break; | |
104 | } | |
105 | ||
106 | /* Write the word at location address. */ | |
107 | case PTRACE_POKETEXT: | |
108 | case PTRACE_POKEDATA: | |
f284ce72 | 109 | ret = generic_ptrace_pokedata(child, addr, data); |
1da177e4 LT |
110 | break; |
111 | ||
112 | /* Write the word at location address in the USER area. */ | |
113 | case PTRACE_POKEUSR: | |
114 | ret = -EIO; | |
475a4b81 | 115 | if ((addr & 3) || regno > PT_MAX) |
1da177e4 LT |
116 | break; |
117 | ||
475a4b81 | 118 | if (regno == PT_DCCR) { |
1da177e4 LT |
119 | /* don't allow the tracing process to change stuff like |
120 | * interrupt enable, kernel/user bit, dma enables etc. | |
121 | */ | |
122 | data &= DCCR_MASK; | |
123 | data |= get_reg(child, PT_DCCR) & ~DCCR_MASK; | |
124 | } | |
475a4b81 | 125 | if (put_reg(child, regno, data)) |
1da177e4 LT |
126 | break; |
127 | ret = 0; | |
128 | break; | |
129 | ||
1da177e4 LT |
130 | /* Get all GP registers from the child. */ |
131 | case PTRACE_GETREGS: { | |
132 | int i; | |
133 | unsigned long tmp; | |
134 | ||
c3508858 | 135 | ret = 0; |
1da177e4 LT |
136 | for (i = 0; i <= PT_MAX; i++) { |
137 | tmp = get_reg(child, i); | |
138 | ||
139 | if (put_user(tmp, datap)) { | |
140 | ret = -EFAULT; | |
c3508858 | 141 | break; |
1da177e4 LT |
142 | } |
143 | ||
475a4b81 | 144 | datap++; |
1da177e4 LT |
145 | } |
146 | ||
1da177e4 LT |
147 | break; |
148 | } | |
149 | ||
150 | /* Set all GP registers in the child. */ | |
151 | case PTRACE_SETREGS: { | |
152 | int i; | |
153 | unsigned long tmp; | |
154 | ||
c3508858 | 155 | ret = 0; |
1da177e4 LT |
156 | for (i = 0; i <= PT_MAX; i++) { |
157 | if (get_user(tmp, datap)) { | |
158 | ret = -EFAULT; | |
c3508858 | 159 | break; |
1da177e4 LT |
160 | } |
161 | ||
162 | if (i == PT_DCCR) { | |
163 | tmp &= DCCR_MASK; | |
164 | tmp |= get_reg(child, PT_DCCR) & ~DCCR_MASK; | |
165 | } | |
166 | ||
167 | put_reg(child, i, tmp); | |
475a4b81 | 168 | datap++; |
1da177e4 LT |
169 | } |
170 | ||
1da177e4 LT |
171 | break; |
172 | } | |
173 | ||
174 | default: | |
175 | ret = ptrace_request(child, request, addr, data); | |
176 | break; | |
177 | } | |
481bed45 | 178 | |
1da177e4 LT |
179 | return ret; |
180 | } | |
181 | ||
182 | void do_syscall_trace(void) | |
183 | { | |
184 | if (!test_thread_flag(TIF_SYSCALL_TRACE)) | |
185 | return; | |
186 | ||
187 | if (!(current->ptrace & PT_PTRACED)) | |
188 | return; | |
189 | ||
190 | /* the 0x80 provides a way for the tracing parent to distinguish | |
191 | between a syscall stop and SIGTRAP delivery */ | |
192 | ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) | |
193 | ? 0x80 : 0)); | |
194 | ||
195 | /* | |
196 | * This isn't the same as continuing with a signal, but it will do for | |
197 | * normal use. | |
198 | */ | |
199 | if (current->exit_code) { | |
200 | send_sig(current->exit_code, current, 1); | |
201 | current->exit_code = 0; | |
202 | } | |
203 | } |