Commit | Line | Data |
---|---|---|
c877c8e6 | 1 | /* PPC linux native support. |
05f13b9c | 2 | Copyright 1988, 1989, 1991, 1992, 1994, 1996, 2000, 2001, 2002 |
b6ba6518 | 3 | Free Software Foundation, Inc. |
c877c8e6 KB |
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 | |
05f13b9c EZ |
19 | Foundation, Inc., 59 Temple Place - Suite 330, |
20 | Boston, MA 02111-1307, USA. */ | |
c877c8e6 KB |
21 | |
22 | #include "defs.h" | |
23 | #include "frame.h" | |
24 | #include "inferior.h" | |
25 | #include "gdbcore.h" | |
4e052eda | 26 | #include "regcache.h" |
c877c8e6 KB |
27 | |
28 | #include <sys/types.h> | |
29 | #include <sys/param.h> | |
30 | #include <signal.h> | |
31 | #include <sys/user.h> | |
32 | #include <sys/ioctl.h> | |
33 | #include <sys/wait.h> | |
34 | #include <fcntl.h> | |
35 | #include <sys/procfs.h> | |
45229ea4 | 36 | #include <sys/ptrace.h> |
c877c8e6 | 37 | |
c60c0f5f MS |
38 | /* Prototypes for supply_gregset etc. */ |
39 | #include "gregset.h" | |
16333c4f | 40 | #include "ppc-tdep.h" |
c60c0f5f | 41 | |
45229ea4 EZ |
42 | #ifndef PT_READ_U |
43 | #define PT_READ_U PTRACE_PEEKUSR | |
44 | #endif | |
45 | #ifndef PT_WRITE_U | |
46 | #define PT_WRITE_U PTRACE_POKEUSR | |
47 | #endif | |
48 | ||
49 | /* Default the type of the ptrace transfer to int. */ | |
50 | #ifndef PTRACE_XFER_TYPE | |
51 | #define PTRACE_XFER_TYPE int | |
52 | #endif | |
53 | ||
c877c8e6 | 54 | int |
fba45db2 | 55 | kernel_u_size (void) |
c877c8e6 KB |
56 | { |
57 | return (sizeof (struct user)); | |
58 | } | |
59 | ||
16333c4f EZ |
60 | /* *INDENT-OFF* */ |
61 | /* registers layout, as presented by the ptrace interface: | |
62 | PT_R0, PT_R1, PT_R2, PT_R3, PT_R4, PT_R5, PT_R6, PT_R7, | |
63 | PT_R8, PT_R9, PT_R10, PT_R11, PT_R12, PT_R13, PT_R14, PT_R15, | |
64 | PT_R16, PT_R17, PT_R18, PT_R19, PT_R20, PT_R21, PT_R22, PT_R23, | |
65 | PT_R24, PT_R25, PT_R26, PT_R27, PT_R28, PT_R29, PT_R30, PT_R31, | |
66 | PT_FPR0, PT_FPR0 + 2, PT_FPR0 + 4, PT_FPR0 + 6, PT_FPR0 + 8, PT_FPR0 + 10, PT_FPR0 + 12, PT_FPR0 + 14, | |
67 | PT_FPR0 + 16, PT_FPR0 + 18, PT_FPR0 + 20, PT_FPR0 + 22, PT_FPR0 + 24, PT_FPR0 + 26, PT_FPR0 + 28, PT_FPR0 + 30, | |
68 | PT_FPR0 + 32, PT_FPR0 + 34, PT_FPR0 + 36, PT_FPR0 + 38, PT_FPR0 + 40, PT_FPR0 + 42, PT_FPR0 + 44, PT_FPR0 + 46, | |
69 | PT_FPR0 + 48, PT_FPR0 + 50, PT_FPR0 + 52, PT_FPR0 + 54, PT_FPR0 + 56, PT_FPR0 + 58, PT_FPR0 + 60, PT_FPR0 + 62, | |
70 | PT_NIP, PT_MSR, PT_CCR, PT_LNK, PT_CTR, PT_XER, PT_MQ */ | |
71 | /* *INDENT_ON * */ | |
c877c8e6 | 72 | |
45229ea4 EZ |
73 | static int |
74 | ppc_register_u_addr (int regno) | |
c877c8e6 | 75 | { |
16333c4f | 76 | int u_addr = -1; |
dc5cfeb6 | 77 | struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); |
16333c4f EZ |
78 | |
79 | /* General purpose registers occupy 1 slot each in the buffer */ | |
dc5cfeb6 | 80 | if (regno >= tdep->ppc_gp0_regnum && regno <= tdep->ppc_gplast_regnum ) |
45229ea4 | 81 | u_addr = ((PT_R0 + regno) * 4); |
16333c4f EZ |
82 | |
83 | /* Floating point regs: 2 slots each */ | |
84 | if (regno >= FP0_REGNUM && regno <= FPLAST_REGNUM) | |
45229ea4 | 85 | u_addr = ((PT_FPR0 + (regno - FP0_REGNUM) * 2) * 4); |
16333c4f EZ |
86 | |
87 | /* UISA special purpose registers: 1 slot each */ | |
88 | if (regno == PC_REGNUM) | |
45229ea4 | 89 | u_addr = PT_NIP * 4; |
dc5cfeb6 | 90 | if (regno == tdep->ppc_lr_regnum) |
45229ea4 | 91 | u_addr = PT_LNK * 4; |
dc5cfeb6 | 92 | if (regno == tdep->ppc_cr_regnum) |
45229ea4 | 93 | u_addr = PT_CCR * 4; |
dc5cfeb6 | 94 | if (regno == tdep->ppc_xer_regnum) |
45229ea4 | 95 | u_addr = PT_XER * 4; |
dc5cfeb6 | 96 | if (regno == tdep->ppc_ctr_regnum) |
45229ea4 | 97 | u_addr = PT_CTR * 4; |
dc5cfeb6 | 98 | if (regno == tdep->ppc_mq_regnum) |
45229ea4 | 99 | u_addr = PT_MQ * 4; |
dc5cfeb6 | 100 | if (regno == tdep->ppc_ps_regnum) |
45229ea4 | 101 | u_addr = PT_MSR * 4; |
16333c4f EZ |
102 | |
103 | return u_addr; | |
c877c8e6 KB |
104 | } |
105 | ||
45229ea4 EZ |
106 | static int |
107 | ppc_ptrace_cannot_fetch_store_register (int regno) | |
108 | { | |
109 | return (ppc_register_u_addr (regno) == -1); | |
110 | } | |
111 | ||
112 | static void | |
05f13b9c | 113 | fetch_register (int tid, int regno) |
45229ea4 EZ |
114 | { |
115 | /* This isn't really an address. But ptrace thinks of it as one. */ | |
116 | char mess[128]; /* For messages */ | |
117 | register int i; | |
118 | unsigned int offset; /* Offset of registers within the u area. */ | |
119 | char *buf = alloca (MAX_REGISTER_RAW_SIZE); | |
45229ea4 EZ |
120 | CORE_ADDR regaddr = ppc_register_u_addr (regno); |
121 | ||
122 | if (regaddr == -1) | |
123 | { | |
124 | memset (buf, '\0', REGISTER_RAW_SIZE (regno)); /* Supply zeroes */ | |
125 | supply_register (regno, buf); | |
126 | return; | |
127 | } | |
128 | ||
45229ea4 EZ |
129 | for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof (PTRACE_XFER_TYPE)) |
130 | { | |
131 | errno = 0; | |
132 | *(PTRACE_XFER_TYPE *) & buf[i] = ptrace (PT_READ_U, tid, | |
133 | (PTRACE_ARG3_TYPE) regaddr, 0); | |
134 | regaddr += sizeof (PTRACE_XFER_TYPE); | |
135 | if (errno != 0) | |
136 | { | |
137 | sprintf (mess, "reading register %s (#%d)", | |
138 | REGISTER_NAME (regno), regno); | |
139 | perror_with_name (mess); | |
140 | } | |
141 | } | |
142 | supply_register (regno, buf); | |
143 | } | |
144 | ||
145 | static void | |
05f13b9c | 146 | fetch_ppc_registers (int tid) |
45229ea4 EZ |
147 | { |
148 | int i; | |
149 | int last_register = gdbarch_tdep (current_gdbarch)->ppc_mq_regnum; | |
150 | ||
151 | for (i = 0; i <= last_register; i++) | |
05f13b9c | 152 | fetch_register (tid, i); |
45229ea4 EZ |
153 | } |
154 | ||
155 | /* Fetch registers from the child process. Fetch all registers if | |
156 | regno == -1, otherwise fetch all general registers or all floating | |
157 | point registers depending upon the value of regno. */ | |
158 | void | |
159 | fetch_inferior_registers (int regno) | |
160 | { | |
05f13b9c EZ |
161 | /* Overload thread id onto process id */ |
162 | int tid = TIDGET (inferior_ptid); | |
163 | ||
164 | /* No thread id, just use process id */ | |
165 | if (tid == 0) | |
166 | tid = PIDGET (inferior_ptid); | |
167 | ||
168 | if (regno == -1) | |
169 | fetch_ppc_registers (tid); | |
45229ea4 | 170 | else |
05f13b9c | 171 | fetch_register (tid, regno); |
45229ea4 EZ |
172 | } |
173 | ||
174 | /* Store one register. */ | |
175 | static void | |
05f13b9c | 176 | store_register (int tid, int regno) |
45229ea4 EZ |
177 | { |
178 | /* This isn't really an address. But ptrace thinks of it as one. */ | |
179 | CORE_ADDR regaddr = ppc_register_u_addr (regno); | |
180 | char mess[128]; /* For messages */ | |
181 | register int i; | |
182 | unsigned int offset; /* Offset of registers within the u area. */ | |
45229ea4 EZ |
183 | char *buf = alloca (MAX_REGISTER_RAW_SIZE); |
184 | ||
185 | if (regaddr == -1) | |
186 | { | |
187 | return; | |
188 | } | |
189 | ||
45229ea4 EZ |
190 | regcache_collect (regno, buf); |
191 | for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof (PTRACE_XFER_TYPE)) | |
192 | { | |
193 | errno = 0; | |
194 | ptrace (PT_WRITE_U, tid, (PTRACE_ARG3_TYPE) regaddr, | |
195 | *(PTRACE_XFER_TYPE *) & buf[i]); | |
196 | regaddr += sizeof (PTRACE_XFER_TYPE); | |
197 | if (errno != 0) | |
198 | { | |
199 | sprintf (mess, "writing register %s (#%d)", | |
200 | REGISTER_NAME (regno), regno); | |
201 | perror_with_name (mess); | |
202 | } | |
203 | } | |
204 | } | |
205 | ||
206 | static void | |
05f13b9c | 207 | store_ppc_registers (int tid) |
45229ea4 EZ |
208 | { |
209 | int i; | |
210 | int last_register = gdbarch_tdep (current_gdbarch)->ppc_mq_regnum; | |
211 | ||
212 | for (i = 0; i <= last_register; i++) | |
05f13b9c | 213 | store_register (tid, i); |
45229ea4 EZ |
214 | } |
215 | ||
216 | void | |
217 | store_inferior_registers (int regno) | |
218 | { | |
05f13b9c EZ |
219 | /* Overload thread id onto process id */ |
220 | int tid = TIDGET (inferior_ptid); | |
221 | ||
222 | /* No thread id, just use process id */ | |
223 | if (tid == 0) | |
224 | tid = PIDGET (inferior_ptid); | |
225 | ||
45229ea4 | 226 | if (regno >= 0) |
05f13b9c | 227 | store_register (tid, regno); |
45229ea4 | 228 | else |
05f13b9c | 229 | store_ppc_registers (tid); |
45229ea4 EZ |
230 | } |
231 | ||
50c9bd31 | 232 | void |
8ae45c11 | 233 | supply_gregset (gdb_gregset_t *gregsetp) |
c877c8e6 KB |
234 | { |
235 | int regi; | |
2ac44c70 | 236 | register elf_greg_t *regp = (elf_greg_t *) gregsetp; |
dc5cfeb6 | 237 | struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); |
c877c8e6 KB |
238 | |
239 | for (regi = 0; regi < 32; regi++) | |
240 | supply_register (regi, (char *) (regp + regi)); | |
241 | ||
16333c4f | 242 | supply_register (PC_REGNUM, (char *) (regp + PT_NIP)); |
dc5cfeb6 EZ |
243 | supply_register (tdep->ppc_lr_regnum, (char *) (regp + PT_LNK)); |
244 | supply_register (tdep->ppc_cr_regnum, (char *) (regp + PT_CCR)); | |
245 | supply_register (tdep->ppc_xer_regnum, (char *) (regp + PT_XER)); | |
246 | supply_register (tdep->ppc_ctr_regnum, (char *) (regp + PT_CTR)); | |
247 | supply_register (tdep->ppc_mq_regnum, (char *) (regp + PT_MQ)); | |
248 | supply_register (tdep->ppc_ps_regnum, (char *) (regp + PT_MSR)); | |
c877c8e6 KB |
249 | } |
250 | ||
fdb28ac4 | 251 | void |
8ae45c11 | 252 | fill_gregset (gdb_gregset_t *gregsetp, int regno) |
fdb28ac4 KB |
253 | { |
254 | int regi; | |
2ac44c70 | 255 | elf_greg_t *regp = (elf_greg_t *) gregsetp; |
dc5cfeb6 | 256 | struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); |
fdb28ac4 | 257 | |
fdb28ac4 KB |
258 | for (regi = 0; regi < 32; regi++) |
259 | { | |
16333c4f EZ |
260 | if ((regno == -1) || regno == regi) |
261 | regcache_collect (regi, regp + PT_R0 + regi); | |
fdb28ac4 KB |
262 | } |
263 | ||
16333c4f EZ |
264 | if ((regno == -1) || regno == PC_REGNUM) |
265 | regcache_collect (PC_REGNUM, regp + PT_NIP); | |
05f13b9c | 266 | if ((regno == -1) || regno == tdep->ppc_lr_regnum) |
dc5cfeb6 | 267 | regcache_collect (tdep->ppc_lr_regnum, regp + PT_LNK); |
05f13b9c | 268 | if ((regno == -1) || regno == tdep->ppc_cr_regnum) |
dc5cfeb6 | 269 | regcache_collect (tdep->ppc_cr_regnum, regp + PT_CCR); |
05f13b9c | 270 | if ((regno == -1) || regno == tdep->ppc_xer_regnum) |
dc5cfeb6 | 271 | regcache_collect (tdep->ppc_xer_regnum, regp + PT_XER); |
05f13b9c | 272 | if ((regno == -1) || regno == tdep->ppc_ctr_regnum) |
dc5cfeb6 | 273 | regcache_collect (tdep->ppc_ctr_regnum, regp + PT_CTR); |
05f13b9c | 274 | if ((regno == -1) || regno == tdep->ppc_mq_regnum) |
dc5cfeb6 | 275 | regcache_collect (tdep->ppc_mq_regnum, regp + PT_MQ); |
05f13b9c | 276 | if ((regno == -1) || regno == tdep->ppc_ps_regnum) |
dc5cfeb6 | 277 | regcache_collect (tdep->ppc_ps_regnum, regp + PT_MSR); |
fdb28ac4 KB |
278 | } |
279 | ||
50c9bd31 | 280 | void |
8ae45c11 | 281 | supply_fpregset (gdb_fpregset_t * fpregsetp) |
c877c8e6 KB |
282 | { |
283 | int regi; | |
284 | for (regi = 0; regi < 32; regi++) | |
05f13b9c | 285 | supply_register (FP0_REGNUM + regi, (char *) (*fpregsetp + regi)); |
c877c8e6 | 286 | } |
fdb28ac4 KB |
287 | |
288 | /* Given a pointer to a floating point register set in /proc format | |
289 | (fpregset_t *), update the register specified by REGNO from gdb's idea | |
290 | of the current floating point register set. If REGNO is -1, update | |
291 | them all. */ | |
292 | ||
293 | void | |
8ae45c11 | 294 | fill_fpregset (gdb_fpregset_t *fpregsetp, int regno) |
fdb28ac4 KB |
295 | { |
296 | int regi; | |
fdb28ac4 KB |
297 | |
298 | for (regi = 0; regi < 32; regi++) | |
299 | { | |
300 | if ((regno == -1) || (regno == FP0_REGNUM + regi)) | |
f00d3753 | 301 | regcache_collect (FP0_REGNUM + regi, (char *) (*fpregsetp + regi)); |
fdb28ac4 KB |
302 | } |
303 | } |