[PATCHv3,05/30] x86: add 32-bit sigframe for rt_sigreturn

Submitted by Dmitry Safonov on June 28, 2016, 7:23 p.m.

Details

Message ID 20160628192423.14943-6-dsafonov@virtuozzo.com
State Rejected
Series "x86 Compatible C/R, part 2"
Headers show

Commit Message

Dmitry Safonov June 28, 2016, 7:23 p.m.
I tried to split this patch as much as it goes, but still
it's quite huge.
Mostly it has many compatible structures declarations.
Lesser it contains adaptation to new native/compat sigframe duality.

The only logic that changed by this patch is the order of
creating sigframe in construct_sigframe.

Cc: Cyrill Gorcunov <gorcunov@openvz.org>
Signed-off-by: Dmitry Safonov <dsafonov@virtuozzo.com>
---
 criu/arch/x86/crtools.c              | 122 +++++++++++++++++---------
 criu/arch/x86/include/asm/restorer.h | 165 +++++++++++++++++++++++++++++++++--
 criu/arch/x86/include/asm/types.h    |  17 +++-
 criu/sigframe.c                      |  17 +++-
 4 files changed, 269 insertions(+), 52 deletions(-)

Patch hide | download patch | download mbox

diff --git a/criu/arch/x86/crtools.c b/criu/arch/x86/crtools.c
index b33764a9ad4c..eb5b9592125d 100644
--- a/criu/arch/x86/crtools.c
+++ b/criu/arch/x86/crtools.c
@@ -482,7 +482,9 @@  static void show_rt_xsave_frame(struct xsave_struct *x)
 
 int restore_fpu(struct rt_sigframe *sigframe, CoreEntry *core)
 {
-	fpu_state_t *fpu_state = &sigframe->fpu_state;
+	fpu_state_t *fpu_state = core_is_compat(core) ?
+		&sigframe->compat.fpu_state :
+		&sigframe->native.fpu_state;
 	struct xsave_struct *x = &fpu_state->xsave;
 
 	/*
@@ -570,60 +572,100 @@  void *mmap_seized(struct parasite_ctl *ctl,
 	return (void *)map;
 }
 
-int restore_gpregs(struct rt_sigframe *f, UserX86RegsEntry *r)
+#ifdef CONFIG_X86_64
+#define CPREG32(d)	f->compat.uc.uc_mcontext.d = r->d
+#else
+#define CPREG32(d)	f->uc.uc_mcontext.d = r->d
+#endif
+static void restore_compat_gpregs(struct rt_sigframe *f, UserX86RegsEntry *r)
 {
-	/* FIXME: rt_sigcontext for compatible tasks */
-	if (r->gpregs_case != USER_X86_REGS_CASE_T__NATIVE) {
-		pr_err("Can't prepare rt_sigframe for compatible task restore\n");
-		return -1;
-	}
+	CPREG32(gs);
+	CPREG32(fs);
+	CPREG32(es);
+	CPREG32(ds);
 
-#define CPREG1(d)	f->uc.uc_mcontext.d = r->d
-#define CPREG2(d, s)	f->uc.uc_mcontext.d = r->s
+	CPREG32(di); CPREG32(si); CPREG32(bp); CPREG32(sp); CPREG32(bx);
+	CPREG32(dx); CPREG32(cx); CPREG32(ip); CPREG32(ax);
+	CPREG32(cs);
+	CPREG32(ss);
+	CPREG32(flags);
 
 #ifdef CONFIG_X86_64
-	CPREG1(r8);
-	CPREG1(r9);
-	CPREG1(r10);
-	CPREG1(r11);
-	CPREG1(r12);
-	CPREG1(r13);
-	CPREG1(r14);
-	CPREG1(r15);
+	f->is_native = false;
 #endif
+}
+#undef CPREG32
 
-	CPREG2(rdi, di);
-	CPREG2(rsi, si);
-	CPREG2(rbp, bp);
-	CPREG2(rbx, bx);
-	CPREG2(rdx, dx);
-	CPREG2(rax, ax);
-	CPREG2(rcx, cx);
-	CPREG2(rsp, sp);
-	CPREG2(rip, ip);
-	CPREG2(eflags, flags);
-
-	CPREG1(cs);
-	CPREG1(ss);
-
-#ifdef CONFIG_X86_32
-	CPREG1(gs);
-	CPREG1(fs);
-	CPREG1(es);
-	CPREG1(ds);
-#endif
+#ifdef CONFIG_X86_64
+#define CPREG64(d, s)	f->native.uc.uc_mcontext.d = r->s
+static void restore_native_gpregs(struct rt_sigframe *f, UserX86RegsEntry *r)
+{
+	CPREG64(rdi, di);
+	CPREG64(rsi, si);
+	CPREG64(rbp, bp);
+	CPREG64(rsp, sp);
+	CPREG64(rbx, bx);
+	CPREG64(rdx, dx);
+	CPREG64(rcx, cx);
+	CPREG64(rip, ip);
+	CPREG64(rax, ax);
+
+	CPREG64(r8, r8);
+	CPREG64(r9, r9);
+	CPREG64(r10, r10);
+	CPREG64(r11, r11);
+	CPREG64(r12, r12);
+	CPREG64(r13, r13);
+	CPREG64(r14, r14);
+	CPREG64(r15, r15);
+
+	CPREG64(cs, cs);
+
+	CPREG64(eflags, flags);
+
+	f->is_native = true;
+}
+#undef CPREG64
 
+int restore_gpregs(struct rt_sigframe *f, UserX86RegsEntry *r)
+{
+	switch (r->gpregs_case) {
+		case USER_X86_REGS_CASE_T__NATIVE:
+			restore_native_gpregs(f, r);
+			break;
+		case USER_X86_REGS_CASE_T__COMPAT:
+			restore_compat_gpregs(f, r);
+			break;
+		default:
+			pr_err("Can't prepare rt_sigframe: regs_case corrupt\n");
+			return -1;
+	}
 	return 0;
 }
+#else /* !CONFIG_X86_64 */
+int restore_gpregs(struct rt_sigframe *f, UserX86RegsEntry *r)
+{
+	restore_compat_gpregs(f, r);
+	return 0;
+}
+#endif
 
 int sigreturn_prep_fpu_frame(struct rt_sigframe *sigframe,
 		struct rt_sigframe *rsigframe)
 {
-	fpu_state_t *fpu_state = RT_SIGFRAME_FPU(rsigframe);
+	/*
+	 * Use local sigframe to check native/compat type,
+	 * but set address for 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 ((addr % 64ul) == 0ul) {
-		sigframe->uc.uc_mcontext.fpstate = &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 = (u32)addr;
 	} else {
 		pr_err("Unaligned address passed: %lx\n", addr);
 		return -1;
diff --git a/criu/arch/x86/include/asm/restorer.h b/criu/arch/x86/include/asm/restorer.h
index 3c17fc67516b..9c57512696fd 100644
--- a/criu/arch/x86/include/asm/restorer.h
+++ b/criu/arch/x86/include/asm/restorer.h
@@ -36,19 +36,138 @@  struct rt_sigcontext {
 	unsigned long			reserved1[8];
 };
 
+struct rt_sigcontext_32 {
+	u32				gs;
+	u32				fs;
+	u32				es;
+	u32				ds;
+	u32				di;
+	u32				si;
+	u32				bp;
+	u32				sp;
+	u32				bx;
+	u32				dx;
+	u32				cx;
+	u32				ax;
+	u32				trapno;
+	u32				err;
+	u32				ip;
+	u32				cs;
+	u32				flags;
+	u32				sp_at_signal;
+	u32				ss;
+
+	u32				fpstate;
+	u32				oldmask;
+	u32				cr2;
+};
+
 #define SIGFRAME_MAX_OFFSET 8
 
 #include "sigframe.h"
 
-struct rt_sigframe {
+/*
+ * XXX: move declarations to generic sigframe.h or sigframe-compat.h
+ *      when (if) other architectures will support compatible C/R
+ */
+
+typedef u32			compat_uptr_t;
+typedef u32			compat_size_t;
+typedef u32			compat_sigset_word;
+
+#define _COMPAT_NSIG		64
+#define _COMPAT_NSIG_BPW	32
+#define _COMPAT_NSIG_WORDS	(_COMPAT_NSIG / _COMPAT_NSIG_BPW)
+
+typedef struct {
+	compat_sigset_word	sig[_COMPAT_NSIG_WORDS];
+} compat_sigset_t;
+
+#ifdef CONFIG_X86_64
+typedef struct compat_siginfo {
+	int	si_signo;
+	int	si_errno;
+	int	si_code;
+	int	_pad[128/sizeof(int) - 3];
+} compat_siginfo_t;
+
+static inline void __always_unused __check_compat_sigset_t(void)
+{
+	BUILD_BUG_ON(sizeof(compat_sigset_t) != sizeof(k_rtsigset_t));
+}
+#else
+#define rt_sigframe_ia32		rt_sigframe
+#endif
+
+typedef struct compat_sigaltstack {
+	compat_uptr_t		ss_sp;
+	int			ss_flags;
+	compat_size_t		ss_size;
+} compat_stack_t;
+
+struct ucontext_ia32 {
+	unsigned int		uc_flags;
+	unsigned int		uc_link;
+	compat_stack_t		uc_stack;
+	struct rt_sigcontext_32	uc_mcontext;
+	k_rtsigset_t		uc_sigmask; /* mask last for extensibility */
+};
+
+struct rt_sigframe_ia32 {
+	u32			pretcode;
+	int			sig;
+	u32			pinfo;
+	u32			puc;
+#ifdef CONFIG_X86_64
+	compat_siginfo_t	info;
+#else
+	struct rt_siginfo	info;
+#endif
+	struct ucontext_ia32	uc;
+	char			retcode[8];
+
+	/* fp state follows here */
+	fpu_state_t		fpu_state;
+};
+
+#ifdef CONFIG_X86_64
+struct rt_sigframe_64 {
 	char			*pretcode;
 	struct rt_ucontext	uc;
 	struct rt_siginfo	info;
 
+	/* fp state follows here */
 	fpu_state_t		fpu_state;
 };
 
-#ifdef CONFIG_X86_64
+struct rt_sigframe {
+	union {
+		struct rt_sigframe_ia32	compat;
+		struct rt_sigframe_64	native;
+	};
+	bool is_native;
+};
+
+#define RT_SIGFRAME_UC_SIGMASK(rt_sigframe) ((rt_sigframe->is_native) ?	\
+	(&rt_sigframe->native.uc.uc_sigmask) :				\
+	(&rt_sigframe->compat.uc.uc_sigmask))
+
+#define RT_SIGFRAME_REGIP(rt_sigframe) ((rt_sigframe->is_native) ?	\
+	(rt_sigframe)->native.uc.uc_mcontext.rip :			\
+	(rt_sigframe)->compat.uc.uc_mcontext.ip)
+
+#define RT_SIGFRAME_FPU(rt_sigframe)	((rt_sigframe->is_native) ?	\
+	(&(rt_sigframe)->native.fpu_state) : (&(rt_sigframe)->compat.fpu_state))
+#define RT_SIGFRAME_HAS_FPU(rt_sigframe) (RT_SIGFRAME_FPU(rt_sigframe)->has_fpu)
+
+/*
+ * Sigframe offset is different for native/compat tasks.
+ * Offsets calculations one may see at kernel:
+ * - compatible is in sys32_rt_sigreturn at arch/x86/ia32/ia32_signal.c
+ * - native is in sys_rt_sigreturn at arch/x86/kernel/signal.c
+ */
+#define RT_SIGFRAME_OFFSET(rt_sigframe)	((rt_sigframe->is_native) ? 8 : 4 )
+
 #define ARCH_RT_SIGRETURN(new_sp)					\
 	asm volatile(							\
 		     "movq %0, %%rax				    \n"	\
@@ -105,7 +224,8 @@  struct rt_sigframe {
 		     :						\
 		     : "r"(ret)					\
 		     : "memory")
-#else /* CONFIG_X86_64 */
+#else /* !CONFIG_X86_64 */
+
 #define ARCH_RT_SIGRETURN(new_sp)					\
 	asm volatile(							\
 		     "movl %0, %%eax				    \n"	\
@@ -133,14 +253,47 @@  struct rt_sigframe {
 		     :						\
 		     : "r"(ret)					\
 		     : "memory")
-#endif /* CONFIG_X86_64 */
 
 #define RT_SIGFRAME_UC(rt_sigframe) (&rt_sigframe->uc)
-#define RT_SIGFRAME_REGIP(rt_sigframe) (rt_sigframe)->uc.uc_mcontext.rip
+#define RT_SIGFRAME_OFFSET(rt_sigframe)	4
+#define RT_SIGFRAME_REGIP(rt_sigframe)					\
+	(unsigned long)(rt_sigframe)->uc.uc_mcontext.ip
 #define RT_SIGFRAME_FPU(rt_sigframe) (&(rt_sigframe)->fpu_state)
 #define RT_SIGFRAME_HAS_FPU(rt_sigframe) (RT_SIGFRAME_FPU(rt_sigframe)->has_fpu)
+#endif /* !CONFIG_X86_64 */
+
+static inline void
+__setup_sas_compat(struct ucontext_ia32* uc, ThreadSasEntry *sas)
+{
+	uc->uc_stack.ss_sp	= (compat_uptr_t)(sas)->ss_sp;
+	uc->uc_stack.ss_flags	= (int)(sas)->ss_flags;
+	uc->uc_stack.ss_size	= (compat_size_t)(sas)->ss_size;
+}
+
+static inline void
+__setup_sas(struct rt_sigframe* sigframe, ThreadSasEntry *sas)
+{
+#ifdef CONFIG_X86_64
+	if (sigframe->is_native) {
+		struct rt_ucontext *uc	= &sigframe->native.uc;
+
+		uc->uc_stack.ss_sp	= (void *)decode_pointer((sas)->ss_sp);
+		uc->uc_stack.ss_flags	= (int)(sas)->ss_flags;
+		uc->uc_stack.ss_size	= (size_t)(sas)->ss_size;
+	} else {
+		__setup_sas_compat(&sigframe->compat.uc, sas);
+	}
+#else
+	__setup_sas_compat(&sigframe->uc, sas);
+#endif
+}
 
-#define RT_SIGFRAME_OFFSET(rt_sigframe) 8
+static inline void _setup_sas(struct rt_sigframe* sigframe, ThreadSasEntry *sas)
+{
+	if (sas)
+		__setup_sas(sigframe, sas);
+}
+#define setup_sas _setup_sas
 
 int restore_gpregs(struct rt_sigframe *f, UserX86RegsEntry *r);
 int restore_nonsigframe_gpregs(UserX86RegsEntry *r);
diff --git a/criu/arch/x86/include/asm/types.h b/criu/arch/x86/include/asm/types.h
index 7c0239bd772d..ec7e2a3c8aaf 100644
--- a/criu/arch/x86/include/asm/types.h
+++ b/criu/arch/x86/include/asm/types.h
@@ -139,7 +139,19 @@  static inline bool user_regs_native(user_regs_struct_t *pregs)
 		((pregs)->native.name) : ((pregs)->compat.name))
 #define set_user_reg(pregs, name, val) ((user_regs_native(pregs)) ?	\
 		((pregs)->native.name = (val)) : ((pregs)->compat.name = (val)))
