Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 9 Mar 2014 03:00:03 +0000 (UTC)
From:      Ian Lepore <ian@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r262941 - in head/sys/arm: arm include
Message-ID:  <201403090300.s29303B2007478@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ian
Date: Sun Mar  9 03:00:03 2014
New Revision: 262941
URL: http://svnweb.freebsd.org/changeset/base/262941

Log:
  Rework the VFP code that handles demand-based save and restore of state.
  
  The old code was full of complexity that would only matter if the
  kernel itself used the VFP hardware.  Now that's reduced to either killing
  the userland process or panicking the kernel on an illegal VFP instruction.
  
  This removes most of the complexity from the assembler code, reducing it
  to just calling the save code if the outgoing thread used the VFP.
  
  The routine that stores the VFP state now takes a flag that indicates
  whether the hardware should be disabled after saving state.  Right now it
  always is, but this makes the code ready to be used by get/set_mcontext()
  (doing so will be addressed in a future commit).
  
  Remove the arm-specific pc_vfpcthread from struct pcpu and use the MI
  field pc_fpcurthread instead.
  
  Reviewed by:	cognet

Modified:
  head/sys/arm/arm/genassym.c
  head/sys/arm/arm/swtch.S
  head/sys/arm/arm/vfp.c
  head/sys/arm/include/pcpu.h
  head/sys/arm/include/vfp.h

Modified: head/sys/arm/arm/genassym.c
==============================================================================
--- head/sys/arm/arm/genassym.c	Sun Mar  9 02:28:30 2014	(r262940)
+++ head/sys/arm/arm/genassym.c	Sun Mar  9 03:00:03 2014	(r262941)
@@ -119,9 +119,7 @@ ASSYM(ARM_RAS_END, ARM_RAS_END);
 
 #ifdef VFP
 ASSYM(PCB_VFPSTATE, offsetof(struct pcb, pcb_vfpstate));
-ASSYM(PCB_VFPCPU, offsetof(struct pcb, pcb_vfpcpu));
 
-ASSYM(PC_VFPCTHREAD, offsetof(struct pcpu, pc_vfpcthread));
 ASSYM(PC_CPU, offsetof(struct pcpu, pc_cpu));
 
 ASSYM(PC_CURPMAP, offsetof(struct pcpu, pc_curpmap));

Modified: head/sys/arm/arm/swtch.S
==============================================================================
--- head/sys/arm/arm/swtch.S	Sun Mar  9 02:28:30 2014	(r262940)
+++ head/sys/arm/arm/swtch.S	Sun Mar  9 03:00:03 2014	(r262941)
@@ -84,6 +84,8 @@
 #include <machine/asm.h>
 #include <machine/asmacros.h>
 #include <machine/armreg.h>
+#include <machine/vfp.h>
+
 __FBSDID("$FreeBSD$");
 
 #define DOMAIN_CLIENT	0x01
@@ -102,6 +104,10 @@ __FBSDID("$FreeBSD$");
 	ldr	tmp, .Lcurpcpu
 #endif
 
+#ifdef VFP
+	.fpu vfp	/* allow VFP instructions */
+#endif
+
 .Lcurpcpu:
         .word   _C_LABEL(__pcpu)
 	.word	PCPU_SIZE
@@ -121,16 +127,10 @@ ENTRY(cpu_throw)
 	GET_PCPU(r7, r9)
 
 #ifdef VFP
