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