]> git.ipfire.org Git - thirdparty/qemu.git/commitdiff
target/hexagon: Add k0 {un,}lock
authorBrian Cain <bcain@quicinc.com>
Mon, 22 Jun 2026 22:28:50 +0000 (15:28 -0700)
committerBrian Cain <brian.cain@oss.qualcomm.com>
Mon, 29 Jun 2026 13:03:00 +0000 (06:03 -0700)
Reviewed-by: Taylor Simpson <ltaylorsimpson@gmail.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
target/hexagon/hex_mmu.c
target/hexagon/op_helper.c
target/hexagon/sys_macros.h

index 1c8ccec69ef03b6c1216da8fe7db0554130df1ed..81d64a6146ccd6e3bc9cc70f6d4371eb2f0a1417 100644 (file)
@@ -206,8 +206,8 @@ void hex_tlb_unlock(CPUHexagonState *env)
         (env->tlb_lock_state != HEX_LOCK_OWNER)) {
         qemu_log_mask(LOG_GUEST_ERROR,
                       "thread %" PRIu32 " attempted to tlbunlock"
-                      " without having the lock, tlb_lock state = %d\n",
-                      env->threadId, env->tlb_lock_state);
+                      " without having the lock, tlb_lock state = %u\n",
+                      env->threadId, (unsigned)env->tlb_lock_state);
         g_assert(env->tlb_lock_state != HEX_LOCK_WAITING);
         return;
     }
index 9fbcd79c9d491e4e1ec86eb4427e0bda71ccf144..b82713bd1c0b0b86c110be7b656c3c339300c2cc 100644 (file)
@@ -37,6 +37,9 @@
 #include "cpu_helper.h"
 #include "translate.h"
 #ifndef CONFIG_USER_ONLY
+#include "hw/hexagon/hexagon_globalreg.h"
+#include "hex_mmu.h"
+#include "hw/hexagon/hexagon_tlb.h"
 #include "hex_interrupts.h"
 #include "hexswi.h"
 #endif
@@ -1233,6 +1236,122 @@ void HELPER(modify_ssr)(CPUHexagonState *env, uint32_t new, uint32_t old)
     BQL_LOCK_GUARD();
     hexagon_modify_ssr(env, new, old);
 }
+
+static void hex_k0_lock(CPUHexagonState *env)
+{
+    HexagonCPU *cpu = env_archcpu(env);
+    CPUState *cs = env_cpu(env);
+    target_ulong syscfg;
+
+    BQL_LOCK_GUARD();
+    g_assert((env->k0_lock_count == 0) || (env->k0_lock_count == 1));
+
+    syscfg = cpu->globalregs ?
+        hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SYSCFG,
+                               env->threadId) : 0;
+    if (GET_SYSCFG_FIELD(SYSCFG_K0LOCK, syscfg)) {
+        if (env->k0_lock_state == HEX_LOCK_QUEUED) {
+            env->next_PC += 4;
+            env->k0_lock_count++;
+            env->k0_lock_state = HEX_LOCK_OWNER;
+            SET_SYSCFG_FIELD(env, SYSCFG_K0LOCK, 1);
+            return;
+        }
+        if (env->k0_lock_state == HEX_LOCK_OWNER) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "Double k0lock at PC: 0x%" PRIx32
+                          ", thread may hang\n",
+                          env->next_PC);
+            env->next_PC += 4;
+            cpu_interrupt(cs, CPU_INTERRUPT_HALT);
+            cpu_loop_exit(cs);
+            return;
+        }
+        env->k0_lock_state = HEX_LOCK_WAITING;
+        cpu_interrupt(cs, CPU_INTERRUPT_HALT);
+        cpu_loop_exit(cs);
+    } else {
+        env->next_PC += 4;
+        env->k0_lock_count++;
+        env->k0_lock_state = HEX_LOCK_OWNER;
+        SET_SYSCFG_FIELD(env, SYSCFG_K0LOCK, 1);
+    }
+}
+
+static void hex_k0_unlock(CPUHexagonState *env)
+{
+    HexagonCPU *cpu = env_archcpu(env);
+    unsigned int this_threadId = env->threadId;
+    CPUHexagonState *unlock_thread = NULL;
+    CPUState *cs;
+    target_ulong syscfg;
+
+    BQL_LOCK_GUARD();
+    g_assert((env->k0_lock_count == 0) || (env->k0_lock_count == 1));
+
+    /* Nothing to do if the k0 isn't locked by this thread */
+    syscfg = cpu->globalregs ?
+        hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SYSCFG,
+                               env->threadId) : 0;
+    if ((GET_SYSCFG_FIELD(SYSCFG_K0LOCK, syscfg) == 0) ||
+        (env->k0_lock_state != HEX_LOCK_OWNER)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "thread %" PRIu32 " attempted to unlock k0 without"
+                      " having the lock, k0_lock state = %u,"
+                      " syscfg:k0 = %" PRIu32 "\n",
+                      env->threadId, (unsigned)env->k0_lock_state,
+                      (uint32_t)GET_SYSCFG_FIELD(SYSCFG_K0LOCK, syscfg));
+        g_assert(env->k0_lock_state != HEX_LOCK_WAITING);
+        return;
+    }
+
+    env->k0_lock_count--;
+    env->k0_lock_state = HEX_LOCK_UNLOCKED;
+    SET_SYSCFG_FIELD(env, SYSCFG_K0LOCK, 0);
+
+    /* Look for a thread to unlock */
+    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->k0_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);
+        unlock_thread->k0_lock_state = HEX_LOCK_QUEUED;
+        SET_SYSCFG_FIELD(unlock_thread, SYSCFG_K0LOCK, 1);
+        cpu_interrupt(cs, CPU_INTERRUPT_K0_UNLOCK);
+    }
+
+}
 #endif
 
 
index 707afc53e1f7909fc16858230bfc4e9a58a1768f..a49c72715dcb5db5641963a6a3f29158179ef3ae 100644 (file)
 #define fSET_TLB_LOCK()       hex_tlb_lock(env);
 #define fCLEAR_TLB_LOCK()     hex_tlb_unlock(env);
 
+#define fSET_K0_LOCK()        hex_k0_lock(env);
+#define fCLEAR_K0_LOCK()      hex_k0_unlock(env);
+
 #define fTLB_IDXMASK(INDEX) \
     ((INDEX) & (fPOW2_ROUNDUP( \
         fCAST4u(hexagon_tlb_get_num_entries(env_archcpu(env)->tlb))) - 1))