[CRIU] [PATCH 6/8] compel: Save and restore regs inside compel by default

Cyrill Gorcunov gorcunov at openvz.org
Mon Nov 21 10:26:18 PST 2016


CRIU keeps all registers on CoreEntry and makes sigframe from
them as well, which means anyone using the compel library
have to provide own handlers, which is inconvenient. So
now it's possible to leave this task for libcompel itself:
it will save the regs and prerare sigframe on its own.

Signed-off-by: Cyrill Gorcunov <gorcunov at openvz.org>
---
 compel/Makefile                      |   1 +
 compel/arch/aarch64/src/lib/infect.c |  29 ++++++++
 compel/arch/arm/src/lib/infect.c     |  38 +++++++++++
 compel/arch/ppc64/src/lib/infect.c   | 125 +++++++++++++++++++++++++++++++++++
 compel/arch/x86/src/lib/infect.c     |  81 +++++++++++++++++++++++
 compel/include/infect-priv.h         |   6 +-
 compel/src/lib/infect.c              |  59 +++++++++++++++++
 criu/arch/ppc64/crtools.c            |   2 +-
 8 files changed, 339 insertions(+), 2 deletions(-)

diff --git a/compel/Makefile b/compel/Makefile
index 27ef59c52bc5..ad0315566ae2 100644
--- a/compel/Makefile
+++ b/compel/Makefile
@@ -7,6 +7,7 @@ COMPEL_SO_VERSION_CODE	:= $(shell expr $(COMPEL_SO_VERSION_MAJOR) \* 65536 \+ $(
 ccflags-y		+= -iquote compel/arch/$(ARCH)/src/lib/include
 ccflags-y		+= -iquote compel/include
 ccflags-y		+= -iquote compel/plugins/include
+ccflags-y		+= -fno-strict-aliasing
 ccflags-y		+= -fPIC
 
 #
diff --git a/compel/arch/aarch64/src/lib/infect.c b/compel/arch/aarch64/src/lib/infect.c
index 95d6fc5920fb..ed8e74d079ca 100644
--- a/compel/arch/aarch64/src/lib/infect.c
+++ b/compel/arch/aarch64/src/lib/infect.c
@@ -26,6 +26,35 @@ static inline void __always_unused __check_code_syscall(void)
 	BUILD_BUG_ON(!is_log2(sizeof(code_syscall)));
 }
 
+int sigreturn_prep_regs_plain(struct rt_sigframe *sigframe,
+			      user_regs_struct_t *regs,
+			      user_fpregs_struct_t *fpregs)
+{
+	struct fpsimd_context *fpsimd = RT_SIGFRAME_FPU(sigframe);
+
+	memcpy(sigframe->uc.uc_mcontext.regs, regs->regs, sizeof(regs->regs));
+
+	sigframe->uc.uc_mcontext.sp	= regs->sp;
+	sigframe->uc.uc_mcontext.pc	= regs->pc;
+	sigframe->uc.uc_mcontext.pstate	= regs->pstate;
+
+	memcpy(fpsimd->vregs, fpregs->vregs, 32 * sizeof(__uint128_t));
+
+	fpsimd->fpsr = fpregs->fpsr;
+	fpsimd->fpcr = fpregs->fpcr;
+
+	fpsimd->head.magic = FPSIMD_MAGIC;
+	fpsimd->head.size = sizeof(*fpsimd);
+
+	return 0;
+}
+
+int sigreturn_prep_fpu_frame_plain(struct rt_sigframe *sigframe,
+				   struct rt_sigframe *rsigframe)
+{
+	return 0;
+}
+
 int get_task_regs(pid_t pid, user_regs_struct_t regs, save_regs_t save, void *arg)
 {
 	struct iovec iov;
diff --git a/compel/arch/arm/src/lib/infect.c b/compel/arch/arm/src/lib/infect.c
index b452392113b0..f352b63f4d0a 100644
--- a/compel/arch/arm/src/lib/infect.c
+++ b/compel/arch/arm/src/lib/infect.c
@@ -25,6 +25,44 @@ static inline __always_unused void __check_code_syscall(void)
 	BUILD_BUG_ON(!is_log2(sizeof(code_syscall)));
 }
 
+int sigreturn_prep_regs_plain(struct rt_sigframe *sigframe,
+			      user_regs_struct_t *regs,
+			      user_fpregs_struct_t *fpregs)
+{
+	struct aux_sigframe *aux = (struct aux_sigframe *)(void *)&sigframe->sig.uc.uc_regspace;
+
+	sigframe->sig.uc.uc_mcontext.arm_r0 = regs->ARM_r0;
+	sigframe->sig.uc.uc_mcontext.arm_r1 = regs->ARM_r1;
+	sigframe->sig.uc.uc_mcontext.arm_r2 = regs->ARM_r2;
+	sigframe->sig.uc.uc_mcontext.arm_r3 = regs->ARM_r3;
+	sigframe->sig.uc.uc_mcontext.arm_r4 = regs->ARM_r4;
+	sigframe->sig.uc.uc_mcontext.arm_r5 = regs->ARM_r5;
+	sigframe->sig.uc.uc_mcontext.arm_r6 = regs->ARM_r6;
+	sigframe->sig.uc.uc_mcontext.arm_r7 = regs->ARM_r7;
+	sigframe->sig.uc.uc_mcontext.arm_r8 = regs->ARM_r8;
+	sigframe->sig.uc.uc_mcontext.arm_r9 = regs->ARM_r9;
+	sigframe->sig.uc.uc_mcontext.arm_r10 = regs->ARM_r10;
+	sigframe->sig.uc.uc_mcontext.arm_fp = regs->ARM_fp;
+	sigframe->sig.uc.uc_mcontext.arm_ip = regs->ARM_ip;
+	sigframe->sig.uc.uc_mcontext.arm_sp = regs->ARM_sp;
+	sigframe->sig.uc.uc_mcontext.arm_lr = regs->ARM_lr;
+	sigframe->sig.uc.uc_mcontext.arm_pc = regs->ARM_pc;
+	sigframe->sig.uc.uc_mcontext.arm_cpsr = regs->ARM_cpsr;
+
+	memcpy(&aux->vfp.ufp.fpregs, &fpregs->fpregs, sizeof(aux->vfp.ufp.fpregs));
+	aux->vfp.ufp.fpscr = fpregs->fpscr;
+	aux->vfp.magic = VFP_MAGIC;
+	aux->vfp.size = VFP_STORAGE_SIZE;
+
+	return 0;
+}
+
+int sigreturn_prep_fpu_frame_plain(struct rt_sigframe *sigframe,
+				   struct rt_sigframe *rsigframe)
+{
+	return 0;
+}
+
 #define PTRACE_GETVFPREGS 27
 int get_task_regs(pid_t pid, user_regs_struct_t regs, save_regs_t save, void *arg)
 {
diff --git a/compel/arch/ppc64/src/lib/infect.c b/compel/arch/ppc64/src/lib/infect.c
index b208f3dbbeb4..142b4edf0c5b 100644
--- a/compel/arch/ppc64/src/lib/infect.c
+++ b/compel/arch/ppc64/src/lib/infect.c
@@ -1,6 +1,7 @@
 #include <sys/ptrace.h>
 #include <sys/types.h>
 #include <sys/uio.h>
+#include <sys/user.h>
 #include <stdint.h>
 #include <errno.h>
 #include <compel/plugins/std/syscall-codes.h>
@@ -33,6 +34,130 @@ static inline void __check_code_syscall(void)
 	BUILD_BUG_ON(!is_log2(sizeof(code_syscall)));
 }
 
+static void prep_gp_regs(mcontext_t *dst, user_regs_struct_t *regs)
+{
+	memcpy(dst->gp_regs, regs->gpr, sizeof(regs->gpr));
+
+	dst->gp_regs[PT_NIP]		= regs->nip;
+	dst->gp_regs[PT_MSR]		= regs->msr;
+	dst->gp_regs[PT_ORIG_R3]	= regs->orig_gpr3;
+	dst->gp_regs[PT_CTR]		= regs->ctr;
+	dst->gp_regs[PT_LNK]		= regs->link;
+	dst->gp_regs[PT_XER]		= regs->xer;
+	dst->gp_regs[PT_CCR]		= regs->ccr;
+	dst->gp_regs[PT_TRAP]		= regs->trap;
+}
+
+static void put_fpu_regs(mcontext_t *mc, uint64_t *fpregs)
+{
+	uint64_t *mcfp = (uint64_t *)mc->fp_regs;
+
+	memcpy(mcfp, fpregs, sizeof(*fpregs) * NFPREG);
+}
+
+static void put_altivec_regs(mcontext_t *mc, __vector128 *vrregs)
+{
+	vrregset_t *v_regs = (vrregset_t *)(((unsigned long)mc->vmx_reserve + 15) & ~0xful);
+
+	memcpy(&v_regs->vrregs[0][0], vrregs, sizeof(uint64_t) * 2 * (NVRREG - 1));
+	v_regs->vrsave = *((uint32_t *)&vrregs[NVRREG - 1]);
+	mc->v_regs = v_regs;
+}
+
+static void put_vsx_regs(mcontext_t *mc, uint64_t *vsxregs)
+{
+	memcpy((uint64_t *)(mc->v_regs + 1), vsxregs, sizeof(*vsxregs) * NVSXREG);
+}
+
+int sigreturn_prep_regs_plain(struct rt_sigframe *sigframe,
+			      user_regs_struct_t *regs,
+			      user_fpregs_struct_t *fpregs)
+{
+	mcontext_t *dst_tc = &sigframe->uc_transact.uc_mcontext;
+	mcontext_t *dst = &sigframe->uc.uc_mcontext;
+
+	if (fpregs->flags & USER_FPREGS_FL_TM) {
+		prep_gp_regs(&sigframe->uc_transact.uc_mcontext, &fpregs->tm.regs);
+		prep_gp_regs(&sigframe->uc.uc_mcontext, &fpregs->tm.regs);
+	} else {
+		prep_gp_regs(&sigframe->uc.uc_mcontext, regs);
+	}
+
+	if (fpregs->flags & USER_FPREGS_FL_TM)
+		sigframe->uc.uc_link = &sigframe->uc_transact;
+
+	if (fpregs->flags & USER_FPREGS_FL_FP) {
+		if (fpregs->flags & USER_FPREGS_FL_TM) {
+			put_fpu_regs(&sigframe->uc_transact.uc_mcontext, fpregs->tm.fpregs);
+			put_fpu_regs(&sigframe->uc.uc_mcontext, fpregs->tm.fpregs);
+		} else {
+			put_fpu_regs(&sigframe->uc.uc_mcontext, fpregs->fpregs);
+		}
+	}
+
+	if (fpregs->flags & USER_FPREGS_FL_ALTIVEC) {
+		if (fpregs->flags & USER_FPREGS_FL_TM) {
+			put_altivec_regs(&sigframe->uc_transact.uc_mcontext, fpregs->tm.vrregs);
+			put_altivec_regs(&sigframe->uc.uc_mcontext, fpregs->tm.vrregs);
+
+			dst_tc->gp_regs[PT_MSR] |= MSR_VEC;
+		} else {
+			put_altivec_regs(&sigframe->uc.uc_mcontext, fpregs->vrregs);
+		}
+
+		dst->gp_regs[PT_MSR] |= MSR_VEC;
+
+		if (fpregs->flags & USER_FPREGS_FL_VSX) {
+			if (fpregs->flags & USER_FPREGS_FL_TM) {
+				put_vsx_regs(&sigframe->uc_transact.uc_mcontext, fpregs->tm.vsxregs);
+				put_vsx_regs(&sigframe->uc.uc_mcontext, fpregs->tm.vsxregs);
+
+				dst_tc->gp_regs[PT_MSR] |= MSR_VSX;
+			} else {
+				put_vsx_regs(&sigframe->uc.uc_mcontext, fpregs->vsxregs);
+			}
+			dst->gp_regs[PT_MSR] |= MSR_VSX;
+		}
+	}
+
+	return 0;
+}
+
+static void update_vregs(mcontext_t *lcontext, mcontext_t *rcontext)
+{
+	if (lcontext->v_regs) {
+		uint64_t offset = (uint64_t)(lcontext->v_regs) - (uint64_t)lcontext;
+		lcontext->v_regs = (vrregset_t *)((uint64_t)rcontext + offset);
+
+		pr_debug("Updated v_regs:%llx (rcontext:%llx)\n",
+			 (unsigned long long)lcontext->v_regs,
+			 (unsigned long long)rcontext);
+	}
+}
+
+int sigreturn_prep_fpu_frame_plain(struct rt_sigframe *frame,
+				   struct rt_sigframe *rframe)
+{
+	uint64_t msr = frame->uc.uc_mcontext.gp_regs[PT_MSR];
+
+	update_vregs(&frame->uc.uc_mcontext, &rframe->uc.uc_mcontext);
+
+	/* Sanity check: If TM so uc_link should be set, otherwise not */
+	if (MSR_TM_ACTIVE(msr) ^ (!!(frame->uc.uc_link))) {
+		BUG();
+		return -1;
+	}
+
+	/* Updating the transactional state address if any */
+	if (frame->uc.uc_link) {
+		update_vregs(&frame->uc_transact.uc_mcontext,
+			     &rframe->uc_transact.uc_mcontext);
+		frame->uc.uc_link =  &rframe->uc_transact;
+	}
+
+	return 0;
+}
+
 /* This is the layout of the POWER7 VSX registers and the way they
  * overlap with the existing FPR and VMX registers.
  *
diff --git a/compel/arch/x86/src/lib/infect.c b/compel/arch/x86/src/lib/infect.c
index b33eaa069873..d9609c4dbbb1 100644
--- a/compel/arch/x86/src/lib/infect.c
+++ b/compel/arch/x86/src/lib/infect.c
@@ -47,6 +47,87 @@ static inline __always_unused void __check_code_syscall(void)
 	BUILD_BUG_ON(!is_log2(sizeof(code_syscall)));
 }
 
+int sigreturn_prep_regs_plain(struct rt_sigframe *sigframe,
+			      user_regs_struct_t *regs,
+			      user_fpregs_struct_t *fpregs)
+{
+	bool is_native = user_regs_native(regs);
+	fpu_state_t *fpu_state = is_native ?
+				&sigframe->native.fpu_state :
+				&sigframe->compat.fpu_state;
+	if (is_native) {
+#define cpreg64_native(d, s)	sigframe->native.uc.uc_mcontext.d = regs->native.s
+		cpreg64_native(rdi, di);
+		cpreg64_native(rsi, si);
+		cpreg64_native(rbp, bp);
+		cpreg64_native(rsp, sp);
+		cpreg64_native(rbx, bx);
+		cpreg64_native(rdx, dx);
+		cpreg64_native(rcx, cx);
+		cpreg64_native(rip, ip);
+		cpreg64_native(rax, ax);
+		cpreg64_native(r8, r8);
+		cpreg64_native(r9, r9);
+		cpreg64_native(r10, r10);
+		cpreg64_native(r11, r11);
+		cpreg64_native(r12, r12);
+		cpreg64_native(r13, r13);
+		cpreg64_native(r14, r14);
+		cpreg64_native(r15, r15);
+		cpreg64_native(cs, cs);
+		cpreg64_native(eflags, flags);
+
+		sigframe->is_native = true;
+#undef cpreg64_native
+	} else {
+#define cpreg32_compat(d)	sigframe->compat.uc.uc_mcontext.d = regs->compat.d
+		cpreg32_compat(gs);
+		cpreg32_compat(fs);
+		cpreg32_compat(es);
+		cpreg32_compat(ds);
+		cpreg32_compat(di);
+		cpreg32_compat(si);
+		cpreg32_compat(bp);
+		cpreg32_compat(sp);
+		cpreg32_compat(bx);
+		cpreg32_compat(dx);
+		cpreg32_compat(cx);
+		cpreg32_compat(ip);
+		cpreg32_compat(ax);
+		cpreg32_compat(cs);
+		cpreg32_compat(ss);
+		cpreg32_compat(flags);
+#undef cpreg32_compat
+		sigframe->is_native = false;
+	}
+
+	fpu_state->has_fpu = true;
+	memcpy(&fpu_state->xsave, fpregs, sizeof(*fpregs));
+
+	return 0;
+}
+
+int sigreturn_prep_fpu_frame_plain(struct rt_sigframe *sigframe,
+				   struct rt_sigframe *rsigframe)
+{
+	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;
+	} else if (!sigframe->is_native && (addr % 32ul) == 0ul) {
+		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;
+	}
+
+	return 0;
+}
+
 #define get_signed_user_reg(pregs, name)				\
 	((user_regs_native(pregs)) ? (int64_t)((pregs)->native.name) :	\
 				(int32_t)((pregs)->compat.name))
diff --git a/compel/include/infect-priv.h b/compel/include/infect-priv.h
index 4eda1acdd3fd..fde5548b7e57 100644
--- a/compel/include/infect-priv.h
+++ b/compel/include/infect-priv.h
@@ -67,5 +67,9 @@ extern void *remote_mmap(struct parasite_ctl *ctl,
 		int flags, int fd, off_t offset);
 extern bool arch_can_dump_task(struct parasite_ctl *ctl);
 extern int get_task_regs(pid_t pid, user_regs_struct_t regs, save_regs_t save, void *arg);
-
+extern int sigreturn_prep_regs_plain(struct rt_sigframe *sigframe,
+				     user_regs_struct_t *regs,
+				     user_fpregs_struct_t *fpregs);
+extern int sigreturn_prep_fpu_frame_plain(struct rt_sigframe *sigframe,
+					  struct rt_sigframe *rsigframe);
 #endif
diff --git a/compel/src/lib/infect.c b/compel/src/lib/infect.c
index a8d3158c34c6..a66265c249e1 100644
--- a/compel/src/lib/infect.c
+++ b/compel/src/lib/infect.c
@@ -1010,6 +1010,58 @@ static void handle_sigchld(int signal, siginfo_t *siginfo, void *data)
 	/* FIXME -- what to do here? */
 }
 
