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