Subject: port-i386/2414: i386 ptrace(2)/gdb floating register fix
To: None <gnats-bugs@NetBSD.ORG>
From: Noriyuki Soda <soda@sra.co.jp>
List: netbsd-bugs
Date: 05/15/1996 11:22:10
>Number:         2414
>Category:       port-i386
>Synopsis:       i386 ptrace(2)/gdb floating register fix
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    gnats-admin (GNATS administrator)
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Tue May 14 22:50:02 1996
>Last-Modified:
>Originator:     Noriyuki Soda
>Organization:
	Software Research Associates, Inc., Japan
>Release:        1.1B (-current May, 1996)
>Environment:
System: NetBSD james 1.1B NetBSD 1.1B (PALM) #29: Wed May 15 09:43:33 JST 1996 soda@james:/usr/src/sys/arch/i386/compile/PALM i386

>Description:

	sys/arch/i386/i386/process_machdep.c has several problems about
	floating register treatment, that is
	- missing #include "npx.h", so that #if NNPX > 0 block  is never
	  used.
	- floating register's bzero/bcopy sizes (sizeof(frame), sizeof(regs)) 
	  are incorrect. these should be sizeof(*regs)
	- NPX control word and tag word is not correctly initialized.

	This patch fixes these problems, and also makes initial	value
	of NPX control word correspond to OS emulation mode.
	But initial value of SVR4 and iBCS2 emulation mode is not sure
	(because I don't have these OSs), please fix initial_npxcw()
	in i386/isa/npx.c, if necessary.

	Fix to process_write_fpregs() is a bit problematic, because 
	it doesn't store whole contents of process_fpframe(p).
	But I think that this fix is better than current source.

	This PR also includes fix to GDB floating register handling.	  

>How-To-Repeat:
	% gdb <program-which-use-floating-register>
	(gdb) info float

>Fix:

[patch to kernel]

------------------------------------------------------------------------
diff -ur /usr/src-org/sys/arch/i386/include/cpu.h sys/arch/i386/include/cpu.h
--- /usr/src-org/sys/arch/i386/include/cpu.h	Mon May  6 20:31:07 1996
+++ sys/arch/i386/include/cpu.h	Wed May 15 10:17:16 1996
@@ -109,6 +109,7 @@
 extern int cpu;
 extern int cpu_class;
 extern struct cpu_nameclass i386_cpus[];
+extern struct proc *npxproc;
 
 /* autoconf.c */
 void	configure __P((void));
@@ -132,6 +133,7 @@
 void	startrtclock __P((void));
 
 /* npx.c */
+u_short	initial_npxcw __P((struct proc *p));
 void	npxdrop __P((void));
 void	npxsave __P((void));
 
diff -ur /usr/src-org/sys/arch/i386/include/npx.h sys/arch/i386/include/npx.h
--- /usr/src-org/sys/arch/i386/include/npx.h	Sat Oct 14 10:57:44 1995
+++ sys/arch/i386/include/npx.h	Wed May 15 10:17:13 1996
@@ -90,6 +90,8 @@
 
 /* Intel prefers long real (53 bit) precision */
 #define	__iBCS_NPXCW__		0x262
+#define	__LINUX_NPXCW__		0x37f	/* `fninit' instruction default */
+#define	__FreeBSD_NPXCW__	0x1272	/* __BDE_NPXCW__ */
 #define	__NetBSD_NPXCW__	0x127f
 
 /*
diff -ur /usr/src-org/sys/arch/i386/isa/npx.c sys/arch/i386/isa/npx.c
--- /usr/src-org/sys/arch/i386/isa/npx.c	Sat May  4 20:47:22 1996
+++ sys/arch/i386/isa/npx.c	Wed May 15 10:10:09 1996
@@ -63,6 +63,19 @@
 #include <dev/isa/isavar.h>
 #include <i386/isa/icu.h>
 
+#ifdef COMPAT_SVR4
+extern struct emul emul_svr4;
+#endif
+#ifdef COMPAT_IBCS2
+extern struct emul emul_ibcs2;
+#endif
+#ifdef COMPAT_LINUX
+extern struct emul emul_linux_aout, emul_linux_elf;
+#endif
+#ifdef COMPAT_FREEBSD
+extern struct emul emul_freebsd;
+#endif
+
 /*
  * 387 and 287 Numeric Coprocessor Extension (NPX) Driver.
  *
@@ -490,6 +503,32 @@
 }
 
 /*
+ * Initial value of npx control word.
+ */
+u_short
+initial_npxcw(p)
+	struct proc *p;
+{
+#ifdef COMPAT_SVR4
+	if (p->p_emul == &emul_svr4)
+		return (__iBCS_NPXCW__);	/* XXX - not sure */
+#endif
+#ifdef COMPAT_IBCS2
+	if (p->p_emul == &emul_ibcs2)
+		return (__iBCS_NPXCW__);	/* XXX - not sure */
+#endif
+#ifdef COMPAT_LINUX
+	if (p->p_emul == &emul_linux_aout || p->p_emul == &emul_linux_elf)
+		return (__LINUX_NPXCW__);
+#endif
+#ifdef COMPAT_FREEBSD
+	if (p->p_emul == &emul_freebsd)
+		return (__FreeBSD_NPXCW__);
+#endif
+	return (__INITIAL_NPXCW__);
+}
+
+/*
  * Implement device not available (DNA) exception
  *
  * If the we were the last process to use the FPU, we can simply return.
@@ -500,8 +539,6 @@
 npxdna(p)
 	struct proc *p;
 {
-	static u_short control = __INITIAL_NPXCW__;
-
 	if (npx_type == NPX_NONE) {
 		iprintf(("Emul"));
 		return (0);
@@ -516,6 +553,8 @@
 	clts();
 
 	if ((p->p_md.md_flags & MDP_USEDFPU) == 0) {
+		u_short control = initial_npxcw(p);
+
 		p->p_md.md_flags |= MDP_USEDFPU;
 		iprintf(("Init"));
 		if (npxproc != 0 && npxproc != p)
diff -ur /usr/src-org/sys/arch/i386/i386/process_machdep.c sys/arch/i386/i386/process_machdep.c
--- /usr/src-org/sys/arch/i386/i386/process_machdep.c	Sat May  4 20:47:09 1996
+++ sys/arch/i386/i386/process_machdep.c	Wed May 15 10:22:55 1996
@@ -72,6 +72,7 @@
 #include <sys/vnode.h>
 #include <sys/ptrace.h>
 
+#include <machine/cpu.h>
 #include <machine/psl.h>
 #include <machine/reg.h>
 #include <machine/segments.h>
@@ -80,6 +81,8 @@
 #include <machine/vm86.h>
 #endif
 
+#include "npx.h"
+
 static __inline struct trapframe *process_frame __P((struct proc *));
 static __inline struct save87 *process_fpframe __P((struct proc *));
 
@@ -143,7 +146,6 @@
 	struct proc *p;
 	struct fpreg *regs;
 {
-
 	if (p->p_md.md_flags & MDP_USEDFPU) {
 		struct save87 *frame = process_fpframe(p);
 
@@ -152,9 +154,15 @@
 			npxsave();
 #endif
 
-		bcopy(frame, regs, sizeof(frame));
-	} else
-		bzero(regs, sizeof(regs));
+		bcopy(frame, regs, sizeof(*regs));
+	} else {
+		struct save87 *regs87 = (struct save87 *)regs;
+
+		/* same effect as `fninit' instruction */
+		bzero(regs, sizeof(*regs));
+		regs87->sv_env.en_tw = 0xffff;
+		regs87->sv_env.en_cw = initial_npxcw(p);
+	}
 
 	return (0);
 }
@@ -240,7 +248,11 @@
 #endif
 
 	p->p_md.md_flags |= MDP_USEDFPU;
-	bcopy(regs, frame, sizeof(frame));
+	/*
+	 * XXX - not all NPX information is stored,
+	 * especially struct save87::sv_ex_sw, sv_ex_tw is not set.
+	 */
+	bcopy(regs, frame, sizeof(*regs));
 
 	return (0);
 }
------------------------------------------------------------------------

[patch to gdb]

------------------------------------------------------------------------
diff -ur /usr/src-org/gnu/usr.bin/gdb/gdb/arch/i386/i386b-nat.c gnu/usr.bin/gdb/gdb/arch/i386/i386b-nat.c
--- /usr/src-org/gnu/usr.bin/gdb/gdb/arch/i386/i386b-nat.c	Thu Mar  7 21:13:22 1996
+++ gnu/usr.bin/gdb/gdb/arch/i386/i386b-nat.c	Wed May 15 07:49:01 1996
@@ -62,6 +62,9 @@
 	struct fpreg freg;
 };
 
+static struct fpreg i386_fp_registers;	/* for i386_float_info() */
+static int i386_fp_read = 0;
+
 void
 fetch_core_registers (core_reg_sect, core_reg_size, which, ignore)
      char *core_reg_sect;
@@ -73,24 +76,16 @@
 
   core_reg = (struct md_core *)core_reg_sect;
 
