ad80e36744
We pass a ResetType argument to the Resettable class enter phase method, but we don't pass it to hold and exit, even though the callsites have it readily available. This means that if a device cared about the ResetType it would need to record it in the enter phase method to use later on. Pass the type to all three of the phase methods to avoid having to do that. Commit created with for dir in hw target include; do \ spatch --macro-file scripts/cocci-macro-file.h \ --sp-file scripts/coccinelle/reset-type.cocci \ --keep-comments --smpl-spacing --in-place \ --include-headers --dir $dir; done and no manual edits. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Edgar E. Iglesias <edgar.iglesias@amd.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Luc Michel <luc.michel@amd.com> Message-id: 20240412160809.1260625-5-peter.maydell@linaro.org
384 lines
12 KiB
C
384 lines
12 KiB
C
/*
|
|
* Copyright(c) 2019-2023 Qualcomm Innovation Center, Inc. All Rights Reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qemu/qemu-print.h"
|
|
#include "cpu.h"
|
|
#include "internal.h"
|
|
#include "exec/exec-all.h"
|
|
#include "qapi/error.h"
|
|
#include "hw/qdev-properties.h"
|
|
#include "fpu/softfloat-helpers.h"
|
|
#include "tcg/tcg.h"
|
|
#include "exec/gdbstub.h"
|
|
|
|
static void hexagon_v67_cpu_init(Object *obj) { }
|
|
static void hexagon_v68_cpu_init(Object *obj) { }
|
|
static void hexagon_v69_cpu_init(Object *obj) { }
|
|
static void hexagon_v71_cpu_init(Object *obj) { }
|
|
static void hexagon_v73_cpu_init(Object *obj) { }
|
|
|
|
static ObjectClass *hexagon_cpu_class_by_name(const char *cpu_model)
|
|
{
|
|
ObjectClass *oc;
|
|
char *typename;
|
|
char **cpuname;
|
|
|
|
cpuname = g_strsplit(cpu_model, ",", 1);
|
|
typename = g_strdup_printf(HEXAGON_CPU_TYPE_NAME("%s"), cpuname[0]);
|
|
oc = object_class_by_name(typename);
|
|
g_strfreev(cpuname);
|
|
g_free(typename);
|
|
|
|
return oc;
|
|
}
|
|
|
|
static Property hexagon_lldb_compat_property =
|
|
DEFINE_PROP_BOOL("lldb-compat", HexagonCPU, lldb_compat, false);
|
|
static Property hexagon_lldb_stack_adjust_property =
|
|
DEFINE_PROP_UNSIGNED("lldb-stack-adjust", HexagonCPU, lldb_stack_adjust,
|
|
0, qdev_prop_uint32, target_ulong);
|
|
static Property hexagon_short_circuit_property =
|
|
DEFINE_PROP_BOOL("short-circuit", HexagonCPU, short_circuit, true);
|
|
|
|
const char * const hexagon_regnames[TOTAL_PER_THREAD_REGS] = {
|
|
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
|
|
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
|
|
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
|
|
"r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
|
|
"sa0", "lc0", "sa1", "lc1", "p3_0", "c5", "m0", "m1",
|
|
"usr", "pc", "ugp", "gp", "cs0", "cs1", "c14", "c15",
|
|
"c16", "c17", "c18", "c19", "pkt_cnt", "insn_cnt", "hvx_cnt", "c23",
|
|
"c24", "c25", "c26", "c27", "c28", "c29", "c30", "c31",
|
|
};
|
|
|
|
/*
|
|
* One of the main debugging techniques is to use "-d cpu" and compare against
|
|
* LLDB output when single stepping. However, the target and qemu put the
|
|
* stacks at different locations. This is used to compensate so the diff is
|
|
* cleaner.
|
|
*/
|
|
static target_ulong adjust_stack_ptrs(CPUHexagonState *env, target_ulong addr)
|
|
{
|
|
HexagonCPU *cpu = env_archcpu(env);
|
|
target_ulong stack_adjust = cpu->lldb_stack_adjust;
|
|
target_ulong stack_start = env->stack_start;
|
|
target_ulong stack_size = 0x10000;
|
|
|
|
if (stack_adjust == 0) {
|
|
return addr;
|
|
}
|
|
|
|
if (stack_start + 0x1000 >= addr && addr >= (stack_start - stack_size)) {
|
|
return addr - stack_adjust;
|
|
}
|
|
return addr;
|
|
}
|
|
|
|
/* HEX_REG_P3_0_ALIASED (aka C4) is an alias for the predicate registers */
|
|
static target_ulong read_p3_0(CPUHexagonState *env)
|
|
{
|
|
int32_t control_reg = 0;
|
|
int i;
|
|
for (i = NUM_PREGS - 1; i >= 0; i--) {
|
|
control_reg <<= 8;
|
|
control_reg |= env->pred[i] & 0xff;
|
|
}
|
|
return control_reg;
|
|
}
|
|
|
|
static void print_reg(FILE *f, CPUHexagonState *env, int regnum)
|
|
{
|
|
target_ulong value;
|
|
|
|
if (regnum == HEX_REG_P3_0_ALIASED) {
|
|
value = read_p3_0(env);
|
|
} else {
|
|
value = regnum < 32 ? adjust_stack_ptrs(env, env->gpr[regnum])
|
|
: env->gpr[regnum];
|
|
}
|
|
|
|
qemu_fprintf(f, " %s = 0x" TARGET_FMT_lx "\n",
|
|
hexagon_regnames[regnum], value);
|
|
}
|
|
|
|
static void print_vreg(FILE *f, CPUHexagonState *env, int regnum,
|
|
bool skip_if_zero)
|
|
{
|
|
if (skip_if_zero) {
|
|
bool nonzero_found = false;
|
|
for (int i = 0; i < MAX_VEC_SIZE_BYTES; i++) {
|
|
if (env->VRegs[regnum].ub[i] != 0) {
|
|
nonzero_found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!nonzero_found) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
qemu_fprintf(f, " v%d = ( ", regnum);
|
|
qemu_fprintf(f, "0x%02x", env->VRegs[regnum].ub[MAX_VEC_SIZE_BYTES - 1]);
|
|
for (int i = MAX_VEC_SIZE_BYTES - 2; i >= 0; i--) {
|
|
qemu_fprintf(f, ", 0x%02x", env->VRegs[regnum].ub[i]);
|
|
}
|
|
qemu_fprintf(f, " )\n");
|
|
}
|
|
|
|
void hexagon_debug_vreg(CPUHexagonState *env, int regnum)
|
|
{
|
|
print_vreg(stdout, env, regnum, false);
|
|
}
|
|
|
|
static void print_qreg(FILE *f, CPUHexagonState *env, int regnum,
|
|
bool skip_if_zero)
|
|
{
|
|
if (skip_if_zero) {
|
|
bool nonzero_found = false;
|
|
for (int i = 0; i < MAX_VEC_SIZE_BYTES / 8; i++) {
|
|
if (env->QRegs[regnum].ub[i] != 0) {
|
|
nonzero_found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!nonzero_found) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
qemu_fprintf(f, " q%d = ( ", regnum);
|
|
qemu_fprintf(f, "0x%02x",
|
|
env->QRegs[regnum].ub[MAX_VEC_SIZE_BYTES / 8 - 1]);
|
|
for (int i = MAX_VEC_SIZE_BYTES / 8 - 2; i >= 0; i--) {
|
|
qemu_fprintf(f, ", 0x%02x", env->QRegs[regnum].ub[i]);
|
|
}
|
|
qemu_fprintf(f, " )\n");
|
|
}
|
|
|
|
void hexagon_debug_qreg(CPUHexagonState *env, int regnum)
|
|
{
|
|
print_qreg(stdout, env, regnum, false);
|
|
}
|
|
|
|
static void hexagon_dump(CPUHexagonState *env, FILE *f, int flags)
|
|
{
|
|
HexagonCPU *cpu = env_archcpu(env);
|
|
|
|
if (cpu->lldb_compat) {
|
|
/*
|
|
* When comparing with LLDB, it doesn't step through single-cycle
|
|
* hardware loops the same way. So, we just skip them here
|
|
*/
|
|
if (env->gpr[HEX_REG_PC] == env->last_pc_dumped) {
|
|
return;
|
|
}
|
|
env->last_pc_dumped = env->gpr[HEX_REG_PC];
|
|
}
|
|
|
|
qemu_fprintf(f, "General Purpose Registers = {\n");
|
|
for (int i = 0; i < 32; i++) {
|
|
print_reg(f, env, i);
|
|
}
|
|
print_reg(f, env, HEX_REG_SA0);
|
|
print_reg(f, env, HEX_REG_LC0);
|
|
print_reg(f, env, HEX_REG_SA1);
|
|
print_reg(f, env, HEX_REG_LC1);
|
|
print_reg(f, env, HEX_REG_M0);
|
|
print_reg(f, env, HEX_REG_M1);
|
|
print_reg(f, env, HEX_REG_USR);
|
|
print_reg(f, env, HEX_REG_P3_0_ALIASED);
|
|
print_reg(f, env, HEX_REG_GP);
|
|
print_reg(f, env, HEX_REG_UGP);
|
|
print_reg(f, env, HEX_REG_PC);
|
|
#ifdef CONFIG_USER_ONLY
|
|
/*
|
|
* Not modelled in user mode, print junk to minimize the diff's
|
|
* with LLDB output
|
|
*/
|
|
qemu_fprintf(f, " cause = 0x000000db\n");
|
|
qemu_fprintf(f, " badva = 0x00000000\n");
|
|
qemu_fprintf(f, " cs0 = 0x00000000\n");
|
|
qemu_fprintf(f, " cs1 = 0x00000000\n");
|
|
#else
|
|
print_reg(f, env, HEX_REG_CAUSE);
|
|
print_reg(f, env, HEX_REG_BADVA);
|
|
print_reg(f, env, HEX_REG_CS0);
|
|
print_reg(f, env, HEX_REG_CS1);
|
|
#endif
|
|
qemu_fprintf(f, "}\n");
|
|
|
|
if (flags & CPU_DUMP_FPU) {
|
|
qemu_fprintf(f, "Vector Registers = {\n");
|
|
for (int i = 0; i < NUM_VREGS; i++) {
|
|
print_vreg(f, env, i, true);
|
|
}
|
|
for (int i = 0; i < NUM_QREGS; i++) {
|
|
print_qreg(f, env, i, true);
|
|
}
|
|
qemu_fprintf(f, "}\n");
|
|
}
|
|
}
|
|
|
|
static void hexagon_dump_state(CPUState *cs, FILE *f, int flags)
|
|
{
|
|
hexagon_dump(cpu_env(cs), f, flags);
|
|
}
|
|
|
|
void hexagon_debug(CPUHexagonState *env)
|
|
{
|
|
hexagon_dump(env, stdout, CPU_DUMP_FPU);
|
|
}
|
|
|
|
static void hexagon_cpu_set_pc(CPUState *cs, vaddr value)
|
|
{
|
|
cpu_env(cs)->gpr[HEX_REG_PC] = value;
|
|
}
|
|
|
|
static vaddr hexagon_cpu_get_pc(CPUState *cs)
|
|
{
|
|
return cpu_env(cs)->gpr[HEX_REG_PC];
|
|
}
|
|
|
|
static void hexagon_cpu_synchronize_from_tb(CPUState *cs,
|
|
const TranslationBlock *tb)
|
|
{
|
|
tcg_debug_assert(!(cs->tcg_cflags & CF_PCREL));
|
|
cpu_env(cs)->gpr[HEX_REG_PC] = tb->pc;
|
|
}
|
|
|
|
static bool hexagon_cpu_has_work(CPUState *cs)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static void hexagon_restore_state_to_opc(CPUState *cs,
|
|
const TranslationBlock *tb,
|
|
const uint64_t *data)
|
|
{
|
|
cpu_env(cs)->gpr[HEX_REG_PC] = data[0];
|
|
}
|
|
|
|
static void hexagon_cpu_reset_hold(Object *obj, ResetType type)
|
|
{
|
|
CPUState *cs = CPU(obj);
|
|
HexagonCPUClass *mcc = HEXAGON_CPU_GET_CLASS(obj);
|
|
CPUHexagonState *env = cpu_env(cs);
|
|
|
|
if (mcc->parent_phases.hold) {
|
|
mcc->parent_phases.hold(obj, type);
|
|
}
|
|
|
|
set_default_nan_mode(1, &env->fp_status);
|
|
set_float_detect_tininess(float_tininess_before_rounding, &env->fp_status);
|
|
}
|
|
|
|
static void hexagon_cpu_disas_set_info(CPUState *s, disassemble_info *info)
|
|
{
|
|
info->print_insn = print_insn_hexagon;
|
|
}
|
|
|
|
static void hexagon_cpu_realize(DeviceState *dev, Error **errp)
|
|
{
|
|
CPUState *cs = CPU(dev);
|
|
HexagonCPUClass *mcc = HEXAGON_CPU_GET_CLASS(dev);
|
|
Error *local_err = NULL;
|
|
|
|
cpu_exec_realizefn(cs, &local_err);
|
|
if (local_err != NULL) {
|
|
error_propagate(errp, local_err);
|
|
return;
|
|
}
|
|
|
|
gdb_register_coprocessor(cs, hexagon_hvx_gdb_read_register,
|
|
hexagon_hvx_gdb_write_register,
|
|
gdb_find_static_feature("hexagon-hvx.xml"), 0);
|
|
|
|
qemu_init_vcpu(cs);
|
|
cpu_reset(cs);
|
|
|
|
mcc->parent_realize(dev, errp);
|
|
}
|
|
|
|
static void hexagon_cpu_init(Object *obj)
|
|
{
|
|
qdev_property_add_static(DEVICE(obj), &hexagon_lldb_compat_property);
|
|
qdev_property_add_static(DEVICE(obj), &hexagon_lldb_stack_adjust_property);
|
|
qdev_property_add_static(DEVICE(obj), &hexagon_short_circuit_property);
|
|
}
|
|
|
|
#include "hw/core/tcg-cpu-ops.h"
|
|
|
|
static const TCGCPUOps hexagon_tcg_ops = {
|
|
.initialize = hexagon_translate_init,
|
|
.synchronize_from_tb = hexagon_cpu_synchronize_from_tb,
|
|
.restore_state_to_opc = hexagon_restore_state_to_opc,
|
|
};
|
|
|
|
static void hexagon_cpu_class_init(ObjectClass *c, void *data)
|
|
{
|
|
HexagonCPUClass *mcc = HEXAGON_CPU_CLASS(c);
|
|
CPUClass *cc = CPU_CLASS(c);
|
|
DeviceClass *dc = DEVICE_CLASS(c);
|
|
ResettableClass *rc = RESETTABLE_CLASS(c);
|
|
|
|
device_class_set_parent_realize(dc, hexagon_cpu_realize,
|
|
&mcc->parent_realize);
|
|
|
|
resettable_class_set_parent_phases(rc, NULL, hexagon_cpu_reset_hold, NULL,
|
|
&mcc->parent_phases);
|
|
|
|
cc->class_by_name = hexagon_cpu_class_by_name;
|
|
cc->has_work = hexagon_cpu_has_work;
|
|
cc->dump_state = hexagon_dump_state;
|
|
cc->set_pc = hexagon_cpu_set_pc;
|
|
cc->get_pc = hexagon_cpu_get_pc;
|
|
cc->gdb_read_register = hexagon_gdb_read_register;
|
|
cc->gdb_write_register = hexagon_gdb_write_register;
|
|
cc->gdb_stop_before_watchpoint = true;
|
|
cc->gdb_core_xml_file = "hexagon-core.xml";
|
|
cc->disas_set_info = hexagon_cpu_disas_set_info;
|
|
cc->tcg_ops = &hexagon_tcg_ops;
|
|
}
|
|
|
|
#define DEFINE_CPU(type_name, initfn) \
|
|
{ \
|
|
.name = type_name, \
|
|
.parent = TYPE_HEXAGON_CPU, \
|
|
.instance_init = initfn \
|
|
}
|
|
|
|
static const TypeInfo hexagon_cpu_type_infos[] = {
|
|
{
|
|
.name = TYPE_HEXAGON_CPU,
|
|
.parent = TYPE_CPU,
|
|
.instance_size = sizeof(HexagonCPU),
|
|
.instance_align = __alignof(HexagonCPU),
|
|
.instance_init = hexagon_cpu_init,
|
|
.abstract = true,
|
|
.class_size = sizeof(HexagonCPUClass),
|
|
.class_init = hexagon_cpu_class_init,
|
|
},
|
|
DEFINE_CPU(TYPE_HEXAGON_CPU_V67, hexagon_v67_cpu_init),
|
|
DEFINE_CPU(TYPE_HEXAGON_CPU_V68, hexagon_v68_cpu_init),
|
|
DEFINE_CPU(TYPE_HEXAGON_CPU_V69, hexagon_v69_cpu_init),
|
|
DEFINE_CPU(TYPE_HEXAGON_CPU_V71, hexagon_v71_cpu_init),
|
|
DEFINE_CPU(TYPE_HEXAGON_CPU_V73, hexagon_v73_cpu_init),
|
|
};
|
|
|
|
DEFINE_TYPES(hexagon_cpu_type_infos)
|