From b0e4f0edf575f72d8d76717ecbefa9c748ab467b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 18 Jul 2017 10:02:33 -1000 Subject: linux-user/sh4: Notice gUSA regions during signal delivery We translate gUSA regions atomically in a parallel context. But in a serial context a gUSA region may be interrupted. In that case, restart the region as the kernel would. Reviewed-by: Aurelien Jarno Signed-off-by: Richard Henderson Message-Id: <20170718200255.31647-9-rth@twiddle.net> Signed-off-by: Aurelien Jarno --- linux-user/signal.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'linux-user') diff --git a/linux-user/signal.c b/linux-user/signal.c index 3d18d1b..c8b0733 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -3471,6 +3471,30 @@ static abi_ulong get_sigframe(struct target_sigaction *ka, return (sp - frame_size) & -8ul; } +/* Notice when we're in the middle of a gUSA region and reset. + Note that this will only occur for !parallel_cpus, as we will + translate such sequences differently in a parallel context. */ +static void unwind_gusa(CPUSH4State *regs) +{ + /* If the stack pointer is sufficiently negative, and we haven't + completed the sequence, then reset to the entry to the region. */ + /* ??? The SH4 kernel checks for and address above 0xC0000000. + However, the page mappings in qemu linux-user aren't as restricted + and we wind up with the normal stack mapped above 0xF0000000. + That said, there is no reason why the kernel should be allowing + a gUSA region that spans 1GB. Use a tighter check here, for what + can actually be enabled by the immediate move. */ + if (regs->gregs[15] >= -128u && regs->pc < regs->gregs[0]) { + /* Reset the PC to before the gUSA region, as computed from + R0 = region end, SP = -(region size), plus one more for the + insn that actually initializes SP to the region size. */ + regs->pc = regs->gregs[0] + regs->gregs[15] - 2; + + /* Reset the SP to the saved version in R1. */ + regs->gregs[15] = regs->gregs[1]; + } +} + static void setup_sigcontext(struct target_sigcontext *sc, CPUSH4State *regs, unsigned long mask) { @@ -3534,6 +3558,8 @@ static void setup_frame(int sig, struct target_sigaction *ka, abi_ulong frame_addr; int i; + unwind_gusa(regs); + frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame)); trace_user_setup_frame(regs, frame_addr); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { @@ -3583,6 +3609,8 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, abi_ulong frame_addr; int i; + unwind_gusa(regs); + frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame)); trace_user_setup_rt_frame(regs, frame_addr); if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { -- cgit v1.1 From b0e9c51a00e0a71b2ab666140ee0ea31b2ed43d2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 18 Jul 2017 10:02:34 -1000 Subject: linux-user/sh4: Clean env->flags on signal boundaries If a signal is delivered during the execution of a delay slot, or a gUSA region, clear those bits from the environment so that the signal handler does not start in that same state. Cleaning the bits on signal return is paranoid good sense. Reviewed-by: Aurelien Jarno Signed-off-by: Richard Henderson Message-Id: <20170718200255.31647-10-rth@twiddle.net> Signed-off-by: Aurelien Jarno --- linux-user/signal.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'linux-user') diff --git a/linux-user/signal.c b/linux-user/signal.c index c8b0733..d68bd26 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -3549,6 +3549,7 @@ static void restore_sigcontext(CPUSH4State *regs, struct target_sigcontext *sc) __get_user(regs->fpul, &sc->sc_fpul); regs->tra = -1; /* disable syscall checks */ + regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK); } static void setup_frame(int sig, struct target_sigaction *ka, @@ -3592,6 +3593,7 @@ static void setup_frame(int sig, struct target_sigaction *ka, regs->gregs[5] = 0; regs->gregs[6] = frame_addr += offsetof(typeof(*frame), sc); regs->pc = (unsigned long) ka->_sa_handler; + regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK); unlock_user_struct(frame, frame_addr, 1); return; @@ -3654,6 +3656,7 @@ static void setup_rt_frame(int sig, struct target_sigaction *ka, regs->gregs[5] = frame_addr + offsetof(typeof(*frame), info); regs->gregs[6] = frame_addr + offsetof(typeof(*frame), uc); regs->pc = (unsigned long) ka->_sa_handler; + regs->flags &= ~(DELAY_SLOT_MASK | GUSA_MASK); unlock_user_struct(frame, frame_addr, 1); return; -- cgit v1.1