target/hexagon: Add k0 {un,}lock

Reviewed-by: Taylor Simpson <ltaylorsimpson@gmail.com>
Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
This commit is contained in:
Brian Cain
2026-06-22 15:28:50 -07:00
committed by Brian Cain
parent 60e435ddde
commit 1b8ec45484
3 changed files with 124 additions and 2 deletions

View 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;
}

View 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

View File

@@ -148,6 +148,9 @@
#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))