From fbdc200ac2d0d29e88ee6af9b77810c0f84265a6 Mon Sep 17 00:00:00 2001 From: Tom Musta Date: Mon, 30 Jun 2014 08:13:36 -0500 Subject: linux-user: Fix Stack Pointer Bug in PPC setup_rt_frame The code that sets the stack frame back pointer is incorrect for the setup_rt_frame() code; qemu will abort (SIGSEGV) in some environments. The setup_frame code was fixed in commit beb526b12134a6b6744125deec5a7fe24a8f92e3 but the setup_rt_frame code was not. Make the setup_rt_frame code consistent with the setup_frame code. Signed-off-by: Tom Musta Signed-off-by: Alexander Graf --- linux-user/signal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'linux-user/signal.c') diff --git a/linux-user/signal.c b/linux-user/signal.c index 26929c5..2952956 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -4751,7 +4751,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, /* Create a stack frame for the caller of the handler. */ newsp = rt_sf_addr - (SIGNAL_FRAMESIZE + 16); - __put_user(env->gpr[1], (target_ulong *)(uintptr_t) newsp); + err |= put_user(env->gpr[1], newsp, target_ulong); if (err) goto sigsegv; -- cgit v1.1 From 7678108b134fc14d0a94fd13c0dd1129525c12d7 Mon Sep 17 00:00:00 2001 From: Tom Musta Date: Mon, 30 Jun 2014 08:13:37 -0500 Subject: linux-user: Split PPC Trampoline Encoding from Register Save Split the encoding of the PowerPC sigreturn trampoline from the saving of register state onto the signal handler stack. This will make it easier in subsequent patches to deal with variations in the stack frame layouts between 32 and 64 bit PowerPC. Signed-off-by: Tom Musta Signed-off-by: Alexander Graf --- linux-user/signal.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'linux-user/signal.c') diff --git a/linux-user/signal.c b/linux-user/signal.c index 2952956..7365d5d 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -4491,8 +4491,7 @@ static target_ulong get_sigframe(struct target_sigaction *ka, return newsp; } -static void save_user_regs(CPUPPCState *env, struct target_mcontext *frame, - int sigret) +static void save_user_regs(CPUPPCState *env, struct target_mcontext *frame) { target_ulong msr = env->msr; int i; @@ -4559,11 +4558,14 @@ static void save_user_regs(CPUPPCState *env, struct target_mcontext *frame, /* Store MSR. */ __put_user(msr, &frame->mc_gregs[TARGET_PT_MSR]); +} +static void encode_trampoline(int sigret, uint32_t *tramp) +{ /* Set up the sigreturn trampoline: li r0,sigret; sc. */ if (sigret) { - __put_user(0x38000000UL | sigret, &frame->tramp[0]); - __put_user(0x44000002UL, &frame->tramp[1]); + __put_user(0x38000000 | sigret, &tramp[0]); + __put_user(0x44000002, &tramp[1]); } } @@ -4674,7 +4676,10 @@ static void setup_frame(int sig, struct target_sigaction *ka, __put_user(sig, &sc->signal); /* Save user regs. */ - save_user_regs(env, &frame->mctx, TARGET_NR_sigreturn); + save_user_regs(env, &frame->mctx); + + /* Construct the trampoline code on the stack. */ + encode_trampoline(TARGET_NR_sigreturn, (uint32_t *)&frame->mctx.tramp); /* The kernel checks for the presence of a VDSO here. We don't emulate a vdso, so use a sigreturn system call. */ @@ -4740,7 +4745,8 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, } frame = &rt_sf->uc.tuc_mcontext; - save_user_regs(env, frame, TARGET_NR_rt_sigreturn); + save_user_regs(env, frame); + encode_trampoline(TARGET_NR_rt_sigreturn, (uint32_t *)&frame->tramp); /* The kernel checks for the presence of a VDSO here. We don't emulate a vdso, so use a sigreturn system call. */ -- cgit v1.1 From 61e75fecef17aff4b0ecef26b300b90de410aaa2 Mon Sep 17 00:00:00 2001 From: Tom Musta Date: Mon, 30 Jun 2014 08:13:38 -0500 Subject: linux-user: Enable Signal Handlers on PPC64 Enable the 64-bit PowerPC signal handling code that was previously disabled via #ifdefs. Specifically: - Move the target_mcontext (register save area) structure and append it to the 64-bit target_sigcontext structure. This provides the space on the stack for saving and restoring context. - Define the target_rt_sigframe for 64-bit. - Adjust the setup_frame and setup_rt_frame routines to properly select the target_mcontext area and trampoline within the stack frame; tthis is different for 32-bit and 64-bit implementations. - Adjust the do_setcontext stub for 64-bit so that it compiles without warnings. The 64-bit signal handling code is still not functional after this change; but the 32-bit code is. Subsequent changes will address specific issues with the 64-bit code. Signed-off-by: Tom Musta [agraf: fix build on 32bit hosts, ppc64abi32] Signed-off-by: Alexander Graf --- linux-user/signal.c | 121 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 73 insertions(+), 48 deletions(-) (limited to 'linux-user/signal.c') diff --git a/linux-user/signal.c b/linux-user/signal.c index 7365d5d..56c38db 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -4325,15 +4325,7 @@ badframe: return 0; } -#elif defined(TARGET_PPC) && !defined(TARGET_PPC64) - -/* FIXME: Many of the structures are defined for both PPC and PPC64, but - the signal handling is different enough that we haven't implemented - support for PPC64 yet. Hence the restriction above. - - There are various #if'd blocks for code for TARGET_PPC64. These - blocks should go away so that we can successfully run 32-bit and - 64-bit binaries on a QEMU configured for PPC64. */ +#elif defined(TARGET_PPC) /* Size of dummy stack frame allocated when calling signal handler. See arch/powerpc/include/asm/ptrace.h. */ @@ -4343,6 +4335,33 @@ badframe: #define SIGNAL_FRAMESIZE 64 #endif +/* See arch/powerpc/include/asm/ucontext.h. Only used for 32-bit PPC; + on 64-bit PPC, sigcontext and mcontext are one and the same. */ +struct target_mcontext { + target_ulong mc_gregs[48]; + /* Includes fpscr. */ + uint64_t mc_fregs[33]; + target_ulong mc_pad[2]; + /* We need to handle Altivec and SPE at the same time, which no + kernel needs to do. Fortunately, the kernel defines this bit to + be Altivec-register-large all the time, rather than trying to + twiddle it based on the specific platform. */ + union { + /* SPE vector registers. One extra for SPEFSCR. */ + uint32_t spe[33]; + /* Altivec vector registers. The packing of VSCR and VRSAVE + varies depending on whether we're PPC64 or not: PPC64 splits + them apart; PPC32 stuffs them together. */ +#if defined(TARGET_PPC64) +#define QEMU_NVRREG 34 +#else +#define QEMU_NVRREG 33 +#endif + ppc_avr_t altivec[QEMU_NVRREG]; +#undef QEMU_NVRREG + } mc_vregs __attribute__((__aligned__(16))); +}; + /* See arch/powerpc/include/asm/sigcontext.h. */ struct target_sigcontext { target_ulong _unused[4]; @@ -4353,7 +4372,9 @@ struct target_sigcontext { target_ulong handler; target_ulong oldmask; target_ulong regs; /* struct pt_regs __user * */ - /* TODO: PPC64 includes extra bits here. */ +#if defined(TARGET_PPC64) + struct target_mcontext mcontext; +#endif }; /* Indices for target_mcontext.mc_gregs, below. @@ -4408,32 +4429,6 @@ enum { TARGET_PT_REGS_COUNT = 44 }; -/* See arch/powerpc/include/asm/ucontext.h. Only used for 32-bit PPC; - on 64-bit PPC, sigcontext and mcontext are one and the same. */ -struct target_mcontext { - target_ulong mc_gregs[48]; - /* Includes fpscr. */ - uint64_t mc_fregs[33]; - target_ulong mc_pad[2]; - /* We need to handle Altivec and SPE at the same time, which no - kernel needs to do. Fortunately, the kernel defines this bit to - be Altivec-register-large all the time, rather than trying to - twiddle it based on the specific platform. */ - union { - /* SPE vector registers. One extra for SPEFSCR. */ - uint32_t spe[33]; - /* Altivec vector registers. The packing of VSCR and VRSAVE - varies depending on whether we're PPC64 or not: PPC64 splits - them apart; PPC32 stuffs them together. */ -#if defined(TARGET_PPC64) -#define QEMU_NVRREG 34 -#else -#define QEMU_NVRREG 33 -#endif - ppc_avr_t altivec[QEMU_NVRREG]; -#undef QEMU_NVRREG - } mc_vregs __attribute__((__aligned__(16))); -}; struct target_ucontext { target_ulong tuc_flags; @@ -4447,7 +4442,7 @@ struct target_ucontext { target_sigset_t tuc_sigmask; #if defined(TARGET_PPC64) target_sigset_t unused[15]; /* Allow for uc_sigmask growth */ - struct target_sigcontext tuc_mcontext; + struct target_sigcontext tuc_sigcontext; #else int32_t tuc_maskext[30]; int32_t tuc_pad2[3]; @@ -4462,12 +4457,32 @@ struct target_sigframe { int32_t abigap[56]; }; +#if defined(TARGET_PPC64) + +#define TARGET_TRAMP_SIZE 6 + +struct target_rt_sigframe { + /* sys_rt_sigreturn requires the ucontext be the first field */ + struct target_ucontext uc; + target_ulong _unused[2]; + uint32_t trampoline[TARGET_TRAMP_SIZE]; + target_ulong pinfo; /* struct siginfo __user * */ + target_ulong puc; /* void __user * */ + struct target_siginfo info; + /* 64 bit ABI allows for 288 bytes below sp before decrementing it. */ + char abigap[288]; +} __attribute__((aligned(16))); + +#else + struct target_rt_sigframe { struct target_siginfo info; struct target_ucontext uc; int32_t abigap[56]; }; +#endif + /* We use the mc_pad field for the signal return trampoline. */ #define tramp mc_pad @@ -4667,7 +4682,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, __put_user(ka->_sa_handler, &sc->handler); __put_user(set->sig[0], &sc->oldmask); -#if defined(TARGET_PPC64) +#if TARGET_ABI_BITS == 64 __put_user(set->sig[0] >> 32, &sc->_unused[3]); #else __put_user(set->sig[1], &sc->_unused[3]); @@ -4717,7 +4732,8 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, target_sigset_t *set, CPUPPCState *env) { struct target_rt_sigframe *rt_sf; - struct target_mcontext *frame; + uint32_t *trampptr = 0; + struct target_mcontext *mctx = 0; target_ulong rt_sf_addr, newsp = 0; int i, err = 0; int signal; @@ -4738,19 +4754,28 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, &rt_sf->uc.tuc_stack.ss_flags); __put_user(target_sigaltstack_used.ss_size, &rt_sf->uc.tuc_stack.ss_size); +#if !defined(TARGET_PPC64) __put_user(h2g (&rt_sf->uc.tuc_mcontext), &rt_sf->uc.tuc_regs); +#endif for(i = 0; i < TARGET_NSIG_WORDS; i++) { __put_user(set->sig[i], &rt_sf->uc.tuc_sigmask.sig[i]); } - frame = &rt_sf->uc.tuc_mcontext; - save_user_regs(env, frame); - encode_trampoline(TARGET_NR_rt_sigreturn, (uint32_t *)&frame->tramp); +#if defined(TARGET_PPC64) + mctx = &rt_sf->uc.tuc_sigcontext.mcontext; + trampptr = &rt_sf->trampoline[0]; +#else + mctx = &rt_sf->uc.tuc_mcontext; + trampptr = (uint32_t *)&rt_sf->uc.tuc_mcontext.tramp; +#endif + + save_user_regs(env, mctx); + encode_trampoline(TARGET_NR_rt_sigreturn, trampptr); /* The kernel checks for the presence of a VDSO here. We don't emulate a vdso, so use a sigreturn system call. */ - env->lr = (target_ulong) h2g(frame->tramp); + env->lr = (target_ulong) h2g(trampptr); /* Turn off all fp exceptions. */ env->fpscr = 0; @@ -4795,7 +4820,7 @@ long do_sigreturn(CPUPPCState *env) goto sigsegv; #if defined(TARGET_PPC64) - set.sig[0] = sc->oldmask + ((long)(sc->_unused[3]) << 32); + set.sig[0] = sc->oldmask + ((uint64_t)(sc->_unused[3]) << 32); #else __get_user(set.sig[0], &sc->oldmask); __get_user(set.sig[1], &sc->_unused[3]); @@ -4823,6 +4848,10 @@ sigsegv: /* See arch/powerpc/kernel/signal_32.c. */ static int do_setcontext(struct target_ucontext *ucp, CPUPPCState *env, int sig) { +#if defined(TARGET_PPC64) + fprintf(stderr, "do_setcontext: not implemented\n"); + return 0; +#else struct target_mcontext *mcp; target_ulong mcp_addr; sigset_t blocked; @@ -4832,10 +4861,6 @@ static int do_setcontext(struct target_ucontext *ucp, CPUPPCState *env, int sig) sizeof (set))) return 1; -#if defined(TARGET_PPC64) - fprintf (stderr, "do_setcontext: not implemented\n"); - return 0; -#else __get_user(mcp_addr, &ucp->tuc_regs); if (!lock_user_struct(VERIFY_READ, mcp, mcp_addr, 1)) -- cgit v1.1 From 8d6ab333ebf2751d2467bd7dae94a04742052e26 Mon Sep 17 00:00:00 2001 From: Tom Musta Date: Mon, 30 Jun 2014 08:13:39 -0500 Subject: linux-user: Properly Dereference PPC64 ELFv1 Signal Handler Pointer Properly dereference 64-bit PPC ELF V1 ABIT function pointers to signal handlers. On this platform, function pointers are pointers to structures and the first 64 bits of such a structure contains the function's entry point. The second 64 bits contains the TOC pointer, which must be placed into GPR 2. Signed-off-by: Tom Musta Signed-off-by: Alexander Graf --- linux-user/signal.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'linux-user/signal.c') diff --git a/linux-user/signal.c b/linux-user/signal.c index 56c38db..e8e49db 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -4483,6 +4483,15 @@ struct target_rt_sigframe { #endif +#if defined(TARGET_PPC64) + +struct target_func_ptr { + target_ulong entry; + target_ulong toc; +}; + +#endif + /* We use the mc_pad field for the signal return trampoline. */ #define tramp mc_pad @@ -4714,7 +4723,17 @@ static void setup_frame(int sig, struct target_sigaction *ka, env->gpr[1] = newsp; env->gpr[3] = signal; env->gpr[4] = frame_addr + offsetof(struct target_sigframe, sctx); + +#if defined(TARGET_PPC64) + /* PPC64 function pointers are pointers to OPD entries. */ + struct target_func_ptr *handler = + (struct target_func_ptr *)g2h(ka->_sa_handler); + env->nip = tswapl(handler->entry); + env->gpr[2] = tswapl(handler->toc); +#else env->nip = (target_ulong) ka->_sa_handler; +#endif + /* Signal handlers are entered in big-endian mode. */ env->msr &= ~MSR_LE; @@ -4793,7 +4812,17 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, env->gpr[4] = (target_ulong) h2g(&rt_sf->info); env->gpr[5] = (target_ulong) h2g(&rt_sf->uc); env->gpr[6] = (target_ulong) h2g(rt_sf); + +#if defined(TARGET_PPC64) + /* PPC64 function pointers are pointers to OPD entries. */ + struct target_func_ptr *handler = + (struct target_func_ptr *)g2h(ka->_sa_handler); + env->nip = tswapl(handler->entry); + env->gpr[2] = tswapl(handler->toc); +#else env->nip = (target_ulong) ka->_sa_handler; +#endif + /* Signal handlers are entered in big-endian mode. */ env->msr &= ~MSR_LE; -- cgit v1.1 From 19774ec5c4019d1802b497d702e1eefd3c193e84 Mon Sep 17 00:00:00 2001 From: Tom Musta Date: Mon, 30 Jun 2014 08:13:40 -0500 Subject: linux-user: Implement do_setcontext for PPC64 Eliminate the stub for the do_setcontext() function for TARGET_PPC64. The implementation re-uses the existing TARGET_PPC32 code with the only change being the computation of the address of the register save area. Signed-off-by: Tom Musta Signed-off-by: Alexander Graf --- linux-user/signal.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'linux-user/signal.c') diff --git a/linux-user/signal.c b/linux-user/signal.c index e8e49db..281d1c4 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -4877,10 +4877,6 @@ sigsegv: /* See arch/powerpc/kernel/signal_32.c. */ static int do_setcontext(struct target_ucontext *ucp, CPUPPCState *env, int sig) { -#if defined(TARGET_PPC64) - fprintf(stderr, "do_setcontext: not implemented\n"); - return 0; -#else struct target_mcontext *mcp; target_ulong mcp_addr; sigset_t blocked; @@ -4890,7 +4886,12 @@ static int do_setcontext(struct target_ucontext *ucp, CPUPPCState *env, int sig) sizeof (set))) return 1; +#if defined(TARGET_PPC64) + mcp_addr = h2g(ucp) + + offsetof(struct target_ucontext, tuc_sigcontext.mcontext); +#else __get_user(mcp_addr, &ucp->tuc_regs); +#endif if (!lock_user_struct(VERIFY_READ, mcp, mcp_addr, 1)) return 1; @@ -4901,7 +4902,6 @@ static int do_setcontext(struct target_ucontext *ucp, CPUPPCState *env, int sig) unlock_user_struct(mcp, mcp_addr, 1); return 0; -#endif } long do_rt_sigreturn(CPUPPCState *env) -- cgit v1.1 From 145855801a002aaf1310630df41425a6bc39cf47 Mon Sep 17 00:00:00 2001 From: Tom Musta Date: Mon, 30 Jun 2014 08:13:42 -0500 Subject: linux-user: Handle PPC64 ELFv2 Function Pointers Function pointers in the 64-bit ELFv2 PowerPC ABI are actual (internal) entry point addresses. However, when invoking a function via a function pointer, GPR 12 must also be set to this address so that the TOC may be handled properly. Add this support to the invocation of a signal handler. Signed-off-by: Tom Musta Signed-off-by: Alexander Graf --- linux-user/signal.c | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) (limited to 'linux-user/signal.c') diff --git a/linux-user/signal.c b/linux-user/signal.c index 281d1c4..e11b208 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -4681,6 +4681,9 @@ static void setup_frame(int sig, struct target_sigaction *ka, target_ulong frame_addr, newsp; int err = 0; int signal; +#if defined(TARGET_PPC64) + struct image_info *image = ((TaskState *)thread_cpu->opaque)->info; +#endif frame_addr = get_sigframe(ka, env, sizeof(*frame)); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 1)) @@ -4725,11 +4728,18 @@ static void setup_frame(int sig, struct target_sigaction *ka, env->gpr[4] = frame_addr + offsetof(struct target_sigframe, sctx); #if defined(TARGET_PPC64) - /* PPC64 function pointers are pointers to OPD entries. */ - struct target_func_ptr *handler = - (struct target_func_ptr *)g2h(ka->_sa_handler); - env->nip = tswapl(handler->entry); - env->gpr[2] = tswapl(handler->toc); + if (get_ppc64_abi(image) < 2) { + /* ELFv1 PPC64 function pointers are pointers to OPD entries. */ + struct target_func_ptr *handler = + (struct target_func_ptr *)g2h(ka->_sa_handler); + env->nip = tswapl(handler->entry); + env->gpr[2] = tswapl(handler->toc); + } else { + /* ELFv2 PPC64 function pointers are entry points, but R12 + * must also be set */ + env->nip = tswapl((target_ulong) ka->_sa_handler); + env->gpr[12] = env->nip; + } #else env->nip = (target_ulong) ka->_sa_handler; #endif @@ -4756,6 +4766,9 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, target_ulong rt_sf_addr, newsp = 0; int i, err = 0; int signal; +#if defined(TARGET_PPC64) + struct image_info *image = ((TaskState *)thread_cpu->opaque)->info; +#endif rt_sf_addr = get_sigframe(ka, env, sizeof(*rt_sf)); if (!lock_user_struct(VERIFY_WRITE, rt_sf, rt_sf_addr, 1)) @@ -4814,11 +4827,18 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, env->gpr[6] = (target_ulong) h2g(rt_sf); #if defined(TARGET_PPC64) - /* PPC64 function pointers are pointers to OPD entries. */ - struct target_func_ptr *handler = - (struct target_func_ptr *)g2h(ka->_sa_handler); - env->nip = tswapl(handler->entry); - env->gpr[2] = tswapl(handler->toc); + if (get_ppc64_abi(image) < 2) { + /* ELFv1 PPC64 function pointers are pointers to OPD entries. */ + struct target_func_ptr *handler = + (struct target_func_ptr *)g2h(ka->_sa_handler); + env->nip = tswapl(handler->entry); + env->gpr[2] = tswapl(handler->toc); + } else { + /* ELFv2 PPC64 function pointers are entry points, but R12 + * must also be set */ + env->nip = tswapl((target_ulong) ka->_sa_handler); + env->gpr[12] = env->nip; + } #else env->nip = (target_ulong) ka->_sa_handler; #endif -- cgit v1.1