Upstream: https://gitlab.com/qemu-project/qemu.git Upstream-Commit: d2e570cc0f97b936902a5b1b86b73c0f5998b475
400 lines
10 KiB
C
400 lines
10 KiB
C
/*
|
|
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "cpu.h"
|
|
#include "cpu_helper.h"
|
|
#include "system/cpus.h"
|
|
#include "hw/core/boards.h"
|
|
#include "hw/hexagon/hexagon.h"
|
|
#include "hw/hexagon/hexagon_globalreg.h"
|
|
#include "hex_interrupts.h"
|
|
#include "hex_mmu.h"
|
|
#include "system/runstate.h"
|
|
#include "exec/cpu-interrupt.h"
|
|
#include "exec/target_page.h"
|
|
#include "accel/tcg/cpu-ldst.h"
|
|
#include "exec/cputlb.h"
|
|
#include "qemu/log.h"
|
|
#include "tcg/tcg-op.h"
|
|
#include "internal.h"
|
|
#include "macros.h"
|
|
#include "sys_macros.h"
|
|
#include "arch.h"
|
|
|
|
|
|
uint32_t hexagon_get_pmu_counter(CPUHexagonState *cur_env, int index)
|
|
{
|
|
g_assert_not_reached();
|
|
}
|
|
|
|
uint64_t hexagon_get_sys_pcycle_count(CPUHexagonState *env)
|
|
{
|
|
uint64_t total = 0;
|
|
CPUState *cs;
|
|
|
|
g_assert(bql_locked());
|
|
CPU_FOREACH(cs) {
|
|
CPUHexagonState *thread_env = cpu_env(cs);
|
|
total += thread_env->t_cycle_count;
|
|
}
|
|
return total;
|
|
}
|
|
|
|
uint32_t hexagon_get_sys_pcycle_count_high(CPUHexagonState *env)
|
|
{
|
|
return (uint32_t)(hexagon_get_sys_pcycle_count(env) >> 32);
|
|
}
|
|
|
|
uint32_t hexagon_get_sys_pcycle_count_low(CPUHexagonState *env)
|
|
{
|
|
return (uint32_t)(hexagon_get_sys_pcycle_count(env));
|
|
}
|
|
|
|
void hexagon_set_sys_pcycle_count_high(CPUHexagonState *env, uint32_t val)
|
|
{
|
|
uint64_t old;
|
|
|
|
g_assert(bql_locked());
|
|
old = hexagon_get_sys_pcycle_count(env);
|
|
old = deposit64(old, 32, 32, val);
|
|
hexagon_set_sys_pcycle_count(env, old);
|
|
}
|
|
|
|
void hexagon_set_sys_pcycle_count_low(CPUHexagonState *env, uint32_t val)
|
|
{
|
|
uint64_t old;
|
|
|
|
g_assert(bql_locked());
|
|
old = hexagon_get_sys_pcycle_count(env);
|
|
old = deposit64(old, 0, 32, val);
|
|
hexagon_set_sys_pcycle_count(env, old);
|
|
}
|
|
|
|
void hexagon_set_sys_pcycle_count(CPUHexagonState *env, uint64_t val)
|
|
{
|
|
CPUState *cs;
|
|
uint64_t total;
|
|
int num_threads;
|
|
int64_t delta, per_thread, remainder;
|
|
|
|
g_assert(bql_locked());
|
|
total = hexagon_get_sys_pcycle_count(env);
|
|
|
|
/* Count active threads */
|
|
num_threads = 0;
|
|
CPU_FOREACH(cs) {
|
|
num_threads++;
|
|
}
|
|
g_assert(num_threads > 0);
|
|
|
|
/*
|
|
* Distribute the delta evenly across all threads.
|
|
* Any remainder goes to the calling thread.
|
|
*/
|
|
delta = (int64_t)(val - total);
|
|
per_thread = delta / num_threads;
|
|
remainder = delta - per_thread * num_threads;
|
|
|
|
CPU_FOREACH(cs) {
|
|
CPUHexagonState *thread_env = cpu_env(cs);
|
|
thread_env->t_cycle_count += per_thread;
|
|
}
|
|
env->t_cycle_count += remainder;
|
|
}
|
|
|
|
static void hexagon_resume_thread(CPUHexagonState *env)
|
|
{
|
|
CPUState *cs = env_cpu(env);
|
|
clear_wait_mode(env);
|
|
/*
|
|
* The wait instruction keeps the PC pointing to itself
|
|
* so that it has an opportunity to check for interrupts.
|
|
*
|
|
* When we come out of wait mode, adjust the PC to the
|
|
* next executable instruction.
|
|
*/
|
|
env->gpr[HEX_REG_PC] = env->wait_next_pc;
|
|
cs = env_cpu(env);
|
|
ASSERT_DIRECT_TO_GUEST_UNSET(env, cs->exception_index);
|
|
cs->halted = false;
|
|
cs->exception_index = HEX_EVENT_NONE;
|
|
qemu_cpu_kick(cs);
|
|
}
|
|
|
|
void hexagon_resume_threads(CPUHexagonState *current_env, uint32_t mask)
|
|
{
|
|
CPUState *cs;
|
|
CPUHexagonState *env;
|
|
|
|
g_assert(bql_locked());
|
|
CPU_FOREACH(cs) {
|
|
env = cpu_env(cs);
|
|
g_assert(env->threadId < THREADS_MAX);
|
|
if ((mask & (0x1 << env->threadId))) {
|
|
if (get_exe_mode(env) == HEX_EXE_MODE_WAIT) {
|
|
hexagon_resume_thread(env);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void hexagon_modify_ssr(CPUHexagonState *env, uint32_t new, uint32_t old)
|
|
{
|
|
bool old_EX, old_UM, old_GM, old_IE;
|
|
bool new_EX, new_UM, new_GM, new_IE;
|
|
uint8_t old_asid, new_asid;
|
|
|
|
g_assert(bql_locked());
|
|
|
|
old_EX = GET_SSR_FIELD(SSR_EX, old);
|
|
old_UM = GET_SSR_FIELD(SSR_UM, old);
|
|
old_GM = GET_SSR_FIELD(SSR_GM, old);
|
|
old_IE = GET_SSR_FIELD(SSR_IE, old);
|
|
new_EX = GET_SSR_FIELD(SSR_EX, new);
|
|
new_UM = GET_SSR_FIELD(SSR_UM, new);
|
|
new_GM = GET_SSR_FIELD(SSR_GM, new);
|
|
new_IE = GET_SSR_FIELD(SSR_IE, new);
|
|
|
|
if ((old_EX != new_EX) ||
|
|
(old_UM != new_UM) ||
|
|
(old_GM != new_GM)) {
|
|
hex_mmu_mode_change(env);
|
|
}
|
|
|
|
old_asid = GET_SSR_FIELD(SSR_ASID, old);
|
|
new_asid = GET_SSR_FIELD(SSR_ASID, new);
|
|
if (new_asid != old_asid) {
|
|
CPUState *cs = env_cpu(env);
|
|
tlb_flush(cs);
|
|
}
|
|
|
|
/* See if the interrupts have been enabled or we have exited EX mode */
|
|
if ((new_IE && !old_IE) ||
|
|
(!new_EX && old_EX)) {
|
|
hex_interrupt_update(env);
|
|
}
|
|
}
|
|
|
|
void clear_wait_mode(CPUHexagonState *env)
|
|
{
|
|
HexagonCPU *cpu;
|
|
uint32_t modectl, thread_wait_mask;
|
|
|
|
g_assert(bql_locked());
|
|
|
|
cpu = env_archcpu(env);
|
|
if (cpu->globalregs) {
|
|
modectl =
|
|
hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL,
|
|
env->threadId);
|
|
thread_wait_mask = GET_FIELD(MODECTL_W, modectl);
|
|
thread_wait_mask &= ~(0x1 << env->threadId);
|
|
SET_SYSTEM_FIELD(env, HEX_SREG_MODECTL, MODECTL_W, thread_wait_mask);
|
|
}
|
|
}
|
|
|
|
void hexagon_ssr_set_cause(CPUHexagonState *env, uint32_t cause)
|
|
{
|
|
uint32_t old, new;
|
|
|
|
g_assert(bql_locked());
|
|
|
|
old = env->t_sreg[HEX_SREG_SSR];
|
|
SET_SYSTEM_FIELD(env, HEX_SREG_SSR, SSR_EX, 1);
|
|
SET_SYSTEM_FIELD(env, HEX_SREG_SSR, SSR_CAUSE, cause);
|
|
new = env->t_sreg[HEX_SREG_SSR];
|
|
|
|
hexagon_modify_ssr(env, new, old);
|
|
}
|
|
|
|
|
|
int get_exe_mode(CPUHexagonState *env)
|
|
{
|
|
HexagonCPU *cpu;
|
|
uint32_t modectl, thread_enabled_mask, thread_wait_mask;
|
|
uint32_t isdbst, debugmode;
|
|
bool E_bit, W_bit, D_bit;
|
|
|
|
g_assert(bql_locked());
|
|
|
|
cpu = env_archcpu(env);
|
|
modectl = cpu->globalregs ?
|
|
hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL,
|
|
env->threadId) : 0;
|
|
thread_enabled_mask = GET_FIELD(MODECTL_E, modectl);
|
|
E_bit = thread_enabled_mask & (0x1 << env->threadId);
|
|
thread_wait_mask = GET_FIELD(MODECTL_W, modectl);
|
|
W_bit = thread_wait_mask & (0x1 << env->threadId);
|
|
isdbst = cpu->globalregs ?
|
|
hexagon_globalreg_read(cpu->globalregs, HEX_SREG_ISDBST,
|
|
env->threadId) : 0;
|
|
debugmode = GET_FIELD(ISDBST_DEBUGMODE, isdbst);
|
|
D_bit = debugmode & (0x1 << env->threadId);
|
|
|
|
if (!D_bit && !W_bit && !E_bit) {
|
|
return HEX_EXE_MODE_OFF;
|
|
}
|
|
if (!D_bit && !W_bit && E_bit) {
|
|
return HEX_EXE_MODE_RUN;
|
|
}
|
|
if (!D_bit && W_bit && E_bit) {
|
|
return HEX_EXE_MODE_WAIT;
|
|
}
|
|
if (D_bit && !W_bit && E_bit) {
|
|
return HEX_EXE_MODE_DEBUG;
|
|
}
|
|
g_assert_not_reached();
|
|
}
|
|
|
|
static uint32_t set_enable_mask(CPUHexagonState *env)
|
|
{
|
|
HexagonCPU *cpu;
|
|
uint32_t modectl, thread_enabled_mask;
|
|
|
|
g_assert(bql_locked());
|
|
|
|
cpu = env_archcpu(env);
|
|
if (!cpu->globalregs) {
|
|
return 0;
|
|
}
|
|
modectl =
|
|
hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL,
|
|
env->threadId);
|
|
thread_enabled_mask = GET_FIELD(MODECTL_E, modectl);
|
|
thread_enabled_mask |= 0x1 << env->threadId;
|
|
SET_SYSTEM_FIELD(env, HEX_SREG_MODECTL, MODECTL_E, thread_enabled_mask);
|
|
return thread_enabled_mask;
|
|
}
|
|
|
|
static uint32_t clear_enable_mask(CPUHexagonState *env)
|
|
{
|
|
HexagonCPU *cpu;
|
|
uint32_t modectl, thread_enabled_mask;
|
|
|
|
g_assert(bql_locked());
|
|
|
|
cpu = env_archcpu(env);
|
|
if (!cpu->globalregs) {
|
|
return 0;
|
|
}
|
|
modectl =
|
|
hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL,
|
|
env->threadId);
|
|
thread_enabled_mask = GET_FIELD(MODECTL_E, modectl);
|
|
thread_enabled_mask &= ~(0x1 << env->threadId);
|
|
SET_SYSTEM_FIELD(env, HEX_SREG_MODECTL, MODECTL_E, thread_enabled_mask);
|
|
return thread_enabled_mask;
|
|
}
|
|
static void do_start_thread(CPUState *cs, run_on_cpu_data tbd)
|
|
{
|
|
CPUHexagonState *env;
|
|
|
|
BQL_LOCK_GUARD();
|
|
|
|
env = cpu_env(cs);
|
|
|
|
hexagon_cpu_soft_reset(env);
|
|
|
|
set_enable_mask(env);
|
|
|
|
cs->halted = 0;
|
|
cs->exception_index = HEX_EVENT_NONE;
|
|
cpu_resume(cs);
|
|
}
|
|
|
|
void hexagon_start_threads(CPUHexagonState *current_env, uint32_t mask)
|
|
{
|
|
CPUState *cs;
|
|
CPU_FOREACH(cs) {
|
|
CPUHexagonState *env = cpu_env(cs);
|
|
if (!(mask & (0x1 << env->threadId))) {
|
|
continue;
|
|
}
|
|
|
|
if (current_env->threadId != env->threadId) {
|
|
async_safe_run_on_cpu(cs, do_start_thread, RUN_ON_CPU_NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* When we have all threads stopped, the return
|
|
* value to the shell is register 2 from thread 0.
|
|
*/
|
|
static uint32_t get_thread0_r2(void)
|
|
{
|
|
CPUState *cs;
|
|
CPU_FOREACH(cs) {
|
|
CPUHexagonState *thread = cpu_env(cs);
|
|
if (thread->threadId == 0) {
|
|
return thread->gpr[2];
|
|
}
|
|
}
|
|
g_assert_not_reached();
|
|
}
|
|
|
|
void hexagon_stop_thread(CPUHexagonState *env)
|
|
{
|
|
uint32_t thread_enabled_mask;
|
|
CPUState *cs;
|
|
|
|
BQL_LOCK_GUARD();
|
|
|
|
thread_enabled_mask = clear_enable_mask(env);
|
|
cs = env_cpu(env);
|
|
cpu_interrupt(cs, CPU_INTERRUPT_HALT);
|
|
if (!thread_enabled_mask) {
|
|
/* All threads are stopped, request shutdown */
|
|
qemu_system_shutdown_request_with_code(
|
|
SHUTDOWN_CAUSE_GUEST_SHUTDOWN, get_thread0_r2());
|
|
}
|
|
}
|
|
|
|
static int sys_in_monitor_mode_ssr(uint32_t ssr)
|
|
{
|
|
if ((GET_SSR_FIELD(SSR_EX, ssr) != 0) ||
|
|
((GET_SSR_FIELD(SSR_EX, ssr) == 0) &&
|
|
(GET_SSR_FIELD(SSR_UM, ssr) == 0))) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int sys_in_guest_mode_ssr(uint32_t ssr)
|
|
{
|
|
if ((GET_SSR_FIELD(SSR_EX, ssr) == 0) &&
|
|
(GET_SSR_FIELD(SSR_UM, ssr) != 0) &&
|
|
(GET_SSR_FIELD(SSR_GM, ssr) != 0)) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int sys_in_user_mode_ssr(uint32_t ssr)
|
|
{
|
|
if ((GET_SSR_FIELD(SSR_EX, ssr) == 0) &&
|
|
(GET_SSR_FIELD(SSR_UM, ssr) != 0) &&
|
|
(GET_SSR_FIELD(SSR_GM, ssr) == 0)) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int get_cpu_mode(CPUHexagonState *env)
|
|
{
|
|
uint32_t ssr = env->t_sreg[HEX_SREG_SSR];
|
|
|
|
if (sys_in_monitor_mode_ssr(ssr)) {
|
|
return HEX_CPU_MODE_MONITOR;
|
|
} else if (sys_in_guest_mode_ssr(ssr)) {
|
|
return HEX_CPU_MODE_GUEST;
|
|
} else if (sys_in_user_mode_ssr(ssr)) {
|
|
return HEX_CPU_MODE_USER;
|
|
}
|
|
return HEX_CPU_MODE_MONITOR;
|
|
}
|