Module Name:    src
Committed By:   dsl
Date:           Tue Feb 25 22:16:52 UTC 2014

Modified Files:
        src/sys/arch/i386/i386: cpufunc.S
        src/sys/arch/x86/include: cpu_extended_state.h cpufunc.h fpu.h
        src/sys/arch/x86/x86: cpu.c fpu.c vm_machdep.c

Log Message:
Add support for saving the AVX-256 ymm registers during FPU context switches.
Add support for the forthcoming AVX-512 registers.
Code compiled with -mavx seems to work, but I've not tested context
  switches with live ymm registers.
There is a small cost on fork/exec (a larger area is copied/zerod),
  but I don't think the ymm registers are read/written unless they
  have been used.
The code use XSAVE on all cpus, I'm not brave enough to enable XSAVEOPT.


To generate a diff of this commit:
cvs rdiff -u -r1.18 -r1.19 src/sys/arch/i386/i386/cpufunc.S
cvs rdiff -u -r1.8 -r1.9 src/sys/arch/x86/include/cpu_extended_state.h
cvs rdiff -u -r1.17 -r1.18 src/sys/arch/x86/include/cpufunc.h
cvs rdiff -u -r1.5 -r1.6 src/sys/arch/x86/include/fpu.h
cvs rdiff -u -r1.109 -r1.110 src/sys/arch/x86/x86/cpu.c
cvs rdiff -u -r1.8 -r1.9 src/sys/arch/x86/x86/fpu.c
cvs rdiff -u -r1.23 -r1.24 src/sys/arch/x86/x86/vm_machdep.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/i386/i386/cpufunc.S
diff -u src/sys/arch/i386/i386/cpufunc.S:1.18 src/sys/arch/i386/i386/cpufunc.S:1.19
--- src/sys/arch/i386/i386/cpufunc.S:1.18	Wed Feb 12 23:24:09 2014
+++ src/sys/arch/i386/i386/cpufunc.S	Tue Feb 25 22:16:52 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: cpufunc.S,v 1.18 2014/02/12 23:24:09 dsl Exp $	*/
+/*	$NetBSD: cpufunc.S,v 1.19 2014/02/25 22:16:52 dsl Exp $	*/
 
 /*-
  * Copyright (c) 1998, 2007 The NetBSD Foundation, Inc.
@@ -38,7 +38,7 @@
 #include <sys/errno.h>
 
 #include <machine/asm.h>
-__KERNEL_RCSID(0, "$NetBSD: cpufunc.S,v 1.18 2014/02/12 23:24:09 dsl Exp $");
+__KERNEL_RCSID(0, "$NetBSD: cpufunc.S,v 1.19 2014/02/25 22:16:52 dsl Exp $");
 
 #include "opt_xen.h"
 
@@ -160,6 +160,24 @@ ENTRY(rdmsr_safe)
 	movl	%eax, PCB_ONFAULT(%ecx)
 
 	ret
+END(rdmsr_safe)
+
+/* uint64_t rdxcr(uint32_t) */
+ENTRY(rdxcr)
+	movl	4(%esp), %ecx	/* extended control reg number */
+	xgetbv			/* Read to %edx:%eax */
+	ret
+END(rdxcr)
+
+/* void wrxcr(uint32_t, uint64_t) */
+ENTRY(wrxcr)
+	movl	4(%esp), %ecx	/* extended control reg number */
+	movl	8(%esp), %eax	/* feature mask bits */
+	movl	12(%esp), %edx
+	xsetbv
+	ret
+END(wrxcr)
+	
 
 /*
  * MSR operations fault handler
@@ -389,15 +407,41 @@ ENTRY(fxrstor)
 	ret
 END(fxrstor)
 
+ENTRY(xsave)
+	movl	4(%esp), %ecx
+	movl	8(%esp), %eax	/* feature mask bits */
+	movl	12(%esp), %edx
+	xsave	(%ecx)
+	ret
+END(xsave)
+
+ENTRY(xsaveopt)
+	movl	4(%esp), %ecx
+	movl	8(%esp), %eax	/* feature mask bits */
+	movl	12(%esp), %edx
+	xsaveopt	(%ecx)
+	ret
+END(xsaveopt)
+
+ENTRY(xrstor)
+	movl	4(%esp), %ecx
+	movl	8(%esp), %eax	/* feature mask bits */
+	movl	12(%esp), %edx
+	xrstor	(%eax)
+	ret
+END(xrstor)
+
 ENTRY(x86_stmxcsr)
 	movl	4(%esp), %eax
 	stmxcsr	(%eax)
 	ret
