]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blame - gdb/s390-nat.c
S/390 31 & 64 bit target and GNU/Linux native support.
[thirdparty/binutils-gdb.git] / gdb / s390-nat.c
CommitLineData
5769d3cd
AC
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
36int
37s390_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. */
79struct watch_area;
80typedef struct watch_area watch_area;
81struct watch_area
82{
83 watch_area *next;
84 CORE_ADDR lo_addr;
85 CORE_ADDR hi_addr;
86};
87
88static watch_area *watch_base = NULL;
89int watch_area_cnt = 0;
90static CORE_ADDR watch_lo_addr = 0, watch_hi_addr = 0;
91
92
93
94CORE_ADDR
95s390_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
109void
110s390_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
137int
138s390_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
164int
165s390_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
232int
233kernel_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))
240void
241supply_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
260void
261supply_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
271void
272fill_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
289void
290fill_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 */