-  switch (which) {
-  case 0:
-    /* integer registers */
-    memcpy(&registers[REGISTER_BYTE (0)], &core_reg->intreg,
-	   sizeof(struct reg));
-    break;
-  case 2:
-    /* floating point registers */
-    /* XXX */
-  default:
-    printf("fetch_core_registers: invalid `which' argument\n");
-    break;
-  }
-}
+  /* We have *all* registers in the first core section. Ignore which. */
 
-#if 0
-#define	fpstate		save87
-#define	U_FPSTATE(u)	u.u_pcb.pcb_savefpu
+  /* integer registers */
+  memcpy(&registers[REGISTER_BYTE (0)], &core_reg->intreg,
+	 sizeof(struct reg));
+
+  /* floating point registers */
+  i386_fp_registers = core_reg->freg; /* save it for i386_float_info() */
+  i386_fp_read = 1;
+}
 
 static void
 i387_to_double (from, to)
@@ -101,7 +96,7 @@
   /* push extended mode on 387 stack, then pop in double mode
    *
    * first, set exception masks so no error is generated -
-   * number will be rounded to inf or 0, if necessary 
+   * number will be rounded to inf or 0, if necessary
    */
   asm ("pushl %eax"); 		/* grab a stack slot */
   asm ("fstcw (%esp)");		/* get 387 control word */
@@ -109,20 +104,21 @@
   asm ("orl $0x3f,%eax");		/* mask all exceptions */
   asm ("pushl %eax");
   asm ("fldcw (%esp)");		/* load new value into 387 */
-  
+
   asm ("movl 8(%ebp),%eax");
   asm ("fldt (%eax)");		/* push extended number on 387 stack */
   asm ("fwait");
   asm ("movl 12(%ebp),%eax");
   asm ("fstpl (%eax)");		/* pop double */
   asm ("fwait");
-  
+
   asm ("popl %eax");		/* flush modified control word */
   asm ("fnclex");			/* clear exceptions */
   asm ("fldcw (%esp)");		/* restore original control word */
   asm ("popl %eax");		/* flush saved copy */
 }
 
+#if 0
 static void
 double_to_i387 (from, to)
      char *from;
@@ -139,8 +135,9 @@
   asm ("fstpt (%eax)");
   asm ("fwait");
 }
+#endif
 
