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