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