Port-hppa archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: Patch: hppa lazy FPU switching and QEMU bugs



On Tue, Aug 18, 2026 at 04:26:21PM -0400, Thor Lancelot Simon wrote:
> On Tue, Aug 18, 2026 at 06:09:20PM +0100, Nick Hudson wrote:
> > 
> > On 18/08/2026 00:11, Thor Lancelot Simon wrote:
> > > On Mon, Aug 17, 2026 at 06:27:43PM +0100, Nick Hudson wrote:
> > > > It doesn't quite work - I saw an unexpected core dump and fp exception. Will investigate more later...
> > >  From a user process, or of the kernel itself?
> > 
> > Only userland problems.
> > 
> > I'm happy to take a diff the just does eager.
> 
> I am still just barely gaining comfort with the architecture and the
> asm syntax and I think that'd take a little more restructuring in
> trap and locore to be what I'd call a clean finished result.  But
> I don't mind poking at it later...
> 
> I think I see the problem with eager mode.  I was testing it only
> with the workload that caused me problems with lazy, namely,
> integer multiplies!  So I was getting away with not properly
> handling the FPU status register, which the lazy path kind of
> does as a side effect (really, the lazy path is nothing but
> side effects, depending how you squint at it).
> 
> I'm fixing.  Will send another patch.

Try the below - hopefully this is it.

Index: sys/arch/hppa/conf/files.hppa
===================================================================
RCS file: /cvsroot/src/sys/arch/hppa/conf/files.hppa,v
retrieving revision 1.29
diff -u -r1.29 files.hppa
--- sys/arch/hppa/conf/files.hppa	20 Oct 2025 09:42:46 -0000	1.29
+++ sys/arch/hppa/conf/files.hppa	18 Aug 2026 22:04:03 -0000
@@ -111,6 +111,7 @@
 device	fpu
 attach	fpu at gedoens
 file	arch/hppa/dev/fpu.c		fpu
+defflag opt_hppa_fpu.h HPPA_FPU_EAGER
 
 # Phantom PseudoBC GSC+ Port
 device	phantomas: gedoens
Index: sys/arch/hppa/hppa/fpu.c
===================================================================
RCS file: /cvsroot/src/sys/arch/hppa/hppa/fpu.c,v
retrieving revision 1.27
diff -u -r1.27 fpu.c
--- sys/arch/hppa/hppa/fpu.c	16 Apr 2020 05:44:43 -0000	1.27
+++ sys/arch/hppa/hppa/fpu.c	18 Aug 2026 22:04:03 -0000
@@ -36,6 +36,10 @@
 #include <sys/cdefs.h>
 __KERNEL_RCSID(0, "$NetBSD: fpu.c,v 1.27 2020/04/16 05:44:43 skrll Exp $");
 
+#ifdef _KERNEL_OPT
+#include "opt_hppa_fpu.h"
+#endif
+
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/proc.h>
@@ -78,10 +82,15 @@
 /* The number of times we have had to switch the FPU context. */
 u_int fpu_csw;
 
+/* Don't use the default lazy FPU switching */
+int fpu_eager;
+
 /* In locore.S, this swaps states in and out of the FPU. */
 void hppa_fpu_swapout(struct pcb *);
 void hppa_fpu_swap(struct fpreg *, struct fpreg *);
 
+void hppa_fpu_switch(struct lwp *);
+
 static int hppa_fpu_ls(struct trapframe *, struct lwp *);
 
 /*
@@ -139,14 +148,16 @@
 void
 hppa_fpu_bootstrap(u_int ccr_enable)
 {
-	uint32_t junk[2];
-	uint32_t vers[2];
+	uint32_t junk[2] __aligned(8);
+	uint32_t vers[2] __aligned(8);
+	u_int ccr;
 
 	/* See if we have a present and functioning hardware FPU. */
 	fpu_present = (ccr_enable & HPPA_FPUS) == HPPA_FPUS;
 	if (!fpu_present) {
 		fpu_csw = 0;
 		curcpu()->ci_fpu_state = 0;
+		curcpu()->ci_fpu_lwp = NULL;
 
 		return;
 	}
@@ -182,9 +193,41 @@
 	 */
 	fpu_csw = 0;
 	curcpu()->ci_fpu_state = 0;
+	curcpu()->ci_fpu_lwp = NULL;
 	mtctl(ccr_enable & (CCR_MASK ^ HPPA_FPUS), CR_CCR);
 
 	fpu_version = vers[0];
