Module Name:    src
Committed By:   matt
Date:           Sun Aug 12 05:05:48 UTC 2012

Modified Files:
        src/sys/arch/arm/arm: arm_machdep.c undefined.c
        src/sys/arch/arm/arm32: cpu.c cpuswitch.S sys_machdep.c vm_machdep.c
        src/sys/arch/arm/conf: files.arm
        src/sys/arch/arm/include: cpu.h pcb.h proc.h sysarch.h types.h
        src/sys/arch/arm/vfp: vfp_init.c
Removed Files:
        src/sys/arch/arm/include: vfpvar.h

Log Message:
Rework VFP support to use PCU.
Add emulation of instruction which save/restore the VFP FPSCR.
Add a sysarch hook to VFP FPSCR manipulation.

[The emulation will be used by libc to store/fetch exception modes and
rounding mode on a per-thread basis.]


To generate a diff of this commit:
cvs rdiff -u -r1.31 -r1.32 src/sys/arch/arm/arm/arm_machdep.c
cvs rdiff -u -r1.45 -r1.46 src/sys/arch/arm/arm/undefined.c
cvs rdiff -u -r1.82 -r1.83 src/sys/arch/arm/arm32/cpu.c
cvs rdiff -u -r1.63 -r1.64 src/sys/arch/arm/arm32/cpuswitch.S
cvs rdiff -u -r1.14 -r1.15 src/sys/arch/arm/arm32/sys_machdep.c
cvs rdiff -u -r1.57 -r1.58 src/sys/arch/arm/arm32/vm_machdep.c
cvs rdiff -u -r1.107 -r1.108 src/sys/arch/arm/conf/files.arm
cvs rdiff -u -r1.65 -r1.66 src/sys/arch/arm/include/cpu.h
cvs rdiff -u -r1.21 -r1.22 src/sys/arch/arm/include/pcb.h
cvs rdiff -u -r1.10 -r1.11 src/sys/arch/arm/include/proc.h
cvs rdiff -u -r1.7 -r1.8 src/sys/arch/arm/include/sysarch.h
cvs rdiff -u -r1.22 -r1.23 src/sys/arch/arm/include/types.h
cvs rdiff -u -r1.1 -r0 src/sys/arch/arm/include/vfpvar.h
cvs rdiff -u -r1.3 -r1.4 src/sys/arch/arm/vfp/vfp_init.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/arm/arm/arm_machdep.c
diff -u src/sys/arch/arm/arm/arm_machdep.c:1.31 src/sys/arch/arm/arm/arm_machdep.c:1.32
--- src/sys/arch/arm/arm/arm_machdep.c:1.31	Sun Feb 19 21:06:04 2012
+++ src/sys/arch/arm/arm/arm_machdep.c	Sun Aug 12 05:05:47 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: arm_machdep.c,v 1.31 2012/02/19 21:06:04 rmind Exp $	*/
+/*	$NetBSD: arm_machdep.c,v 1.32 2012/08/12 05:05:47 matt Exp $	*/
 
 /*
  * Copyright (c) 2001 Wasabi Systems, Inc.
@@ -78,7 +78,7 @@
 
 #include <sys/param.h>
 
-__KERNEL_RCSID(0, "$NetBSD: arm_machdep.c,v 1.31 2012/02/19 21:06:04 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: arm_machdep.c,v 1.32 2012/08/12 05:05:47 matt Exp $");
 
 #include <sys/exec.h>
 #include <sys/proc.h>
@@ -109,6 +109,12 @@ struct cpu_info cpu_info_store = {
 #endif
 };
 
+const pcu_ops_t * const pcu_ops_md_defs[PCU_UNIT_COUNT] = {
+#if defined(FPU_VFP)
+	[PCU_FPU] = &arm_vfp_ops,
+#endif
+};
+
 /*
  * The ARM architecture places the vector page at address 0.
  * Later ARM architecture versions, however, allow it to be
@@ -174,9 +180,7 @@ setregs(struct lwp *l, struct exec_packa
 #endif
 	pcb->pcb_flags = 0;
 #ifdef FPU_VFP
-	l->l_md.md_flags &= ~MDP_VFPUSED;
-	if (pcb->pcb_vfpcpu != NULL)
-		vfp_saveregs_lwp(l, 0);
+	vfp_discardcontext();
 #endif
 }
 

Index: src/sys/arch/arm/arm/undefined.c
diff -u src/sys/arch/arm/arm/undefined.c:1.45 src/sys/arch/arm/arm/undefined.c:1.46
--- src/sys/arch/arm/arm/undefined.c:1.45	Sat Aug 11 07:05:57 2012
+++ src/sys/arch/arm/arm/undefined.c	Sun Aug 12 05:05:47 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: undefined.c,v 1.45 2012/08/11 07:05:57 matt Exp $	*/
+/*	$NetBSD: undefined.c,v 1.46 2012/08/12 05:05:47 matt Exp $	*/
 
 /*
  * Copyright (c) 2001 Ben Harris.
@@ -54,9 +54,9 @@
 #include <sys/kgdb.h>
 #endif
 
-__KERNEL_RCSID(0, "$NetBSD: undefined.c,v 1.45 2012/08/11 07:05:57 matt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: undefined.c,v 1.46 2012/08/12 05:05:47 matt Exp $");
 
-#include <sys/malloc.h>
+#include <sys/kmem.h>
 #include <sys/queue.h>
 #include <sys/signal.h>
 #include <sys/systm.h>
@@ -99,8 +99,7 @@ install_coproc_handler(int coproc, undef
 	KASSERT(coproc >= 0 && coproc < NUM_UNKNOWN_HANDLERS);
 	KASSERT(handler != NULL); /* Used to be legal. */
 
