Commit 51b10d68 authored by Cyrill Gorcunov's avatar Cyrill Gorcunov Committed by Andrei Vagin

compel: arch, x86 -- Add support of FPU restore in ia32 compat mode

To support ia32 compat mode on x86-64 we need to things

 - extend fpu_state_t type to carry ia32 specifics
 - fill up additional members in fpu_state_ia32_t type
   before calling sigreturn (this also requires the
   uc_mcontext::fpstate won't be aligned on 32 bytes)

Because we touches base types in compel the criu
has been updated accordingly.
Acked-by: 's avatarDmitry Safonov <dsafonov@virtuozzo.com>
Acked-by: 's avatarPavel Emelyanov <xemul@virtuozzo.com>
Signed-off-by: 's avatarCyrill Gorcunov <gorcunov@openvz.org>
Signed-off-by: 's avatarAndrei Vagin <avagin@virtuozzo.com>
parent 88014eb0
...@@ -82,9 +82,12 @@ struct xsave_struct { ...@@ -82,9 +82,12 @@ struct xsave_struct {
struct ymmh_struct ymmh; struct ymmh_struct ymmh;
} __aligned(FP_MIN_ALIGN_BYTES) __packed; } __aligned(FP_MIN_ALIGN_BYTES) __packed;
/* struct xsave_struct_ia32 {
* This one is used in restorer. struct i387_fxsave_struct i387;
*/ struct xsave_hdr_struct xsave_hdr;
struct ymmh_struct ymmh;
} __packed;
typedef struct { typedef struct {
/* /*
* The FPU xsave area must be continious and FP_MIN_ALIGN_BYTES * The FPU xsave area must be continious and FP_MIN_ALIGN_BYTES
...@@ -96,7 +99,46 @@ typedef struct { ...@@ -96,7 +99,46 @@ typedef struct {
uint8_t __pad[sizeof(struct xsave_struct) + FP_XSTATE_MAGIC2_SIZE]; uint8_t __pad[sizeof(struct xsave_struct) + FP_XSTATE_MAGIC2_SIZE];
}; };
uint8_t has_fpu;
} fpu_state_64_t;
struct user_i387_ia32_struct {
uint32_t cwd; /* FPU Control Word */
uint32_t swd; /* FPU Status Word */
uint32_t twd; /* FPU Tag Word */
uint32_t fip; /* FPU IP Offset */
uint32_t fcs; /* FPU IP Selector */
uint32_t foo; /* FPU Operand Pointer Offset */
uint32_t fos; /* FPU Operand Pointer Selector */
uint32_t st_space[20]; /* 8*10 bytes for each FP-reg = 80 bytes */
} __packed;
typedef struct {
struct {
struct user_i387_ia32_struct i387_ia32;
/* Software status information [not touched by FSAVE]: */
uint32_t status;
} __packed fregs_state;
union {
struct xsave_struct_ia32 xsave;
uint8_t __pad[sizeof(struct xsave_struct) + FP_XSTATE_MAGIC2_SIZE];
} __packed;
} __packed fpu_state_ia32_t;
/*
* This one is used in restorer.
*/
typedef struct {
union {
fpu_state_64_t fpu_state_64;
fpu_state_ia32_t fpu_state_ia32;
};
uint8_t has_fpu; uint8_t has_fpu;
} fpu_state_t; } fpu_state_t;
extern void compel_convert_from_fxsr(struct user_i387_ia32_struct *env,
struct i387_fxsave_struct *fxsave);
#endif /* __CR_ASM_FPU_H__ */ #endif /* __CR_ASM_FPU_H__ */
...@@ -45,6 +45,91 @@ static inline __always_unused void __check_code_syscall(void) ...@@ -45,6 +45,91 @@ static inline __always_unused void __check_code_syscall(void)
BUILD_BUG_ON(!is_log2(sizeof(code_syscall))); BUILD_BUG_ON(!is_log2(sizeof(code_syscall)));
} }
/* 10-byte legacy floating point register */
struct fpreg {
uint16_t significand[4];
uint16_t exponent;
};
/* 16-byte floating point register */
struct fpxreg {
uint16_t significand[4];
uint16_t exponent;
uint16_t padding[3];
};
#define FPREG_ADDR(f, n) ((void *)&(f)->st_space + (n) * 16)
#define FP_EXP_TAG_VALID 0
#define FP_EXP_TAG_ZERO 1
#define FP_EXP_TAG_SPECIAL 2
#define FP_EXP_TAG_EMPTY 3
static inline uint32_t twd_fxsr_to_i387(struct i387_fxsave_struct *fxsave)
{
struct fpxreg *st;
uint32_t tos = (fxsave->swd >> 11) & 7;
uint32_t twd = (unsigned long)fxsave->twd;
uint32_t tag;
uint32_t ret = 0xffff0000u;
int i;
for (i = 0; i < 8; i++, twd >>= 1) {
if (twd & 0x1) {
st = FPREG_ADDR(fxsave, (i - tos) & 7);
switch (st->exponent & 0x7fff) {
case 0x7fff:
tag = FP_EXP_TAG_SPECIAL;
break;
case 0x0000:
if (!st->significand[0] &&
!st->significand[1] &&
!st->significand[2] &&
!st->significand[3])
tag = FP_EXP_TAG_ZERO;
else
tag = FP_EXP_TAG_SPECIAL;
break;
default:
if (st->significand[3] & 0x8000)
tag = FP_EXP_TAG_VALID;
else
tag = FP_EXP_TAG_SPECIAL;
break;
}
} else {
tag = FP_EXP_TAG_EMPTY;
}
ret |= tag << (2 * i);
}
return ret;
}
void compel_convert_from_fxsr(struct user_i387_ia32_struct *env,
struct i387_fxsave_struct *fxsave)
{
struct fpxreg *from = (struct fpxreg *)&fxsave->st_space[0];
struct fpreg *to = (struct fpreg *)&env->st_space[0];
int i;
env->cwd = fxsave->cwd | 0xffff0000u;
env->swd = fxsave->swd | 0xffff0000u;
env->twd = twd_fxsr_to_i387(fxsave);
env->fip = fxsave->rip;
env->foo = fxsave->rdp;
/*
* should be actually ds/cs at fpu exception time, but
* that information is not available in 64bit mode.
*/
env->fcs = 0x23; /* __USER32_CS */
env->fos = 0x2b; /* __USER32_DS */
env->fos |= 0xffff0000;
for (i = 0; i < 8; ++i)
memcpy(&to[i], &from[i], sizeof(to[0]));
}
int sigreturn_prep_regs_plain(struct rt_sigframe *sigframe, int sigreturn_prep_regs_plain(struct rt_sigframe *sigframe,
user_regs_struct_t *regs, user_regs_struct_t *regs,
user_fpregs_struct_t *fpregs) user_fpregs_struct_t *fpregs)
...@@ -100,7 +185,13 @@ int sigreturn_prep_regs_plain(struct rt_sigframe *sigframe, ...@@ -100,7 +185,13 @@ int sigreturn_prep_regs_plain(struct rt_sigframe *sigframe,
} }
fpu_state->has_fpu = true; fpu_state->has_fpu = true;
memcpy(&fpu_state->xsave, fpregs, sizeof(*fpregs)); if (is_native) {
memcpy(&fpu_state->fpu_state_64.xsave, fpregs, sizeof(*fpregs));
} else {
memcpy(&fpu_state->fpu_state_ia32.xsave, fpregs, sizeof(*fpregs));
compel_convert_from_fxsr(&fpu_state->fpu_state_ia32.fregs_state.i387_ia32,
&fpu_state->fpu_state_ia32.xsave.i387);
}
return 0; return 0;
} }
...@@ -111,16 +202,20 @@ int sigreturn_prep_fpu_frame_plain(struct rt_sigframe *sigframe, ...@@ -111,16 +202,20 @@ int sigreturn_prep_fpu_frame_plain(struct rt_sigframe *sigframe,
fpu_state_t *fpu_state = (sigframe->is_native) ? fpu_state_t *fpu_state = (sigframe->is_native) ?
&rsigframe->native.fpu_state : &rsigframe->native.fpu_state :
&rsigframe->compat.fpu_state; &rsigframe->compat.fpu_state;
unsigned long addr = (unsigned long)(void *)&fpu_state->xsave;
if (sigframe->is_native && (addr % 64ul) == 0ul) { if (sigframe->is_native) {
sigframe->native.uc.uc_mcontext.fpstate = &fpu_state->xsave; unsigned long addr = (unsigned long)(void *)&fpu_state->fpu_state_64.xsave;
if ((addr % 64ul)) {
pr_err("Unaligned address passed: %lx (native %d)\n",
addr, sigframe->is_native);
return -1;
}
sigframe->native.uc.uc_mcontext.fpstate = (void *)addr;
} else if (!sigframe->is_native) { } else if (!sigframe->is_native) {
sigframe->compat.uc.uc_mcontext.fpstate = (uint32_t)addr; sigframe->compat.uc.uc_mcontext.fpstate =
} else { (uint32_t)(unsigned long)(void *)&fpu_state->fpu_state_ia32;
pr_err("Unaligned address passed: %lx (native %d)\n",
addr, sigframe->is_native);
return -1;
} }
return 0; return 0;
......
...@@ -302,7 +302,9 @@ int restore_fpu(struct rt_sigframe *sigframe, CoreEntry *core) ...@@ -302,7 +302,9 @@ int restore_fpu(struct rt_sigframe *sigframe, CoreEntry *core)
fpu_state_t *fpu_state = core_is_compat(core) ? fpu_state_t *fpu_state = core_is_compat(core) ?
&sigframe->compat.fpu_state : &sigframe->compat.fpu_state :
&sigframe->native.fpu_state; &sigframe->native.fpu_state;
struct xsave_struct *x = &fpu_state->xsave; struct xsave_struct *x = core_is_compat(core) ?
(void *)&fpu_state->fpu_state_ia32.xsave :
(void *)&fpu_state->fpu_state_64.xsave;
/* /*
* If no FPU information provided -- we're restoring * If no FPU information provided -- we're restoring
...@@ -334,6 +336,10 @@ int restore_fpu(struct rt_sigframe *sigframe, CoreEntry *core) ...@@ -334,6 +336,10 @@ int restore_fpu(struct rt_sigframe *sigframe, CoreEntry *core)
assign_array(x->i387, core->thread_info->fpregs, st_space); assign_array(x->i387, core->thread_info->fpregs, st_space);
assign_array(x->i387, core->thread_info->fpregs, xmm_space); assign_array(x->i387, core->thread_info->fpregs, xmm_space);
if (core_is_compat(core))
compel_convert_from_fxsr(&fpu_state->fpu_state_ia32.fregs_state.i387_ia32,
&fpu_state->fpu_state_ia32.xsave.i387);
if (compel_cpu_has_feature(X86_FEATURE_OSXSAVE)) { if (compel_cpu_has_feature(X86_FEATURE_OSXSAVE)) {
struct fpx_sw_bytes *fpx_sw = (void *)&x->i387.sw_reserved; struct fpx_sw_bytes *fpx_sw = (void *)&x->i387.sw_reserved;
void *magic2; void *magic2;
...@@ -355,7 +361,7 @@ int restore_fpu(struct rt_sigframe *sigframe, CoreEntry *core) ...@@ -355,7 +361,7 @@ int restore_fpu(struct rt_sigframe *sigframe, CoreEntry *core)
/* /*
* This should be at the end of xsave frame. * This should be at the end of xsave frame.
*/ */
magic2 = fpu_state->__pad + sizeof(struct xsave_struct); magic2 = (void *)x + sizeof(struct xsave_struct);
*(u32 *)magic2 = FP_XSTATE_MAGIC2; *(u32 *)magic2 = FP_XSTATE_MAGIC2;
} }
......
...@@ -16,16 +16,20 @@ int sigreturn_prep_fpu_frame(struct rt_sigframe *sigframe, ...@@ -16,16 +16,20 @@ int sigreturn_prep_fpu_frame(struct rt_sigframe *sigframe,
fpu_state_t *fpu_state = (sigframe->is_native) ? fpu_state_t *fpu_state = (sigframe->is_native) ?
&rsigframe->native.fpu_state : &rsigframe->native.fpu_state :
&rsigframe->compat.fpu_state; &rsigframe->compat.fpu_state;
unsigned long addr = (unsigned long)(void *)&fpu_state->xsave;
if (sigframe->is_native && (addr % 64ul) == 0ul) { if (sigframe->is_native) {
sigframe->native.uc.uc_mcontext.fpstate = &fpu_state->xsave; unsigned long addr = (unsigned long)(void *)&fpu_state->fpu_state_64.xsave;
if ((addr % 64ul)) {
pr_err("Unaligned address passed: %lx (native %d)\n",
addr, sigframe->is_native);
return -1;
}
sigframe->native.uc.uc_mcontext.fpstate = (void *)addr;
} else if (!sigframe->is_native) { } else if (!sigframe->is_native) {
sigframe->compat.uc.uc_mcontext.fpstate = (uint32_t)addr; sigframe->compat.uc.uc_mcontext.fpstate =
} else { (uint32_t)(unsigned long)(void *)&fpu_state->fpu_state_ia32;
pr_err("Unaligned address passed: %lx (native %d)\n",
addr, sigframe->is_native);
return -1;
} }
return 0; return 0;
......
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