git: 03a07de0d5ee - main - arm: Handle VFP exceptions from the kernel

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Mon, 11 Dec 2023 14:16:37 UTC
The branch main has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=03a07de0d5ee7d58069152070c42d55f7ec32b7c

commit 03a07de0d5ee7d58069152070c42d55f7ec32b7c
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2023-12-11 14:08:34 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2023-12-11 14:08:34 +0000

    arm: Handle VFP exceptions from the kernel
    
    vfp_bounce() is called when handling an undefined instruction exception,
    to see if we need to enable the VFP.  Previously it would
    unconditionally panic if the exception came from the kernel, which was
    simply wrong, and it did not permit lazy initialization of VFP state in
    the kernel.  However, this functionality can be useful and is supported
    by arm's fpu_kern_enter() implementation.  Thus, relax assertions and
    consume the exception if the thread was in an FPU section.
    
    Based on a patch from Stormshield.
    
    Reviewed by:    andrew
    MFC after:      2 weeks
    Sponsored by:   Klara, Inc.
    Sponsored by:   Stormshield
    Differential Revision:  https://reviews.freebsd.org/D42971
---
 sys/arm/arm/vfp.c | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/sys/arm/arm/vfp.c b/sys/arm/arm/vfp.c
index f2979d4a2b27..bbcb468391b6 100644
--- a/sys/arm/arm/vfp.c
+++ b/sys/arm/arm/vfp.c
@@ -196,8 +196,9 @@ vfp_init(void)
 
 SYSINIT(vfp, SI_SUB_CPU, SI_ORDER_ANY, vfp_init, NULL);
 
-/* start VFP unit, restore the vfp registers from the PCB  and retry
- * the instruction
+/*
+ * Start the VFP unit, restore the VFP registers from the PCB and retry
+ * the instruction.
  */
 static int
 vfp_bounce(u_int addr, u_int insn, struct trapframe *frame, int code)
@@ -206,9 +207,6 @@ vfp_bounce(u_int addr, u_int insn, struct trapframe *frame, int code)
 	struct pcb *curpcb;
 	ksiginfo_t ksi;
 
-	if ((code & FAULT_USER) == 0)
-		panic("undefined floating point instruction in supervisor mode");
-
 	critical_enter();
 
 	/*
@@ -242,13 +240,19 @@ vfp_bounce(u_int addr, u_int insn, struct trapframe *frame, int code)
 		return 1;
 	}
 
+	curpcb = curthread->td_pcb;
+	if ((code & FAULT_USER) == 0 &&
+	    (curpcb->pcb_fpflags & PCB_FP_KERN) == 0) {
+		critical_exit();
+		return (1);
+	}
+
 	/*
 	 * If the last time this thread used the VFP it was on this core, and
 	 * the last thread to use the VFP on this core was this thread, then the
 	 * VFP state is valid, otherwise restore this thread's state to the VFP.
 	 */
 	fmxr(fpexc, fpexc | VFPEXC_EN);
-	curpcb = curthread->td_pcb;
 	cpu = PCPU_GET(cpuid);
 	if (curpcb->pcb_vfpcpu != cpu || curthread != PCPU_GET(fpcurthread)) {
 		vfp_restore(curpcb->pcb_vfpsaved);
@@ -258,7 +262,8 @@ vfp_bounce(u_int addr, u_int insn, struct trapframe *frame, int code)
 
 	critical_exit();
 
-	KASSERT(curpcb->pcb_vfpsaved == &curpcb->pcb_vfpstate,
+	KASSERT((code & FAULT_USER) == 0 ||
+	    curpcb->pcb_vfpsaved == &curpcb->pcb_vfpstate,
 	    ("Kernel VFP state in use when entering userspace"));
 
 	return (0);