xref: /openbsd-src/gnu/usr.bin/binutils/gdb/go32-nat.c (revision 11efff7f3ac2b3cfeff0c0cddc14294d9b3aca4f)
1b725ae77Skettenis /* Native debugging support for Intel x86 running DJGPP.
2b725ae77Skettenis    Copyright 1997, 1999, 2000, 2001 Free Software Foundation, Inc.
3b725ae77Skettenis    Written by Robert Hoehne.
4b725ae77Skettenis 
5b725ae77Skettenis    This file is part of GDB.
6b725ae77Skettenis 
7b725ae77Skettenis    This program is free software; you can redistribute it and/or modify
8b725ae77Skettenis    it under the terms of the GNU General Public License as published by
9b725ae77Skettenis    the Free Software Foundation; either version 2 of the License, or
10b725ae77Skettenis    (at your option) any later version.
11b725ae77Skettenis 
12b725ae77Skettenis    This program is distributed in the hope that it will be useful,
13b725ae77Skettenis    but WITHOUT ANY WARRANTY; without even the implied warranty of
14b725ae77Skettenis    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15b725ae77Skettenis    GNU General Public License for more details.
16b725ae77Skettenis 
17b725ae77Skettenis    You should have received a copy of the GNU General Public License
18b725ae77Skettenis    along with this program; if not, write to the Free Software
19b725ae77Skettenis    Foundation, Inc., 59 Temple Place - Suite 330,
20b725ae77Skettenis    Boston, MA 02111-1307, USA.  */
21b725ae77Skettenis 
22b725ae77Skettenis #include <fcntl.h>
23b725ae77Skettenis 
24b725ae77Skettenis #include "defs.h"
25b725ae77Skettenis #include "inferior.h"
26b725ae77Skettenis #include "gdb_wait.h"
27b725ae77Skettenis #include "gdbcore.h"
28b725ae77Skettenis #include "command.h"
29b725ae77Skettenis #include "gdbcmd.h"
30b725ae77Skettenis #include "floatformat.h"
31b725ae77Skettenis #include "buildsym.h"
32b725ae77Skettenis #include "i387-tdep.h"
33b725ae77Skettenis #include "i386-tdep.h"
34b725ae77Skettenis #include "value.h"
35b725ae77Skettenis #include "regcache.h"
36b725ae77Skettenis #include "gdb_string.h"
37b725ae77Skettenis 
38b725ae77Skettenis #include <stdio.h>		/* might be required for __DJGPP_MINOR__ */
39b725ae77Skettenis #include <stdlib.h>
40b725ae77Skettenis #include <ctype.h>
41b725ae77Skettenis #include <errno.h>
42b725ae77Skettenis #include <unistd.h>
43b725ae77Skettenis #include <sys/utsname.h>
44b725ae77Skettenis #include <io.h>
45b725ae77Skettenis #include <dos.h>
46b725ae77Skettenis #include <dpmi.h>
47b725ae77Skettenis #include <go32.h>
48b725ae77Skettenis #include <sys/farptr.h>
49b725ae77Skettenis #include <debug/v2load.h>
50b725ae77Skettenis #include <debug/dbgcom.h>
51b725ae77Skettenis #if __DJGPP_MINOR__ > 2
52b725ae77Skettenis #include <debug/redir.h>
53b725ae77Skettenis #endif
54b725ae77Skettenis 
55b725ae77Skettenis #if __DJGPP_MINOR__ < 3
56b725ae77Skettenis /* This code will be provided from DJGPP 2.03 on. Until then I code it
57b725ae77Skettenis    here */
58b725ae77Skettenis typedef struct
59b725ae77Skettenis   {
60b725ae77Skettenis     unsigned short sig0;
61b725ae77Skettenis     unsigned short sig1;
62b725ae77Skettenis     unsigned short sig2;
63b725ae77Skettenis     unsigned short sig3;
64b725ae77Skettenis     unsigned short exponent:15;
65b725ae77Skettenis     unsigned short sign:1;
66b725ae77Skettenis   }
67b725ae77Skettenis NPXREG;
68b725ae77Skettenis 
69b725ae77Skettenis typedef struct
70b725ae77Skettenis   {
71b725ae77Skettenis     unsigned int control;
72b725ae77Skettenis     unsigned int status;
73b725ae77Skettenis     unsigned int tag;
74b725ae77Skettenis     unsigned int eip;
75b725ae77Skettenis     unsigned int cs;
76b725ae77Skettenis     unsigned int dataptr;
77b725ae77Skettenis     unsigned int datasel;
78b725ae77Skettenis     NPXREG reg[8];
79b725ae77Skettenis   }
80b725ae77Skettenis NPX;
81b725ae77Skettenis 
82b725ae77Skettenis static NPX npx;
83b725ae77Skettenis 
84b725ae77Skettenis static void save_npx (void);	/* Save the FPU of the debugged program */
85b725ae77Skettenis static void load_npx (void);	/* Restore the FPU of the debugged program */
86b725ae77Skettenis 
87b725ae77Skettenis /* ------------------------------------------------------------------------- */
88b725ae77Skettenis /* Store the contents of the NPX in the global variable `npx'.  */
89b725ae77Skettenis /* *INDENT-OFF* */
90b725ae77Skettenis 
91b725ae77Skettenis static void
save_npx(void)92b725ae77Skettenis save_npx (void)
93b725ae77Skettenis {
94b725ae77Skettenis   asm ("inb    $0xa0, %%al  \n\
95b725ae77Skettenis        testb $0x20, %%al    \n\
96b725ae77Skettenis        jz 1f 	    	    \n\
97b725ae77Skettenis        xorb %%al, %%al	    \n\
98b725ae77Skettenis        outb %%al, $0xf0     \n\
99b725ae77Skettenis        movb $0x20, %%al	    \n\
100b725ae77Skettenis        outb %%al, $0xa0     \n\
101b725ae77Skettenis        outb %%al, $0x20     \n\
102b725ae77Skettenis 1:     	       	   	    \n\
103b725ae77Skettenis        fnsave %0	    \n\
104b725ae77Skettenis        fwait "
105b725ae77Skettenis :     "=m" (npx)
106b725ae77Skettenis :				/* No input */
107b725ae77Skettenis :     "%eax");
108b725ae77Skettenis }
109b725ae77Skettenis 
110b725ae77Skettenis /* *INDENT-ON* */
111b725ae77Skettenis 
112b725ae77Skettenis 
113b725ae77Skettenis /* ------------------------------------------------------------------------- */
114b725ae77Skettenis /* Reload the contents of the NPX from the global variable `npx'.  */
115b725ae77Skettenis 
116b725ae77Skettenis static void
load_npx(void)117b725ae77Skettenis load_npx (void)
118b725ae77Skettenis {
119b725ae77Skettenis   asm ("frstor %0":"=m" (npx));
120b725ae77Skettenis }
121b725ae77Skettenis /* ------------------------------------------------------------------------- */
122b725ae77Skettenis /* Stubs for the missing redirection functions.  */
123b725ae77Skettenis typedef struct {
124b725ae77Skettenis   char *command;
125b725ae77Skettenis   int redirected;
126b725ae77Skettenis } cmdline_t;
127b725ae77Skettenis 
128b725ae77Skettenis void
redir_cmdline_delete(cmdline_t * ptr)129b725ae77Skettenis redir_cmdline_delete (cmdline_t *ptr)
130b725ae77Skettenis {
131b725ae77Skettenis   ptr->redirected = 0;
132b725ae77Skettenis }
133b725ae77Skettenis 
134b725ae77Skettenis int
redir_cmdline_parse(const char * args,cmdline_t * ptr)135b725ae77Skettenis redir_cmdline_parse (const char *args, cmdline_t *ptr)
136b725ae77Skettenis {
137b725ae77Skettenis   return -1;
138b725ae77Skettenis }
139b725ae77Skettenis 
140b725ae77Skettenis int
redir_to_child(cmdline_t * ptr)141b725ae77Skettenis redir_to_child (cmdline_t *ptr)
142b725ae77Skettenis {
143b725ae77Skettenis   return 1;
144b725ae77Skettenis }
145b725ae77Skettenis 
146b725ae77Skettenis int
redir_to_debugger(cmdline_t * ptr)147b725ae77Skettenis redir_to_debugger (cmdline_t *ptr)
148b725ae77Skettenis {
149b725ae77Skettenis   return 1;
150b725ae77Skettenis }
151b725ae77Skettenis 
152b725ae77Skettenis int
redir_debug_init(cmdline_t * ptr)153b725ae77Skettenis redir_debug_init (cmdline_t *ptr)
154b725ae77Skettenis {
155b725ae77Skettenis   return 0;
156b725ae77Skettenis }
157b725ae77Skettenis #endif /* __DJGPP_MINOR < 3 */
158b725ae77Skettenis 
159b725ae77Skettenis typedef enum { wp_insert, wp_remove, wp_count } wp_op;
160b725ae77Skettenis 
161b725ae77Skettenis /* This holds the current reference counts for each debug register.  */
162b725ae77Skettenis static int dr_ref_count[4];
163b725ae77Skettenis 
164b725ae77Skettenis #define SOME_PID 42
165b725ae77Skettenis 
166b725ae77Skettenis static int prog_has_started = 0;
167b725ae77Skettenis static void go32_open (char *name, int from_tty);
168b725ae77Skettenis static void go32_close (int quitting);
169b725ae77Skettenis static void go32_attach (char *args, int from_tty);
170b725ae77Skettenis static void go32_detach (char *args, int from_tty);
171b725ae77Skettenis static void go32_resume (ptid_t ptid, int step,
172b725ae77Skettenis                          enum target_signal siggnal);
173b725ae77Skettenis static ptid_t go32_wait (ptid_t ptid,
174b725ae77Skettenis                                struct target_waitstatus *status);
175b725ae77Skettenis static void go32_fetch_registers (int regno);
176b725ae77Skettenis static void store_register (int regno);
177b725ae77Skettenis static void go32_store_registers (int regno);
178b725ae77Skettenis static void go32_prepare_to_store (void);
179b725ae77Skettenis static int go32_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len,
180b725ae77Skettenis 			     int write,
181b725ae77Skettenis 			     struct mem_attrib *attrib,
182b725ae77Skettenis 			     struct target_ops *target);
183b725ae77Skettenis static void go32_files_info (struct target_ops *target);
184b725ae77Skettenis static void go32_stop (void);
185b725ae77Skettenis static void go32_kill_inferior (void);
186*11efff7fSkettenis static void go32_create_inferior (char *exec_file, char *args, char **env, int from_tty);
187b725ae77Skettenis static void go32_mourn_inferior (void);
188b725ae77Skettenis static int go32_can_run (void);
189b725ae77Skettenis 
190b725ae77Skettenis static struct target_ops go32_ops;
191b725ae77Skettenis static void go32_terminal_init (void);
192b725ae77Skettenis static void go32_terminal_inferior (void);
193b725ae77Skettenis static void go32_terminal_ours (void);
194b725ae77Skettenis 
195b725ae77Skettenis #define r_ofs(x) (offsetof(TSS,x))
196b725ae77Skettenis 
197b725ae77Skettenis static struct
198b725ae77Skettenis {
199b725ae77Skettenis   size_t tss_ofs;
200b725ae77Skettenis   size_t size;
201b725ae77Skettenis }
202b725ae77Skettenis regno_mapping[] =
203b725ae77Skettenis {
204b725ae77Skettenis   {r_ofs (tss_eax), 4},	/* normal registers, from a_tss */
205b725ae77Skettenis   {r_ofs (tss_ecx), 4},
206b725ae77Skettenis   {r_ofs (tss_edx), 4},
207b725ae77Skettenis   {r_ofs (tss_ebx), 4},
208b725ae77Skettenis   {r_ofs (tss_esp), 4},
209b725ae77Skettenis   {r_ofs (tss_ebp), 4},
210b725ae77Skettenis   {r_ofs (tss_esi), 4},
211b725ae77Skettenis   {r_ofs (tss_edi), 4},
212b725ae77Skettenis   {r_ofs (tss_eip), 4},
213b725ae77Skettenis   {r_ofs (tss_eflags), 4},
214b725ae77Skettenis   {r_ofs (tss_cs), 2},
215b725ae77Skettenis   {r_ofs (tss_ss), 2},
216b725ae77Skettenis   {r_ofs (tss_ds), 2},
217b725ae77Skettenis   {r_ofs (tss_es), 2},
218b725ae77Skettenis   {r_ofs (tss_fs), 2},
219b725ae77Skettenis   {r_ofs (tss_gs), 2},
220b725ae77Skettenis   {0, 10},		/* 8 FP registers, from npx.reg[] */
221b725ae77Skettenis   {1, 10},
222b725ae77Skettenis   {2, 10},
223b725ae77Skettenis   {3, 10},
224b725ae77Skettenis   {4, 10},
225b725ae77Skettenis   {5, 10},
226b725ae77Skettenis   {6, 10},
227b725ae77Skettenis   {7, 10},
228b725ae77Skettenis 	/* The order of the next 7 registers must be consistent
229b725ae77Skettenis 	   with their numbering in config/i386/tm-i386.h, which see.  */
230b725ae77Skettenis   {0, 2},		/* control word, from npx */
231b725ae77Skettenis   {4, 2},		/* status word, from npx */
232b725ae77Skettenis   {8, 2},		/* tag word, from npx */
233b725ae77Skettenis   {16, 2},		/* last FP exception CS from npx */
234b725ae77Skettenis   {12, 4},		/* last FP exception EIP from npx */
235b725ae77Skettenis   {24, 2},		/* last FP exception operand selector from npx */
236b725ae77Skettenis   {20, 4},		/* last FP exception operand offset from npx */
237b725ae77Skettenis   {18, 2}		/* last FP opcode from npx */
238b725ae77Skettenis };
239b725ae77Skettenis 
240b725ae77Skettenis static struct
241b725ae77Skettenis   {
242b725ae77Skettenis     int go32_sig;
243b725ae77Skettenis     enum target_signal gdb_sig;
244b725ae77Skettenis   }
245b725ae77Skettenis sig_map[] =
246b725ae77Skettenis {
247b725ae77Skettenis   {0, TARGET_SIGNAL_FPE},
248b725ae77Skettenis   {1, TARGET_SIGNAL_TRAP},
249b725ae77Skettenis   /* Exception 2 is triggered by the NMI.  DJGPP handles it as SIGILL,
250b725ae77Skettenis      but I think SIGBUS is better, since the NMI is usually activated
251b725ae77Skettenis      as a result of a memory parity check failure.  */
252b725ae77Skettenis   {2, TARGET_SIGNAL_BUS},
253b725ae77Skettenis   {3, TARGET_SIGNAL_TRAP},
254b725ae77Skettenis   {4, TARGET_SIGNAL_FPE},
255b725ae77Skettenis   {5, TARGET_SIGNAL_SEGV},
256b725ae77Skettenis   {6, TARGET_SIGNAL_ILL},
257b725ae77Skettenis   {7, TARGET_SIGNAL_EMT},	/* no-coprocessor exception */
258b725ae77Skettenis   {8, TARGET_SIGNAL_SEGV},
259b725ae77Skettenis   {9, TARGET_SIGNAL_SEGV},
260b725ae77Skettenis   {10, TARGET_SIGNAL_BUS},
261b725ae77Skettenis   {11, TARGET_SIGNAL_SEGV},
262b725ae77Skettenis   {12, TARGET_SIGNAL_SEGV},
263b725ae77Skettenis   {13, TARGET_SIGNAL_SEGV},
264b725ae77Skettenis   {14, TARGET_SIGNAL_SEGV},
265b725ae77Skettenis   {16, TARGET_SIGNAL_FPE},
266b725ae77Skettenis   {17, TARGET_SIGNAL_BUS},
267b725ae77Skettenis   {31, TARGET_SIGNAL_ILL},
268b725ae77Skettenis   {0x1b, TARGET_SIGNAL_INT},
269b725ae77Skettenis   {0x75, TARGET_SIGNAL_FPE},
270b725ae77Skettenis   {0x78, TARGET_SIGNAL_ALRM},
271b725ae77Skettenis   {0x79, TARGET_SIGNAL_INT},
272b725ae77Skettenis   {0x7a, TARGET_SIGNAL_QUIT},
273b725ae77Skettenis   {-1, TARGET_SIGNAL_LAST}
274b725ae77Skettenis };
275b725ae77Skettenis 
276b725ae77Skettenis static struct {
277b725ae77Skettenis   enum target_signal gdb_sig;
278b725ae77Skettenis   int djgpp_excepno;
279b725ae77Skettenis } excepn_map[] = {
280b725ae77Skettenis   {TARGET_SIGNAL_0, -1},
281b725ae77Skettenis   {TARGET_SIGNAL_ILL, 6},	/* Invalid Opcode */
282b725ae77Skettenis   {TARGET_SIGNAL_EMT, 7},	/* triggers SIGNOFP */
283b725ae77Skettenis   {TARGET_SIGNAL_SEGV, 13},	/* GPF */
284b725ae77Skettenis   {TARGET_SIGNAL_BUS, 17},	/* Alignment Check */
285b725ae77Skettenis   /* The rest are fake exceptions, see dpmiexcp.c in djlsr*.zip for
286b725ae77Skettenis      details.  */
287b725ae77Skettenis   {TARGET_SIGNAL_TERM, 0x1b},	/* triggers Ctrl-Break type of SIGINT */
288b725ae77Skettenis   {TARGET_SIGNAL_FPE, 0x75},
289b725ae77Skettenis   {TARGET_SIGNAL_INT, 0x79},
290b725ae77Skettenis   {TARGET_SIGNAL_QUIT, 0x7a},
291b725ae77Skettenis   {TARGET_SIGNAL_ALRM, 0x78},	/* triggers SIGTIMR */
292b725ae77Skettenis   {TARGET_SIGNAL_PROF, 0x78},
293b725ae77Skettenis   {TARGET_SIGNAL_LAST, -1}
294b725ae77Skettenis };
295b725ae77Skettenis 
296b725ae77Skettenis static void
go32_open(char * name,int from_tty)297b725ae77Skettenis go32_open (char *name, int from_tty)
298b725ae77Skettenis {
299b725ae77Skettenis   printf_unfiltered ("Done.  Use the \"run\" command to run the program.\n");
300b725ae77Skettenis }
301b725ae77Skettenis 
302b725ae77Skettenis static void
go32_close(int quitting)303b725ae77Skettenis go32_close (int quitting)
304b725ae77Skettenis {
305b725ae77Skettenis }
306b725ae77Skettenis 
307b725ae77Skettenis static void
go32_attach(char * args,int from_tty)308b725ae77Skettenis go32_attach (char *args, int from_tty)
309b725ae77Skettenis {
310b725ae77Skettenis   error ("\
311b725ae77Skettenis You cannot attach to a running program on this platform.\n\
312b725ae77Skettenis Use the `run' command to run DJGPP programs.");
313b725ae77Skettenis }
314b725ae77Skettenis 
315b725ae77Skettenis static void
go32_detach(char * args,int from_tty)316b725ae77Skettenis go32_detach (char *args, int from_tty)
317b725ae77Skettenis {
318b725ae77Skettenis }
319b725ae77Skettenis 
320b725ae77Skettenis static int resume_is_step;
321b725ae77Skettenis static int resume_signal = -1;
322b725ae77Skettenis 
323b725ae77Skettenis static void
go32_resume(ptid_t ptid,int step,enum target_signal siggnal)324b725ae77Skettenis go32_resume (ptid_t ptid, int step, enum target_signal siggnal)
325b725ae77Skettenis {
326b725ae77Skettenis   int i;
327b725ae77Skettenis 
328b725ae77Skettenis   resume_is_step = step;
329b725ae77Skettenis 
330b725ae77Skettenis   if (siggnal != TARGET_SIGNAL_0 && siggnal != TARGET_SIGNAL_TRAP)
331b725ae77Skettenis   {
332b725ae77Skettenis     for (i = 0, resume_signal = -1;
333b725ae77Skettenis 	 excepn_map[i].gdb_sig != TARGET_SIGNAL_LAST; i++)
334b725ae77Skettenis       if (excepn_map[i].gdb_sig == siggnal)
335b725ae77Skettenis       {
336b725ae77Skettenis 	resume_signal = excepn_map[i].djgpp_excepno;
337b725ae77Skettenis 	break;
338b725ae77Skettenis       }
339b725ae77Skettenis     if (resume_signal == -1)
340b725ae77Skettenis       printf_unfiltered ("Cannot deliver signal %s on this platform.\n",
341b725ae77Skettenis 			 target_signal_to_name (siggnal));
342b725ae77Skettenis   }
343b725ae77Skettenis }
344b725ae77Skettenis 
345b725ae77Skettenis static char child_cwd[FILENAME_MAX];
346b725ae77Skettenis 
347b725ae77Skettenis static ptid_t
go32_wait(ptid_t ptid,struct target_waitstatus * status)348b725ae77Skettenis go32_wait (ptid_t ptid, struct target_waitstatus *status)
349b725ae77Skettenis {
350b725ae77Skettenis   int i;
351b725ae77Skettenis   unsigned char saved_opcode;
352b725ae77Skettenis   unsigned long INT3_addr = 0;
353b725ae77Skettenis   int stepping_over_INT = 0;
354b725ae77Skettenis 
355b725ae77Skettenis   a_tss.tss_eflags &= 0xfeff;	/* reset the single-step flag (TF) */
356b725ae77Skettenis   if (resume_is_step)
357b725ae77Skettenis     {
358b725ae77Skettenis       /* If the next instruction is INT xx or INTO, we need to handle
359b725ae77Skettenis 	 them specially.  Intel manuals say that these instructions
360b725ae77Skettenis 	 reset the single-step flag (a.k.a. TF).  However, it seems
361b725ae77Skettenis 	 that, at least in the DPMI environment, and at least when
362b725ae77Skettenis 	 stepping over the DPMI interrupt 31h, the problem is having
363b725ae77Skettenis 	 TF set at all when INT 31h is executed: the debuggee either
364b725ae77Skettenis 	 crashes (and takes the system with it) or is killed by a
365b725ae77Skettenis 	 SIGTRAP.
366b725ae77Skettenis 
367b725ae77Skettenis 	 So we need to emulate single-step mode: we put an INT3 opcode
368b725ae77Skettenis 	 right after the INT xx instruction, let the debuggee run
369b725ae77Skettenis 	 until it hits INT3 and stops, then restore the original
370b725ae77Skettenis 	 instruction which we overwrote with the INT3 opcode, and back
371b725ae77Skettenis 	 up the debuggee's EIP to that instruction.  */
372b725ae77Skettenis       read_child (a_tss.tss_eip, &saved_opcode, 1);
373b725ae77Skettenis       if (saved_opcode == 0xCD || saved_opcode == 0xCE)
374b725ae77Skettenis 	{
375b725ae77Skettenis 	  unsigned char INT3_opcode = 0xCC;
376b725ae77Skettenis 
377b725ae77Skettenis 	  INT3_addr
378b725ae77Skettenis 	    = saved_opcode == 0xCD ? a_tss.tss_eip + 2 : a_tss.tss_eip + 1;
379b725ae77Skettenis 	  stepping_over_INT = 1;
380b725ae77Skettenis 	  read_child (INT3_addr, &saved_opcode, 1);
381b725ae77Skettenis 	  write_child (INT3_addr, &INT3_opcode, 1);
382b725ae77Skettenis 	}
383b725ae77Skettenis       else
384b725ae77Skettenis 	a_tss.tss_eflags |= 0x0100; /* normal instruction: set TF */
385b725ae77Skettenis     }
386b725ae77Skettenis 
387b725ae77Skettenis   /* The special value FFFFh in tss_trap indicates to run_child that
388b725ae77Skettenis      tss_irqn holds a signal to be delivered to the debuggee.  */
389b725ae77Skettenis   if (resume_signal <= -1)
390b725ae77Skettenis     {
391b725ae77Skettenis       a_tss.tss_trap = 0;
392b725ae77Skettenis       a_tss.tss_irqn = 0xff;
393b725ae77Skettenis     }
394b725ae77Skettenis   else
395b725ae77Skettenis     {
396b725ae77Skettenis       a_tss.tss_trap = 0xffff;	/* run_child looks for this */
397b725ae77Skettenis       a_tss.tss_irqn = resume_signal;
398b725ae77Skettenis     }
399b725ae77Skettenis 
400b725ae77Skettenis   /* The child might change working directory behind our back.  The
401b725ae77Skettenis      GDB users won't like the side effects of that when they work with
402b725ae77Skettenis      relative file names, and GDB might be confused by its current
403b725ae77Skettenis      directory not being in sync with the truth.  So we always make a
404b725ae77Skettenis      point of changing back to where GDB thinks is its cwd, when we
405b725ae77Skettenis      return control to the debugger, but restore child's cwd before we
406b725ae77Skettenis      run it.  */
407b725ae77Skettenis   /* Initialize child_cwd, before the first call to run_child and not
408b725ae77Skettenis      in the initialization, so the child get also the changed directory
409b725ae77Skettenis      set with the gdb-command "cd ..." */
410b725ae77Skettenis   if (!*child_cwd)
411b725ae77Skettenis     /* Initialize child's cwd with the current one.  */
412b725ae77Skettenis     getcwd (child_cwd, sizeof (child_cwd));
413b725ae77Skettenis 
414b725ae77Skettenis   chdir (child_cwd);
415b725ae77Skettenis 
416b725ae77Skettenis #if __DJGPP_MINOR__ < 3
417b725ae77Skettenis   load_npx ();
418b725ae77Skettenis #endif
419b725ae77Skettenis   run_child ();
420b725ae77Skettenis #if __DJGPP_MINOR__ < 3
421b725ae77Skettenis   save_npx ();
422b725ae77Skettenis #endif
423b725ae77Skettenis 
424b725ae77Skettenis   /* Did we step over an INT xx instruction?  */
425b725ae77Skettenis   if (stepping_over_INT && a_tss.tss_eip == INT3_addr + 1)
426b725ae77Skettenis     {
427b725ae77Skettenis       /* Restore the original opcode.  */
428b725ae77Skettenis       a_tss.tss_eip--;	/* EIP points *after* the INT3 instruction */
429b725ae77Skettenis       write_child (a_tss.tss_eip, &saved_opcode, 1);
430b725ae77Skettenis       /* Simulate a TRAP exception.  */
431b725ae77Skettenis       a_tss.tss_irqn = 1;
432b725ae77Skettenis       a_tss.tss_eflags |= 0x0100;
433b725ae77Skettenis     }
434b725ae77Skettenis 
435b725ae77Skettenis   getcwd (child_cwd, sizeof (child_cwd)); /* in case it has changed */
436b725ae77Skettenis   chdir (current_directory);
437b725ae77Skettenis 
438b725ae77Skettenis   if (a_tss.tss_irqn == 0x21)
439b725ae77Skettenis     {
440b725ae77Skettenis       status->kind = TARGET_WAITKIND_EXITED;
441b725ae77Skettenis       status->value.integer = a_tss.tss_eax & 0xff;
442b725ae77Skettenis     }
443b725ae77Skettenis   else
444b725ae77Skettenis     {
445b725ae77Skettenis       status->value.sig = TARGET_SIGNAL_UNKNOWN;
446b725ae77Skettenis       status->kind = TARGET_WAITKIND_STOPPED;
447b725ae77Skettenis       for (i = 0; sig_map[i].go32_sig != -1; i++)
448b725ae77Skettenis 	{
449b725ae77Skettenis 	  if (a_tss.tss_irqn == sig_map[i].go32_sig)
450b725ae77Skettenis 	    {
451b725ae77Skettenis #if __DJGPP_MINOR__ < 3
452b725ae77Skettenis 	      if ((status->value.sig = sig_map[i].gdb_sig) !=
453b725ae77Skettenis 		  TARGET_SIGNAL_TRAP)
454b725ae77Skettenis 		status->kind = TARGET_WAITKIND_SIGNALLED;
455b725ae77Skettenis #else
456b725ae77Skettenis 	      status->value.sig = sig_map[i].gdb_sig;
457b725ae77Skettenis #endif
458b725ae77Skettenis 	      break;
459b725ae77Skettenis 	    }
460b725ae77Skettenis 	}
461b725ae77Skettenis     }
462b725ae77Skettenis   return pid_to_ptid (SOME_PID);
463b725ae77Skettenis }
464b725ae77Skettenis 
465b725ae77Skettenis static void
fetch_register(int regno)466b725ae77Skettenis fetch_register (int regno)
467b725ae77Skettenis {
468b725ae77Skettenis   if (regno < FP0_REGNUM)
469*11efff7fSkettenis     regcache_raw_supply (current_regcache, regno,
470*11efff7fSkettenis 			 (char *) &a_tss + regno_mapping[regno].tss_ofs);
471b725ae77Skettenis   else if (i386_fp_regnum_p (regno) || i386_fpc_regnum_p (regno))
472b725ae77Skettenis     i387_supply_fsave (current_regcache, regno, &npx);
473b725ae77Skettenis   else
474b725ae77Skettenis     internal_error (__FILE__, __LINE__,
475b725ae77Skettenis 		    "Invalid register no. %d in fetch_register.", regno);
476b725ae77Skettenis }
477b725ae77Skettenis 
478b725ae77Skettenis static void
go32_fetch_registers(int regno)479b725ae77Skettenis go32_fetch_registers (int regno)
480b725ae77Skettenis {
481b725ae77Skettenis   if (regno >= 0)
482b725ae77Skettenis     fetch_register (regno);
483b725ae77Skettenis   else
484b725ae77Skettenis     {
485b725ae77Skettenis       for (regno = 0; regno < FP0_REGNUM; regno++)
486b725ae77Skettenis 	fetch_register (regno);
487b725ae77Skettenis       i387_supply_fsave (current_regcache, -1, &npx);
488b725ae77Skettenis     }
489b725ae77Skettenis }
490b725ae77Skettenis 
491b725ae77Skettenis static void
store_register(int regno)492b725ae77Skettenis store_register (int regno)
493b725ae77Skettenis {
494b725ae77Skettenis   if (regno < FP0_REGNUM)
495*11efff7fSkettenis     regcache_raw_collect (current_regcache, regno,
496*11efff7fSkettenis 			  (char *) &a_tss + regno_mapping[regno].tss_ofs);
497b725ae77Skettenis   else if (i386_fp_regnum_p (regno) || i386_fpc_regnum_p (regno))
498b725ae77Skettenis     i387_fill_fsave ((char *) &npx, regno);
499b725ae77Skettenis   else
500b725ae77Skettenis     internal_error (__FILE__, __LINE__,
501b725ae77Skettenis 		    "Invalid register no. %d in store_register.", regno);
502b725ae77Skettenis }
503b725ae77Skettenis 
504b725ae77Skettenis static void
go32_store_registers(int regno)505b725ae77Skettenis go32_store_registers (int regno)
506b725ae77Skettenis {
507b725ae77Skettenis   unsigned r;
508b725ae77Skettenis 
509b725ae77Skettenis   if (regno >= 0)
510b725ae77Skettenis     store_register (regno);
511b725ae77Skettenis   else
512b725ae77Skettenis     {
513b725ae77Skettenis       for (r = 0; r < FP0_REGNUM; r++)
514b725ae77Skettenis 	store_register (r);
515b725ae77Skettenis       i387_fill_fsave ((char *) &npx, -1);
516b725ae77Skettenis     }
517b725ae77Skettenis }
518b725ae77Skettenis 
519b725ae77Skettenis static void
go32_prepare_to_store(void)520b725ae77Skettenis go32_prepare_to_store (void)
521b725ae77Skettenis {
522b725ae77Skettenis }
523b725ae77Skettenis 
524b725ae77Skettenis static int
go32_xfer_memory(CORE_ADDR memaddr,char * myaddr,int len,int write,struct mem_attrib * attrib,struct target_ops * target)525b725ae77Skettenis go32_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int write,
526b725ae77Skettenis 		  struct mem_attrib *attrib, struct target_ops *target)
527b725ae77Skettenis {
528b725ae77Skettenis   if (write)
529b725ae77Skettenis     {
530b725ae77Skettenis       if (write_child (memaddr, myaddr, len))
531b725ae77Skettenis 	{
532b725ae77Skettenis 	  return 0;
533b725ae77Skettenis 	}
534b725ae77Skettenis       else
535b725ae77Skettenis 	{
536b725ae77Skettenis 	  return len;
537b725ae77Skettenis 	}
538b725ae77Skettenis     }
539b725ae77Skettenis   else
540b725ae77Skettenis     {
541b725ae77Skettenis       if (read_child (memaddr, myaddr, len))
542b725ae77Skettenis 	{
543b725ae77Skettenis 	  return 0;
544b725ae77Skettenis 	}
545b725ae77Skettenis       else
546b725ae77Skettenis 	{
547b725ae77Skettenis 	  return len;
548b725ae77Skettenis 	}
549b725ae77Skettenis     }
550b725ae77Skettenis }
551b725ae77Skettenis 
552b725ae77Skettenis static cmdline_t child_cmd;	/* parsed child's command line kept here */
553b725ae77Skettenis 
554b725ae77Skettenis static void
go32_files_info(struct target_ops * target)555b725ae77Skettenis go32_files_info (struct target_ops *target)
556b725ae77Skettenis {
557b725ae77Skettenis   printf_unfiltered ("You are running a DJGPP V2 program.\n");
558b725ae77Skettenis }
559b725ae77Skettenis 
560b725ae77Skettenis static void
go32_stop(void)561b725ae77Skettenis go32_stop (void)
562b725ae77Skettenis {
563b725ae77Skettenis   normal_stop ();
564b725ae77Skettenis   cleanup_client ();
565b725ae77Skettenis   inferior_ptid = null_ptid;
566b725ae77Skettenis   prog_has_started = 0;
567b725ae77Skettenis }
568b725ae77Skettenis 
569b725ae77Skettenis static void
go32_kill_inferior(void)570b725ae77Skettenis go32_kill_inferior (void)
571b725ae77Skettenis {
572b725ae77Skettenis   redir_cmdline_delete (&child_cmd);
573b725ae77Skettenis   resume_signal = -1;
574b725ae77Skettenis   resume_is_step = 0;
575b725ae77Skettenis   unpush_target (&go32_ops);
576b725ae77Skettenis }
577b725ae77Skettenis 
578b725ae77Skettenis static void
go32_create_inferior(char * exec_file,char * args,char ** env,int from_tty)579*11efff7fSkettenis go32_create_inferior (char *exec_file, char *args, char **env, int from_tty)
580b725ae77Skettenis {
581b725ae77Skettenis   extern char **environ;
582b725ae77Skettenis   jmp_buf start_state;
583b725ae77Skettenis   char *cmdline;
584b725ae77Skettenis   char **env_save = environ;
585b725ae77Skettenis   size_t cmdlen;
586b725ae77Skettenis 
587b725ae77Skettenis   /* If no exec file handed to us, get it from the exec-file command -- with
588b725ae77Skettenis      a good, common error message if none is specified.  */
589b725ae77Skettenis   if (exec_file == 0)
590b725ae77Skettenis     exec_file = get_exec_file (1);
591b725ae77Skettenis 
592b725ae77Skettenis   if (prog_has_started)
593b725ae77Skettenis     {
594b725ae77Skettenis       go32_stop ();
595b725ae77Skettenis       go32_kill_inferior ();
596b725ae77Skettenis     }
597b725ae77Skettenis   resume_signal = -1;
598b725ae77Skettenis   resume_is_step = 0;
599b725ae77Skettenis 
600b725ae77Skettenis   /* Initialize child's cwd as empty to be initialized when starting
601b725ae77Skettenis      the child.  */
602b725ae77Skettenis   *child_cwd = 0;
603b725ae77Skettenis 
604b725ae77Skettenis   /* Init command line storage.  */
605b725ae77Skettenis   if (redir_debug_init (&child_cmd) == -1)
606b725ae77Skettenis     internal_error (__FILE__, __LINE__,
607b725ae77Skettenis 		    "Cannot allocate redirection storage: not enough memory.\n");
608b725ae77Skettenis 
609b725ae77Skettenis   /* Parse the command line and create redirections.  */
610b725ae77Skettenis   if (strpbrk (args, "<>"))
611b725ae77Skettenis     {
612b725ae77Skettenis       if (redir_cmdline_parse (args, &child_cmd) == 0)
613b725ae77Skettenis 	args = child_cmd.command;
614b725ae77Skettenis       else
615b725ae77Skettenis 	error ("Syntax error in command line.");
616b725ae77Skettenis     }
617b725ae77Skettenis   else
618b725ae77Skettenis     child_cmd.command = xstrdup (args);
619b725ae77Skettenis 
620b725ae77Skettenis   cmdlen = strlen (args);
621b725ae77Skettenis   /* v2loadimage passes command lines via DOS memory, so it cannot
622b725ae77Skettenis      possibly handle commands longer than 1MB.  */
623b725ae77Skettenis   if (cmdlen > 1024*1024)
624b725ae77Skettenis     error ("Command line too long.");
625b725ae77Skettenis 
626b725ae77Skettenis   cmdline = xmalloc (cmdlen + 4);
627b725ae77Skettenis   strcpy (cmdline + 1, args);
628b725ae77Skettenis   /* If the command-line length fits into DOS 126-char limits, use the
629b725ae77Skettenis      DOS command tail format; otherwise, tell v2loadimage to pass it
630b725ae77Skettenis      through a buffer in conventional memory.  */
631b725ae77Skettenis   if (cmdlen < 127)
632b725ae77Skettenis     {
633b725ae77Skettenis       cmdline[0] = strlen (args);
634b725ae77Skettenis       cmdline[cmdlen + 1] = 13;
635b725ae77Skettenis     }
636b725ae77Skettenis   else
637b725ae77Skettenis     cmdline[0] = 0xff;	/* signal v2loadimage it's a long command */
638b725ae77Skettenis 
639b725ae77Skettenis   environ = env;
640b725ae77Skettenis 
641b725ae77Skettenis   if (v2loadimage (exec_file, cmdline, start_state))
642b725ae77Skettenis     {
643b725ae77Skettenis       environ = env_save;
644b725ae77Skettenis       printf_unfiltered ("Load failed for image %s\n", exec_file);
645b725ae77Skettenis       exit (1);
646b725ae77Skettenis     }
647b725ae77Skettenis   environ = env_save;
648b725ae77Skettenis   xfree (cmdline);
649b725ae77Skettenis 
650b725ae77Skettenis   edi_init (start_state);
651b725ae77Skettenis #if __DJGPP_MINOR__ < 3
652b725ae77Skettenis   save_npx ();
653b725ae77Skettenis #endif
654b725ae77Skettenis 
655b725ae77Skettenis   inferior_ptid = pid_to_ptid (SOME_PID);
656b725ae77Skettenis   push_target (&go32_ops);
657b725ae77Skettenis   clear_proceed_status ();
658b725ae77Skettenis   insert_breakpoints ();
659b725ae77Skettenis   proceed ((CORE_ADDR) -1, TARGET_SIGNAL_0, 0);
660b725ae77Skettenis   prog_has_started = 1;
661b725ae77Skettenis }
662b725ae77Skettenis 
663b725ae77Skettenis static void
go32_mourn_inferior(void)664b725ae77Skettenis go32_mourn_inferior (void)
665b725ae77Skettenis {
666b725ae77Skettenis   /* We need to make sure all the breakpoint enable bits in the DR7
667b725ae77Skettenis      register are reset when the inferior exits.  Otherwise, if they
668b725ae77Skettenis      rerun the inferior, the uncleared bits may cause random SIGTRAPs,
669b725ae77Skettenis      failure to set more watchpoints, and other calamities.  It would
670b725ae77Skettenis      be nice if GDB itself would take care to remove all breakpoints
671b725ae77Skettenis      at all times, but it doesn't, probably under an assumption that
672b725ae77Skettenis      the OS cleans up when the debuggee exits.  */
673b725ae77Skettenis   i386_cleanup_dregs ();
674b725ae77Skettenis   go32_kill_inferior ();
675b725ae77Skettenis   generic_mourn_inferior ();
676b725ae77Skettenis }
677b725ae77Skettenis 
678b725ae77Skettenis static int
go32_can_run(void)679b725ae77Skettenis go32_can_run (void)
680b725ae77Skettenis {
681b725ae77Skettenis   return 1;
682b725ae77Skettenis }
683b725ae77Skettenis 
684b725ae77Skettenis /* Hardware watchpoint support.  */
685b725ae77Skettenis 
686b725ae77Skettenis #define D_REGS edi.dr
687b725ae77Skettenis #define CONTROL D_REGS[7]
688b725ae77Skettenis #define STATUS D_REGS[6]
689b725ae77Skettenis 
690b725ae77Skettenis /* Pass the address ADDR to the inferior in the I'th debug register.
691b725ae77Skettenis    Here we just store the address in D_REGS, the watchpoint will be
692b725ae77Skettenis    actually set up when go32_wait runs the debuggee.  */
693b725ae77Skettenis void
go32_set_dr(int i,CORE_ADDR addr)694b725ae77Skettenis go32_set_dr (int i, CORE_ADDR addr)
695b725ae77Skettenis {
696b725ae77Skettenis   if (i < 0 || i > 3)
697b725ae77Skettenis     internal_error (__FILE__, __LINE__,
698b725ae77Skettenis 		    "Invalid register %d in go32_set_dr.\n", i);
699b725ae77Skettenis   D_REGS[i] = addr;
700b725ae77Skettenis }
701b725ae77Skettenis 
702b725ae77Skettenis /* Pass the value VAL to the inferior in the DR7 debug control
703b725ae77Skettenis    register.  Here we just store the address in D_REGS, the watchpoint
704b725ae77Skettenis    will be actually set up when go32_wait runs the debuggee.  */
705b725ae77Skettenis void
go32_set_dr7(unsigned val)706b725ae77Skettenis go32_set_dr7 (unsigned val)
707b725ae77Skettenis {
708b725ae77Skettenis   CONTROL = val;
709b725ae77Skettenis }
710b725ae77Skettenis 
711b725ae77Skettenis /* Get the value of the DR6 debug status register from the inferior.
712b725ae77Skettenis    Here we just return the value stored in D_REGS, as we've got it
713b725ae77Skettenis    from the last go32_wait call.  */
714b725ae77Skettenis unsigned
go32_get_dr6(void)715b725ae77Skettenis go32_get_dr6 (void)
716b725ae77Skettenis {
717b725ae77Skettenis   return STATUS;
718b725ae77Skettenis }
719b725ae77Skettenis 
720b725ae77Skettenis /* Put the device open on handle FD into either raw or cooked
721b725ae77Skettenis    mode, return 1 if it was in raw mode, zero otherwise.  */
722b725ae77Skettenis 
723b725ae77Skettenis static int
device_mode(int fd,int raw_p)724b725ae77Skettenis device_mode (int fd, int raw_p)
725b725ae77Skettenis {
726b725ae77Skettenis   int oldmode, newmode;
727b725ae77Skettenis   __dpmi_regs regs;
728b725ae77Skettenis 
729b725ae77Skettenis   regs.x.ax = 0x4400;
730b725ae77Skettenis   regs.x.bx = fd;
731b725ae77Skettenis   __dpmi_int (0x21, &regs);
732b725ae77Skettenis   if (regs.x.flags & 1)
733b725ae77Skettenis     return -1;
734b725ae77Skettenis   newmode = oldmode = regs.x.dx;
735b725ae77Skettenis 
736b725ae77Skettenis   if (raw_p)
737b725ae77Skettenis     newmode |= 0x20;
738b725ae77Skettenis   else
739b725ae77Skettenis     newmode &= ~0x20;
740b725ae77Skettenis 
741b725ae77Skettenis   if (oldmode & 0x80)	/* Only for character dev */
742b725ae77Skettenis   {
743b725ae77Skettenis     regs.x.ax = 0x4401;
744b725ae77Skettenis     regs.x.bx = fd;
745b725ae77Skettenis     regs.x.dx = newmode & 0xff;   /* Force upper byte zero, else it fails */
746b725ae77Skettenis     __dpmi_int (0x21, &regs);
747b725ae77Skettenis     if (regs.x.flags & 1)
748b725ae77Skettenis       return -1;
749b725ae77Skettenis   }
750b725ae77Skettenis   return (oldmode & 0x20) == 0x20;
751b725ae77Skettenis }
752b725ae77Skettenis 
753b725ae77Skettenis 
754b725ae77Skettenis static int inf_mode_valid = 0;
755b725ae77Skettenis static int inf_terminal_mode;
756b725ae77Skettenis 
757b725ae77Skettenis /* This semaphore is needed because, amazingly enough, GDB calls
758b725ae77Skettenis    target.to_terminal_ours more than once after the inferior stops.
759b725ae77Skettenis    But we need the information from the first call only, since the
760b725ae77Skettenis    second call will always see GDB's own cooked terminal.  */
761b725ae77Skettenis static int terminal_is_ours = 1;
762b725ae77Skettenis 
763b725ae77Skettenis static void
go32_terminal_init(void)764b725ae77Skettenis go32_terminal_init (void)
765b725ae77Skettenis {
766b725ae77Skettenis   inf_mode_valid = 0;	/* reinitialize, in case they are restarting child */
767b725ae77Skettenis   terminal_is_ours = 1;
768b725ae77Skettenis }
769b725ae77Skettenis 
770b725ae77Skettenis static void
go32_terminal_info(char * args,int from_tty)771b725ae77Skettenis go32_terminal_info (char *args, int from_tty)
772b725ae77Skettenis {
773b725ae77Skettenis   printf_unfiltered ("Inferior's terminal is in %s mode.\n",
774b725ae77Skettenis 		     !inf_mode_valid
775b725ae77Skettenis 		     ? "default" : inf_terminal_mode ? "raw" : "cooked");
776b725ae77Skettenis 
777b725ae77Skettenis #if __DJGPP_MINOR__ > 2
778b725ae77Skettenis   if (child_cmd.redirection)
779b725ae77Skettenis   {
780b725ae77Skettenis     int i;
781b725ae77Skettenis 
782b725ae77Skettenis     for (i = 0; i < DBG_HANDLES; i++)
783b725ae77Skettenis     {
784b725ae77Skettenis       if (child_cmd.redirection[i]->file_name)
785b725ae77Skettenis 	printf_unfiltered ("\tFile handle %d is redirected to `%s'.\n",
786b725ae77Skettenis 			   i, child_cmd.redirection[i]->file_name);
787b725ae77Skettenis       else if (_get_dev_info (child_cmd.redirection[i]->inf_handle) == -1)
788b725ae77Skettenis 	printf_unfiltered
789b725ae77Skettenis 	  ("\tFile handle %d appears to be closed by inferior.\n", i);
790b725ae77Skettenis       /* Mask off the raw/cooked bit when comparing device info words.  */
791b725ae77Skettenis       else if ((_get_dev_info (child_cmd.redirection[i]->inf_handle) & 0xdf)
792b725ae77Skettenis 	       != (_get_dev_info (i) & 0xdf))
793b725ae77Skettenis 	printf_unfiltered
794b725ae77Skettenis 	  ("\tFile handle %d appears to be redirected by inferior.\n", i);
795b725ae77Skettenis     }
796b725ae77Skettenis   }
797b725ae77Skettenis #endif
798b725ae77Skettenis }
799b725ae77Skettenis 
800b725ae77Skettenis static void
go32_terminal_inferior(void)801b725ae77Skettenis go32_terminal_inferior (void)
802b725ae77Skettenis {
803b725ae77Skettenis   /* Redirect standard handles as child wants them.  */
804b725ae77Skettenis   errno = 0;
805b725ae77Skettenis   if (redir_to_child (&child_cmd) == -1)
806b725ae77Skettenis   {
807b725ae77Skettenis     redir_to_debugger (&child_cmd);
808b725ae77Skettenis     error ("Cannot redirect standard handles for program: %s.",
809b725ae77Skettenis 	   safe_strerror (errno));
810b725ae77Skettenis   }
811b725ae77Skettenis   /* set the console device of the inferior to whatever mode
812b725ae77Skettenis      (raw or cooked) we found it last time */
813b725ae77Skettenis   if (terminal_is_ours)
814b725ae77Skettenis   {
815b725ae77Skettenis     if (inf_mode_valid)
816b725ae77Skettenis       device_mode (0, inf_terminal_mode);
817b725ae77Skettenis     terminal_is_ours = 0;
818b725ae77Skettenis   }
819b725ae77Skettenis }
820b725ae77Skettenis 
821b725ae77Skettenis static void
go32_terminal_ours(void)822b725ae77Skettenis go32_terminal_ours (void)
823b725ae77Skettenis {
824b725ae77Skettenis   /* Switch to cooked mode on the gdb terminal and save the inferior
825b725ae77Skettenis      terminal mode to be restored when it is resumed */
826b725ae77Skettenis   if (!terminal_is_ours)
827b725ae77Skettenis   {
828b725ae77Skettenis     inf_terminal_mode = device_mode (0, 0);
829b725ae77Skettenis     if (inf_terminal_mode != -1)
830b725ae77Skettenis       inf_mode_valid = 1;
831b725ae77Skettenis     else
832b725ae77Skettenis       /* If device_mode returned -1, we don't know what happens with
833b725ae77Skettenis 	 handle 0 anymore, so make the info invalid.  */
834b725ae77Skettenis       inf_mode_valid = 0;
835b725ae77Skettenis     terminal_is_ours = 1;
836b725ae77Skettenis 
837b725ae77Skettenis     /* Restore debugger's standard handles.  */
838b725ae77Skettenis     errno = 0;
839b725ae77Skettenis     if (redir_to_debugger (&child_cmd) == -1)
840b725ae77Skettenis     {
841b725ae77Skettenis       redir_to_child (&child_cmd);
842b725ae77Skettenis       error ("Cannot redirect standard handles for debugger: %s.",
843b725ae77Skettenis 	     safe_strerror (errno));
844b725ae77Skettenis     }
845b725ae77Skettenis   }
846b725ae77Skettenis }
847b725ae77Skettenis 
848b725ae77Skettenis static void
init_go32_ops(void)849b725ae77Skettenis init_go32_ops (void)
850b725ae77Skettenis {
851b725ae77Skettenis   go32_ops.to_shortname = "djgpp";
852b725ae77Skettenis   go32_ops.to_longname = "djgpp target process";
853b725ae77Skettenis   go32_ops.to_doc =
854b725ae77Skettenis     "Program loaded by djgpp, when gdb is used as an external debugger";
855b725ae77Skettenis   go32_ops.to_open = go32_open;
856b725ae77Skettenis   go32_ops.to_close = go32_close;
857b725ae77Skettenis   go32_ops.to_attach = go32_attach;
858b725ae77Skettenis   go32_ops.to_detach = go32_detach;
859b725ae77Skettenis   go32_ops.to_resume = go32_resume;
860b725ae77Skettenis   go32_ops.to_wait = go32_wait;
861b725ae77Skettenis   go32_ops.to_fetch_registers = go32_fetch_registers;
862b725ae77Skettenis   go32_ops.to_store_registers = go32_store_registers;
863b725ae77Skettenis   go32_ops.to_prepare_to_store = go32_prepare_to_store;
864*11efff7fSkettenis   go32_ops.deprecated_xfer_memory = go32_xfer_memory;
865b725ae77Skettenis   go32_ops.to_files_info = go32_files_info;
866b725ae77Skettenis   go32_ops.to_insert_breakpoint = memory_insert_breakpoint;
867b725ae77Skettenis   go32_ops.to_remove_breakpoint = memory_remove_breakpoint;
868b725ae77Skettenis   go32_ops.to_terminal_init = go32_terminal_init;
869b725ae77Skettenis   go32_ops.to_terminal_inferior = go32_terminal_inferior;
870b725ae77Skettenis   go32_ops.to_terminal_ours_for_output = go32_terminal_ours;
871b725ae77Skettenis   go32_ops.to_terminal_ours = go32_terminal_ours;
872b725ae77Skettenis   go32_ops.to_terminal_info = go32_terminal_info;
873b725ae77Skettenis   go32_ops.to_kill = go32_kill_inferior;
874b725ae77Skettenis   go32_ops.to_create_inferior = go32_create_inferior;
875b725ae77Skettenis   go32_ops.to_mourn_inferior = go32_mourn_inferior;
876b725ae77Skettenis   go32_ops.to_can_run = go32_can_run;
877b725ae77Skettenis   go32_ops.to_stop = go32_stop;
878b725ae77Skettenis   go32_ops.to_stratum = process_stratum;
879b725ae77Skettenis   go32_ops.to_has_all_memory = 1;
880b725ae77Skettenis   go32_ops.to_has_memory = 1;
881b725ae77Skettenis   go32_ops.to_has_stack = 1;
882b725ae77Skettenis   go32_ops.to_has_registers = 1;
883b725ae77Skettenis   go32_ops.to_has_execution = 1;
884b725ae77Skettenis   go32_ops.to_magic = OPS_MAGIC;
885b725ae77Skettenis 
886b725ae77Skettenis   /* Initialize child's cwd as empty to be initialized when starting
887b725ae77Skettenis      the child.  */
888b725ae77Skettenis   *child_cwd = 0;
889b725ae77Skettenis 
890b725ae77Skettenis   /* Initialize child's command line storage.  */
891b725ae77Skettenis   if (redir_debug_init (&child_cmd) == -1)
892b725ae77Skettenis     internal_error (__FILE__, __LINE__,
893b725ae77Skettenis 		    "Cannot allocate redirection storage: not enough memory.\n");
894b725ae77Skettenis 
895b725ae77Skettenis   /* We are always processing GCC-compiled programs.  */
896b725ae77Skettenis   processing_gcc_compilation = 2;
897b725ae77Skettenis }
898b725ae77Skettenis 
899b725ae77Skettenis unsigned short windows_major, windows_minor;
900b725ae77Skettenis 
901b725ae77Skettenis /* Compute the version Windows reports via Int 2Fh/AX=1600h.  */
902b725ae77Skettenis static void
go32_get_windows_version(void)903b725ae77Skettenis go32_get_windows_version(void)
904b725ae77Skettenis {
905b725ae77Skettenis   __dpmi_regs r;
906b725ae77Skettenis 
907b725ae77Skettenis   r.x.ax = 0x1600;
908b725ae77Skettenis   __dpmi_int(0x2f, &r);
909b725ae77Skettenis   if (r.h.al > 2 && r.h.al != 0x80 && r.h.al != 0xff
910b725ae77Skettenis       && (r.h.al > 3 || r.h.ah > 0))
911b725ae77Skettenis     {
912b725ae77Skettenis       windows_major = r.h.al;
913b725ae77Skettenis       windows_minor = r.h.ah;
914b725ae77Skettenis     }
915b725ae77Skettenis   else
916b725ae77Skettenis     windows_major = 0xff;	/* meaning no Windows */
917b725ae77Skettenis }
918b725ae77Skettenis 
919b725ae77Skettenis /* A subroutine of go32_sysinfo to display memory info.  */
920b725ae77Skettenis static void
print_mem(unsigned long datum,const char * header,int in_pages_p)921b725ae77Skettenis print_mem (unsigned long datum, const char *header, int in_pages_p)
922b725ae77Skettenis {
923b725ae77Skettenis   if (datum != 0xffffffffUL)
924b725ae77Skettenis     {
925b725ae77Skettenis       if (in_pages_p)
926b725ae77Skettenis 	datum <<= 12;
927b725ae77Skettenis       puts_filtered (header);
928b725ae77Skettenis       if (datum > 1024)
929b725ae77Skettenis 	{
930b725ae77Skettenis 	  printf_filtered ("%lu KB", datum >> 10);
931b725ae77Skettenis 	  if (datum > 1024 * 1024)
932b725ae77Skettenis 	    printf_filtered (" (%lu MB)", datum >> 20);
933b725ae77Skettenis 	}
934b725ae77Skettenis       else
935b725ae77Skettenis 	printf_filtered ("%lu Bytes", datum);
936b725ae77Skettenis       puts_filtered ("\n");
937b725ae77Skettenis     }
938b725ae77Skettenis }
939b725ae77Skettenis 
940b725ae77Skettenis /* Display assorted information about the underlying OS.  */
941b725ae77Skettenis static void
go32_sysinfo(char * arg,int from_tty)942b725ae77Skettenis go32_sysinfo (char *arg, int from_tty)
943b725ae77Skettenis {
944b725ae77Skettenis   struct utsname u;
945b725ae77Skettenis   char cpuid_vendor[13];
946b725ae77Skettenis   unsigned cpuid_max = 0, cpuid_eax, cpuid_ebx, cpuid_ecx, cpuid_edx;
947b725ae77Skettenis   unsigned true_dos_version = _get_dos_version (1);
948b725ae77Skettenis   unsigned advertized_dos_version = ((unsigned int)_osmajor << 8) | _osminor;
949b725ae77Skettenis   int dpmi_flags;
950b725ae77Skettenis   char dpmi_vendor_info[129];
951b725ae77Skettenis   int dpmi_vendor_available =
952b725ae77Skettenis     __dpmi_get_capabilities (&dpmi_flags, dpmi_vendor_info);
953b725ae77Skettenis   __dpmi_version_ret dpmi_version_data;
954b725ae77Skettenis   long eflags;
955b725ae77Skettenis   __dpmi_free_mem_info mem_info;
956b725ae77Skettenis   __dpmi_regs regs;
957b725ae77Skettenis 
958b725ae77Skettenis   cpuid_vendor[0] = '\0';
959b725ae77Skettenis   if (uname (&u))
960b725ae77Skettenis     strcpy (u.machine, "Unknown x86");
961b725ae77Skettenis   else if (u.machine[0] == 'i' && u.machine[1] > 4)
962b725ae77Skettenis     {
963b725ae77Skettenis       /* CPUID with EAX = 0 returns the Vendor ID.  */
964b725ae77Skettenis       __asm__ __volatile__ ("xorl   %%ebx, %%ebx;"
965b725ae77Skettenis 			    "xorl   %%ecx, %%ecx;"
966b725ae77Skettenis 			    "xorl   %%edx, %%edx;"
967b725ae77Skettenis 			    "movl   $0,    %%eax;"
968b725ae77Skettenis 			    "cpuid;"
969b725ae77Skettenis 			    "movl   %%ebx,  %0;"
970b725ae77Skettenis 			    "movl   %%edx,  %1;"
971b725ae77Skettenis 			    "movl   %%ecx,  %2;"
972b725ae77Skettenis 			    "movl   %%eax,  %3;"
973b725ae77Skettenis 			    : "=m" (cpuid_vendor[0]),
974b725ae77Skettenis 			      "=m" (cpuid_vendor[4]),
975b725ae77Skettenis 			      "=m" (cpuid_vendor[8]),
976b725ae77Skettenis 			      "=m" (cpuid_max)
977b725ae77Skettenis 			    :
978b725ae77Skettenis 			    : "%eax", "%ebx", "%ecx", "%edx");
979b725ae77Skettenis       cpuid_vendor[12] = '\0';
980b725ae77Skettenis     }
981b725ae77Skettenis 
982b725ae77Skettenis   printf_filtered ("CPU Type.......................%s", u.machine);
983b725ae77Skettenis   if (cpuid_vendor[0])
984b725ae77Skettenis     printf_filtered (" (%s)", cpuid_vendor);
985b725ae77Skettenis   puts_filtered ("\n");
986b725ae77Skettenis 
987b725ae77Skettenis   /* CPUID with EAX = 1 returns processor signature and features.  */
988b725ae77Skettenis   if (cpuid_max >= 1)
989b725ae77Skettenis     {
990b725ae77Skettenis       static char *brand_name[] = {
991b725ae77Skettenis 	"",
992b725ae77Skettenis 	" Celeron",
993b725ae77Skettenis 	" III",
994b725ae77Skettenis 	" III Xeon",
995b725ae77Skettenis 	"", "", "", "",
996b725ae77Skettenis 	" 4"
997b725ae77Skettenis       };
998b725ae77Skettenis       char cpu_string[80];
999b725ae77Skettenis       char cpu_brand[20];
1000b725ae77Skettenis       unsigned brand_idx;
1001b725ae77Skettenis       int intel_p = strcmp (cpuid_vendor, "GenuineIntel") == 0;
1002b725ae77Skettenis       int amd_p = strcmp (cpuid_vendor, "AuthenticAMD") == 0;
1003b725ae77Skettenis       unsigned cpu_family, cpu_model;
1004b725ae77Skettenis 
1005b725ae77Skettenis       __asm__ __volatile__ ("movl   $1, %%eax;"
1006b725ae77Skettenis 			    "cpuid;"
1007b725ae77Skettenis 			    : "=a" (cpuid_eax),
1008b725ae77Skettenis 			      "=b" (cpuid_ebx),
1009b725ae77Skettenis 			      "=d" (cpuid_edx)
1010b725ae77Skettenis 			    :
1011b725ae77Skettenis 			    : "%ecx");
1012b725ae77Skettenis       brand_idx = cpuid_ebx & 0xff;
1013b725ae77Skettenis       cpu_family = (cpuid_eax >> 8) & 0xf;
1014b725ae77Skettenis       cpu_model  = (cpuid_eax >> 4) & 0xf;
1015b725ae77Skettenis       cpu_brand[0] = '\0';
1016b725ae77Skettenis       if (intel_p)
1017b725ae77Skettenis 	{
1018b725ae77Skettenis 	  if (brand_idx > 0
1019b725ae77Skettenis 	      && brand_idx < sizeof(brand_name)/sizeof(brand_name[0])
1020b725ae77Skettenis 	      && *brand_name[brand_idx])
1021b725ae77Skettenis 	    strcpy (cpu_brand, brand_name[brand_idx]);
1022b725ae77Skettenis 	  else if (cpu_family == 5)
1023b725ae77Skettenis 	    {
1024b725ae77Skettenis 	      if (((cpuid_eax >> 12) & 3) == 0 && cpu_model == 4)
1025b725ae77Skettenis 		strcpy (cpu_brand, " MMX");
1026b725ae77Skettenis 	      else if (cpu_model > 1 && ((cpuid_eax >> 12) & 3) == 1)
1027b725ae77Skettenis 		strcpy (cpu_brand, " OverDrive");
1028b725ae77Skettenis 	      else if (cpu_model > 1 && ((cpuid_eax >> 12) & 3) == 2)
1029b725ae77Skettenis 		strcpy (cpu_brand, " Dual");
1030b725ae77Skettenis 	    }
1031b725ae77Skettenis 	  else if (cpu_family == 6 && cpu_model < 8)
1032b725ae77Skettenis 	    {
1033b725ae77Skettenis 	      switch (cpu_model)
1034b725ae77Skettenis 		{
1035b725ae77Skettenis 		  case 1:
1036b725ae77Skettenis 		    strcpy (cpu_brand, " Pro");
1037b725ae77Skettenis 		    break;
1038b725ae77Skettenis 		  case 3:
1039b725ae77Skettenis 		    strcpy (cpu_brand, " II");
1040b725ae77Skettenis 		    break;
1041b725ae77Skettenis 		  case 5:
1042b725ae77Skettenis 		    strcpy (cpu_brand, " II Xeon");
1043b725ae77Skettenis 		    break;
1044b725ae77Skettenis 		  case 6:
1045b725ae77Skettenis 		    strcpy (cpu_brand, " Celeron");
1046b725ae77Skettenis 		    break;
1047b725ae77Skettenis 		  case 7:
1048b725ae77Skettenis 		    strcpy (cpu_brand, " III");
1049b725ae77Skettenis 		    break;
1050b725ae77Skettenis 		}
1051b725ae77Skettenis 	    }
1052b725ae77Skettenis 	}
1053b725ae77Skettenis       else if (amd_p)
1054b725ae77Skettenis 	{
1055b725ae77Skettenis 	  switch (cpu_family)
1056b725ae77Skettenis 	    {
1057b725ae77Skettenis 	      case 4:
1058b725ae77Skettenis 		strcpy (cpu_brand, "486/5x86");
1059b725ae77Skettenis 		break;
1060b725ae77Skettenis 	      case 5:
1061b725ae77Skettenis 		switch (cpu_model)
1062b725ae77Skettenis 		  {
1063b725ae77Skettenis 		    case 0:
1064b725ae77Skettenis 		    case 1:
1065b725ae77Skettenis 		    case 2:
1066b725ae77Skettenis 		    case 3:
1067b725ae77Skettenis 		      strcpy (cpu_brand, "-K5");
1068b725ae77Skettenis 		      break;
1069b725ae77Skettenis 		    case 6:
1070b725ae77Skettenis 		    case 7:
1071b725ae77Skettenis 		      strcpy (cpu_brand, "-K6");
1072b725ae77Skettenis 		      break;
1073b725ae77Skettenis 		    case 8:
1074b725ae77Skettenis 		      strcpy (cpu_brand, "-K6-2");
1075b725ae77Skettenis 		      break;
1076b725ae77Skettenis 		    case 9:
1077b725ae77Skettenis 		      strcpy (cpu_brand, "-K6-III");
1078b725ae77Skettenis 		      break;
1079b725ae77Skettenis 		  }
1080b725ae77Skettenis 		break;
1081b725ae77Skettenis 	      case 6:
1082b725ae77Skettenis 		switch (cpu_model)
1083b725ae77Skettenis 		  {
1084b725ae77Skettenis 		    case 1:
1085b725ae77Skettenis 		    case 2:
1086b725ae77Skettenis 		    case 4:
1087b725ae77Skettenis 		      strcpy (cpu_brand, " Athlon");
1088b725ae77Skettenis 		      break;
1089b725ae77Skettenis 		    case 3:
1090b725ae77Skettenis 		      strcpy (cpu_brand, " Duron");
1091b725ae77Skettenis 		      break;
1092b725ae77Skettenis 		  }
1093b725ae77Skettenis 		break;
1094b725ae77Skettenis 	    }
1095b725ae77Skettenis 	}
1096b725ae77Skettenis       sprintf (cpu_string, "%s%s Model %d Stepping %d",
1097b725ae77Skettenis 	       intel_p ? "Pentium" : (amd_p ? "AMD" : "ix86"),
1098b725ae77Skettenis 	       cpu_brand, cpu_model, cpuid_eax & 0xf);
1099b725ae77Skettenis       printfi_filtered (31, "%s\n", cpu_string);
1100b725ae77Skettenis       if (((cpuid_edx & (6 | (0x0d << 23))) != 0)
1101b725ae77Skettenis 	  || ((cpuid_edx & 1) == 0)
1102b725ae77Skettenis 	  || (amd_p && (cpuid_edx & (3 << 30)) != 0))
1103b725ae77Skettenis 	{
1104b725ae77Skettenis 	  puts_filtered ("CPU Features...................");
1105b725ae77Skettenis 	  /* We only list features which might be useful in the DPMI
1106b725ae77Skettenis 	     environment.  */
1107b725ae77Skettenis 	  if ((cpuid_edx & 1) == 0)
1108b725ae77Skettenis 	    puts_filtered ("No FPU "); /* it's unusual to not have an FPU */
1109b725ae77Skettenis 	  if ((cpuid_edx & (1 << 1)) != 0)
1110b725ae77Skettenis 	    puts_filtered ("VME ");
1111b725ae77Skettenis 	  if ((cpuid_edx & (1 << 2)) != 0)
1112b725ae77Skettenis 	    puts_filtered ("DE ");
1113b725ae77Skettenis 	  if ((cpuid_edx & (1 << 4)) != 0)
1114b725ae77Skettenis 	    puts_filtered ("TSC ");
1115b725ae77Skettenis 	  if ((cpuid_edx & (1 << 23)) != 0)
1116b725ae77Skettenis 	    puts_filtered ("MMX ");
1117b725ae77Skettenis 	  if ((cpuid_edx & (1 << 25)) != 0)
1118b725ae77Skettenis 	    puts_filtered ("SSE ");
1119b725ae77Skettenis 	  if ((cpuid_edx & (1 << 26)) != 0)
1120b725ae77Skettenis 	    puts_filtered ("SSE2 ");
1121b725ae77Skettenis 	  if (amd_p)
1122b725ae77Skettenis 	    {
1123b725ae77Skettenis 	      if ((cpuid_edx & (1 << 31)) != 0)
1124b725ae77Skettenis 		puts_filtered ("3DNow! ");
1125b725ae77Skettenis 	      if ((cpuid_edx & (1 << 30)) != 0)
1126b725ae77Skettenis 		puts_filtered ("3DNow!Ext");
1127b725ae77Skettenis 	    }
1128b725ae77Skettenis 	  puts_filtered ("\n");
1129b725ae77Skettenis 	}
1130b725ae77Skettenis     }
1131b725ae77Skettenis   puts_filtered ("\n");
1132b725ae77Skettenis   printf_filtered ("DOS Version....................%s %s.%s",
1133b725ae77Skettenis 		   _os_flavor, u.release, u.version);
1134b725ae77Skettenis   if (true_dos_version != advertized_dos_version)
1135b725ae77Skettenis     printf_filtered (" (disguised as v%d.%d)", _osmajor, _osminor);
1136b725ae77Skettenis   puts_filtered ("\n");
1137b725ae77Skettenis   if (!windows_major)
1138b725ae77Skettenis     go32_get_windows_version ();
1139b725ae77Skettenis   if (windows_major != 0xff)
1140b725ae77Skettenis     {
1141b725ae77Skettenis       const char *windows_flavor;
1142b725ae77Skettenis 
1143b725ae77Skettenis       printf_filtered ("Windows Version................%d.%02d (Windows ",
1144b725ae77Skettenis 		       windows_major, windows_minor);
1145b725ae77Skettenis       switch (windows_major)
1146b725ae77Skettenis 	{
1147b725ae77Skettenis 	  case 3:
1148b725ae77Skettenis 	    windows_flavor = "3.X";
1149b725ae77Skettenis 	    break;
1150b725ae77Skettenis 	  case 4:
1151b725ae77Skettenis 	    switch (windows_minor)
1152b725ae77Skettenis 	      {
1153b725ae77Skettenis 		case 0:
1154b725ae77Skettenis 		  windows_flavor = "95, 95A, or 95B";
1155b725ae77Skettenis 		  break;
1156b725ae77Skettenis 		case 3:
1157b725ae77Skettenis 		  windows_flavor = "95B OSR2.1 or 95C OSR2.5";
1158b725ae77Skettenis 		  break;
1159b725ae77Skettenis 		case 10:
1160b725ae77Skettenis 		  windows_flavor = "98 or 98 SE";
1161b725ae77Skettenis 		  break;
1162b725ae77Skettenis 		case 90:
1163b725ae77Skettenis 		  windows_flavor = "ME";
1164b725ae77Skettenis 		  break;
1165b725ae77Skettenis 		default:
1166b725ae77Skettenis 		  windows_flavor = "9X";
1167b725ae77Skettenis 		  break;
1168b725ae77Skettenis 	      }
1169b725ae77Skettenis 	    break;
1170b725ae77Skettenis 	  default:
1171b725ae77Skettenis 	    windows_flavor = "??";
1172b725ae77Skettenis 	    break;
1173b725ae77Skettenis 	}
1174b725ae77Skettenis       printf_filtered ("%s)\n", windows_flavor);
1175b725ae77Skettenis     }
1176b725ae77Skettenis   else if (true_dos_version == 0x532 && advertized_dos_version == 0x500)
1177b725ae77Skettenis     printf_filtered ("Windows Version................Windows NT or Windows 2000\n");
1178b725ae77Skettenis   puts_filtered ("\n");
1179b725ae77Skettenis   if (dpmi_vendor_available == 0)
1180b725ae77Skettenis     {
1181b725ae77Skettenis       /* The DPMI spec says the vendor string should be ASCIIZ, but
1182b725ae77Skettenis 	 I don't trust the vendors to follow that...  */
1183b725ae77Skettenis       if (!memchr (&dpmi_vendor_info[2], 0, 126))
1184b725ae77Skettenis 	dpmi_vendor_info[128] = '\0';
1185b725ae77Skettenis       printf_filtered ("DPMI Host......................%s v%d.%d (capabilities: %#x)\n",
1186b725ae77Skettenis 		       &dpmi_vendor_info[2],
1187b725ae77Skettenis 		       (unsigned)dpmi_vendor_info[0],
1188b725ae77Skettenis 		       (unsigned)dpmi_vendor_info[1],
1189b725ae77Skettenis 		       ((unsigned)dpmi_flags & 0x7f));
1190b725ae77Skettenis     }
1191b725ae77Skettenis   __dpmi_get_version (&dpmi_version_data);
1192b725ae77Skettenis   printf_filtered ("DPMI Version...................%d.%02d\n",
1193b725ae77Skettenis 		   dpmi_version_data.major, dpmi_version_data.minor);
1194b725ae77Skettenis   printf_filtered ("DPMI Info......................%s-bit DPMI, with%s Virtual Memory support\n",
1195b725ae77Skettenis 		   (dpmi_version_data.flags & 1) ? "32" : "16",
1196b725ae77Skettenis 		   (dpmi_version_data.flags & 4) ? "" : "out");
1197b725ae77Skettenis   printfi_filtered (31, "Interrupts reflected to %s mode\n",
1198b725ae77Skettenis 		   (dpmi_version_data.flags & 2) ? "V86" : "Real");
1199b725ae77Skettenis   printfi_filtered (31, "Processor type: i%d86\n",
1200b725ae77Skettenis 		   dpmi_version_data.cpu);
1201b725ae77Skettenis   printfi_filtered (31, "PIC base interrupt: Master: %#x  Slave: %#x\n",
1202b725ae77Skettenis 		   dpmi_version_data.master_pic, dpmi_version_data.slave_pic);
1203b725ae77Skettenis 
1204b725ae77Skettenis   /* a_tss is only initialized when the debuggee is first run.  */
1205b725ae77Skettenis   if (prog_has_started)
1206b725ae77Skettenis     {
1207b725ae77Skettenis       __asm__ __volatile__ ("pushfl ; popl %0" : "=g" (eflags));
1208b725ae77Skettenis       printf_filtered ("Protection.....................Ring %d (in %s), with%s I/O protection\n",
1209b725ae77Skettenis 		       a_tss.tss_cs & 3, (a_tss.tss_cs & 4) ? "LDT" : "GDT",
1210b725ae77Skettenis 		       (a_tss.tss_cs & 3) > ((eflags >> 12) & 3) ? "" : "out");
1211b725ae77Skettenis     }
1212b725ae77Skettenis   puts_filtered ("\n");
1213b725ae77Skettenis   __dpmi_get_free_memory_information (&mem_info);
1214b725ae77Skettenis   print_mem (mem_info.total_number_of_physical_pages,
1215b725ae77Skettenis 	     "DPMI Total Physical Memory.....", 1);
1216b725ae77Skettenis   print_mem (mem_info.total_number_of_free_pages,
1217b725ae77Skettenis 	     "DPMI Free Physical Memory......", 1);
1218b725ae77Skettenis   print_mem (mem_info.size_of_paging_file_partition_in_pages,
1219b725ae77Skettenis 	     "DPMI Swap Space................", 1);
1220b725ae77Skettenis   print_mem (mem_info.linear_address_space_size_in_pages,
1221b725ae77Skettenis 	     "DPMI Total Linear Address Size.", 1);
1222b725ae77Skettenis   print_mem (mem_info.free_linear_address_space_in_pages,
1223b725ae77Skettenis 	     "DPMI Free Linear Address Size..", 1);
1224b725ae77Skettenis   print_mem (mem_info.largest_available_free_block_in_bytes,
1225b725ae77Skettenis 	     "DPMI Largest Free Memory Block.", 0);
1226b725ae77Skettenis 
1227b725ae77Skettenis   regs.h.ah = 0x48;
1228b725ae77Skettenis   regs.x.bx = 0xffff;
1229b725ae77Skettenis   __dpmi_int (0x21, &regs);
1230b725ae77Skettenis   print_mem (regs.x.bx << 4, "Free DOS Memory................", 0);
1231b725ae77Skettenis   regs.x.ax = 0x5800;
1232b725ae77Skettenis   __dpmi_int (0x21, &regs);
1233b725ae77Skettenis   if ((regs.x.flags & 1) == 0)
1234b725ae77Skettenis     {
1235b725ae77Skettenis       static const char *dos_hilo[] = {
1236b725ae77Skettenis 	"Low", "", "", "", "High", "", "", "", "High, then Low"
1237b725ae77Skettenis       };
1238b725ae77Skettenis       static const char *dos_fit[] = {
1239b725ae77Skettenis 	"First", "Best", "Last"
1240b725ae77Skettenis       };
1241b725ae77Skettenis       int hilo_idx = (regs.x.ax >> 4) & 0x0f;
1242b725ae77Skettenis       int fit_idx  = regs.x.ax & 0x0f;
1243b725ae77Skettenis 
1244b725ae77Skettenis       if (hilo_idx > 8)
1245b725ae77Skettenis 	hilo_idx = 0;
1246b725ae77Skettenis       if (fit_idx > 2)
1247b725ae77Skettenis 	fit_idx = 0;
1248b725ae77Skettenis       printf_filtered ("DOS Memory Allocation..........%s memory, %s fit\n",
1249b725ae77Skettenis 		       dos_hilo[hilo_idx], dos_fit[fit_idx]);
1250b725ae77Skettenis       regs.x.ax = 0x5802;
1251b725ae77Skettenis       __dpmi_int (0x21, &regs);
1252b725ae77Skettenis       if ((regs.x.flags & 1) != 0)
1253b725ae77Skettenis 	regs.h.al = 0;
1254b725ae77Skettenis       printfi_filtered (31, "UMBs %sin DOS memory chain\n",
1255b725ae77Skettenis 			regs.h.al == 0 ? "not " : "");
1256b725ae77Skettenis     }
1257b725ae77Skettenis }
1258b725ae77Skettenis 
1259b725ae77Skettenis struct seg_descr {
1260b725ae77Skettenis   unsigned short limit0          __attribute__((packed));
1261b725ae77Skettenis   unsigned short base0           __attribute__((packed));
1262b725ae77Skettenis   unsigned char  base1           __attribute__((packed));
1263b725ae77Skettenis   unsigned       stype:5         __attribute__((packed));
1264b725ae77Skettenis   unsigned       dpl:2           __attribute__((packed));
1265b725ae77Skettenis   unsigned       present:1       __attribute__((packed));
1266b725ae77Skettenis   unsigned       limit1:4        __attribute__((packed));
1267b725ae77Skettenis   unsigned       available:1     __attribute__((packed));
1268b725ae77Skettenis   unsigned       dummy:1         __attribute__((packed));
1269b725ae77Skettenis   unsigned       bit32:1         __attribute__((packed));
1270b725ae77Skettenis   unsigned       page_granular:1 __attribute__((packed));
1271b725ae77Skettenis   unsigned char  base2           __attribute__((packed));
1272b725ae77Skettenis };
1273b725ae77Skettenis 
1274b725ae77Skettenis struct gate_descr {
1275b725ae77Skettenis   unsigned short offset0         __attribute__((packed));
1276b725ae77Skettenis   unsigned short selector        __attribute__((packed));
1277b725ae77Skettenis   unsigned       param_count:5   __attribute__((packed));
1278b725ae77Skettenis   unsigned       dummy:3         __attribute__((packed));
1279b725ae77Skettenis   unsigned       stype:5         __attribute__((packed));
1280b725ae77Skettenis   unsigned       dpl:2           __attribute__((packed));
1281b725ae77Skettenis   unsigned       present:1       __attribute__((packed));
1282b725ae77Skettenis   unsigned short offset1         __attribute__((packed));
1283b725ae77Skettenis };
1284b725ae77Skettenis 
1285b725ae77Skettenis /* Read LEN bytes starting at logical address ADDR, and put the result
1286b725ae77Skettenis    into DEST.  Return 1 if success, zero if not.  */
1287b725ae77Skettenis static int
read_memory_region(unsigned long addr,void * dest,size_t len)1288b725ae77Skettenis read_memory_region (unsigned long addr, void *dest, size_t len)
1289b725ae77Skettenis {
1290b725ae77Skettenis   unsigned long dos_ds_limit = __dpmi_get_segment_limit (_dos_ds);
1291b725ae77Skettenis   int retval = 1;
1292b725ae77Skettenis 
1293b725ae77Skettenis   /* For the low memory, we can simply use _dos_ds.  */
1294b725ae77Skettenis   if (addr <= dos_ds_limit - len)
1295b725ae77Skettenis     dosmemget (addr, len, dest);
1296b725ae77Skettenis   else
1297b725ae77Skettenis     {
1298b725ae77Skettenis       /* For memory above 1MB we need to set up a special segment to
1299b725ae77Skettenis 	 be able to access that memory.  */
1300b725ae77Skettenis       int sel = __dpmi_allocate_ldt_descriptors (1);
1301b725ae77Skettenis 
1302b725ae77Skettenis       if (sel <= 0)
1303b725ae77Skettenis 	retval = 0;
1304b725ae77Skettenis       else
1305b725ae77Skettenis 	{
1306b725ae77Skettenis 	  int access_rights = __dpmi_get_descriptor_access_rights (sel);
1307b725ae77Skettenis 	  size_t segment_limit = len - 1;
1308b725ae77Skettenis 
1309b725ae77Skettenis 	  /* Make sure the crucial bits in the descriptor access
1310b725ae77Skettenis 	     rights are set correctly.  Some DPMI providers might barf
1311b725ae77Skettenis 	     if we set the segment limit to something that is not an
1312b725ae77Skettenis 	     integral multiple of 4KB pages if the granularity bit is
1313b725ae77Skettenis 	     not set to byte-granular, even though the DPMI spec says
1314b725ae77Skettenis 	     it's the host's responsibility to set that bit correctly.  */
1315b725ae77Skettenis 	  if (len > 1024 * 1024)
1316b725ae77Skettenis 	    {
1317b725ae77Skettenis 	      access_rights |= 0x8000;
1318b725ae77Skettenis 	      /* Page-granular segments should have the low 12 bits of
1319b725ae77Skettenis 		 the limit set.  */
1320b725ae77Skettenis 	      segment_limit |= 0xfff;
1321b725ae77Skettenis 	    }
1322b725ae77Skettenis 	  else
1323b725ae77Skettenis 	    access_rights &= ~0x8000;
1324b725ae77Skettenis 
1325b725ae77Skettenis 	  if (__dpmi_set_segment_base_address (sel, addr) != -1
1326b725ae77Skettenis 	      && __dpmi_set_descriptor_access_rights (sel, access_rights) != -1
1327b725ae77Skettenis 	      && __dpmi_set_segment_limit (sel, segment_limit) != -1
1328b725ae77Skettenis 	      /* W2K silently fails to set the segment limit, leaving
1329b725ae77Skettenis 		 it at zero; this test avoids the resulting crash.  */
1330b725ae77Skettenis 	      && __dpmi_get_segment_limit (sel) >= segment_limit)
1331b725ae77Skettenis 	    movedata (sel, 0, _my_ds (), (unsigned)dest, len);
1332b725ae77Skettenis 	  else
1333b725ae77Skettenis 	    retval = 0;
1334b725ae77Skettenis 
1335b725ae77Skettenis 	  __dpmi_free_ldt_descriptor (sel);
1336b725ae77Skettenis 	}
1337b725ae77Skettenis     }
1338b725ae77Skettenis   return retval;
1339b725ae77Skettenis }
1340b725ae77Skettenis 
1341b725ae77Skettenis /* Get a segment descriptor stored at index IDX in the descriptor
1342b725ae77Skettenis    table whose base address is TABLE_BASE.  Return the descriptor
1343b725ae77Skettenis    type, or -1 if failure.  */
1344b725ae77Skettenis static int
get_descriptor(unsigned long table_base,int idx,void * descr)1345b725ae77Skettenis get_descriptor (unsigned long table_base, int idx, void *descr)
1346b725ae77Skettenis {
1347b725ae77Skettenis   unsigned long addr = table_base + idx * 8; /* 8 bytes per entry */
1348b725ae77Skettenis 
1349b725ae77Skettenis   if (read_memory_region (addr, descr, 8))
1350b725ae77Skettenis     return (int)((struct seg_descr *)descr)->stype;
1351b725ae77Skettenis   return -1;
1352b725ae77Skettenis }
1353b725ae77Skettenis 
1354b725ae77Skettenis struct dtr_reg {
1355b725ae77Skettenis   unsigned short limit __attribute__((packed));
1356b725ae77Skettenis   unsigned long  base  __attribute__((packed));
1357b725ae77Skettenis };
1358b725ae77Skettenis 
1359b725ae77Skettenis /* Display a segment descriptor stored at index IDX in a descriptor
1360b725ae77Skettenis    table whose type is TYPE and whose base address is BASE_ADDR.  If
1361b725ae77Skettenis    FORCE is non-zero, display even invalid descriptors.  */
1362b725ae77Skettenis static void
display_descriptor(unsigned type,unsigned long base_addr,int idx,int force)1363b725ae77Skettenis display_descriptor (unsigned type, unsigned long base_addr, int idx, int force)
1364b725ae77Skettenis {
1365b725ae77Skettenis   struct seg_descr descr;
1366b725ae77Skettenis   struct gate_descr gate;
1367b725ae77Skettenis 
1368b725ae77Skettenis   /* Get the descriptor from the table.  */
1369b725ae77Skettenis   if (idx == 0 && type == 0)
1370b725ae77Skettenis     puts_filtered ("0x000: null descriptor\n");
1371b725ae77Skettenis   else if (get_descriptor (base_addr, idx, &descr) != -1)
1372b725ae77Skettenis     {
1373b725ae77Skettenis       /* For each type of descriptor table, this has a bit set if the
1374b725ae77Skettenis 	 corresponding type of selectors is valid in that table.  */
1375b725ae77Skettenis       static unsigned allowed_descriptors[] = {
1376b725ae77Skettenis 	  0xffffdafeL,   /* GDT */
1377b725ae77Skettenis 	  0x0000c0e0L,   /* IDT */
1378b725ae77Skettenis 	  0xffffdafaL    /* LDT */
1379b725ae77Skettenis       };
1380b725ae77Skettenis 
1381b725ae77Skettenis       /* If the program hasn't started yet, assume the debuggee will
1382b725ae77Skettenis 	 have the same CPL as the debugger.  */
1383b725ae77Skettenis       int cpl = prog_has_started ? (a_tss.tss_cs & 3) : _my_cs () & 3;
1384b725ae77Skettenis       unsigned long limit = (descr.limit1 << 16) | descr.limit0;
1385b725ae77Skettenis 
1386b725ae77Skettenis       if (descr.present
1387b725ae77Skettenis 	  && (allowed_descriptors[type] & (1 << descr.stype)) != 0)
1388b725ae77Skettenis 	{
1389b725ae77Skettenis 	  printf_filtered ("0x%03x: ",
1390b725ae77Skettenis 			   type == 1
1391b725ae77Skettenis 			   ? idx : (idx * 8) | (type ? (cpl | 4) : 0));
1392b725ae77Skettenis 	  if (descr.page_granular)
1393b725ae77Skettenis 	    limit = (limit << 12) | 0xfff; /* big segment: low 12 bit set */
1394b725ae77Skettenis 	  if (descr.stype == 1 || descr.stype == 2 || descr.stype == 3
1395b725ae77Skettenis 	      || descr.stype == 9 || descr.stype == 11
1396b725ae77Skettenis 	      || (descr.stype >= 16 && descr.stype < 32))
1397b725ae77Skettenis 	    printf_filtered ("base=0x%02x%02x%04x limit=0x%08lx",
1398b725ae77Skettenis 			     descr.base2, descr.base1, descr.base0, limit);
1399b725ae77Skettenis 
1400b725ae77Skettenis 	  switch (descr.stype)
1401b725ae77Skettenis 	    {
1402b725ae77Skettenis 	      case 1:
1403b725ae77Skettenis 	      case 3:
1404b725ae77Skettenis 		printf_filtered (" 16-bit TSS  (task %sactive)",
1405b725ae77Skettenis 				 descr.stype == 3 ? "" : "in");
1406b725ae77Skettenis 		break;
1407b725ae77Skettenis 	      case 2:
1408b725ae77Skettenis 		puts_filtered (" LDT");
1409b725ae77Skettenis 		break;
1410b725ae77Skettenis 	      case 4:
1411b725ae77Skettenis 		memcpy (&gate, &descr, sizeof gate);
1412b725ae77Skettenis 		printf_filtered ("selector=0x%04x  offs=0x%04x%04x",
1413b725ae77Skettenis 				 gate.selector, gate.offset1, gate.offset0);
1414b725ae77Skettenis 		printf_filtered (" 16-bit Call Gate (params=%d)",
1415b725ae77Skettenis 				 gate.param_count);
1416b725ae77Skettenis 		break;
1417b725ae77Skettenis 	      case 5:
1418b725ae77Skettenis 		printf_filtered ("TSS selector=0x%04x", descr.base0);
1419b725ae77Skettenis 		printfi_filtered (16, "Task Gate");
1420b725ae77Skettenis 		break;
1421b725ae77Skettenis 	      case 6:
1422b725ae77Skettenis 	      case 7:
1423b725ae77Skettenis 		memcpy (&gate, &descr, sizeof gate);
1424b725ae77Skettenis 		printf_filtered ("selector=0x%04x  offs=0x%04x%04x",
1425b725ae77Skettenis 				 gate.selector, gate.offset1, gate.offset0);
1426b725ae77Skettenis 		printf_filtered (" 16-bit %s Gate",
1427b725ae77Skettenis 				 descr.stype == 6 ? "Interrupt" : "Trap");
1428b725ae77Skettenis 		break;
1429b725ae77Skettenis 	      case 9:
1430b725ae77Skettenis 	      case 11:
1431b725ae77Skettenis 		printf_filtered (" 32-bit TSS (task %sactive)",
1432b725ae77Skettenis 				 descr.stype == 3 ? "" : "in");
1433b725ae77Skettenis 		break;
1434b725ae77Skettenis 	      case 12:
1435b725ae77Skettenis 		memcpy (&gate, &descr, sizeof gate);
1436b725ae77Skettenis 		printf_filtered ("selector=0x%04x  offs=0x%04x%04x",
1437b725ae77Skettenis 				 gate.selector, gate.offset1, gate.offset0);
1438b725ae77Skettenis 		printf_filtered (" 32-bit Call Gate (params=%d)",
1439b725ae77Skettenis 				 gate.param_count);
1440b725ae77Skettenis 		break;
1441b725ae77Skettenis 	      case 14:
1442b725ae77Skettenis 	      case 15:
1443b725ae77Skettenis 		memcpy (&gate, &descr, sizeof gate);
1444b725ae77Skettenis 		printf_filtered ("selector=0x%04x  offs=0x%04x%04x",
1445b725ae77Skettenis 				 gate.selector, gate.offset1, gate.offset0);
1446b725ae77Skettenis 		printf_filtered (" 32-bit %s Gate",
1447b725ae77Skettenis 				 descr.stype == 14 ? "Interrupt" : "Trap");
1448b725ae77Skettenis 		break;
1449b725ae77Skettenis 	      case 16:		/* data segments */
1450b725ae77Skettenis 	      case 17:
1451b725ae77Skettenis 	      case 18:
1452b725ae77Skettenis 	      case 19:
1453b725ae77Skettenis 	      case 20:
1454b725ae77Skettenis 	      case 21:
1455b725ae77Skettenis 	      case 22:
1456b725ae77Skettenis 	      case 23:
1457b725ae77Skettenis 		printf_filtered (" %s-bit Data (%s Exp-%s%s)",
1458b725ae77Skettenis 				 descr.bit32 ? "32" : "16",
1459b725ae77Skettenis 				 descr.stype & 2 ? "Read/Write," : "Read-Only, ",
1460b725ae77Skettenis 				 descr.stype & 4 ? "down" : "up",
1461b725ae77Skettenis 				 descr.stype & 1 ? "" : ", N.Acc");
1462b725ae77Skettenis 		break;
1463b725ae77Skettenis 	      case 24:		/* code segments */
1464b725ae77Skettenis 	      case 25:
1465b725ae77Skettenis 	      case 26:
1466b725ae77Skettenis 	      case 27:
1467b725ae77Skettenis 	      case 28:
1468b725ae77Skettenis 	      case 29:
1469b725ae77Skettenis 	      case 30:
1470b725ae77Skettenis 	      case 31:
1471b725ae77Skettenis 		printf_filtered (" %s-bit Code (%s,  %sConf%s)",
1472b725ae77Skettenis 				 descr.bit32 ? "32" : "16",
1473b725ae77Skettenis 				 descr.stype & 2 ? "Exec/Read" : "Exec-Only",
1474b725ae77Skettenis 				 descr.stype & 4 ? "" : "N.",
1475b725ae77Skettenis 				 descr.stype & 1 ? "" : ", N.Acc");
1476b725ae77Skettenis 		break;
1477b725ae77Skettenis 	      default:
1478b725ae77Skettenis 		printf_filtered ("Unknown type 0x%02x", descr.stype);
1479b725ae77Skettenis 		break;
1480b725ae77Skettenis 	    }
1481b725ae77Skettenis 	  puts_filtered ("\n");
1482b725ae77Skettenis 	}
1483b725ae77Skettenis       else if (force)
1484b725ae77Skettenis 	{
1485b725ae77Skettenis 	  printf_filtered ("0x%03x: ",
1486b725ae77Skettenis 			   type == 1
1487b725ae77Skettenis 			   ? idx : (idx * 8) | (type ? (cpl | 4) : 0));
1488b725ae77Skettenis 	  if (!descr.present)
1489b725ae77Skettenis 	    puts_filtered ("Segment not present\n");
1490b725ae77Skettenis 	  else
1491b725ae77Skettenis 	    printf_filtered ("Segment type 0x%02x is invalid in this table\n",
1492b725ae77Skettenis 			     descr.stype);
1493b725ae77Skettenis 	}
1494b725ae77Skettenis     }
1495b725ae77Skettenis   else if (force)
1496b725ae77Skettenis     printf_filtered ("0x%03x: Cannot read this descriptor\n", idx);
1497b725ae77Skettenis }
1498b725ae77Skettenis 
1499b725ae77Skettenis static void
go32_sldt(char * arg,int from_tty)1500b725ae77Skettenis go32_sldt (char *arg, int from_tty)
1501b725ae77Skettenis {
1502b725ae77Skettenis   struct dtr_reg gdtr;
1503b725ae77Skettenis   unsigned short ldtr = 0;
1504b725ae77Skettenis   int ldt_idx;
1505b725ae77Skettenis   struct seg_descr ldt_descr;
1506b725ae77Skettenis   long ldt_entry = -1L;
1507b725ae77Skettenis   int cpl = (prog_has_started ? a_tss.tss_cs : _my_cs ()) & 3;
1508b725ae77Skettenis 
1509b725ae77Skettenis   if (arg && *arg)
1510b725ae77Skettenis     {
1511b725ae77Skettenis       while (*arg && isspace(*arg))
1512b725ae77Skettenis 	arg++;
1513b725ae77Skettenis 
1514b725ae77Skettenis       if (*arg)
1515b725ae77Skettenis 	{
1516b725ae77Skettenis 	  ldt_entry = parse_and_eval_long (arg);
1517b725ae77Skettenis 	  if (ldt_entry < 0
1518b725ae77Skettenis 	      || (ldt_entry & 4) == 0
1519b725ae77Skettenis 	      || (ldt_entry & 3) != (cpl & 3))
1520b725ae77Skettenis 	    error ("Invalid LDT entry 0x%03lx.", (unsigned long)ldt_entry);
1521b725ae77Skettenis 	}
1522b725ae77Skettenis     }
1523b725ae77Skettenis 
1524b725ae77Skettenis   __asm__ __volatile__ ("sgdt   %0" : "=m" (gdtr) : /* no inputs */ );
1525b725ae77Skettenis   __asm__ __volatile__ ("sldt   %0" : "=m" (ldtr) : /* no inputs */ );
1526b725ae77Skettenis   ldt_idx = ldtr / 8;
1527b725ae77Skettenis   if (ldt_idx == 0)
1528b725ae77Skettenis     puts_filtered ("There is no LDT.\n");
1529b725ae77Skettenis   /* LDT's entry in the GDT must have the type LDT, which is 2.  */
1530b725ae77Skettenis   else if (get_descriptor (gdtr.base, ldt_idx, &ldt_descr) != 2)
1531b725ae77Skettenis     printf_filtered ("LDT is present (at %#x), but unreadable by GDB.\n",
1532b725ae77Skettenis 		     ldt_descr.base0
1533b725ae77Skettenis 		     | (ldt_descr.base1 << 16)
1534b725ae77Skettenis 		     | (ldt_descr.base2 << 24));
1535b725ae77Skettenis   else
1536b725ae77Skettenis     {
1537b725ae77Skettenis       unsigned base =
1538b725ae77Skettenis 	ldt_descr.base0
1539b725ae77Skettenis 	| (ldt_descr.base1 << 16)
1540b725ae77Skettenis 	| (ldt_descr.base2 << 24);
1541b725ae77Skettenis       unsigned limit = ldt_descr.limit0 | (ldt_descr.limit1 << 16);
1542b725ae77Skettenis       int max_entry;
1543b725ae77Skettenis 
1544b725ae77Skettenis       if (ldt_descr.page_granular)
1545b725ae77Skettenis 	/* Page-granular segments must have the low 12 bits of their
1546b725ae77Skettenis 	   limit set.  */
1547b725ae77Skettenis 	limit = (limit << 12) | 0xfff;
1548b725ae77Skettenis       /* LDT cannot have more than 8K 8-byte entries, i.e. more than
1549b725ae77Skettenis 	 64KB.  */
1550b725ae77Skettenis       if (limit > 0xffff)
1551b725ae77Skettenis 	limit = 0xffff;
1552b725ae77Skettenis 
1553b725ae77Skettenis       max_entry = (limit + 1) / 8;
1554b725ae77Skettenis 
1555b725ae77Skettenis       if (ldt_entry >= 0)
1556b725ae77Skettenis 	{
1557b725ae77Skettenis 	  if (ldt_entry > limit)
1558b725ae77Skettenis 	    error ("Invalid LDT entry %#lx: outside valid limits [0..%#x]",
1559b725ae77Skettenis 		   (unsigned long)ldt_entry, limit);
1560b725ae77Skettenis 
1561b725ae77Skettenis 	  display_descriptor (ldt_descr.stype, base, ldt_entry / 8, 1);
1562b725ae77Skettenis 	}
1563b725ae77Skettenis       else
1564b725ae77Skettenis 	{
1565b725ae77Skettenis 	  int i;
1566b725ae77Skettenis 
1567b725ae77Skettenis 	  for (i = 0; i < max_entry; i++)
1568b725ae77Skettenis 	    display_descriptor (ldt_descr.stype, base, i, 0);
1569b725ae77Skettenis 	}
1570b725ae77Skettenis     }
1571b725ae77Skettenis }
1572b725ae77Skettenis 
1573b725ae77Skettenis static void
go32_sgdt(char * arg,int from_tty)1574b725ae77Skettenis go32_sgdt (char *arg, int from_tty)
1575b725ae77Skettenis {
1576b725ae77Skettenis   struct dtr_reg gdtr;
1577b725ae77Skettenis   long gdt_entry = -1L;
1578b725ae77Skettenis   int max_entry;
1579b725ae77Skettenis 
1580b725ae77Skettenis   if (arg && *arg)
1581b725ae77Skettenis     {
1582b725ae77Skettenis       while (*arg && isspace(*arg))
1583b725ae77Skettenis 	arg++;
1584b725ae77Skettenis 
1585b725ae77Skettenis       if (*arg)
1586b725ae77Skettenis 	{
1587b725ae77Skettenis 	  gdt_entry = parse_and_eval_long (arg);
1588b725ae77Skettenis 	  if (gdt_entry < 0 || (gdt_entry & 7) != 0)
1589b725ae77Skettenis 	    error ("Invalid GDT entry 0x%03lx: not an integral multiple of 8.",
1590b725ae77Skettenis 		   (unsigned long)gdt_entry);
1591b725ae77Skettenis 	}
1592b725ae77Skettenis     }
1593b725ae77Skettenis 
1594b725ae77Skettenis   __asm__ __volatile__ ("sgdt   %0" : "=m" (gdtr) : /* no inputs */ );
1595b725ae77Skettenis   max_entry = (gdtr.limit + 1) / 8;
1596b725ae77Skettenis 
1597b725ae77Skettenis   if (gdt_entry >= 0)
1598b725ae77Skettenis     {
1599b725ae77Skettenis       if (gdt_entry > gdtr.limit)
1600b725ae77Skettenis 	error ("Invalid GDT entry %#lx: outside valid limits [0..%#x]",
1601b725ae77Skettenis 	       (unsigned long)gdt_entry, gdtr.limit);
1602b725ae77Skettenis 
1603b725ae77Skettenis       display_descriptor (0, gdtr.base, gdt_entry / 8, 1);
1604b725ae77Skettenis     }
1605b725ae77Skettenis   else
1606b725ae77Skettenis     {
1607b725ae77Skettenis       int i;
1608b725ae77Skettenis 
1609b725ae77Skettenis       for (i = 0; i < max_entry; i++)
1610b725ae77Skettenis 	display_descriptor (0, gdtr.base, i, 0);
1611b725ae77Skettenis     }
1612b725ae77Skettenis }
1613b725ae77Skettenis 
1614b725ae77Skettenis static void
go32_sidt(char * arg,int from_tty)1615b725ae77Skettenis go32_sidt (char *arg, int from_tty)
1616b725ae77Skettenis {
1617b725ae77Skettenis   struct dtr_reg idtr;
1618b725ae77Skettenis   long idt_entry = -1L;
1619b725ae77Skettenis   int max_entry;
1620b725ae77Skettenis 
1621b725ae77Skettenis   if (arg && *arg)
1622b725ae77Skettenis     {
1623b725ae77Skettenis       while (*arg && isspace(*arg))
1624b725ae77Skettenis 	arg++;
1625b725ae77Skettenis 
1626b725ae77Skettenis       if (*arg)
1627b725ae77Skettenis 	{
1628b725ae77Skettenis 	  idt_entry = parse_and_eval_long (arg);
1629b725ae77Skettenis 	  if (idt_entry < 0)
1630b725ae77Skettenis 	    error ("Invalid (negative) IDT entry %ld.", idt_entry);
1631b725ae77Skettenis 	}
1632b725ae77Skettenis     }
1633b725ae77Skettenis 
1634b725ae77Skettenis   __asm__ __volatile__ ("sidt   %0" : "=m" (idtr) : /* no inputs */ );
1635b725ae77Skettenis   max_entry = (idtr.limit + 1) / 8;
1636b725ae77Skettenis   if (max_entry > 0x100)	/* no more than 256 entries */
1637b725ae77Skettenis     max_entry = 0x100;
1638b725ae77Skettenis 
1639b725ae77Skettenis   if (idt_entry >= 0)
1640b725ae77Skettenis     {
1641b725ae77Skettenis       if (idt_entry > idtr.limit)
1642b725ae77Skettenis 	error ("Invalid IDT entry %#lx: outside valid limits [0..%#x]",
1643b725ae77Skettenis 	       (unsigned long)idt_entry, idtr.limit);
1644b725ae77Skettenis 
1645b725ae77Skettenis       display_descriptor (1, idtr.base, idt_entry, 1);
1646b725ae77Skettenis     }
1647b725ae77Skettenis   else
1648b725ae77Skettenis     {
1649b725ae77Skettenis       int i;
1650b725ae77Skettenis 
1651b725ae77Skettenis       for (i = 0; i < max_entry; i++)
1652b725ae77Skettenis 	display_descriptor (1, idtr.base, i, 0);
1653b725ae77Skettenis     }
1654b725ae77Skettenis }
1655b725ae77Skettenis 
1656b725ae77Skettenis /* Cached linear address of the base of the page directory.  For
1657b725ae77Skettenis    now, available only under CWSDPMI.  Code based on ideas and
1658b725ae77Skettenis    suggestions from Charles Sandmann <sandmann@clio.rice.edu>.  */
1659b725ae77Skettenis static unsigned long pdbr;
1660b725ae77Skettenis 
1661b725ae77Skettenis static unsigned long
get_cr3(void)1662b725ae77Skettenis get_cr3 (void)
1663b725ae77Skettenis {
1664b725ae77Skettenis   unsigned offset;
1665b725ae77Skettenis   unsigned taskreg;
1666b725ae77Skettenis   unsigned long taskbase, cr3;
1667b725ae77Skettenis   struct dtr_reg gdtr;
1668b725ae77Skettenis 
1669b725ae77Skettenis   if (pdbr > 0 && pdbr <= 0xfffff)
1670b725ae77Skettenis     return pdbr;
1671b725ae77Skettenis 
1672b725ae77Skettenis   /* Get the linear address of GDT and the Task Register.  */
1673b725ae77Skettenis   __asm__ __volatile__ ("sgdt   %0" : "=m" (gdtr) : /* no inputs */ );
1674b725ae77Skettenis   __asm__ __volatile__ ("str    %0" : "=m" (taskreg) : /* no inputs */ );
1675b725ae77Skettenis 
1676b725ae77Skettenis   /* Task Register is a segment selector for the TSS of the current
1677b725ae77Skettenis      task.  Therefore, it can be used as an index into the GDT to get
1678b725ae77Skettenis      at the segment descriptor for the TSS.  To get the index, reset
1679b725ae77Skettenis      the low 3 bits of the selector (which give the CPL).  Add 2 to the
1680b725ae77Skettenis      offset to point to the 3 low bytes of the base address.  */
1681b725ae77Skettenis   offset = gdtr.base + (taskreg & 0xfff8) + 2;
1682b725ae77Skettenis 
1683b725ae77Skettenis 
1684b725ae77Skettenis   /* CWSDPMI's task base is always under the 1MB mark.  */
1685b725ae77Skettenis   if (offset > 0xfffff)
1686b725ae77Skettenis     return 0;
1687b725ae77Skettenis 
1688b725ae77Skettenis   _farsetsel (_dos_ds);
1689b725ae77Skettenis   taskbase  = _farnspeekl (offset) & 0xffffffU;
1690b725ae77Skettenis   taskbase += _farnspeekl (offset + 2) & 0xff000000U;
1691b725ae77Skettenis   if (taskbase > 0xfffff)
1692b725ae77Skettenis     return 0;
1693b725ae77Skettenis 
1694b725ae77Skettenis   /* CR3 (a.k.a. PDBR, the Page Directory Base Register) is stored at
1695b725ae77Skettenis      offset 1Ch in the TSS.  */
1696b725ae77Skettenis   cr3 = _farnspeekl (taskbase + 0x1c) & ~0xfff;
1697b725ae77Skettenis   if (cr3 > 0xfffff)
1698b725ae77Skettenis     {
1699b725ae77Skettenis #if 0  /* not fullly supported yet */
1700b725ae77Skettenis       /* The Page Directory is in UMBs.  In that case, CWSDPMI puts
1701b725ae77Skettenis 	 the first Page Table right below the Page Directory.  Thus,
1702b725ae77Skettenis 	 the first Page Table's entry for its own address and the Page
1703b725ae77Skettenis 	 Directory entry for that Page Table will hold the same
1704b725ae77Skettenis 	 physical address.  The loop below searches the entire UMB
1705b725ae77Skettenis 	 range of addresses for such an occurence.  */
1706b725ae77Skettenis       unsigned long addr, pte_idx;
1707b725ae77Skettenis 
1708b725ae77Skettenis       for (addr = 0xb0000, pte_idx = 0xb0;
1709b725ae77Skettenis 	   pte_idx < 0xff;
1710b725ae77Skettenis 	   addr += 0x1000, pte_idx++)
1711b725ae77Skettenis 	{
1712b725ae77Skettenis 	  if (((_farnspeekl (addr + 4 * pte_idx) & 0xfffff027) ==
1713b725ae77Skettenis 	       (_farnspeekl (addr + 0x1000) & 0xfffff027))
1714b725ae77Skettenis 	      && ((_farnspeekl (addr + 4 * pte_idx + 4) & 0xfffff000) == cr3))
1715b725ae77Skettenis 	    {
1716b725ae77Skettenis 	      cr3 = addr + 0x1000;
1717b725ae77Skettenis 	      break;
1718b725ae77Skettenis 	    }
1719b725ae77Skettenis 	}
1720b725ae77Skettenis #endif
1721b725ae77Skettenis 
1722b725ae77Skettenis       if (cr3 > 0xfffff)
1723b725ae77Skettenis 	cr3 = 0;
1724b725ae77Skettenis     }
1725b725ae77Skettenis 
1726b725ae77Skettenis   return cr3;
1727b725ae77Skettenis }
1728b725ae77Skettenis 
1729b725ae77Skettenis /* Return the N'th Page Directory entry.  */
1730b725ae77Skettenis static unsigned long
get_pde(int n)1731b725ae77Skettenis get_pde (int n)
1732b725ae77Skettenis {
1733b725ae77Skettenis   unsigned long pde = 0;
1734b725ae77Skettenis 
1735b725ae77Skettenis   if (pdbr && n >= 0 && n < 1024)
1736b725ae77Skettenis     {
1737b725ae77Skettenis       pde = _farpeekl (_dos_ds, pdbr + 4*n);
1738b725ae77Skettenis     }
1739b725ae77Skettenis   return pde;
1740b725ae77Skettenis }
1741b725ae77Skettenis 
1742b725ae77Skettenis /* Return the N'th entry of the Page Table whose Page Directory entry
1743b725ae77Skettenis    is PDE.  */
1744b725ae77Skettenis static unsigned long
get_pte(unsigned long pde,int n)1745b725ae77Skettenis get_pte (unsigned long pde, int n)
1746b725ae77Skettenis {
1747b725ae77Skettenis   unsigned long pte = 0;
1748b725ae77Skettenis 
1749b725ae77Skettenis   /* pde & 0x80 tests the 4MB page bit.  We don't support 4MB
1750b725ae77Skettenis      page tables, for now.  */
1751b725ae77Skettenis   if ((pde & 1) && !(pde & 0x80) && n >= 0 && n < 1024)
1752b725ae77Skettenis     {
1753b725ae77Skettenis       pde &= ~0xfff;	/* clear non-address bits */
1754b725ae77Skettenis       pte = _farpeekl (_dos_ds, pde + 4*n);
1755b725ae77Skettenis     }
1756b725ae77Skettenis   return pte;
1757b725ae77Skettenis }
1758b725ae77Skettenis 
1759b725ae77Skettenis /* Display a Page Directory or Page Table entry.  IS_DIR, if non-zero,
1760b725ae77Skettenis    says this is a Page Directory entry.  If FORCE is non-zero, display
1761b725ae77Skettenis    the entry even if its Present flag is off.  OFF is the offset of the
1762b725ae77Skettenis    address from the page's base address.  */
1763b725ae77Skettenis static void
display_ptable_entry(unsigned long entry,int is_dir,int force,unsigned off)1764b725ae77Skettenis display_ptable_entry (unsigned long entry, int is_dir, int force, unsigned off)
1765b725ae77Skettenis {
1766b725ae77Skettenis   if ((entry & 1) != 0)
1767b725ae77Skettenis     {
1768b725ae77Skettenis       printf_filtered ("Base=0x%05lx000", entry >> 12);
1769b725ae77Skettenis       if ((entry & 0x100) && !is_dir)
1770b725ae77Skettenis 	puts_filtered (" Global");
1771b725ae77Skettenis       if ((entry & 0x40) && !is_dir)
1772b725ae77Skettenis 	puts_filtered (" Dirty");
1773b725ae77Skettenis       printf_filtered (" %sAcc.", (entry & 0x20) ? "" : "Not-");
1774b725ae77Skettenis       printf_filtered (" %sCached", (entry & 0x10) ? "" : "Not-");
1775b725ae77Skettenis       printf_filtered (" Write-%s", (entry & 8) ? "Thru" : "Back");
1776b725ae77Skettenis       printf_filtered (" %s", (entry & 4) ? "Usr" : "Sup");
1777b725ae77Skettenis       printf_filtered (" Read-%s", (entry & 2) ? "Write" : "Only");
1778b725ae77Skettenis       if (off)
1779b725ae77Skettenis 	printf_filtered (" +0x%x", off);
1780b725ae77Skettenis       puts_filtered ("\n");
1781b725ae77Skettenis     }
1782b725ae77Skettenis   else if (force)
1783b725ae77Skettenis     printf_filtered ("Page%s not present or not supported; value=0x%lx.\n",
1784b725ae77Skettenis 		     is_dir ? " Table" : "", entry >> 1);
1785b725ae77Skettenis }
1786b725ae77Skettenis 
1787b725ae77Skettenis static void
go32_pde(char * arg,int from_tty)1788b725ae77Skettenis go32_pde (char *arg, int from_tty)
1789b725ae77Skettenis {
1790b725ae77Skettenis   long pde_idx = -1, i;
1791b725ae77Skettenis 
1792b725ae77Skettenis   if (arg && *arg)
1793b725ae77Skettenis     {
1794b725ae77Skettenis       while (*arg && isspace(*arg))
1795b725ae77Skettenis 	arg++;
1796b725ae77Skettenis 
1797b725ae77Skettenis       if (*arg)
1798b725ae77Skettenis 	{
1799b725ae77Skettenis 	  pde_idx = parse_and_eval_long (arg);
1800b725ae77Skettenis 	  if (pde_idx < 0 || pde_idx >= 1024)
1801b725ae77Skettenis 	    error ("Entry %ld is outside valid limits [0..1023].", pde_idx);
1802b725ae77Skettenis 	}
1803b725ae77Skettenis     }
1804b725ae77Skettenis 
1805b725ae77Skettenis   pdbr = get_cr3 ();
1806b725ae77Skettenis   if (!pdbr)
1807b725ae77Skettenis     puts_filtered ("Access to Page Directories is not supported on this system.\n");
1808b725ae77Skettenis   else if (pde_idx >= 0)
1809b725ae77Skettenis     display_ptable_entry (get_pde (pde_idx), 1, 1, 0);
1810b725ae77Skettenis   else
1811b725ae77Skettenis     for (i = 0; i < 1024; i++)
1812b725ae77Skettenis       display_ptable_entry (get_pde (i), 1, 0, 0);
1813b725ae77Skettenis }
1814b725ae77Skettenis 
1815b725ae77Skettenis /* A helper function to display entries in a Page Table pointed to by
1816b725ae77Skettenis    the N'th entry in the Page Directory.  If FORCE is non-zero, say
1817b725ae77Skettenis    something even if the Page Table is not accessible.  */
1818b725ae77Skettenis static void
display_page_table(long n,int force)1819b725ae77Skettenis display_page_table (long n, int force)
1820b725ae77Skettenis {
1821b725ae77Skettenis   unsigned long pde = get_pde (n);
1822b725ae77Skettenis 
1823b725ae77Skettenis   if ((pde & 1) != 0)
1824b725ae77Skettenis     {
1825b725ae77Skettenis       int i;
1826b725ae77Skettenis 
1827b725ae77Skettenis       printf_filtered ("Page Table pointed to by Page Directory entry 0x%lx:\n", n);
1828b725ae77Skettenis       for (i = 0; i < 1024; i++)
1829b725ae77Skettenis 	display_ptable_entry (get_pte (pde, i), 0, 0, 0);
1830b725ae77Skettenis       puts_filtered ("\n");
1831b725ae77Skettenis     }
1832b725ae77Skettenis   else if (force)
1833b725ae77Skettenis     printf_filtered ("Page Table not present; value=0x%lx.\n", pde >> 1);
1834b725ae77Skettenis }
1835b725ae77Skettenis 
1836b725ae77Skettenis static void
go32_pte(char * arg,int from_tty)1837b725ae77Skettenis go32_pte (char *arg, int from_tty)
1838b725ae77Skettenis {
1839b725ae77Skettenis   long pde_idx = -1L, i;
1840b725ae77Skettenis 
1841b725ae77Skettenis   if (arg && *arg)
1842b725ae77Skettenis     {
1843b725ae77Skettenis       while (*arg && isspace(*arg))
1844b725ae77Skettenis 	arg++;
1845b725ae77Skettenis 
1846b725ae77Skettenis       if (*arg)
1847b725ae77Skettenis 	{
1848b725ae77Skettenis 	  pde_idx = parse_and_eval_long (arg);
1849b725ae77Skettenis 	  if (pde_idx < 0 || pde_idx >= 1024)
1850b725ae77Skettenis 	    error ("Entry %ld is outside valid limits [0..1023].", pde_idx);
1851b725ae77Skettenis 	}
1852b725ae77Skettenis     }
1853b725ae77Skettenis 
1854b725ae77Skettenis   pdbr = get_cr3 ();
1855b725ae77Skettenis   if (!pdbr)
1856b725ae77Skettenis     puts_filtered ("Access to Page Tables is not supported on this system.\n");
1857b725ae77Skettenis   else if (pde_idx >= 0)
1858b725ae77Skettenis     display_page_table (pde_idx, 1);
1859b725ae77Skettenis   else
1860b725ae77Skettenis     for (i = 0; i < 1024; i++)
1861b725ae77Skettenis       display_page_table (i, 0);
1862b725ae77Skettenis }
1863b725ae77Skettenis 
1864b725ae77Skettenis static void
go32_pte_for_address(char * arg,int from_tty)1865b725ae77Skettenis go32_pte_for_address (char *arg, int from_tty)
1866b725ae77Skettenis {
1867b725ae77Skettenis   CORE_ADDR addr = 0, i;
1868b725ae77Skettenis 
1869b725ae77Skettenis   if (arg && *arg)
1870b725ae77Skettenis     {
1871b725ae77Skettenis       while (*arg && isspace(*arg))
1872b725ae77Skettenis 	arg++;
1873b725ae77Skettenis 
1874b725ae77Skettenis       if (*arg)
1875b725ae77Skettenis 	addr = parse_and_eval_address (arg);
1876b725ae77Skettenis     }
1877b725ae77Skettenis   if (!addr)
1878b725ae77Skettenis     error_no_arg ("linear address");
1879b725ae77Skettenis 
1880b725ae77Skettenis   pdbr = get_cr3 ();
1881b725ae77Skettenis   if (!pdbr)
1882b725ae77Skettenis     puts_filtered ("Access to Page Tables is not supported on this system.\n");
1883b725ae77Skettenis   else
1884b725ae77Skettenis     {
1885b725ae77Skettenis       int pde_idx = (addr >> 22) & 0x3ff;
1886b725ae77Skettenis       int pte_idx = (addr >> 12) & 0x3ff;
1887b725ae77Skettenis       unsigned offs = addr & 0xfff;
1888b725ae77Skettenis 
1889b725ae77Skettenis       printf_filtered ("Page Table entry for address 0x%llx:\n",
1890b725ae77Skettenis 		       (unsigned long long)addr);
1891b725ae77Skettenis       display_ptable_entry (get_pte (get_pde (pde_idx), pte_idx), 0, 1, offs);
1892b725ae77Skettenis     }
1893b725ae77Skettenis }
1894b725ae77Skettenis 
1895b725ae77Skettenis static struct cmd_list_element *info_dos_cmdlist = NULL;
1896b725ae77Skettenis 
1897b725ae77Skettenis static void
go32_info_dos_command(char * args,int from_tty)1898b725ae77Skettenis go32_info_dos_command (char *args, int from_tty)
1899b725ae77Skettenis {
1900b725ae77Skettenis   help_list (info_dos_cmdlist, "info dos ", class_info, gdb_stdout);
1901b725ae77Skettenis }
1902b725ae77Skettenis 
1903b725ae77Skettenis void
_initialize_go32_nat(void)1904b725ae77Skettenis _initialize_go32_nat (void)
1905b725ae77Skettenis {
1906b725ae77Skettenis   init_go32_ops ();
1907b725ae77Skettenis   add_target (&go32_ops);
1908b725ae77Skettenis 
1909b725ae77Skettenis   add_prefix_cmd ("dos", class_info, go32_info_dos_command,
1910b725ae77Skettenis 		  "Print information specific to DJGPP (aka MS-DOS) debugging.",
1911b725ae77Skettenis 		  &info_dos_cmdlist, "info dos ", 0, &infolist);
1912b725ae77Skettenis 
1913b725ae77Skettenis   add_cmd ("sysinfo", class_info, go32_sysinfo,
1914b725ae77Skettenis 	    "Display information about the target system, including CPU, OS, DPMI, etc.",
1915b725ae77Skettenis 	   &info_dos_cmdlist);
1916b725ae77Skettenis   add_cmd ("ldt", class_info, go32_sldt,
1917b725ae77Skettenis 	   "Display entries in the LDT (Local Descriptor Table).\n"
1918b725ae77Skettenis 	   "Entry number (an expression) as an argument means display only that entry.",
1919b725ae77Skettenis 	   &info_dos_cmdlist);
1920b725ae77Skettenis   add_cmd ("gdt", class_info, go32_sgdt,
1921b725ae77Skettenis 	   "Display entries in the GDT (Global Descriptor Table).\n"
1922b725ae77Skettenis 	   "Entry number (an expression) as an argument means display only that entry.",
1923b725ae77Skettenis 	   &info_dos_cmdlist);
1924b725ae77Skettenis   add_cmd ("idt", class_info, go32_sidt,
1925b725ae77Skettenis 	   "Display entries in the IDT (Interrupt Descriptor Table).\n"
1926b725ae77Skettenis 	   "Entry number (an expression) as an argument means display only that entry.",
1927b725ae77Skettenis 	   &info_dos_cmdlist);
1928b725ae77Skettenis   add_cmd ("pde", class_info, go32_pde,
1929b725ae77Skettenis 	   "Display entries in the Page Directory.\n"
1930b725ae77Skettenis 	   "Entry number (an expression) as an argument means display only that entry.",
1931b725ae77Skettenis 	   &info_dos_cmdlist);
1932b725ae77Skettenis   add_cmd ("pte", class_info, go32_pte,
1933b725ae77Skettenis 	   "Display entries in Page Tables.\n"
1934b725ae77Skettenis 	   "Entry number (an expression) as an argument means display only entries\n"
1935b725ae77Skettenis 	   "from the Page Table pointed to by the specified Page Directory entry.",
1936b725ae77Skettenis 	   &info_dos_cmdlist);
1937b725ae77Skettenis   add_cmd ("address-pte", class_info, go32_pte_for_address,
1938b725ae77Skettenis 	   "Display a Page Table entry for a linear address.\n"
1939b725ae77Skettenis 	   "The address argument must be a linear address, after adding to\n"
1940b725ae77Skettenis 	   "it the base address of the appropriate segment.\n"
1941b725ae77Skettenis 	   "The base address of variables and functions in the debuggee's data\n"
1942b725ae77Skettenis 	   "or code segment is stored in the variable __djgpp_base_address,\n"
1943b725ae77Skettenis 	   "so use `__djgpp_base_address + (char *)&var' as the argument.\n"
1944b725ae77Skettenis 	   "For other segments, look up their base address in the output of\n"
1945b725ae77Skettenis 	   "the `info dos ldt' command.",
1946b725ae77Skettenis 	   &info_dos_cmdlist);
1947b725ae77Skettenis }
1948b725ae77Skettenis 
1949b725ae77Skettenis pid_t
tcgetpgrp(int fd)1950b725ae77Skettenis tcgetpgrp (int fd)
1951b725ae77Skettenis {
1952b725ae77Skettenis   if (isatty (fd))
1953b725ae77Skettenis     return SOME_PID;
1954b725ae77Skettenis   errno = ENOTTY;
1955b725ae77Skettenis   return -1;
1956b725ae77Skettenis }
1957b725ae77Skettenis 
1958b725ae77Skettenis int
tcsetpgrp(int fd,pid_t pgid)1959b725ae77Skettenis tcsetpgrp (int fd, pid_t pgid)
1960b725ae77Skettenis {
1961b725ae77Skettenis   if (isatty (fd) && pgid == SOME_PID)
1962b725ae77Skettenis     return 0;
1963b725ae77Skettenis   errno = pgid == SOME_PID ? ENOTTY : ENOSYS;
1964b725ae77Skettenis   return -1;
1965b725ae77Skettenis }
1966