aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Filippov <jcmvbkbc@gmail.com>2019-09-06 09:57:13 -0700
committerLaurent Vivier <laurent@vivier.eu>2019-09-11 08:47:06 +0200
commit130ea8322bd01b27095079632f1946d9a2120870 (patch)
tree877a743f8b87d77b757ca3849ade8424cb032d14
parentdc12567a53c88d7a91b9d71db3775782c7f35c84 (diff)
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>
-rw-r--r--linux-user/main.c11
-rw-r--r--linux-user/xtensa/signal.c25
-rw-r--r--target/xtensa/cpu.c24
-rw-r--r--target/xtensa/cpu.h3
4 files changed, 51 insertions, 12 deletions
diff --git a/linux-user/main.c b/linux-user/main.c
index 24cb24f0bf..27d9a87bc8 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -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}
};
diff --git a/linux-user/xtensa/signal.c b/linux-user/xtensa/signal.c
index 8d54ef3ae3..590f0313ff 100644
--- a/linux-user/xtensa/signal.c
+++ b/linux-user/xtensa/signal.c
@@ -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;
diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c
index 76db1741a7..c65dcf9dd7 100644
--- a/target/xtensa/cpu.c
+++ b/target/xtensa/cpu.c
@@ -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;
diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h
index 0459243e6b..b363ffcf10 100644
--- a/target/xtensa/cpu.h
+++ b/target/xtensa/cpu.h
@@ -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)