diff options
author | David Daney <ddaney@avtrex.com> | 2008-09-23 03:11:26 -0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2008-10-11 11:18:57 -0400 |
commit | 0926bf953ee79b8f139741b442e5a18520f81705 (patch) | |
tree | 5e2cbdb2060ec9324866e6a072d1d7666849445c /arch/mips | |
parent | 2c708cbaa6031b73be6b992adb3ec1811f91cdb1 (diff) |
MIPS: Ptrace support for HARDWARE_WATCHPOINTS
This is the final part of the watch register patch. Here we hook up
ptrace so that the user space debugger (gdb), can set and read the
registers.
Signed-off-by: David Daney <ddaney@avtrex.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips')
-rw-r--r-- | arch/mips/include/asm/ptrace.h | 46 | ||||
-rw-r--r-- | arch/mips/kernel/ptrace.c | 100 | ||||
-rw-r--r-- | arch/mips/kernel/ptrace32.c | 10 |
3 files changed, 155 insertions, 1 deletions
diff --git a/arch/mips/include/asm/ptrace.h b/arch/mips/include/asm/ptrace.h index c00cca24dae0..cd5e8a47812b 100644 --- a/arch/mips/include/asm/ptrace.h +++ b/arch/mips/include/asm/ptrace.h | |||
@@ -74,11 +74,57 @@ struct pt_regs { | |||
74 | #define PTRACE_POKEDATA_3264 0xc3 | 74 | #define PTRACE_POKEDATA_3264 0xc3 |
75 | #define PTRACE_GET_THREAD_AREA_3264 0xc4 | 75 | #define PTRACE_GET_THREAD_AREA_3264 0xc4 |
76 | 76 | ||
77 | /* Read and write watchpoint registers. */ | ||
78 | enum pt_watch_style { | ||
79 | pt_watch_style_mips32, | ||
80 | pt_watch_style_mips64 | ||
81 | }; | ||
82 | struct mips32_watch_regs { | ||
83 | uint32_t watchlo[8]; | ||
84 | /* Lower 16 bits of watchhi. */ | ||
85 | uint16_t watchhi[8]; | ||
86 | /* Valid mask and I R W bits. | ||
87 | * bit 0 -- 1 if W bit is usable. | ||
88 | * bit 1 -- 1 if R bit is usable. | ||
89 | * bit 2 -- 1 if I bit is usable. | ||
90 | * bits 3 - 11 -- Valid watchhi mask bits. | ||
91 | */ | ||
92 | uint16_t watch_masks[8]; | ||
93 | /* The number of valid watch register pairs. */ | ||
94 | uint32_t num_valid; | ||
95 | } __attribute__((aligned(8))); | ||
96 | |||
97 | struct mips64_watch_regs { | ||
98 | uint64_t watchlo[8]; | ||
99 | uint16_t watchhi[8]; | ||
100 | uint16_t watch_masks[8]; | ||
101 | uint32_t num_valid; | ||
102 | } __attribute__((aligned(8))); | ||
103 | |||
104 | struct pt_watch_regs { | ||
105 | enum pt_watch_style style; | ||
106 | union { | ||
107 | struct mips32_watch_regs mips32; | ||
108 | struct mips32_watch_regs mips64; | ||
109 | }; | ||
110 | }; | ||
111 | |||
112 | #define PTRACE_GET_WATCH_REGS 0xd0 | ||
113 | #define PTRACE_SET_WATCH_REGS 0xd1 | ||
114 | |||
77 | #ifdef __KERNEL__ | 115 | #ifdef __KERNEL__ |
78 | 116 | ||
117 | #include <linux/compiler.h> | ||
79 | #include <linux/linkage.h> | 118 | #include <linux/linkage.h> |
80 | #include <asm/isadep.h> | 119 | #include <asm/isadep.h> |
81 | 120 | ||
121 | struct task_struct; | ||
122 | |||
123 | extern int ptrace_get_watch_regs(struct task_struct *child, | ||
124 | struct pt_watch_regs __user *addr); | ||
125 | extern int ptrace_set_watch_regs(struct task_struct *child, | ||
126 | struct pt_watch_regs __user *addr); | ||
127 | |||
82 | /* | 128 | /* |
83 | * Does the process account for user or for system time? | 129 | * Does the process account for user or for system time? |
84 | */ | 130 | */ |
diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c index 96ffc9c6d194..054861ccb4dd 100644 --- a/arch/mips/kernel/ptrace.c +++ b/arch/mips/kernel/ptrace.c | |||
@@ -46,7 +46,8 @@ | |||
46 | */ | 46 | */ |
47 | void ptrace_disable(struct task_struct *child) | 47 | void ptrace_disable(struct task_struct *child) |
48 | { | 48 | { |
49 | /* Nothing to do.. */ | 49 | /* Don't load the watchpoint registers for the ex-child. */ |
50 | clear_tsk_thread_flag(child, TIF_LOAD_WATCH); | ||
50 | } | 51 | } |
51 | 52 | ||
52 | /* | 53 | /* |
@@ -167,6 +168,93 @@ int ptrace_setfpregs(struct task_struct *child, __u32 __user *data) | |||
167 | return 0; | 168 | return 0; |
168 | } | 169 | } |
169 | 170 | ||
171 | int ptrace_get_watch_regs(struct task_struct *child, | ||
172 | struct pt_watch_regs __user *addr) | ||
173 | { | ||
174 | enum pt_watch_style style; | ||
175 | int i; | ||
176 | |||
177 | if (!cpu_has_watch || current_cpu_data.watch_reg_use_cnt == 0) | ||
178 | return -EIO; | ||
179 | if (!access_ok(VERIFY_WRITE, addr, sizeof(struct pt_watch_regs))) | ||
180 | return -EIO; | ||
181 | |||
182 | #ifdef CONFIG_32BIT | ||
183 | style = pt_watch_style_mips32; | ||
184 | #define WATCH_STYLE mips32 | ||
185 | #else | ||
186 | style = pt_watch_style_mips64; | ||
187 | #define WATCH_STYLE mips64 | ||
188 | #endif | ||
189 | |||
190 | __put_user(style, &addr->style); | ||
191 | __put_user(current_cpu_data.watch_reg_use_cnt, | ||
192 | &addr->WATCH_STYLE.num_valid); | ||
193 | for (i = 0; i < current_cpu_data.watch_reg_use_cnt; i++) { | ||
194 | __put_user(child->thread.watch.mips3264.watchlo[i], | ||
195 | &addr->WATCH_STYLE.watchlo[i]); | ||
196 | __put_user(child->thread.watch.mips3264.watchhi[i] & 0xfff, | ||
197 | &addr->WATCH_STYLE.watchhi[i]); | ||
198 | __put_user(current_cpu_data.watch_reg_masks[i], | ||
199 | &addr->WATCH_STYLE.watch_masks[i]); | ||
200 | } | ||
201 | for (; i < 8; i++) { | ||
202 | __put_user(0, &addr->WATCH_STYLE.watchlo[i]); | ||
203 | __put_user(0, &addr->WATCH_STYLE.watchhi[i]); | ||
204 | __put_user(0, &addr->WATCH_STYLE.watch_masks[i]); | ||
205 | } | ||
206 | |||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | int ptrace_set_watch_regs(struct task_struct *child, | ||
211 | struct pt_watch_regs __user *addr) | ||
212 | { | ||
213 | int i; | ||
214 | int watch_active = 0; | ||
215 | unsigned long lt[NUM_WATCH_REGS]; | ||
216 | u16 ht[NUM_WATCH_REGS]; | ||
217 | |||
218 | if (!cpu_has_watch || current_cpu_data.watch_reg_use_cnt == 0) | ||
219 | return -EIO; | ||
220 | if (!access_ok(VERIFY_READ, addr, sizeof(struct pt_watch_regs))) | ||
221 | return -EIO; | ||
222 | /* Check the values. */ | ||
223 | for (i = 0; i < current_cpu_data.watch_reg_use_cnt; i++) { | ||
224 | __get_user(lt[i], &addr->WATCH_STYLE.watchlo[i]); | ||
225 | #ifdef CONFIG_32BIT | ||
226 | if (lt[i] & __UA_LIMIT) | ||
227 | return -EINVAL; | ||
228 | #else | ||
229 | if (test_tsk_thread_flag(child, TIF_32BIT_ADDR)) { | ||
230 | if (lt[i] & 0xffffffff80000000UL) | ||
231 | return -EINVAL; | ||
232 | } else { | ||
233 | if (lt[i] & __UA_LIMIT) | ||
234 | return -EINVAL; | ||
235 | } | ||
236 | #endif | ||
237 | __get_user(ht[i], &addr->WATCH_STYLE.watchhi[i]); | ||
238 | if (ht[i] & ~0xff8) | ||
239 | return -EINVAL; | ||
240 | } | ||
241 | /* Install them. */ | ||
242 | for (i = 0; i < current_cpu_data.watch_reg_use_cnt; i++) { | ||
243 | if (lt[i] & 7) | ||
244 | watch_active = 1; | ||
245 | child->thread.watch.mips3264.watchlo[i] = lt[i]; | ||
246 | /* Set the G bit. */ | ||
247 | child->thread.watch.mips3264.watchhi[i] = ht[i]; | ||
248 | } | ||
249 | |||
250 | if (watch_active) | ||
251 | set_tsk_thread_flag(child, TIF_LOAD_WATCH); | ||
252 | else | ||
253 | clear_tsk_thread_flag(child, TIF_LOAD_WATCH); | ||
254 | |||
255 | return 0; | ||
256 | } | ||
257 | |||
170 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) | 258 | long arch_ptrace(struct task_struct *child, long request, long addr, long data) |
171 | { | 259 | { |
172 | int ret; | 260 | int ret; |
@@ -440,6 +528,16 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
440 | (unsigned long __user *) data); | 528 | (unsigned long __user *) data); |
441 | break; | 529 | break; |
442 | 530 | ||
531 | case PTRACE_GET_WATCH_REGS: | ||
532 | ret = ptrace_get_watch_regs(child, | ||
533 | (struct pt_watch_regs __user *) addr); | ||
534 | break; | ||
535 | |||
536 | case PTRACE_SET_WATCH_REGS: | ||
537 | ret = ptrace_set_watch_regs(child, | ||
538 | (struct pt_watch_regs __user *) addr); | ||
539 | break; | ||
540 | |||
443 | default: | 541 | default: |
444 | ret = ptrace_request(child, request, addr, data); | 542 | ret = ptrace_request(child, request, addr, data); |
445 | break; | 543 | break; |
diff --git a/arch/mips/kernel/ptrace32.c b/arch/mips/kernel/ptrace32.c index cac56a8c8679..e45105e3ef00 100644 --- a/arch/mips/kernel/ptrace32.c +++ b/arch/mips/kernel/ptrace32.c | |||
@@ -387,6 +387,16 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request, | |||
387 | (unsigned long __user *) (unsigned long) data); | 387 | (unsigned long __user *) (unsigned long) data); |
388 | break; | 388 | break; |
389 | 389 | ||
390 | case PTRACE_GET_WATCH_REGS: | ||
391 | ret = ptrace_get_watch_regs(child, | ||
392 | (struct pt_watch_regs __user *) (unsigned long) addr); | ||
393 | break; | ||
394 | |||
395 | case PTRACE_SET_WATCH_REGS: | ||
396 | ret = ptrace_set_watch_regs(child, | ||
397 | (struct pt_watch_regs __user *) (unsigned long) addr); | ||
398 | break; | ||
399 | |||
390 | default: | 400 | default: |
391 | ret = ptrace_request(child, request, addr, data); | 401 | ret = ptrace_request(child, request, addr, data); |
392 | break; | 402 | break; |