-	/*
-	 * vfp_discard will clear pcpu->pc_vfpcthread, and modify
-	 * and modify the control as needed.
-	 */
-	ldr     r4, [r7, #(PC_VFPCTHREAD)]      /* this thread using vfp? */
-	cmp     r0, r4
-	bne     3f
-	bl      _C_LABEL(vfp_discard)           /* yes, shut down vfp */
-3:
-#endif		/* VFP */
+	fmrx	r0, fpexc		/* This thread is dying, if the VFP */
+	tst	r0, #(VFPEXC_EN)	/* is enabled, go shut it down */
+	blne	_C_LABEL(vfp_discard)	/* without preserving its state. */
+#endif
 
 	ldr	r7, [r5, #(TD_PCB)]		/* r7 = new thread's PCB */
   
@@ -319,30 +319,14 @@ ENTRY(cpu_switch)
 	/* rem: interrupts are enabled */
 
 #ifdef VFP
-	/*
-	 * vfp_store will clear pcpu->pc_vfpcthread, save 
-	 * registers and state, and modify the control as needed.
-	 * a future exception will bounce the backup settings in the fp unit.
-	 * XXX vfp_store can't change r4
-	 */
-	GET_PCPU(r7, r8)
-	ldr	r8, [r7, #(PC_VFPCTHREAD)]
-	cmp	r4, r8				/* old thread used vfp? */
-	bne	1f				/* no, don't save */
-	cmp	r1, r4				/* same thread ? */
-	beq	1f				/* yes, skip vfp store */
-#ifdef SMP
-	ldr	r8, [r7, #(PC_CPU)]		/* last used on this cpu? */
-	ldr	r3, [r2, #(PCB_VFPCPU)]
-	cmp	r8, r3		/* last cpu to use these registers? */
-	bne	1f		/* no. these values are stale */
+	fmrx	r0, fpexc		/* If the VFP is enabled */
+	tst	r0, #(VFPEXC_EN)	/* the current thread has */
+	movne	r1, #1			/* used it, so go save */
+	addne	r0, r2, #(PCB_VFPSTATE)	/* the state into the PCB */
+	blne	_C_LABEL(vfp_store)	/* and disable the VFP. */
 #endif
-	add	r0, r2, #(PCB_VFPSTATE)
-	bl	_C_LABEL(vfp_store)
-1:
-#endif		/* VFP */
 
-	/* r1 now free! */
+	/* r0-r3 now free! */
 
 	/* Third phase : restore saved context */
 
@@ -520,26 +504,12 @@ ENTRY(savectx)
 	add	r2, r0, #(PCB_R8)
 	stmia	r2, {r8-r13}
 #ifdef VFP
-	/*
-	 * vfp_store will clear pcpu->pc_vfpcthread, save 
-	 * registers and state, and modify the control as needed.
-	 * a future exception will bounce the backup settings in the fp unit.
-	 */
-	GET_PCPU(r7, r4)
-	ldr	r4, [r7, #(PC_VFPCTHREAD)]      /* vfp thread */
-	ldr	r2, [r7, #(PC_CURTHREAD)]       /* current thread */
-	cmp	r4, r2
-	bne	1f
-#ifdef SMP
-	ldr	r2, [r7, #(PC_CPU)]     /* last used on this cpu? */
-	ldr	r3, [r0, #(PCB_VFPCPU)]
-	cmp	r2, r3
-	bne	1f              /* no. these values are stale */
+	fmrx	r2, fpexc		/* If the VFP is enabled */
+	tst	r2, #(VFPEXC_EN)	/* the current thread has */
+	movne	r1, #1			/* used it, so go save */
+	addne	r0, r0, #(PCB_VFPSTATE)	/* the state into the PCB */
+	blne	_C_LABEL(vfp_store)	/* and disable the VFP. */
 #endif
-	add	r0, r0, #(PCB_VFPSTATE)
-	bl	_C_LABEL(vfp_store)
-1:
-#endif		/* VFP */
 	add	sp, sp, #4;
 	ldmfd	sp!, {r4-r7, pc}
 END(savectx)

Modified: head/sys/arm/arm/vfp.c
==============================================================================
--- head/sys/arm/arm/vfp.c	Sun Mar  9 02:28:30 2014	(r262940)
+++ head/sys/arm/arm/vfp.c	Sun Mar  9 03:00:03 2014	(r262941)
@@ -1,4 +1,5 @@
-/*
+/*-
+ * Copyright (c) 2014 Ian Lepore <ian@freebsd.org>
  * Copyright (c) 2012 Mark Tinguely
  *
  * All rights reserved.
@@ -34,6 +35,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/proc.h>
 #include <sys/kernel.h>
 
+#include <machine/armreg.h>
 #include <machine/frame.h>
 #include <machine/fp.h>
 #include <machine/pcb.h>
@@ -41,12 +43,8 @@ __FBSDID("$FreeBSD$");
 #include <machine/vfp.h>
 
 /* function prototypes */
-unsigned int get_coprocessorACR(void);
 static int vfp_bounce(u_int, u_int, struct trapframe *, int);
 static void vfp_restore(struct vfp_state *);
-void	vfp_discard(void);
-void	vfp_store(struct vfp_state *);
-void	set_coprocessorACR(u_int);
 
 extern int vfp_exists;
 static struct undefined_handler vfp10_uh, vfp11_uh;
@@ -64,7 +62,20 @@ static int is_d32;
     val; \
 })
 
-u_int
+/*
+ * Work around an issue with GCC where the asm it generates is not unified
+ * syntax and fails to assemble because it expects the ldcleq instruction in the
+ * form ldc<c>l, not in the UAL form ldcl<c>, and similar for stcleq.
+ */
+#ifdef __clang__
+#define	LDCLNE  "ldclne "
+#define	STCLNE  "stclne "
+#else
+#define	LDCLNE  "ldcnel "
+#define	STCLNE  "stcnel "
+#endif
+
+static u_int
 get_coprocessorACR(void)
 {
 	u_int val;
@@ -72,7 +83,7 @@ get_coprocessorACR(void)
 	return val;
 }
 
-void
+static void
 set_coprocessorACR(u_int val)
 {
 	__asm __volatile("mcr p15, 0, %0, c1, c0, 2\n\t"
@@ -136,147 +147,103 @@ SYSINIT(vfp, SI_SUB_CPU, SI_ORDER_ANY, v
 static int
 vfp_bounce(u_int addr, u_int insn, struct trapframe *frame, int code)
 {
-	u_int fpexc;
+	u_int cpu, fpexc;
 	struct pcb *curpcb;
-	struct thread *vfptd;
-	int i;
 
-	if (!vfp_exists)
-		return 1;		/* vfp does not exist */
-	i = disable_interrupts(I32_bit|F32_bit);
-	fpexc = fmrx(VFPEXC);		/* read the vfp exception reg */
-	if (fpexc & VFPEXC_EN) {
-		vfptd = PCPU_GET(vfpcthread);
-		/* did the kernel call the vfp or exception that expect us
-		 * to emulate the command. Newer hardware does not require
-		 * emulation, so we don't emulate yet.
-		 */
-#ifdef SMP
-		/* don't save if newer registers are on another processor */
-		if (vfptd /* && (vfptd == curthread) */ &&
-		   (vfptd->td_pcb->pcb_vfpcpu == PCPU_GET(cpu)))
-#else
-		/* someone did not save their registers, */
-		if (vfptd /* && (vfptd == curthread) */)
-#endif
-			vfp_store(&vfptd->td_pcb->pcb_vfpstate);
+	if ((code & FAULT_USER) == 0)
+		panic("undefined floating point instruction in supervisor mode");
 
-		fpexc &= ~VFPEXC_EN;
-		fmxr(VFPEXC, fpexc);	/* turn vfp hardware off */
-		if (vfptd == curthread) {
-			/* kill the process - we do not handle emulation */
-			restore_interrupts(i);
-			killproc(curthread->td_proc, "vfp emulation");
-			return 1;
-		}
-		/* should not happen. someone did not save their context */
-		printf("vfp_bounce: vfpcthread: %p curthread: %p\n",
-			vfptd, curthread);
+	critical_enter();
+
+	/*
+	 * If the VFP is already on and we got an undefined instruction, then
+	 * something tried to executate a truly invalid instruction that maps to
+	 * the VFP.
+	 */
+	fpexc = fmrx(VFPEXC);
+	if (fpexc & VFPEXC_EN) {
+		/* kill the process - we do not handle emulation */
+		critical_exit();
+		killproc(curthread->td_proc, "vfp emulation");
+		return 1;
 	}
-	fpexc |= VFPEXC_EN;
-	fmxr(VFPEXC, fpexc);	/* enable the vfp and repeat command */
-	curpcb = curthread->td_pcb;
-	/* If we were the last process to use the VFP, the process did not
-	 * use a VFP on another processor, then the registers in the VFP
-	 * will still be ours and are current. Eventually, we will make the
-	 * restore smarter.
+
+	/*
+	 * 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.
 	 */
-	vfp_restore(&curpcb->pcb_vfpstate);
-#ifdef SMP
-	curpcb->pcb_vfpcpu = PCPU_GET(cpu);
-#endif
-	PCPU_SET(vfpcthread, curthread);
-	restore_interrupts(i);
-	return 0;
+	fmxr(VFPEXC, fpexc | VFPEXC_EN);
+	curpcb = curthread->td_pcb;
+	cpu = PCPU_GET(cpu);
+	if (curpcb->pcb_vfpcpu != cpu || curthread != PCPU_GET(fpcurthread)) {
+		vfp_restore(&curpcb->pcb_vfpstate);
+		curpcb->pcb_vfpcpu = cpu;
+		PCPU_SET(fpcurthread, curthread);
+	}
+
+	critical_exit();
+	return (0);
 }
 
-/* vfs_store is called from from a VFP command to restore the registers and
- * turn on the VFP hardware.
- * Eventually we will use the information that this process was the last
- * to use the VFP hardware and bypass the restore, just turn on the hardware.
+/*
+ * Restore the given state to the VFP hardware.
  */
 static void
 vfp_restore(struct vfp_state *vfpsave)
 {
 	u_int vfpscr = 0;
 
-	/*
-	 * Work around an issue with GCC where the asm it generates is
-	 * not unified syntax and fails to assemble because it expects
-	 * the ldcleq instruction in the form ldc<c>l, not in the UAL
-	 * form ldcl<c>, and similar for stcleq.
-	 */
-#ifdef __clang__
-#define	ldclne	"ldclne"
-#define	stclne	"stclne"
-#else
-#define	ldclne	"ldcnel"
-#define	stclne	"stcnel"
-#endif
-	if (vfpsave) {
-		__asm __volatile("ldc	p10, c0, [%1], #128\n" /* d0-d15 */
+	__asm __volatile("ldc	p10, c0, [%1], #128\n" /* d0-d15 */
 			"cmp	%2, #0\n"		/* -D16 or -D32? */
-			ldclne"	p11, c0, [%1], #128\n"	/* d16-d31 */
+			LDCLNE "p11, c0, [%1], #128\n"	/* d16-d31 */
 			"addeq	%1, %1, #128\n"		/* skip missing regs */
 			"ldr	%0, [%1]\n"		/* set old vfpscr */
 			"mcr	p10, 7, %0, cr1, c0, 0\n"
 			: "=&r" (vfpscr) : "r" (vfpsave), "r" (is_d32) : "cc");
-	}
 }
 
-/* vfs_store is called from switch to save the vfp hardware registers
- * into the pcb before switching to another process.
- * we already know that the new process is different from this old
- * process and that this process last used the VFP registers.
- * Below we check to see if the VFP has been enabled since the last
- * register save.
- * This routine will exit with the VFP turned off. The next VFP user
- * will trap to restore its registers and turn on the VFP hardware.
+/*
+ * If the VFP is on, save its current state and turn it off if requested to do
+ * so.  If the VFP is not on, does not change the values at *vfpsave.  Caller is
+ * responsible for preventing a context switch while this is running.
  */
 void
-vfp_store(struct vfp_state *vfpsave)
+vfp_store(struct vfp_state *vfpsave, boolean_t disable_vfp)
 {
-	u_int tmp, vfpscr = 0;
+	u_int tmp, vfpscr;
 
 	tmp = fmrx(VFPEXC);		/* Is the vfp enabled? */
-	if (vfpsave && (tmp & VFPEXC_EN)) {
-		__asm __volatile("stc	p11, c0, [%1], #128\n" /* d0-d15 */
+	if (tmp & VFPEXC_EN) {
+		__asm __volatile(
+			"stc	p11, c0, [%1], #128\n"  /* d0-d15 */
 			"cmp	%2, #0\n"		/* -D16 or -D32? */
-			stclne"	p11, c0, [%1], #128\n"	/* d16-d31 */
+			STCLNE "p11, c0, [%1], #128\n"	/* d16-d31 */
 			"addeq	%1, %1, #128\n"		/* skip missing regs */
 			"mrc	p10, 7, %0, cr1, c0, 0\n" /* fmxr(VFPSCR) */
 			"str	%0, [%1]\n"		/* save vfpscr */
 			: "=&r" (vfpscr) : "r" (vfpsave), "r" (is_d32) : "cc");
+		if (disable_vfp)
+			fmxr(VFPEXC , tmp & ~VFPEXC_EN);
 	}
-#undef ldcleq
-#undef stcleq
-
-#ifndef SMP
-		/* eventually we will use this information for UP also */
-	PCPU_SET(vfpcthread, 0);
-#endif
-	tmp &= ~VFPEXC_EN;	/* disable the vfp hardware */
-	fmxr(VFPEXC , tmp);
 }
 
-/* discard the registers at cpu_thread_free() when fpcurthread == td.
- * Turn off the VFP hardware.
+/*
+ * If the VFP hardware is on, the current thread was using it but now that
+ * thread is dying.  Turn off the VFP and set pcpu fpcurthread to 0, to indicate
+ * that the VFP hardware state does not belong to any thread.   Called only from
+ * cpu_throw(), so we don't have to worry about a context switch here.
  */
 void
 vfp_discard()
 {
-	u_int tmp = 0;
+	u_int tmp;
 
-	/*
-	 * No need to protect the access to vfpcthread by disabling
-	 * interrupts, since it's called from cpu_throw(), who is called
-	 * with interrupts disabled.
-	 */
-	  
-	PCPU_SET(vfpcthread, 0);	/* permanent forget about reg */
 	tmp = fmrx(VFPEXC);
-	tmp &= ~VFPEXC_EN;		/* turn off VFP hardware */
-	fmxr(VFPEXC, tmp);
+	if (tmp & VFPEXC_EN) {
+		fmxr(VFPEXC, tmp & ~VFPEXC_EN);
+		PCPU_SET(fpcurthread, 0);
+	}
 }
 
 #endif

Modified: head/sys/arm/include/pcpu.h
==============================================================================
--- head/sys/arm/include/pcpu.h	Sun Mar  9 02:28:30 2014	(r262940)
+++ head/sys/arm/include/pcpu.h	Sun Mar  9 03:00:03 2014	(r262941)
@@ -46,9 +46,8 @@ struct vmspace;
 	unsigned int pc_vfpsid;						\
 	unsigned int pc_vfpmvfr0;					\
 	unsigned int pc_vfpmvfr1;					\
-	struct thread *pc_vfpcthread;					\
 	struct pmap *pc_curpmap;					\
-	char __pad[133]
+	char __pad[137]
 #else
 #define PCPU_MD_FIELDS							\
 	char __pad[157]

Modified: head/sys/arm/include/vfp.h
==============================================================================
--- head/sys/arm/include/vfp.h	Sun Mar  9 02:28:30 2014	(r262940)
+++ head/sys/arm/include/vfp.h	Sun Mar  9 03:00:03 2014	(r262941)
@@ -126,6 +126,10 @@
 #define COPROC10		(0x3 << 20)
 #define COPROC11		(0x3 << 22)
 
+#ifndef LOCORE
 void    vfp_init(void);
+void    vfp_store(struct vfp_state *, boolean_t);
+void    vfp_discard(void);
+#endif
 
 #endif



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201403090300.s29303B2007478>