-struct env387 
+struct env387
 {
   unsigned short control;
   unsigned short r0;
@@ -163,7 +160,7 @@
 {
   printf ("control 0x%04x: ", control);
   printf ("compute to ");
-  switch ((control >> 8) & 3) 
+  switch ((control >> 8) & 3)
     {
     case 0: printf ("24 bits; "); break;
     case 1: printf ("(bad); "); break;
@@ -171,14 +168,14 @@
     case 3: printf ("64 bits; "); break;
     }
   printf ("round ");
-  switch ((control >> 10) & 3) 
+  switch ((control >> 10) & 3)
     {
     case 0: printf ("NEAREST; "); break;
     case 1: printf ("DOWN; "); break;
     case 2: printf ("UP; "); break;
     case 3: printf ("CHOP; "); break;
     }
-  if (control & 0x3f) 
+  if (control & 0x3f)
     {
       printf ("mask:");
       if (control & 0x0001) printf (" INVALID");
@@ -199,7 +196,7 @@
      unsigned int status;
 {
   printf ("status 0x%04x: ", status);
-  if (status & 0xff) 
+  if (status & 0xff)
     {
       printf ("exceptions:");
       if (status & 0x0001) printf (" INVALID");
@@ -216,7 +213,7 @@
 	  (status & 0x0400) != 0,
 	  (status & 0x0200) != 0,
 	  (status & 0x0100) != 0);
-  
+
   printf ("top %d\n", (status >> 11) & 7);
 }
 
@@ -230,44 +227,44 @@
   int top;
   int fpreg;
   unsigned char *p;
-  
+
   bothstatus = ((status != 0) && (ep->status != 0));
-  if (status != 0) 
+  if (status != 0)
     {
       if (bothstatus)
 	printf ("u: ");
       print_387_status_word ((unsigned int)status);
     }
-  
-  if (ep->status != 0) 
+
+  if (ep->status != 0)
     {
       if (bothstatus)
 	printf ("e: ");
       print_387_status_word ((unsigned int)ep->status);
     }
-  
+
   print_387_control_word ((unsigned int)ep->control);
   printf ("last exception: ");
   printf ("opcode 0x%x; ", ep->opcode);
   printf ("pc 0x%x:0x%x; ", ep->code_seg, ep->eip);
   printf ("operand 0x%x:0x%x\n", ep->operand_seg, ep->operand);
-  
+
   top = (ep->status >> 11) & 7;
-  
+
   printf (" regno     tag  msb              lsb  value\n");
-  for (fpreg = 7; fpreg >= 0; fpreg--) 
+  for (fpreg = 7; fpreg >= 0; fpreg--)
     {
       int st_regno;
       double val;
-      
+
       /* The physical regno `fpreg' is only relevant as an index into the
-       * tag word.  Logical `%st' numbers are required for indexing `p->regs.
+       * tag word.  Logical `%st' numbers are required for indexing ep->regs.
        */
-      st_regno = (fpreg + 8 - top) & 0x7;
+      st_regno = (fpreg + 8 - top) & 7;
 
       printf ("%%st(%d) %s ", st_regno, fpreg == top ? "=>" : "  ");
-      
-      switch ((ep->tag >> (fpreg * 2)) & 3) 
+
+      switch ((ep->tag >> (fpreg * 2)) & 3)
 	{
 	case 0: printf ("valid "); break;
 	case 1: printf ("zero  "); break;
@@ -276,7 +273,7 @@
 	}
       for (i = 9; i >= 0; i--)
 	printf ("%02x", ep->regs[st_regno][i]);
-      
+
       i387_to_double (ep->regs[st_regno], (char *)&val);
       printf ("  %g\n", val);
     }
@@ -285,53 +282,27 @@
 void
 i386_float_info ()
 {
-  struct user u; /* just for address computations */
-  int i;
-  /* fpstate defined in <sys/user.h> */
-  struct fpstate *fpstatep;
-  char buf[sizeof (struct fpstate) + 2 * sizeof (int)];
-  unsigned int uaddr;
-  char fpvalid;
-  unsigned int rounded_addr;
-  unsigned int rounded_size;
-  /*extern int corechan;*/
-  int skip;
+  struct fpreg fpstate;
   extern int inferior_pid;
-  
-  uaddr = (char *)&U_FPSTATE(u) - (char *)&u;
-  if (inferior_pid) 
+
+  if (inferior_pid)
     {
-      int *ip;
-      
-      rounded_addr = uaddr & -sizeof (int);
-      rounded_size = (((uaddr + sizeof (struct fpstate)) - uaddr) +
-		      sizeof (int) - 1) / sizeof (int);
-      skip = uaddr - rounded_addr;
-      
-      ip = (int *)buf;
-      for (i = 0; i < rounded_size; i++) 
-	{
-	  *ip++ = ptrace (PT_READ_U, inferior_pid, (caddr_t)rounded_addr, 0);
-	  rounded_addr += sizeof (int);
-	}
-    } 
-  else 
+      ptrace (PT_GETFPREGS, inferior_pid, (caddr_t)&fpstate, 0);
+    }
+  else if (i386_fp_read)
     {
-		 printf("float info: can't do a core file (yet)\n");
-		 
-		 return;
-#if 0
-      if (lseek (corechan, uaddr, 0) < 0)
-	perror_with_name ("seek on core file");
-      if (myread (corechan, buf, sizeof (struct fpstate)) < 0) 
-	perror_with_name ("read from core file");
-      skip = 0;
-#endif
+      fpstate = i386_fp_registers;
+    }
+  else
+    {
+      error ("The program has no floating registers now.");
     }
-  
-  print_387_status (0, (struct env387 *)buf);
+
+  /* cannot pass `((struct save87 *)&fpstate)->sv_ex_sw' as first argument, 
+   * because PT_GETFPREGS doesn't return it.
+   */
+  print_387_status (0, (struct env387 *)&fpstate);
 }
-#endif
 
 void
 fetch_kcore_registers(pcb)
diff -ur /usr/src-org/gnu/usr.bin/gdb/gdb/arch/i386/nm.h gnu/usr.bin/gdb/gdb/arch/i386/nm.h
--- /usr/src-org/gnu/usr.bin/gdb/gdb/arch/i386/nm.h	Tue Dec 12 05:20:22 1995
+++ gnu/usr.bin/gdb/gdb/arch/i386/nm.h	Wed May 15 06:45:21 1996
@@ -25,9 +25,7 @@
 
 #include <machine/vmparam.h>
 
-#if 0
 #define FLOAT_INFO	{ extern i386_float_info(); i386_float_info(); }
-#endif
 
 #define PTRACE_ARG3_TYPE	caddr_t
 
------------------------------------------------------------------------
--
soda@sra.co.jp		Software Research Associates, Inc., Japan
(Noriyuki Soda)		   software tools and technology group
>Audit-Trail:
>Unformatted: