]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame_incremental - gdb/s390-nat.c
*** empty log message ***
[thirdparty/binutils-gdb.git] / gdb / s390-nat.c
... / ...
CommitLineData
1/* S390 native-dependent code for GDB, the GNU debugger.
2 Copyright (C) 2001, 2003, 2004, 2005, 2006
3 Free Software Foundation, Inc
4
5 Contributed by D.J. Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
6 for IBM Deutschland Entwicklung GmbH, IBM Corporation.
7
8 This file is part of GDB.
9
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>. */
22
23#include "defs.h"
24#include "regcache.h"
25#include "inferior.h"
26#include "target.h"
27#include "linux-nat.h"
28
29#include "s390-tdep.h"
30
31#include <asm/ptrace.h>
32#include <sys/ptrace.h>
33#include <asm/types.h>
34#include <sys/procfs.h>
35#include <sys/ucontext.h>
36
37
38/* Map registers to gregset/ptrace offsets.
39 These arrays are defined in s390-tdep.c. */
40
41#ifdef __s390x__
42#define regmap_gregset s390x_regmap_gregset
43#else
44#define regmap_gregset s390_regmap_gregset
45#endif
46
47#define regmap_fpregset s390_regmap_fpregset
48
49/* When debugging a 32-bit executable running under a 64-bit kernel,
50 we have to fix up the 64-bit registers we get from the kernel
51 to make them look like 32-bit registers. */
52#ifdef __s390x__
53#define SUBOFF(gdbarch, i) \
54 ((gdbarch_ptr_bit (gdbarch) == 32 \
55 && ((i) == S390_PSWA_REGNUM \
56 || ((i) >= S390_R0_REGNUM && (i) <= S390_R15_REGNUM)))? 4 : 0)
57#else
58#define SUBOFF(gdbarch, i) 0
59#endif
60
61
62/* Fill GDB's register array with the general-purpose register values
63 in *REGP. */
64void
65supply_gregset (struct regcache *regcache, const gregset_t *regp)
66{
67 struct gdbarch *gdbarch = get_regcache_arch (regcache);
68 int i;
69 for (i = 0; i < S390_NUM_REGS; i++)
70 if (regmap_gregset[i] != -1)
71 regcache_raw_supply (regcache, i,
72 (const char *)regp + regmap_gregset[i]
73 + SUBOFF (gdbarch, i));
74}
75
76/* Fill register REGNO (if it is a general-purpose register) in
77 *REGP with the value in GDB's register array. If REGNO is -1,
78 do this for all registers. */
79void
80fill_gregset (const struct regcache *regcache, gregset_t *regp, int regno)
81{
82 struct gdbarch *gdbarch = get_regcache_arch (regcache);
83 int i;
84 for (i = 0; i < S390_NUM_REGS; i++)
85 if (regmap_gregset[i] != -1)
86 if (regno == -1 || regno == i)
87 regcache_raw_collect (regcache, i,
88 (char *)regp + regmap_gregset[i]
89 + SUBOFF (gdbarch, i));
90}
91
92/* Fill GDB's register array with the floating-point register values
93 in *REGP. */
94void
95supply_fpregset (struct regcache *regcache, const fpregset_t *regp)
96{
97 int i;
98 for (i = 0; i < S390_NUM_REGS; i++)
99 if (regmap_fpregset[i] != -1)
100 regcache_raw_supply (regcache, i,
101 (const char *)regp + regmap_fpregset[i]);
102}
103
104/* Fill register REGNO (if it is a general-purpose register) in
105 *REGP with the value in GDB's register array. If REGNO is -1,
106 do this for all registers. */
107void
108fill_fpregset (const struct regcache *regcache, fpregset_t *regp, int regno)
109{
110 int i;
111 for (i = 0; i < S390_NUM_REGS; i++)
112 if (regmap_fpregset[i] != -1)
113 if (regno == -1 || regno == i)
114 regcache_raw_collect (regcache, i,
115 (char *)regp + regmap_fpregset[i]);
116}
117
118/* Find the TID for the current inferior thread to use with ptrace. */
119static int
120s390_inferior_tid (void)
121{
122 /* GNU/Linux LWP ID's are process ID's. */
123 int tid = TIDGET (inferior_ptid);
124 if (tid == 0)
125 tid = PIDGET (inferior_ptid); /* Not a threaded program. */
126
127 return tid;
128}
129
130/* Fetch all general-purpose registers from process/thread TID and
131 store their values in GDB's register cache. */
132static void
133fetch_regs (struct regcache *regcache, int tid)
134{
135 gregset_t regs;
136 ptrace_area parea;
137
138 parea.len = sizeof (regs);
139 parea.process_addr = (addr_t) &regs;
140 parea.kernel_addr = offsetof (struct user_regs_struct, psw);
141 if (ptrace (PTRACE_PEEKUSR_AREA, tid, (long) &parea) < 0)
142 perror_with_name (_("Couldn't get registers"));
143
144 supply_gregset (regcache, (const gregset_t *) &regs);
145}
146
147/* Store all valid general-purpose registers in GDB's register cache
148 into the process/thread specified by TID. */
149static void
150store_regs (const struct regcache *regcache, int tid, int regnum)
151{
152 gregset_t regs;
153 ptrace_area parea;
154
155 parea.len = sizeof (regs);
156 parea.process_addr = (addr_t) &regs;
157 parea.kernel_addr = offsetof (struct user_regs_struct, psw);
158 if (ptrace (PTRACE_PEEKUSR_AREA, tid, (long) &parea) < 0)
159 perror_with_name (_("Couldn't get registers"));
160
161 fill_gregset (regcache, &regs, regnum);
162
163 if (ptrace (PTRACE_POKEUSR_AREA, tid, (long) &parea) < 0)
164 perror_with_name (_("Couldn't write registers"));
165}
166
167/* Fetch all floating-point registers from process/thread TID and store
168 their values in GDB's register cache. */
169static void
170fetch_fpregs (struct regcache *regcache, int tid)
171{
172 fpregset_t fpregs;
173 ptrace_area parea;
174
175 parea.len = sizeof (fpregs);
176 parea.process_addr = (addr_t) &fpregs;
177 parea.kernel_addr = offsetof (struct user_regs_struct, fp_regs);
178 if (ptrace (PTRACE_PEEKUSR_AREA, tid, (long) &parea) < 0)
179 perror_with_name (_("Couldn't get floating point status"));
180
181 supply_fpregset (regcache, (const fpregset_t *) &fpregs);
182}
183
184/* Store all valid floating-point registers in GDB's register cache
185 into the process/thread specified by TID. */
186static void
187store_fpregs (const struct regcache *regcache, int tid, int regnum)
188{
189 fpregset_t fpregs;
190 ptrace_area parea;
191
192 parea.len = sizeof (fpregs);
193 parea.process_addr = (addr_t) &fpregs;
194 parea.kernel_addr = offsetof (struct user_regs_struct, fp_regs);
195 if (ptrace (PTRACE_PEEKUSR_AREA, tid, (long) &parea) < 0)
196 perror_with_name (_("Couldn't get floating point status"));
197
198 fill_fpregset (regcache, &fpregs, regnum);
199
200 if (ptrace (PTRACE_POKEUSR_AREA, tid, (long) &parea) < 0)
201 perror_with_name (_("Couldn't write floating point status"));
202}
203
204/* Fetch register REGNUM from the child process. If REGNUM is -1, do
205 this for all registers. */
206static void
207s390_linux_fetch_inferior_registers (struct regcache *regcache, int regnum)
208{
209 int tid = s390_inferior_tid ();
210
211 if (regnum == -1
212 || (regnum < S390_NUM_REGS && regmap_gregset[regnum] != -1))
213 fetch_regs (regcache, tid);
214
215 if (regnum == -1
216 || (regnum < S390_NUM_REGS && regmap_fpregset[regnum] != -1))
217 fetch_fpregs (regcache, tid);
218}
219
220/* Store register REGNUM back into the child process. If REGNUM is
221 -1, do this for all registers. */
222static void
223s390_linux_store_inferior_registers (struct regcache *regcache, int regnum)
224{
225 int tid = s390_inferior_tid ();
226
227 if (regnum == -1
228 || (regnum < S390_NUM_REGS && regmap_gregset[regnum] != -1))
229 store_regs (regcache, tid, regnum);
230
231 if (regnum == -1
232 || (regnum < S390_NUM_REGS && regmap_fpregset[regnum] != -1))
233 store_fpregs (regcache, tid, regnum);
234}
235
236
237/* Hardware-assisted watchpoint handling. */
238
239/* We maintain a list of all currently active watchpoints in order
240 to properly handle watchpoint removal.
241
242 The only thing we actually need is the total address space area
243 spanned by the watchpoints. */
244
245struct watch_area
246{
247 struct watch_area *next;
248 CORE_ADDR lo_addr;
249 CORE_ADDR hi_addr;
250};
251
252static struct watch_area *watch_base = NULL;
253
254static int
255s390_stopped_by_watchpoint (void)
256{
257 per_lowcore_bits per_lowcore;
258 ptrace_area parea;
259 int result;
260
261 /* Speed up common case. */
262 if (!watch_base)
263 return 0;
264
265 parea.len = sizeof (per_lowcore);
266 parea.process_addr = (addr_t) & per_lowcore;
267 parea.kernel_addr = offsetof (struct user_regs_struct, per_info.lowcore);
268 if (ptrace (PTRACE_PEEKUSR_AREA, s390_inferior_tid (), &parea) < 0)
269 perror_with_name (_("Couldn't retrieve watchpoint status"));
270
271 result = (per_lowcore.perc_storage_alteration == 1
272 && per_lowcore.perc_store_real_address == 0);
273
274 if (result)
275 {
276 /* Do not report this watchpoint again. */
277 memset (&per_lowcore, 0, sizeof (per_lowcore));
278 if (ptrace (PTRACE_POKEUSR_AREA, s390_inferior_tid (), &parea) < 0)
279 perror_with_name (_("Couldn't clear watchpoint status"));
280 }
281
282 return result;
283}
284
285static void
286s390_fix_watch_points (ptid_t ptid)
287{
288 int tid;
289
290 per_struct per_info;
291 ptrace_area parea;
292
293 CORE_ADDR watch_lo_addr = (CORE_ADDR)-1, watch_hi_addr = 0;
294 struct watch_area *area;
295
296 tid = TIDGET (ptid);
297 if (tid == 0)
298 tid = PIDGET (ptid);
299
300 for (area = watch_base; area; area = area->next)
301 {
302 watch_lo_addr = min (watch_lo_addr, area->lo_addr);
303 watch_hi_addr = max (watch_hi_addr, area->hi_addr);
304 }
305
306 parea.len = sizeof (per_info);
307 parea.process_addr = (addr_t) & per_info;
308 parea.kernel_addr = offsetof (struct user_regs_struct, per_info);
309 if (ptrace (PTRACE_PEEKUSR_AREA, tid, &parea) < 0)
310 perror_with_name (_("Couldn't retrieve watchpoint status"));
311
312 if (watch_base)
313 {
314 per_info.control_regs.bits.em_storage_alteration = 1;
315 per_info.control_regs.bits.storage_alt_space_ctl = 1;
316 }
317 else
318 {
319 per_info.control_regs.bits.em_storage_alteration = 0;
320 per_info.control_regs.bits.storage_alt_space_ctl = 0;
321 }
322 per_info.starting_addr = watch_lo_addr;
323 per_info.ending_addr = watch_hi_addr;
324
325 if (ptrace (PTRACE_POKEUSR_AREA, tid, &parea) < 0)
326 perror_with_name (_("Couldn't modify watchpoint status"));
327}
328
329static int
330s390_insert_watchpoint (CORE_ADDR addr, int len, int type)
331{
332 struct lwp_info *lp;
333 ptid_t ptid;
334 struct watch_area *area = xmalloc (sizeof (struct watch_area));
335
336 if (!area)
337 return -1;
338
339 area->lo_addr = addr;
340 area->hi_addr = addr + len - 1;
341
342 area->next = watch_base;
343 watch_base = area;
344
345 ALL_LWPS (lp, ptid)
346 s390_fix_watch_points (ptid);
347 return 0;
348}
349
350static int
351s390_remove_watchpoint (CORE_ADDR addr, int len, int type)
352{
353 struct lwp_info *lp;
354 ptid_t ptid;
355 struct watch_area *area, **parea;
356
357 for (parea = &watch_base; *parea; parea = &(*parea)->next)
358 if ((*parea)->lo_addr == addr
359 && (*parea)->hi_addr == addr + len - 1)
360 break;
361
362 if (!*parea)
363 {
364 fprintf_unfiltered (gdb_stderr,
365 "Attempt to remove nonexistent watchpoint.\n");
366 return -1;
367 }
368
369 area = *parea;
370 *parea = area->next;
371 xfree (area);
372
373 ALL_LWPS (lp, ptid)
374 s390_fix_watch_points (ptid);
375 return 0;
376}
377
378static int
379s390_can_use_hw_breakpoint (int type, int cnt, int othertype)
380{
381 return 1;
382}
383
384static int
385s390_region_ok_for_hw_watchpoint (CORE_ADDR addr, int cnt)
386{
387 return 1;
388}
389
390
391void _initialize_s390_nat (void);
392
393void
394_initialize_s390_nat (void)
395{
396 struct target_ops *t;
397
398 /* Fill in the generic GNU/Linux methods. */
399 t = linux_target ();
400
401 /* Add our register access methods. */
402 t->to_fetch_registers = s390_linux_fetch_inferior_registers;
403 t->to_store_registers = s390_linux_store_inferior_registers;
404
405 /* Add our watchpoint methods. */
406 t->to_can_use_hw_breakpoint = s390_can_use_hw_breakpoint;
407 t->to_region_ok_for_hw_watchpoint = s390_region_ok_for_hw_watchpoint;
408 t->to_have_continuable_watchpoint = 1;
409 t->to_stopped_by_watchpoint = s390_stopped_by_watchpoint;
410 t->to_insert_watchpoint = s390_insert_watchpoint;
411 t->to_remove_watchpoint = s390_remove_watchpoint;
412
413 /* Register the target. */
414 linux_nat_add_target (t);
415 linux_nat_set_new_thread (t, s390_fix_watch_points);
416}