Commit f21ec409 authored by Linus Torvalds's avatar Linus Torvalds

Fix x87 fnsave Tag Word emulation when using FXSR (SSE)

From: Roland McGrath <roland@redhat.com>

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: default avatarRoland McGrath <roland@redhat.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 72635e22
...@@ -111,16 +111,17 @@ static inline unsigned short twd_i387_to_fxsr( unsigned short twd ) ...@@ -111,16 +111,17 @@ static inline unsigned short twd_i387_to_fxsr( unsigned short twd )
static inline unsigned long twd_fxsr_to_i387( struct i387_fxsave_struct *fxsave ) static inline unsigned long twd_fxsr_to_i387( struct i387_fxsave_struct *fxsave )
{ {
struct _fpxreg *st = NULL; struct _fpxreg *st = NULL;
unsigned long tos = (fxsave->swd >> 11) & 7;
unsigned long twd = (unsigned long) fxsave->twd; unsigned long twd = (unsigned long) fxsave->twd;
unsigned long tag; unsigned long tag;
unsigned long ret = 0xffff0000u; unsigned long ret = 0xffff0000u;
int i; 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++ ) { for ( i = 0 ; i < 8 ; i++ ) {
if ( twd & 0x1 ) { if ( twd & 0x1 ) {
st = (struct _fpxreg *) FPREG_ADDR( fxsave, i ); st = FPREG_ADDR( fxsave, (i - tos) & 7 );
switch ( st->exponent & 0x7fff ) { switch ( st->exponent & 0x7fff ) {
case 0x7fff: case 0x7fff:
......
...@@ -28,16 +28,17 @@ static inline unsigned short twd_i387_to_fxsr(unsigned short twd) ...@@ -28,16 +28,17 @@ static inline unsigned short twd_i387_to_fxsr(unsigned short twd)
static inline unsigned long twd_fxsr_to_i387(struct i387_fxsave_struct *fxsave) static inline unsigned long twd_fxsr_to_i387(struct i387_fxsave_struct *fxsave)
{ {
struct _fpxreg *st = NULL; struct _fpxreg *st = NULL;
unsigned long tos = (fxsave->swd >> 11) & 7;
unsigned long twd = (unsigned long) fxsave->twd; unsigned long twd = (unsigned long) fxsave->twd;
unsigned long tag; unsigned long tag;
unsigned long ret = 0xffff0000; unsigned long ret = 0xffff0000;
int i; 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++) { for (i = 0 ; i < 8 ; i++) {
if (twd & 0x1) { if (twd & 0x1) {
st = (struct _fpxreg *) FPREG_ADDR( fxsave, i ); st = FPREG_ADDR( fxsave, (i - tos) & 7 );
switch (st->exponent & 0x7fff) { switch (st->exponent & 0x7fff) {
case 0x7fff: case 0x7fff:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment