1/* Intel 386 native support for System V systems (pre-SVR4).
2
3   Copyright 1988, 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1998,
4   1999, 2000, 2002 Free Software Foundation, Inc.
5
6   This file is part of GDB.
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2 of the License, or
11   (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program; if not, write to the Free Software
20   Foundation, Inc., 59 Temple Place - Suite 330,
21   Boston, MA 02111-1307, USA.  */
22
23#include "defs.h"
24
25#ifdef HAVE_PTRACE_H
26#include <ptrace.h>
27#else
28#ifdef HAVE_SYS_PTRACE_H
29#include <sys/ptrace.h>
30#endif
31#endif
32
33#include "frame.h"
34#include "inferior.h"
35#include "language.h"
36#include "gdbcore.h"
37
38#ifdef USG
39#include <sys/types.h>
40#endif
41
42#include <sys/param.h>
43#include <sys/dir.h>
44#include <signal.h>
45#include <sys/user.h>
46#include <sys/ioctl.h>
47#include <fcntl.h>
48
49#ifdef TARGET_HAS_HARDWARE_WATCHPOINTS
50#include <sys/debugreg.h>
51#endif
52
53#include <sys/file.h>
54#include "gdb_stat.h"
55
56#ifdef HAVE_SYS_REG_H
57#include <sys/reg.h>
58#endif
59
60#include "floatformat.h"
61
62#include "target.h"
63
64#include "i386-tdep.h"
65
66
67/* Mapping between the general-purpose registers in `struct user'
68   format and GDB's register array layout.  */
69static int regmap[] =
70{
71  EAX, ECX, EDX, EBX,
72  UESP, EBP, ESI, EDI,
73  EIP, EFL, CS, SS,
74  DS, ES, FS, GS,
75};
76
77/* Support for the user struct.  */
78
79/* Return the address of register REGNUM.  BLOCKEND is the value of
80   u.u_ar0, and points to the place where GS is stored.  */
81
82CORE_ADDR
83register_u_addr (CORE_ADDR blockend, int regnum)
84{
85  struct user u;
86  CORE_ADDR fpstate;
87
88  if (i386_fp_regnum_p (regnum))
89    {
90#ifdef KSTKSZ			/* SCO, and others?  */
91      blockend += 4 * (SS + 1) - KSTKSZ;
92      fpstate = blockend + ((char *) &u.u_fps.u_fpstate - (char *) &u);
93      return (fpstate + 0x1c + 10 * (regnum - FP0_REGNUM));
94#else
95      fpstate = blockend + ((char *) &u.i387.st_space - (char *) &u);
96      return (fpstate + 10 * (regnum - FP0_REGNUM));
97#endif
98    }
99
100  return (blockend + 4 * regmap[regnum]);
101}
102
103/* Return the size of the user struct.  */
104
105int
106kernel_u_size (void)
107{
108  return (sizeof (struct user));
109}
110
111#ifdef TARGET_HAS_HARDWARE_WATCHPOINTS
112
113#if !defined (offsetof)
114#define offsetof(TYPE, MEMBER) ((unsigned long) &((TYPE *)0)->MEMBER)
115#endif
116
117/* Record the value of the debug control register.  */
118static int debug_control_mirror;
119
120/* Record which address associates with which register.  */
121static CORE_ADDR address_lookup[DR_LASTADDR - DR_FIRSTADDR + 1];
122
123static int i386_insert_aligned_watchpoint (int, CORE_ADDR, CORE_ADDR, int,
124					   int);
125
126static int i386_insert_nonaligned_watchpoint (int, CORE_ADDR, CORE_ADDR, int,
127					      int);
128
129/* Insert a watchpoint.  */
130
131int
132i386_insert_watchpoint (int pid, CORE_ADDR addr, int len, int rw)
133{
134  return i386_insert_aligned_watchpoint (pid, addr, addr, len, rw);
135}
136
137static int
138i386_insert_aligned_watchpoint (int pid, CORE_ADDR waddr, CORE_ADDR addr,
139				int len, int rw)
140{
141  int i;
142  int read_write_bits, len_bits;
143  int free_debug_register;
144  int register_number;
145
146  /* Look for a free debug register.  */
147  for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
148    {
149      if (address_lookup[i - DR_FIRSTADDR] == 0)
150	break;
151    }
152
153  /* No more debug registers!  */
154  if (i > DR_LASTADDR)
155    return -1;
156
157  read_write_bits = (rw & 1) ? DR_RW_READ : DR_RW_WRITE;
158
159  if (len == 1)
160    len_bits = DR_LEN_1;
161  else if (len == 2)
162    {
163      if (addr % 2)
164	return i386_insert_nonaligned_watchpoint (pid, waddr, addr, len, rw);
165      len_bits = DR_LEN_2;
166    }
167
168  else if (len == 4)
169    {
170      if (addr % 4)
171	return i386_insert_nonaligned_watchpoint (pid, waddr, addr, len, rw);
172      len_bits = DR_LEN_4;
173    }
174  else
175    return i386_insert_nonaligned_watchpoint (pid, waddr, addr, len, rw);
176
177  free_debug_register = i;
178  register_number = free_debug_register - DR_FIRSTADDR;
179  debug_control_mirror |=
180    ((read_write_bits | len_bits)
181     << (DR_CONTROL_SHIFT + DR_CONTROL_SIZE * register_number));
182  debug_control_mirror |=
183    (1 << (DR_LOCAL_ENABLE_SHIFT + DR_ENABLE_SIZE * register_number));
184  debug_control_mirror |= DR_LOCAL_SLOWDOWN;
185  debug_control_mirror &= ~DR_CONTROL_RESERVED;
186
187  ptrace (6, pid, offsetof (struct user, u_debugreg[DR_CONTROL]),
188	  debug_control_mirror);
189  ptrace (6, pid, offsetof (struct user, u_debugreg[free_debug_register]),
190	  addr);
191
192  /* Record where we came from.  */
193  address_lookup[register_number] = addr;
194  return 0;
195}
196
197static int
198i386_insert_nonaligned_watchpoint (int pid, CORE_ADDR waddr, CORE_ADDR addr,
199				   int len, int rw)
200{
201  int align;
202  int size;
203  int rv;
204
205  static int size_try_array[4][4] =
206  {
207    { 1, 1, 1, 1 },		/* trying size one */
208    { 2, 1, 2, 1 },		/* trying size two */
209    { 2, 1, 2, 1 },		/* trying size three */
210    { 4, 1, 2, 1 }		/* trying size four */
211  };
212
213  rv = 0;
214  while (len > 0)
215    {
216      align = addr % 4;
217      /* Four is the maximum length for 386.  */
218      size = size_try_array[len > 4 ? 3 : len - 1][align];
219
220      rv = i386_insert_aligned_watchpoint (pid, waddr, addr, size, rw);
221      if (rv)
222	{
223	  i386_remove_watchpoint (pid, waddr, size);
224	  return rv;
225	}
226      addr += size;
227      len -= size;
228    }
229  return rv;
230}
231
232/* Remove a watchpoint.  */
233
234int
235i386_remove_watchpoint (int pid, CORE_ADDR addr, int len)
236{
237  int i;
238  int register_number;
239
240  for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
241    {
242      register_number = i - DR_FIRSTADDR;
243      if (address_lookup[register_number] == addr)
244	{
245	  debug_control_mirror &=
246	    ~(1 << (DR_LOCAL_ENABLE_SHIFT + DR_ENABLE_SIZE * register_number));
247	  address_lookup[register_number] = 0;
248	}
249    }
250  ptrace (6, pid, offsetof (struct user, u_debugreg[DR_CONTROL]),
251	  debug_control_mirror);
252  ptrace (6, pid, offsetof (struct user, u_debugreg[DR_STATUS]), 0);
253
254  return 0;
255}
256
257/* Check if stopped by a watchpoint.  */
258
259CORE_ADDR
260i386_stopped_by_watchpoint (int pid)
261{
262  int i;
263  int status;
264
265  status = ptrace (3, pid, offsetof (struct user, u_debugreg[DR_STATUS]), 0);
266  ptrace (6, pid, offsetof (struct user, u_debugreg[DR_STATUS]), 0);
267
268  for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
269    {
270      if (status & (1 << (i - DR_FIRSTADDR)))
271	return address_lookup[i - DR_FIRSTADDR];
272    }
273
274  return 0;
275}
276
277#endif /* TARGET_HAS_HARDWARE_WATCHPOINTS */
278