git: 1c091d11261a - main - x86: handle MXCSR from XSAVEOPT when x87 state was optimized

From: Konstantin Belousov <kib_at_FreeBSD.org>
Date: Thu, 28 Mar 2024 11:56:58 UTC
The branch main has been updated by kib:

URL: https://cgit.FreeBSD.org/src/commit/?id=1c091d11261a3c8cc3728b92760e65242c0f5949

commit 1c091d11261a3c8cc3728b92760e65242c0f5949
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2024-03-27 11:01:44 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2024-03-28 11:56:21 +0000

    x86: handle MXCSR from XSAVEOPT when x87 state was optimized
    
    PR:     275322
    Reported by:    Cheyenne Wills <cheyenne.wills@gmail.com>
    Reviewed by:    emaste, jhb, olce
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D44522
---
 sys/amd64/amd64/fpu.c | 21 +++++++++++++++++++++
 sys/i386/i386/npx.c   | 21 +++++++++++++++++++++
 2 files changed, 42 insertions(+)

diff --git a/sys/amd64/amd64/fpu.c b/sys/amd64/amd64/fpu.c
index dcc6ff1a03a8..ebc8b869b368 100644
--- a/sys/amd64/amd64/fpu.c
+++ b/sys/amd64/amd64/fpu.c
@@ -850,7 +850,10 @@ fpugetregs(struct thread *td)
 	struct pcb *pcb;
 	uint64_t *xstate_bv, bit;
 	char *sa;
+	struct savefpu *s;
+	uint32_t mxcsr, mxcsr_mask;
 	int max_ext_n, i, owned;
+	bool do_mxcsr;
 
 	pcb = td->td_pcb;
 	critical_enter();
@@ -881,10 +884,28 @@ fpugetregs(struct thread *td)
 			bit = 1ULL << i;
 			if ((xsave_mask & bit) == 0 || (*xstate_bv & bit) != 0)
 				continue;
+			do_mxcsr = false;
+			if (i == 0 && (*xstate_bv & (XFEATURE_ENABLED_SSE |
+			    XFEATURE_ENABLED_AVX)) != 0) {
+				/*
+				 * x87 area was not saved by XSAVEOPT,
+				 * but one of XMM or AVX was.  Then we need
+				 * to preserve MXCSR from being overwritten
+				 * with the default value.
+				 */
+				s = (struct savefpu *)sa;
+				mxcsr = s->sv_env.en_mxcsr;
+				mxcsr_mask = s->sv_env.en_mxcsr_mask;
+				do_mxcsr = true;
+			}
 			bcopy((char *)fpu_initialstate +
 			    xsave_area_desc[i].offset,
 			    sa + xsave_area_desc[i].offset,
 			    xsave_area_desc[i].size);
+			if (do_mxcsr) {
+				s->sv_env.en_mxcsr = mxcsr;
+				s->sv_env.en_mxcsr_mask = mxcsr_mask;
+			}
 			*xstate_bv |= bit;
 		}
 	}
diff --git a/sys/i386/i386/npx.c b/sys/i386/i386/npx.c
index 16ad7a96ab35..0334aedd740b 100644
--- a/sys/i386/i386/npx.c
+++ b/sys/i386/i386/npx.c
@@ -975,8 +975,11 @@ npxgetregs(struct thread *td)
 	struct pcb *pcb;
 	uint64_t *xstate_bv, bit;
 	char *sa;
+	union savefpu *s;
+	uint32_t mxcsr, mxcsr_mask;
 	int max_ext_n, i;
 	int owned;
+	bool do_mxcsr;
 
 	if (!hw_float)
 		return (_MC_FPOWNED_NONE);
@@ -1019,10 +1022,28 @@ npxgetregs(struct thread *td)
 			bit = 1ULL << i;
 			if ((xsave_mask & bit) == 0 || (*xstate_bv & bit) != 0)
 				continue;
+			do_mxcsr = false;
+			if (i == 0 && (*xstate_bv & (XFEATURE_ENABLED_SSE |
+			    XFEATURE_ENABLED_AVX)) != 0) {
+				/*
+				 * x87 area was not saved by XSAVEOPT,
+				 * but one of XMM or AVX was.  Then we need
+				 * to preserve MXCSR from being overwritten
+				 * with the default value.
+				 */
+				s = (union savefpu *)sa;
+				mxcsr = s->sv_xmm.sv_env.en_mxcsr;
+				mxcsr_mask = s->sv_xmm.sv_env.en_mxcsr_mask;
+				do_mxcsr = true;
+			}
 			bcopy((char *)npx_initialstate +
 			    xsave_area_desc[i].offset,
 			    sa + xsave_area_desc[i].offset,
 			    xsave_area_desc[i].size);
+			if (do_mxcsr) {
+				s->sv_xmm.sv_env.en_mxcsr = mxcsr;
+				s->sv_xmm.sv_env.en_mxcsr_mask = mxcsr_mask;
+			}
 			*xstate_bv |= bit;
 		}
 	}