[PATCH v6 29/37] target/hexagon: Add sreg_{read,write} helpers

Brian Cain posted 37 patches 5 hours ago
Maintainers: Brian Cain <brian.cain@oss.qualcomm.com>, Pierrick Bouvier <pierrick.bouvier@linaro.org>, Laurent Vivier <laurent@vivier.eu>, Alessandro Di Federico <ale@rev.ng>, Anton Johansson <anjo@rev.ng>
[PATCH v6 29/37] target/hexagon: Add sreg_{read,write} helpers
Posted by Brian Cain 5 hours ago
From: Brian Cain <bcain@quicinc.com>

Co-authored-by: Sid Manning <sidneym@quicinc.com>
Reviewed-by: Taylor Simpson <ltaylorsimpson@gmail.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
---
 target/hexagon/cpu.c        |   1 -
 target/hexagon/cpu_helper.c | 354 ++++++++++++++++++++++++++++++++++++
 target/hexagon/op_helper.c  |  33 +++-
 3 files changed, 384 insertions(+), 4 deletions(-)
 create mode 100644 target/hexagon/cpu_helper.c

diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
index c79b19d059e..79ee4264c70 100644
--- a/target/hexagon/cpu.c
+++ b/target/hexagon/cpu.c
@@ -339,7 +339,6 @@ static void hexagon_cpu_realize(DeviceState *dev, Error **errp)
 
     qemu_init_vcpu(cs);
     cpu_reset(cs);
-
     mcc->parent_realize(dev, errp);
 }
 
diff --git a/target/hexagon/cpu_helper.c b/target/hexagon/cpu_helper.c
new file mode 100644
index 00000000000..a2b486f4bb5
--- /dev/null
+++ b/target/hexagon/cpu_helper.c
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "cpu.h"
+#include "cpu_helper.h"
+#include "system/cpus.h"
+#include "hw/core/boards.h"
+#include "hw/hexagon/hexagon.h"
+#include "hw/hexagon/hexagon_globalreg.h"
+#include "hex_interrupts.h"
+#include "hex_mmu.h"
+#include "system/runstate.h"
+#include "exec/cpu-interrupt.h"
+#include "exec/target_page.h"
+#include "accel/tcg/cpu-ldst.h"
+#include "exec/cputlb.h"
+#include "qemu/log.h"
+#include "tcg/tcg-op.h"
+#include "internal.h"
+#include "macros.h"
+#include "sys_macros.h"
+#include "arch.h"
+
+
+uint32_t hexagon_get_pmu_counter(CPUHexagonState *cur_env, int index)
+{
+    g_assert_not_reached();
+}
+
+uint64_t hexagon_get_sys_pcycle_count(CPUHexagonState *env)
+{
+    g_assert_not_reached();
+}
+
+uint32_t hexagon_get_sys_pcycle_count_high(CPUHexagonState *env)
+{
+    g_assert_not_reached();
+}
+
+uint32_t hexagon_get_sys_pcycle_count_low(CPUHexagonState *env)
+{
+    g_assert_not_reached();
+}
+
+void hexagon_set_sys_pcycle_count_high(CPUHexagonState *env, uint32_t val)
+{
+    g_assert_not_reached();
+}
+
+void hexagon_set_sys_pcycle_count_low(CPUHexagonState *env, uint32_t val)
+{
+    g_assert_not_reached();
+}
+
+void hexagon_set_sys_pcycle_count(CPUHexagonState *env, uint64_t val)
+{
+    g_assert_not_reached();
+}
+
+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)
+{
+    bool old_EX, old_UM, old_GM, old_IE;
+    bool new_EX, new_UM, new_GM, new_IE;
+    uint8_t old_asid, new_asid;
+
+    g_assert(bql_locked());
+
+    old_EX = GET_SSR_FIELD(SSR_EX, old);
+    old_UM = GET_SSR_FIELD(SSR_UM, old);
+    old_GM = GET_SSR_FIELD(SSR_GM, old);
+    old_IE = GET_SSR_FIELD(SSR_IE, old);
+    new_EX = GET_SSR_FIELD(SSR_EX, new);
+    new_UM = GET_SSR_FIELD(SSR_UM, new);
+    new_GM = GET_SSR_FIELD(SSR_GM, new);
+    new_IE = GET_SSR_FIELD(SSR_IE, new);
+
+    if ((old_EX != new_EX) ||
+        (old_UM != new_UM) ||
+        (old_GM != new_GM)) {
+        hex_mmu_mode_change(env);
+    }
+
+    old_asid = GET_SSR_FIELD(SSR_ASID, old);
+    new_asid = GET_SSR_FIELD(SSR_ASID, new);
+    if (new_asid != old_asid) {
+        CPUState *cs = env_cpu(env);
+        tlb_flush(cs);
+    }
+
+    /* See if the interrupts have been enabled or we have exited EX mode */
+    if ((new_IE && !old_IE) ||
+        (!new_EX && old_EX)) {
+        hex_interrupt_update(env);
+    }
+}
+
+void clear_wait_mode(CPUHexagonState *env)
+{
+    HexagonCPU *cpu;
+    uint32_t modectl, thread_wait_mask;
+
+    g_assert(bql_locked());
+
+    cpu = env_archcpu(env);
+    if (cpu->globalregs) {
+        modectl =
+            hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL,
+                                   env->threadId);
+        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_ssr_set_cause(CPUHexagonState *env, uint32_t cause)
+{
+    uint32_t old, new;
+
+    g_assert(bql_locked());
+
+    old = env->t_sreg[HEX_SREG_SSR];
+    SET_SYSTEM_FIELD(env, HEX_SREG_SSR, SSR_EX, 1);
+    SET_SYSTEM_FIELD(env, HEX_SREG_SSR, SSR_CAUSE, cause);
+    new = env->t_sreg[HEX_SREG_SSR];
+
+    hexagon_modify_ssr(env, new, old);
+}
+
+
+int get_exe_mode(CPUHexagonState *env)
+{
+    HexagonCPU *cpu;
+    uint32_t modectl, thread_enabled_mask, thread_wait_mask;
+    uint32_t isdbst, debugmode;
+    bool E_bit, W_bit, D_bit;
+
+    g_assert(bql_locked());
+
+    cpu = env_archcpu(env);
+    modectl = cpu->globalregs ?
+        hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL,
+                               env->threadId) : 0;
+    thread_enabled_mask = GET_FIELD(MODECTL_E, modectl);
+    E_bit = thread_enabled_mask & (0x1 << env->threadId);
+    thread_wait_mask = GET_FIELD(MODECTL_W, modectl);
+    W_bit = thread_wait_mask & (0x1 << env->threadId);
+    isdbst = cpu->globalregs ?
+        hexagon_globalreg_read(cpu->globalregs, HEX_SREG_ISDBST,
+                               env->threadId) : 0;
+    debugmode = GET_FIELD(ISDBST_DEBUGMODE, isdbst);
+    D_bit = debugmode & (0x1 << env->threadId);
+
+    if (!D_bit && !W_bit && !E_bit) {
+        return HEX_EXE_MODE_OFF;
+    }
+    if (!D_bit && !W_bit && E_bit) {
+        return HEX_EXE_MODE_RUN;
+    }
+    if (!D_bit && W_bit && E_bit) {
+        return HEX_EXE_MODE_WAIT;
+    }
+    if (D_bit && !W_bit && E_bit) {
+        return HEX_EXE_MODE_DEBUG;
+    }
+    g_assert_not_reached();
+}
+
+static uint32_t set_enable_mask(CPUHexagonState *env)
+{
+    HexagonCPU *cpu;
+    uint32_t modectl, thread_enabled_mask;
+
+    g_assert(bql_locked());
+
+    cpu = env_archcpu(env);
+    if (!cpu->globalregs) {
+        return 0;
+    }
+    modectl =
+        hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL,
+                               env->threadId);
+    thread_enabled_mask = GET_FIELD(MODECTL_E, modectl);
+    thread_enabled_mask |= 0x1 << env->threadId;
+    SET_SYSTEM_FIELD(env, HEX_SREG_MODECTL, MODECTL_E, thread_enabled_mask);
+    return thread_enabled_mask;
+}
+
+static uint32_t clear_enable_mask(CPUHexagonState *env)
+{
+    HexagonCPU *cpu;
+    uint32_t modectl, thread_enabled_mask;
+
+    g_assert(bql_locked());
+
+    cpu = env_archcpu(env);
+    if (!cpu->globalregs) {
+        return 0;
+    }
+    modectl =
+        hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL,
+                               env->threadId);
+    thread_enabled_mask = GET_FIELD(MODECTL_E, modectl);
+    thread_enabled_mask &= ~(0x1 << env->threadId);
+    SET_SYSTEM_FIELD(env, HEX_SREG_MODECTL, MODECTL_E, thread_enabled_mask);
+    return thread_enabled_mask;
+}
+static void do_start_thread(CPUState *cs, run_on_cpu_data tbd)
+{
+    CPUHexagonState *env;
+
+    BQL_LOCK_GUARD();
+
+    env = cpu_env(cs);
+
+    hexagon_cpu_soft_reset(env);
+
+    set_enable_mask(env);
+
+    cs->halted = 0;
+    cs->exception_index = HEX_EVENT_NONE;
+    cpu_resume(cs);
+}
+
+void hexagon_start_threads(CPUHexagonState *current_env, uint32_t mask)
+{
+    CPUState *cs;
+    CPU_FOREACH(cs) {
+        CPUHexagonState *env = cpu_env(cs);
+        if (!(mask & (0x1 << env->threadId))) {
+            continue;
+        }
+
+        if (current_env->threadId != env->threadId) {
+            async_safe_run_on_cpu(cs, do_start_thread, RUN_ON_CPU_NULL);
+        }
+    }
+}
+
+/*
+ * When we have all threads stopped, the return
+ * value to the shell is register 2 from thread 0.
+ */
+static uint32_t get_thread0_r2(void)
+{
+    CPUState *cs;
+    CPU_FOREACH(cs) {
+        CPUHexagonState *thread = cpu_env(cs);
+        if (thread->threadId == 0) {
+            return thread->gpr[2];
+        }
+    }
+    g_assert_not_reached();
+}
+
+void hexagon_stop_thread(CPUHexagonState *env)
+{
+    uint32_t thread_enabled_mask;
+    CPUState *cs;
+
+    BQL_LOCK_GUARD();
+
+    thread_enabled_mask = clear_enable_mask(env);
+    cs = env_cpu(env);
+    cpu_interrupt(cs, CPU_INTERRUPT_HALT);
+    if (!thread_enabled_mask) {
+        /* All threads are stopped, request shutdown */
+        qemu_system_shutdown_request_with_code(
+            SHUTDOWN_CAUSE_GUEST_SHUTDOWN, get_thread0_r2());
+    }
+}
+
+static int sys_in_monitor_mode_ssr(uint32_t ssr)
+{
+    if ((GET_SSR_FIELD(SSR_EX, ssr) != 0) ||
+        ((GET_SSR_FIELD(SSR_EX, ssr) == 0) &&
+         (GET_SSR_FIELD(SSR_UM, ssr) == 0))) {
+        return 1;
+    }
+    return 0;
+}
+
+static int sys_in_guest_mode_ssr(uint32_t ssr)
+{
+    if ((GET_SSR_FIELD(SSR_EX, ssr) == 0) &&
+        (GET_SSR_FIELD(SSR_UM, ssr) != 0) &&
+        (GET_SSR_FIELD(SSR_GM, ssr) != 0)) {
+        return 1;
+    }
+    return 0;
+}
+
+static int sys_in_user_mode_ssr(uint32_t ssr)
+{
+    if ((GET_SSR_FIELD(SSR_EX, ssr) == 0) &&
+        (GET_SSR_FIELD(SSR_UM, ssr) != 0) &&
+        (GET_SSR_FIELD(SSR_GM, ssr) == 0)) {
+        return 1;
+    }
+    return 0;
+}
+
+int get_cpu_mode(CPUHexagonState *env)
+{
+    uint32_t ssr = env->t_sreg[HEX_SREG_SSR];
+
+    if (sys_in_monitor_mode_ssr(ssr)) {
+        return HEX_CPU_MODE_MONITOR;
+    } else if (sys_in_guest_mode_ssr(ssr)) {
+        return HEX_CPU_MODE_GUEST;
+    } else if (sys_in_user_mode_ssr(ssr)) {
+        return HEX_CPU_MODE_USER;
+    }
+    return HEX_CPU_MODE_MONITOR;
+}
diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c
index 87da1fe430c..947988d2456 100644
--- a/target/hexagon/op_helper.c
+++ b/target/hexagon/op_helper.c
@@ -19,6 +19,7 @@
 #include "qemu/log.h"
 #include "accel/tcg/cpu-ldst.h"
 #include "accel/tcg/probe.h"
+#include "qemu/main-loop.h"
 #include "cpu.h"
 #include "exec/helper-proto.h"
 #include "fpu/softfloat.h"
@@ -1451,17 +1452,43 @@ void HELPER(setimask)(CPUHexagonState *env, uint32_t tid, uint32_t imask)
 
 void HELPER(sreg_write_masked)(CPUHexagonState *env, uint32_t reg, uint32_t val)
 {
-    g_assert_not_reached();
+    BQL_LOCK_GUARD();
+    if (reg < HEX_SREG_GLB_START) {
+        env->t_sreg[reg] = val;
+    } else {
+        HexagonCPU *cpu = env_archcpu(env);
+        if (cpu->globalregs) {
+            hexagon_globalreg_write_masked(cpu->globalregs, reg, val);
+        }
+    }
+}
+
+static inline QEMU_ALWAYS_INLINE uint32_t sreg_read(CPUHexagonState *env,
+                                                    uint32_t reg)
+{
+    HexagonCPU *cpu;
+
+    g_assert(bql_locked());
+    if (reg < HEX_SREG_GLB_START) {
+        return env->t_sreg[reg];
+    }
+    cpu = env_archcpu(env);
+    return cpu->globalregs ?
+        hexagon_globalreg_read(cpu->globalregs, reg, env->threadId) : 0;
 }
 
 uint32_t HELPER(sreg_read)(CPUHexagonState *env, uint32_t reg)
 {
-    g_assert_not_reached();
+    BQL_LOCK_GUARD();
+    return sreg_read(env, reg);
 }
 
 uint64_t HELPER(sreg_read_pair)(CPUHexagonState *env, uint32_t reg)
 {
-    g_assert_not_reached();
+    BQL_LOCK_GUARD();
+
+    return deposit64((uint64_t) sreg_read(env, reg), 32, 32,
+        sreg_read(env, reg + 1));
 }
 
 uint32_t HELPER(greg_read)(CPUHexagonState *env, uint32_t reg)
-- 
2.34.1