Commit | Line | Data |
---|---|---|
ed9a39eb | 1 | /* GNU/Linux on ARM native support. |
f973ed9c | 2 | Copyright (C) 1999, 2000, 2001, 2002, 2004, 2005, 2006 |
10d6c8cd | 3 | Free Software Foundation, Inc. |
ed9a39eb JM |
4 | |
5 | This file is part of GDB. | |
6 | ||
7 | This program is free software; you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 2 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | This program is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with this program; if not, write to the Free Software | |
197e01b6 EZ |
19 | Foundation, Inc., 51 Franklin Street, Fifth Floor, |
20 | Boston, MA 02110-1301, USA. */ | |
ed9a39eb JM |
21 | |
22 | #include "defs.h" | |
23 | #include "inferior.h" | |
24 | #include "gdbcore.h" | |
25 | #include "gdb_string.h" | |
4e052eda | 26 | #include "regcache.h" |
10d6c8cd DJ |
27 | #include "target.h" |
28 | #include "linux-nat.h" | |
ed9a39eb | 29 | |
aeb98c60 RE |
30 | #include "arm-tdep.h" |
31 | ||
ed9a39eb JM |
32 | #include <sys/user.h> |
33 | #include <sys/ptrace.h> | |
34 | #include <sys/utsname.h> | |
41c49b06 | 35 | #include <sys/procfs.h> |
ed9a39eb | 36 | |
c60c0f5f MS |
37 | /* Prototypes for supply_gregset etc. */ |
38 | #include "gregset.h" | |
39 | ||
9308fc88 DJ |
40 | /* Defines ps_err_e, struct ps_prochandle. */ |
41 | #include "gdb_proc_service.h" | |
42 | ||
43 | #ifndef PTRACE_GET_THREAD_AREA | |
44 | #define PTRACE_GET_THREAD_AREA 22 | |
45 | #endif | |
46 | ||
ed9a39eb JM |
47 | extern int arm_apcs_32; |
48 | ||
49 | #define typeNone 0x00 | |
50 | #define typeSingle 0x01 | |
51 | #define typeDouble 0x02 | |
52 | #define typeExtended 0x03 | |
53 | #define FPWORDS 28 | |
34e8f22d | 54 | #define ARM_CPSR_REGNUM 16 |
ed9a39eb JM |
55 | |
56 | typedef union tagFPREG | |
57 | { | |
58 | unsigned int fSingle; | |
59 | unsigned int fDouble[2]; | |
60 | unsigned int fExtended[3]; | |
61 | } | |
62 | FPREG; | |
63 | ||
64 | typedef struct tagFPA11 | |
65 | { | |
66 | FPREG fpreg[8]; /* 8 floating point registers */ | |
67 | unsigned int fpsr; /* floating point status register */ | |
68 | unsigned int fpcr; /* floating point control register */ | |
69 | unsigned char fType[8]; /* type of floating point value held in | |
70 | floating point registers. */ | |
71 | int initflag; /* NWFPE initialization flag. */ | |
72 | } | |
73 | FPA11; | |
74 | ||
75 | /* The following variables are used to determine the version of the | |
fdf39c9a | 76 | underlying GNU/Linux operating system. Examples: |
ed9a39eb | 77 | |
fdf39c9a | 78 | GNU/Linux 2.0.35 GNU/Linux 2.2.12 |
ed9a39eb JM |
79 | os_version = 0x00020023 os_version = 0x0002020c |
80 | os_major = 2 os_major = 2 | |
81 | os_minor = 0 os_minor = 2 | |
82 | os_release = 35 os_release = 12 | |
83 | ||
84 | Note: os_version = (os_major << 16) | (os_minor << 8) | os_release | |
85 | ||
86 | These are initialized using get_linux_version() from | |
87 | _initialize_arm_linux_nat(). */ | |
88 | ||
89 | static unsigned int os_version, os_major, os_minor, os_release; | |
90 | ||
fdf39c9a | 91 | /* On GNU/Linux, threads are implemented as pseudo-processes, in which |
41c49b06 | 92 | case we may be tracing more than one process at a time. In that |
39f77062 | 93 | case, inferior_ptid will contain the main process ID and the |
fdf39c9a RE |
94 | individual thread (process) ID. get_thread_id () is used to get |
95 | the thread id if it's available, and the process id otherwise. */ | |
41c49b06 SB |
96 | |
97 | int | |
39f77062 | 98 | get_thread_id (ptid_t ptid) |
41c49b06 | 99 | { |
39f77062 KB |
100 | int tid = TIDGET (ptid); |
101 | if (0 == tid) | |
102 | tid = PIDGET (ptid); | |
41c49b06 SB |
103 | return tid; |
104 | } | |
39f77062 | 105 | #define GET_THREAD_ID(PTID) get_thread_id ((PTID)); |
41c49b06 | 106 | |
ed9a39eb | 107 | static void |
56624b0a | 108 | fetch_nwfpe_single (unsigned int fn, FPA11 * fpa11) |
ed9a39eb JM |
109 | { |
110 | unsigned int mem[3]; | |
111 | ||
112 | mem[0] = fpa11->fpreg[fn].fSingle; | |
113 | mem[1] = 0; | |
114 | mem[2] = 0; | |
23a6d369 | 115 | regcache_raw_supply (current_regcache, ARM_F0_REGNUM + fn, (char *) &mem[0]); |
ed9a39eb JM |
116 | } |
117 | ||
118 | static void | |
56624b0a | 119 | fetch_nwfpe_double (unsigned int fn, FPA11 * fpa11) |
ed9a39eb JM |
120 | { |
121 | unsigned int mem[3]; | |
122 | ||
123 | mem[0] = fpa11->fpreg[fn].fDouble[1]; | |
124 | mem[1] = fpa11->fpreg[fn].fDouble[0]; | |
125 | mem[2] = 0; | |
23a6d369 | 126 | regcache_raw_supply (current_regcache, ARM_F0_REGNUM + fn, (char *) &mem[0]); |
ed9a39eb JM |
127 | } |
128 | ||
129 | static void | |
56624b0a | 130 | fetch_nwfpe_none (unsigned int fn) |
ed9a39eb JM |
131 | { |
132 | unsigned int mem[3] = | |
133 | {0, 0, 0}; | |
134 | ||
23a6d369 | 135 | regcache_raw_supply (current_regcache, ARM_F0_REGNUM + fn, (char *) &mem[0]); |
ed9a39eb JM |
136 | } |
137 | ||
138 | static void | |
56624b0a | 139 | fetch_nwfpe_extended (unsigned int fn, FPA11 * fpa11) |
ed9a39eb JM |
140 | { |
141 | unsigned int mem[3]; | |
142 | ||
143 | mem[0] = fpa11->fpreg[fn].fExtended[0]; /* sign & exponent */ | |
144 | mem[1] = fpa11->fpreg[fn].fExtended[2]; /* ls bits */ | |
145 | mem[2] = fpa11->fpreg[fn].fExtended[1]; /* ms bits */ | |
23a6d369 | 146 | regcache_raw_supply (current_regcache, ARM_F0_REGNUM + fn, (char *) &mem[0]); |
ed9a39eb JM |
147 | } |
148 | ||
41c49b06 SB |
149 | static void |
150 | fetch_nwfpe_register (int regno, FPA11 * fpa11) | |
151 | { | |
34e8f22d | 152 | int fn = regno - ARM_F0_REGNUM; |
41c49b06 SB |
153 | |
154 | switch (fpa11->fType[fn]) | |
155 | { | |
156 | case typeSingle: | |
157 | fetch_nwfpe_single (fn, fpa11); | |
158 | break; | |
159 | ||
160 | case typeDouble: | |
161 | fetch_nwfpe_double (fn, fpa11); | |
162 | break; | |
163 | ||
164 | case typeExtended: | |
165 | fetch_nwfpe_extended (fn, fpa11); | |
166 | break; | |
167 | ||
168 | default: | |
169 | fetch_nwfpe_none (fn); | |
170 | } | |
171 | } | |
172 | ||
ed9a39eb | 173 | static void |
85ae890c | 174 | store_nwfpe_single (unsigned int fn, FPA11 *fpa11) |
ed9a39eb JM |
175 | { |
176 | unsigned int mem[3]; | |
177 | ||
822c9732 AC |
178 | regcache_raw_collect (current_regcache, ARM_F0_REGNUM + fn, |
179 | (char *) &mem[0]); | |
ed9a39eb JM |
180 | fpa11->fpreg[fn].fSingle = mem[0]; |
181 | fpa11->fType[fn] = typeSingle; | |
182 | } | |
183 | ||
184 | static void | |
85ae890c | 185 | store_nwfpe_double (unsigned int fn, FPA11 *fpa11) |
ed9a39eb JM |
186 | { |
187 | unsigned int mem[3]; | |
188 | ||
822c9732 AC |
189 | regcache_raw_collect (current_regcache, ARM_F0_REGNUM + fn, |
190 | (char *) &mem[0]); | |
ed9a39eb JM |
191 | fpa11->fpreg[fn].fDouble[1] = mem[0]; |
192 | fpa11->fpreg[fn].fDouble[0] = mem[1]; | |
193 | fpa11->fType[fn] = typeDouble; | |
194 | } | |
195 | ||
196 | void | |
85ae890c | 197 | store_nwfpe_extended (unsigned int fn, FPA11 *fpa11) |
ed9a39eb JM |
198 | { |
199 | unsigned int mem[3]; | |
200 | ||
822c9732 AC |
201 | regcache_raw_collect (current_regcache, ARM_F0_REGNUM + fn, |
202 | (char *) &mem[0]); | |
ed9a39eb JM |
203 | fpa11->fpreg[fn].fExtended[0] = mem[0]; /* sign & exponent */ |
204 | fpa11->fpreg[fn].fExtended[2] = mem[1]; /* ls bits */ | |
205 | fpa11->fpreg[fn].fExtended[1] = mem[2]; /* ms bits */ | |
206 | fpa11->fType[fn] = typeDouble; | |
207 | } | |
208 | ||
41c49b06 SB |
209 | void |
210 | store_nwfpe_register (int regno, FPA11 * fpa11) | |
211 | { | |
c6b92abd | 212 | if (register_cached (regno)) |
41c49b06 | 213 | { |
34e8f22d | 214 | unsigned int fn = regno - ARM_F0_REGNUM; |
41c49b06 SB |
215 | switch (fpa11->fType[fn]) |
216 | { | |
217 | case typeSingle: | |
218 | store_nwfpe_single (fn, fpa11); | |
219 | break; | |
220 | ||
221 | case typeDouble: | |
222 | store_nwfpe_double (fn, fpa11); | |
223 | break; | |
224 | ||
225 | case typeExtended: | |
226 | store_nwfpe_extended (fn, fpa11); | |
227 | break; | |
228 | } | |
229 | } | |
230 | } | |
231 | ||
232 | ||
233 | /* Get the value of a particular register from the floating point | |
c6b92abd | 234 | state of the process and store it into regcache. */ |
41c49b06 SB |
235 | |
236 | static void | |
237 | fetch_fpregister (int regno) | |
238 | { | |
239 | int ret, tid; | |
240 | FPA11 fp; | |
241 | ||
242 | /* Get the thread id for the ptrace call. */ | |
39f77062 | 243 | tid = GET_THREAD_ID (inferior_ptid); |
41c49b06 SB |
244 | |
245 | /* Read the floating point state. */ | |
246 | ret = ptrace (PT_GETFPREGS, tid, 0, &fp); | |
247 | if (ret < 0) | |
248 | { | |
edefbb7c | 249 | warning (_("Unable to fetch floating point register.")); |
41c49b06 SB |
250 | return; |
251 | } | |
252 | ||
253 | /* Fetch fpsr. */ | |
34e8f22d | 254 | if (ARM_FPS_REGNUM == regno) |
23a6d369 | 255 | regcache_raw_supply (current_regcache, ARM_FPS_REGNUM, (char *) &fp.fpsr); |
41c49b06 SB |
256 | |
257 | /* Fetch the floating point register. */ | |
34e8f22d | 258 | if (regno >= ARM_F0_REGNUM && regno <= ARM_F7_REGNUM) |
41c49b06 | 259 | { |
34e8f22d | 260 | int fn = regno - ARM_F0_REGNUM; |
41c49b06 SB |
261 | |
262 | switch (fp.fType[fn]) | |
263 | { | |
264 | case typeSingle: | |
265 | fetch_nwfpe_single (fn, &fp); | |
266 | break; | |
267 | ||
268 | case typeDouble: | |
269 | fetch_nwfpe_double (fn, &fp); | |
270 | break; | |
271 | ||
272 | case typeExtended: | |
273 | fetch_nwfpe_extended (fn, &fp); | |
274 | break; | |
275 | ||
276 | default: | |
277 | fetch_nwfpe_none (fn); | |
278 | } | |
279 | } | |
280 | } | |
281 | ||
282 | /* Get the whole floating point state of the process and store it | |
c6b92abd | 283 | into regcache. */ |
ed9a39eb JM |
284 | |
285 | static void | |
286 | fetch_fpregs (void) | |
287 | { | |
41c49b06 | 288 | int ret, regno, tid; |
ed9a39eb JM |
289 | FPA11 fp; |
290 | ||
41c49b06 | 291 | /* Get the thread id for the ptrace call. */ |
39f77062 | 292 | tid = GET_THREAD_ID (inferior_ptid); |
41c49b06 | 293 | |
ed9a39eb | 294 | /* Read the floating point state. */ |
41c49b06 | 295 | ret = ptrace (PT_GETFPREGS, tid, 0, &fp); |
ed9a39eb JM |
296 | if (ret < 0) |
297 | { | |
edefbb7c | 298 | warning (_("Unable to fetch the floating point registers.")); |
ed9a39eb JM |
299 | return; |
300 | } | |
301 | ||
302 | /* Fetch fpsr. */ | |
23a6d369 | 303 | regcache_raw_supply (current_regcache, ARM_FPS_REGNUM, (char *) &fp.fpsr); |
ed9a39eb JM |
304 | |
305 | /* Fetch the floating point registers. */ | |
34e8f22d | 306 | for (regno = ARM_F0_REGNUM; regno <= ARM_F7_REGNUM; regno++) |
ed9a39eb | 307 | { |
34e8f22d | 308 | int fn = regno - ARM_F0_REGNUM; |
ed9a39eb JM |
309 | |
310 | switch (fp.fType[fn]) | |
311 | { | |
312 | case typeSingle: | |
56624b0a | 313 | fetch_nwfpe_single (fn, &fp); |
ed9a39eb JM |
314 | break; |
315 | ||
316 | case typeDouble: | |
56624b0a | 317 | fetch_nwfpe_double (fn, &fp); |
ed9a39eb JM |
318 | break; |
319 | ||
320 | case typeExtended: | |
56624b0a | 321 | fetch_nwfpe_extended (fn, &fp); |
ed9a39eb JM |
322 | break; |
323 | ||
324 | default: | |
56624b0a | 325 | fetch_nwfpe_none (fn); |
ed9a39eb JM |
326 | } |
327 | } | |
328 | } | |
329 | ||
41c49b06 | 330 | /* Save a particular register into the floating point state of the |
c6b92abd | 331 | process using the contents from regcache. */ |
41c49b06 SB |
332 | |
333 | static void | |
334 | store_fpregister (int regno) | |
335 | { | |
336 | int ret, tid; | |
337 | FPA11 fp; | |
338 | ||
339 | /* Get the thread id for the ptrace call. */ | |
39f77062 | 340 | tid = GET_THREAD_ID (inferior_ptid); |
41c49b06 SB |
341 | |
342 | /* Read the floating point state. */ | |
343 | ret = ptrace (PT_GETFPREGS, tid, 0, &fp); | |
344 | if (ret < 0) | |
345 | { | |
edefbb7c | 346 | warning (_("Unable to fetch the floating point registers.")); |
41c49b06 SB |
347 | return; |
348 | } | |
349 | ||
350 | /* Store fpsr. */ | |
34e8f22d | 351 | if (ARM_FPS_REGNUM == regno && register_cached (ARM_FPS_REGNUM)) |
822c9732 | 352 | regcache_raw_collect (current_regcache, ARM_FPS_REGNUM, (char *) &fp.fpsr); |
41c49b06 SB |
353 | |
354 | /* Store the floating point register. */ | |
34e8f22d | 355 | if (regno >= ARM_F0_REGNUM && regno <= ARM_F7_REGNUM) |
41c49b06 SB |
356 | { |
357 | store_nwfpe_register (regno, &fp); | |
358 | } | |
359 | ||
360 | ret = ptrace (PTRACE_SETFPREGS, tid, 0, &fp); | |
361 | if (ret < 0) | |
362 | { | |
edefbb7c | 363 | warning (_("Unable to store floating point register.")); |
41c49b06 SB |
364 | return; |
365 | } | |
366 | } | |
367 | ||
ed9a39eb | 368 | /* Save the whole floating point state of the process using |
c6b92abd | 369 | the contents from regcache. */ |
ed9a39eb JM |
370 | |
371 | static void | |
372 | store_fpregs (void) | |
373 | { | |
41c49b06 | 374 | int ret, regno, tid; |
ed9a39eb JM |
375 | FPA11 fp; |
376 | ||
41c49b06 | 377 | /* Get the thread id for the ptrace call. */ |
39f77062 | 378 | tid = GET_THREAD_ID (inferior_ptid); |
41c49b06 SB |
379 | |
380 | /* Read the floating point state. */ | |
381 | ret = ptrace (PT_GETFPREGS, tid, 0, &fp); | |
382 | if (ret < 0) | |
383 | { | |
edefbb7c | 384 | warning (_("Unable to fetch the floating point registers.")); |
41c49b06 SB |
385 | return; |
386 | } | |
387 | ||
ed9a39eb | 388 | /* Store fpsr. */ |
34e8f22d | 389 | if (register_cached (ARM_FPS_REGNUM)) |
822c9732 | 390 | regcache_raw_collect (current_regcache, ARM_FPS_REGNUM, (char *) &fp.fpsr); |
ed9a39eb JM |
391 | |
392 | /* Store the floating point registers. */ | |
34e8f22d | 393 | for (regno = ARM_F0_REGNUM; regno <= ARM_F7_REGNUM; regno++) |
ed9a39eb | 394 | { |
41c49b06 | 395 | fetch_nwfpe_register (regno, &fp); |
ed9a39eb JM |
396 | } |
397 | ||
41c49b06 | 398 | ret = ptrace (PTRACE_SETFPREGS, tid, 0, &fp); |
ed9a39eb JM |
399 | if (ret < 0) |
400 | { | |
edefbb7c | 401 | warning (_("Unable to store floating point registers.")); |
ed9a39eb JM |
402 | return; |
403 | } | |
404 | } | |
405 | ||
41c49b06 | 406 | /* Fetch a general register of the process and store into |
c6b92abd | 407 | regcache. */ |
41c49b06 SB |
408 | |
409 | static void | |
410 | fetch_register (int regno) | |
411 | { | |
412 | int ret, tid; | |
c2152441 | 413 | elf_gregset_t regs; |
41c49b06 SB |
414 | |
415 | /* Get the thread id for the ptrace call. */ | |
39f77062 | 416 | tid = GET_THREAD_ID (inferior_ptid); |
41c49b06 SB |
417 | |
418 | ret = ptrace (PTRACE_GETREGS, tid, 0, ®s); | |
419 | if (ret < 0) | |
420 | { | |
edefbb7c | 421 | warning (_("Unable to fetch general register.")); |
41c49b06 SB |
422 | return; |
423 | } | |
424 | ||
34e8f22d | 425 | if (regno >= ARM_A1_REGNUM && regno < ARM_PC_REGNUM) |
23a6d369 | 426 | regcache_raw_supply (current_regcache, regno, (char *) ®s[regno]); |
41c49b06 | 427 | |
34e8f22d | 428 | if (ARM_PS_REGNUM == regno) |
41c49b06 SB |
429 | { |
430 | if (arm_apcs_32) | |
23a6d369 AC |
431 | regcache_raw_supply (current_regcache, ARM_PS_REGNUM, |
432 | (char *) ®s[ARM_CPSR_REGNUM]); | |
41c49b06 | 433 | else |
23a6d369 AC |
434 | regcache_raw_supply (current_regcache, ARM_PS_REGNUM, |
435 | (char *) ®s[ARM_PC_REGNUM]); | |
41c49b06 SB |
436 | } |
437 | ||
34e8f22d | 438 | if (ARM_PC_REGNUM == regno) |
41c49b06 | 439 | { |
34e8f22d | 440 | regs[ARM_PC_REGNUM] = ADDR_BITS_REMOVE (regs[ARM_PC_REGNUM]); |
23a6d369 AC |
441 | regcache_raw_supply (current_regcache, ARM_PC_REGNUM, |
442 | (char *) ®s[ARM_PC_REGNUM]); | |
41c49b06 SB |
443 | } |
444 | } | |
445 | ||
ed9a39eb | 446 | /* Fetch all general registers of the process and store into |
c6b92abd | 447 | regcache. */ |
ed9a39eb JM |
448 | |
449 | static void | |
450 | fetch_regs (void) | |
451 | { | |
41c49b06 | 452 | int ret, regno, tid; |
c2152441 | 453 | elf_gregset_t regs; |
ed9a39eb | 454 | |
41c49b06 | 455 | /* Get the thread id for the ptrace call. */ |
39f77062 | 456 | tid = GET_THREAD_ID (inferior_ptid); |
41c49b06 SB |
457 | |
458 | ret = ptrace (PTRACE_GETREGS, tid, 0, ®s); | |
ed9a39eb JM |
459 | if (ret < 0) |
460 | { | |
edefbb7c | 461 | warning (_("Unable to fetch general registers.")); |
ed9a39eb JM |
462 | return; |
463 | } | |
464 | ||
34e8f22d | 465 | for (regno = ARM_A1_REGNUM; regno < ARM_PC_REGNUM; regno++) |
23a6d369 | 466 | regcache_raw_supply (current_regcache, regno, (char *) ®s[regno]); |
ed9a39eb JM |
467 | |
468 | if (arm_apcs_32) | |
23a6d369 AC |
469 | regcache_raw_supply (current_regcache, ARM_PS_REGNUM, |
470 | (char *) ®s[ARM_CPSR_REGNUM]); | |
ed9a39eb | 471 | else |
23a6d369 AC |
472 | regcache_raw_supply (current_regcache, ARM_PS_REGNUM, |
473 | (char *) ®s[ARM_PC_REGNUM]); | |
ed9a39eb | 474 | |
34e8f22d | 475 | regs[ARM_PC_REGNUM] = ADDR_BITS_REMOVE (regs[ARM_PC_REGNUM]); |
23a6d369 AC |
476 | regcache_raw_supply (current_regcache, ARM_PC_REGNUM, |
477 | (char *) ®s[ARM_PC_REGNUM]); | |
ed9a39eb JM |
478 | } |
479 | ||
480 | /* Store all general registers of the process from the values in | |
c6b92abd | 481 | regcache. */ |
ed9a39eb | 482 | |
41c49b06 SB |
483 | static void |
484 | store_register (int regno) | |
485 | { | |
486 | int ret, tid; | |
c2152441 | 487 | elf_gregset_t regs; |
41c49b06 | 488 | |
c6b92abd | 489 | if (!register_cached (regno)) |
41c49b06 SB |
490 | return; |
491 | ||
492 | /* Get the thread id for the ptrace call. */ | |
39f77062 | 493 | tid = GET_THREAD_ID (inferior_ptid); |
41c49b06 SB |
494 | |
495 | /* Get the general registers from the process. */ | |
496 | ret = ptrace (PTRACE_GETREGS, tid, 0, ®s); | |
497 | if (ret < 0) | |
498 | { | |
edefbb7c | 499 | warning (_("Unable to fetch general registers.")); |
41c49b06 SB |
500 | return; |
501 | } | |
502 | ||
34e8f22d | 503 | if (regno >= ARM_A1_REGNUM && regno <= ARM_PC_REGNUM) |
822c9732 | 504 | regcache_raw_collect (current_regcache, regno, (char *) ®s[regno]); |
adb8a87c DJ |
505 | else if (arm_apcs_32 && regno == ARM_PS_REGNUM) |
506 | regcache_raw_collect (current_regcache, regno, | |
507 | (char *) ®s[ARM_CPSR_REGNUM]); | |
508 | else if (!arm_apcs_32 && regno == ARM_PS_REGNUM) | |
509 | regcache_raw_collect (current_regcache, ARM_PC_REGNUM, | |
510 | (char *) ®s[ARM_PC_REGNUM]); | |
41c49b06 SB |
511 | |
512 | ret = ptrace (PTRACE_SETREGS, tid, 0, ®s); | |
513 | if (ret < 0) | |
514 | { | |
edefbb7c | 515 | warning (_("Unable to store general register.")); |
41c49b06 SB |
516 | return; |
517 | } | |
518 | } | |
519 | ||
ed9a39eb JM |
520 | static void |
521 | store_regs (void) | |
522 | { | |
41c49b06 | 523 | int ret, regno, tid; |
c2152441 | 524 | elf_gregset_t regs; |
ed9a39eb | 525 | |
41c49b06 | 526 | /* Get the thread id for the ptrace call. */ |
39f77062 | 527 | tid = GET_THREAD_ID (inferior_ptid); |
41c49b06 SB |
528 | |
529 | /* Fetch the general registers. */ | |
530 | ret = ptrace (PTRACE_GETREGS, tid, 0, ®s); | |
ed9a39eb JM |
531 | if (ret < 0) |
532 | { | |
edefbb7c | 533 | warning (_("Unable to fetch general registers.")); |
ed9a39eb JM |
534 | return; |
535 | } | |
536 | ||
34e8f22d | 537 | for (regno = ARM_A1_REGNUM; regno <= ARM_PC_REGNUM; regno++) |
ed9a39eb | 538 | { |
c6b92abd | 539 | if (register_cached (regno)) |
822c9732 | 540 | regcache_raw_collect (current_regcache, regno, (char *) ®s[regno]); |
ed9a39eb JM |
541 | } |
542 | ||
adb8a87c DJ |
543 | if (arm_apcs_32 && register_cached (ARM_PS_REGNUM)) |
544 | regcache_raw_collect (current_regcache, ARM_PS_REGNUM, | |
545 | (char *) ®s[ARM_CPSR_REGNUM]); | |
546 | ||
41c49b06 | 547 | ret = ptrace (PTRACE_SETREGS, tid, 0, ®s); |
ed9a39eb JM |
548 | |
549 | if (ret < 0) | |
550 | { | |
edefbb7c | 551 | warning (_("Unable to store general registers.")); |
ed9a39eb JM |
552 | return; |
553 | } | |
554 | } | |
555 | ||
556 | /* Fetch registers from the child process. Fetch all registers if | |
557 | regno == -1, otherwise fetch all general registers or all floating | |
558 | point registers depending upon the value of regno. */ | |
559 | ||
10d6c8cd DJ |
560 | static void |
561 | arm_linux_fetch_inferior_registers (int regno) | |
ed9a39eb | 562 | { |
41c49b06 SB |
563 | if (-1 == regno) |
564 | { | |
565 | fetch_regs (); | |
566 | fetch_fpregs (); | |
567 | } | |
568 | else | |
569 | { | |
34e8f22d | 570 | if (regno < ARM_F0_REGNUM || regno > ARM_FPS_REGNUM) |
41c49b06 | 571 | fetch_register (regno); |
ed9a39eb | 572 | |
34e8f22d | 573 | if (regno >= ARM_F0_REGNUM && regno <= ARM_FPS_REGNUM) |
41c49b06 SB |
574 | fetch_fpregister (regno); |
575 | } | |
ed9a39eb JM |
576 | } |
577 | ||
578 | /* Store registers back into the inferior. Store all registers if | |
579 | regno == -1, otherwise store all general registers or all floating | |
580 | point registers depending upon the value of regno. */ | |
581 | ||
10d6c8cd DJ |
582 | static void |
583 | arm_linux_store_inferior_registers (int regno) | |
ed9a39eb | 584 | { |
41c49b06 SB |
585 | if (-1 == regno) |
586 | { | |
587 | store_regs (); | |
588 | store_fpregs (); | |
589 | } | |
590 | else | |
591 | { | |
34e8f22d | 592 | if ((regno < ARM_F0_REGNUM) || (regno > ARM_FPS_REGNUM)) |
41c49b06 | 593 | store_register (regno); |
ed9a39eb | 594 | |
34e8f22d | 595 | if ((regno >= ARM_F0_REGNUM) && (regno <= ARM_FPS_REGNUM)) |
41c49b06 SB |
596 | store_fpregister (regno); |
597 | } | |
ed9a39eb JM |
598 | } |
599 | ||
41c49b06 SB |
600 | /* Fill register regno (if it is a general-purpose register) in |
601 | *gregsetp with the appropriate value from GDB's register array. | |
602 | If regno is -1, do this for all registers. */ | |
603 | ||
604 | void | |
713f0374 | 605 | fill_gregset (gdb_gregset_t *gregsetp, int regno) |
41c49b06 SB |
606 | { |
607 | if (-1 == regno) | |
608 | { | |
609 | int regnum; | |
34e8f22d | 610 | for (regnum = ARM_A1_REGNUM; regnum <= ARM_PC_REGNUM; regnum++) |
822c9732 AC |
611 | regcache_raw_collect (current_regcache, regnum, |
612 | (char *) &(*gregsetp)[regnum]); | |
41c49b06 | 613 | } |
34e8f22d | 614 | else if (regno >= ARM_A1_REGNUM && regno <= ARM_PC_REGNUM) |
822c9732 AC |
615 | regcache_raw_collect (current_regcache, regno, |
616 | (char *) &(*gregsetp)[regno]); | |
41c49b06 | 617 | |
34e8f22d | 618 | if (ARM_PS_REGNUM == regno || -1 == regno) |
41c49b06 | 619 | { |
17fd1ad9 | 620 | if (arm_apcs_32) |
822c9732 AC |
621 | regcache_raw_collect (current_regcache, ARM_PS_REGNUM, |
622 | (char *) &(*gregsetp)[ARM_CPSR_REGNUM]); | |
17fd1ad9 | 623 | else |
822c9732 AC |
624 | regcache_raw_collect (current_regcache, ARM_PC_REGNUM, |
625 | (char *) &(*gregsetp)[ARM_PC_REGNUM]); | |
41c49b06 | 626 | } |
41c49b06 SB |
627 | } |
628 | ||
629 | /* Fill GDB's register array with the general-purpose register values | |
630 | in *gregsetp. */ | |
631 | ||
632 | void | |
713f0374 | 633 | supply_gregset (gdb_gregset_t *gregsetp) |
41c49b06 SB |
634 | { |
635 | int regno, reg_pc; | |
636 | ||
34e8f22d | 637 | for (regno = ARM_A1_REGNUM; regno < ARM_PC_REGNUM; regno++) |
23a6d369 AC |
638 | regcache_raw_supply (current_regcache, regno, |
639 | (char *) &(*gregsetp)[regno]); | |
41c49b06 SB |
640 | |
641 | if (arm_apcs_32) | |
23a6d369 AC |
642 | regcache_raw_supply (current_regcache, ARM_PS_REGNUM, |
643 | (char *) &(*gregsetp)[ARM_CPSR_REGNUM]); | |
41c49b06 | 644 | else |
23a6d369 AC |
645 | regcache_raw_supply (current_regcache, ARM_PS_REGNUM, |
646 | (char *) &(*gregsetp)[ARM_PC_REGNUM]); | |
41c49b06 | 647 | |
34e8f22d | 648 | reg_pc = ADDR_BITS_REMOVE ((CORE_ADDR)(*gregsetp)[ARM_PC_REGNUM]); |
23a6d369 | 649 | regcache_raw_supply (current_regcache, ARM_PC_REGNUM, (char *) ®_pc); |
41c49b06 SB |
650 | } |
651 | ||
652 | /* Fill register regno (if it is a floating-point register) in | |
653 | *fpregsetp with the appropriate value from GDB's register array. | |
654 | If regno is -1, do this for all registers. */ | |
655 | ||
656 | void | |
713f0374 | 657 | fill_fpregset (gdb_fpregset_t *fpregsetp, int regno) |
41c49b06 SB |
658 | { |
659 | FPA11 *fp = (FPA11 *) fpregsetp; | |
660 | ||
661 | if (-1 == regno) | |
662 | { | |
663 | int regnum; | |
34e8f22d | 664 | for (regnum = ARM_F0_REGNUM; regnum <= ARM_F7_REGNUM; regnum++) |
41c49b06 SB |
665 | store_nwfpe_register (regnum, fp); |
666 | } | |
34e8f22d | 667 | else if (regno >= ARM_F0_REGNUM && regno <= ARM_F7_REGNUM) |
41c49b06 SB |
668 | { |
669 | store_nwfpe_register (regno, fp); | |
670 | return; | |
671 | } | |
672 | ||
673 | /* Store fpsr. */ | |
34e8f22d | 674 | if (ARM_FPS_REGNUM == regno || -1 == regno) |
822c9732 AC |
675 | regcache_raw_collect (current_regcache, ARM_FPS_REGNUM, |
676 | (char *) &fp->fpsr); | |
41c49b06 SB |
677 | } |
678 | ||
679 | /* Fill GDB's register array with the floating-point register values | |
680 | in *fpregsetp. */ | |
681 | ||
682 | void | |
713f0374 | 683 | supply_fpregset (gdb_fpregset_t *fpregsetp) |
ed9a39eb | 684 | { |
41c49b06 SB |
685 | int regno; |
686 | FPA11 *fp = (FPA11 *) fpregsetp; | |
687 | ||
688 | /* Fetch fpsr. */ | |
23a6d369 | 689 | regcache_raw_supply (current_regcache, ARM_FPS_REGNUM, (char *) &fp->fpsr); |
41c49b06 SB |
690 | |
691 | /* Fetch the floating point registers. */ | |
34e8f22d | 692 | for (regno = ARM_F0_REGNUM; regno <= ARM_F7_REGNUM; regno++) |
41c49b06 SB |
693 | { |
694 | fetch_nwfpe_register (regno, fp); | |
695 | } | |
ed9a39eb JM |
696 | } |
697 | ||
698 | int | |
699 | arm_linux_kernel_u_size (void) | |
700 | { | |
701 | return (sizeof (struct user)); | |
702 | } | |
703 | ||
9308fc88 DJ |
704 | /* Fetch the thread-local storage pointer for libthread_db. */ |
705 | ||
706 | ps_err_e | |
707 | ps_get_thread_area (const struct ps_prochandle *ph, | |
708 | lwpid_t lwpid, int idx, void **base) | |
709 | { | |
710 | if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, NULL, base) != 0) | |
711 | return PS_ERR; | |
712 | ||
713 | /* IDX is the bias from the thread pointer to the beginning of the | |
714 | thread descriptor. It has to be subtracted due to implementation | |
715 | quirks in libthread_db. */ | |
716 | *base = (void *) ((char *)*base - idx); | |
717 | ||
718 | return PS_OK; | |
719 | } | |
720 | ||
ed9a39eb JM |
721 | static unsigned int |
722 | get_linux_version (unsigned int *vmajor, | |
723 | unsigned int *vminor, | |
724 | unsigned int *vrelease) | |
725 | { | |
726 | struct utsname info; | |
727 | char *pmajor, *pminor, *prelease, *tail; | |
728 | ||
729 | if (-1 == uname (&info)) | |
730 | { | |
edefbb7c | 731 | warning (_("Unable to determine GNU/Linux version.")); |
ed9a39eb JM |
732 | return -1; |
733 | } | |
734 | ||
735 | pmajor = strtok (info.release, "."); | |
736 | pminor = strtok (NULL, "."); | |
737 | prelease = strtok (NULL, "."); | |
738 | ||
739 | *vmajor = (unsigned int) strtoul (pmajor, &tail, 0); | |
740 | *vminor = (unsigned int) strtoul (pminor, &tail, 0); | |
741 | *vrelease = (unsigned int) strtoul (prelease, &tail, 0); | |
742 | ||
743 | return ((*vmajor << 16) | (*vminor << 8) | *vrelease); | |
744 | } | |
745 | ||
10d6c8cd DJ |
746 | void _initialize_arm_linux_nat (void); |
747 | ||
ed9a39eb JM |
748 | void |
749 | _initialize_arm_linux_nat (void) | |
750 | { | |
10d6c8cd DJ |
751 | struct target_ops *t; |
752 | ||
ed9a39eb | 753 | os_version = get_linux_version (&os_major, &os_minor, &os_release); |
10d6c8cd DJ |
754 | |
755 | /* Fill in the generic GNU/Linux methods. */ | |
756 | t = linux_target (); | |
757 | ||
758 | /* Add our register access methods. */ | |
759 | t->to_fetch_registers = arm_linux_fetch_inferior_registers; | |
760 | t->to_store_registers = arm_linux_store_inferior_registers; | |
761 | ||
762 | /* Register the target. */ | |
f973ed9c | 763 | linux_nat_add_target (t); |
ed9a39eb | 764 | } |