[PATCH 09/14] restore: Restore FPU state

Cyrill Gorcunov gorcunov at openvz.org
Fri Dec 14 08:39:25 EST 2012


Since at moment we stick with sigreturn restore
we need to form a proper FPU frame and set a pointer
to it inside sigreturn frame.

For this sake we read the FPU image and here are two
cases are possible

 - xsave frame is present but the host cpu supports only
   fxsave instruction: we refuse to continue, since it means
   there are no ymm registers on the machine where we're trying
   to restore

 - fxsave frame is present but the host cpu has xsave feature:
   at moment we refuse to continue, requiring complete match
   between "checkpoint and restore hosts", but in real we could
   extend logic and form complete xsave frame from fxsave and
   continue processing

Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
---
 cr-restore.c   | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 pie/restorer.c |  18 ++++++++
 2 files changed, 154 insertions(+)

diff --git a/cr-restore.c b/cr-restore.c
index c81f0f5..72b40e4 100644
--- a/cr-restore.c
+++ b/cr-restore.c
@@ -54,6 +54,7 @@
 #include "net.h"
 #include "tty.h"
 #include "cpu.h"
+#include "fpu.h"
 
 #include "protobuf.h"
 #include "protobuf/sa.pb-c.h"
@@ -1574,6 +1575,135 @@ static int prep_sched_info(struct rst_sched_param *sp, ThreadCoreEntry *tc)
 	return 0;
 }
 
+static bool valid_xsave_frame(CoreEntry *core)
+{
+	struct xsave_struct *x = NULL;
+
+	if (core->thread_info->fpregs->n_st_space < ARRAY_SIZE(x->i387.st_space)) {
+		pr_err("Corruption in FPU st_space area "
+		       "(got %li but %li expected)\n",
+		       (long)core->thread_info->fpregs->n_st_space,
+		       (long)ARRAY_SIZE(x->i387.st_space));
+		return false;
+	}
+
+	if (core->thread_info->fpregs->n_xmm_space < ARRAY_SIZE(x->i387.xmm_space)) {
+		pr_err("Corruption in FPU xmm_space area "
+		       "(got %li but %li expected)\n",
+		       (long)core->thread_info->fpregs->n_st_space,
+		       (long)ARRAY_SIZE(x->i387.xmm_space));
+		return false;
+	}
+
+	if (cpu_has_feature(X86_FEATURE_XSAVE)) {
+		if (!core->thread_info->fpregs->xsave) {
+			pr_err("FPU xsave area is missing, "
+			       "but host cpu requires it\n");
+			return false;
+		}
+		if (core->thread_info->fpregs->xsave->n_ymmh_space < ARRAY_SIZE(x->ymmh.ymmh_space)) {
+			pr_err("Corruption in FPU ymmh_space area "
+			       "(got %li but %li expected)\n",
+			       (long)core->thread_info->fpregs->xsave->n_ymmh_space,
+			       (long)ARRAY_SIZE(x->ymmh.ymmh_space));
+			return false;
+		}
+	} else {
+		if (core->thread_info->fpregs->xsave) {
+			pr_err("FPU xsave area present, "
+			       "but host cpu doesn't support it\n");
+			return false;
+		}
+		return true;
+	}
+
+	return true;
+}
+
+static void show_rt_xsave_frame(struct xsave_struct *x)
+{
+	struct fpx_sw_bytes *fpx = (void *)&x->i387.sw_reserved;
+	struct xsave_hdr_struct *xsave_hdr = &x->xsave_hdr;
+	struct i387_fxsave_struct *i387 = &x->i387;
+
+	pr_debug("xsave runtime structure\n");
+	pr_debug("-----------------------\n");
+
+	pr_debug("cwd:%x swd:%x twd:%x fop:%x mxcsr:%x mxcsr_mask:%x\n",
+		 (int)i387->cwd, (int)i387->swd, (int)i387->twd,
+		 (int)i387->fop, (int)i387->mxcsr, (int)i387->mxcsr_mask);
+
+	pr_debug("magic1:%x extended_size:%x xstate_bv:%lx xstate_size:%x\n",
+		 fpx->magic1, fpx->extended_size, fpx->xstate_bv, fpx->xstate_size);
+
+	pr_debug("xstate_bv: %lx\n", xsave_hdr->xstate_bv);
+
+	pr_debug("-----------------------\n");
+}
+
+static int sigreturn_prep_xsave_frame(struct thread_restore_args *args, CoreEntry *core)
+{
+	struct xsave_struct *x = &args->xsave;
+
+	/*
+	 * If no FPU information provided -- we're restoring
+	 * old image which has no FPU support, or the dump simply
+	 * has no FPU support at all.
+	 */
+	if (!core->thread_info->fpregs) {
+		args->has_fpu = false;
+		return 0;
+	}
+
+	if (!valid_xsave_frame(core))
+		return -1;
+
+	args->has_fpu = true;
+
+#define assign_reg(dst, src, e)		do { dst.e = (__typeof__(dst.e))src->e; } while (0)
+#define assign_array(dst, src, e)	memcpy(dst.e, (src)->e, sizeof(dst.e))
+
+	*x = (struct xsave_struct) { };
+
+	assign_reg(x->i387, core->thread_info->fpregs, cwd);
+	assign_reg(x->i387, core->thread_info->fpregs, swd);
+	assign_reg(x->i387, core->thread_info->fpregs, twd);
+	assign_reg(x->i387, core->thread_info->fpregs, fop);
+	assign_reg(x->i387, core->thread_info->fpregs, rip);
+	assign_reg(x->i387, core->thread_info->fpregs, rdp);
+	assign_reg(x->i387, core->thread_info->fpregs, mxcsr);
+	assign_reg(x->i387, core->thread_info->fpregs, mxcsr_mask);
+
+	assign_array(x->i387, core->thread_info->fpregs, st_space);
+	assign_array(x->i387, core->thread_info->fpregs, xmm_space);
+
+	if (cpu_has_feature(X86_FEATURE_XSAVE)) {
+		struct fpx_sw_bytes *fpx_sw = (void *)&x->i387.sw_reserved;
+		void *magic2;
+
+		x->xsave_hdr.xstate_bv	= XSTATE_FP | XSTATE_SSE | XSTATE_YMM;
+		assign_array(x->ymmh, core->thread_info->fpregs->xsave, ymmh_space);
+
+		fpx_sw->magic1		= FP_XSTATE_MAGIC1;
+		fpx_sw->xstate_bv	= XSTATE_FP | XSTATE_SSE | XSTATE_YMM;
+		fpx_sw->xstate_size	= sizeof(struct xsave_struct);
+		fpx_sw->extended_size	= sizeof(struct xsave_struct) + FP_XSTATE_MAGIC2_SIZE;
+
+		/*
+		 * This should be at the end of xsave frame.
+		 */
+		magic2 = args->__pad + sizeof(struct xsave_struct);
+		*(u32 *)magic2 = FP_XSTATE_MAGIC2;
+	}
+
+	show_rt_xsave_frame(x);
+
+#undef assign_reg
+#undef assign_array
+
+	return 0;
+}
+
 static int sigreturn_restore(pid_t pid, CoreEntry *core)
 {
 	long restore_task_vma_len;
@@ -1756,6 +1886,9 @@ static int sigreturn_restore(pid_t pid, CoreEntry *core)
 			goto err;
 	}
 