+
+#ifdef HPPA_FPU_EAGER
+	fpu_eager = 1;
+	aprint_normal("fpu: eager switching\n");
+#else
+	/*
+	 * Handle QEMU, which does not check whether the FPU
+	 * is disabled before trying to run instructions on it,
+	 * thus never traps, thus never triggers our lazy FPU
+	 * switching scheme.
+	 */
+	mtctl(0, CR_CCR);
+	__asm volatile("fstds %%fr0, 0(%0)" :: "r" (junk) : "memory");
+	mfctl(CR_CCR, ccr);
+	if (!(ccr & HPPA_FPUS)) {
+		fpu_eager = 1;
+		/* No trap, so no trap handler, so re-enable FPU ourselves. */
+		mtctl(HPPA_FPUS, CR_CCR);
+		aprint_normal("fpu: emulation trap failure; "
+		    "using eager switching\n");
+	} else {
+		/*
+		 * This probe is the only thing that ever traps from lwp0,
+		 * and we're not really set up for that to work right.
+		 * Fortunately, we know what state we should put back.
+		 */
+		curcpu()->ci_fpu_state = 0;
+		mtctl(ccr_enable & (CCR_MASK ^ HPPA_FPUS), CR_CCR);
+	}
+
+#endif
 }
 
 /*
@@ -212,6 +255,14 @@
 
 	hppa_fpu_swapout(pcb);
 	ci->ci_fpu_state = 0;
+	/*
+	 * If we are doing eager FPU switching, hang onto the
+	 * LWP - if it uses the FPU again, the regs will need to be
+	 * saved again.
+	 */
+	if (!fpu_eager) {
+		ci->ci_fpu_lwp = NULL;
+	}
 }
 
 /*
@@ -424,3 +475,44 @@
 		trapsignal(l, &ksi);
 	}
 }
+/*
+ * Immediately switch current LWP's FP regs for those of the new LWP's;
+ * called from cpu_switchto if hppa_fpu_switch is set (if we are avoiding
+ * lazy FPU switching).
+ */
+void
+hppa_fpu_switch(struct lwp *newl)
+{
+    struct cpu_info *ci = curcpu();
+    struct pcb *oldpcb, *newpcb;
+    struct fpreg *oldregs = NULL, *newregs;
+
+    if (!fpu_present) return;
+    if (ci->ci_fpu_lwp == newl) return;
+
+    if (ci->ci_fpu_lwp != NULL) {
+	oldpcb = lwp_getpcb(ci->ci_fpu_lwp);
+	oldregs = oldpcb->pcb_fpregs;
+    }
+
+    newpcb = lwp_getpcb(newl);
+    newregs = newpcb->pcb_fpregs;
+
+    hppa_fpu_swap(oldregs, newregs);
+}
+/*
+ * With eager FPU switching, we can't rely on trap side effects to catch
+ * use of the FPU and sync regs like the status register.  Push a change
+ * out to the hardware, to avoid use of some other process's FPU status etc.
+ */
+void
+hppa_fpu_commit(struct lwp *l)
+{
+    struct cpu_info *ci = curcpu();
+    
+    KASSERT(kpreempt_disabled());
+    
+    if (fpu_eager && ci->ci_fpu_lwp == l) {
+        hppa_fpu_swap(NULL, lwp_getpcb(l)->pcb_fpregs);
+    }
+}
Index: sys/arch/hppa/hppa/hppa_machdep.c
===================================================================
RCS file: /cvsroot/src/sys/arch/hppa/hppa/hppa_machdep.c,v
retrieving revision 1.33
diff -u -r1.33 hppa_machdep.c
--- sys/arch/hppa/hppa/hppa_machdep.c	13 May 2022 18:40:02 -0000	1.33
+++ sys/arch/hppa/hppa/hppa_machdep.c	18 Aug 2026 22:04:03 -0000
@@ -252,8 +252,11 @@
 	if ((flags & _UC_FPU) != 0) {
 		struct pcb *pcb = lwp_getpcb(l);
 
+		kpreempt_disable();
 		hppa_fpu_flush(l);
 		memcpy(pcb->pcb_fpregs, &mcp->__fpregs, sizeof(mcp->__fpregs));
+		hppa_fpu_commit(l);
+		kpreempt_enable();
 	}
 
 	mutex_enter(p->p_lock);
Index: sys/arch/hppa/hppa/locore.S
===================================================================
RCS file: /cvsroot/src/sys/arch/hppa/hppa/locore.S,v
retrieving revision 1.7
diff -u -r1.7 locore.S
--- sys/arch/hppa/hppa/locore.S	3 Apr 2025 17:49:49 -0000	1.7
+++ sys/arch/hppa/hppa/locore.S	18 Aug 2026 22:04:03 -0000
@@ -945,13 +945,35 @@
 noras:
 
 	/*
-	 * We do have a hardware FPU.  If the LWP
+	 * We do have a hardware FPU.  Two paths: eager
+	 * (if selected by kernel config, or if emulation trap broken),
+	 * or lazy.
+	 */
+	.import fpu_eager, data
+	ldil	L%fpu_eager, %t1
+	ldw	R%fpu_eager(%t1), %t1
+	comib,=,n 0, %t1, L$fpu_lazy
+
+	/*
+	 * Eager path - via C helper
+	 */
+	stw	%arg0, HPPA_FRAME_ARG(0)(%r3)
+	stw	%arg1, HPPA_FRAME_ARG(1)(%r3)
+	copy	%arg1, %arg0
+	CALL(hppa_fpu_switch, %r1)
+	ldw	HPPA_FRAME_ARG(1)(%r3), %arg1
+	ldw	HPPA_FRAME_ARG(0)(%r3), %arg0
+	b,n	switch_return
+
+	/*
+	 * Lazy path.  If the LWP
 	 * that we just switched to has its state in the
 	 * FPU, enable the FPU, else disable it, so if
 	 * the LWP does try to use the coprocessor
 	 * we'll get an assist emulation trap to swap
 	 * states.
 	 */
+L$fpu_lazy:
 	GET_CURCPU(%t1)
 	mfctl	CR_CCR, %r1
 	mfctl	CR_FPPADDR, %t2
Index: sys/arch/hppa/hppa/machdep.c
===================================================================
RCS file: /cvsroot/src/sys/arch/hppa/hppa/machdep.c,v
retrieving revision 1.27
diff -u -r1.27 machdep.c
--- sys/arch/hppa/hppa/machdep.c	15 Aug 2026 11:14:59 -0000	1.27
+++ sys/arch/hppa/hppa/machdep.c	18 Aug 2026 22:04:03 -0000
@@ -1923,12 +1923,15 @@
 	hppa_setvmspace(l);
 
 	/* reset any of the pending FPU exceptions */
+	kpreempt_disable();
 	hppa_fpu_flush(l);
 	memset(pcb->pcb_fpregs, 0, sizeof(*pcb->pcb_fpregs));
 	pcb->pcb_fpregs->fpr_regs[0] = ((uint64_t)HPPA_FPU_INIT) << 32;
 	pcb->pcb_fpregs->fpr_regs[1] = 0;
 	pcb->pcb_fpregs->fpr_regs[2] = 0;
 	pcb->pcb_fpregs->fpr_regs[3] = 0;
+	hppa_fpu_commit(l);
+	kpreempt_enable();
 
 	l->l_md.md_bpva = 0;
 
Index: sys/arch/hppa/hppa/machdep.h
===================================================================
RCS file: /cvsroot/src/sys/arch/hppa/hppa/machdep.h,v
retrieving revision 1.19
diff -u -r1.19 machdep.h
--- sys/arch/hppa/hppa/machdep.h	14 Sep 2020 16:11:00 -0000	1.19
+++ sys/arch/hppa/hppa/machdep.h	18 Aug 2026 22:04:03 -0000
@@ -80,6 +80,7 @@
 void hppa_fpu_bootstrap(u_int);
 void hppa_fpu_flush(struct lwp *);
 void hppa_fpu_emulate(struct trapframe *, struct lwp *, u_int);
+void hppa_fpu_commit(struct lwp *);
 
 /* Set up of space registers and protection IDs */
 void hppa_setvmspace(struct lwp *);
Index: sys/arch/hppa/hppa/process_machdep.c
===================================================================
RCS file: /cvsroot/src/sys/arch/hppa/hppa/process_machdep.c,v
retrieving revision 1.18
diff -u -r1.18 process_machdep.c
--- sys/arch/hppa/hppa/process_machdep.c	4 Jan 2014 00:10:02 -0000	1.18
+++ sys/arch/hppa/hppa/process_machdep.c	18 Aug 2026 22:04:03 -0000
@@ -181,8 +181,11 @@
 {
 	struct pcb *pcb = lwp_getpcb(l);
 
+	kpreempt_disable();
 	hppa_fpu_flush(l);
 	memcpy(pcb->pcb_fpregs, fpregs, sizeof(*fpregs));
+	hppa_fpu_commit(l);
+	kpreempt_enable();
 	return 0;
 }
 
Index: sys/arch/hppa/include/cpu.h
===================================================================
RCS file: /cvsroot/src/sys/arch/hppa/include/cpu.h,v
retrieving revision 1.13
diff -u -r1.13 cpu.h
--- sys/arch/hppa/include/cpu.h	23 Feb 2023 14:55:36 -0000	1.13
+++ sys/arch/hppa/include/cpu.h	18 Aug 2026 22:04:03 -0000
@@ -290,6 +290,8 @@
 	hppa_hpa_t	ci_hpa;
 	register_t	ci_psw;		/* Processor Status Word. */
 	paddr_t		ci_fpu_state;	/* LWP FPU state address, or zero. */
+	struct lwp	*ci_fpu_lwp;	/* If eager mode, LWP vaddr or NULL */
+
 	u_long		ci_itmr;
 
 	int		ci_cpuid;	/* CPU index (see cpus[] array) */



Home | Main Index | Thread Index | Old Index