+END(x86_stmxcsr)
 
 ENTRY(x86_ldmxcsr)
 	movl	4(%esp), %eax
 	ldmxcsr	(%eax)
 	ret
+END(x86_ldmxcsr)
 
 ENTRY(fldummy)
 	ffree	%st(7)

Index: src/sys/arch/x86/include/cpu_extended_state.h
diff -u src/sys/arch/x86/include/cpu_extended_state.h:1.8 src/sys/arch/x86/include/cpu_extended_state.h:1.9
--- src/sys/arch/x86/include/cpu_extended_state.h:1.8	Tue Feb 18 18:39:10 2014
+++ src/sys/arch/x86/include/cpu_extended_state.h	Tue Feb 25 22:16:52 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: cpu_extended_state.h,v 1.8 2014/02/18 18:39:10 dsl Exp $	*/
+/*	$NetBSD: cpu_extended_state.h,v 1.9 2014/02/25 22:16:52 dsl Exp $	*/
 
 #ifndef _X86_CPU_EXTENDED_STATE_H_
 #define _X86_CPU_EXTENDED_STATE_H_
@@ -122,19 +122,13 @@ struct fxsave_os {
 	uint16_t	fxo_dflt_cw;	/* Control word for signal handlers */
 };
 
-union savefpu {
-	struct save87		sv_87;
-	struct fxsave		sv_xmm;
-	struct fxsave_os	sv_os;
-};
-
 /*
- * For XSAVE a 64byte header follows the above.
+ * For XSAVE a 64byte header follows the fxsave data.
  * Currently it only contains one field of which only 3 bits are defined.
  * Some other parts must be zero - zero it all.
  *
  * The xsh_xstate_bv bits match those of XCR0:
- *   XCR0_X87        0x00000001      x87 FPU/MMX state (always set)
+ *   XCR0_X87        0x00000001      x87 FPU/MMX state
  *   XCR0_SSE        0x00000002      SSE state
  *   XCR0_AVX        0x00000004      AVX state (ymmn registers)
  *
@@ -143,11 +137,12 @@ union savefpu {
  */
 
 struct xsave_header {
+	uint64_t	xsh_fxsave[64];	/* to align in the union */
 	uint64_t	xsh_xstate_bv;	/* bitmap of saved sub structures */
 	uint64_t	xsh_rsrvd[2];	/* must be zero */
 	uint64_t	xsh_reserved[5];/* best if zero */
 };
-__CTASSERT(sizeof (struct xsave_header) == 64);
+__CTASSERT(sizeof (struct xsave_header) == 512 + 64);
 
 /*
  * The ymm save area actually follows the xsave_header.
@@ -157,6 +152,20 @@ struct xsave_ymm {
 };
 __CTASSERT(sizeof (struct xsave_ymm) == 256);
 
+/*
+ * The following union is placed at the end of the pcb.
+ * It is defined this way to separate the definitions and to
+ * minimise the number of union/struct selectors.
+ * NB: Some userspace stuff (eg firefox) uses it to parse ucontext.
+ */
+union savefpu {
+	struct save87		sv_87;
+	struct fxsave		sv_xmm;
+#ifdef _KERNEL
+	struct fxsave_os	sv_os;
+	struct xsave_header	sv_xsave_hdr;
+#endif
+};
 
 /*
  * 80387 control and status word bits

Index: src/sys/arch/x86/include/cpufunc.h
diff -u src/sys/arch/x86/include/cpufunc.h:1.17 src/sys/arch/x86/include/cpufunc.h:1.18
--- src/sys/arch/x86/include/cpufunc.h:1.17	Thu Feb 13 19:37:08 2014
+++ src/sys/arch/x86/include/cpufunc.h	Tue Feb 25 22:16:52 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: cpufunc.h,v 1.17 2014/02/13 19:37:08 dsl Exp $	*/
+/*	$NetBSD: cpufunc.h,v 1.18 2014/02/25 22:16:52 dsl Exp $	*/
 
 /*-
  * Copyright (c) 1998, 2007 The NetBSD Foundation, Inc.
@@ -77,27 +77,36 @@ void	x86_hlt(void);
 void	x86_stihlt(void);
 u_int	x86_getss(void);
 
-struct save87;
-struct fxsave;
+/* fpu save, restore etc */
+union savefpu;
 void	fldcw(const uint16_t *);
 void	fnclex(void);
 void	fninit(void);
