summaryrefslogtreecommitdiff
path: root/linux-user
diff options
context:
space:
mode:
authorMax Filippov <jcmvbkbc@gmail.com>2018-03-28 00:25:22 -0700
committerMax Filippov <jcmvbkbc@gmail.com>2018-03-31 14:06:35 -0700
commit20ef66706020a874d03092a673a24ae74685cb4d (patch)
tree26eb640b6e0a05280cabcef55e2b03b449287e92 /linux-user
parentdfe732fb68ef9195517f4f380a477d58a054edc1 (diff)
downloadqemu-20ef66706020a874d03092a673a24ae74685cb4d.tar.gz
target/xtensa: fix flush_window_regs
flush_window_regs uses wrong stack frame to save overflow registers in call8 and call12 frames, which results in wrong register values in callers of a function that received a signal. Reimplement flush_window_regs closely following window overflow sequence. Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
Diffstat (limited to 'linux-user')
-rw-r--r--linux-user/signal.c55
1 files changed, 24 insertions, 31 deletions
diff --git a/linux-user/signal.c b/linux-user/signal.c
index 2ea3e0321f..33d5ced30c 100644
--- a/linux-user/signal.c
+++ b/linux-user/signal.c
@@ -7094,52 +7094,45 @@ static abi_ulong get_sigframe(struct target_sigaction *sa,
static int flush_window_regs(CPUXtensaState *env)
{
- const uint32_t nareg_mask = env->config->nareg - 1;
uint32_t wb = env->sregs[WINDOW_BASE];
- uint32_t ws = (xtensa_replicate_windowstart(env) >> (wb + 1)) &
- ((1 << env->config->nareg / 4) - 1);
- uint32_t d = ctz32(ws) + 1;
- uint32_t sp;
- abi_long ret = 0;
-
- wb += d;
- ws >>= d;
+ uint32_t ws = xtensa_replicate_windowstart(env) >> (wb + 1);
+ unsigned d = ctz32(ws) + 1;
+ unsigned i;
+ int ret = 0;
- xtensa_sync_phys_from_window(env);
- sp = env->phys_regs[(wb * 4 + 1) & nareg_mask];
+ for (i = d; i < env->config->nareg / 4; i += d) {
+ uint32_t ssp, osp;
+ unsigned j;
- while (ws && ret == 0) {
- int d;
- int i;
- int idx;
+ ws >>= d;
+ xtensa_rotate_window(env, d);
if (ws & 0x1) {
- ws >>= 1;
+ ssp = env->regs[5];
d = 1;
} else if (ws & 0x2) {
- ws >>= 2;
+ ssp = env->regs[9];
+ ret |= get_user_ual(osp, env->regs[1] - 12);
+ osp -= 32;
d = 2;
- for (i = 0; i < 4; ++i) {
- idx = (wb * 4 + 4 + i) & nareg_mask;
- ret |= put_user_ual(env->phys_regs[idx], sp + (i - 12) * 4);
- }
} else if (ws & 0x4) {
- ws >>= 3;
+ ssp = env->regs[13];
+ ret |= get_user_ual(osp, env->regs[1] - 12);
+ osp -= 48;
d = 3;
- for (i = 0; i < 8; ++i) {
- idx = (wb * 4 + 4 + i) & nareg_mask;
- ret |= put_user_ual(env->phys_regs[idx], sp + (i - 16) * 4);
- }
} else {
g_assert_not_reached();
}
- sp = env->phys_regs[((wb + d) * 4 + 1) & nareg_mask];
- for (i = 0; i < 4; ++i) {
- idx = (wb * 4 + i) & nareg_mask;
- ret |= put_user_ual(env->phys_regs[idx], sp + (i - 4) * 4);
+
+ for (j = 0; j < 4; ++j) {
+ ret |= put_user_ual(env->regs[j], ssp - 16 + j * 4);
+ }
+ for (j = 4; j < d * 4; ++j) {
+ ret |= put_user_ual(env->regs[j], osp - 16 + j * 4);
}
- wb += d;
}
+ xtensa_rotate_window(env, d);
+ g_assert(env->sregs[WINDOW_BASE] == wb);
return ret == 0;
}