]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gdb/s390-nat.c
2004-02-17 Ulrich Weigand <Ulrich.Weigand@de.ibm.com>
[thirdparty/binutils-gdb.git] / gdb / s390-nat.c
CommitLineData
5769d3cd 1/* S390 native-dependent code for GDB, the GNU debugger.
d0f54f9d
JB
2 Copyright 2001, 2003 Free Software Foundation, Inc
3
5769d3cd
AC
4 Contributed by D.J. Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
5 for IBM Deutschland Entwicklung GmbH, IBM Corporation.
d0f54f9d 6
5769d3cd
AC
7 This file is part of GDB.
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
22 02111-1307, USA. */
23
24#include "defs.h"
25#include "tm.h"
3ecc0ae2 26#include "regcache.h"
d0f54f9d
JB
27#include "inferior.h"
28
29#include "s390-tdep.h"
30
5769d3cd
AC
31#include <asm/ptrace.h>
32#include <sys/ptrace.h>
2d0c7962 33#include <asm/types.h>
5769d3cd
AC
34#include <sys/procfs.h>
35#include <sys/user.h>
5769d3cd 36#include <sys/ucontext.h>
5769d3cd
AC
37
38
d0f54f9d
JB
39/* Map registers to gregset/ptrace offsets.
40 These arrays are defined in s390-tdep.c. */
41
42#ifdef __s390x__
43#define regmap_gregset s390x_regmap_gregset
5769d3cd 44#else
d0f54f9d 45#define regmap_gregset s390_regmap_gregset
5769d3cd 46#endif
d0f54f9d
JB
47
48#define regmap_fpregset s390_regmap_fpregset
49
50
51/* Fill GDB's register array with the general-purpose register values
52 in *REGP. */
53void
54supply_gregset (gregset_t *regp)
55{
56 int i;
57 for (i = 0; i < S390_NUM_REGS; i++)
58 if (regmap_gregset[i] != -1)
59 regcache_raw_supply (current_regcache, i,
60 (char *)regp + regmap_gregset[i]);
61}
62
63/* Fill register REGNO (if it is a general-purpose register) in
64 *REGP with the value in GDB's register array. If REGNO is -1,
65 do this for all registers. */
66void
67fill_gregset (gregset_t *regp, int regno)
68{
69 int i;
70 for (i = 0; i < S390_NUM_REGS; i++)
71 if (regmap_gregset[i] != -1)
72 if (regno == -1 || regno == i)
73 regcache_raw_collect (current_regcache, i,
74 (char *)regp + regmap_gregset[i]);
75}
76
77/* Fill GDB's register array with the floating-point register values
78 in *REGP. */
79void
80supply_fpregset (fpregset_t *regp)
81{
82 int i;
83 for (i = 0; i < S390_NUM_REGS; i++)
84 if (regmap_fpregset[i] != -1)
85 regcache_raw_supply (current_regcache, i,
86 ((char *)regp) + regmap_fpregset[i]);
87}
88
89/* Fill register REGNO (if it is a general-purpose register) in
90 *REGP with the value in GDB's register array. If REGNO is -1,
91 do this for all registers. */
92void
93fill_fpregset (fpregset_t *regp, int regno)
94{
95 int i;
96 for (i = 0; i < S390_NUM_REGS; i++)
97 if (regmap_fpregset[i] != -1)
98 if (regno == -1 || regno == i)
99 regcache_raw_collect (current_regcache, i,
100 ((char *)regp) + regmap_fpregset[i]);
101}
102
103/* Find the TID for the current inferior thread to use with ptrace. */
104static int
105s390_inferior_tid (void)
106{
107 /* GNU/Linux LWP ID's are process ID's. */
108 int tid = TIDGET (inferior_ptid);
109 if (tid == 0)
110 tid = PIDGET (inferior_ptid); /* Not a threaded program. */
111
112 return tid;
113}
114
115/* Fetch all general-purpose registers from process/thread TID and
116 store their values in GDB's register cache. */
117static void
118fetch_regs (int tid)
119{
120 gregset_t regs;
121 ptrace_area parea;
122
123 parea.len = sizeof (regs);
124 parea.process_addr = (addr_t) &regs;
125 parea.kernel_addr = offsetof (struct user_regs_struct, psw);
126 if (ptrace (PTRACE_PEEKUSR_AREA, tid, (long) &parea) < 0)
127 perror_with_name ("Couldn't get registers");
128
129 supply_gregset (&regs);
130}
131
132/* Store all valid general-purpose registers in GDB's register cache
133 into the process/thread specified by TID. */
134static void
135store_regs (int tid, int regnum)
136{
137 gregset_t regs;
138 ptrace_area parea;
139
140 parea.len = sizeof (regs);
141 parea.process_addr = (addr_t) &regs;
142 parea.kernel_addr = offsetof (struct user_regs_struct, psw);
143 if (ptrace (PTRACE_PEEKUSR_AREA, tid, (long) &parea) < 0)
144 perror_with_name ("Couldn't get registers");
145
146 fill_gregset (&regs, regnum);
147
148 if (ptrace (PTRACE_POKEUSR_AREA, tid, (long) &parea) < 0)
149 perror_with_name ("Couldn't write registers");
150}
151
152/* Fetch all floating-point registers from process/thread TID and store
153 their values in GDB's register cache. */
154static void
155fetch_fpregs (int tid)
156{
157 fpregset_t fpregs;
158 ptrace_area parea;
159
160 parea.len = sizeof (fpregs);
161 parea.process_addr = (addr_t) &fpregs;
162 parea.kernel_addr = offsetof (struct user_regs_struct, fp_regs);
163 if (ptrace (PTRACE_PEEKUSR_AREA, tid, (long) &parea) < 0)
164 perror_with_name ("Couldn't get floating point status");
165
166 supply_fpregset (&fpregs);
167}
168
169/* Store all valid floating-point registers in GDB's register cache
170 into the process/thread specified by TID. */
171static void
172store_fpregs (int tid, int regnum)
173{
174 fpregset_t fpregs;
175 ptrace_area parea;
176
177 parea.len = sizeof (fpregs);
178 parea.process_addr = (addr_t) &fpregs;
179 parea.kernel_addr = offsetof (struct user_regs_struct, fp_regs);
180 if (ptrace (PTRACE_PEEKUSR_AREA, tid, (long) &parea) < 0)
181 perror_with_name ("Couldn't get floating point status");
182
183 fill_fpregset (&fpregs, regnum);
184
185 if (ptrace (PTRACE_POKEUSR_AREA, tid, (long) &parea) < 0)
186 perror_with_name ("Couldn't write floating point status");
187}
188
189/* Fetch register REGNUM from the child process. If REGNUM is -1, do
190 this for all registers. */
191void
192fetch_inferior_registers (int regnum)
193{
194 int tid = s390_inferior_tid ();
195
196 if (regnum == -1
197 || (regnum < S390_NUM_REGS && regmap_gregset[regnum] != -1))
198 fetch_regs (tid);
199
200 if (regnum == -1
201 || (regnum < S390_NUM_REGS && regmap_fpregset[regnum] != -1))
202 fetch_fpregs (tid);
203}
204
205/* Store register REGNUM back into the child process. If REGNUM is
206 -1, do this for all registers. */
207void
208store_inferior_registers (int regnum)
209{
210 int tid = s390_inferior_tid ();
211
212 if (regnum == -1
213 || (regnum < S390_NUM_REGS && regmap_gregset[regnum] != -1))
214 store_regs (tid, regnum);
215
216 if (regnum == -1
217 || (regnum < S390_NUM_REGS && regmap_fpregset[regnum] != -1))
218 store_fpregs (tid, regnum);
5769d3cd
AC
219}
220
d0f54f9d 221
5769d3cd
AC
222/* watch_areas are required if you put 2 or more watchpoints on the same
223 address or overlapping areas gdb will call us to delete the watchpoint
224 more than once when we try to delete them.
225 attempted reference counting to reduce the number of areas unfortunately
226 they didn't shrink when areas had to be split overlapping occurs. */
227struct watch_area;
228typedef struct watch_area watch_area;
229struct watch_area
230{
231 watch_area *next;
232 CORE_ADDR lo_addr;
233 CORE_ADDR hi_addr;
234};
235
236static watch_area *watch_base = NULL;
237int watch_area_cnt = 0;
238static CORE_ADDR watch_lo_addr = 0, watch_hi_addr = 0;
239
240
241
242CORE_ADDR
243s390_stopped_by_watchpoint (int pid)
244{
245 per_lowcore_bits per_lowcore;
246 ptrace_area parea;
247
248 parea.len = sizeof (per_lowcore);
249 parea.process_addr = (addr_t) & per_lowcore;
250 parea.kernel_addr = offsetof (struct user_regs_struct, per_info.lowcore);
251 ptrace (PTRACE_PEEKUSR_AREA, pid, &parea);
252 return ((per_lowcore.perc_storage_alteration == 1) &&
253 (per_lowcore.perc_store_real_address == 0));
254}
255
256
257void
258s390_fix_watch_points (int pid)
259{
260 per_struct per_info;
261 ptrace_area parea;
262
263 parea.len = sizeof (per_info);
264 parea.process_addr = (addr_t) & per_info;
265 parea.kernel_addr = PT_CR_9;
266 ptrace (PTRACE_PEEKUSR_AREA, pid, &parea);
267 /* The kernel automatically sets the psw for per depending */
268 /* on whether the per control registers are set for event recording */
269 /* & sets cr9 & cr10 appropriately also */
270 if (watch_area_cnt)
271 {
272 per_info.control_regs.bits.em_storage_alteration = 1;
273 per_info.control_regs.bits.storage_alt_space_ctl = 1;
274 }
275 else
276 {
277 per_info.control_regs.bits.em_storage_alteration = 0;
278 per_info.control_regs.bits.storage_alt_space_ctl = 0;
279 }
280 per_info.starting_addr = watch_lo_addr;
281 per_info.ending_addr = watch_hi_addr;
282 ptrace (PTRACE_POKEUSR_AREA, pid, &parea);
283}
284
285int
286s390_insert_watchpoint (int pid, CORE_ADDR addr, int len, int rw)
287{
288 CORE_ADDR hi_addr = addr + len - 1;
1de2edba 289 watch_area *newarea = (watch_area *) xmalloc (sizeof (watch_area));
5769d3cd
AC
290
291
292 if (newarea)
293 {
294 newarea->next = watch_base;
295 watch_base = newarea;
296 watch_lo_addr = min (watch_lo_addr, addr);
297 watch_hi_addr = max (watch_hi_addr, hi_addr);
298 newarea->lo_addr = addr;
299 newarea->hi_addr = hi_addr;
300 if (watch_area_cnt == 0)
301 {
302 watch_lo_addr = newarea->lo_addr;
303 watch_hi_addr = newarea->hi_addr;
304 }
305 watch_area_cnt++;
306 s390_fix_watch_points (pid);
307 }
308 return newarea ? 0 : -1;
309}
310
311
312int
313s390_remove_watchpoint (int pid, CORE_ADDR addr, int len)
314{
315 watch_area *curr = watch_base, *prev, *matchCurr;
316 CORE_ADDR hi_addr = addr + len - 1;
317 CORE_ADDR watch_second_lo_addr = 0xffffffffUL, watch_second_hi_addr = 0;
318 int lo_addr_ref_cnt, hi_addr_ref_cnt;
319 prev = matchCurr = NULL;
320 lo_addr_ref_cnt = (addr == watch_lo_addr);
321 hi_addr_ref_cnt = (addr == watch_hi_addr);
322 while (curr)
323 {
324 if (matchCurr == NULL)
325 {
326 if (curr->lo_addr == addr && curr->hi_addr == hi_addr)
327 {
328 matchCurr = curr;
329 if (prev)
330 prev->next = curr->next;
331 else
332 watch_base = curr->next;
333 }
334 prev = curr;
335 }
336 if (lo_addr_ref_cnt)
337 {
338 if (watch_lo_addr == curr->lo_addr)
339 lo_addr_ref_cnt++;
340 if (curr->lo_addr > watch_lo_addr &&
341 curr->lo_addr < watch_second_lo_addr)
342 watch_second_lo_addr = curr->lo_addr;
343 }
344 if (hi_addr_ref_cnt)
345 {
346 if (watch_hi_addr == curr->hi_addr)
347 hi_addr_ref_cnt++;
348 if (curr->hi_addr < watch_hi_addr &&
349 curr->hi_addr > watch_second_hi_addr)
350 watch_second_hi_addr = curr->hi_addr;
351 }
352 curr = curr->next;
353 }
354 if (matchCurr)
355 {
1de2edba 356 xfree (matchCurr);
5769d3cd
AC
357 watch_area_cnt--;
358 if (watch_area_cnt)
359 {
360 if (lo_addr_ref_cnt == 2)
361 watch_lo_addr = watch_second_lo_addr;
362 if (hi_addr_ref_cnt == 2)
363 watch_hi_addr = watch_second_hi_addr;
364 }
365 else
366 {
367 watch_lo_addr = watch_hi_addr = 0;
368 }
369 s390_fix_watch_points (pid);
370 return 0;
371 }
372 else
373 {
374 fprintf_unfiltered (gdb_stderr,
375 "Attempt to remove nonexistent watchpoint in s390_remove_watchpoint\n");
376 return -1;
377 }
378}
379
380int
381kernel_u_size (void)
382{
383 return sizeof (struct user);
384}
385