+struct plain_regs_struct {
+	user_regs_struct_t regs;
+	user_fpregs_struct_t fpregs;
+};
+
+static int save_regs_plain(void *to, user_regs_struct_t *r, user_fpregs_struct_t *f)
+{
+	struct plain_regs_struct *prs = to;
+
+	prs->regs = *r;
+	prs->fpregs = *f;
+
+	return 0;
+}
+
+#ifndef RT_SIGFRAME_UC_SIGMASK
+#define RT_SIGFRAME_UC_SIGMASK(sigframe)				\
+	(k_rtsigset_t*)&RT_SIGFRAME_UC(sigframe)->uc_sigmask
+#endif
+
+static int make_sigframe_plain(void *from, struct rt_sigframe *f, struct rt_sigframe *rtf, k_rtsigset_t *b)
+{
+	struct plain_regs_struct *prs = from;
+	k_rtsigset_t *blk_sigset;
+
+	/*
+	 * Make sure it's zeroified.
+	 */
+	memset(f, 0, sizeof(*f));
+
+	if (sigreturn_prep_regs_plain(f, &prs->regs, &prs->fpregs))
+		return -1;
+
+	blk_sigset = RT_SIGFRAME_UC_SIGMASK(f);
+	if (b)
+		memcpy(blk_sigset, b, sizeof(k_rtsigset_t));
+	else
+		memset(blk_sigset, 0, sizeof(k_rtsigset_t));
+
+	if (RT_SIGFRAME_HAS_FPU(f)) {
+		if (sigreturn_prep_fpu_frame_plain(f, rtf))
+			return -1;
+	}
+
+	/*
+	 * FIXME What about sas?
+	 * setup_sas(sigframe, core->thread_core->sas);
+	 */
+
+	return 0;
+}
+
 struct parasite_ctl *compel_prepare(int pid)
 {
 	struct parasite_ctl *ctl;
@@ -1026,6 +1078,12 @@ struct parasite_ctl *compel_prepare(int pid)
 	ictx->child_handler = handle_sigchld;
 	sigaction(SIGCHLD, NULL, &ictx->orig_handler);
 
+	ictx->save_regs = save_regs_plain;
+	ictx->make_sigframe = make_sigframe_plain;
+	ictx->regs_arg = xmalloc(sizeof(struct plain_regs_struct));
+	if (ictx->regs_arg == NULL)
+		goto err;
+
 	if (ictx->syscall_ip == (unsigned long)MAP_FAILED)
 		goto err;
 	ictx->sock = make_sock_for(pid);
@@ -1036,6 +1094,7 @@ out:
 	return ctl;
 
 err:
+	free(ictx->regs_arg);
 	free(ctl);
 	goto out;
 }
diff --git a/criu/arch/ppc64/crtools.c b/criu/arch/ppc64/crtools.c
index 074490956b5a..c4f0df790ffc 100644
--- a/criu/arch/ppc64/crtools.c
+++ b/criu/arch/ppc64/crtools.c
@@ -49,8 +49,8 @@ static UserPpc64FpstateEntry *copy_fp_regs(uint64_t *fpregs)
 
 static void put_fpu_regs(mcontext_t *mc, UserPpc64FpstateEntry *fpe)
 {
-	int i;
 	uint64_t *mcfp = (uint64_t *)mc->fp_regs;
+	size_t i;
 
 	for (i = 0; i < fpe->n_fpregs; i++)
 		mcfp[i] =  fpe->fpregs[i];
-- 
2.7.4



More information about the CRIU mailing list