Add hex_mmu.[ch], cpu mode helpers, and additional includes/stubs
that integrate the TLB device with the CPU model.

Reviewed-by: Taylor Simpson <[email protected]>
Signed-off-by: Brian Cain <[email protected]>
---
 target/hexagon/cpu-param.h  |   4 +
 target/hexagon/cpu.h        |  17 +++
 target/hexagon/cpu_helper.h |   2 +
 target/hexagon/hex_mmu.h    |  25 ++++
 target/hexagon/internal.h   |   9 ++
 target/hexagon/sys_macros.h |   3 +
 target/hexagon/cpu.c        |  25 ++++
 target/hexagon/cpu_helper.c |  10 ++
 target/hexagon/hex_mmu.c    | 277 ++++++++++++++++++++++++++++++++++++
 target/hexagon/translate.c  |   2 +-
 10 files changed, 373 insertions(+), 1 deletion(-)
 create mode 100644 target/hexagon/hex_mmu.h
 create mode 100644 target/hexagon/hex_mmu.c

diff --git a/target/hexagon/cpu-param.h b/target/hexagon/cpu-param.h
index 1f0f22a7968..9eae7d2361c 100644
--- a/target/hexagon/cpu-param.h
+++ b/target/hexagon/cpu-param.h
@@ -18,7 +18,11 @@
 #ifndef HEXAGON_CPU_PARAM_H
 #define HEXAGON_CPU_PARAM_H
 
+#ifdef CONFIG_USER_ONLY
 #define TARGET_PAGE_BITS 16     /* 64K pages */
+#else
+#define TARGET_PAGE_BITS 12     /* 4K pages */
+#endif
 
 #define TARGET_VIRT_ADDR_SPACE_BITS 32
 
diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h
index e39e6e39fec..d8092cb6fe7 100644
--- a/target/hexagon/cpu.h
+++ b/target/hexagon/cpu.h
@@ -38,6 +38,9 @@
 #error "Hexagon does not support system emulation"
 #endif
 
+#ifndef CONFIG_USER_ONLY
+#endif
+
 #define NUM_PREGS 4
 #define TOTAL_PER_THREAD_REGS 64
 
@@ -51,6 +54,8 @@
 #define CPU_RESOLVING_TYPE TYPE_HEXAGON_CPU
 #ifndef CONFIG_USER_ONLY
 #define CPU_INTERRUPT_SWI      CPU_INTERRUPT_TGT_INT_0
+#define CPU_INTERRUPT_K0_UNLOCK CPU_INTERRUPT_TGT_INT_1
+#define CPU_INTERRUPT_TLB_UNLOCK CPU_INTERRUPT_TGT_INT_2
 
 #define HEX_CPU_MODE_USER    1
 #define HEX_CPU_MODE_GUEST   2
@@ -67,6 +72,12 @@
 #define MMU_GUEST_IDX        1
 #define MMU_KERNEL_IDX       2
 
+typedef enum {
+    HEX_LOCK_UNLOCKED       = 0,
+    HEX_LOCK_WAITING        = 1,
+    HEX_LOCK_OWNER          = 2,
+    HEX_LOCK_QUEUED        = 3
+} hex_lock_state_t;
 #endif
 
 
@@ -128,6 +139,10 @@ typedef struct CPUArchState {
 
     /* This alias of CPUState.cpu_index is used by imported sources: */
     uint32_t threadId;
+    hex_lock_state_t tlb_lock_state;
+    hex_lock_state_t k0_lock_state;
+    uint32_t tlb_lock_count;
+    uint32_t k0_lock_count;
 #endif
     uint32_t next_PC;
     target_ulong new_value_usr;
@@ -177,12 +192,14 @@ struct ArchCPU {
     bool short_circuit;
 #ifndef CONFIG_USER_ONLY
     struct HexagonTLBState *tlb;
+    uint32_t htid;
 #endif
 };
 
 #include "cpu_bits.h"
 
 FIELD(TB_FLAGS, IS_TIGHT_LOOP, 0, 1)
+FIELD(TB_FLAGS, MMU_INDEX, 1, 3)
 
 G_NORETURN void hexagon_raise_exception_err(CPUHexagonState *env,
                                             uint32_t exception,
diff --git a/target/hexagon/cpu_helper.h b/target/hexagon/cpu_helper.h
index 1486a03c64a..18300657f3d 100644
--- a/target/hexagon/cpu_helper.h
+++ b/target/hexagon/cpu_helper.h
@@ -14,5 +14,7 @@ uint32_t hexagon_get_sys_pcycle_count_high(CPUHexagonState 
*env);
 void hexagon_set_sys_pcycle_count(CPUHexagonState *env, uint64_t);
 void hexagon_set_sys_pcycle_count_low(CPUHexagonState *env, uint32_t);
 void hexagon_set_sys_pcycle_count_high(CPUHexagonState *env, uint32_t);
+void hexagon_modify_ssr(CPUHexagonState *env, uint32_t new, uint32_t old);
+int get_exe_mode(CPUHexagonState *env);
 
 #endif
diff --git a/target/hexagon/hex_mmu.h b/target/hexagon/hex_mmu.h
new file mode 100644
index 00000000000..99ddb3736a2
--- /dev/null
+++ b/target/hexagon/hex_mmu.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HEXAGON_MMU_H
+#define HEXAGON_MMU_H
+
+#include "cpu.h"
+
+extern void hex_tlbw(CPUHexagonState *env, uint32_t index, uint64_t value);
+extern uint32_t hex_tlb_lookup(CPUHexagonState *env, uint32_t ssr, uint32_t 
VA);
+extern void hex_mmu_on(CPUHexagonState *env);
+extern void hex_mmu_off(CPUHexagonState *env);
+extern void hex_mmu_mode_change(CPUHexagonState *env);
+extern bool hex_tlb_find_match(CPUHexagonState *env, uint32_t VA,
+                               MMUAccessType access_type, hwaddr *PA, int 
*prot,
+                               uint64_t *size, int32_t *excp, int mmu_idx);
+extern int hex_tlb_check_overlap(CPUHexagonState *env, uint64_t entry,
+                                 uint64_t index);
+extern void hex_tlb_lock(CPUHexagonState *env);
+extern void hex_tlb_unlock(CPUHexagonState *env);
+void dump_mmu(CPUHexagonState *env);
+#endif
diff --git a/target/hexagon/internal.h b/target/hexagon/internal.h
index 33d73ed18d1..4338914efb5 100644
--- a/target/hexagon/internal.h
+++ b/target/hexagon/internal.h
@@ -36,6 +36,15 @@ void G_NORETURN do_raise_exception(CPUHexagonState *env,
         uint32_t PC,
         uintptr_t retaddr);
 
+#define hexagon_cpu_mmu_enabled(env) ({ \
+    HexagonCPU *cpu = env_archcpu(env); \
+    cpu->globalregs ? \
+        GET_SYSCFG_FIELD(SYSCFG_MMUEN, \
+            hexagon_globalreg_read(cpu->globalregs, \
+                                   HEX_SREG_SYSCFG, (env)->threadId)) : \
+        0; \
+})
+
 #ifndef CONFIG_USER_ONLY
 extern const VMStateDescription vmstate_hexagon_cpu;
 #endif
diff --git a/target/hexagon/sys_macros.h b/target/hexagon/sys_macros.h
index f497d55bb81..364fcde7383 100644
--- a/target/hexagon/sys_macros.h
+++ b/target/hexagon/sys_macros.h
@@ -139,6 +139,9 @@
 #define fDCINVIDX(REG)
 #define fDCINVA(REG) do { REG = REG; } while (0) /* Nothing to do in qemu */
 
+#define fSET_TLB_LOCK()       hex_tlb_lock(env);
+#define fCLEAR_TLB_LOCK()     hex_tlb_unlock(env);
+
 #define fTLB_IDXMASK(INDEX) \
     ((INDEX) & (fPOW2_ROUNDUP( \
         fCAST4u(hexagon_tlb_get_num_entries(env_archcpu(env)->tlb))) - 1))
diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c
index 32d158684a0..5c937fa1cd1 100644
--- a/target/hexagon/cpu.c
+++ b/target/hexagon/cpu.c
@@ -27,6 +27,13 @@
 #include "tcg/tcg.h"
 #include "exec/gdbstub.h"
 #include "accel/tcg/cpu-ops.h"
+#include "cpu_helper.h"
+#include "hex_mmu.h"
+
+#ifndef CONFIG_USER_ONLY
+#include "sys_macros.h"
+#include "accel/tcg/cpu-ldst.h"
+#endif
 
 static void hexagon_v66_cpu_init(Object *obj) { }
 static void hexagon_v67_cpu_init(Object *obj) { }
@@ -54,6 +61,7 @@ static const Property hexagon_cpu_properties[] = {
 #if !defined(CONFIG_USER_ONLY)
     DEFINE_PROP_LINK("tlb", HexagonCPU, tlb, TYPE_HEXAGON_TLB,
                      HexagonTLBState *),
+    DEFINE_PROP_UINT32("htid", HexagonCPU, htid, 0),
 #endif
     DEFINE_PROP_BOOL("lldb-compat", HexagonCPU, lldb_compat, false),
     DEFINE_PROP_UNSIGNED("lldb-stack-adjust", HexagonCPU, lldb_stack_adjust, 0,
@@ -280,6 +288,13 @@ static TCGTBCPUState hexagon_get_tb_cpu_state(CPUState *cs)
         hexagon_raise_exception_err(env, HEX_CAUSE_PC_NOT_ALIGNED, 0);
     }
 
+#ifndef CONFIG_USER_ONLY
+    hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, MMU_INDEX,
+                           cpu_mmu_index(env_cpu(env), false));
+#else
+    hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, MMU_INDEX, MMU_USER_IDX);
+#endif
+
     return (TCGTBCPUState){ .pc = pc, .flags = hex_flags };
 }
 
@@ -297,6 +312,7 @@ static void hexagon_restore_state_to_opc(CPUState *cs,
     cpu_env(cs)->gpr[HEX_REG_PC] = data[0];
 }
 
+
 static void hexagon_cpu_reset_hold(Object *obj, ResetType type)
 {
     CPUState *cs = CPU(obj);
@@ -315,7 +331,15 @@ static void hexagon_cpu_reset_hold(Object *obj, ResetType 
type)
     memset(env->t_sreg, 0, sizeof(uint32_t) * NUM_SREGS);
     memset(env->greg, 0, sizeof(uint32_t) * NUM_GREGS);
     env->wait_next_pc = 0;
+    env->tlb_lock_state = HEX_LOCK_UNLOCKED;
+    env->k0_lock_state = HEX_LOCK_UNLOCKED;
+    env->tlb_lock_count = 0;
+    env->k0_lock_count = 0;
     env->next_PC = 0;
+
+    HexagonCPU *cpu = HEXAGON_CPU(cs);
+    env->t_sreg[HEX_SREG_HTID] = cpu->htid;
+    env->threadId = cpu->htid;
 #endif
     env->cause_code = HEX_EVENT_NONE;
 }
@@ -344,6 +368,7 @@ static void hexagon_cpu_realize(DeviceState *dev, Error 
**errp)
                              gdb_find_static_feature("hexagon-hvx.xml"), 0);
 
     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
index b8e0625e3f8..b6a8bd35309 100644
--- a/target/hexagon/cpu_helper.c
+++ b/target/hexagon/cpu_helper.c
@@ -58,3 +58,13 @@ void hexagon_set_sys_pcycle_count(CPUHexagonState *env, 
uint64_t cycles)
 {
     g_assert_not_reached();
 }
+
+void hexagon_modify_ssr(CPUHexagonState *env, uint32_t new, uint32_t old)
+{
+    g_assert_not_reached();
+}
+
+int get_exe_mode(CPUHexagonState *env)
+{
+    g_assert_not_reached();
+}
diff --git a/target/hexagon/hex_mmu.c b/target/hexagon/hex_mmu.c
new file mode 100644
index 00000000000..1cdc92fdc31
--- /dev/null
+++ b/target/hexagon/hex_mmu.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/main-loop.h"
+#include "qemu/qemu-print.h"
+#include "cpu.h"
+#include "system/cpus.h"
+#include "internal.h"
+#include "exec/cpu-interrupt.h"
+#include "cpu_helper.h"
+#include "exec/cputlb.h"
+#include "hex_mmu.h"
+#include "macros.h"
+#include "sys_macros.h"
+#include "hw/hexagon/hexagon_tlb.h"
+#include "hw/hexagon/hexagon_globalreg.h"
+
+static inline void hex_log_tlbw(uint32_t index, uint64_t entry)
+{
+    if (qemu_loglevel_mask(CPU_LOG_MMU)) {
+        if (qemu_log_enabled()) {
+            FILE *logfile = qemu_log_trylock();
+            if (logfile) {
+                fprintf(logfile, "tlbw[%03d]: ", index);
+                if (!hexagon_tlb_dump_entry(logfile, entry)) {
+                    fprintf(logfile, "invalid\n");
+                }
+                qemu_log_unlock(logfile);
+            }
+        }
+    }
+}
+
+void hex_tlbw(CPUHexagonState *env, uint32_t index, uint64_t value)
+{
+    uint32_t myidx = fTLB_NONPOW2WRAP(fTLB_IDXMASK(index));
+    HexagonTLBState *tlb = env_archcpu(env)->tlb;
+    uint64_t old_entry = hexagon_tlb_read(tlb, myidx);
+
+    bool old_entry_valid = extract64(old_entry, 63, 1);
+    if (old_entry_valid && hexagon_cpu_mmu_enabled(env)) {
+        CPUState *cs = env_cpu(env);
+        tlb_flush(cs);
+    }
+    hexagon_tlb_write(tlb, myidx, value);
+    hex_log_tlbw(myidx, value);
+}
+
+void hex_mmu_on(CPUHexagonState *env)
+{
+    CPUState *cs = env_cpu(env);
+    qemu_log_mask(CPU_LOG_MMU, "Hexagon MMU turned on!\n");
+    tlb_flush(cs);
+}
+
+void hex_mmu_off(CPUHexagonState *env)
+{
+    CPUState *cs = env_cpu(env);
+    qemu_log_mask(CPU_LOG_MMU, "Hexagon MMU turned off!\n");
+    tlb_flush(cs);
+}
+
+void hex_mmu_mode_change(CPUHexagonState *env)
+{
+    qemu_log_mask(CPU_LOG_MMU, "Hexagon mode change!\n");
+    CPUState *cs = env_cpu(env);
+    tlb_flush(cs);
+}
+
+bool hex_tlb_find_match(CPUHexagonState *env, uint32_t VA,
+                        MMUAccessType access_type, hwaddr *PA, int *prot,
+                        uint64_t *size, int32_t *excp, int mmu_idx)
+{
+    HexagonCPU *cpu = env_archcpu(env);
+    uint32_t ssr = env->t_sreg[HEX_SREG_SSR];
+    uint8_t asid = GET_SSR_FIELD(SSR_ASID, ssr);
+    int cause_code = 0;
+
+    bool found = hexagon_tlb_find_match(cpu->tlb, asid, VA, access_type,
+                                        PA, prot, size, excp, &cause_code,
+                                        mmu_idx);
+    if (cause_code) {
+        env->cause_code = cause_code;
+    }
+    return found;
+}
+
+/* Called from tlbp instruction */
+uint32_t hex_tlb_lookup(CPUHexagonState *env, uint32_t ssr, uint32_t VA)
+{
+    HexagonCPU *cpu = env_archcpu(env);
+    uint8_t asid = GET_SSR_FIELD(SSR_ASID, ssr);
+    int cause_code = 0;
+
+    uint32_t result = hexagon_tlb_lookup(cpu->tlb, asid, VA, &cause_code);
+    if (cause_code) {
+        env->cause_code = cause_code;
+    }
+    return result;
+}
+
+/*
+ * Return codes:
+ * 0 or positive             index of match
+ * -1                        multiple matches
+ * -2                        no match
+ */
+int hex_tlb_check_overlap(CPUHexagonState *env, uint64_t entry, uint64_t index)
+{
+    HexagonCPU *cpu = env_archcpu(env);
+    return hexagon_tlb_check_overlap(cpu->tlb, entry, index);
+}
+
+void dump_mmu(CPUHexagonState *env)
+{
+    HexagonCPU *cpu = env_archcpu(env);
+    hexagon_tlb_dump(cpu->tlb);
+}
+
+static inline void print_thread(const char *str, CPUState *cs)
+{
+    g_assert(bql_locked());
+    CPUHexagonState *thread = cpu_env(cs);
+    bool is_stopped = cpu_is_stopped(cs);
+    int exe_mode = get_exe_mode(thread);
+    hex_lock_state_t lock_state = thread->tlb_lock_state;
+    qemu_log_mask(CPU_LOG_MMU,
+           "%s: threadId = %d: %s, exe_mode = %s, tlb_lock_state = %s\n",
+           str,
+           thread->threadId,
+           is_stopped ? "stopped" : "running",
+           exe_mode == HEX_EXE_MODE_OFF ? "off" :
+           exe_mode == HEX_EXE_MODE_RUN ? "run" :
+           exe_mode == HEX_EXE_MODE_WAIT ? "wait" :
+           exe_mode == HEX_EXE_MODE_DEBUG ? "debug" :
+           "unknown",
+           lock_state == HEX_LOCK_UNLOCKED ? "unlocked" :
+           lock_state == HEX_LOCK_WAITING ? "waiting" :
+           lock_state == HEX_LOCK_OWNER ? "owner" :
+           "unknown");
+}
+
+static inline void print_thread_states(const char *str)
+{
+    CPUState *cs;
+    CPU_FOREACH(cs) {
+        print_thread(str, cs);
+    }
+}
+
+void hex_tlb_lock(CPUHexagonState *env)
+{
+    qemu_log_mask(CPU_LOG_MMU, "hex_tlb_lock: " TARGET_FMT_ld "\n",
+                  env->threadId);
+    BQL_LOCK_GUARD();
+    g_assert((env->tlb_lock_count == 0) || (env->tlb_lock_count == 1));
+
+    HexagonCPU *cpu = env_archcpu(env);
+    uint32_t syscfg = cpu->globalregs ?
+        hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SYSCFG,
+                               env->threadId) : 0;
+    uint8_t tlb_lock = GET_SYSCFG_FIELD(SYSCFG_TLBLOCK, syscfg);
+    if (tlb_lock) {
+        if (env->tlb_lock_state == HEX_LOCK_QUEUED) {
+            env->next_PC += 4;
+            env->tlb_lock_count++;
+            env->tlb_lock_state = HEX_LOCK_OWNER;
+            SET_SYSCFG_FIELD(env, SYSCFG_TLBLOCK, 1);
+            return;
+        }
+        if (env->tlb_lock_state == HEX_LOCK_OWNER) {
+            qemu_log_mask(CPU_LOG_MMU | LOG_GUEST_ERROR,
+                          "Double tlblock at PC: 0x%x, thread may hang\n",
+                          env->next_PC);
+            env->next_PC += 4;
+            CPUState *cs = env_cpu(env);
+            cpu_interrupt(cs, CPU_INTERRUPT_HALT);
+            return;
+        }
+        env->tlb_lock_state = HEX_LOCK_WAITING;
+        CPUState *cs = env_cpu(env);
+        cpu_interrupt(cs, CPU_INTERRUPT_HALT);
+    } else {
+        env->next_PC += 4;
+        env->tlb_lock_count++;
+        env->tlb_lock_state = HEX_LOCK_OWNER;
+        SET_SYSCFG_FIELD(env, SYSCFG_TLBLOCK, 1);
+    }
+
+    if (qemu_loglevel_mask(CPU_LOG_MMU)) {
+        qemu_log_mask(CPU_LOG_MMU, "Threads after hex_tlb_lock:\n");
+        print_thread_states("\tThread");
+    }
+}
+
+void hex_tlb_unlock(CPUHexagonState *env)
+{
+    BQL_LOCK_GUARD();
+    g_assert((env->tlb_lock_count == 0) || (env->tlb_lock_count == 1));
+
+    /* Nothing to do if the TLB isn't locked by this thread */
+    HexagonCPU *cpu = env_archcpu(env);
+    uint32_t syscfg = cpu->globalregs ?
+        hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SYSCFG,
+                               env->threadId) : 0;
+    uint8_t tlb_lock = GET_SYSCFG_FIELD(SYSCFG_TLBLOCK, syscfg);
+    if ((tlb_lock == 0) ||
+        (env->tlb_lock_state != HEX_LOCK_OWNER)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "thread %d attempted to tlbunlock without having the "
+                      "lock, tlb_lock state = %d\n",
+                      env->threadId, env->tlb_lock_state);
+        g_assert(env->tlb_lock_state != HEX_LOCK_WAITING);
+        return;
+    }
+
+    env->tlb_lock_count--;
+    env->tlb_lock_state = HEX_LOCK_UNLOCKED;
+    SET_SYSCFG_FIELD(env, SYSCFG_TLBLOCK, 0);
+
+    /* Look for a thread to unlock */
+    unsigned int this_threadId = env->threadId;
+    CPUHexagonState *unlock_thread = NULL;
+    CPUState *cs;
+    CPU_FOREACH(cs) {
+        CPUHexagonState *thread = cpu_env(cs);
+
+        /*
+         * The hardware implements round-robin fairness, so we look for threads
+         * starting at env->threadId + 1 and incrementing modulo the number of
+         * threads.
+         *
+         * To implement this, we check if thread is a earlier in the modulo
+         * sequence than unlock_thread.
+         *     if unlock thread is higher than this thread
+         *         thread must be between this thread and unlock_thread
+         *     else
+         *         thread higher than this thread is ahead of unlock_thread
+         *         thread must be lower then unlock thread
+         */
+        if (thread->tlb_lock_state == HEX_LOCK_WAITING) {
+            if (!unlock_thread) {
+                unlock_thread = thread;
+            } else if (unlock_thread->threadId > this_threadId) {
+                if (this_threadId < thread->threadId &&
+                    thread->threadId < unlock_thread->threadId) {
+                    unlock_thread = thread;
+                }
+            } else {
+                if (thread->threadId > this_threadId) {
+                    unlock_thread = thread;
+                }
+                if (thread->threadId < unlock_thread->threadId) {
+                    unlock_thread = thread;
+                }
+            }
+        }
+    }
+    if (unlock_thread) {
+        cs = env_cpu(unlock_thread);
+        print_thread("\tWaiting thread found", cs);
+        unlock_thread->tlb_lock_state = HEX_LOCK_QUEUED;
+        SET_SYSCFG_FIELD(unlock_thread, SYSCFG_TLBLOCK, 1);
+        cpu_interrupt(cs, CPU_INTERRUPT_TLB_UNLOCK);
+    }
+
+    if (qemu_loglevel_mask(CPU_LOG_MMU)) {
+        qemu_log_mask(CPU_LOG_MMU, "Threads after hex_tlb_unlock:\n");
+        print_thread_states("\tThread");
+    }
+
+}
diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c
index cd6affa2f49..4df4226cbcb 100644
--- a/target/hexagon/translate.c
+++ b/target/hexagon/translate.c
@@ -987,7 +987,7 @@ static void hexagon_tr_init_disas_context(DisasContextBase 
*dcbase,
     HexagonCPU *hex_cpu = env_archcpu(cpu_env(cs));
     uint32_t hex_flags = dcbase->tb->flags;
 
-    ctx->mem_idx = MMU_USER_IDX;
+    ctx->mem_idx = FIELD_EX32(hex_flags, TB_FLAGS, MMU_INDEX);
     ctx->num_packets = 0;
     ctx->num_insns = 0;
     ctx->num_hvx_insns = 0;
-- 
2.34.1

Reply via email to