diff --git a/Makefile b/Makefile index ad052f158..f2583a272 100644 --- a/Makefile +++ b/Makefile @@ -68,11 +68,7 @@ endif # # CFLAGS_PIE: # -# We assume that compel code does not change floating point registers. -# On s390 gcc uses fprs to cache gprs. Therefore disable floating point -# with -msoft-float. -# -# Also ensure with -fno-optimize-sibling-calls that we don't create GOT +# Ensure with -fno-optimize-sibling-calls that we don't create GOT # (Global Offset Table) relocations with gcc compilers that don't have # commit "S/390: Fix 64 bit sibcall". ifeq ($(ARCH),s390) @@ -80,7 +76,7 @@ ifeq ($(ARCH),s390) SRCARCH := s390 VDSO := y DEFINES := -DCONFIG_S390 - CFLAGS_PIE := -msoft-float -fno-optimize-sibling-calls + CFLAGS_PIE := -fno-optimize-sibling-calls endif export CFLAGS_PIE diff --git a/compel/arch/s390/src/lib/infect.c b/compel/arch/s390/src/lib/infect.c index 1614fa60e..3c1fff68c 100644 --- a/compel/arch/s390/src/lib/infect.c +++ b/compel/arch/s390/src/lib/infect.c @@ -148,32 +148,6 @@ int get_vx_regs(pid_t pid, user_fpregs_struct_t *fpregs) return 0; } -/* - * Set vector registers - */ -int set_vx_regs(pid_t pid, user_fpregs_struct_t *fpregs) -{ - struct iovec iov; - int rc; - - if (!(fpregs->flags & USER_FPREGS_VXRS)) - return 0; - - iov.iov_base = &fpregs->vxrs_low; - iov.iov_len = sizeof(fpregs->vxrs_low); - rc = ptrace(PTRACE_SETREGSET, pid, NT_S390_VXRS_LOW, &iov); - if (rc) { - pr_perror("Couldn't set VXRS_LOW registers\n"); - return rc; - } - - iov.iov_base = &fpregs->vxrs_high; - iov.iov_len = sizeof(fpregs->vxrs_high); - rc = ptrace(PTRACE_SETREGSET, pid, NT_S390_VXRS_HIGH, &iov); - if (rc) - pr_perror("Couldn't set VXRS_HIGH registers\n"); - return rc; -} /* * Prepare task registers for restart diff --git a/criu/arch/s390/crtools.c b/criu/arch/s390/crtools.c index 4bd21ec97..cfab50868 100644 --- a/criu/arch/s390/crtools.c +++ b/criu/arch/s390/crtools.c @@ -22,6 +22,12 @@ #include "protobuf.h" #include "images/core.pb-c.h" #include "images/creds.pb-c.h" +#include "ptrace.h" +#include "pstree.h" + +#define NT_PRFPREG 2 +#define NT_S390_VXRS_LOW 0x309 +#define NT_S390_VXRS_HIGH 0x30a /* * Print general purpose and access registers @@ -339,3 +345,106 @@ void arch_free_thread_info(CoreEntry *core) xfree(CORE_THREAD_ARCH_INFO(core)); CORE_THREAD_ARCH_INFO(core) = NULL; } + +/* + * Set regset for pid + */ +static int setregset(int pid, int set, const char *set_str, struct iovec *iov) +{ + if (ptrace(PTRACE_SETREGSET, pid, set, iov) == 0) + return 0; + pr_perror("Couldn't set %s registers for pid %d", set_str, pid); + return -1; +} + +/* + * Set floating point registers for pid from fpregs + */ +static int set_fp_regs(pid_t pid, user_fpregs_struct_t *fpregs) +{ + struct iovec iov; + + iov.iov_base = &fpregs->prfpreg; + iov.iov_len = sizeof(fpregs->prfpreg); + return setregset(pid, NT_PRFPREG, "PRFPREG", &iov); +} + +/* + * Set vector registers + */ +static int set_vx_regs(pid_t pid, user_fpregs_struct_t *fpregs) +{ + struct iovec iov; + + if (!(fpregs->flags & USER_FPREGS_VXRS)) + return 0; + + iov.iov_base = &fpregs->vxrs_low; + iov.iov_len = sizeof(fpregs->vxrs_low); + if (setregset(pid, NT_S390_VXRS_LOW, "S390_VXRS_LOW", &iov)) + return -1; + + iov.iov_base = &fpregs->vxrs_high; + iov.iov_len = sizeof(fpregs->vxrs_high); + return setregset(pid, NT_S390_VXRS_HIGH, "S390_VXRS_HIGH", &iov); +} + +/* + * Restore registers for pid from core + */ +static int set_task_regs(pid_t pid, CoreEntry *core) +{ + UserS390VxrsHighEntry *cvxrs_high; + UserS390VxrsLowEntry *cvxrs_low; + UserS390FpregsEntry *cfpregs; + user_fpregs_struct_t fpregs; + + memset(&fpregs, 0, sizeof(fpregs)); + /* Floating point registers */ + cfpregs = CORE_THREAD_ARCH_INFO(core)->fpregs; + if (!cfpregs) + return -1; + fpregs.prfpreg.fpc = cfpregs->fpc; + memcpy(fpregs.prfpreg.fprs, cfpregs->fprs, sizeof(fpregs.prfpreg.fprs)); + if (set_fp_regs(pid, &fpregs) < 0) + return -1; + /* Vector registers (optional) */ + cvxrs_low = CORE_THREAD_ARCH_INFO(core)->vxrs_low; + if (!cvxrs_low) + return 0; + cvxrs_high = CORE_THREAD_ARCH_INFO(core)->vxrs_high; + if (!cvxrs_high) + return -1; + fpregs.flags |= USER_FPREGS_VXRS; + memcpy(&fpregs.vxrs_low, cvxrs_low->regs, sizeof(fpregs.vxrs_low)); + memcpy(&fpregs.vxrs_high, cvxrs_high->regs, sizeof(fpregs.vxrs_high)); + + return set_vx_regs(pid, &fpregs); +} + +/* + * Restore vector and floating point registers for all threads + */ +int arch_set_thread_regs(struct pstree_item *item) +{ + int i; + + for_each_pstree_item(item) { + if (item->pid->state == TASK_DEAD || + item->pid->state == TASK_ZOMBIE || + item->pid->state == TASK_HELPER) + continue; + for (i = 0; i < item->nr_threads; i++) { + if (item->threads[i]->state == TASK_DEAD || + item->threads[i]->state == TASK_ZOMBIE) + continue; + if (set_task_regs(item->threads[i]->real, + item->core[i])) { + pr_perror("Not set registers for task %d", + item->threads[i]->real); + return -1; + } + } + } + return 0; +} diff --git a/criu/arch/s390/include/asm/restore.h b/criu/arch/s390/include/asm/restore.h index 96358ffc3..6463d8e62 100644 --- a/criu/arch/s390/include/asm/restore.h +++ b/criu/arch/s390/include/asm/restore.h @@ -4,7 +4,6 @@ #include "asm/restorer.h" #include "images/core.pb-c.h" - /* * Load stack to %r15, return address in %r14 and argument 1 into %r2 */ @@ -25,5 +24,4 @@ #define core_get_tls(pcore, ptls) int restore_fpu(struct rt_sigframe *sigframe, CoreEntry *core); - #endif diff --git a/criu/cr-dump.c b/criu/cr-dump.c index a5682df99..f9ff0f468 100644 --- a/criu/cr-dump.c +++ b/criu/cr-dump.c @@ -83,6 +83,15 @@ #include "fault-injection.h" #include "dump.h" +/* + * Architectures can overwrite this function to restore register sets that + * are not covered by ptrace_set/get_regs(). + */ +int __attribute__((weak)) arch_set_thread_regs(struct pstree_item *item) +{ + return 0; +} + static char loc_buf[PAGE_SIZE]; void free_mappings(struct vm_area_list *vma_area_list) @@ -1641,6 +1650,7 @@ static int cr_dump_finish(int ret) delete_link_remaps(); clean_cr_time_mounts(); } + arch_set_thread_regs(root_item); pstree_switch_state(root_item, (ret || post_dump_ret) ? TASK_ALIVE : opts.final_state); diff --git a/criu/parasite-syscall.c b/criu/parasite-syscall.c index 3afc7b651..8ace6ad66 100644 --- a/criu/parasite-syscall.c +++ b/criu/parasite-syscall.c @@ -186,6 +186,11 @@ int parasite_dump_thread_seized(struct parasite_ctl *ctl, int id, tc->has_blk_sigset = true; memcpy(&tc->blk_sigset, compel_thread_sigmask(tctl), sizeof(k_rtsigset_t)); + ret = compel_get_thread_regs(tctl, save_task_regs, core); + if (ret) { + pr_err("Can't obtain regs for thread %d\n", pid); + goto err_rth; + } ret = compel_run_in_thread(tctl, PARASITE_CMD_DUMP_THREAD); if (ret) { @@ -199,12 +204,6 @@ int parasite_dump_thread_seized(struct parasite_ctl *ctl, int id, goto err_rth; } - ret = compel_get_thread_regs(tctl, save_task_regs, core); - if (ret) { - pr_err("Can't obtain regs for thread %d\n", pid); - goto err_rth; - } - compel_release_thread(tctl); tid->ns[0].virt = args->tid;