This commit was generated by cvs2svn to track changes on a CVS vendor
[platform/upstream/binutils.git] / gdb / i386v-nat.c
1 /* Intel 386 native support for SYSV systems (pre-SVR4).
2    Copyright (C) 1988, 1989, 1991, 1992, 1994, 1996 Free Software Foundation, Inc.
3
4 This file is part of GDB.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
19
20 #include "defs.h"
21 #include "frame.h"
22 #include "inferior.h"
23 #include "language.h"
24 #include "gdbcore.h"
25
26 #ifdef USG
27 #include <sys/types.h>
28 #endif
29
30 #include <sys/param.h>
31 #include <sys/dir.h>
32 #include <signal.h>
33 #include <sys/user.h>
34 #include <sys/ioctl.h>
35 #include <fcntl.h>
36
37 #ifdef TARGET_HAS_HARDWARE_WATCHPOINTS
38 #include <sys/debugreg.h>
39 #endif
40
41 #include <sys/file.h>
42 #include "gdb_stat.h"
43
44 #ifndef NO_SYS_REG_H
45 #include <sys/reg.h>
46 #endif
47
48 #include "floatformat.h"
49
50 #include "target.h"
51
52 \f
53 /* this table must line up with REGISTER_NAMES in tm-i386v.h */
54 /* symbols like 'EAX' come from <sys/reg.h> */
55 static int regmap[] = 
56 {
57   EAX, ECX, EDX, EBX,
58   UESP, EBP, ESI, EDI,
59   EIP, EFL, CS, SS,
60   DS, ES, FS, GS,
61 };
62
63 /* blockend is the value of u.u_ar0, and points to the
64  * place where GS is stored
65  */
66
67 int
68 i386_register_u_addr (blockend, regnum)
69      int blockend;
70      int regnum;
71 {
72   struct user u;
73   int fpstate;
74   int ubase;
75
76   ubase = blockend;
77   /* FIXME:  Should have better way to test floating point range */
78   if (regnum >= FP0_REGNUM && regnum <= (FP0_REGNUM + 7)) 
79     {
80 #ifdef KSTKSZ   /* SCO, and others? */
81       ubase += 4 * (SS + 1) - KSTKSZ;
82       fpstate = ubase + ((char *)&u.u_fps.u_fpstate - (char *)&u);
83       return (fpstate + 0x1c + 10 * (regnum - FP0_REGNUM));
84 #else
85       fpstate = ubase + ((char *)&u.i387.st_space - (char *)&u);
86       return (fpstate + 10 * (regnum - FP0_REGNUM));
87 #endif
88     } 
89   else
90     {
91       return (ubase + 4 * regmap[regnum]);
92     }
93   
94 }
95 \f
96 int
97 kernel_u_size ()
98 {
99   return (sizeof (struct user));
100 }
101 \f
102 #ifdef TARGET_HAS_HARDWARE_WATCHPOINTS
103
104 #if !defined (offsetof)
105 #define offsetof(TYPE, MEMBER) ((unsigned long) &((TYPE *)0)->MEMBER)
106 #endif
107
108 /* Record the value of the debug control register.  */
109 static int debug_control_mirror;
110
111 /* Record which address associates with which register.  */
112 static CORE_ADDR address_lookup[DR_LASTADDR - DR_FIRSTADDR + 1];
113
114 static int
115 i386_insert_aligned_watchpoint PARAMS ((int, CORE_ADDR, CORE_ADDR, int,
116                                            int));
117
118 static int
119 i386_insert_nonaligned_watchpoint PARAMS ((int, CORE_ADDR, CORE_ADDR, int,
120                                            int));
121
122 /* Insert a watchpoint.  */
123
124 int
125 i386_insert_watchpoint (pid, addr, len, rw)
126      int pid;
127      CORE_ADDR addr;
128      int len;
129      int rw;
130 {
131   return i386_insert_aligned_watchpoint (pid, addr, addr, len, rw);
132 }
133
134 static int
135 i386_insert_aligned_watchpoint (pid, waddr, addr, len, rw)
136      int pid;
137      CORE_ADDR waddr;
138      CORE_ADDR addr;
139      int len;
140      int rw;
141 {
142   int i;
143   int read_write_bits, len_bits;
144   int free_debug_register;
145   int register_number;
146   
147   /* Look for a free debug register.  */
148   for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
149     {
150       if (address_lookup[i - DR_FIRSTADDR] == 0)
151         break;
152     }
153
154   /* No more debug registers!  */
155   if (i > DR_LASTADDR)
156     return -1;
157
158   read_write_bits = ((rw & 1) ? DR_RW_READ : 0) | ((rw & 2) ? DR_RW_WRITE : 0);
159
160   if (len == 1)
161     len_bits = DR_LEN_1;
162   else if (len == 2)
163     {
164       if (addr % 2)
165         return i386_insert_nonaligned_watchpoint (pid, waddr, addr, len, rw);
166       len_bits = DR_LEN_2;
167     }
168
169   else if (len == 4)
170     {
171       if (addr % 4)
172         return i386_insert_nonaligned_watchpoint (pid, waddr, addr, len, rw);
173       len_bits = DR_LEN_4;
174     }
175   else
176     return i386_insert_nonaligned_watchpoint (pid, waddr, addr, len, rw);
177   
178   free_debug_register = i;
179   register_number = free_debug_register - DR_FIRSTADDR;
180   debug_control_mirror |=
181     ((read_write_bits | len_bits)
182      << (DR_CONTROL_SHIFT + DR_CONTROL_SIZE * register_number));
183   debug_control_mirror |=
184     (1 << (DR_LOCAL_ENABLE_SHIFT + DR_ENABLE_SIZE * register_number));
185   debug_control_mirror |= DR_LOCAL_SLOWDOWN;
186   debug_control_mirror &= ~DR_CONTROL_RESERVED;
187   
188   ptrace (6, pid, offsetof (struct user, u_debugreg[DR_CONTROL]),
189           debug_control_mirror);
190   ptrace (6, pid, offsetof (struct user, u_debugreg[free_debug_register]),
191           addr);
192
193   /* Record where we came from.  */
194   address_lookup[register_number] = addr;
195   return 0;
196 }
197
198 static int
199 i386_insert_nonaligned_watchpoint (pid, waddr, addr, len, rw)
200      int pid;
201      CORE_ADDR waddr;
202      CORE_ADDR addr;
203      int len;
204      int rw;
205 {
206   int align;
207   int size;
208   int rv;
209
210   static int size_try_array[16] = {
211     1, 1, 1, 1,                 /* trying size one */
212     2, 1, 2, 1,                 /* trying size two */
213     2, 1, 2, 1,                 /* trying size three */
214     4, 1, 2, 1                  /* trying size four */
215   };
216
217   rv = 0;
218   while (len > 0)
219     {
220       align = addr % 4;
221       /* Four is the maximum length for 386.  */
222       size = (len > 4) ? 3 : len - 1;
223       size = size_try_array[size * 4 + align];
224
225       rv = i386_insert_aligned_watchpoint (pid, waddr, addr, size, rw);
226       if (rv)
227         {
228           i386_remove_watchpoint (pid, waddr, size);
229           return rv;
230         }
231       addr += size;
232       len -= size;
233     }
234   return rv;
235 }
236
237 /* Remove a watchpoint.  */
238
239 int
240 i386_remove_watchpoint (pid, addr, len)
241      int pid;
242      CORE_ADDR addr;
243      int len;
244 {
245   int i;
246   int register_number;
247
248   for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
249     {
250       register_number = i - DR_FIRSTADDR;
251       if (address_lookup[register_number] == addr)
252         {
253           debug_control_mirror &=
254             ~(1 << (DR_LOCAL_ENABLE_SHIFT + DR_ENABLE_SIZE * register_number));
255           address_lookup[register_number] = 0;
256         }
257     }
258   ptrace (6, pid, offsetof (struct user, u_debugreg[DR_CONTROL]),
259           debug_control_mirror);
260   ptrace (6, pid, offsetof (struct user, u_debugreg[DR_STATUS]), 0);
261
262   return 0;
263 }
264
265 /* Check if stopped by a watchpoint.  */
266
267 CORE_ADDR
268 i386_stopped_by_watchpoint (pid)
269     int pid;
270 {
271   int i;
272   int status;
273
274   status = ptrace (3, pid, offsetof (struct user, u_debugreg[DR_STATUS]), 0);
275   ptrace (6, pid, offsetof (struct user, u_debugreg[DR_STATUS]), 0);
276
277   for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++)
278     {
279       if (status & (1 << (i - DR_FIRSTADDR)))
280         return address_lookup[i - DR_FIRSTADDR];
281     }
282
283   return 0;
284 }
285
286 #endif /* TARGET_HAS_HARDWARE_WATCHPOINTS */
287
288 #if 0
289 /* using FLOAT_INFO as is would be a problem.  FLOAT_INFO is called
290    via a command xxx and eventually calls ptrace without ever having
291    traversed the target vector.  This would be terribly impolite
292    behaviour for a sun4 hosted remote gdb.
293
294    A fix might be to move this code into the "info registers" command.
295    rich@cygnus.com 15 Sept 92. */
296 i386_float_info ()
297 {
298   struct user u; /* just for address computations */
299   int i;
300   /* fpstate defined in <sys/user.h> */
301   struct fpstate *fpstatep;
302   char buf[sizeof (struct fpstate) + 2 * sizeof (int)];
303   unsigned int uaddr;
304   char fpvalid = 0;
305   unsigned int rounded_addr;
306   unsigned int rounded_size;
307   extern int corechan;
308   int skip;
309   
310   uaddr = (char *)&u.u_fpvalid - (char *)&u;
311   if (target_has_execution)
312     {
313       unsigned int data;
314       unsigned int mask;
315       
316       rounded_addr = uaddr & -sizeof (int);
317       data = ptrace (3, inferior_pid, (PTRACE_ARG3_TYPE) rounded_addr, 0);
318       mask = 0xff << ((uaddr - rounded_addr) * 8);
319       
320       fpvalid = ((data & mask) != 0);
321     } 
322 #if 0
323   else 
324     {
325       if (lseek (corechan, uaddr, 0) < 0)
326         perror ("seek on core file");
327       if (myread (corechan, &fpvalid, 1) < 0) 
328         perror ("read on core file");
329       
330     }
331 #endif  /* no core support yet */
332   
333   if (fpvalid == 0) 
334     {
335       printf_unfiltered ("no floating point status saved\n");
336       return;
337     }
338   
339   uaddr = (char *)&U_FPSTATE(u) - (char *)&u;
340   if (target_has_execution)
341     {
342       int *ip;
343       
344       rounded_addr = uaddr & -sizeof (int);
345       rounded_size = (((uaddr + sizeof (struct fpstate)) - uaddr) +
346                       sizeof (int) - 1) / sizeof (int);
347       skip = uaddr - rounded_addr;
348       
349       ip = (int *)buf;
350       for (i = 0; i < rounded_size; i++) 
351         {
352           *ip++ = ptrace (3, inferior_pid, (PTRACE_ARG3_TYPE) rounded_addr, 0);
353           rounded_addr += sizeof (int);
354         }
355     } 
356 #if 0
357   else 
358     {
359       if (lseek (corechan, uaddr, 0) < 0)
360         perror_with_name ("seek on core file");
361       if (myread (corechan, buf, sizeof (struct fpstate)) < 0) 
362         perror_with_name ("read from core file");
363       skip = 0;
364     }
365 #endif  /* 0 */ 
366
367   fpstatep = (struct fpstate *)(buf + skip);
368   print_387_status (fpstatep->status, (struct env387 *)fpstatep->state);
369 }
370
371 #endif /* never */