Skip to content

Commit

Permalink
Protect read-only bits of syscfg.
Browse files Browse the repository at this point in the history
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 <sidneym@quicinc.com>
  • Loading branch information
SidManning authored and quic-mathbern committed Jul 15, 2024
1 parent 608cc69 commit 7f37344
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 7 deletions.
13 changes: 11 additions & 2 deletions target/hexagon/genptr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
}
Expand Down
18 changes: 13 additions & 5 deletions target/hexagon/op_helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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");
}
Expand Down Expand Up @@ -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 */
Expand Down

0 comments on commit 7f37344

Please sign in to comment.