Commit | Line | Data |
---|---|---|
75c9abc6 | 1 | /* Native-dependent code for GNU/Linux on MIPS processors. |
a094c6fb | 2 | |
dc60ece8 DJ |
3 | Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 |
4 | Free Software Foundation, Inc. | |
2aa830e4 DJ |
5 | |
6 | This file is part of GDB. | |
7 | ||
8 | This program is free software; you can redistribute it and/or modify | |
9 | it under the terms of the GNU General Public License as published by | |
10 | the Free Software Foundation; either version 2 of the License, or | |
11 | (at your option) any later version. | |
12 | ||
13 | This program is distributed in the hope that it will be useful, | |
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | GNU General Public License for more details. | |
17 | ||
18 | You should have received a copy of the GNU General Public License | |
19 | along with this program; if not, write to the Free Software | |
197e01b6 EZ |
20 | Foundation, Inc., 51 Franklin Street, Fifth Floor, |
21 | Boston, MA 02110-1301, USA. */ | |
2aa830e4 DJ |
22 | |
23 | #include "defs.h" | |
d37eb719 | 24 | #include "inferior.h" |
6b753f60 | 25 | #include "mips-tdep.h" |
10d6c8cd DJ |
26 | #include "target.h" |
27 | #include "linux-nat.h" | |
d37eb719 | 28 | #include "mips-linux-tdep.h" |
2aa830e4 | 29 | |
dc60ece8 DJ |
30 | #include "gdb_proc_service.h" |
31 | ||
d37eb719 DJ |
32 | #include <sys/ptrace.h> |
33 | ||
dc60ece8 DJ |
34 | #ifndef PTRACE_GET_THREAD_AREA |
35 | #define PTRACE_GET_THREAD_AREA 25 | |
36 | #endif | |
37 | ||
d37eb719 DJ |
38 | /* Assume that we have PTRACE_GETREGS et al. support. If we do not, |
39 | we'll clear this and use PTRACE_PEEKUSER instead. */ | |
40 | static int have_ptrace_regsets = 1; | |
41 | ||
42 | /* Saved function pointers to fetch and store a single register using | |
43 | PTRACE_PEEKUSER and PTRACE_POKEUSER. */ | |
44 | ||
45 | void (*super_fetch_registers) (int); | |
46 | void (*super_store_registers) (int); | |
47 | ||
2aa830e4 | 48 | /* Pseudo registers can not be read. ptrace does not provide a way to |
24e05951 AC |
49 | read (or set) MIPS_PS_REGNUM, and there's no point in reading or |
50 | setting MIPS_ZERO_REGNUM. We also can not set BADVADDR, CAUSE, or | |
51 | FCRIR via ptrace(). */ | |
2aa830e4 DJ |
52 | |
53 | int | |
54 | mips_linux_cannot_fetch_register (int regno) | |
55 | { | |
613e114f | 56 | if (regno > MIPS_ZERO_REGNUM && regno < MIPS_ZERO_REGNUM + 32) |
2aa830e4 | 57 | return 0; |
56cea623 AC |
58 | else if (regno >= mips_regnum (current_gdbarch)->fp0 |
59 | && regno <= mips_regnum (current_gdbarch)->fp0 + 32) | |
fd8f87c5 | 60 | return 0; |
56cea623 AC |
61 | else if (regno == mips_regnum (current_gdbarch)->lo |
62 | || regno == mips_regnum (current_gdbarch)->hi | |
63 | || regno == mips_regnum (current_gdbarch)->badvaddr | |
64 | || regno == mips_regnum (current_gdbarch)->cause | |
65 | || regno == mips_regnum (current_gdbarch)->pc | |
66 | || regno == mips_regnum (current_gdbarch)->fp_control_status | |
67 | || regno == mips_regnum (current_gdbarch)->fp_implementation_revision) | |
68 | return 0; | |
69 | else | |
70 | return 1; | |
2aa830e4 DJ |
71 | } |
72 | ||
73 | int | |
74 | mips_linux_cannot_store_register (int regno) | |
75 | { | |
613e114f | 76 | if (regno > MIPS_ZERO_REGNUM && regno < MIPS_ZERO_REGNUM + 32) |
2aa830e4 | 77 | return 0; |
fd8f87c5 DJ |
78 | else if (regno >= FP0_REGNUM && regno <= FP0_REGNUM + 32) |
79 | return 0; | |
56cea623 AC |
80 | else if (regno == mips_regnum (current_gdbarch)->lo |
81 | || regno == mips_regnum (current_gdbarch)->hi | |
82 | || regno == mips_regnum (current_gdbarch)->pc | |
83 | || regno == mips_regnum (current_gdbarch)->fp_control_status) | |
84 | return 0; | |
85 | else | |
86 | return 1; | |
2aa830e4 | 87 | } |
10d6c8cd | 88 | |
dc60ece8 DJ |
89 | /* Fetch the thread-local storage pointer for libthread_db. */ |
90 | ||
91 | ps_err_e | |
92 | ps_get_thread_area (const struct ps_prochandle *ph, | |
93 | lwpid_t lwpid, int idx, void **base) | |
94 | { | |
95 | if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, NULL, base) != 0) | |
96 | return PS_ERR; | |
97 | ||
98 | /* IDX is the bias from the thread pointer to the beginning of the | |
99 | thread descriptor. It has to be subtracted due to implementation | |
100 | quirks in libthread_db. */ | |
101 | *base = (void *) ((char *)*base - idx); | |
102 | ||
103 | return PS_OK; | |
104 | } | |
105 | ||
d37eb719 DJ |
106 | /* Fetch REGNO (or all registers if REGNO == -1) from the target |
107 | using PTRACE_GETREGS et al. */ | |
108 | ||
109 | static void | |
110 | mips64_linux_regsets_fetch_registers (int regno) | |
111 | { | |
112 | int is_fp; | |
113 | int tid; | |
114 | ||
115 | if (regno >= mips_regnum (current_gdbarch)->fp0 | |
116 | && regno <= mips_regnum (current_gdbarch)->fp0 + 32) | |
117 | is_fp = 1; | |
118 | else if (regno == mips_regnum (current_gdbarch)->fp_control_status) | |
119 | is_fp = 1; | |
120 | else if (regno == mips_regnum (current_gdbarch)->fp_implementation_revision) | |
121 | is_fp = 1; | |
122 | else | |
123 | is_fp = 0; | |
124 | ||
125 | tid = ptid_get_lwp (inferior_ptid); | |
126 | if (tid == 0) | |
127 | tid = ptid_get_pid (inferior_ptid); | |
128 | ||
129 | if (regno == -1 || !is_fp) | |
130 | { | |
131 | mips64_elf_gregset_t regs; | |
132 | ||
133 | if (ptrace (PTRACE_GETREGS, tid, 0L, (PTRACE_TYPE_ARG3) ®s) == -1) | |
134 | { | |
135 | if (errno == EIO) | |
136 | { | |
137 | have_ptrace_regsets = 0; | |
138 | return; | |
139 | } | |
140 | perror_with_name (_("Couldn't get registers")); | |
141 | } | |
142 | ||
143 | mips64_supply_gregset (®s); | |
144 | } | |
145 | ||
146 | if (regno == -1 || is_fp) | |
147 | { | |
148 | mips64_elf_fpregset_t fp_regs; | |
149 | ||
150 | if (ptrace (PTRACE_GETFPREGS, tid, 0L, | |
151 | (PTRACE_TYPE_ARG3) &fp_regs) == -1) | |
152 | { | |
153 | if (errno == EIO) | |
154 | { | |
155 | have_ptrace_regsets = 0; | |
156 | return; | |
157 | } | |
158 | perror_with_name (_("Couldn't get FP registers")); | |
159 | } | |
160 | ||
161 | mips64_supply_fpregset (&fp_regs); | |
162 | } | |
163 | } | |
164 | ||
165 | /* Store REGNO (or all registers if REGNO == -1) to the target | |
166 | using PTRACE_SETREGS et al. */ | |
167 | ||
168 | static void | |
169 | mips64_linux_regsets_store_registers (int regno) | |
170 | { | |
171 | int is_fp; | |
172 | int tid; | |
173 | ||
174 | if (regno >= mips_regnum (current_gdbarch)->fp0 | |
175 | && regno <= mips_regnum (current_gdbarch)->fp0 + 32) | |
176 | is_fp = 1; | |
177 | else if (regno == mips_regnum (current_gdbarch)->fp_control_status) | |
178 | is_fp = 1; | |
179 | else if (regno == mips_regnum (current_gdbarch)->fp_implementation_revision) | |
180 | is_fp = 1; | |
181 | else | |
182 | is_fp = 0; | |
183 | ||
184 | tid = ptid_get_lwp (inferior_ptid); | |
185 | if (tid == 0) | |
186 | tid = ptid_get_pid (inferior_ptid); | |
187 | ||
188 | if (regno == -1 || !is_fp) | |
189 | { | |
190 | mips64_elf_gregset_t regs; | |
191 | ||
192 | if (ptrace (PTRACE_GETREGS, tid, 0L, (PTRACE_TYPE_ARG3) ®s) == -1) | |
193 | perror_with_name (_("Couldn't get registers")); | |
194 | ||
195 | mips64_fill_gregset (®s, regno); | |
196 | ||
197 | if (ptrace (PTRACE_SETREGS, tid, 0L, (PTRACE_TYPE_ARG3) ®s) == -1) | |
198 | perror_with_name (_("Couldn't set registers")); | |
199 | } | |
200 | ||
201 | if (regno == -1 || is_fp) | |
202 | { | |
203 | mips64_elf_fpregset_t fp_regs; | |
204 | ||
205 | if (ptrace (PTRACE_GETFPREGS, tid, 0L, | |
206 | (PTRACE_TYPE_ARG3) &fp_regs) == -1) | |
207 | perror_with_name (_("Couldn't get FP registers")); | |
208 | ||
209 | mips64_fill_fpregset (&fp_regs, regno); | |
210 | ||
211 | if (ptrace (PTRACE_SETFPREGS, tid, 0L, | |
212 | (PTRACE_TYPE_ARG3) &fp_regs) == -1) | |
213 | perror_with_name (_("Couldn't set FP registers")); | |
214 | } | |
215 | } | |
216 | ||
217 | /* Fetch REGNO (or all registers if REGNO == -1) from the target | |
218 | using any working method. */ | |
219 | ||
220 | static void | |
221 | mips64_linux_fetch_registers (int regnum) | |
222 | { | |
223 | /* Unless we already know that PTRACE_GETREGS does not work, try it. */ | |
224 | if (have_ptrace_regsets) | |
225 | mips64_linux_regsets_fetch_registers (regnum); | |
226 | ||
227 | /* If we know, or just found out, that PTRACE_GETREGS does not work, fall | |
228 | back to PTRACE_PEEKUSER. */ | |
229 | if (!have_ptrace_regsets) | |
230 | super_fetch_registers (regnum); | |
231 | } | |
232 | ||
233 | /* Store REGNO (or all registers if REGNO == -1) to the target | |
234 | using any working method. */ | |
235 | ||
236 | static void | |
237 | mips64_linux_store_registers (int regnum) | |
238 | { | |
239 | /* Unless we already know that PTRACE_GETREGS does not work, try it. */ | |
240 | if (have_ptrace_regsets) | |
241 | mips64_linux_regsets_store_registers (regnum); | |
242 | ||
243 | /* If we know, or just found out, that PTRACE_GETREGS does not work, fall | |
244 | back to PTRACE_PEEKUSER. */ | |
245 | if (!have_ptrace_regsets) | |
246 | super_store_registers (regnum); | |
247 | } | |
248 | ||
10d6c8cd DJ |
249 | void _initialize_mips_linux_nat (void); |
250 | ||
251 | void | |
252 | _initialize_mips_linux_nat (void) | |
253 | { | |
d37eb719 DJ |
254 | struct target_ops *t = linux_target (); |
255 | ||
256 | super_fetch_registers = t->to_fetch_registers; | |
257 | super_store_registers = t->to_store_registers; | |
258 | ||
259 | t->to_fetch_registers = mips64_linux_fetch_registers; | |
260 | t->to_store_registers = mips64_linux_store_registers; | |
261 | ||
f973ed9c | 262 | linux_nat_add_target (t); |
10d6c8cd | 263 | } |