From: Brian Cain <bcain@quicinc.com>
Reviewed-by: Taylor Simpson <ltaylorsimpson@gmail.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
target/hexagon/cpu.h | 1 +
target/hexagon/cpu_helper.h | 1 +
target/hexagon/cpu_helper.c | 76 +++++++++++++++++++++++++++++++++++++
target/hexagon/op_helper.c | 3 +-
4 files changed, 80 insertions(+), 1 deletion(-)
diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h
index 1491313d06..92b32f434b 100644
--- a/target/hexagon/cpu.h
+++ b/target/hexagon/cpu.h
@@ -51,6 +51,7 @@ typedef struct CPUHexagonTLBContext CPUHexagonTLBContext;
#define PRED_WRITES_MAX 5 /* 4 insns + endloop */
#define VSTORES_MAX 2
#define MAX_TLB_ENTRIES 1024
+#define THREADS_MAX 8
#define CPU_RESOLVING_TYPE TYPE_HEXAGON_CPU
#ifndef CONFIG_USER_ONLY
diff --git a/target/hexagon/cpu_helper.h b/target/hexagon/cpu_helper.h
index f7400031ef..c8b0a37b7f 100644
--- a/target/hexagon/cpu_helper.h
+++ b/target/hexagon/cpu_helper.h
@@ -21,6 +21,7 @@ void hexagon_ssr_set_cause(CPUHexagonState *env, uint32_t cause);
void hexagon_start_threads(CPUHexagonState *env, uint32_t mask);
void hexagon_stop_thread(CPUHexagonState *env);
void hexagon_wait_thread(CPUHexagonState *env, target_ulong PC);
+void hexagon_resume_threads(CPUHexagonState *env, uint32_t mask);
static inline void arch_set_thread_reg(CPUHexagonState *env, uint32_t reg,
uint32_t val)
diff --git a/target/hexagon/cpu_helper.c b/target/hexagon/cpu_helper.c
index f540eadef4..18c929bf5f 100644
--- a/target/hexagon/cpu_helper.c
+++ b/target/hexagon/cpu_helper.c
@@ -146,6 +146,82 @@ void hexagon_set_sys_pcycle_count(CPUHexagonState *env, uint64_t cycles)
}
}
+static void set_wait_mode(CPUHexagonState *env)
+{
+ g_assert(bql_locked());
+
+ const uint32_t modectl = arch_get_system_reg(env, HEX_SREG_MODECTL);
+ uint32_t thread_wait_mask = GET_FIELD(MODECTL_W, modectl);
+ thread_wait_mask |= 0x1 << env->threadId;
+ SET_SYSTEM_FIELD(env, HEX_SREG_MODECTL, MODECTL_W, thread_wait_mask);
+}
+
+void hexagon_wait_thread(CPUHexagonState *env, target_ulong PC)
+{
+ g_assert(bql_locked());
+
+ if (qemu_loglevel_mask(LOG_GUEST_ERROR) &&
+ (env->k0_lock_state != HEX_LOCK_UNLOCKED ||
+ env->tlb_lock_state != HEX_LOCK_UNLOCKED)) {
+ qemu_log("WARNING: executing wait() with acquired lock"
+ "may lead to deadlock\n");
+ }
+ g_assert(get_exe_mode(env) != HEX_EXE_MODE_WAIT);
+
+ CPUState *cs = env_cpu(env);
+ /*
+ * The addtion of cpu_has_work is borrowed from arm's wfi helper
+ * and is critical for our stability
+ */
+ if ((cs->exception_index != HEX_EVENT_NONE) ||
+ (cpu_has_work(cs))) {
+ qemu_log_mask(CPU_LOG_INT,
+ "%s: thread %d skipping WAIT mode, have some work\n",
+ __func__, env->threadId);
+ return;
+ }
+ set_wait_mode(env);
+ env->wait_next_pc = PC + 4;
+
+ cpu_interrupt(cs, CPU_INTERRUPT_HALT);
+}
+
+static void hexagon_resume_thread(CPUHexagonState *env)
+{
+ CPUState *cs = env_cpu(env);
+ clear_wait_mode(env);
+ /*
+ * The wait instruction keeps the PC pointing to itself
+ * so that it has an opportunity to check for interrupts.
+ *
+ * When we come out of wait mode, adjust the PC to the
+ * next executable instruction.
+ */
+ env->gpr[HEX_REG_PC] = env->wait_next_pc;
+ cs = env_cpu(env);
+ ASSERT_DIRECT_TO_GUEST_UNSET(env, cs->exception_index);
+ cs->halted = false;
+ cs->exception_index = HEX_EVENT_NONE;
+ qemu_cpu_kick(cs);
+}
+
+void hexagon_resume_threads(CPUHexagonState *current_env, uint32_t mask)
+{
+ CPUState *cs;
+ CPUHexagonState *env;
+
+ g_assert(bql_locked());
+ CPU_FOREACH(cs) {
+ env = cpu_env(cs);
+ g_assert(env->threadId < THREADS_MAX);
+ if ((mask & (0x1 << env->threadId))) {
+ if (get_exe_mode(env) == HEX_EXE_MODE_WAIT) {
+ hexagon_resume_thread(env);
+ }
+ }
+ }
+}
+
void hexagon_modify_ssr(CPUHexagonState *env, uint32_t new, uint32_t old)
{
g_assert(bql_locked());
diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c
index b887302d3c..63b9dda9a3 100644
--- a/target/hexagon/op_helper.c
+++ b/target/hexagon/op_helper.c
@@ -1483,7 +1483,8 @@ void HELPER(wait)(CPUHexagonState *env, target_ulong PC)
void HELPER(resume)(CPUHexagonState *env, uint32_t mask)
{
- g_assert_not_reached();
+ BQL_LOCK_GUARD();
+ hexagon_resume_threads(env, mask);
}
uint32_t HELPER(getimask)(CPUHexagonState *env, uint32_t tid)
--
2.34.1