Commit 20ef6670 authored by Max Filippov's avatar Max Filippov
Browse files

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: default avatarMax Filippov <jcmvbkbc@gmail.com>
parent dfe732fb
Loading
Loading
Loading
Loading
+24 −31
Original line number Diff line number Diff line
@@ -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;
}