diff --git a/criu/arch/x86/crtools.c b/criu/arch/x86/crtools.c index 245b87444..3972aa32d 100644 --- a/criu/arch/x86/crtools.c +++ b/criu/arch/x86/crtools.c @@ -496,7 +496,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; /* @@ -584,51 +586,83 @@ 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); +#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); - CPREG1(cs); - CPREG1(ss); + CPREG64(r8, r8); + CPREG64(r9, r9); + CPREG64(r10, r10); + CPREG64(r11, r11); + CPREG64(r12, r12); + CPREG64(r13, r13); + CPREG64(r14, r14); + CPREG64(r15, r15); -#ifdef CONFIG_X86_32 - CPREG1(gs); - CPREG1(fs); - CPREG1(es); - CPREG1(ds); -#endif + 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 /* Copied from the gdb header gdb/nat/x86-dregs.h */ diff --git a/criu/arch/x86/include/asm/restorer.h b/criu/arch/x86/include/asm/restorer.h index ac8745144..e2ae30399 100644 --- a/criu/arch/x86/include/asm/restorer.h +++ b/criu/arch/x86/include/asm/restorer.h @@ -74,6 +74,39 @@ : "memory") #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 +} + +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/sigframe.h b/criu/arch/x86/include/asm/sigframe.h index 2bee1ad00..2dc1a6933 100644 --- a/criu/arch/x86/include/asm/sigframe.h +++ b/criu/arch/x86/include/asm/sigframe.h @@ -39,24 +39,141 @@ struct rt_sigcontext { unsigned long reserved1[8]; }; +struct rt_sigcontext_32 { + uint32_t gs; + uint32_t fs; + uint32_t es; + uint32_t ds; + uint32_t di; + uint32_t si; + uint32_t bp; + uint32_t sp; + uint32_t bx; + uint32_t dx; + uint32_t cx; + uint32_t ax; + uint32_t trapno; + uint32_t err; + uint32_t ip; + uint32_t cs; + uint32_t flags; + uint32_t sp_at_signal; + uint32_t ss; + + uint32_t fpstate; + uint32_t oldmask; + uint32_t cr2; +}; + #include "sigframe-common.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; + +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)); +} + +#ifdef CONFIG_X86_32 +#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 { + uint32_t pretcode; + int32_t sig; + uint32_t pinfo; + uint32_t 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; }; -#define RT_SIGFRAME_UC(rt_sigframe) (&rt_sigframe->uc) -#define RT_SIGFRAME_REGIP(rt_sigframe) (rt_sigframe)->uc.uc_mcontext.rip -#define RT_SIGFRAME_FPU(rt_sigframe) (&(rt_sigframe)->fpu_state) +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) -#define RT_SIGFRAME_OFFSET(rt_sigframe) 8 +/* + * 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 ) -#ifdef CONFIG_X86_64 #define ARCH_RT_SIGRETURN(new_sp) \ asm volatile( \ "movq %0, %%rax \n" \ @@ -67,6 +184,13 @@ struct rt_sigframe { : "r"(new_sp) \ : "rax","rsp","memory") #else /* CONFIG_X86_64 */ +#define RT_SIGFRAME_UC(rt_sigframe) (&rt_sigframe->uc) +#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) + #define ARCH_RT_SIGRETURN(new_sp) \ asm volatile( \ "movl %0, %%eax \n" \ diff --git a/criu/arch/x86/include/asm/types.h b/criu/arch/x86/include/asm/types.h index 1160c1bf7..743dc69fd 100644 --- a/criu/arch/x86/include/asm/types.h +++ b/criu/arch/x86/include/asm/types.h @@ -10,6 +10,22 @@ #include "images/core.pb-c.h" +#ifdef CONFIG_X86_64 +static inline int core_is_compat(CoreEntry *c) +{ + switch (c->thread_info->gpregs->mode) { + case USER_X86_REGS_MODE__NATIVE: + return 0; + case USER_X86_REGS_MODE__COMPAT: + return 1; + default: + return -1; + } +} +#else /* CONFIG_X86_64 */ +static inline int core_is_compat(CoreEntry *c) { return 0; } +#endif /* CONFIG_X86_64 */ + typedef void rt_signalfn_t(int, siginfo_t *, void *); typedef rt_signalfn_t *rt_sighandler_t; diff --git a/criu/arch/x86/sigframe.c b/criu/arch/x86/sigframe.c index 7da4a89d5..7faed892a 100644 --- a/criu/arch/x86/sigframe.c +++ b/criu/arch/x86/sigframe.c @@ -7,15 +7,24 @@ #include "log.h" int sigreturn_prep_fpu_frame(struct rt_sigframe *sigframe, - struct rt_sigframe *rsigframe) + 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 = (uint32_t)addr; } else { - pr_err("Unaligned address passed: %lx\n", addr); + pr_err("Unaligned address passed: %lx (native %d)\n", + addr, sigframe->is_native); return -1; } diff --git a/criu/sigframe.c b/criu/sigframe.c index 67363d372..b2dd85add 100644 --- a/criu/sigframe.c +++ b/criu/sigframe.c @@ -3,6 +3,7 @@ #include "restore.h" #include "images/core.pb-c.h" +#ifndef setup_sas static inline void setup_sas(struct rt_sigframe* sigframe, ThreadSasEntry *sas) { if (sas) { @@ -14,20 +15,28 @@ 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, k_rtsigset_t *blkset, 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 (blkset) memcpy(blk_sigset, blkset, sizeof(k_rtsigset_t)); else