-void	fnsave(struct save87 *);
+void	fnsave(union savefpu *);
 void	fnstcw(uint16_t *);
 uint16_t fngetsw(void);
 void	fnstsw(uint16_t *);
-void	frstor(const struct save87 *);
+void	frstor(const union savefpu *);
 void	fwait(void);
 void	clts(void);
 void	stts(void);
-void	fxsave(struct fxsave *);
-void	fxrstor(const struct fxsave *);
+void	fxsave(union savefpu *);
+void	fxrstor(const union savefpu *);
 void	x86_ldmxcsr(const uint32_t *);
 void	x86_stmxcsr(uint32_t *);
 
 void	fldummy(void);
 void	fp_divide_by_0(void);
 
+/* Extended processor state functions (for AVX registers etc) */
+
+uint64_t rdxcr(uint32_t);		/* xgetbv */
+void	wrxcr(uint32_t, uint64_t);	/* xsetgv */
+
+void	xrstor(const union savefpu *, uint64_t);
+void	xsave(union savefpu *, uint64_t);
+void	xsaveopt(union savefpu *, uint64_t);
+
 void	x86_monitor(const void *, uint32_t, uint32_t);
 void	x86_mwait(uint32_t, uint32_t);
 /* x86_cpuid2() writes four 32bit values, %eax, %ebx, %ecx and %edx */