+	if (sigreturn_prep_xsave_frame(&task_args->t, core))
+		goto err;
+
 	/* No longer need it */
 	core_entry__free_unpacked(core, NULL);
 
@@ -1829,6 +1962,9 @@ static int sigreturn_restore(pid_t pid, CoreEntry *core)
 				goto err;
 		}
 
+		if (sigreturn_prep_xsave_frame(&thread_args[i], core))
+			goto err;
+
 		core_entry__free_unpacked(core, NULL);
 
 		pr_info("Thread %4d stack %8p heap %8p rt_sigframe %8p\n",
diff --git a/pie/restorer.c b/pie/restorer.c
index b6211f1..4b3f58e 100644
--- a/pie/restorer.c
+++ b/pie/restorer.c
@@ -141,6 +141,22 @@ static void restore_sched_info(struct rst_sched_param *p)
 	sys_sched_setscheduler(0, p->policy, &parm);
 }
 
+static int restore_fpu(struct rt_sigframe *sigframe, struct thread_restore_args *args)
+{
+	if (args->has_fpu) {
+		unsigned long addr = (unsigned long)(void *)&args->xsave;
+
+		if ((addr % 64ul) == 0ul) {
+			sigframe->uc.uc_mcontext.fpstate = &args->xsave;
+		} else {
+			pr_err("Unaligned address passed: %lx\n", addr);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
 static int restore_gpregs(struct rt_sigframe *f, UserX86RegsEntry *r)
 {
 	long ret;
@@ -204,6 +220,8 @@ static int restore_thread_common(struct rt_sigframe *sigframe,
 		sigframe->uc.uc_sigmask.sig[0] = args->blk_sigset;
 
 	restore_sched_info(&args->sp);
+	if (restore_fpu(sigframe, args))
+		return -1;
 
 	return restore_gpregs(sigframe, &args->gpregs);
 }
-- 
1.8.0.1


--DocE+STaALJfprDB--


More information about the CRIU mailing list