]>
Commit | Line | Data |
---|---|---|
43dec91f GU |
1 | /* |
2 | * linux/arch/m68k/kernel/ptrace.c | |
3 | * | |
4 | * Copyright (C) 1994 by Hamish Macdonald | |
5 | * Taken from linux/kernel/ptrace.c and modified for M680x0. | |
6 | * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds | |
7 | * | |
8 | * This file is subject to the terms and conditions of the GNU General | |
9 | * Public License. See the file COPYING in the main directory of | |
10 | * this archive for more details. | |
11 | */ | |
12 | ||
13 | #include <linux/kernel.h> | |
14 | #include <linux/sched.h> | |
15 | #include <linux/mm.h> | |
16 | #include <linux/smp.h> | |
17 | #include <linux/errno.h> | |
18 | #include <linux/ptrace.h> | |
19 | #include <linux/user.h> | |
20 | #include <linux/signal.h> | |
21 | #include <linux/tracehook.h> | |
22 | ||
23 | #include <asm/uaccess.h> | |
24 | #include <asm/page.h> | |
25 | #include <asm/pgtable.h> | |
43dec91f GU |
26 | #include <asm/processor.h> |
27 | ||
28 | /* | |
29 | * does not yet catch signals sent when the child dies. | |
30 | * in exit.c or in signal.c. | |
31 | */ | |
32 | ||
33 | /* determines which bits in the SR the user has access to. */ | |
34 | /* 1 = access 0 = no access */ | |
35 | #define SR_MASK 0x001f | |
36 | ||
37 | /* sets the trace bits. */ | |
38 | #define TRACE_BITS 0xC000 | |
39 | #define T1_BIT 0x8000 | |
40 | #define T0_BIT 0x4000 | |
41 | ||
42 | /* Find the stack offset for a register, relative to thread.esp0. */ | |
43 | #define PT_REG(reg) ((long)&((struct pt_regs *)0)->reg) | |
44 | #define SW_REG(reg) ((long)&((struct switch_stack *)0)->reg \ | |
45 | - sizeof(struct switch_stack)) | |
46 | /* Mapping from PT_xxx to the stack offset at which the register is | |
47 | saved. Notice that usp has no stack-slot and needs to be treated | |
48 | specially (see get_reg/put_reg below). */ | |
49 | static const int regoff[] = { | |
50 | [0] = PT_REG(d1), | |
51 | [1] = PT_REG(d2), | |
52 | [2] = PT_REG(d3), | |
53 | [3] = PT_REG(d4), | |
54 | [4] = PT_REG(d5), | |
55 | [5] = SW_REG(d6), | |
56 | [6] = SW_REG(d7), | |
57 | [7] = PT_REG(a0), | |
58 | [8] = PT_REG(a1), | |
59 | [9] = PT_REG(a2), | |
60 | [10] = SW_REG(a3), | |
61 | [11] = SW_REG(a4), | |
62 | [12] = SW_REG(a5), | |
63 | [13] = SW_REG(a6), | |
64 | [14] = PT_REG(d0), | |
65 | [15] = -1, | |
66 | [16] = PT_REG(orig_d0), | |
67 | [17] = PT_REG(sr), | |
68 | [18] = PT_REG(pc), | |
69 | }; | |
70 | ||
71 | /* | |
72 | * Get contents of register REGNO in task TASK. | |
73 | */ | |
74 | static inline long get_reg(struct task_struct *task, int regno) | |
75 | { | |
76 | unsigned long *addr; | |
77 | ||
78 | if (regno == PT_USP) | |
79 | addr = &task->thread.usp; | |
80 | else if (regno < ARRAY_SIZE(regoff)) | |
81 | addr = (unsigned long *)(task->thread.esp0 + regoff[regno]); | |
82 | else | |
83 | return 0; | |
84 | /* Need to take stkadj into account. */ | |
85 | if (regno == PT_SR || regno == PT_PC) { | |
86 | long stkadj = *(long *)(task->thread.esp0 + PT_REG(stkadj)); | |
87 | addr = (unsigned long *) ((unsigned long)addr + stkadj); | |
88 | /* The sr is actually a 16 bit register. */ | |
89 | if (regno == PT_SR) | |
90 | return *(unsigned short *)addr; | |
91 | } | |
92 | return *addr; | |
93 | } | |
94 | ||
95 | /* | |
96 | * Write contents of register REGNO in task TASK. | |
97 | */ | |
98 | static inline int put_reg(struct task_struct *task, int regno, | |
99 | unsigned long data) | |
100 | { | |
101 | unsigned long *addr; | |
102 | ||
103 | if (regno == PT_USP) | |
104 | addr = &task->thread.usp; | |
105 | else if (regno < ARRAY_SIZE(regoff)) | |
106 | addr = (unsigned long *)(task->thread.esp0 + regoff[regno]); | |
107 | else | |
108 | return -1; | |
109 | /* Need to take stkadj into account. */ | |
110 | if (regno == PT_SR || regno == PT_PC) { | |
111 | long stkadj = *(long *)(task->thread.esp0 + PT_REG(stkadj)); | |
112 | addr = (unsigned long *) ((unsigned long)addr + stkadj); | |
113 | /* The sr is actually a 16 bit register. */ | |
114 | if (regno == PT_SR) { | |
115 | *(unsigned short *)addr = data; | |
116 | return 0; | |
117 | } | |
118 | } | |
119 | *addr = data; | |
120 | return 0; | |
121 | } | |
122 | ||
123 | /* | |
124 | * Make sure the single step bit is not set. | |
125 | */ | |
126 | static inline void singlestep_disable(struct task_struct *child) | |
127 | { | |
128 | unsigned long tmp = get_reg(child, PT_SR) & ~TRACE_BITS; | |
129 | put_reg(child, PT_SR, tmp); | |
130 | clear_tsk_thread_flag(child, TIF_DELAYED_TRACE); | |
131 | } | |
132 | ||
133 | /* | |
134 | * Called by kernel/ptrace.c when detaching.. | |
135 | */ | |
136 | void ptrace_disable(struct task_struct *child) | |
137 | { | |
138 | singlestep_disable(child); | |
139 | } | |
140 | ||
141 | void user_enable_single_step(struct task_struct *child) | |
142 | { | |
143 | unsigned long tmp = get_reg(child, PT_SR) & ~TRACE_BITS; | |
144 | put_reg(child, PT_SR, tmp | T1_BIT); | |
145 | set_tsk_thread_flag(child, TIF_DELAYED_TRACE); | |
146 | } | |
147 | ||
66d857b0 | 148 | #ifdef CONFIG_MMU |
43dec91f GU |
149 | void user_enable_block_step(struct task_struct *child) |
150 | { | |
151 | unsigned long tmp = get_reg(child, PT_SR) & ~TRACE_BITS; | |
152 | put_reg(child, PT_SR, tmp | T0_BIT); | |
153 | } | |
66d857b0 | 154 | #endif |
43dec91f GU |
155 | |
156 | void user_disable_single_step(struct task_struct *child) | |
157 | { | |
158 | singlestep_disable(child); | |
159 | } | |
160 | ||
161 | long arch_ptrace(struct task_struct *child, long request, | |
162 | unsigned long addr, unsigned long data) | |
163 | { | |
164 | unsigned long tmp; | |
165 | int i, ret = 0; | |
166 | int regno = addr >> 2; /* temporary hack. */ | |
167 | unsigned long __user *datap = (unsigned long __user *) data; | |
168 | ||
169 | switch (request) { | |
170 | /* read the word at location addr in the USER area. */ | |
171 | case PTRACE_PEEKUSR: | |
172 | if (addr & 3) | |
173 | goto out_eio; | |
174 | ||
175 | if (regno >= 0 && regno < 19) { | |
176 | tmp = get_reg(child, regno); | |
177 | } else if (regno >= 21 && regno < 49) { | |
178 | tmp = child->thread.fp[regno - 21]; | |
179 | /* Convert internal fpu reg representation | |
180 | * into long double format | |
181 | */ | |
182 | if (FPU_IS_EMU && (regno < 45) && !(regno % 3)) | |
183 | tmp = ((tmp & 0xffff0000) << 15) | | |
184 | ((tmp & 0x0000ffff) << 16); | |
185 | #ifndef CONFIG_MMU | |
186 | } else if (regno == 49) { | |
187 | tmp = child->mm->start_code; | |
188 | } else if (regno == 50) { | |
189 | tmp = child->mm->start_data; | |
190 | } else if (regno == 51) { | |
191 | tmp = child->mm->end_code; | |
192 | #endif | |
193 | } else | |
194 | goto out_eio; | |
195 | ret = put_user(tmp, datap); | |
196 | break; | |
197 | ||
198 | case PTRACE_POKEUSR: | |
199 | /* write the word at location addr in the USER area */ | |
200 | if (addr & 3) | |
201 | goto out_eio; | |
202 | ||
203 | if (regno == PT_SR) { | |
204 | data &= SR_MASK; | |
205 | data |= get_reg(child, PT_SR) & ~SR_MASK; | |
206 | } | |
207 | if (regno >= 0 && regno < 19) { | |
208 | if (put_reg(child, regno, data)) | |
209 | goto out_eio; | |
210 | } else if (regno >= 21 && regno < 48) { | |
211 | /* Convert long double format | |
212 | * into internal fpu reg representation | |
213 | */ | |
214 | if (FPU_IS_EMU && (regno < 45) && !(regno % 3)) { | |
215 | data <<= 15; | |
216 | data = (data & 0xffff0000) | | |
217 | ((data & 0x0000ffff) >> 1); | |
218 | } | |
219 | child->thread.fp[regno - 21] = data; | |
220 | } else | |
221 | goto out_eio; | |
222 | break; | |
223 | ||
224 | case PTRACE_GETREGS: /* Get all gp regs from the child. */ | |
225 | for (i = 0; i < 19; i++) { | |
226 | tmp = get_reg(child, i); | |
227 | ret = put_user(tmp, datap); | |
228 | if (ret) | |
229 | break; | |
230 | datap++; | |
231 | } | |
232 | break; | |
233 | ||
234 | case PTRACE_SETREGS: /* Set all gp regs in the child. */ | |
235 | for (i = 0; i < 19; i++) { | |
236 | ret = get_user(tmp, datap); | |
237 | if (ret) | |
238 | break; | |
239 | if (i == PT_SR) { | |
240 | tmp &= SR_MASK; | |
241 | tmp |= get_reg(child, PT_SR) & ~SR_MASK; | |
242 | } | |
243 | put_reg(child, i, tmp); | |
244 | datap++; | |
245 | } | |
246 | break; | |
247 | ||
248 | case PTRACE_GETFPREGS: /* Get the child FPU state. */ | |
249 | if (copy_to_user(datap, &child->thread.fp, | |
250 | sizeof(struct user_m68kfp_struct))) | |
251 | ret = -EFAULT; | |
252 | break; | |
253 | ||
254 | case PTRACE_SETFPREGS: /* Set the child FPU state. */ | |
255 | if (copy_from_user(&child->thread.fp, datap, | |
256 | sizeof(struct user_m68kfp_struct))) | |
257 | ret = -EFAULT; | |
258 | break; | |
259 | ||
260 | case PTRACE_GET_THREAD_AREA: | |
261 | ret = put_user(task_thread_info(child)->tp_value, datap); | |
262 | break; | |
263 | ||
264 | default: | |
265 | ret = ptrace_request(child, request, addr, data); | |
266 | break; | |
267 | } | |
268 | ||
269 | return ret; | |
270 | out_eio: | |
271 | return -EIO; | |
272 | } | |
273 | ||
274 | asmlinkage void syscall_trace(void) | |
275 | { | |
276 | ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) | |
277 | ? 0x80 : 0)); | |
278 | /* | |
279 | * this isn't the same as continuing with a signal, but it will do | |
280 | * for normal use. strace only continues with a signal if the | |
281 | * stopping signal is not SIGTRAP. -brl | |
282 | */ | |
283 | if (current->exit_code) { | |
284 | send_sig(current->exit_code, current, 1); | |
285 | current->exit_code = 0; | |
286 | } | |
287 | } | |
288 | ||
70c778f7 | 289 | #if defined(CONFIG_COLDFIRE) || !defined(CONFIG_MMU) |
43dec91f GU |
290 | asmlinkage int syscall_trace_enter(void) |
291 | { | |
292 | int ret = 0; | |
293 | ||
294 | if (test_thread_flag(TIF_SYSCALL_TRACE)) | |
295 | ret = tracehook_report_syscall_entry(task_pt_regs(current)); | |
296 | return ret; | |
297 | } | |
298 | ||
299 | asmlinkage void syscall_trace_leave(void) | |
300 | { | |
301 | if (test_thread_flag(TIF_SYSCALL_TRACE)) | |
302 | tracehook_report_syscall_exit(task_pt_regs(current), 0); | |
303 | } | |
304 | #endif /* CONFIG_COLDFIRE */ |