@@ -134,14 +143,6 @@ void		wrmsr_locked(u_int, u_int, uint64_
 void		setfs(int);
 void		setusergs(int);
 
-/* Extended processor state functions (for AVX registers etc) */
-
-uint64_t	rdxcr(uint32_t);		/* xgetbv */
-void		wrxcr(uint32_t, uint64_t);	/* xsetgv */
-void		xrstor(const void *, uint64_t);
-void		xsave(void *, uint64_t);
-void		xsaveopt(const void *, uint64_t);
-
 #endif /* _KERNEL */
 
 #endif /* !_X86_CPUFUNC_H_ */

Index: src/sys/arch/x86/include/fpu.h
diff -u src/sys/arch/x86/include/fpu.h:1.5 src/sys/arch/x86/include/fpu.h:1.6
--- src/sys/arch/x86/include/fpu.h:1.5	Sun Feb 23 22:35:27 2014
+++ src/sys/arch/x86/include/fpu.h	Tue Feb 25 22:16:52 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: fpu.h,v 1.5 2014/02/23 22:35:27 dsl Exp $	*/
+/*	$NetBSD: fpu.h,v 1.6 2014/02/25 22:16:52 dsl Exp $	*/
 
 #ifndef	_X86_FPU_H_
 #define	_X86_FPU_H_
@@ -28,6 +28,9 @@ void fpu_save_area_clear(struct lwp *, u
 /* Reset control words only - for signal handlers */
 void fpu_save_area_reset(struct lwp *);
 
+/* Copy data outside pcb during fork */
+void fpu_save_area_fork(struct pcb *, const struct pcb *);
+
 /* Load FP registers with user-supplied values */
 void process_write_fpregs_xmm(struct lwp *lwp, const struct fxsave *fpregs);
 void process_write_fpregs_s87(struct lwp *lwp, const struct save87 *fpregs);

Index: src/sys/arch/x86/x86/cpu.c
diff -u src/sys/arch/x86/x86/cpu.c:1.109 src/sys/arch/x86/x86/cpu.c:1.110
--- src/sys/arch/x86/x86/cpu.c:1.109	Wed Feb 19 21:23:02 2014
+++ src/sys/arch/x86/x86/cpu.c	Tue Feb 25 22:16:52 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: cpu.c,v 1.109 2014/02/19 21:23:02 dsl Exp $	*/
+/*	$NetBSD: cpu.c,v 1.110 2014/02/25 22:16:52 dsl Exp $	*/
 
 /*-
  * Copyright (c) 2000-2012 NetBSD Foundation, Inc.
@@ -62,7 +62,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.109 2014/02/19 21:23:02 dsl Exp $");
+__KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.110 2014/02/25 22:16:52 dsl Exp $");
 
 #include "opt_ddb.h"
 #include "opt_mpbios.h"		/* for MPDEBUG */
@@ -552,29 +552,41 @@ cpu_childdetached(device_t self, device_
 void
 cpu_init(struct cpu_info *ci)
 {
+	uint32_t cr4;
 
 	lcr0(rcr0() | CR0_WP);
 
+	cr4 = rcr4();
 	/*
 	 * On a P6 or above, enable global TLB caching if the
 	 * hardware supports it.
 	 */
 	if (cpu_feature[0] & CPUID_PGE)
-		lcr4(rcr4() | CR4_PGE);	/* enable global TLB caching */
+		cr4 |= CR4_PGE;	/* enable global TLB caching */
 
 	/*
 	 * If we have FXSAVE/FXRESTOR, use them.
 	 */
 	if (cpu_feature[0] & CPUID_FXSR) {
-		lcr4(rcr4() | CR4_OSFXSR);
+		cr4 |= CR4_OSFXSR;
 
 		/*
 		 * If we have SSE/SSE2, enable XMM exceptions.
 		 */
 		if (cpu_feature[0] & (CPUID_SSE|CPUID_SSE2))
-			lcr4(rcr4() | CR4_OSXMMEXCPT);
+			cr4 |= CR4_OSXMMEXCPT;
 	}
 
+	/* If xsave is supported, enable it */
+	if (cpu_feature[1] & CPUID2_XSAVE)
+		cr4 |= CR4_OSXSAVE;
+
+	lcr4(cr4);
+
+	/* If xsave is enabled, enable all fpu features */
+	if (cr4 & CR4_OSXSAVE)
+		wrxcr(0, x86_xsave_features & XCR0_FPU);
+
 #ifdef MTRR
 	/*
 	 * On a P6 or above, initialize MTRR's if the hardware supports them.

Index: src/sys/arch/x86/x86/fpu.c
diff -u src/sys/arch/x86/x86/fpu.c:1.8 src/sys/arch/x86/x86/fpu.c:1.9
--- src/sys/arch/x86/x86/fpu.c:1.8	Sun Feb 23 22:35:28 2014
+++ src/sys/arch/x86/x86/fpu.c	Tue Feb 25 22:16:52 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: fpu.c,v 1.8 2014/02/23 22:35:28 dsl Exp $	*/
+/*	$NetBSD: fpu.c,v 1.9 2014/02/25 22:16:52 dsl Exp $	*/
 
 /*-
  * Copyright (c) 2008 The NetBSD Foundation, Inc.  All
@@ -100,7 +100,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: fpu.c,v 1.8 2014/02/23 22:35:28 dsl Exp $");
+__KERNEL_RCSID(0, "$NetBSD: fpu.c,v 1.9 2014/02/25 22:16:52 dsl Exp $");
 
 #include "opt_multiprocessor.h"
 
@@ -429,21 +429,26 @@ fpudna(struct trapframe *frame)
 	pcb->pcb_fpcpu = ci;
 
 	if (i386_use_fxsave) {
-		/*
-		 * AMD FPU's do not restore FIP, FDP, and FOP on fxrstor,
-		 * leaking other process's execution history.
-		 * Clear them manually by loading a zero onto the fpu stack.
-		 *
-		 * Clear the ES bit in the x87 status word if it is currently
-		 * set, in order to avoid causing a fault in the upcoming load.
-		 */
-		if (fngetsw() & 0x80)
-			fnclex();
-		fldummy();
+		if (x86_xsave_features != 0) {
+			xrstor(&pcb->pcb_savefpu, x86_xsave_features);
+		} else {
+			/*
+			 * AMD FPU's do not restore FIP, FDP, and FOP on
+			 * fxrstor, leaking other process's execution history.
+			 * Clear them manually by loading a zero.
+			 *
+			 * Clear the ES bit in the x87 status word if it is
+			 * currently set, in order to avoid causing a fault
+			 * in the upcoming load.
+			 */
+			if (fngetsw() & 0x80)
+				fnclex();
+			fldummy();
 
-		fxrstor(&pcb->pcb_savefpu.sv_xmm);
+			fxrstor(&pcb->pcb_savefpu);
+		}
 	} else {
-		frstor(&pcb->pcb_savefpu.sv_87);
+		frstor(&pcb->pcb_savefpu);
 	}
 
 	KASSERT(ci == curcpu());
@@ -472,9 +477,12 @@ fpusave_cpu(bool save)
 	if (save) {
 		clts();
 		if (i386_use_fxsave) {
-			fxsave(&pcb->pcb_savefpu.sv_xmm);
+			if (x86_xsave_features != 0)
+				xsave(&pcb->pcb_savefpu, x86_xsave_features);
+			else
+				fxsave(&pcb->pcb_savefpu);
 		} else {
-			fnsave(&pcb->pcb_savefpu.sv_87);
+			fnsave(&pcb->pcb_savefpu);
 		}
 	}
 
@@ -533,7 +541,7 @@ fpusave_lwp(struct lwp *l, bool save)
 /*
  * exec needs to clear the fpu save area to avoid leaking info from the
  * old process to userspace.
- * We must also load these values into the fpu - otherwise the process
+ * We must also (later) load these values into the fpu - otherwise the process
  * will see another processes fpu registers.
  */
 void
@@ -551,7 +559,7 @@ fpu_save_area_clear(struct lwp *lwp, uns
 		fpu_save->sv_xmm.fx_mxcsr_mask = __INITIAL_MXCSR_MASK__;
 		fpu_save->sv_xmm.fx_cw = x87_cw;
 	} else {
-		memset(&fpu_save->sv_87, 0, sizeof fpu_save->sv_87);
+		memset(&fpu_save->sv_87, 0, x86_fpu_save_size);
 		fpu_save->sv_87.s87_tw = 0xffff;
 		fpu_save->sv_87.s87_cw = x87_cw;
 	}
@@ -575,6 +583,23 @@ fpu_save_area_reset(struct lwp *lwp)
 	}
 }
 
+/* During fork the xsave data needs to be copied */
+void
+fpu_save_area_fork(struct pcb *pcb2, const struct pcb *pcb1)
+{
+	ssize_t extra;
+
+	/* The pcb itself has been copied, but the xsave area
+	 * extends further. */
+
+	extra = offsetof(struct pcb, pcb_savefpu) + x86_fpu_save_size -
+	    sizeof (struct pcb);
+
+	if (extra > 0)
+		memcpy(pcb2 + 1, pcb1 + 1, extra);
+}
+
+
 /*
  * Write the FP registers.
  * Buffer has usually come from userspace so should not be trusted.
@@ -621,6 +646,7 @@ void
 process_read_fpregs_xmm(struct lwp *lwp, struct fxsave *fpregs)
 {
 	fpusave_lwp(lwp, true);
+
 	if (i386_use_fxsave) {
 		memcpy(fpregs, &process_fpframe(lwp)->sv_xmm,
 		    sizeof process_fpframe(lwp)->sv_xmm);

Index: src/sys/arch/x86/x86/vm_machdep.c
diff -u src/sys/arch/x86/x86/vm_machdep.c:1.23 src/sys/arch/x86/x86/vm_machdep.c:1.24
--- src/sys/arch/x86/x86/vm_machdep.c:1.23	Thu Feb 20 18:19:10 2014
+++ src/sys/arch/x86/x86/vm_machdep.c	Tue Feb 25 22:16:52 2014
@@ -1,4 +1,4 @@
-/*	$NetBSD: vm_machdep.c,v 1.23 2014/02/20 18:19:10 dsl Exp $	*/
+/*	$NetBSD: vm_machdep.c,v 1.24 2014/02/25 22:16:52 dsl Exp $	*/
 
 /*-
  * Copyright (c) 1982, 1986 The Regents of the University of California.
@@ -80,7 +80,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: vm_machdep.c,v 1.23 2014/02/20 18:19:10 dsl Exp $");
+__KERNEL_RCSID(0, "$NetBSD: vm_machdep.c,v 1.24 2014/02/25 22:16:52 dsl Exp $");
 
 #include "opt_mtrr.h"
 
@@ -154,6 +154,8 @@ cpu_lwp_fork(struct lwp *l1, struct lwp 
 
 	/* Copy the PCB from parent. */
 	memcpy(pcb2, pcb1, sizeof(struct pcb));
+	/* Copy any additional fpu state */
+	fpu_save_area_fork(pcb2, pcb1);
 
 #if defined(XEN)
 	pcb2->pcb_iopl = SEL_KPL;

Reply via email to