-	/* XXX: M_TEMP??? */
-	uh = malloc(sizeof(*uh), M_TEMP, M_WAITOK);
+	uh = kmem_alloc(sizeof(*uh), KM_SLEEP);
 	uh->uh_handler = handler;
 	install_coproc_handler_static(coproc, uh);
 	return uh;
@@ -119,7 +118,7 @@ remove_coproc_handler(void *cookie)
 	struct undefined_handler *uh = cookie;
 
 	LIST_REMOVE(uh, uh_link);
-	free(uh, M_TEMP);
+	kmem_free(uh, sizeof(*uh));
 }
 
 static struct evcnt cp15_ev =

Index: src/sys/arch/arm/arm32/cpu.c
diff -u src/sys/arch/arm/arm32/cpu.c:1.82 src/sys/arch/arm/arm32/cpu.c:1.83
--- src/sys/arch/arm/arm32/cpu.c:1.82	Sat Jul 14 07:55:28 2012
+++ src/sys/arch/arm/arm32/cpu.c	Sun Aug 12 05:05:47 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: cpu.c,v 1.82 2012/07/14 07:55:28 matt Exp $	*/
+/*	$NetBSD: cpu.c,v 1.83 2012/08/12 05:05:47 matt Exp $	*/
 
 /*
  * Copyright (c) 1995 Mark Brinicombe.
@@ -46,7 +46,7 @@
 
 #include <sys/param.h>
 
-__KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.82 2012/07/14 07:55:28 matt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.83 2012/08/12 05:05:47 matt Exp $");
 
 #include <sys/systm.h>
 #include <sys/malloc.h>
@@ -64,10 +64,6 @@ __KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.82
 #include <arm/fpe-arm/armfpe.h>
 #endif
 
-#ifdef FPU_VFP
-#include <arm/vfpvar.h>
-#endif
-
 char cpu_model[256];
 
 /* Prototypes */
@@ -161,9 +157,7 @@ cpu_attach(struct device *dv)
 		initialise_arm_fpe();
 #endif
 
-#ifdef FPU_VFP
 	vfp_attach();
-#endif
 }
 
 enum cpu_class {

Index: src/sys/arch/arm/arm32/cpuswitch.S
diff -u src/sys/arch/arm/arm32/cpuswitch.S:1.63 src/sys/arch/arm/arm32/cpuswitch.S:1.64
--- src/sys/arch/arm/arm32/cpuswitch.S:1.63	Thu Apr  7 10:03:47 2011
+++ src/sys/arch/arm/arm32/cpuswitch.S	Sun Aug 12 05:05:47 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: cpuswitch.S,v 1.63 2011/04/07 10:03:47 matt Exp $	*/
+/*	$NetBSD: cpuswitch.S,v 1.64 2012/08/12 05:05:47 matt Exp $	*/
 
 /*
  * Copyright 2003 Wasabi Systems, Inc.
@@ -89,7 +89,7 @@
 #include <machine/asm.h>
 #include <machine/cpu.h>
 
-	RCSID("$NetBSD: cpuswitch.S,v 1.63 2011/04/07 10:03:47 matt Exp $")
+	RCSID("$NetBSD: cpuswitch.S,v 1.64 2012/08/12 05:05:47 matt Exp $")
 
 /* LINTSTUB: include <sys/param.h> */
 	
@@ -235,15 +235,6 @@ ENTRY(cpu_switchto)
 	/* rem: r7 = new pcb */
 	/* rem: interrupts are enabled */
 
-#ifdef FPU_VFP
-	/*
-	 * Now's a good time to 'save' the VFP context.  Note that we
-	 * don't really force a save here, which can save time if we
-	 * end up restarting the same context.
-	 */
-	bl	_C_LABEL(vfp_savecontext)
-#endif
-
 	/* Restore saved context */
 
 .Ldo_switch:
@@ -290,10 +281,6 @@ ENTRY(cpu_switchto)
 	/* rem: r6 = new lwp */
 	/* rem: r7 = new pcb */
 
-#ifdef FPU_VFP
-	mov	r0, r6
-	bl	_C_LABEL(vfp_loadcontext)
-#endif
 #ifdef ARMFPE
 	add	r0, r7, #(PCB_SIZE) & 0x00ff
 	add	r0, r0, #(PCB_SIZE) & 0xff00 

Index: src/sys/arch/arm/arm32/sys_machdep.c
diff -u src/sys/arch/arm/arm32/sys_machdep.c:1.14 src/sys/arch/arm/arm32/sys_machdep.c:1.15
--- src/sys/arch/arm/arm32/sys_machdep.c:1.14	Thu Nov 17 15:02:22 2011
+++ src/sys/arch/arm/arm32/sys_machdep.c	Sun Aug 12 05:05:47 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: sys_machdep.c,v 1.14 2011/11/17 15:02:22 joerg Exp $	*/
+/*	$NetBSD: sys_machdep.c,v 1.15 2012/08/12 05:05:47 matt Exp $	*/
 
 /*
  * Copyright (c) 1995-1997 Mark Brinicombe.
@@ -41,7 +41,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sys_machdep.c,v 1.14 2011/11/17 15:02:22 joerg Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sys_machdep.c,v 1.15 2012/08/12 05:05:47 matt Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -54,10 +54,13 @@ __KERNEL_RCSID(0, "$NetBSD: sys_machdep.
 #include <sys/syscallargs.h>
 
 #include <machine/sysarch.h>
+#include <machine/pcb.h>
+#include <arm/vfpreg.h>
 
 /* Prototypes */
 static int arm32_sync_icache(struct lwp *, const void *, register_t *);
 static int arm32_drain_writebuf(struct lwp *, const void *, register_t *);
+static int arm32_vfp_fpscr(struct lwp *, const void *, register_t *);
 
 static int
 arm32_sync_icache(struct lwp *l, const void *args, register_t *retval)
@@ -86,6 +89,34 @@ arm32_drain_writebuf(struct lwp *l, cons
 	return(0);
 }
 
+static int
+arm32_vfp_fpscr(struct lwp *l, const void *uap, register_t *retval)
+{
+	struct pcb * const pcb = lwp_getpcb(l);
+
+#ifdef FPU_VFP
+	/*
+	 * Save the current VFP state (to make sure the FPSCR copy is
+	 * up to date).
+	 */
+	vfp_savecontext();
+#endif
+
+	retval[0] = pcb->pcb_vfp.vfp_fpscr;
+	if (uap) {
+		struct arm_vfp_fpscr_args ua;
+		int error;
+		if ((error = copyin(uap, &ua, sizeof(ua))) != 0)
+			return (error);
+		if (((ua.fpscr_clear|ua.fpscr_set) & ~VFP_FPSCR_RMODE) != 0)
+			return EINVAL;
+		pcb->pcb_vfp.vfp_fpscr &= ~ua.fpscr_clear;
+		pcb->pcb_vfp.vfp_fpscr |= ua.fpscr_set;
+	}
+
+	return 0;
+}
+
 int
 sys_sysarch(struct lwp *l, const struct sys_sysarch_args *uap, register_t *retval)
 {
@@ -104,6 +135,10 @@ sys_sysarch(struct lwp *l, const struct 
 		error = arm32_drain_writebuf(l, SCARG(uap, parms), retval);
 		break;
 
+	case ARM_VFP_FPSCR :
+		error = arm32_vfp_fpscr(l, SCARG(uap, parms), retval);
+		break;
+
 	default:
 		error = EINVAL;
 		break;

Index: src/sys/arch/arm/arm32/vm_machdep.c
diff -u src/sys/arch/arm/arm32/vm_machdep.c:1.57 src/sys/arch/arm/arm32/vm_machdep.c:1.58
--- src/sys/arch/arm/arm32/vm_machdep.c:1.57	Fri Aug  3 07:59:22 2012
+++ src/sys/arch/arm/arm32/vm_machdep.c	Sun Aug 12 05:05:47 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: vm_machdep.c,v 1.57 2012/08/03 07:59:22 matt Exp $	*/
+/*	$NetBSD: vm_machdep.c,v 1.58 2012/08/12 05:05:47 matt Exp $	*/
 
 /*
  * Copyright (c) 1994-1998 Mark Brinicombe.
@@ -44,7 +44,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: vm_machdep.c,v 1.57 2012/08/03 07:59:22 matt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: vm_machdep.c,v 1.58 2012/08/12 05:05:47 matt Exp $");
 
 #include "opt_armfpe.h"
 #include "opt_pmap_debug.h"
@@ -137,16 +137,7 @@ cpu_lwp_fork(struct lwp *l1, struct lwp 
 	}
 #endif
 
-	l2->l_md.md_flags = l1->l_md.md_flags & MDP_VFPUSED;
-
-#ifdef FPU_VFP
-	/*
-	 * Copy the floating point state from the VFP to the PCB
-	 * if this process has state stored there.
-	 */
-	if (pcb1->pcb_vfpcpu != NULL)
-		vfp_saveregs_lwp(l1, 1);
-#endif
+	l2->l_md.md_flags = l1->l_md.md_flags & MDLWP_VFPUSED;
 
 	/* Copy the pcb */
 	*pcb2 = *pcb1;
@@ -209,22 +200,12 @@ cpu_lwp_fork(struct lwp *l1, struct lwp 
 void
 cpu_lwp_free(struct lwp *l, int proc)
 {
-#ifdef FPU_VFP
-	struct pcb *pcb;
-#endif
-
 #ifdef ARMFPE
 	/* Abort any active FP operation and deactivate the context */
 	arm_fpe_core_abort(FP_CONTEXT(l), NULL, NULL);
 	arm_fpe_core_changecontext(0);
 #endif	/* ARMFPE */
 
-#ifdef FPU_VFP
-	pcb = lwp_getpcb(l);
-	if (pcb->pcb_vfpcpu != NULL)
-		vfp_saveregs_lwp(l, 0);
-#endif
-
 #ifdef STACKCHECKS
 	/* Report how much stack has been used - debugging */
 	if (l) {

Index: src/sys/arch/arm/conf/files.arm
diff -u src/sys/arch/arm/conf/files.arm:1.107 src/sys/arch/arm/conf/files.arm:1.108
--- src/sys/arch/arm/conf/files.arm:1.107	Fri Aug  3 07:59:22 2012
+++ src/sys/arch/arm/conf/files.arm	Sun Aug 12 05:05:47 2012
@@ -1,4 +1,4 @@
-#	$NetBSD: files.arm,v 1.107 2012/08/03 07:59:22 matt Exp $
+#	$NetBSD: files.arm,v 1.108 2012/08/12 05:05:47 matt Exp $
 
 # temporary define to allow easy moving to ../arch/arm/arm32
 defflag				ARM32
@@ -54,7 +54,7 @@ file	arch/arm/fpe-arm/armfpe_init.c		arm
 file	arch/arm/fpe-arm/armfpe.S		armfpe
 
 # VFP support
-file	arch/arm/vfp/vfp_init.c			fpu_vfp
+file	arch/arm/vfp/vfp_init.c			arm32
 
 # PMAP_DEBUG (heavily abused option)
 defflag				PMAP_DEBUG

Index: src/sys/arch/arm/include/cpu.h
diff -u src/sys/arch/arm/include/cpu.h:1.65 src/sys/arch/arm/include/cpu.h:1.66
--- src/sys/arch/arm/include/cpu.h:1.65	Thu Aug  2 15:56:07 2012
+++ src/sys/arch/arm/include/cpu.h	Sun Aug 12 05:05:47 2012
@@ -78,9 +78,6 @@
 #ifndef _LOCORE
 #include <machine/frame.h>
 #include <machine/pcb.h>
-#ifdef FPU_VFP
-#include <arm/vfpvar.h>
-#endif
 #endif	/* !_LOCORE */
 
 #include <arm/armreg.h>
@@ -258,12 +255,10 @@ struct cpu_info {
 	struct evcnt ci_arm700bugcount;
 	int32_t ci_mtx_count;
 	int ci_mtx_oldspl;
+	uint32_t ci_vfp_id;
 #ifdef MULTIPROCESSOR
 	MP_CPU_INFO_MEMBERS
 #endif
-#ifdef FPU_VFP
-	struct vfp_info ci_vfp;
-#endif
 };
 
 #ifndef MULTIPROCESSOR
@@ -397,6 +392,12 @@ void swi_handler(trapframe_t *);
 /* arm_machdep.c */
 void ucas_ras_check(trapframe_t *);
 
+/* vfp_init.c */
+void vfp_attach(void);
+void vfp_discardcontext(void);
+void vfp_savecontext(void);
+extern const pcu_ops_t arm_vfp_ops;
+
 #endif	/* !_LOCORE */
 
 #endif /* _KERNEL */

Index: src/sys/arch/arm/include/pcb.h
diff -u src/sys/arch/arm/include/pcb.h:1.21 src/sys/arch/arm/include/pcb.h:1.22
--- src/sys/arch/arm/include/pcb.h:1.21	Wed Aug  8 16:09:42 2012
+++ src/sys/arch/arm/include/pcb.h	Sun Aug 12 05:05:47 2012
@@ -94,7 +94,6 @@ struct pcb {
 	} pcb_un;
 	struct	fpe_sp_state pcb_fpstate;	/* FPA Floating Point state */
 	struct	vfpreg pcb_vfp;			/* VFP registers */
-	struct	cpu_info *pcb_vfpcpu;		/* CPU holding VFP state */
 };
 #define	pcb_ff	pcb_fpstate			/* for arm26 */
 

Index: src/sys/arch/arm/include/proc.h
diff -u src/sys/arch/arm/include/proc.h:1.10 src/sys/arch/arm/include/proc.h:1.11
--- src/sys/arch/arm/include/proc.h:1.10	Fri Jan 14 02:06:24 2011
+++ src/sys/arch/arm/include/proc.h	Sun Aug 12 05:05:47 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: proc.h,v 1.10 2011/01/14 02:06:24 rmind Exp $	*/
+/*	$NetBSD: proc.h,v 1.11 2012/08/12 05:05:47 matt Exp $	*/
 
 /*
  * Copyright (c) 1994 Mark Brinicombe.
@@ -47,7 +47,7 @@ struct mdlwp {
 };
 
 /* Flags setttings for md_flags */
-#define MDP_VFPUSED	0x00000001	/* Process used the VFP */
+#define MDLWP_VFPUSED	0x00000001	/* Process used the VFP */
 
 
 struct mdproc {

Index: src/sys/arch/arm/include/sysarch.h
diff -u src/sys/arch/arm/include/sysarch.h:1.7 src/sys/arch/arm/include/sysarch.h:1.8
--- src/sys/arch/arm/include/sysarch.h:1.7	Sat Mar 14 14:45:55 2009
+++ src/sys/arch/arm/include/sysarch.h	Sun Aug 12 05:05:47 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: sysarch.h,v 1.7 2009/03/14 14:45:55 dsl Exp $	*/
+/*	$NetBSD: sysarch.h,v 1.8 2012/08/12 05:05:47 matt Exp $	*/
 
 /*
  * Copyright (c) 1996-1997 Mark Brinicombe.
@@ -48,12 +48,18 @@
 
 #define ARM_SYNC_ICACHE		0
 #define ARM_DRAIN_WRITEBUF	1
+#define ARM_VFP_FPSCR		2
 
 struct arm_sync_icache_args {
 	uintptr_t	addr;		/* Virtual start address */
 	size_t		len;		/* Region size */
 };
 
+struct arm_vfp_fpscr_args {
+	uint32_t	fpscr_clear;	/* bits to clear */
+	uint32_t	fpscr_set;	/* bits to set */
+};
+
 #ifndef _KERNEL
 __BEGIN_DECLS
 int	arm_sync_icache(u_int addr, int len);

Index: src/sys/arch/arm/include/types.h
diff -u src/sys/arch/arm/include/types.h:1.22 src/sys/arch/arm/include/types.h:1.23
--- src/sys/arch/arm/include/types.h:1.22	Fri Aug  3 07:59:23 2012
+++ src/sys/arch/arm/include/types.h	Sun Aug 12 05:05:47 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: types.h,v 1.22 2012/08/03 07:59:23 matt Exp $	*/
+/*	$NetBSD: types.h,v 1.23 2012/08/12 05:05:47 matt Exp $	*/
 
 /*
  * Copyright (c) 1990 The Regents of the University of California.
@@ -89,6 +89,11 @@ typedef	volatile int		__cpu_simple_lock_
 #define	__HAVE_COMMON___TLS_GET_ADDR
 #define	__HAVE_TLS_VARIANT_I
 
+#if defined(_KERNEL) || defined(_KMEMUSER)
+#define	PCU_FPU			0
+#define	PCU_UNIT_COUNT		1
+#endif
+
 #if defined(_KERNEL)
 #define	__HAVE_RAS
 #endif

Index: src/sys/arch/arm/vfp/vfp_init.c
diff -u src/sys/arch/arm/vfp/vfp_init.c:1.3 src/sys/arch/arm/vfp/vfp_init.c:1.4
--- src/sys/arch/arm/vfp/vfp_init.c:1.3	Sat Nov 21 20:32:28 2009
+++ src/sys/arch/arm/vfp/vfp_init.c	Sun Aug 12 05:05:47 2012
@@ -1,4 +1,4 @@
-/*      $NetBSD: vfp_init.c,v 1.3 2009/11/21 20:32:28 rmind Exp $ */
+/*      $NetBSD: vfp_init.c,v 1.4 2012/08/12 05:05:47 matt Exp $ */
 
 /*
  * Copyright (c) 2008 ARM Ltd
@@ -34,11 +34,9 @@
 #include <sys/systm.h>
 #include <sys/device.h>
 #include <sys/proc.h>
+#include <sys/cpu.h>
 
 #include <arm/undefined.h>
-#include <machine/cpu.h>
-
-#include <arm/vfpvar.h>
 #include <arm/vfpreg.h>
 
 /* 
@@ -46,20 +44,50 @@
  */
 
 /* FMRX <X>, fpsid */
-#define read_fpsid(X)	__asm __volatile("mrc p10, 7, %0, c0, c0, 0" \
-			    : "=r" (*(X)) : : "memory")
-/* FMRX <X>, fpscr */
-#define read_fpscr(X)	__asm __volatile("mrc p10, 7, %0, c1, c0, 0" \
-			    : "=r" (*(X)))
+static inline uint32_t
+read_fpsid(void)
+{
+	uint32_t rv;
+	__asm __volatile("mrc p10, 7, %0, c0, c0, 0" : "=r" (rv));
+	return rv;
+}
+
+/* FMRX <X>, fpexc */
+static inline uint32_t
+read_fpscr(void)
+{
+	uint32_t rv;
+	__asm __volatile("mrc p10, 7, %0, c1, c0, 0" : "=r" (rv));
+	return rv;
+}
+
 /* FMRX <X>, fpexc */
-#define read_fpexc(X)	__asm __volatile("mrc p10, 7, %0, c8, c0, 0" \
-			    : "=r" (*(X)))
+static inline uint32_t
+read_fpexc(void)
+{
+	uint32_t rv;
+	__asm __volatile("mrc p10, 7, %0, c8, c0, 0" : "=r" (rv));
+	return rv;
+}
+
 /* FMRX <X>, fpinst */
-#define read_fpinst(X)	__asm __volatile("mrc p10, 7, %0, c9, c0, 0" \
-			    : "=r" (*(X)))
+static inline uint32_t
+read_fpinst(void)
+{
+	uint32_t rv;
+	__asm __volatile("mrc p10, 7, %0, c9, c0, 0" : "=r" (rv));
+	return rv;
+}
+
 /* FMRX <X>, fpinst2 */
-#define read_fpinst2(X)	__asm __volatile("mrc p10, 7, %0, c10, c0, 0" \
-			    : "=r" (*(X)))
+static inline uint32_t
+read_fpinst2(void)
+{
+	uint32_t rv;
+	__asm __volatile("mrc p10, 7, %0, c10, c0, 0" : "=r" (rv));
+	return rv;
+}
+
 /* FSTMD <X>, {d0-d15} */
 #define save_vfpregs(X)	__asm __volatile("stc p11, c0, [%0], {32}" : \
 			    : "r" (X) : "memory")
@@ -80,10 +108,22 @@
 #define load_vfpregs(X)	__asm __volatile("ldc p11, c0, [%0], {32}" : \
 			    : "r" (X) : "memory");
 
+#ifdef FPU_VFP
+
 /* The real handler for VFP bounces.  */
 static int vfp_handler(u_int, u_int, trapframe_t *, int);
+static int vfp_handler(u_int, u_int, trapframe_t *, int);
 
-static void vfp_load_regs(struct vfpreg *);
+static void vfp_state_load(lwp_t *, bool);
+static void vfp_state_save(lwp_t *);
+static void vfp_state_release(lwp_t *);
+
+const pcu_ops_t arm_vfp_ops = {
+	.pcu_id = PCU_FPU,
+	.pcu_state_load = vfp_state_load,
+	.pcu_state_save = vfp_state_save,
+	.pcu_state_release = vfp_state_release,
+};
 
 struct evcnt vfpevent_use;
 struct evcnt vfpevent_reuse;
@@ -98,51 +138,114 @@ struct evcnt vfpevent_reuse;
 static int undefined_test;
 
 static int
-vfp_test(u_int address, u_int instruction, trapframe_t *frame, int fault_code)
+vfp_test(u_int address, u_int insn, trapframe_t *frame, int fault_code)
 {
 
 	frame->tf_pc += INSN_SIZE;
 	++undefined_test;
-	return(0);
+	return 0;
+}
+
+#endif /* FPU_VFP */
+
+struct evcnt vfp_fpscr_ev = 
+    EVCNT_INITIALIZER(EVCNT_TYPE_TRAP, NULL, "VFP", "FPSCR traps");
+EVCNT_ATTACH_STATIC(vfp_fpscr_ev);
+
+static int
+vfp_fpscr_handler(u_int address, u_int insn, trapframe_t *frame, int fault_code)
+{
+	struct lwp * const l = curlwp;
+	const u_int regno = (insn >> 12) & 0xf;
+	/*
+	 * Only match move to/from the FPSCR register and we
+	 * can't be using the SP,LR,PC as a source.
+	 */
+	if ((insn & 0xffef0fff) != 0xeee10a10 || regno > 12)
+		return 1;
+
+	struct pcb * const pcb = lwp_getpcb(l);
+
+#ifdef FPU_VFP
+	/*
+	 * If FPU is valid somewhere, let's just reenable VFP and
+	 * retry the instruction (only safe thing to do since the
+	 * pcb has a stale copy).
+	 */
+	if (pcb->pcb_vfp.vfp_fpexc & VFP_FPEXC_EN)
+		return 1;
+#endif
+
+	if (__predict_false((l->l_md.md_flags & MDLWP_VFPUSED) == 0)) {
+		l->l_md.md_flags |= MDLWP_VFPUSED;
+		pcb->pcb_vfp.vfp_fpscr =
+		    (VFP_FPSCR_DN | VFP_FPSCR_FZ);	/* Runfast */
+	}
+
+	/*
+	 * We know know the pcb has the saved copy.
+	 */
+	register_t * const regp = &frame->tf_r0 + regno;
+	if (insn & 0x00100000) {
+		*regp = pcb->pcb_vfp.vfp_fpscr;
+	} else {
+		pcb->pcb_vfp.vfp_fpscr = *regp;
+	}
+
+	vfp_fpscr_ev.ev_count++;
+		
+	frame->tf_pc += INSN_SIZE;
+	return 0;
 }
 
+#ifndef FPU_VFP
+/*
+ * If we don't want VFP support, we still need to handle emulating VFP FPSCR
+ * instructions.
+ */
 void
 vfp_attach(void)
 {
-	void *uh;
-	uint32_t fpsid;
+	install_coproc_handler(VFP_COPROC, vfp_fpscr_handler);
+}
+
+#else
+void
+vfp_attach(void)
+{
+	struct cpu_info * const ci = curcpu();
 	const char *model = NULL;
+	void *uh;
 
 	uh = install_coproc_handler(VFP_COPROC, vfp_test);
 
 	undefined_test = 0;
 
-	read_fpsid(&fpsid);
+	const uint32_t fpsid = read_fpsid();
 
 	remove_coproc_handler(uh);
 
 	if (undefined_test != 0) {
-		aprint_normal("%s: No VFP detected\n",
-		    curcpu()->ci_dev->dv_xname);
-		curcpu()->ci_vfp.vfp_id = 0;
+		aprint_normal_dev(ci->ci_dev, "No VFP detected\n");
+		install_coproc_handler(VFP_COPROC, vfp_fpscr_handler);
+		ci->ci_vfp_id = 0;
 		return;
 	}
 
-	curcpu()->ci_vfp.vfp_id = fpsid;
-	switch (fpsid & ~ VFP_FPSID_REV_MSK)
-		{
-		case FPU_VFP10_ARM10E:
-			model = "VFP10 R1";
-			break;
-		case FPU_VFP11_ARM11:
-			model = "VFP11";
-			break;
-		default:
-			aprint_normal("%s: unrecognized VFP version %x\n",
-			    curcpu()->ci_dev->dv_xname, fpsid);
-			fpsid = 0;	/* Not recognised. */
-			return;
-		}
+	ci->ci_vfp_id = fpsid;
+	switch (fpsid & ~ VFP_FPSID_REV_MSK) {
+	case FPU_VFP10_ARM10E:
+		model = "VFP10 R1";
+		break;
+	case FPU_VFP11_ARM11:
+		model = "VFP11";
+		break;
+	default:
+		aprint_normal_dev(ci->ci_dev, "unrecognized VFP version %x\n",
+		    fpsid);
+		install_coproc_handler(VFP_COPROC, vfp_fpscr_handler);
+		return;
+	}
 
 	if (fpsid != 0) {
 		aprint_normal("vfp%d at %s: %s\n",
@@ -158,54 +261,38 @@ vfp_attach(void)
 }
 
 /* The real handler for VFP bounces.  */
-static int vfp_handler(u_int address, u_int instruction, trapframe_t *frame,
+static int
+vfp_handler(u_int address, u_int insn, trapframe_t *frame,
     int fault_code)
 {
-	struct cpu_info *ci = curcpu();
-	struct pcb *pcb;
-	struct lwp *l;
+	struct cpu_info * const ci = curcpu();
 
 	/* This shouldn't ever happen.  */
 	if (fault_code != FAULT_USER)
 		panic("VFP fault in non-user mode");
 
-	if (ci->ci_vfp.vfp_id == 0)
+	if (ci->ci_vfp_id == 0)
 		/* No VFP detected, just fault.  */
 		return 1;
 
-	l = curlwp;
-	pcb = lwp_getpcb(l);
-
-	if ((l->l_md.md_flags & MDP_VFPUSED) && ci->ci_vfp.vfp_fpcurlwp == l) {
-		uint32_t fpexc;
-
-		printf("VFP bounce @%x (insn=%x) lwp=%p\n", address,
-		    instruction, l);
-		read_fpexc(&fpexc);
-		if ((fpexc & VFP_FPEXC_EN) == 0)
-			printf("vfp not enabled\n");
-		vfp_saveregs_lwp(l, 1);
-		printf(" fpexc = 0x%08x  fpscr = 0x%08x\n", fpexc,
-		    pcb->pcb_vfp.vfp_fpscr);
-		printf(" fpinst = 0x%08x fpinst2 = 0x%08x\n", 
-		    pcb->pcb_vfp.vfp_fpinst,
-		    pcb->pcb_vfp.vfp_fpinst2);
-		return 1;
-	}
-
-	if (ci->ci_vfp.vfp_fpcurlwp != NULL)
-		vfp_saveregs_cpu(ci, 1);
-
-	KDASSERT(ci->ci_vfp.vfp_fpcurlwp == NULL);
-
-	KDASSERT(pcb->pcb_vfpcpu == NULL);
+	/*
+	 * If we are just changing/fetching FPSCR, don't bother loading it.
+	 */
+	if (!vfp_fpscr_handler(address, insn, frame, fault_code))
+		return 0;
 
-//	VFPCPU_LOCK(pcb, s);
+	pcu_load(&arm_vfp_ops);
 
-	pcb->pcb_vfpcpu = ci;
-	ci->ci_vfp.vfp_fpcurlwp = l;
+	/* Need to restart the faulted instruction.  */
+//	frame->tf_pc -= INSN_SIZE;
+	return 0;
+}
 
-//	VFPCPU_UNLOCK(pcb, s);
+static void
+vfp_state_load(lwp_t *l, bool used)
+{
+	struct pcb * const pcb = lwp_getpcb(l);
+	struct vfpreg * const fregs = &pcb->pcb_vfp;
 
 	/*
 	 * Instrument VFP usage -- if a process has not previously
@@ -215,150 +302,128 @@ static int vfp_handler(u_int address, u_
 	 * If a process has used the VFP, count a "used VFP, and took
 	 * a trap to use it again" event.
 	 */
-	if ((l->l_md.md_flags & MDP_VFPUSED) == 0) {
+	if (__predict_false((l->l_md.md_flags & MDLWP_VFPUSED) == 0)) {
 		vfpevent_use.ev_count++;
-		l->l_md.md_flags |= MDP_VFPUSED;
+		l->l_md.md_flags |= MDLWP_VFPUSED;
 		pcb->pcb_vfp.vfp_fpscr =
 		    (VFP_FPSCR_DN | VFP_FPSCR_FZ);	/* Runfast */
-	} else
+	} else {
 		vfpevent_reuse.ev_count++;
+	}
 
-	vfp_load_regs(&pcb->pcb_vfp);
-
-	/* Need to restart the faulted instruction.  */
-//	frame->tf_pc -= INSN_SIZE;
-	return 0;
-}
-
-static void
-vfp_load_regs(struct vfpreg *fregs)
-{
-	uint32_t fpexc;
+	if (fregs->vfp_fpexc & VFP_FPEXC_EN) {
+		/*
+		 * If we think the VFP is enabled, it must have be disabled by
+		 * vfp_state_release for another LWP so we can just restore
+		 * FPEXC and return since our VFP state is still loaded.
+		 */
+		write_fpexc(fregs->vfp_fpexc);
+		return;
+	}
 
 	/* Enable the VFP (so that we can write the registers).  */
-	read_fpexc(&fpexc);
+	uint32_t fpexc = read_fpexc();
 	KDASSERT((fpexc & VFP_FPEXC_EX) == 0);
 	write_fpexc(fpexc | VFP_FPEXC_EN);
 
 	load_vfpregs(fregs->vfp_regs);
 	write_fpscr(fregs->vfp_fpscr);
+
 	if (fregs->vfp_fpexc & VFP_FPEXC_EX) {
+		struct cpu_info * const ci = curcpu();
 		/* Need to restore the exception handling state.  */
-		switch (curcpu()->ci_vfp.vfp_id) {
+		switch (ci->ci_vfp_id) {
 		case FPU_VFP10_ARM10E:
 		case FPU_VFP11_ARM11:
 			write_fpinst2(fregs->vfp_fpinst2);
 			write_fpinst(fregs->vfp_fpinst);
 			break;
 		default:
-			panic("vfp_load_regs: Unsupported VFP");
+			panic("%s: Unsupported VFP %#x",
+			    __func__, ci->ci_vfp_id);
 		}
 	}
-	/* Finally, restore the FPEXC and enable the VFP. */
-	write_fpexc(fregs->vfp_fpexc | VFP_FPEXC_EN);
+
+	/* Finally, restore the FPEXC but don't enable the VFP. */
+	fregs->vfp_fpexc |= VFP_FPEXC_EN;
+	write_fpexc(fregs->vfp_fpexc);
 }
 
 void
-vfp_saveregs_cpu(struct cpu_info *ci, int save)
+vfp_state_save(lwp_t *l)
 {
-	struct lwp *l;
-	struct pcb *pcb;
-	uint32_t fpexc;
+	struct pcb * const pcb = lwp_getpcb(l);
+	struct vfpreg * const fregs = &pcb->pcb_vfp;
 
-	KDASSERT(ci == curcpu());
-
-	l = ci->ci_vfp.vfp_fpcurlwp;
-	if (l == NULL)
+	/*
+	 * If it's already disabled, then the state has been saved
+	 * (or discarded).
+	 */
+	if ((fregs->vfp_fpexc & VFP_FPEXC_EN) == 0)
 		return;
 
-	pcb = lwp_getpcb(l);
-	read_fpexc(&fpexc);
-
-	if (save) {
-		struct vfpreg *fregs = &pcb->pcb_vfp;
-
-		/*
-		 * Enable the VFP (so we can read the registers).  
-		 * Make sure the exception bit is cleared so that we can
-		 * safely dump the registers.
-		 */
-		write_fpexc((fpexc | VFP_FPEXC_EN) & ~VFP_FPEXC_EX);
+	/*
+	 * Enable the VFP (so we can read the registers).  
+	 * Make sure the exception bit is cleared so that we can
+	 * safely dump the registers.
+	 */
+	uint32_t fpexc = read_fpexc();
+	write_fpexc((fpexc | VFP_FPEXC_EN) & ~VFP_FPEXC_EX);
 
-		fregs->vfp_fpexc = fpexc;
-		if (fpexc & VFP_FPEXC_EX) {
-			/* Need to save the exception handling state */
-			switch (ci->ci_vfp.vfp_id) {
-			case FPU_VFP10_ARM10E:
-			case FPU_VFP11_ARM11:
-				read_fpinst(&fregs->vfp_fpinst);
-				read_fpinst2(&fregs->vfp_fpinst2);
-				break;
-			default:
-				panic("vfp_saveregs_cpu: Unsupported VFP");
-			}
+	fregs->vfp_fpexc = fpexc;
+	if (fpexc & VFP_FPEXC_EX) {
+		struct cpu_info * const ci = curcpu();
+		/* Need to save the exception handling state */
+		switch (ci->ci_vfp_id) {
+		case FPU_VFP10_ARM10E:
+		case FPU_VFP11_ARM11:
+			fregs->vfp_fpinst = read_fpinst();
+			fregs->vfp_fpinst2 = read_fpinst2();
+			break;
+		default:
+			panic("%s: Unsupported VFP %#x",
+			    __func__, ci->ci_vfp_id);
 		}
-		read_fpscr(&fregs->vfp_fpscr);
-		save_vfpregs(fregs->vfp_regs);
 	}
-	/* Disable the VFP.  */
-	write_fpexc(fpexc & ~VFP_FPEXC_EN);
-//	VFPCPU_LOCK(pcb, s);
+	fregs->vfp_fpscr = read_fpscr();
+	save_vfpregs(fregs->vfp_regs);
 
-        pcb->pcb_vfpcpu = NULL;
-        ci->ci_vfp.vfp_fpcurlwp = NULL;
-//	VFPCPU_UNLOCK(pcb, s);
+	/* Disable the VFP.  */
+	write_fpexc(fpexc);
 }
 
 void
-vfp_saveregs_lwp(struct lwp *l, int save)
+vfp_state_release(lwp_t *l)
 {
-	struct cpu_info *ci = curcpu();
-	struct cpu_info *oci;
-	struct pcb *pcb;
-
-	pcb = lwp_getpcb(l);
-	KDASSERT(pcb != NULL);
-
-//	VFPCPU_LOCK(pcb, s);
+	struct pcb * const pcb = lwp_getpcb(l);
 
-	oci = pcb->pcb_vfpcpu;
-	if (oci == NULL) {
-		// VFPCPU_UNLOCK(pcb, s);
-		return;
-	}
+	/*
+	 * Now mark the VFP as disabled (and our state has been already
+	 * saved or is being discarded).
+	 */
+	pcb->pcb_vfp.vfp_fpexc &= ~VFP_FPEXC_EN;
 
-#if defined(MULTIPROCESSOR)
 	/*
-	 * On a multiprocessor system this is where we would send an IPI
-	 * to the processor holding the VFP state for this process.
+	 * Turn off the FPU so the next time a VFP instruction is issued
+	 * an exception happens.  We don't know if this LWP's state was
+	 * loaded but if we turned off the FPU for some other LWP, when
+	 * pcu_load invokes vfp_state_load it will see that VFP_FPEXC_EN
+	 * is still set so it just restroe fpexc and return since its
+	 * contents are still sitting in the VFP.
 	 */
-#error MULTIPROCESSOR
-#else
-	KASSERT(ci->ci_vfp.vfp_fpcurlwp == l);
-//	VFPCPU_UNLOCK(pcb, s);
-	vfp_saveregs_cpu(ci, save);
-#endif
+	write_fpexc(read_fpexc() & ~VFP_FPEXC_EN);
 }
 
 void
 vfp_savecontext(void)
 {
-	struct cpu_info *ci = curcpu();
-	uint32_t fpexc;
-
-	if (ci->ci_vfp.vfp_fpcurlwp != NULL) {
-		read_fpexc(&fpexc);
-		write_fpexc(fpexc & ~VFP_FPEXC_EN);
-	}
+	pcu_save(&arm_vfp_ops);
 }
 
 void
-vfp_loadcontext(struct lwp *l)
+vfp_discardcontext(void)
 {
-	uint32_t fpexc;
-
-	if (curcpu()->ci_vfp.vfp_fpcurlwp == l) {
-		read_fpexc(&fpexc);
-		write_fpexc(fpexc | VFP_FPEXC_EN);
-	}
+	pcu_discard(&arm_vfp_ops);
 }
+
+#endif /* FPU_VFP */

Reply via email to