1130812Smarcel/* Intel 386 native support for System V systems (pre-SVR4).
2130812Smarcel
3130812Smarcel   Copyright 1988, 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1998,
4130812Smarcel   1999, 2000, 2002 Free Software Foundation, Inc.
5130812Smarcel
6130812Smarcel   This file is part of GDB.
7130812Smarcel
8130812Smarcel   This program is free software; you can redistribute it and/or modify
9130812Smarcel   it under the terms of the GNU General Public License as published by
10130812Smarcel   the Free Software Foundation; either version 2 of the License, or
11130812Smarcel   (at your option) any later version.
12130812Smarcel
13130812Smarcel   This program is distributed in the hope that it will be useful,
14130812Smarcel   but WITHOUT ANY WARRANTY; without even the implied warranty of
15130812Smarcel   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16130812Smarcel   GNU General Public License for more details.
17130812Smarcel
18130812Smarcel   You should have received a copy of the GNU General Public License
19130812Smarcel   along with this program; if not, write to the Free Software
20130812Smarcel   Foundation, Inc., 59 Temple Place - Suite 330,
21130812Smarcel   Boston, MA 02111-1307, USA.  */
22130812Smarcel
23130812Smarcel#include "defs.h"
24130812Smarcel
25130812Smarcel#ifdef HAVE_PTRACE_H
26130812Smarcel#include <ptrace.h>
27130812Smarcel#else
28130812Smarcel#ifdef HAVE_SYS_PTRACE_H
29130812Smarcel#include <sys/ptrace.h>
30130812Smarcel#endif
31130812Smarcel#endif
32130812Smarcel
33130812Smarcel#include "frame.h"
34130812Smarcel#include "inferior.h"
35130812Smarcel#include "language.h"
36130812Smarcel#include "gdbcore.h"
37130812Smarcel
38130812Smarcel#ifdef USG
39130812Smarcel#include <sys/types.h>
40130812Smarcel#endif
41130812Smarcel
42130812Smarcel#include <sys/param.h>
43130812Smarcel#include <sys/dir.h>
44130812Smarcel#include <signal.h>
45130812Smarcel#include <sys/user.h>
46130812Smarcel#include <sys/ioctl.h>
47130812Smarcel#include <fcntl.h>
48130812Smarcel
49130812Smarcel#ifdef TARGET_HAS_HARDWARE_WATCHPOINTS
50130812Smarcel#include <sys/debugreg.h>
51130812Smarcel#endif
52130812Smarcel
53130812Smarcel#include <sys/file.h>
54130812Smarcel#include "gdb_stat.h"
55130812Smarcel
56130812Smarcel#ifdef HAVE_SYS_REG_H
57130812Smarcel#include <sys/reg.h>
58130812Smarcel#endif
59130812Smarcel
60130812Smarcel#include "floatformat.h"
61130812Smarcel
62130812Smarcel#include "target.h"
63130812Smarcel
64130812Smarcel#include "i386-tdep.h"
65130812Smarcel
66130812Smarcel
67130812Smarcel/* Mapping between the general-purpose registers in `struct user'
68130812Smarcel   format and GDB's register array layout.  */
69130812Smarcelstatic int regmap[] =
70130812Smarcel{
71130812Smarcel  EAX, ECX, EDX, EBX,
72130812Smarcel  UESP, EBP, ESI, EDI,
73130812Smarcel  EIP, EFL, CS, SS,
74130812Smarcel  DS, ES, FS, GS,
75130812Smarcel};
76130812Smarcel
77130812Smarcel/* Support for the user struct.  */
78130812Smarcel
79130812Smarcel/* Return the address of register REGNUM.  BLOCKEND is the value of
80130812Smarcel   u.u_ar0, and points to the place where GS is stored.  */
81130812Smarcel
82130812SmarcelCORE_ADDR
83130812Smarcelregister_u_addr (CORE_ADDR blockend, int regnum)
84130812Smarcel{
85130812Smarcel  struct user u;
86130812Smarcel  CORE_ADDR fpstate;
87130812Smarcel
88130812Smarcel  if (i386_fp_regnum_p (regnum))
89130812Smarcel    {
90130812Smarcel#ifdef KSTKSZ			/* SCO, and others?  */
91130812Smarcel      blockend += 4 * (SS + 1) - KSTKSZ;
92130812Smarcel      fpstate = blockend + ((char *) &u.u_fps.u_fpstate - (char *) &u);
93130812Smarcel      return (fpstate + 0x1c + 10 * (regnum - FP0_REGNUM));
94130812Smarcel#else
95130812Smarcel      fpstate = blockend + ((char *) &u.i387.st_space - (char *) &u);
96130812Smarcel      return (fpstate + 10 * (regnum - FP0_REGNUM));
97130812Smarcel#endif
98130812Smarcel    }
99130812Smarcel
100130812Smarcel  return (blockend + 4 * regmap[regnum]);
101130812Smarcel}
102130812Smarcel
103130812Smarcel/* Return the size of the user struct.  */
104130812Smarcel
105130812Smarcelint
106130812Smarcelkernel_u_size (void)
107130812Smarcel{
108130812Smarcel  return (sizeof (struct user));
109130812Smarcel}
110130812Smarcel
111130812Smarcel#ifdef TARGET_HAS_HARDWARE_WATCHPOINTS
112130812Smarcel
113130812Smarcel#if !defined (offsetof)
114130812Smarcel#define offsetof(TYPE, MEMBER) ((unsigned long) &((TYPE *)0)->MEMBER)
115130812Smarcel#endif
116130812Smarcel
117130812Smarcel/* Record the value of the debug control register.  */
118130812Smarcelstatic int debug_control_mirror;
119130812Smarcel
120130812Smarcel/* Record which address associates with which register.  */
121130812Smarcelstatic CORE_ADDR address_lookup[DR_LASTADDR - DR_FIRSTADDR + 1];
122130812Smarcel
123130812Smarcelstatic int i386_insert_aligned_watchpoint (int, CORE_ADDR, CORE_ADDR, int,
124130812Smarcel					   int);
125130812Smarcel
126130812Smarcelstatic int i386_insert_nonaligned_watchpoint (int, CORE_ADDR, CORE_ADDR, int,
127130812Smarcel					      int);
128130812Smarcel
129130812Smarcel/* Insert a watchpoint.  */
130130812Smarcel
131130812Smarcelint
132130812Smarceli386_insert_watchpoint (int pid, CORE_ADDR addr, int len, int rw)
133130812Smarcel{
134130812Smarcel  return i386_insert_aligned_watchpoint (pid, addr, addr, len, rw);
135130812Smarcel}
136130812Smarcel
137130812Smarcelstatic int
138130812Smarceli386_insert_aligned_watchpoint (int pid, CORE_ADDR waddr, CORE_ADDR addr,
139130812Smarcel				int len, int rw)
140130812Smarcel{
141130812Smarcel  int i;
142130812Smarcel  int read_write_bits, len_bits;
143130812Smarcel  int free_debug_register;
144130812Smarcel  int register_number;
145130812Smarcel
146130812Smarcel  /* Look for a free debug register.  */
147130812Smarcel  for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
148130812Smarcel    {
149130812Smarcel      if (address_lookup[i - DR_FIRSTADDR] == 0)
150130812Smarcel	break;
151130812Smarcel    }
152130812Smarcel
153130812Smarcel  /* No more debug registers!  */
154130812Smarcel  if (i > DR_LASTADDR)
155130812Smarcel    return -1;
156130812Smarcel
157130812Smarcel  read_write_bits = (rw & 1) ? DR_RW_READ : DR_RW_WRITE;
158130812Smarcel
159130812Smarcel  if (len == 1)
160130812Smarcel    len_bits = DR_LEN_1;
161130812Smarcel  else if (len == 2)
162130812Smarcel    {
163130812Smarcel      if (addr % 2)
164130812Smarcel	return i386_insert_nonaligned_watchpoint (pid, waddr, addr, len, rw);
165130812Smarcel      len_bits = DR_LEN_2;
166130812Smarcel    }
167130812Smarcel
168130812Smarcel  else if (len == 4)
169130812Smarcel    {
170130812Smarcel      if (addr % 4)
171130812Smarcel	return i386_insert_nonaligned_watchpoint (pid, waddr, addr, len, rw);
172130812Smarcel      len_bits = DR_LEN_4;
173130812Smarcel    }
174130812Smarcel  else
175130812Smarcel    return i386_insert_nonaligned_watchpoint (pid, waddr, addr, len, rw);
176130812Smarcel
177130812Smarcel  free_debug_register = i;
178130812Smarcel  register_number = free_debug_register - DR_FIRSTADDR;
179130812Smarcel  debug_control_mirror |=
180130812Smarcel    ((read_write_bits | len_bits)
181130812Smarcel     << (DR_CONTROL_SHIFT + DR_CONTROL_SIZE * register_number));
182130812Smarcel  debug_control_mirror |=
183130812Smarcel    (1 << (DR_LOCAL_ENABLE_SHIFT + DR_ENABLE_SIZE * register_number));
184130812Smarcel  debug_control_mirror |= DR_LOCAL_SLOWDOWN;
185130812Smarcel  debug_control_mirror &= ~DR_CONTROL_RESERVED;
186130812Smarcel
187130812Smarcel  ptrace (6, pid, offsetof (struct user, u_debugreg[DR_CONTROL]),
188130812Smarcel	  debug_control_mirror);
189130812Smarcel  ptrace (6, pid, offsetof (struct user, u_debugreg[free_debug_register]),
190130812Smarcel	  addr);
191130812Smarcel
192130812Smarcel  /* Record where we came from.  */
193130812Smarcel  address_lookup[register_number] = addr;
194130812Smarcel  return 0;
195130812Smarcel}
196130812Smarcel
197130812Smarcelstatic int
198130812Smarceli386_insert_nonaligned_watchpoint (int pid, CORE_ADDR waddr, CORE_ADDR addr,
199130812Smarcel				   int len, int rw)
200130812Smarcel{
201130812Smarcel  int align;
202130812Smarcel  int size;
203130812Smarcel  int rv;
204130812Smarcel
205130812Smarcel  static int size_try_array[4][4] =
206130812Smarcel  {
207130812Smarcel    { 1, 1, 1, 1 },		/* trying size one */
208130812Smarcel    { 2, 1, 2, 1 },		/* trying size two */
209130812Smarcel    { 2, 1, 2, 1 },		/* trying size three */
210130812Smarcel    { 4, 1, 2, 1 }		/* trying size four */
211130812Smarcel  };
212130812Smarcel
213130812Smarcel  rv = 0;
214130812Smarcel  while (len > 0)
215130812Smarcel    {
216130812Smarcel      align = addr % 4;
217130812Smarcel      /* Four is the maximum length for 386.  */
218130812Smarcel      size = size_try_array[len > 4 ? 3 : len - 1][align];
219130812Smarcel
220130812Smarcel      rv = i386_insert_aligned_watchpoint (pid, waddr, addr, size, rw);
221130812Smarcel      if (rv)
222130812Smarcel	{
223130812Smarcel	  i386_remove_watchpoint (pid, waddr, size);
224130812Smarcel	  return rv;
225130812Smarcel	}
226130812Smarcel      addr += size;
227130812Smarcel      len -= size;
228130812Smarcel    }
229130812Smarcel  return rv;
230130812Smarcel}
231130812Smarcel
232130812Smarcel/* Remove a watchpoint.  */
233130812Smarcel
234130812Smarcelint
235130812Smarceli386_remove_watchpoint (int pid, CORE_ADDR addr, int len)
236130812Smarcel{
237130812Smarcel  int i;
238130812Smarcel  int register_number;
239130812Smarcel
240130812Smarcel  for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
241130812Smarcel    {
242130812Smarcel      register_number = i - DR_FIRSTADDR;
243130812Smarcel      if (address_lookup[register_number] == addr)
244130812Smarcel	{
245130812Smarcel	  debug_control_mirror &=
246130812Smarcel	    ~(1 << (DR_LOCAL_ENABLE_SHIFT + DR_ENABLE_SIZE * register_number));
247130812Smarcel	  address_lookup[register_number] = 0;
248130812Smarcel	}
249130812Smarcel    }
250130812Smarcel  ptrace (6, pid, offsetof (struct user, u_debugreg[DR_CONTROL]),
251130812Smarcel	  debug_control_mirror);
252130812Smarcel  ptrace (6, pid, offsetof (struct user, u_debugreg[DR_STATUS]), 0);
253130812Smarcel
254130812Smarcel  return 0;
255130812Smarcel}
256130812Smarcel
257130812Smarcel/* Check if stopped by a watchpoint.  */
258130812Smarcel
259130812SmarcelCORE_ADDR
260130812Smarceli386_stopped_by_watchpoint (int pid)
261130812Smarcel{
262130812Smarcel  int i;
263130812Smarcel  int status;
264130812Smarcel
265130812Smarcel  status = ptrace (3, pid, offsetof (struct user, u_debugreg[DR_STATUS]), 0);
266130812Smarcel  ptrace (6, pid, offsetof (struct user, u_debugreg[DR_STATUS]), 0);
267130812Smarcel
268130812Smarcel  for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
269130812Smarcel    {
270130812Smarcel      if (status & (1 << (i - DR_FIRSTADDR)))
271130812Smarcel	return address_lookup[i - DR_FIRSTADDR];
272130812Smarcel    }
273130812Smarcel
274130812Smarcel  return 0;
275130812Smarcel}
276130812Smarcel
277130812Smarcel#endif /* TARGET_HAS_HARDWARE_WATCHPOINTS */
278