S/390 31 & 64 bit target and GNU/Linux native support.
[external/binutils.git] / gdb / s390-nat.c
1 /* S390 native-dependent code for GDB, the GNU debugger.
2    Copyright 2001 Free Software Foundation, Inc
3    Contributed by D.J. Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
4    for IBM Deutschland Entwicklung GmbH, IBM Corporation.
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
19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20    02111-1307, USA.  */
21
22 #include "defs.h"
23 #include "tm.h"
24 #include <asm/ptrace.h>
25 #include <sys/ptrace.h>
26 #include <asm/processor.h>
27 #include <sys/procfs.h>
28 #include <sys/user.h>
29 #include <value.h>
30 #include <sys/ucontext.h>
31 #ifndef offsetof
32 #define offsetof(type,member) ((size_t) &((type *)0)->member)
33 #endif
34
35
36 int
37 s390_register_u_addr (int blockend, int regnum)
38 {
39   int retval;
40
41   if (regnum >= S390_GP0_REGNUM && regnum <= S390_GP_LAST_REGNUM)
42     retval = PT_GPR0 + ((regnum - S390_GP0_REGNUM) * S390_GPR_SIZE);
43   else if (regnum >= S390_PSWM_REGNUM && regnum <= S390_PC_REGNUM)
44     retval = PT_PSWMASK + ((regnum - S390_PSWM_REGNUM) * S390_PSW_MASK_SIZE);
45   else if (regnum == S390_FPC_REGNUM)
46     retval = PT_FPC;
47   else if (regnum >= S390_FP0_REGNUM && regnum <= S390_FPLAST_REGNUM)
48     retval =
49 #if CONFIG_ARCH_S390X
50       PT_FPR0
51 #else
52       PT_FPR0_HI
53 #endif
54       + ((regnum - S390_FP0_REGNUM) * S390_FPR_SIZE);
55   else if (regnum >= S390_FIRST_ACR && regnum <= S390_LAST_ACR)
56     retval = PT_ACR0 + ((regnum - S390_FIRST_ACR) * S390_ACR_SIZE);
57   else if (regnum >= (S390_FIRST_CR + 9) && regnum <= (S390_FIRST_CR + 11))
58     retval = PT_CR_9 + ((regnum - (S390_FIRST_CR + 9)) * S390_CR_SIZE);
59   else
60     {
61 #ifdef GDBSERVER
62       error
63 #else
64       internal_error
65 #endif
66         ("s390_register_u_addr invalid regnum %s %d regnum=%d", __FILE__,
67          (int) __LINE__, regnum);
68       retval = 0;
69     }
70   return retval + blockend;
71 }
72
73 #ifndef GDBSERVER
74 /* watch_areas are required if you put 2 or more watchpoints on the same 
75    address or overlapping areas gdb will call us to delete the watchpoint 
76    more than once when we try to delete them.
77    attempted reference counting to reduce the number of areas unfortunately
78    they didn't shrink when areas had to be split overlapping occurs. */
79 struct watch_area;
80 typedef struct watch_area watch_area;
81 struct watch_area
82 {
83   watch_area *next;
84   CORE_ADDR lo_addr;
85   CORE_ADDR hi_addr;
86 };
87
88 static watch_area *watch_base = NULL;
89 int watch_area_cnt = 0;
90 static CORE_ADDR watch_lo_addr = 0, watch_hi_addr = 0;
91
92
93
94 CORE_ADDR
95 s390_stopped_by_watchpoint (int pid)
96 {
97   per_lowcore_bits per_lowcore;
98   ptrace_area parea;
99
100   parea.len = sizeof (per_lowcore);
101   parea.process_addr = (addr_t) & per_lowcore;
102   parea.kernel_addr = offsetof (struct user_regs_struct, per_info.lowcore);
103   ptrace (PTRACE_PEEKUSR_AREA, pid, &parea);
104   return ((per_lowcore.perc_storage_alteration == 1) &&
105           (per_lowcore.perc_store_real_address == 0));
106 }
107
108
109 void
110 s390_fix_watch_points (int pid)
111 {
112   per_struct per_info;
113   ptrace_area parea;
114
115   parea.len = sizeof (per_info);
116   parea.process_addr = (addr_t) & per_info;
117   parea.kernel_addr = PT_CR_9;
118   ptrace (PTRACE_PEEKUSR_AREA, pid, &parea);
119   /* The kernel automatically sets the psw for per depending */
120   /* on whether the per control registers are set for event recording */
121   /* & sets cr9 & cr10 appropriately also */
122   if (watch_area_cnt)
123     {
124       per_info.control_regs.bits.em_storage_alteration = 1;
125       per_info.control_regs.bits.storage_alt_space_ctl = 1;
126     }
127   else
128     {
129       per_info.control_regs.bits.em_storage_alteration = 0;
130       per_info.control_regs.bits.storage_alt_space_ctl = 0;
131     }
132   per_info.starting_addr = watch_lo_addr;
133   per_info.ending_addr = watch_hi_addr;
134   ptrace (PTRACE_POKEUSR_AREA, pid, &parea);
135 }
136
137 int
138 s390_insert_watchpoint (int pid, CORE_ADDR addr, int len, int rw)
139 {
140   CORE_ADDR hi_addr = addr + len - 1;
141   watch_area *newarea = (watch_area *) malloc (sizeof (watch_area));
142
143
144   if (newarea)
145     {
146       newarea->next = watch_base;
147       watch_base = newarea;
148       watch_lo_addr = min (watch_lo_addr, addr);
149       watch_hi_addr = max (watch_hi_addr, hi_addr);
150       newarea->lo_addr = addr;
151       newarea->hi_addr = hi_addr;
152       if (watch_area_cnt == 0)
153         {
154           watch_lo_addr = newarea->lo_addr;
155           watch_hi_addr = newarea->hi_addr;
156         }
157       watch_area_cnt++;
158       s390_fix_watch_points (pid);
159     }
160   return newarea ? 0 : -1;
161 }
162
163
164 int
165 s390_remove_watchpoint (int pid, CORE_ADDR addr, int len)
166 {
167   watch_area *curr = watch_base, *prev, *matchCurr;
168   CORE_ADDR hi_addr = addr + len - 1;
169   CORE_ADDR watch_second_lo_addr = 0xffffffffUL, watch_second_hi_addr = 0;
170   int lo_addr_ref_cnt, hi_addr_ref_cnt;
171   prev = matchCurr = NULL;
172   lo_addr_ref_cnt = (addr == watch_lo_addr);
173   hi_addr_ref_cnt = (addr == watch_hi_addr);
174   while (curr)
175     {
176       if (matchCurr == NULL)
177         {
178           if (curr->lo_addr == addr && curr->hi_addr == hi_addr)
179             {
180               matchCurr = curr;
181               if (prev)
182                 prev->next = curr->next;
183               else
184                 watch_base = curr->next;
185             }
186           prev = curr;
187         }
188       if (lo_addr_ref_cnt)
189         {
190           if (watch_lo_addr == curr->lo_addr)
191             lo_addr_ref_cnt++;
192           if (curr->lo_addr > watch_lo_addr &&
193               curr->lo_addr < watch_second_lo_addr)
194             watch_second_lo_addr = curr->lo_addr;
195         }
196       if (hi_addr_ref_cnt)
197         {
198           if (watch_hi_addr == curr->hi_addr)
199             hi_addr_ref_cnt++;
200           if (curr->hi_addr < watch_hi_addr &&
201               curr->hi_addr > watch_second_hi_addr)
202             watch_second_hi_addr = curr->hi_addr;
203         }
204       curr = curr->next;
205     }
206   if (matchCurr)
207     {
208       free (matchCurr);
209       watch_area_cnt--;
210       if (watch_area_cnt)
211         {
212           if (lo_addr_ref_cnt == 2)
213             watch_lo_addr = watch_second_lo_addr;
214           if (hi_addr_ref_cnt == 2)
215             watch_hi_addr = watch_second_hi_addr;
216         }
217       else
218         {
219           watch_lo_addr = watch_hi_addr = 0;
220         }
221       s390_fix_watch_points (pid);
222       return 0;
223     }
224   else
225     {
226       fprintf_unfiltered (gdb_stderr,
227                           "Attempt to remove nonexistent watchpoint in s390_remove_watchpoint\n");
228       return -1;
229     }
230 }
231
232 int
233 kernel_u_size (void)
234 {
235   return sizeof (struct user);
236 }
237
238
239 #if  (defined (S390_FP0_REGNUM) && defined (HAVE_FPREGSET_T) && defined(HAVE_SYS_PROCFS_H) && defined (HAVE_GREGSET_T))
240 void
241 supply_gregset (gregset_t * gregsetp)
242 {
243   int regi;
244   greg_t *gregp = (greg_t *) gregsetp;
245
246   supply_register (S390_PSWM_REGNUM, (char *) &gregp[S390_PSWM_REGNUM]);
247   supply_register (S390_PC_REGNUM, (char *) &gregp[S390_PC_REGNUM]);
248   for (regi = 0; regi < S390_NUM_GPRS; regi++)
249     supply_register (S390_GP0_REGNUM + regi,
250                      (char *) &gregp[S390_GP0_REGNUM + regi]);
251   for (regi = 0; regi < S390_NUM_ACRS; regi++)
252     supply_register (S390_FIRST_ACR + regi,
253                      (char *) &gregp[S390_FIRST_ACR + regi]);
254   /* unfortunately this isn't in gregsetp */
255   for (regi = 0; regi < S390_NUM_CRS; regi++)
256     supply_register (S390_FIRST_CR + regi, NULL);
257 }
258
259
260 void
261 supply_fpregset (fpregset_t * fpregsetp)
262 {
263   int regi;
264
265   supply_register (S390_FPC_REGNUM, (char *) &fpregsetp->fpc);
266   for (regi = 0; regi < S390_NUM_FPRS; regi++)
267     supply_register (S390_FP0_REGNUM + regi, (char *) &fpregsetp->fprs[regi]);
268
269 }
270
271 void
272 fill_gregset (gregset_t * gregsetp, int regno)
273 {
274   greg_t *gregp = (greg_t *) gregsetp;
275
276   if (regno >= S390_FIRST_CR && regno <= S390_LAST_CR)
277     supply_register (regno, NULL);
278   else if (regno != -1)
279     supply_register (regno, (char *) &gregp[regno]);
280   else
281     supply_gregset (gregsetp);
282 }
283
284 /*  Given a pointer to a floating point register set in /proc format
285    (fpregset_t *), update the register specified by REGNO from gdb's idea
286    of the current floating point register set.  If REGNO is -1, update
287    them all. */
288
289 void
290 fill_fpregset (fpregset_t * fpregsetp, int regno)
291 {
292   if (regno == -1)
293     supply_fpregset (fpregsetp);
294   else
295     supply_register (regno,
296                      &((char *) fpregsetp)[REGISTER_BYTE (regno) -
297                                            REGISTER_BYTE (S390_FPC_REGNUM)]);
298 }
299
300
301 #else
302 #error "There are a few possibilities here"
303 #error "1) You aren't compiling for linux & don't need a core dumps to work."
304 #error "2) The header files sys/elf.h sys/user.h sys/ptrace.h & sys/procfs.h"
305 #error "libc files are inconsistent with linux/include/asm-s390/"
306 #error "3) you didn't do a completely clean build & delete config.cache."
307 #endif
308 #endif /* GDBSERVER */