-#else
+static inline int core_is_compat(CoreEntry *c)
+{
+	switch (c->thread_info->gpregs->gpregs_case)
+	{
+		case USER_X86_REGS_CASE_T__NATIVE:
+			return 0;
+		case USER_X86_REGS_CASE_T__COMPAT:
+			return 1;
+		default:
+			return -1;
+	}
+}
+#else /* !CONFIG_X86_64 */
 typedef struct {
 	union {
 		user_regs_struct32 native;
@@ -148,7 +160,8 @@  typedef struct {
 #define user_regs_native(pregs)		true
 #define get_user_reg(pregs, name)	((pregs)->native.name)
 #define set_user_reg(pregs, name, val)	((pregs)->native.name = val)
-#endif
+static inline int core_is_compat(CoreEntry *c) { return 0; }
+#endif /* !CONFIG_X86_64 */
 
 typedef struct {
 	unsigned short	cwd;
diff --git a/criu/sigframe.c b/criu/sigframe.c
index 72eaee965ecf..be471a806efb 100644
--- a/criu/sigframe.c
+++ b/criu/sigframe.c
@@ -6,6 +6,7 @@ 
 
 #include "images/core.pb-c.h"
 
+#ifndef setup_sas
 static inline void setup_sas(struct rt_sigframe* sigframe, ThreadSasEntry *sas)
 {
 	if (sas) {
@@ -17,16 +18,27 @@  static inline void setup_sas(struct rt_sigframe* sigframe, ThreadSasEntry *sas)
 #undef UC
 	}
 }
+#endif
 
+#ifndef RT_SIGFRAME_UC_SIGMASK
 #define RT_SIGFRAME_UC_SIGMASK(sigframe)				\
 	(k_rtsigset_t*)&RT_SIGFRAME_UC(sigframe)->uc_sigmask
+#endif
 
 int construct_sigframe(struct rt_sigframe *sigframe,
 				     struct rt_sigframe *rsigframe,
 				     CoreEntry *core)
 {
-	k_rtsigset_t *blk_sigset = RT_SIGFRAME_UC_SIGMASK(sigframe);
+	k_rtsigset_t *blk_sigset;
 
+	/*
+	 * Copy basic register set in the first place: this will set
+	 * rt_sigframe type: native/compat.
+	 */
+	if (restore_gpregs(sigframe, CORE_THREAD_ARCH_INFO(core)->gpregs))
+		return -1;
+
+	blk_sigset = RT_SIGFRAME_UC_SIGMASK(sigframe);
 	if (core->tc)
 		memcpy(blk_sigset, &core->tc->blk_sigset, sizeof(k_rtsigset_t));
 	else if (core->thread_core->has_blk_sigset) {
@@ -42,9 +54,6 @@  int construct_sigframe(struct rt_sigframe *sigframe,
 		if (sigreturn_prep_fpu_frame(sigframe, rsigframe))
 			return -1;
 
-	if (restore_gpregs(sigframe, CORE_THREAD_ARCH_INFO(core)->gpregs))
-		return -1;
-
 	setup_sas(sigframe, core->thread_core->sas);
 
 	return 0;