From 7f37344051958858a5ae7b8231ba58cff268c30c Mon Sep 17 00:00:00 2001 From: Sid Manning Date: Tue, 11 Jun 2024 13:32:44 -0700 Subject: [PATCH] Protect read-only bits of syscfg. Users may do a read-modify-write to update syscfg. Between the read of the original value of syscfg and the write of the new value another thread may do a k0lock/unlock or tlblock/unlock changing read-only bits k0 or tk. This patch protects those bits from being lost during the read-modify-write sequence. Signed-off-by: Sid Manning --- target/hexagon/genptr.c | 13 +++++++++++-- target/hexagon/op_helper.c | 18 +++++++++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c index adc8c8939717..9fbe1abdd604 100644 --- a/target/hexagon/genptr.c +++ b/target/hexagon/genptr.c @@ -265,8 +265,17 @@ static void gen_log_sreg_write(DisasContext *ctx, int rnum, TCGv val) gen_masked_reg_write(val, hex_t_sreg[rnum], reg_mask); tcg_gen_mov_tl(ctx->t_sreg_new_value[rnum], val); } else { - gen_masked_reg_write(val, hex_g_sreg[rnum], reg_mask); - gen_helper_sreg_write(tcg_env, tcg_constant_i32(rnum), val); + /* + * SYSCFG can be updated by k0lock or tlblock. + * The helper will protect the immutable bits and do the + * RMW sequence with BQL held, the tcg RWM is not thread safe. + */ + if (rnum == HEX_SREG_SYSCFG) { + gen_helper_sreg_write(tcg_env, tcg_constant_i32(rnum), val); + } else { + gen_masked_reg_write(val, hex_g_sreg[rnum], reg_mask); + gen_helper_sreg_write(tcg_env, tcg_constant_i32(rnum), val); + } } } } diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index cc8c083284b6..9d24623a9f20 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -1762,16 +1762,17 @@ static void hex_k0_unlock(CPUHexagonState *env) { HEX_DEBUG_LOG("Before hex_k0_unlock: %d\n", env->threadId); print_thread_states("\tThread"); - qemu_mutex_lock_iothread(); + BQL_LOCK_GUARD(); /* Nothing to do if the k0 isn't locked by this thread */ uint32_t syscfg = ARCH_GET_SYSTEM_REG(env, HEX_SREG_SYSCFG); if ((GET_SYSCFG_FIELD(SYSCFG_K0LOCK, syscfg) == 0) || (ATOMIC_LOAD(env->k0_lock_state) != HEX_LOCK_OWNER)) { qemu_log_mask(LOG_GUEST_ERROR, - "thread %d attempted to unlock k0 without having the lock\n", - env->threadId); - qemu_mutex_unlock_iothread(); + "thread %d attempted to unlock k0 without having the " + "lock, k0_lock state = %d, syscfg:k0 = %d\n", + env->threadId, ATOMIC_LOAD(env->k0_lock_state), + GET_SYSCFG_FIELD(SYSCFG_K0LOCK, syscfg)); return; } @@ -1826,7 +1827,6 @@ static void hex_k0_unlock(CPUHexagonState *env) cpu_resume(cs); } - qemu_mutex_unlock_iothread(); HEX_DEBUG_LOG("After hex_k0_unlock: %d\n", env->threadId); print_thread_states("\tThread"); } @@ -2226,6 +2226,14 @@ static void modify_syscfg(CPUHexagonState *env, uint32_t val) { /* get old value and then store new value */ uint32_t old; + uint32_t syscfg_read_only_mask = 0x80001c00; + uint32_t syscfg = ARCH_GET_SYSTEM_REG(env, HEX_SREG_SYSCFG); + + /* clear read-only bits if they are set in the new value. */ + val &= ~syscfg_read_only_mask; + /* if read-only are currently set in syscfg keep them set. */ + val |= (syscfg & syscfg_read_only_mask); + ATOMIC_EXCHANGE(ARCH_GET_SYSTEM_REG_ADDR(env, HEX_SREG_SYSCFG), val, old); /* Check for change in MMU enable */