[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