Commit | Line | Data |
---|---|---|
ed9a39eb | 1 | /* GNU/Linux on ARM native support. |
32d0add0 | 2 | Copyright (C) 1999-2015 Free Software Foundation, Inc. |
ed9a39eb JM |
3 | |
4 | This file is part of GDB. | |
5 | ||
6 | This program is free software; you can redistribute it and/or modify | |
7 | it under the terms of the GNU General Public License as published by | |
a9762ec7 | 8 | the Free Software Foundation; either version 3 of the License, or |
ed9a39eb JM |
9 | (at your option) any later version. |
10 | ||
11 | This program is distributed in the hope that it will be useful, | |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | GNU General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
a9762ec7 | 17 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
ed9a39eb JM |
18 | |
19 | #include "defs.h" | |
20 | #include "inferior.h" | |
21 | #include "gdbcore.h" | |
4e052eda | 22 | #include "regcache.h" |
10d6c8cd DJ |
23 | #include "target.h" |
24 | #include "linux-nat.h" | |
05a4558a | 25 | #include "target-descriptions.h" |
3b273a55 | 26 | #include "auxv.h" |
e3039479 UW |
27 | #include "observer.h" |
28 | #include "gdbthread.h" | |
ed9a39eb | 29 | |
aeb98c60 | 30 | #include "arm-tdep.h" |
cb587d83 | 31 | #include "arm-linux-tdep.h" |
aeb98c60 | 32 | |
3b273a55 | 33 | #include <elf/common.h> |
ed9a39eb JM |
34 | #include <sys/user.h> |
35 | #include <sys/ptrace.h> | |
36 | #include <sys/utsname.h> | |
41c49b06 | 37 | #include <sys/procfs.h> |
ed9a39eb | 38 | |
95855ca8 MS |
39 | #include "nat/linux-ptrace.h" |
40 | ||
0963b4bd | 41 | /* Prototypes for supply_gregset etc. */ |
c60c0f5f MS |
42 | #include "gregset.h" |
43 | ||
9308fc88 DJ |
44 | /* Defines ps_err_e, struct ps_prochandle. */ |
45 | #include "gdb_proc_service.h" | |
46 | ||
47 | #ifndef PTRACE_GET_THREAD_AREA | |
48 | #define PTRACE_GET_THREAD_AREA 22 | |
49 | #endif | |
50 | ||
05a4558a DJ |
51 | #ifndef PTRACE_GETWMMXREGS |
52 | #define PTRACE_GETWMMXREGS 18 | |
53 | #define PTRACE_SETWMMXREGS 19 | |
54 | #endif | |
55 | ||
3b273a55 RE |
56 | #ifndef PTRACE_GETVFPREGS |
57 | #define PTRACE_GETVFPREGS 27 | |
58 | #define PTRACE_SETVFPREGS 28 | |
59 | #endif | |
60 | ||
e3039479 UW |
61 | #ifndef PTRACE_GETHBPREGS |
62 | #define PTRACE_GETHBPREGS 29 | |
63 | #define PTRACE_SETHBPREGS 30 | |
64 | #endif | |
65 | ||
ed9a39eb JM |
66 | extern int arm_apcs_32; |
67 | ||
fdf39c9a | 68 | /* On GNU/Linux, threads are implemented as pseudo-processes, in which |
41c49b06 | 69 | case we may be tracing more than one process at a time. In that |
39f77062 | 70 | case, inferior_ptid will contain the main process ID and the |
fdf39c9a RE |
71 | individual thread (process) ID. get_thread_id () is used to get |
72 | the thread id if it's available, and the process id otherwise. */ | |
41c49b06 | 73 | |
4e841acf | 74 | static int |
39f77062 | 75 | get_thread_id (ptid_t ptid) |
41c49b06 | 76 | { |
dfd4cc63 | 77 | int tid = ptid_get_lwp (ptid); |
39f77062 | 78 | if (0 == tid) |
dfd4cc63 | 79 | tid = ptid_get_pid (ptid); |
41c49b06 SB |
80 | return tid; |
81 | } | |
3b273a55 | 82 | |
05a4558a | 83 | #define GET_THREAD_ID(PTID) get_thread_id (PTID) |
41c49b06 | 84 | |
41c49b06 | 85 | /* Get the value of a particular register from the floating point |
c6b92abd | 86 | state of the process and store it into regcache. */ |
41c49b06 SB |
87 | |
88 | static void | |
56be3814 | 89 | fetch_fpregister (struct regcache *regcache, int regno) |
41c49b06 SB |
90 | { |
91 | int ret, tid; | |
cb587d83 | 92 | gdb_byte fp[ARM_LINUX_SIZEOF_NWFPE]; |
df9d7ec9 | 93 | |
41c49b06 | 94 | /* Get the thread id for the ptrace call. */ |
39f77062 | 95 | tid = GET_THREAD_ID (inferior_ptid); |
41c49b06 SB |
96 | |
97 | /* Read the floating point state. */ | |
0bdb2f78 | 98 | if (have_ptrace_getregset == TRIBOOL_TRUE) |
df9d7ec9 YQ |
99 | { |
100 | struct iovec iov; | |
101 | ||
102 | iov.iov_base = &fp; | |
103 | iov.iov_len = ARM_LINUX_SIZEOF_NWFPE; | |
104 | ||
105 | ret = ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, &iov); | |
106 | } | |
107 | else | |
108 | ret = ptrace (PT_GETFPREGS, tid, 0, fp); | |
109 | ||
41c49b06 SB |
110 | if (ret < 0) |
111 | { | |
edefbb7c | 112 | warning (_("Unable to fetch floating point register.")); |
41c49b06 SB |
113 | return; |
114 | } | |
115 | ||
116 | /* Fetch fpsr. */ | |
34e8f22d | 117 | if (ARM_FPS_REGNUM == regno) |
56be3814 | 118 | regcache_raw_supply (regcache, ARM_FPS_REGNUM, |
cb587d83 | 119 | fp + NWFPE_FPSR_OFFSET); |
41c49b06 SB |
120 | |
121 | /* Fetch the floating point register. */ | |
34e8f22d | 122 | if (regno >= ARM_F0_REGNUM && regno <= ARM_F7_REGNUM) |
56be3814 | 123 | supply_nwfpe_register (regcache, regno, fp); |
41c49b06 SB |
124 | } |
125 | ||
126 | /* Get the whole floating point state of the process and store it | |
c6b92abd | 127 | into regcache. */ |
ed9a39eb JM |
128 | |
129 | static void | |
56be3814 | 130 | fetch_fpregs (struct regcache *regcache) |
ed9a39eb | 131 | { |
41c49b06 | 132 | int ret, regno, tid; |
cb587d83 | 133 | gdb_byte fp[ARM_LINUX_SIZEOF_NWFPE]; |
ed9a39eb | 134 | |
41c49b06 | 135 | /* Get the thread id for the ptrace call. */ |
39f77062 | 136 | tid = GET_THREAD_ID (inferior_ptid); |
df9d7ec9 | 137 | |
ed9a39eb | 138 | /* Read the floating point state. */ |
0bdb2f78 | 139 | if (have_ptrace_getregset == TRIBOOL_TRUE) |
df9d7ec9 YQ |
140 | { |
141 | struct iovec iov; | |
142 | ||
143 | iov.iov_base = &fp; | |
144 | iov.iov_len = ARM_LINUX_SIZEOF_NWFPE; | |
145 | ||
146 | ret = ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, &iov); | |
147 | } | |
148 | else | |
149 | ret = ptrace (PT_GETFPREGS, tid, 0, fp); | |
150 | ||
ed9a39eb JM |
151 | if (ret < 0) |
152 | { | |
edefbb7c | 153 | warning (_("Unable to fetch the floating point registers.")); |
ed9a39eb JM |
154 | return; |
155 | } | |
156 | ||
157 | /* Fetch fpsr. */ | |
56be3814 | 158 | regcache_raw_supply (regcache, ARM_FPS_REGNUM, |
cb587d83 | 159 | fp + NWFPE_FPSR_OFFSET); |
ed9a39eb JM |
160 | |
161 | /* Fetch the floating point registers. */ | |
34e8f22d | 162 | for (regno = ARM_F0_REGNUM; regno <= ARM_F7_REGNUM; regno++) |
56be3814 | 163 | supply_nwfpe_register (regcache, regno, fp); |
ed9a39eb JM |
164 | } |
165 | ||
41c49b06 | 166 | /* Save a particular register into the floating point state of the |
c6b92abd | 167 | process using the contents from regcache. */ |
41c49b06 SB |
168 | |
169 | static void | |
56be3814 | 170 | store_fpregister (const struct regcache *regcache, int regno) |
41c49b06 SB |
171 | { |
172 | int ret, tid; | |
cb587d83 | 173 | gdb_byte fp[ARM_LINUX_SIZEOF_NWFPE]; |
41c49b06 SB |
174 | |
175 | /* Get the thread id for the ptrace call. */ | |
39f77062 | 176 | tid = GET_THREAD_ID (inferior_ptid); |
df9d7ec9 | 177 | |
41c49b06 | 178 | /* Read the floating point state. */ |
0bdb2f78 | 179 | if (have_ptrace_getregset == TRIBOOL_TRUE) |
df9d7ec9 YQ |
180 | { |
181 | struct iovec iov; | |
182 | ||
183 | iov.iov_base = &fp; | |
184 | iov.iov_len = ARM_LINUX_SIZEOF_NWFPE; | |
185 | ||
186 | ret = ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, &iov); | |
187 | } | |
188 | else | |
189 | ret = ptrace (PT_GETFPREGS, tid, 0, fp); | |
190 | ||
41c49b06 SB |
191 | if (ret < 0) |
192 | { | |
edefbb7c | 193 | warning (_("Unable to fetch the floating point registers.")); |
41c49b06 SB |
194 | return; |
195 | } | |
196 | ||
197 | /* Store fpsr. */ | |
672c9795 YQ |
198 | if (ARM_FPS_REGNUM == regno |
199 | && REG_VALID == regcache_register_status (regcache, ARM_FPS_REGNUM)) | |
56be3814 | 200 | regcache_raw_collect (regcache, ARM_FPS_REGNUM, fp + NWFPE_FPSR_OFFSET); |
41c49b06 SB |
201 | |
202 | /* Store the floating point register. */ | |
34e8f22d | 203 | if (regno >= ARM_F0_REGNUM && regno <= ARM_F7_REGNUM) |
56be3814 | 204 | collect_nwfpe_register (regcache, regno, fp); |
41c49b06 | 205 | |
0bdb2f78 | 206 | if (have_ptrace_getregset == TRIBOOL_TRUE) |
df9d7ec9 YQ |
207 | { |
208 | struct iovec iov; | |
209 | ||
210 | iov.iov_base = &fp; | |
211 | iov.iov_len = ARM_LINUX_SIZEOF_NWFPE; | |
212 | ||
213 | ret = ptrace (PTRACE_SETREGSET, tid, NT_FPREGSET, &iov); | |
214 | } | |
215 | else | |
216 | ret = ptrace (PTRACE_SETFPREGS, tid, 0, fp); | |
217 | ||
41c49b06 SB |
218 | if (ret < 0) |
219 | { | |
edefbb7c | 220 | warning (_("Unable to store floating point register.")); |
41c49b06 SB |
221 | return; |
222 | } | |
223 | } | |
224 | ||
ed9a39eb | 225 | /* Save the whole floating point state of the process using |
c6b92abd | 226 | the contents from regcache. */ |
ed9a39eb JM |
227 | |
228 | static void | |
56be3814 | 229 | store_fpregs (const struct regcache *regcache) |
ed9a39eb | 230 | { |
41c49b06 | 231 | int ret, regno, tid; |
cb587d83 | 232 | gdb_byte fp[ARM_LINUX_SIZEOF_NWFPE]; |
ed9a39eb | 233 | |
41c49b06 | 234 | /* Get the thread id for the ptrace call. */ |
39f77062 | 235 | tid = GET_THREAD_ID (inferior_ptid); |
df9d7ec9 | 236 | |
41c49b06 | 237 | /* Read the floating point state. */ |
0bdb2f78 | 238 | if (have_ptrace_getregset == TRIBOOL_TRUE) |
df9d7ec9 YQ |
239 | { |
240 | elf_fpregset_t fpregs; | |
241 | struct iovec iov; | |
242 | ||
243 | iov.iov_base = &fpregs; | |
244 | iov.iov_len = sizeof (fpregs); | |
245 | ||
246 | ret = ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, &iov); | |
247 | } | |
248 | else | |
249 | ret = ptrace (PT_GETFPREGS, tid, 0, fp); | |
250 | ||
41c49b06 SB |
251 | if (ret < 0) |
252 | { | |
edefbb7c | 253 | warning (_("Unable to fetch the floating point registers.")); |
41c49b06 SB |
254 | return; |
255 | } | |
256 | ||
ed9a39eb | 257 | /* Store fpsr. */ |
672c9795 | 258 | if (REG_VALID == regcache_register_status (regcache, ARM_FPS_REGNUM)) |
56be3814 | 259 | regcache_raw_collect (regcache, ARM_FPS_REGNUM, fp + NWFPE_FPSR_OFFSET); |
ed9a39eb JM |
260 | |
261 | /* Store the floating point registers. */ | |
34e8f22d | 262 | for (regno = ARM_F0_REGNUM; regno <= ARM_F7_REGNUM; regno++) |
672c9795 | 263 | if (REG_VALID == regcache_register_status (regcache, regno)) |
56be3814 | 264 | collect_nwfpe_register (regcache, regno, fp); |
ed9a39eb | 265 | |
0bdb2f78 | 266 | if (have_ptrace_getregset == TRIBOOL_TRUE) |
df9d7ec9 YQ |
267 | { |
268 | struct iovec iov; | |
269 | ||
270 | iov.iov_base = &fp; | |
271 | iov.iov_len = ARM_LINUX_SIZEOF_NWFPE; | |
272 | ||
273 | ret = ptrace (PTRACE_SETREGSET, tid, NT_FPREGSET, &iov); | |
274 | } | |
275 | else | |
276 | ret = ptrace (PTRACE_SETFPREGS, tid, 0, fp); | |
277 | ||
ed9a39eb JM |
278 | if (ret < 0) |
279 | { | |
edefbb7c | 280 | warning (_("Unable to store floating point registers.")); |
ed9a39eb JM |
281 | return; |
282 | } | |
283 | } | |
284 | ||
41c49b06 | 285 | /* Fetch a general register of the process and store into |
c6b92abd | 286 | regcache. */ |
41c49b06 SB |
287 | |
288 | static void | |
56be3814 | 289 | fetch_register (struct regcache *regcache, int regno) |
41c49b06 SB |
290 | { |
291 | int ret, tid; | |
c2152441 | 292 | elf_gregset_t regs; |
41c49b06 SB |
293 | |
294 | /* Get the thread id for the ptrace call. */ | |
39f77062 | 295 | tid = GET_THREAD_ID (inferior_ptid); |
10766686 | 296 | |
0bdb2f78 | 297 | if (have_ptrace_getregset == TRIBOOL_TRUE) |
10766686 YQ |
298 | { |
299 | struct iovec iov; | |
300 | ||
301 | iov.iov_base = ®s; | |
302 | iov.iov_len = sizeof (regs); | |
303 | ||
304 | ret = ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, &iov); | |
305 | } | |
306 | else | |
307 | ret = ptrace (PTRACE_GETREGS, tid, 0, ®s); | |
308 | ||
41c49b06 SB |
309 | if (ret < 0) |
310 | { | |
edefbb7c | 311 | warning (_("Unable to fetch general register.")); |
41c49b06 SB |
312 | return; |
313 | } | |
314 | ||
34e8f22d | 315 | if (regno >= ARM_A1_REGNUM && regno < ARM_PC_REGNUM) |
56be3814 | 316 | regcache_raw_supply (regcache, regno, (char *) ®s[regno]); |
41c49b06 | 317 | |
34e8f22d | 318 | if (ARM_PS_REGNUM == regno) |
41c49b06 SB |
319 | { |
320 | if (arm_apcs_32) | |
56be3814 | 321 | regcache_raw_supply (regcache, ARM_PS_REGNUM, |
17c12639 | 322 | (char *) ®s[ARM_CPSR_GREGNUM]); |
41c49b06 | 323 | else |
56be3814 | 324 | regcache_raw_supply (regcache, ARM_PS_REGNUM, |
23a6d369 | 325 | (char *) ®s[ARM_PC_REGNUM]); |
41c49b06 SB |
326 | } |
327 | ||
34e8f22d | 328 | if (ARM_PC_REGNUM == regno) |
41c49b06 | 329 | { |
bf6ae464 | 330 | regs[ARM_PC_REGNUM] = gdbarch_addr_bits_remove |
08790784 UW |
331 | (get_regcache_arch (regcache), |
332 | regs[ARM_PC_REGNUM]); | |
56be3814 | 333 | regcache_raw_supply (regcache, ARM_PC_REGNUM, |
23a6d369 | 334 | (char *) ®s[ARM_PC_REGNUM]); |
41c49b06 SB |
335 | } |
336 | } | |
337 | ||
ed9a39eb | 338 | /* Fetch all general registers of the process and store into |
c6b92abd | 339 | regcache. */ |
ed9a39eb JM |
340 | |
341 | static void | |
56be3814 | 342 | fetch_regs (struct regcache *regcache) |
ed9a39eb | 343 | { |
41c49b06 | 344 | int ret, regno, tid; |
c2152441 | 345 | elf_gregset_t regs; |
ed9a39eb | 346 | |
41c49b06 | 347 | /* Get the thread id for the ptrace call. */ |
39f77062 | 348 | tid = GET_THREAD_ID (inferior_ptid); |
10766686 | 349 | |
0bdb2f78 | 350 | if (have_ptrace_getregset == TRIBOOL_TRUE) |
10766686 YQ |
351 | { |
352 | struct iovec iov; | |
353 | ||
354 | iov.iov_base = ®s; | |
355 | iov.iov_len = sizeof (regs); | |
356 | ||
357 | ret = ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, &iov); | |
358 | } | |
359 | else | |
360 | ret = ptrace (PTRACE_GETREGS, tid, 0, ®s); | |
361 | ||
ed9a39eb JM |
362 | if (ret < 0) |
363 | { | |
edefbb7c | 364 | warning (_("Unable to fetch general registers.")); |
ed9a39eb JM |
365 | return; |
366 | } | |
367 | ||
34e8f22d | 368 | for (regno = ARM_A1_REGNUM; regno < ARM_PC_REGNUM; regno++) |
56be3814 | 369 | regcache_raw_supply (regcache, regno, (char *) ®s[regno]); |
ed9a39eb JM |
370 | |
371 | if (arm_apcs_32) | |
56be3814 | 372 | regcache_raw_supply (regcache, ARM_PS_REGNUM, |
17c12639 | 373 | (char *) ®s[ARM_CPSR_GREGNUM]); |
ed9a39eb | 374 | else |
56be3814 | 375 | regcache_raw_supply (regcache, ARM_PS_REGNUM, |
23a6d369 | 376 | (char *) ®s[ARM_PC_REGNUM]); |
ed9a39eb | 377 | |
bf6ae464 | 378 | regs[ARM_PC_REGNUM] = gdbarch_addr_bits_remove |
08790784 | 379 | (get_regcache_arch (regcache), regs[ARM_PC_REGNUM]); |
56be3814 | 380 | regcache_raw_supply (regcache, ARM_PC_REGNUM, |
23a6d369 | 381 | (char *) ®s[ARM_PC_REGNUM]); |
ed9a39eb JM |
382 | } |
383 | ||
384 | /* Store all general registers of the process from the values in | |
c6b92abd | 385 | regcache. */ |
ed9a39eb | 386 | |
41c49b06 | 387 | static void |
56be3814 | 388 | store_register (const struct regcache *regcache, int regno) |
41c49b06 SB |
389 | { |
390 | int ret, tid; | |
c2152441 | 391 | elf_gregset_t regs; |
41c49b06 | 392 | |
672c9795 | 393 | if (REG_VALID != regcache_register_status (regcache, regno)) |
41c49b06 SB |
394 | return; |
395 | ||
396 | /* Get the thread id for the ptrace call. */ | |
39f77062 | 397 | tid = GET_THREAD_ID (inferior_ptid); |
10766686 | 398 | |
41c49b06 | 399 | /* Get the general registers from the process. */ |
0bdb2f78 | 400 | if (have_ptrace_getregset == TRIBOOL_TRUE) |
10766686 YQ |
401 | { |
402 | struct iovec iov; | |
403 | ||
404 | iov.iov_base = ®s; | |
405 | iov.iov_len = sizeof (regs); | |
406 | ||
407 | ret = ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, &iov); | |
408 | } | |
409 | else | |
410 | ret = ptrace (PTRACE_GETREGS, tid, 0, ®s); | |
411 | ||
41c49b06 SB |
412 | if (ret < 0) |
413 | { | |
edefbb7c | 414 | warning (_("Unable to fetch general registers.")); |
41c49b06 SB |
415 | return; |
416 | } | |
417 | ||
34e8f22d | 418 | if (regno >= ARM_A1_REGNUM && regno <= ARM_PC_REGNUM) |
56be3814 | 419 | regcache_raw_collect (regcache, regno, (char *) ®s[regno]); |
adb8a87c | 420 | else if (arm_apcs_32 && regno == ARM_PS_REGNUM) |
56be3814 | 421 | regcache_raw_collect (regcache, regno, |
17c12639 | 422 | (char *) ®s[ARM_CPSR_GREGNUM]); |
adb8a87c | 423 | else if (!arm_apcs_32 && regno == ARM_PS_REGNUM) |
56be3814 | 424 | regcache_raw_collect (regcache, ARM_PC_REGNUM, |
adb8a87c | 425 | (char *) ®s[ARM_PC_REGNUM]); |
41c49b06 | 426 | |
0bdb2f78 | 427 | if (have_ptrace_getregset == TRIBOOL_TRUE) |
10766686 YQ |
428 | { |
429 | struct iovec iov; | |
430 | ||
431 | iov.iov_base = ®s; | |
432 | iov.iov_len = sizeof (regs); | |
433 | ||
434 | ret = ptrace (PTRACE_SETREGSET, tid, NT_PRSTATUS, &iov); | |
435 | } | |
436 | else | |
437 | ret = ptrace (PTRACE_SETREGS, tid, 0, ®s); | |
438 | ||
41c49b06 SB |
439 | if (ret < 0) |
440 | { | |
edefbb7c | 441 | warning (_("Unable to store general register.")); |
41c49b06 SB |
442 | return; |
443 | } | |
444 | } | |
445 | ||
ed9a39eb | 446 | static void |
56be3814 | 447 | store_regs (const struct regcache *regcache) |
ed9a39eb | 448 | { |
41c49b06 | 449 | int ret, regno, tid; |
c2152441 | 450 | elf_gregset_t regs; |
ed9a39eb | 451 | |
41c49b06 | 452 | /* Get the thread id for the ptrace call. */ |
39f77062 | 453 | tid = GET_THREAD_ID (inferior_ptid); |
10766686 | 454 | |
41c49b06 | 455 | /* Fetch the general registers. */ |
0bdb2f78 | 456 | if (have_ptrace_getregset == TRIBOOL_TRUE) |
10766686 YQ |
457 | { |
458 | struct iovec iov; | |
459 | ||
460 | iov.iov_base = ®s; | |
461 | iov.iov_len = sizeof (regs); | |
462 | ||
463 | ret = ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, &iov); | |
464 | } | |
465 | else | |
466 | ret = ptrace (PTRACE_GETREGS, tid, 0, ®s); | |
467 | ||
ed9a39eb JM |
468 | if (ret < 0) |
469 | { | |
edefbb7c | 470 | warning (_("Unable to fetch general registers.")); |
ed9a39eb JM |
471 | return; |
472 | } | |
473 | ||
34e8f22d | 474 | for (regno = ARM_A1_REGNUM; regno <= ARM_PC_REGNUM; regno++) |
ed9a39eb | 475 | { |
672c9795 | 476 | if (REG_VALID == regcache_register_status (regcache, regno)) |
56be3814 | 477 | regcache_raw_collect (regcache, regno, (char *) ®s[regno]); |
ed9a39eb JM |
478 | } |
479 | ||
672c9795 | 480 | if (arm_apcs_32 && REG_VALID == regcache_register_status (regcache, ARM_PS_REGNUM)) |
56be3814 | 481 | regcache_raw_collect (regcache, ARM_PS_REGNUM, |
17c12639 | 482 | (char *) ®s[ARM_CPSR_GREGNUM]); |
adb8a87c | 483 | |
0bdb2f78 | 484 | if (have_ptrace_getregset == TRIBOOL_TRUE) |
10766686 YQ |
485 | { |
486 | struct iovec iov; | |
487 | ||
488 | iov.iov_base = ®s; | |
489 | iov.iov_len = sizeof (regs); | |
490 | ||
491 | ret = ptrace (PTRACE_SETREGSET, tid, NT_PRSTATUS, &iov); | |
492 | } | |
493 | else | |
494 | ret = ptrace (PTRACE_SETREGS, tid, 0, ®s); | |
ed9a39eb JM |
495 | |
496 | if (ret < 0) | |
497 | { | |
edefbb7c | 498 | warning (_("Unable to store general registers.")); |
ed9a39eb JM |
499 | return; |
500 | } | |
501 | } | |
502 | ||
05a4558a DJ |
503 | /* Fetch all WMMX registers of the process and store into |
504 | regcache. */ | |
505 | ||
506 | #define IWMMXT_REGS_SIZE (16 * 8 + 6 * 4) | |
507 | ||
508 | static void | |
56be3814 | 509 | fetch_wmmx_regs (struct regcache *regcache) |
05a4558a DJ |
510 | { |
511 | char regbuf[IWMMXT_REGS_SIZE]; | |
512 | int ret, regno, tid; | |
513 | ||
514 | /* Get the thread id for the ptrace call. */ | |
515 | tid = GET_THREAD_ID (inferior_ptid); | |
516 | ||
517 | ret = ptrace (PTRACE_GETWMMXREGS, tid, 0, regbuf); | |
518 | if (ret < 0) | |
519 | { | |
520 | warning (_("Unable to fetch WMMX registers.")); | |
521 | return; | |
522 | } | |
523 | ||
524 | for (regno = 0; regno < 16; regno++) | |
56be3814 | 525 | regcache_raw_supply (regcache, regno + ARM_WR0_REGNUM, |
05a4558a DJ |
526 | ®buf[regno * 8]); |
527 | ||
528 | for (regno = 0; regno < 2; regno++) | |
56be3814 | 529 | regcache_raw_supply (regcache, regno + ARM_WCSSF_REGNUM, |
05a4558a DJ |
530 | ®buf[16 * 8 + regno * 4]); |
531 | ||
532 | for (regno = 0; regno < 4; regno++) | |
56be3814 | 533 | regcache_raw_supply (regcache, regno + ARM_WCGR0_REGNUM, |
05a4558a DJ |
534 | ®buf[16 * 8 + 2 * 4 + regno * 4]); |
535 | } | |
536 | ||
537 | static void | |
56be3814 | 538 | store_wmmx_regs (const struct regcache *regcache) |
05a4558a DJ |
539 | { |
540 | char regbuf[IWMMXT_REGS_SIZE]; | |
541 | int ret, regno, tid; | |
542 | ||
543 | /* Get the thread id for the ptrace call. */ | |
544 | tid = GET_THREAD_ID (inferior_ptid); | |
545 | ||
546 | ret = ptrace (PTRACE_GETWMMXREGS, tid, 0, regbuf); | |
547 | if (ret < 0) | |
548 | { | |
549 | warning (_("Unable to fetch WMMX registers.")); | |
550 | return; | |
551 | } | |
552 | ||
553 | for (regno = 0; regno < 16; regno++) | |
672c9795 YQ |
554 | if (REG_VALID == regcache_register_status (regcache, |
555 | regno + ARM_WR0_REGNUM)) | |
56be3814 | 556 | regcache_raw_collect (regcache, regno + ARM_WR0_REGNUM, |
05a4558a DJ |
557 | ®buf[regno * 8]); |
558 | ||
559 | for (regno = 0; regno < 2; regno++) | |
672c9795 YQ |
560 | if (REG_VALID == regcache_register_status (regcache, |
561 | regno + ARM_WCSSF_REGNUM)) | |
56be3814 | 562 | regcache_raw_collect (regcache, regno + ARM_WCSSF_REGNUM, |
05a4558a DJ |
563 | ®buf[16 * 8 + regno * 4]); |
564 | ||
565 | for (regno = 0; regno < 4; regno++) | |
672c9795 YQ |
566 | if (REG_VALID == regcache_register_status (regcache, |
567 | regno + ARM_WCGR0_REGNUM)) | |
56be3814 | 568 | regcache_raw_collect (regcache, regno + ARM_WCGR0_REGNUM, |
05a4558a DJ |
569 | ®buf[16 * 8 + 2 * 4 + regno * 4]); |
570 | ||
571 | ret = ptrace (PTRACE_SETWMMXREGS, tid, 0, regbuf); | |
572 | ||
573 | if (ret < 0) | |
574 | { | |
575 | warning (_("Unable to store WMMX registers.")); | |
576 | return; | |
577 | } | |
578 | } | |
579 | ||
3b273a55 RE |
580 | /* Fetch and store VFP Registers. The kernel object has space for 32 |
581 | 64-bit registers, and the FPSCR. This is even when on a VFPv2 or | |
582 | VFPv3D16 target. */ | |
583 | #define VFP_REGS_SIZE (32 * 8 + 4) | |
584 | ||
585 | static void | |
586 | fetch_vfp_regs (struct regcache *regcache) | |
587 | { | |
588 | char regbuf[VFP_REGS_SIZE]; | |
589 | int ret, regno, tid; | |
330c6ca9 YQ |
590 | struct gdbarch *gdbarch = get_regcache_arch (regcache); |
591 | struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); | |
3b273a55 RE |
592 | |
593 | /* Get the thread id for the ptrace call. */ | |
594 | tid = GET_THREAD_ID (inferior_ptid); | |
595 | ||
0bdb2f78 | 596 | if (have_ptrace_getregset == TRIBOOL_TRUE) |
bd16da51 YQ |
597 | { |
598 | struct iovec iov; | |
599 | ||
600 | iov.iov_base = regbuf; | |
601 | iov.iov_len = VFP_REGS_SIZE; | |
602 | ret = ptrace (PTRACE_GETREGSET, tid, NT_ARM_VFP, &iov); | |
603 | } | |
604 | else | |
605 | ret = ptrace (PTRACE_GETVFPREGS, tid, 0, regbuf); | |
606 | ||
3b273a55 RE |
607 | if (ret < 0) |
608 | { | |
609 | warning (_("Unable to fetch VFP registers.")); | |
610 | return; | |
611 | } | |
612 | ||
330c6ca9 | 613 | for (regno = 0; regno < tdep->vfp_register_count; regno++) |
3b273a55 RE |
614 | regcache_raw_supply (regcache, regno + ARM_D0_REGNUM, |
615 | (char *) regbuf + regno * 8); | |
616 | ||
617 | regcache_raw_supply (regcache, ARM_FPSCR_REGNUM, | |
618 | (char *) regbuf + 32 * 8); | |
619 | } | |
620 | ||
621 | static void | |
622 | store_vfp_regs (const struct regcache *regcache) | |
623 | { | |
624 | char regbuf[VFP_REGS_SIZE]; | |
625 | int ret, regno, tid; | |
330c6ca9 YQ |
626 | struct gdbarch *gdbarch = get_regcache_arch (regcache); |
627 | struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); | |
3b273a55 RE |
628 | |
629 | /* Get the thread id for the ptrace call. */ | |
630 | tid = GET_THREAD_ID (inferior_ptid); | |
631 | ||
0bdb2f78 | 632 | if (have_ptrace_getregset == TRIBOOL_TRUE) |
bd16da51 YQ |
633 | { |
634 | struct iovec iov; | |
635 | ||
636 | iov.iov_base = regbuf; | |
637 | iov.iov_len = VFP_REGS_SIZE; | |
638 | ret = ptrace (PTRACE_GETREGSET, tid, NT_ARM_VFP, &iov); | |
639 | } | |
640 | else | |
641 | ret = ptrace (PTRACE_GETVFPREGS, tid, 0, regbuf); | |
642 | ||
3b273a55 RE |
643 | if (ret < 0) |
644 | { | |
645 | warning (_("Unable to fetch VFP registers (for update).")); | |
646 | return; | |
647 | } | |
648 | ||
330c6ca9 | 649 | for (regno = 0; regno < tdep->vfp_register_count; regno++) |
3b273a55 RE |
650 | regcache_raw_collect (regcache, regno + ARM_D0_REGNUM, |
651 | (char *) regbuf + regno * 8); | |
652 | ||
653 | regcache_raw_collect (regcache, ARM_FPSCR_REGNUM, | |
654 | (char *) regbuf + 32 * 8); | |
655 | ||
0bdb2f78 | 656 | if (have_ptrace_getregset == TRIBOOL_TRUE) |
bd16da51 YQ |
657 | { |
658 | struct iovec iov; | |
659 | ||
660 | iov.iov_base = regbuf; | |
661 | iov.iov_len = VFP_REGS_SIZE; | |
662 | ret = ptrace (PTRACE_SETREGSET, tid, NT_ARM_VFP, &iov); | |
663 | } | |
664 | else | |
665 | ret = ptrace (PTRACE_SETVFPREGS, tid, 0, regbuf); | |
3b273a55 RE |
666 | |
667 | if (ret < 0) | |
668 | { | |
669 | warning (_("Unable to store VFP registers.")); | |
670 | return; | |
671 | } | |
672 | } | |
673 | ||
ed9a39eb JM |
674 | /* Fetch registers from the child process. Fetch all registers if |
675 | regno == -1, otherwise fetch all general registers or all floating | |
676 | point registers depending upon the value of regno. */ | |
677 | ||
10d6c8cd | 678 | static void |
28439f5e PA |
679 | arm_linux_fetch_inferior_registers (struct target_ops *ops, |
680 | struct regcache *regcache, int regno) | |
ed9a39eb | 681 | { |
330c6ca9 YQ |
682 | struct gdbarch *gdbarch = get_regcache_arch (regcache); |
683 | struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); | |
684 | ||
41c49b06 SB |
685 | if (-1 == regno) |
686 | { | |
56be3814 UW |
687 | fetch_regs (regcache); |
688 | fetch_fpregs (regcache); | |
a56cc1ce | 689 | if (tdep->have_wmmx_registers) |
56be3814 | 690 | fetch_wmmx_regs (regcache); |
330c6ca9 | 691 | if (tdep->vfp_register_count > 0) |
3b273a55 | 692 | fetch_vfp_regs (regcache); |
41c49b06 SB |
693 | } |
694 | else | |
695 | { | |
05a4558a | 696 | if (regno < ARM_F0_REGNUM || regno == ARM_PS_REGNUM) |
56be3814 | 697 | fetch_register (regcache, regno); |
05a4558a | 698 | else if (regno >= ARM_F0_REGNUM && regno <= ARM_FPS_REGNUM) |
56be3814 | 699 | fetch_fpregister (regcache, regno); |
a56cc1ce | 700 | else if (tdep->have_wmmx_registers |
05a4558a | 701 | && regno >= ARM_WR0_REGNUM && regno <= ARM_WCGR7_REGNUM) |
56be3814 | 702 | fetch_wmmx_regs (regcache); |
330c6ca9 | 703 | else if (tdep->vfp_register_count > 0 |
3b273a55 | 704 | && regno >= ARM_D0_REGNUM |
330c6ca9 | 705 | && regno <= ARM_D0_REGNUM + tdep->vfp_register_count) |
3b273a55 | 706 | fetch_vfp_regs (regcache); |
41c49b06 | 707 | } |
ed9a39eb JM |
708 | } |
709 | ||
710 | /* Store registers back into the inferior. Store all registers if | |
711 | regno == -1, otherwise store all general registers or all floating | |
712 | point registers depending upon the value of regno. */ | |
713 | ||
10d6c8cd | 714 | static void |
28439f5e PA |
715 | arm_linux_store_inferior_registers (struct target_ops *ops, |
716 | struct regcache *regcache, int regno) | |
ed9a39eb | 717 | { |
330c6ca9 YQ |
718 | struct gdbarch *gdbarch = get_regcache_arch (regcache); |
719 | struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); | |
720 | ||
41c49b06 SB |
721 | if (-1 == regno) |
722 | { | |
56be3814 UW |
723 | store_regs (regcache); |
724 | store_fpregs (regcache); | |
a56cc1ce | 725 | if (tdep->have_wmmx_registers) |
56be3814 | 726 | store_wmmx_regs (regcache); |
330c6ca9 | 727 | if (tdep->vfp_register_count > 0) |
3b273a55 | 728 | store_vfp_regs (regcache); |
41c49b06 SB |
729 | } |
730 | else | |
731 | { | |
05a4558a | 732 | if (regno < ARM_F0_REGNUM || regno == ARM_PS_REGNUM) |
56be3814 | 733 | store_register (regcache, regno); |
05a4558a | 734 | else if ((regno >= ARM_F0_REGNUM) && (regno <= ARM_FPS_REGNUM)) |
56be3814 | 735 | store_fpregister (regcache, regno); |
a56cc1ce | 736 | else if (tdep->have_wmmx_registers |
05a4558a | 737 | && regno >= ARM_WR0_REGNUM && regno <= ARM_WCGR7_REGNUM) |
56be3814 | 738 | store_wmmx_regs (regcache); |
330c6ca9 | 739 | else if (tdep->vfp_register_count > 0 |
3b273a55 | 740 | && regno >= ARM_D0_REGNUM |
330c6ca9 | 741 | && regno <= ARM_D0_REGNUM + tdep->vfp_register_count) |
3b273a55 | 742 | store_vfp_regs (regcache); |
41c49b06 | 743 | } |
ed9a39eb JM |
744 | } |
745 | ||
cb587d83 DJ |
746 | /* Wrapper functions for the standard regset handling, used by |
747 | thread debugging. */ | |
41c49b06 SB |
748 | |
749 | void | |
7f7fe91e UW |
750 | fill_gregset (const struct regcache *regcache, |
751 | gdb_gregset_t *gregsetp, int regno) | |
41c49b06 | 752 | { |
7f7fe91e | 753 | arm_linux_collect_gregset (NULL, regcache, regno, gregsetp, 0); |
41c49b06 SB |
754 | } |
755 | ||
41c49b06 | 756 | void |
7f7fe91e | 757 | supply_gregset (struct regcache *regcache, const gdb_gregset_t *gregsetp) |
41c49b06 | 758 | { |
7f7fe91e | 759 | arm_linux_supply_gregset (NULL, regcache, -1, gregsetp, 0); |
41c49b06 SB |
760 | } |
761 | ||
41c49b06 | 762 | void |
7f7fe91e UW |
763 | fill_fpregset (const struct regcache *regcache, |
764 | gdb_fpregset_t *fpregsetp, int regno) | |
41c49b06 | 765 | { |
7f7fe91e | 766 | arm_linux_collect_nwfpe (NULL, regcache, regno, fpregsetp, 0); |
41c49b06 SB |
767 | } |
768 | ||
769 | /* Fill GDB's register array with the floating-point register values | |
770 | in *fpregsetp. */ | |
771 | ||
772 | void | |
7f7fe91e | 773 | supply_fpregset (struct regcache *regcache, const gdb_fpregset_t *fpregsetp) |
ed9a39eb | 774 | { |
7f7fe91e | 775 | arm_linux_supply_nwfpe (NULL, regcache, -1, fpregsetp, 0); |
ed9a39eb JM |
776 | } |
777 | ||
9308fc88 DJ |
778 | /* Fetch the thread-local storage pointer for libthread_db. */ |
779 | ||
780 | ps_err_e | |
781 | ps_get_thread_area (const struct ps_prochandle *ph, | |
782 | lwpid_t lwpid, int idx, void **base) | |
783 | { | |
784 | if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, NULL, base) != 0) | |
785 | return PS_ERR; | |
786 | ||
787 | /* IDX is the bias from the thread pointer to the beginning of the | |
788 | thread descriptor. It has to be subtracted due to implementation | |
789 | quirks in libthread_db. */ | |
790 | *base = (void *) ((char *)*base - idx); | |
791 | ||
792 | return PS_OK; | |
793 | } | |
794 | ||
81adfced DJ |
795 | static const struct target_desc * |
796 | arm_linux_read_description (struct target_ops *ops) | |
05a4558a | 797 | { |
3b273a55 | 798 | CORE_ADDR arm_hwcap = 0; |
05a4558a | 799 | |
0bdb2f78 | 800 | if (have_ptrace_getregset == TRIBOOL_UNKNOWN) |
7efe48d1 YQ |
801 | { |
802 | elf_gregset_t gpregs; | |
803 | struct iovec iov; | |
804 | int tid = GET_THREAD_ID (inferior_ptid); | |
805 | ||
806 | iov.iov_base = &gpregs; | |
807 | iov.iov_len = sizeof (gpregs); | |
808 | ||
809 | /* Check if PTRACE_GETREGSET works. */ | |
810 | if (ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, &iov) < 0) | |
0bdb2f78 | 811 | have_ptrace_getregset = TRIBOOL_FALSE; |
7efe48d1 | 812 | else |
0bdb2f78 | 813 | have_ptrace_getregset = TRIBOOL_TRUE; |
7efe48d1 YQ |
814 | } |
815 | ||
3b273a55 RE |
816 | if (target_auxv_search (ops, AT_HWCAP, &arm_hwcap) != 1) |
817 | { | |
2117c711 | 818 | return ops->beneath->to_read_description (ops->beneath); |
3b273a55 | 819 | } |
81adfced | 820 | |
3b273a55 | 821 | if (arm_hwcap & HWCAP_IWMMXT) |
a56cc1ce | 822 | return tdesc_arm_with_iwmmxt; |
3b273a55 RE |
823 | |
824 | if (arm_hwcap & HWCAP_VFP) | |
825 | { | |
826 | int pid; | |
827 | char *buf; | |
828 | const struct target_desc * result = NULL; | |
829 | ||
830 | /* NEON implies VFPv3-D32 or no-VFP unit. Say that we only support | |
831 | Neon with VFPv3-D32. */ | |
832 | if (arm_hwcap & HWCAP_NEON) | |
330c6ca9 | 833 | result = tdesc_arm_with_neon; |
3b273a55 | 834 | else if ((arm_hwcap & (HWCAP_VFPv3 | HWCAP_VFPv3D16)) == HWCAP_VFPv3) |
330c6ca9 | 835 | result = tdesc_arm_with_vfpv3; |
3b273a55 | 836 | else |
330c6ca9 | 837 | result = tdesc_arm_with_vfpv2; |
3b273a55 RE |
838 | |
839 | /* Now make sure that the kernel supports reading these | |
840 | registers. Support was added in 2.6.30. */ | |
dfd4cc63 | 841 | pid = ptid_get_lwp (inferior_ptid); |
3b273a55 RE |
842 | errno = 0; |
843 | buf = alloca (VFP_REGS_SIZE); | |
844 | if (ptrace (PTRACE_GETVFPREGS, pid, 0, buf) < 0 | |
845 | && errno == EIO) | |
846 | result = NULL; | |
847 | ||
848 | return result; | |
849 | } | |
850 | ||
2117c711 | 851 | return ops->beneath->to_read_description (ops->beneath); |
05a4558a DJ |
852 | } |
853 | ||
e3039479 UW |
854 | /* Information describing the hardware breakpoint capabilities. */ |
855 | struct arm_linux_hwbp_cap | |
856 | { | |
857 | gdb_byte arch; | |
858 | gdb_byte max_wp_length; | |
859 | gdb_byte wp_count; | |
860 | gdb_byte bp_count; | |
861 | }; | |
862 | ||
638c5f49 OJ |
863 | /* Since we cannot dynamically allocate subfields of arm_linux_process_info, |
864 | assume a maximum number of supported break-/watchpoints. */ | |
865 | #define MAX_BPTS 16 | |
866 | #define MAX_WPTS 16 | |
867 | ||
e3039479 UW |
868 | /* Get hold of the Hardware Breakpoint information for the target we are |
869 | attached to. Returns NULL if the kernel doesn't support Hardware | |
870 | breakpoints at all, or a pointer to the information structure. */ | |
871 | static const struct arm_linux_hwbp_cap * | |
872 | arm_linux_get_hwbp_cap (void) | |
873 | { | |
874 | /* The info structure we return. */ | |
875 | static struct arm_linux_hwbp_cap info; | |
876 | ||
877 | /* Is INFO in a good state? -1 means that no attempt has been made to | |
878 | initialize INFO; 0 means an attempt has been made, but it failed; 1 | |
879 | means INFO is in an initialized state. */ | |
880 | static int available = -1; | |
881 | ||
882 | if (available == -1) | |
883 | { | |
884 | int tid; | |
885 | unsigned int val; | |
886 | ||
887 | tid = GET_THREAD_ID (inferior_ptid); | |
888 | if (ptrace (PTRACE_GETHBPREGS, tid, 0, &val) < 0) | |
889 | available = 0; | |
890 | else | |
891 | { | |
892 | info.arch = (gdb_byte)((val >> 24) & 0xff); | |
893 | info.max_wp_length = (gdb_byte)((val >> 16) & 0xff); | |
894 | info.wp_count = (gdb_byte)((val >> 8) & 0xff); | |
895 | info.bp_count = (gdb_byte)(val & 0xff); | |
638c5f49 OJ |
896 | |
897 | if (info.wp_count > MAX_WPTS) | |
898 | { | |
899 | warning (_("arm-linux-gdb supports %d hardware watchpoints but target \ | |
900 | supports %d"), MAX_WPTS, info.wp_count); | |
901 | info.wp_count = MAX_WPTS; | |
902 | } | |
903 | ||
904 | if (info.bp_count > MAX_BPTS) | |
905 | { | |
906 | warning (_("arm-linux-gdb supports %d hardware breakpoints but target \ | |
907 | supports %d"), MAX_BPTS, info.bp_count); | |
908 | info.bp_count = MAX_BPTS; | |
909 | } | |
e3039479 UW |
910 | available = (info.arch != 0); |
911 | } | |
912 | } | |
913 | ||
914 | return available == 1 ? &info : NULL; | |
915 | } | |
916 | ||
917 | /* How many hardware breakpoints are available? */ | |
918 | static int | |
919 | arm_linux_get_hw_breakpoint_count (void) | |
920 | { | |
921 | const struct arm_linux_hwbp_cap *cap = arm_linux_get_hwbp_cap (); | |
922 | return cap != NULL ? cap->bp_count : 0; | |
923 | } | |
924 | ||
925 | /* How many hardware watchpoints are available? */ | |
926 | static int | |
927 | arm_linux_get_hw_watchpoint_count (void) | |
928 | { | |
929 | const struct arm_linux_hwbp_cap *cap = arm_linux_get_hwbp_cap (); | |
930 | return cap != NULL ? cap->wp_count : 0; | |
931 | } | |
932 | ||
933 | /* Have we got a free break-/watch-point available for use? Returns -1 if | |
934 | there is not an appropriate resource available, otherwise returns 1. */ | |
935 | static int | |
5461485a TT |
936 | arm_linux_can_use_hw_breakpoint (struct target_ops *self, |
937 | int type, int cnt, int ot) | |
e3039479 UW |
938 | { |
939 | if (type == bp_hardware_watchpoint || type == bp_read_watchpoint | |
940 | || type == bp_access_watchpoint || type == bp_watchpoint) | |
941 | { | |
dbbf180a YQ |
942 | int count = arm_linux_get_hw_watchpoint_count (); |
943 | ||
944 | if (count == 0) | |
945 | return 0; | |
946 | else if (cnt + ot > count) | |
e3039479 UW |
947 | return -1; |
948 | } | |
949 | else if (type == bp_hardware_breakpoint) | |
950 | { | |
dbbf180a YQ |
951 | int count = arm_linux_get_hw_breakpoint_count (); |
952 | ||
953 | if (count == 0) | |
954 | return 0; | |
955 | else if (cnt > count) | |
e3039479 UW |
956 | return -1; |
957 | } | |
958 | else | |
959 | gdb_assert (FALSE); | |
960 | ||
961 | return 1; | |
962 | } | |
963 | ||
964 | /* Enum describing the different types of ARM hardware break-/watch-points. */ | |
965 | typedef enum | |
966 | { | |
967 | arm_hwbp_break = 0, | |
968 | arm_hwbp_load = 1, | |
969 | arm_hwbp_store = 2, | |
970 | arm_hwbp_access = 3 | |
971 | } arm_hwbp_type; | |
972 | ||
973 | /* Type describing an ARM Hardware Breakpoint Control register value. */ | |
974 | typedef unsigned int arm_hwbp_control_t; | |
975 | ||
976 | /* Structure used to keep track of hardware break-/watch-points. */ | |
977 | struct arm_linux_hw_breakpoint | |
978 | { | |
979 | /* Address to break on, or being watched. */ | |
980 | unsigned int address; | |
981 | /* Control register for break-/watch- point. */ | |
982 | arm_hwbp_control_t control; | |
983 | }; | |
984 | ||
638c5f49 OJ |
985 | /* Structure containing arrays of per process hardware break-/watchpoints |
986 | for caching address and control information. | |
e3039479 UW |
987 | |
988 | The Linux ptrace interface to hardware break-/watch-points presents the | |
989 | values in a vector centred around 0 (which is used fo generic information). | |
990 | Positive indicies refer to breakpoint addresses/control registers, negative | |
991 | indices to watchpoint addresses/control registers. | |
992 | ||
993 | The Linux vector is indexed as follows: | |
994 | -((i << 1) + 2): Control register for watchpoint i. | |
995 | -((i << 1) + 1): Address register for watchpoint i. | |
996 | 0: Information register. | |
997 | ((i << 1) + 1): Address register for breakpoint i. | |
998 | ((i << 1) + 2): Control register for breakpoint i. | |
999 | ||
1000 | This structure is used as a per-thread cache of the state stored by the | |
1001 | kernel, so that we don't need to keep calling into the kernel to find a | |
1002 | free breakpoint. | |
1003 | ||
1004 | We treat break-/watch-points with their enable bit clear as being deleted. | |
1005 | */ | |
638c5f49 | 1006 | struct arm_linux_debug_reg_state |
e3039479 | 1007 | { |
638c5f49 OJ |
1008 | /* Hardware breakpoints for this process. */ |
1009 | struct arm_linux_hw_breakpoint bpts[MAX_BPTS]; | |
1010 | /* Hardware watchpoints for this process. */ | |
1011 | struct arm_linux_hw_breakpoint wpts[MAX_WPTS]; | |
1012 | }; | |
1013 | ||
1014 | /* Per-process arch-specific data we want to keep. */ | |
1015 | struct arm_linux_process_info | |
e3039479 | 1016 | { |
638c5f49 OJ |
1017 | /* Linked list. */ |
1018 | struct arm_linux_process_info *next; | |
1019 | /* The process identifier. */ | |
1020 | pid_t pid; | |
1021 | /* Hardware break-/watchpoints state information. */ | |
1022 | struct arm_linux_debug_reg_state state; | |
e3039479 | 1023 | |
638c5f49 OJ |
1024 | }; |
1025 | ||
1026 | /* Per-thread arch-specific data we want to keep. */ | |
1027 | struct arch_lwp_info | |
1028 | { | |
1029 | /* Non-zero if our copy differs from what's recorded in the thread. */ | |
1030 | char bpts_changed[MAX_BPTS]; | |
1031 | char wpts_changed[MAX_WPTS]; | |
1032 | }; | |
1033 | ||
1034 | static struct arm_linux_process_info *arm_linux_process_list = NULL; | |
1035 | ||
1036 | /* Find process data for process PID. */ | |
1037 | ||
1038 | static struct arm_linux_process_info * | |
1039 | arm_linux_find_process_pid (pid_t pid) | |
1040 | { | |
1041 | struct arm_linux_process_info *proc; | |
1042 | ||
1043 | for (proc = arm_linux_process_list; proc; proc = proc->next) | |
1044 | if (proc->pid == pid) | |
1045 | return proc; | |
1046 | ||
1047 | return NULL; | |
1048 | } | |
1049 | ||
1050 | /* Add process data for process PID. Returns newly allocated info | |
1051 | object. */ | |
1052 | ||
1053 | static struct arm_linux_process_info * | |
1054 | arm_linux_add_process (pid_t pid) | |
1055 | { | |
1056 | struct arm_linux_process_info *proc; | |
e3039479 | 1057 | |
638c5f49 OJ |
1058 | proc = xcalloc (1, sizeof (*proc)); |
1059 | proc->pid = pid; | |
e3039479 | 1060 | |
638c5f49 OJ |
1061 | proc->next = arm_linux_process_list; |
1062 | arm_linux_process_list = proc; | |
1063 | ||
1064 | return proc; | |
1065 | } | |
1066 | ||
1067 | /* Get data specific info for process PID, creating it if necessary. | |
1068 | Never returns NULL. */ | |
1069 | ||
1070 | static struct arm_linux_process_info * | |
1071 | arm_linux_process_info_get (pid_t pid) | |
1072 | { | |
1073 | struct arm_linux_process_info *proc; | |
1074 | ||
1075 | proc = arm_linux_find_process_pid (pid); | |
1076 | if (proc == NULL) | |
1077 | proc = arm_linux_add_process (pid); | |
1078 | ||
1079 | return proc; | |
1080 | } | |
1081 | ||
1082 | /* Called whenever GDB is no longer debugging process PID. It deletes | |
1083 | data structures that keep track of debug register state. */ | |
1084 | ||
1085 | static void | |
1086 | arm_linux_forget_process (pid_t pid) | |
1087 | { | |
1088 | struct arm_linux_process_info *proc, **proc_link; | |
1089 | ||
1090 | proc = arm_linux_process_list; | |
1091 | proc_link = &arm_linux_process_list; | |
1092 | ||
1093 | while (proc != NULL) | |
1094 | { | |
1095 | if (proc->pid == pid) | |
e3039479 | 1096 | { |
638c5f49 OJ |
1097 | *proc_link = proc->next; |
1098 | ||
1099 | xfree (proc); | |
1100 | return; | |
e3039479 UW |
1101 | } |
1102 | ||
638c5f49 OJ |
1103 | proc_link = &proc->next; |
1104 | proc = *proc_link; | |
1105 | } | |
1106 | } | |
1107 | ||
1108 | /* Get hardware break-/watchpoint state for process PID. */ | |
1109 | ||
1110 | static struct arm_linux_debug_reg_state * | |
1111 | arm_linux_get_debug_reg_state (pid_t pid) | |
1112 | { | |
1113 | return &arm_linux_process_info_get (pid)->state; | |
e3039479 UW |
1114 | } |
1115 | ||
1116 | /* Initialize an ARM hardware break-/watch-point control register value. | |
1117 | BYTE_ADDRESS_SELECT is the mask of bytes to trigger on; HWBP_TYPE is the | |
1118 | type of break-/watch-point; ENABLE indicates whether the point is enabled. | |
1119 | */ | |
1120 | static arm_hwbp_control_t | |
1121 | arm_hwbp_control_initialize (unsigned byte_address_select, | |
1122 | arm_hwbp_type hwbp_type, | |
1123 | int enable) | |
1124 | { | |
1125 | gdb_assert ((byte_address_select & ~0xffU) == 0); | |
1126 | gdb_assert (hwbp_type != arm_hwbp_break | |
1127 | || ((byte_address_select & 0xfU) != 0)); | |
1128 | ||
1129 | return (byte_address_select << 5) | (hwbp_type << 3) | (3 << 1) | enable; | |
1130 | } | |
1131 | ||
1132 | /* Does the breakpoint control value CONTROL have the enable bit set? */ | |
1133 | static int | |
1134 | arm_hwbp_control_is_enabled (arm_hwbp_control_t control) | |
1135 | { | |
1136 | return control & 0x1; | |
1137 | } | |
1138 | ||
1139 | /* Change a breakpoint control word so that it is in the disabled state. */ | |
1140 | static arm_hwbp_control_t | |
1141 | arm_hwbp_control_disable (arm_hwbp_control_t control) | |
1142 | { | |
1143 | return control & ~0x1; | |
1144 | } | |
1145 | ||
1146 | /* Initialise the hardware breakpoint structure P. The breakpoint will be | |
1147 | enabled, and will point to the placed address of BP_TGT. */ | |
1148 | static void | |
1149 | arm_linux_hw_breakpoint_initialize (struct gdbarch *gdbarch, | |
1150 | struct bp_target_info *bp_tgt, | |
1151 | struct arm_linux_hw_breakpoint *p) | |
1152 | { | |
1153 | unsigned mask; | |
0d5ed153 | 1154 | CORE_ADDR address = bp_tgt->placed_address = bp_tgt->reqstd_address; |
e3039479 UW |
1155 | |
1156 | /* We have to create a mask for the control register which says which bits | |
1157 | of the word pointed to by address to break on. */ | |
1158 | if (arm_pc_is_thumb (gdbarch, address)) | |
fcf303ab UW |
1159 | { |
1160 | mask = 0x3; | |
1161 | address &= ~1; | |
1162 | } | |
e3039479 | 1163 | else |
fcf303ab UW |
1164 | { |
1165 | mask = 0xf; | |
1166 | address &= ~3; | |
1167 | } | |
e3039479 | 1168 | |
fcf303ab | 1169 | p->address = (unsigned int) address; |
e3039479 UW |
1170 | p->control = arm_hwbp_control_initialize (mask, arm_hwbp_break, 1); |
1171 | } | |
1172 | ||
1173 | /* Get the ARM hardware breakpoint type from the RW value we're given when | |
1174 | asked to set a watchpoint. */ | |
1175 | static arm_hwbp_type | |
1176 | arm_linux_get_hwbp_type (int rw) | |
1177 | { | |
1178 | if (rw == hw_read) | |
1179 | return arm_hwbp_load; | |
1180 | else if (rw == hw_write) | |
1181 | return arm_hwbp_store; | |
1182 | else | |
1183 | return arm_hwbp_access; | |
1184 | } | |
1185 | ||
1186 | /* Initialize the hardware breakpoint structure P for a watchpoint at ADDR | |
1187 | to LEN. The type of watchpoint is given in RW. */ | |
1188 | static void | |
1189 | arm_linux_hw_watchpoint_initialize (CORE_ADDR addr, int len, int rw, | |
1190 | struct arm_linux_hw_breakpoint *p) | |
1191 | { | |
1192 | const struct arm_linux_hwbp_cap *cap = arm_linux_get_hwbp_cap (); | |
1193 | unsigned mask; | |
1194 | ||
1195 | gdb_assert (cap != NULL); | |
1196 | gdb_assert (cap->max_wp_length != 0); | |
1197 | ||
1198 | mask = (1 << len) - 1; | |
1199 | ||
1200 | p->address = (unsigned int) addr; | |
1201 | p->control = arm_hwbp_control_initialize (mask, | |
1202 | arm_linux_get_hwbp_type (rw), 1); | |
1203 | } | |
1204 | ||
1205 | /* Are two break-/watch-points equal? */ | |
1206 | static int | |
1207 | arm_linux_hw_breakpoint_equal (const struct arm_linux_hw_breakpoint *p1, | |
1208 | const struct arm_linux_hw_breakpoint *p2) | |
1209 | { | |
1210 | return p1->address == p2->address && p1->control == p2->control; | |
1211 | } | |
1212 | ||
638c5f49 OJ |
1213 | /* Callback to mark a watch-/breakpoint to be updated in all threads of |
1214 | the current process. */ | |
1215 | ||
1216 | struct update_registers_data | |
1217 | { | |
1218 | int watch; | |
1219 | int index; | |
1220 | }; | |
1221 | ||
1222 | static int | |
1223 | update_registers_callback (struct lwp_info *lwp, void *arg) | |
1224 | { | |
1225 | struct update_registers_data *data = (struct update_registers_data *) arg; | |
1226 | ||
1227 | if (lwp->arch_private == NULL) | |
1228 | lwp->arch_private = XCNEW (struct arch_lwp_info); | |
1229 | ||
1230 | /* The actual update is done later just before resuming the lwp, | |
1231 | we just mark that the registers need updating. */ | |
1232 | if (data->watch) | |
1233 | lwp->arch_private->wpts_changed[data->index] = 1; | |
1234 | else | |
1235 | lwp->arch_private->bpts_changed[data->index] = 1; | |
1236 | ||
1237 | /* If the lwp isn't stopped, force it to momentarily pause, so | |
1238 | we can update its breakpoint registers. */ | |
1239 | if (!lwp->stopped) | |
1240 | linux_stop_lwp (lwp); | |
1241 | ||
1242 | return 0; | |
1243 | } | |
1244 | ||
e3039479 UW |
1245 | /* Insert the hardware breakpoint (WATCHPOINT = 0) or watchpoint (WATCHPOINT |
1246 | =1) BPT for thread TID. */ | |
1247 | static void | |
1248 | arm_linux_insert_hw_breakpoint1 (const struct arm_linux_hw_breakpoint* bpt, | |
638c5f49 | 1249 | int watchpoint) |
e3039479 | 1250 | { |
638c5f49 OJ |
1251 | int pid; |
1252 | ptid_t pid_ptid; | |
e3039479 UW |
1253 | gdb_byte count, i; |
1254 | struct arm_linux_hw_breakpoint* bpts; | |
638c5f49 | 1255 | struct update_registers_data data; |
e3039479 | 1256 | |
638c5f49 OJ |
1257 | pid = ptid_get_pid (inferior_ptid); |
1258 | pid_ptid = pid_to_ptid (pid); | |
e3039479 UW |
1259 | |
1260 | if (watchpoint) | |
1261 | { | |
1262 | count = arm_linux_get_hw_watchpoint_count (); | |
638c5f49 | 1263 | bpts = arm_linux_get_debug_reg_state (pid)->wpts; |
e3039479 UW |
1264 | } |
1265 | else | |
1266 | { | |
1267 | count = arm_linux_get_hw_breakpoint_count (); | |
638c5f49 | 1268 | bpts = arm_linux_get_debug_reg_state (pid)->bpts; |
e3039479 UW |
1269 | } |
1270 | ||
1271 | for (i = 0; i < count; ++i) | |
1272 | if (!arm_hwbp_control_is_enabled (bpts[i].control)) | |
1273 | { | |
638c5f49 OJ |
1274 | data.watch = watchpoint; |
1275 | data.index = i; | |
1276 | bpts[i] = *bpt; | |
1277 | iterate_over_lwps (pid_ptid, update_registers_callback, &data); | |
1278 | break; | |
e3039479 UW |
1279 | } |
1280 | ||
1281 | gdb_assert (i != count); | |
1282 | } | |
1283 | ||
1284 | /* Remove the hardware breakpoint (WATCHPOINT = 0) or watchpoint | |
1285 | (WATCHPOINT = 1) BPT for thread TID. */ | |
1286 | static void | |
1287 | arm_linux_remove_hw_breakpoint1 (const struct arm_linux_hw_breakpoint *bpt, | |
638c5f49 | 1288 | int watchpoint) |
e3039479 | 1289 | { |
638c5f49 | 1290 | int pid; |
e3039479 | 1291 | gdb_byte count, i; |
638c5f49 OJ |
1292 | ptid_t pid_ptid; |
1293 | struct arm_linux_hw_breakpoint* bpts; | |
1294 | struct update_registers_data data; | |
e3039479 | 1295 | |
638c5f49 OJ |
1296 | pid = ptid_get_pid (inferior_ptid); |
1297 | pid_ptid = pid_to_ptid (pid); | |
e3039479 UW |
1298 | |
1299 | if (watchpoint) | |
1300 | { | |
1301 | count = arm_linux_get_hw_watchpoint_count (); | |
638c5f49 | 1302 | bpts = arm_linux_get_debug_reg_state (pid)->wpts; |
e3039479 UW |
1303 | } |
1304 | else | |
1305 | { | |
1306 | count = arm_linux_get_hw_breakpoint_count (); | |
638c5f49 | 1307 | bpts = arm_linux_get_debug_reg_state (pid)->bpts; |
e3039479 UW |
1308 | } |
1309 | ||
1310 | for (i = 0; i < count; ++i) | |
1311 | if (arm_linux_hw_breakpoint_equal (bpt, bpts + i)) | |
1312 | { | |
638c5f49 OJ |
1313 | data.watch = watchpoint; |
1314 | data.index = i; | |
1315 | bpts[i].control = arm_hwbp_control_disable (bpts[i].control); | |
1316 | iterate_over_lwps (pid_ptid, update_registers_callback, &data); | |
1317 | break; | |
e3039479 UW |
1318 | } |
1319 | ||
1320 | gdb_assert (i != count); | |
1321 | } | |
1322 | ||
1323 | /* Insert a Hardware breakpoint. */ | |
1324 | static int | |
23a26771 TT |
1325 | arm_linux_insert_hw_breakpoint (struct target_ops *self, |
1326 | struct gdbarch *gdbarch, | |
e3039479 UW |
1327 | struct bp_target_info *bp_tgt) |
1328 | { | |
e3039479 UW |
1329 | struct lwp_info *lp; |
1330 | struct arm_linux_hw_breakpoint p; | |
1331 | ||
1332 | if (arm_linux_get_hw_breakpoint_count () == 0) | |
1333 | return -1; | |
1334 | ||
1335 | arm_linux_hw_breakpoint_initialize (gdbarch, bp_tgt, &p); | |
638c5f49 OJ |
1336 | |
1337 | arm_linux_insert_hw_breakpoint1 (&p, 0); | |
e3039479 UW |
1338 | |
1339 | return 0; | |
1340 | } | |
1341 | ||
1342 | /* Remove a hardware breakpoint. */ | |
1343 | static int | |
a64dc96c TT |
1344 | arm_linux_remove_hw_breakpoint (struct target_ops *self, |
1345 | struct gdbarch *gdbarch, | |
e3039479 UW |
1346 | struct bp_target_info *bp_tgt) |
1347 | { | |
e3039479 UW |
1348 | struct lwp_info *lp; |
1349 | struct arm_linux_hw_breakpoint p; | |
1350 | ||
1351 | if (arm_linux_get_hw_breakpoint_count () == 0) | |
1352 | return -1; | |
1353 | ||
1354 | arm_linux_hw_breakpoint_initialize (gdbarch, bp_tgt, &p); | |
638c5f49 OJ |
1355 | |
1356 | arm_linux_remove_hw_breakpoint1 (&p, 0); | |
e3039479 UW |
1357 | |
1358 | return 0; | |
1359 | } | |
1360 | ||
1361 | /* Are we able to use a hardware watchpoint for the LEN bytes starting at | |
1362 | ADDR? */ | |
1363 | static int | |
31568a15 TT |
1364 | arm_linux_region_ok_for_hw_watchpoint (struct target_ops *self, |
1365 | CORE_ADDR addr, int len) | |
e3039479 UW |
1366 | { |
1367 | const struct arm_linux_hwbp_cap *cap = arm_linux_get_hwbp_cap (); | |
1368 | CORE_ADDR max_wp_length, aligned_addr; | |
1369 | ||
1370 | /* Can not set watchpoints for zero or negative lengths. */ | |
1371 | if (len <= 0) | |
1372 | return 0; | |
1373 | ||
1374 | /* Need to be able to use the ptrace interface. */ | |
1375 | if (cap == NULL || cap->wp_count == 0) | |
1376 | return 0; | |
1377 | ||
1378 | /* Test that the range [ADDR, ADDR + LEN) fits into the largest address | |
1379 | range covered by a watchpoint. */ | |
1380 | max_wp_length = (CORE_ADDR)cap->max_wp_length; | |
1381 | aligned_addr = addr & ~(max_wp_length - 1); | |
1382 | ||
1383 | if (aligned_addr + max_wp_length < addr + len) | |
1384 | return 0; | |
1385 | ||
1386 | /* The current ptrace interface can only handle watchpoints that are a | |
1387 | power of 2. */ | |
1388 | if ((len & (len - 1)) != 0) | |
1389 | return 0; | |
1390 | ||
1391 | /* All tests passed so we must be able to set a watchpoint. */ | |
1392 | return 1; | |
1393 | } | |
1394 | ||
1395 | /* Insert a Hardware breakpoint. */ | |
1396 | static int | |
7bb99c53 TT |
1397 | arm_linux_insert_watchpoint (struct target_ops *self, |
1398 | CORE_ADDR addr, int len, int rw, | |
e3039479 UW |
1399 | struct expression *cond) |
1400 | { | |
e3039479 UW |
1401 | struct lwp_info *lp; |
1402 | struct arm_linux_hw_breakpoint p; | |
1403 | ||
1404 | if (arm_linux_get_hw_watchpoint_count () == 0) | |
1405 | return -1; | |
1406 | ||
1407 | arm_linux_hw_watchpoint_initialize (addr, len, rw, &p); | |
638c5f49 OJ |
1408 | |
1409 | arm_linux_insert_hw_breakpoint1 (&p, 1); | |
e3039479 UW |
1410 | |
1411 | return 0; | |
1412 | } | |
1413 | ||
1414 | /* Remove a hardware breakpoint. */ | |
1415 | static int | |
11b5219a TT |
1416 | arm_linux_remove_watchpoint (struct target_ops *self, |
1417 | CORE_ADDR addr, int len, int rw, | |
e3039479 UW |
1418 | struct expression *cond) |
1419 | { | |
e3039479 UW |
1420 | struct lwp_info *lp; |
1421 | struct arm_linux_hw_breakpoint p; | |
1422 | ||
1423 | if (arm_linux_get_hw_watchpoint_count () == 0) | |
1424 | return -1; | |
1425 | ||
1426 | arm_linux_hw_watchpoint_initialize (addr, len, rw, &p); | |
638c5f49 OJ |
1427 | |
1428 | arm_linux_remove_hw_breakpoint1 (&p, 1); | |
e3039479 UW |
1429 | |
1430 | return 0; | |
1431 | } | |
1432 | ||
1433 | /* What was the data address the target was stopped on accessing. */ | |
1434 | static int | |
1435 | arm_linux_stopped_data_address (struct target_ops *target, CORE_ADDR *addr_p) | |
1436 | { | |
f865ee35 JK |
1437 | siginfo_t siginfo; |
1438 | int slot; | |
1439 | ||
1440 | if (!linux_nat_get_siginfo (inferior_ptid, &siginfo)) | |
1441 | return 0; | |
e3039479 UW |
1442 | |
1443 | /* This must be a hardware breakpoint. */ | |
f865ee35 JK |
1444 | if (siginfo.si_signo != SIGTRAP |
1445 | || (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */) | |
e3039479 UW |
1446 | return 0; |
1447 | ||
1448 | /* We must be able to set hardware watchpoints. */ | |
1449 | if (arm_linux_get_hw_watchpoint_count () == 0) | |
1450 | return 0; | |
1451 | ||
f865ee35 JK |
1452 | slot = siginfo.si_errno; |
1453 | ||
e3039479 UW |
1454 | /* If we are in a positive slot then we're looking at a breakpoint and not |
1455 | a watchpoint. */ | |
1456 | if (slot >= 0) | |
1457 | return 0; | |
1458 | ||
f865ee35 | 1459 | *addr_p = (CORE_ADDR) (uintptr_t) siginfo.si_addr; |
e3039479 UW |
1460 | return 1; |
1461 | } | |
1462 | ||
1463 | /* Has the target been stopped by hitting a watchpoint? */ | |
1464 | static int | |
6a109b6b | 1465 | arm_linux_stopped_by_watchpoint (struct target_ops *ops) |
e3039479 UW |
1466 | { |
1467 | CORE_ADDR addr; | |
6a109b6b | 1468 | return arm_linux_stopped_data_address (ops, &addr); |
e3039479 UW |
1469 | } |
1470 | ||
1471 | static int | |
1472 | arm_linux_watchpoint_addr_within_range (struct target_ops *target, | |
1473 | CORE_ADDR addr, | |
1474 | CORE_ADDR start, int length) | |
1475 | { | |
1476 | return start <= addr && start + length - 1 >= addr; | |
1477 | } | |
1478 | ||
1479 | /* Handle thread creation. We need to copy the breakpoints and watchpoints | |
1480 | in the parent thread to the child thread. */ | |
1481 | static void | |
7b50312a | 1482 | arm_linux_new_thread (struct lwp_info *lp) |
e3039479 | 1483 | { |
638c5f49 OJ |
1484 | int i; |
1485 | struct arch_lwp_info *info = XCNEW (struct arch_lwp_info); | |
1486 | ||
1487 | /* Mark that all the hardware breakpoint/watchpoint register pairs | |
1488 | for this thread need to be initialized. */ | |
e3039479 | 1489 | |
638c5f49 | 1490 | for (i = 0; i < MAX_BPTS; i++) |
e3039479 | 1491 | { |
638c5f49 OJ |
1492 | info->bpts_changed[i] = 1; |
1493 | info->wpts_changed[i] = 1; | |
e3039479 | 1494 | } |
638c5f49 OJ |
1495 | |
1496 | lp->arch_private = info; | |
e3039479 UW |
1497 | } |
1498 | ||
638c5f49 OJ |
1499 | /* Called when resuming a thread. |
1500 | The hardware debug registers are updated when there is any change. */ | |
1501 | ||
e3039479 | 1502 | static void |
638c5f49 | 1503 | arm_linux_prepare_to_resume (struct lwp_info *lwp) |
e3039479 | 1504 | { |
638c5f49 OJ |
1505 | int pid, i; |
1506 | struct arm_linux_hw_breakpoint *bpts, *wpts; | |
1507 | struct arch_lwp_info *arm_lwp_info = lwp->arch_private; | |
1508 | ||
1509 | pid = ptid_get_lwp (lwp->ptid); | |
1510 | bpts = arm_linux_get_debug_reg_state (ptid_get_pid (lwp->ptid))->bpts; | |
1511 | wpts = arm_linux_get_debug_reg_state (ptid_get_pid (lwp->ptid))->wpts; | |
1512 | ||
1513 | /* NULL means this is the main thread still going through the shell, | |
1514 | or, no watchpoint has been set yet. In that case, there's | |
1515 | nothing to do. */ | |
1516 | if (arm_lwp_info == NULL) | |
1517 | return; | |
e3039479 | 1518 | |
638c5f49 OJ |
1519 | for (i = 0; i < arm_linux_get_hw_breakpoint_count (); i++) |
1520 | if (arm_lwp_info->bpts_changed[i]) | |
1521 | { | |
1522 | errno = 0; | |
1523 | if (arm_hwbp_control_is_enabled (bpts[i].control)) | |
1524 | if (ptrace (PTRACE_SETHBPREGS, pid, | |
1525 | (PTRACE_TYPE_ARG3) ((i << 1) + 1), &bpts[i].address) < 0) | |
1526 | perror_with_name (_("Unexpected error setting breakpoint")); | |
1527 | ||
1528 | if (bpts[i].control != 0) | |
1529 | if (ptrace (PTRACE_SETHBPREGS, pid, | |
1530 | (PTRACE_TYPE_ARG3) ((i << 1) + 2), &bpts[i].control) < 0) | |
1531 | perror_with_name (_("Unexpected error setting breakpoint")); | |
1532 | ||
1533 | arm_lwp_info->bpts_changed[i] = 0; | |
1534 | } | |
e3039479 | 1535 | |
638c5f49 OJ |
1536 | for (i = 0; i < arm_linux_get_hw_watchpoint_count (); i++) |
1537 | if (arm_lwp_info->wpts_changed[i]) | |
1538 | { | |
1539 | errno = 0; | |
1540 | if (arm_hwbp_control_is_enabled (wpts[i].control)) | |
1541 | if (ptrace (PTRACE_SETHBPREGS, pid, | |
1542 | (PTRACE_TYPE_ARG3) -((i << 1) + 1), &wpts[i].address) < 0) | |
1543 | perror_with_name (_("Unexpected error setting watchpoint")); | |
1544 | ||
1545 | if (wpts[i].control != 0) | |
1546 | if (ptrace (PTRACE_SETHBPREGS, pid, | |
1547 | (PTRACE_TYPE_ARG3) -((i << 1) + 2), &wpts[i].control) < 0) | |
1548 | perror_with_name (_("Unexpected error setting watchpoint")); | |
1549 | ||
1550 | arm_lwp_info->wpts_changed[i] = 0; | |
1551 | } | |
1552 | } | |
1553 | ||
1554 | /* linux_nat_new_fork hook. */ | |
1555 | ||
1556 | static void | |
1557 | arm_linux_new_fork (struct lwp_info *parent, pid_t child_pid) | |
1558 | { | |
1559 | pid_t parent_pid; | |
1560 | struct arm_linux_debug_reg_state *parent_state; | |
1561 | struct arm_linux_debug_reg_state *child_state; | |
e3039479 | 1562 | |
638c5f49 OJ |
1563 | /* NULL means no watchpoint has ever been set in the parent. In |
1564 | that case, there's nothing to do. */ | |
1565 | if (parent->arch_private == NULL) | |
1566 | return; | |
e3039479 | 1567 | |
638c5f49 OJ |
1568 | /* GDB core assumes the child inherits the watchpoints/hw |
1569 | breakpoints of the parent, and will remove them all from the | |
1570 | forked off process. Copy the debug registers mirrors into the | |
1571 | new process so that all breakpoints and watchpoints can be | |
1572 | removed together. */ | |
e3039479 | 1573 | |
638c5f49 OJ |
1574 | parent_pid = ptid_get_pid (parent->ptid); |
1575 | parent_state = arm_linux_get_debug_reg_state (parent_pid); | |
1576 | child_state = arm_linux_get_debug_reg_state (child_pid); | |
1577 | *child_state = *parent_state; | |
e3039479 UW |
1578 | } |
1579 | ||
10d6c8cd DJ |
1580 | void _initialize_arm_linux_nat (void); |
1581 | ||
ed9a39eb JM |
1582 | void |
1583 | _initialize_arm_linux_nat (void) | |
1584 | { | |
10d6c8cd DJ |
1585 | struct target_ops *t; |
1586 | ||
10d6c8cd DJ |
1587 | /* Fill in the generic GNU/Linux methods. */ |
1588 | t = linux_target (); | |
1589 | ||
1590 | /* Add our register access methods. */ | |
1591 | t->to_fetch_registers = arm_linux_fetch_inferior_registers; | |
1592 | t->to_store_registers = arm_linux_store_inferior_registers; | |
1593 | ||
e3039479 UW |
1594 | /* Add our hardware breakpoint and watchpoint implementation. */ |
1595 | t->to_can_use_hw_breakpoint = arm_linux_can_use_hw_breakpoint; | |
1596 | t->to_insert_hw_breakpoint = arm_linux_insert_hw_breakpoint; | |
1597 | t->to_remove_hw_breakpoint = arm_linux_remove_hw_breakpoint; | |
1598 | t->to_region_ok_for_hw_watchpoint = arm_linux_region_ok_for_hw_watchpoint; | |
1599 | t->to_insert_watchpoint = arm_linux_insert_watchpoint; | |
1600 | t->to_remove_watchpoint = arm_linux_remove_watchpoint; | |
1601 | t->to_stopped_by_watchpoint = arm_linux_stopped_by_watchpoint; | |
1602 | t->to_stopped_data_address = arm_linux_stopped_data_address; | |
1603 | t->to_watchpoint_addr_within_range = arm_linux_watchpoint_addr_within_range; | |
1604 | ||
81adfced | 1605 | t->to_read_description = arm_linux_read_description; |
05a4558a | 1606 | |
10d6c8cd | 1607 | /* Register the target. */ |
f973ed9c | 1608 | linux_nat_add_target (t); |
e3039479 | 1609 | |
638c5f49 | 1610 | /* Handle thread creation and exit. */ |
e3039479 | 1611 | linux_nat_set_new_thread (t, arm_linux_new_thread); |
638c5f49 OJ |
1612 | linux_nat_set_prepare_to_resume (t, arm_linux_prepare_to_resume); |
1613 | ||
1614 | /* Handle process creation and exit. */ | |
1615 | linux_nat_set_new_fork (t, arm_linux_new_fork); | |
1616 | linux_nat_set_forget_process (t, arm_linux_forget_process); | |
ed9a39eb | 1617 | } |