[6/6] compel: arch, x86 -- Convert frame for ia32 sigreturn

Submitted by Cyrill Gorcunov on Feb. 13, 2017, 3:03 p.m.

Details

Message ID 1486998203-2834-7-git-send-email-gorcunov@openvz.org
State New
Series "ia32: Add support for FPU c/r in compat environment"
Headers show

Commit Message

Cyrill Gorcunov Feb. 13, 2017, 3:03 p.m.
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
---
 compel/arch/x86/src/lib/include/uapi/asm/fpu.h |  3 +
 compel/arch/x86/src/lib/infect.c               | 87 ++++++++++++++++++++++++++
 criu/arch/x86/crtools.c                        |  4 ++
 3 files changed, 94 insertions(+)

Patch hide | download patch | download mbox

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 ee6e81bcd9d8..97fb7f921f9d 100644
--- a/compel/arch/x86/src/lib/include/uapi/asm/fpu.h
+++ b/compel/arch/x86/src/lib/include/uapi/asm/fpu.h
@@ -130,4 +130,7 @@  typedef struct {
 	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 43b080000fe3..785761dc4e0e 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)
@@ -106,6 +191,8 @@  int sigreturn_prep_regs_plain(struct rt_sigframe *sigframe,
 		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;
diff --git a/criu/arch/x86/crtools.c b/criu/arch/x86/crtools.c
index 0986055d53fa..417a6725d3a3 100644
--- a/criu/arch/x86/crtools.c
+++ b/criu/arch/x86/crtools.c
@@ -337,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;