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