Commit 130ea832 authored by Max Filippov's avatar Max Filippov Committed by Laurent Vivier
Browse files

target/xtensa: linux-user: add call0 ABI support



Xtensa binaries built for call0 ABI don't rotate register window on
function calls and returns. Invocation of signal handlers from the
kernel is therefore different in windowed and call0 ABIs.
There's currently no way to determine xtensa ELF binary ABI from the
binary itself. Add handler for the -xtensa-abi-call0 command line
parameter/QEMU_XTENSA_ABI_CALL0 envitonment variable to the qemu-user
and record ABI choice. Use it to initialize PS.WOE in xtensa_cpu_reset.
Check PS.WOE in setup_rt_frame to determine how a signal should be
delivered.

Reviewed-by: default avatarLaurent Vivier <laurent@vivier.eu>
Signed-off-by: default avatarMax Filippov <jcmvbkbc@gmail.com>
Message-Id: <20190906165713.5558-1-jcmvbkbc@gmail.com>
Signed-off-by: default avatarLaurent Vivier <laurent@vivier.eu>
parent dc12567a
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -391,6 +391,13 @@ static void handle_arg_trace(const char *arg)
    trace_file = trace_opt_parse(arg);
}

#if defined(TARGET_XTENSA)
static void handle_arg_abi_call0(const char *arg)
{
    xtensa_set_abi_call0();
}
#endif

struct qemu_argument {
    const char *argv;
    const char *env;
@@ -444,6 +451,10 @@ static const struct qemu_argument arg_table[] = {
     "",           "[[enable=]<pattern>][,events=<file>][,file=<file>]"},
    {"version",    "QEMU_VERSION",     false, handle_arg_version,
     "",           "display version information and exit"},
#if defined(TARGET_XTENSA)
    {"xtensa-abi-call0", "QEMU_XTENSA_ABI_CALL0", false, handle_arg_abi_call0,
     "",           "assume CALL0 Xtensa ABI"},
#endif
    {NULL, NULL, false, NULL, NULL, NULL}
};

+17 −8
Original line number Diff line number Diff line
@@ -134,6 +134,8 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
    abi_ulong frame_addr;
    struct target_rt_sigframe *frame;
    uint32_t ra;
    bool abi_call0;
    unsigned base;
    int i;

    frame_addr = get_sigframe(ka, env, sizeof(*frame));
@@ -182,20 +184,27 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
        __put_user(0x00, &frame->retcode[5]);
#endif
    }
    env->sregs[PS] = PS_UM | (3 << PS_RING_SHIFT);
    if (xtensa_option_enabled(env->config, XTENSA_OPTION_WINDOWED_REGISTER)) {
        env->sregs[PS] |= PS_WOE | (1 << PS_CALLINC_SHIFT);
    }
    memset(env->regs, 0, sizeof(env->regs));
    env->pc = ka->_sa_handler;
    env->regs[1] = frame_addr;
    env->sregs[WINDOW_BASE] = 0;
    env->sregs[WINDOW_START] = 1;

    env->regs[4] = (ra & 0x3fffffff) | 0x40000000;
    env->regs[6] = sig;
    env->regs[7] = frame_addr + offsetof(struct target_rt_sigframe, info);
    env->regs[8] = frame_addr + offsetof(struct target_rt_sigframe, uc);
    abi_call0 = (env->sregs[PS] & PS_WOE) == 0;
    env->sregs[PS] = PS_UM | (3 << PS_RING_SHIFT);

    if (abi_call0) {
        base = 0;
        env->regs[base] = ra;
    } else {
        env->sregs[PS] |= PS_WOE | (1 << PS_CALLINC_SHIFT);
        base = 4;
        env->regs[base] = (ra & 0x3fffffff) | 0x40000000;
    }
    env->regs[base + 2] = sig;
    env->regs[base + 3] = frame_addr + offsetof(struct target_rt_sigframe,
                                                info);
    env->regs[base + 4] = frame_addr + offsetof(struct target_rt_sigframe, uc);
    unlock_user_struct(frame, frame_addr, 1);
    return;

+20 −4
Original line number Diff line number Diff line
@@ -53,6 +53,20 @@ static bool xtensa_cpu_has_work(CPUState *cs)
#endif
}

#ifdef CONFIG_USER_ONLY
static bool abi_call0;

void xtensa_set_abi_call0(void)
{
    abi_call0 = true;
}

bool xtensa_abi_call0(void)
{
    return abi_call0;
}
#endif

/* CPUClass::reset() */
static void xtensa_cpu_reset(CPUState *s)
{
@@ -70,10 +84,12 @@ static void xtensa_cpu_reset(CPUState *s)
            XTENSA_OPTION_INTERRUPT) ? 0x1f : 0x10;
    env->pending_irq_level = 0;
#else
    env->sregs[PS] =
        (xtensa_option_enabled(env->config,
                               XTENSA_OPTION_WINDOWED_REGISTER) ? PS_WOE : 0) |
        PS_UM | (3 << PS_RING_SHIFT);
    env->sregs[PS] = PS_UM | (3 << PS_RING_SHIFT);
    if (xtensa_option_enabled(env->config,
                              XTENSA_OPTION_WINDOWED_REGISTER) &&
        !xtensa_abi_call0()) {
        env->sregs[PS] |= PS_WOE;
    }
#endif
    env->sregs[VECBASE] = env->config->vecbase;
    env->sregs[IBREAKENABLE] = 0;
+3 −0
Original line number Diff line number Diff line
@@ -673,6 +673,9 @@ static inline MemoryRegion *xtensa_get_er_region(CPUXtensaState *env)
{
    return env->system_er;
}
#else
void xtensa_set_abi_call0(void);
bool xtensa_abi_call0(void);
#endif

static inline uint32_t xtensa_replicate_windowstart(CPUXtensaState *env)