mirror of
https://github.com/qemu/qemu.git
synced 2026-07-08 17:46:10 +00:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user