powerpc FPU emulation fails for single precision arithmetic (Was: kernel panic on ibm4xx-based powerpc box with DDB)

Rin Okuyama <[email protected]> Wed, 28 Dec 2016 18:54:32 +0900
Newsgroups gmane.os.netbsd.ports.powerpc,gmane.os.netbsd.devel.kernel
Message-ID <[email protected]>
On 2016/12/28 18:27, Matt Thomas wrote:
>
>> On Dec 27, 2016, at 11:59 PM, Rin Okuyama <[email protected]> wrote:
>>
>> Thank you for your kind explanation. I'm starting to understand.
>> I will read again the reference manual from this point of view.
>> So, could I commit the patch?
>
> Go commit it.
>

Thanks, I committed it!

By the way, I'd also like to fix port-powerpc/51368:

   http://gnats.netbsd.org/51368

FPU emulation for ppc gives wrong results for single precision
arithmetic. As described in the PR, I found the fix from FreeBSD:

====
   https://svnweb.freebsd.org/base?view=revision&revision=258250

   Make single precision floating point arithmetic actually work -- I think
   it never did -- and fix an obvious missing line. Floating point emulation
   on Book-E still needs some work but this gets it basically functional on
   soft-FPU systems (hard FPU for Book-E is not yet implemented).

   MFC after:	1 week
====

With this fix, single precision calculations get sane on ibm4xx and
booke as far as I checked.

Thanks,
Rin
====
--- src/sys/arch/powerpc/fpu/fpu_emu.c.orig	2016-07-27 10:04:00.737524067 +0900
+++ src/sys/arch/powerpc/fpu/fpu_emu.c	2016-07-27 10:03:44.803486129 +0900
@@ -626,9 +626,11 @@
  			rb = instr.i_a.i_frb;
  			rc = instr.i_a.i_frc;
  
-			type = FTYPE_SNG;
-			if (instr.i_any.i_opcd & 0x4)
-				type = FTYPE_DBL;
+			/*
+			 * All arithmetic operations work on registers, which
+			 * are stored as doubles.
+			 */
+			type = FTYPE_DBL;
  			switch ((unsigned int)instr.i_a.i_xo) {
  			case	OPC59_FDIVS:
  				FPU_EMU_EVCNT_INCR(fdiv);
@@ -745,6 +747,13 @@
  				return (NOTFPU);
  				break;
  			}
+
+			/* If the instruction was single precision, round */
+			if (!(instr.i_any.i_opcd & 0x4)) {
+				fpu_implode(fe, fp, FTYPE_SNG,
+					(u_int *)&fs->fpreg[rt]);
+				fpu_explode(fe, fp = &fe->fe_f1, FTYPE_SNG, rt);
+			}
  		}
  	} else {
  		return (NOTFPU);
--- src/sys/arch/powerpc/fpu/fpu_explode.c.orig	2016-07-27 10:00:01.060507066 +0900
+++ src/sys/arch/powerpc/fpu/fpu_explode.c	2016-07-27 10:00:12.431852649 +0900
@@ -235,6 +235,7 @@
  		s = fpu_dtof(fp, s, space[1]);
  		break;
  
+	default:
  		panic("fpu_explode");
  		panic("fpu_explode: invalid type %d", type);
  	}