@samitouri / QOSamiQemu / commits / 1b8ec45484

target/hexagon: Add k0 {un,}lock

Reviewed-by: Taylor Simpson <ltaylorsimpson@gmail.com> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>

Brian Cain committed Jun 22, 2026 at 15:28 UTC 1b8ec454840ea02df83b79316e5701eefa55fa06
3 files changed +124 -2
target/hexagon/hex_mmu.c
+2 -2
@@ -206,8 +206,8 @@ void hex_tlb_unlock(CPUHexagonState *env)
206 (env->tlb_lock_state != HEX_LOCK_OWNER)) {
207 qemu_log_mask(LOG_GUEST_ERROR,
208 "thread %" PRIu32 " attempted to tlbunlock"
209 - " without having the lock, tlb_lock state = %d\n",
210 - env->threadId, env->tlb_lock_state);
209 + " without having the lock, tlb_lock state = %u\n",
210 + env->threadId, (unsigned)env->tlb_lock_state);
211 g_assert(env->tlb_lock_state != HEX_LOCK_WAITING);
212 return;
213 }
target/hexagon/op_helper.c
+119
@@ -37,6 +37,9 @@
37 #include "cpu_helper.h"
38 #include "translate.h"
39 #ifndef CONFIG_USER_ONLY
40 +#include "hw/hexagon/hexagon_globalreg.h"
41 +#include "hex_mmu.h"
42 +#include "hw/hexagon/hexagon_tlb.h"
43 #include "hex_interrupts.h"
44 #include "hexswi.h"
45 #endif
@@ -1233,6 +1236,122 @@ void HELPER(modify_ssr)(CPUHexagonState *env, uint32_t new, uint32_t old)
1236 BQL_LOCK_GUARD();
1237 hexagon_modify_ssr(env, new, old);
1238 }
1239 +
1240 +static void hex_k0_lock(CPUHexagonState *env)
1241 +{
1242 + HexagonCPU *cpu = env_archcpu(env);
1243 + CPUState *cs = env_cpu(env);
1244 + target_ulong syscfg;
1245 +
1246 + BQL_LOCK_GUARD();
1247 + g_assert((env->k0_lock_count == 0) || (env->k0_lock_count == 1));
1248 +
1249 + syscfg = cpu->globalregs ?
1250 + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SYSCFG,
1251 + env->threadId) : 0;
1252 + if (GET_SYSCFG_FIELD(SYSCFG_K0LOCK, syscfg)) {
1253 + if (env->k0_lock_state == HEX_LOCK_QUEUED) {
1254 + env->next_PC += 4;
1255 + env->k0_lock_count++;
1256 + env->k0_lock_state = HEX_LOCK_OWNER;
1257 + SET_SYSCFG_FIELD(env, SYSCFG_K0LOCK, 1);
1258 + return;
1259 + }
1260 + if (env->k0_lock_state == HEX_LOCK_OWNER) {
1261 + qemu_log_mask(LOG_GUEST_ERROR,
1262 + "Double k0lock at PC: 0x%" PRIx32
1263 + ", thread may hang\n",
1264 + env->next_PC);
1265 + env->next_PC += 4;
1266 + cpu_interrupt(cs, CPU_INTERRUPT_HALT);
1267 + cpu_loop_exit(cs);
1268 + return;
1269 + }
1270 + env->k0_lock_state = HEX_LOCK_WAITING;
1271 + cpu_interrupt(cs, CPU_INTERRUPT_HALT);
1272 + cpu_loop_exit(cs);
1273 + } else {
1274 + env->next_PC += 4;
1275 + env->k0_lock_count++;
1276 + env->k0_lock_state = HEX_LOCK_OWNER;
1277 + SET_SYSCFG_FIELD(env, SYSCFG_K0LOCK, 1);
1278 + }
1279 +}
1280 +
1281 +static void hex_k0_unlock(CPUHexagonState *env)
1282 +{
1283 + HexagonCPU *cpu = env_archcpu(env);
1284 + unsigned int this_threadId = env->threadId;
1285 + CPUHexagonState *unlock_thread = NULL;
1286 + CPUState *cs;
1287 + target_ulong syscfg;
1288 +
1289 + BQL_LOCK_GUARD();
1290 + g_assert((env->k0_lock_count == 0) || (env->k0_lock_count == 1));
1291 +
1292 + /* Nothing to do if the k0 isn't locked by this thread */
1293 + syscfg = cpu->globalregs ?
1294 + hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SYSCFG,
1295 + env->threadId) : 0;
1296 + if ((GET_SYSCFG_FIELD(SYSCFG_K0LOCK, syscfg) == 0) ||
1297 + (env->k0_lock_state != HEX_LOCK_OWNER)) {
1298 + qemu_log_mask(LOG_GUEST_ERROR,
1299 + "thread %" PRIu32 " attempted to unlock k0 without"
1300 + " having the lock, k0_lock state = %u,"
1301 + " syscfg:k0 = %" PRIu32 "\n",
1302 + env->threadId, (unsigned)env->k0_lock_state,
1303 + (uint32_t)GET_SYSCFG_FIELD(SYSCFG_K0LOCK, syscfg));
1304 + g_assert(env->k0_lock_state != HEX_LOCK_WAITING);
1305 + return;
1306 + }
1307 +
1308 + env->k0_lock_count--;
1309 + env->k0_lock_state = HEX_LOCK_UNLOCKED;
1310 + SET_SYSCFG_FIELD(env, SYSCFG_K0LOCK, 0);
1311 +
1312 + /* Look for a thread to unlock */
1313 + CPU_FOREACH(cs) {
1314 + CPUHexagonState *thread = cpu_env(cs);
1315 +
1316 + /*
1317 + * The hardware implements round-robin fairness, so we look for threads
1318 + * starting at env->threadId + 1 and incrementing modulo the number of
1319 + * threads.
1320 + *
1321 + * To implement this, we check if thread is a earlier in the modulo
1322 + * sequence than unlock_thread.
1323 + * if unlock thread is higher than this thread
1324 + * thread must be between this thread and unlock_thread
1325 + * else
1326 + * thread higher than this thread is ahead of unlock_thread
1327 + * thread must be lower then unlock thread
1328 + */
1329 + if (thread->k0_lock_state == HEX_LOCK_WAITING) {
1330 + if (!unlock_thread) {
1331 + unlock_thread = thread;
1332 + } else if (unlock_thread->threadId > this_threadId) {
1333 + if (this_threadId < thread->threadId &&
1334 + thread->threadId < unlock_thread->threadId) {
1335 + unlock_thread = thread;
1336 + }
1337 + } else {
1338 + if (thread->threadId > this_threadId) {
1339 + unlock_thread = thread;
1340 + }
1341 + if (thread->threadId < unlock_thread->threadId) {
1342 + unlock_thread = thread;
1343 + }
1344 + }
1345 + }
1346 + }
1347 + if (unlock_thread) {
1348 + cs = env_cpu(unlock_thread);
1349 + unlock_thread->k0_lock_state = HEX_LOCK_QUEUED;
1350 + SET_SYSCFG_FIELD(unlock_thread, SYSCFG_K0LOCK, 1);
1351 + cpu_interrupt(cs, CPU_INTERRUPT_K0_UNLOCK);
1352 + }
1353 +
1354 +}
1355 #endif
1356
1357
target/hexagon/sys_macros.h
+3
@@ -148,6 +148,9 @@
148 #define fSET_TLB_LOCK() hex_tlb_lock(env);
149 #define fCLEAR_TLB_LOCK() hex_tlb_unlock(env);
150
151 +#define fSET_K0_LOCK() hex_k0_lock(env);
152 +#define fCLEAR_K0_LOCK() hex_k0_unlock(env);
153 +
154 #define fTLB_IDXMASK(INDEX) \
155 ((INDEX) & (fPOW2_ROUNDUP( \
156 fCAST4u(hexagon_tlb_get_num_entries(env_archcpu(env)->tlb))) - 1))