@@ -67,7 +67,9 @@ typedef struct target_sigaltstack {
#define TARGET_MINSIGSTKSZ 4096
#define TARGET_SIGSTKSZ 16384
+#if !defined(TARGET_SPARC64) || defined(TARGET_ABI32)
#define TARGET_ARCH_HAS_SETUP_FRAME
+#endif
/* bit-flags */
#define TARGET_SS_AUTODISARM (1U << 31) /* disable sas during sighandling */
@@ -4,14 +4,16 @@
#include "../sparc/target_errno.h"
struct target_pt_regs {
- abi_ulong u_regs[16];
- abi_ulong tstate;
- abi_ulong pc;
- abi_ulong npc;
- abi_ulong y;
- abi_ulong fprs;
+ abi_ulong u_regs[16];
+ abi_ulong tstate;
+ abi_ulong pc;
+ abi_ulong npc;
+ uint32_t y;
+ uint32_t magic;
};
+#define TARGET_PT_REGS_MAGIC 0x57ac6c00
+
#define UNAME_MACHINE "sparc64"
#define UNAME_MINIMUM_RELEASE "2.6.32"
@@ -17,7 +17,10 @@
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
-#include "../sparc/signal.c"
+#include "qemu/osdep.h"
+#include "qemu.h"
+#include "signal-common.h"
+#include "linux-user/trace.h"
#define SPARC_MC_TSTATE 0
#define SPARC_MC_PC 1
@@ -295,3 +298,243 @@ void sparc64_get_context(CPUSPARCState *env)
unlock_user_struct(ucp, ucp_addr, 1);
force_sig(TARGET_SIGSEGV);
}
+
+struct target_sparc_stackf {
+ struct target_reg_window win;
+ uint64_t xargs[8];
+};
+
+struct target_siginfo_fpu_t {
+ uint64_t dregs[32];
+ uint64_t fsr;
+ uint64_t gsr;
+ uint64_t fprs;
+};
+
+struct target_sigcontext {
+ target_siginfo_t info;
+ struct target_pt_regs regs;
+ uint64_t fpu_save;
+ target_stack_t stack;
+ target_sigset_t mask;
+ uint64_t rwin_save;
+};
+
+struct target_rt_sigframe {
+ struct target_sparc_stackf ss;
+ struct target_sigcontext sc;
+ struct target_siginfo_fpu_t fpu;
+};
+
+static abi_ulong get_sigframe(struct target_sigaction *sa,
+ CPUSPARCState *env, int framesize)
+{
+ abi_ulong sp = target_sigsp(get_sp_from_cpustate(env), sa);
+ return (sp - framesize) & -16;
+}
+
+static void save_pt_regs(struct target_pt_regs *regs, CPUSPARCState *env)
+{
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ __put_user(env->gregs[i], ®s->u_regs[i]);
+ }
+ for (i = 0; i < 8; i++) {
+ __put_user(env->regwptr[WREG_O0 + i], ®s->u_regs[i + 8]);
+ }
+ __put_user(sparc64_tstate(env), ®s->tstate);
+ __put_user(env->pc, ®s->pc);
+ __put_user(env->npc, ®s->npc);
+ __put_user(env->y, ®s->y);
+ __put_user(TARGET_PT_REGS_MAGIC, ®s->magic);
+}
+
+static void restore_pt_regs(struct target_pt_regs *regs, CPUSPARCState *env)
+{
+ uint64_t tstate;
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ __get_user(env->gregs[i], ®s->u_regs[i]);
+ }
+ for (i = 0; i < 8; i++) {
+ __get_user(env->regwptr[WREG_O0 + i], ®s->u_regs[i + 8]);
+ }
+
+ __get_user(env->y, ®s->y);
+ __get_user(tstate, ®s->tstate);
+
+ /* User can only change condition codes and %asi in tstate. */
+ cpu_put_ccr(env, tstate >> 32);
+ env->asi = extract64(tstate, 24, 8);
+}
+
+static void save_fpu_state(struct target_siginfo_fpu_t *regs,
+ CPUSPARCState *env)
+{
+ int i;
+
+ /* QEMU does not lazy fpu saving. Save the entire fp register bank. */
+ for (i = 0; i < 32; ++i) {
+ __put_user(env->fpr[i].ll, ®s->dregs[i]);
+ }
+ __put_user(env->fsr, ®s->fsr);
+ __put_user(env->gsr, ®s->gsr);
+ __put_user(env->fprs, ®s->fprs);
+}
+
+static void restore_fpu_state(struct target_siginfo_fpu_t *regs,
+ CPUSPARCState *env)
+{
+ uint64_t fprs;
+ int i;
+
+ /* In case the user mucks about with FPRS, restore as directed. */
+ __get_user(fprs, ®s->fprs);
+ if (fprs & FPRS_DL) {
+ for (i = 0; i < 16; ++i) {
+ __get_user(env->fpr[i].ll, ®s->dregs[i]);
+ }
+ }
+ if (fprs & FPRS_DU) {
+ for (i = 16; i < 32; ++i) {
+ __get_user(env->fpr[i].ll, ®s->dregs[i]);
+ }
+ }
+ __get_user(env->fsr, ®s->fsr);
+ __get_user(env->gsr, ®s->gsr);
+ env->fprs |= fprs;
+}
+
+void setup_rt_frame(int sig, struct target_sigaction *ka,
+ target_siginfo_t *info,
+ target_sigset_t *set, CPUSPARCState *env)
+{
+ abi_ulong sf_addr, sp;
+ struct target_rt_sigframe *sf = NULL;
+ void *window;
+
+ sf_addr = get_sigframe(ka, env, sizeof(*sf));
+ trace_user_setup_rt_frame(env, sf_addr);
+ if (!lock_user_struct(VERIFY_WRITE, sf, sf_addr, 0)) {
+ goto do_sigsegv;
+ }
+
+ /* 2. Save the current process state */
+ save_pt_regs(&sf->sc.regs, env);
+ save_fpu_state(&sf->fpu, env);
+ __put_user(sf_addr + offsetof(struct target_rt_sigframe, fpu),
+ &sf->sc.fpu_save);
+ __put_user(0, &sf->sc.rwin_save); /* TODO: save_rwin_state */
+
+ /*
+ * Copy one register window from the top-of-stack into the signal frame.
+ * The balance of the sparc_stackf struct is for the callee --- the call
+ * abi requires the space for spilling argument registers.
+ */
+ sp = get_sp_from_cpustate(env);
+ window = lock_user(VERIFY_READ, sp, sizeof(struct target_reg_window), 1);
+ if (!window) {
+ goto do_sigsegv;
+ }
+ memcpy(sf, window, sizeof(struct target_reg_window));
+ unlock_user(window, sp, 0);
+
+ target_save_altstack(&sf->sc.stack, env);
+ for (int i = 0; i < TARGET_NSIG_WORDS; ++i) {
+ __put_user(set->sig[i], &sf->sc.mask.sig[i]);
+ }
+
+ unlock_user(sf, sf_addr, sizeof(*sf));
+
+ /* 3. signal handler back-trampoline and parameters */
+ env->regwptr[WREG_SP] = sf_addr - TARGET_STACK_BIAS;
+ env->regwptr[WREG_O0] = sig;
+ env->regwptr[WREG_O1] = sf_addr + offsetof(struct target_rt_sigframe, sc);
+ env->regwptr[WREG_O2] = sf_addr + offsetof(struct target_rt_sigframe, sc);
+
+ /* 4. return to kernel instructions */
+ env->regwptr[WREG_O7] = ka->ka_restorer;
+
+ /* 5. signal handler */
+ env->pc = ka->_sa_handler;
+ env->npc = env->pc + 4;
+ return;
+
+ do_sigsegv:
+ unlock_user(sf, sf_addr, 0);
+ force_sigsegv(sig);
+}
+
+/*
+ * __NR_sigreturn still exists for backward compatiblity,
+ * but it is set to sys_nis_syscall for sparc64.
+ */
+long do_sigreturn(CPUSPARCState *env)
+{
+ return -TARGET_ENOSYS;
+}
+
+long do_rt_sigreturn(CPUSPARCState *env)
+{
+ abi_ulong sf_addr, sc_addr, tpc, tnpc, ptr;
+ struct target_sigcontext *sc = NULL;
+ sigset_t set;
+
+ sf_addr = get_sp_from_cpustate(env);
+ trace_user_do_rt_sigreturn(env, sf_addr);
+
+ if (sf_addr & 15) {
+ goto do_sigsegv;
+ }
+ sc_addr = sf_addr + offsetof(struct target_rt_sigframe, sc);
+ if (!lock_user_struct(VERIFY_READ, sc, sc_addr, 1)) {
+ goto do_sigsegv;
+ }
+
+ /* Validate SP alignment. */
+ __get_user(ptr, &sc->regs.u_regs[8 + WREG_SP]);
+ if ((ptr + TARGET_STACK_BIAS) & 7) {
+ goto do_sigsegv;
+ }
+
+ /* Validate PC and NPC alignment. */
+ __get_user(tpc, &sc->regs.pc);
+ __get_user(tnpc, &sc->regs.npc);
+ if ((tpc | tnpc) & 3) {
+ goto do_sigsegv;
+ }
+
+ restore_pt_regs(&sc->regs, env);
+
+ __get_user(ptr, &sc->fpu_save);
+ if (ptr) {
+ struct target_siginfo_fpu_t *fpu;
+ if ((ptr & 7) || !lock_user_struct(VERIFY_READ, fpu, ptr, 1)) {
+ goto do_sigsegv;
+ }
+ restore_fpu_state(fpu, env);
+ unlock_user_struct(fpu, ptr, 0);
+ }
+
+ __get_user(ptr, &sc->rwin_save);
+ if (ptr) {
+ goto do_sigsegv; /* TODO: restore_rwin_state */
+ }
+
+ target_to_host_sigset(&set, &sc->mask);
+ set_sigmask(&set);
+ target_restore_altstack(&sc->stack, env);
+
+ env->pc = tpc;
+ env->npc = tnpc;
+
+ unlock_user_struct(sc, sc_addr, 0);
+ return -TARGET_QEMU_ESIGRETURN;
+
+ do_sigsegv:
+ unlock_user_struct(sc, sc_addr, 0);
+ force_sig(TARGET_SIGSEGV);
+ return -TARGET_QEMU_ESIGRETURN;
+}
We've been using the 32-bit sparc abi for 64-bit signals. There's a surprising amount of difference, beginning with the fact that 64-bit always uses rt signal frames. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> --- linux-user/sparc/target_signal.h | 2 + linux-user/sparc64/target_syscall.h | 14 +- linux-user/sparc64/signal.c | 245 +++++++++++++++++++++++++++- 3 files changed, 254 insertions(+), 7 deletions(-) -- 2.25.1