[PATCH] i386/x86_64 fpu: fix x87 tag word simulation using fxsave

Linux Kernel Mailing List <[email protected]> Sat, 12 Mar 2005 12:31:25 +0000
Newsgroups gmane.linux.kernel.commits.2-4
Message-ID <[email protected]>
ChangeSet 1.1575, 2005/03/12 09:31:25-03:00, [email protected]

	[PATCH] i386/x86_64 fpu: fix x87 tag word simulation using fxsave
	
	Fix x87 fnsave Tag Word emulation when using FXSR (SSE)
	
	From: Roland McGrath <[email protected]>
	
	The fxsave instruction does not save the x87 tag word (only the
	empty bits), and we re-created the old-style x87 tags incorrectly.
	The registers are saved in "stack order" in the save area, but the
	tag word bits are in "hardware order", and we need to get the right
	register state.
	
	Both x86 and x86-64 needed this fix.
	
	Signed-off-by: Roland McGrath <[email protected]>
	Signed-off-by: Linus Torvalds <[email protected]>



 i386/kernel/i387.c  |    5 +++--
 x86_64/ia32/fpu32.c |    5 +++--
 2 files changed, 6 insertions(+), 4 deletions(-)


diff -Nru a/arch/i386/kernel/i387.c b/arch/i386/kernel/i387.c
--- a/arch/i386/kernel/i387.c	2005-03-12 10:03:13 -08:00
+++ b/arch/i386/kernel/i387.c	2005-03-12 10:03:13 -08:00
@@ -128,16 +128,17 @@
 static inline unsigned long twd_fxsr_to_i387( struct i387_fxsave_struct *fxsave )
 {
 	struct _fpxreg *st = NULL;
+	unsigned long tos = (fxsave->swd >> 11) & 7;
 	unsigned long twd = (unsigned long) fxsave->twd;
 	unsigned long tag;
 	unsigned long ret = 0xffff0000;
 	int i;
 
-#define FPREG_ADDR(f, n)	((char *)&(f)->st_space + (n) * 16);
+#define FPREG_ADDR(f, n)	((void *)&(f)->st_space + (n) * 16);
 
 	for ( i = 0 ; i < 8 ; i++ ) {
 		if ( twd & 0x1 ) {
-			st = (struct _fpxreg *) FPREG_ADDR( fxsave, i );
+			st = FPREG_ADDR( fxsave, (i - tos) & 7 );
 
 			switch ( st->exponent & 0x7fff ) {
 			case 0x7fff:
diff -Nru a/arch/x86_64/ia32/fpu32.c b/arch/x86_64/ia32/fpu32.c
--- a/arch/x86_64/ia32/fpu32.c	2005-03-12 10:03:13 -08:00
+++ b/arch/x86_64/ia32/fpu32.c	2005-03-12 10:03:13 -08:00
@@ -28,16 +28,17 @@
 static inline unsigned long twd_fxsr_to_i387(struct i387_fxsave_struct *fxsave)
 {
 	struct _fpxreg *st = NULL;
+	unsigned long tos = (fxsave->swd >> 11) & 7;
 	unsigned long twd = (unsigned long) fxsave->twd;
 	unsigned long tag;
 	unsigned long ret = 0xffff0000;
 	int i;
 
-#define FPREG_ADDR(f, n)	((char *)&(f)->st_space + (n) * 16);
+#define FPREG_ADDR(f, n)	((void *)&(f)->st_space + (n) * 16);
 
 	for (i = 0 ; i < 8 ; i++) {
 		if (twd & 0x1) {
-			st = (struct _fpxreg *) FPREG_ADDR( fxsave, i );
+			st = FPREG_ADDR( fxsave, (i - tos) & 7 );
 
 			switch (st->exponent & 0x7fff) {
 			case 0x7fff: