[CRIU] [PATCH 4/4] compel: arch, x86 -- Add support of FPU restore in ia32 compat mode
Cyrill Gorcunov
gorcunov at openvz.org
Tue Feb 14 02:26:35 PST 2017
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.
Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
---
compel/arch/x86/src/lib/include/uapi/asm/fpu.h | 48 ++++++++++-
compel/arch/x86/src/lib/infect.c | 113 +++++++++++++++++++++++--
criu/arch/x86/crtools.c | 10 ++-
criu/arch/x86/sigframe.c | 20 +++--
4 files changed, 169 insertions(+), 22 deletions(-)
diff --git a/compel/arch/x86/src/lib/include/uapi/asm/fpu.h b/compel/arch/x86/src/lib/include/uapi/asm/fpu.h
index 7525f153a7e1..f60112b9ba50 100644
--- a/compel/arch/x86/src/lib/include/uapi/asm/fpu.h
+++ b/compel/arch/x86/src/lib/include/uapi/asm/fpu.h
@@ -82,9 +82,12 @@ struct xsave_struct {
struct ymmh_struct ymmh;
} __aligned(FP_MIN_ALIGN_BYTES) __packed;
-/*
- * This one is used in restorer.
- */
+struct xsave_struct_ia32 {
+ struct i387_fxsave_struct i387;
+ struct xsave_hdr_struct xsave_hdr;
+ struct ymmh_struct ymmh;
+} __packed;
+
typedef struct {
/*
* The FPU xsave area must be continious and FP_MIN_ALIGN_BYTES
@@ -97,6 +100,45 @@ typedef struct {
};
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;
} 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__ */
diff --git a/compel/arch/x86/src/lib/infect.c b/compel/arch/x86/src/lib/infect.c
index 92bbb528dc78..9842075c4ae6 100644
--- a/compel/arch/x86/src/lib/infect.c
+++ b/compel/arch/x86/src/lib/infect.c
@@ -47,6 +47,91 @@ static inline __always_unused void __check_code_syscall(void)
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,
user_regs_struct_t *regs,
user_fpregs_struct_t *fpregs)
@@ -102,7 +187,13 @@ int sigreturn_prep_regs_plain(struct rt_sigframe *sigframe,
}
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;
}
@@ -113,16 +204,20 @@ int sigreturn_prep_fpu_frame_plain(struct rt_sigframe *sigframe,
fpu_state_t *fpu_state = (sigframe->is_native) ?
&rsigframe->native.fpu_state :
&rsigframe->compat.fpu_state;
- unsigned long addr = (unsigned long)(void *)&fpu_state->xsave;
- if (sigframe->is_native && (addr % 64ul) == 0ul) {
- sigframe->native.uc.uc_mcontext.fpstate = &fpu_state->xsave;
+ if (sigframe->is_native) {
+ 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) {
- sigframe->compat.uc.uc_mcontext.fpstate = (uint32_t)addr;
- } else {
- pr_err("Unaligned address passed: %lx (native %d)\n",
- addr, sigframe->is_native);
- return -1;
+ sigframe->compat.uc.uc_mcontext.fpstate =
+ (uint32_t)(unsigned long)(void *)&fpu_state->fpu_state_ia32;
}
return 0;
diff --git a/criu/arch/x86/crtools.c b/criu/arch/x86/crtools.c
index 33f4048704ff..7d169ec88832 100644
--- a/criu/arch/x86/crtools.c
+++ b/criu/arch/x86/crtools.c
@@ -303,7 +303,9 @@ int restore_fpu(struct rt_sigframe *sigframe, CoreEntry *core)
fpu_state_t *fpu_state = core_is_compat(core) ?
&sigframe->compat.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
@@ -335,6 +337,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, 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 (cpu_has_feature(X86_FEATURE_OSXSAVE)) {
struct fpx_sw_bytes *fpx_sw = (void *)&x->i387.sw_reserved;
void *magic2;
@@ -356,7 +362,7 @@ int restore_fpu(struct rt_sigframe *sigframe, CoreEntry *core)
/*
* 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;
}
diff --git a/criu/arch/x86/sigframe.c b/criu/arch/x86/sigframe.c
index 755439fcf15b..89bfa4a0b7a8 100644
--- a/criu/arch/x86/sigframe.c
+++ b/criu/arch/x86/sigframe.c
@@ -16,16 +16,20 @@ int sigreturn_prep_fpu_frame(struct rt_sigframe *sigframe,
fpu_state_t *fpu_state = (sigframe->is_native) ?
&rsigframe->native.fpu_state :
&rsigframe->compat.fpu_state;
- unsigned long addr = (unsigned long)(void *)&fpu_state->xsave;
- if (sigframe->is_native && (addr % 64ul) == 0ul) {
- sigframe->native.uc.uc_mcontext.fpstate = &fpu_state->xsave;
+ if (sigframe->is_native) {
+ 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) {
- sigframe->compat.uc.uc_mcontext.fpstate = (uint32_t)addr;
- } else {
- pr_err("Unaligned address passed: %lx (native %d)\n",
- addr, sigframe->is_native);
- return -1;
+ sigframe->compat.uc.uc_mcontext.fpstate =
+ (uint32_t)(unsigned long)(void *)&fpu_state->fpu_state_ia32;
}
return 0;
--
2.7.4
More information about the CRIU
mailing list