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: Laurent Vivier <laurent@vivier.eu>
Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
Message-Id: <20190906165713.5558-1-jcmvbkbc@gmail.com>
Signed-off-by: Laurent Vivier <laurent@vivier.eu>
This commit is contained in:
Max Filippov 2019-09-06 09:57:13 -07:00 committed by Laurent Vivier
parent dc12567a53
commit 130ea8322b
4 changed files with 51 additions and 12 deletions

View file

@ -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;