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;