qemu/target/loongarch/cpu.c
Xiaojuan Yang a8a506c390 hw/loongarch: Add support loongson3 virt machine type.
Emulate a 3A5000 board use the new loongarch instruction.
3A5000 belongs to the Loongson3 series processors.
The board consists of a 3A5000 cpu model and the virt
bridge. The host 3A5000 board is really complicated and
contains many functions.Now for the tcg softmmu mode
only part functions are emulated.

More detailed info you can see
https://github.com/loongson/LoongArch-Documentation

Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-Id: <20220606124333.2060567-31-yangxiaojuan@loongson.cn>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2022-06-06 18:09:03 +00:00

695 lines
23 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* QEMU LoongArch CPU
*
* Copyright (c) 2021 Loongson Technology Corporation Limited
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "qemu/qemu-print.h"
#include "qapi/error.h"
#include "qemu/module.h"
#include "sysemu/qtest.h"
#include "exec/exec-all.h"
#include "qapi/qapi-commands-machine-target.h"
#include "cpu.h"
#include "internals.h"
#include "fpu/softfloat-helpers.h"
#include "cpu-csr.h"
#include "sysemu/reset.h"
#include "hw/loader.h"
const char * const regnames[32] = {
"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",
};
const char * const fregnames[32] = {
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
"f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",
"f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
"f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31",
};
static const char * const excp_names[] = {
[EXCCODE_INT] = "Interrupt",
[EXCCODE_PIL] = "Page invalid exception for load",
[EXCCODE_PIS] = "Page invalid exception for store",
[EXCCODE_PIF] = "Page invalid exception for fetch",
[EXCCODE_PME] = "Page modified exception",
[EXCCODE_PNR] = "Page Not Readable exception",
[EXCCODE_PNX] = "Page Not Executable exception",
[EXCCODE_PPI] = "Page Privilege error",
[EXCCODE_ADEF] = "Address error for instruction fetch",
[EXCCODE_ADEM] = "Address error for Memory access",
[EXCCODE_SYS] = "Syscall",
[EXCCODE_BRK] = "Break",
[EXCCODE_INE] = "Instruction Non-Existent",
[EXCCODE_IPE] = "Instruction privilege error",
[EXCCODE_FPE] = "Floating Point Exception",
[EXCCODE_DBP] = "Debug breakpoint",
};
const char *loongarch_exception_name(int32_t exception)
{
assert(excp_names[exception]);
return excp_names[exception];
}
void G_NORETURN do_raise_exception(CPULoongArchState *env,
uint32_t exception,
uintptr_t pc)
{
CPUState *cs = env_cpu(env);
qemu_log_mask(CPU_LOG_INT, "%s: %d (%s)\n",
__func__,
exception,
loongarch_exception_name(exception));
cs->exception_index = exception;
cpu_loop_exit_restore(cs, pc);
}
static void loongarch_cpu_set_pc(CPUState *cs, vaddr value)
{
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
CPULoongArchState *env = &cpu->env;
env->pc = value;
}
#include "hw/loongarch/virt.h"
void loongarch_cpu_set_irq(void *opaque, int irq, int level)
{
LoongArchCPU *cpu = opaque;
CPULoongArchState *env = &cpu->env;
CPUState *cs = CPU(cpu);
if (irq < 0 || irq >= N_IRQS) {
return;
}
env->CSR_ESTAT = deposit64(env->CSR_ESTAT, irq, 1, level != 0);
if (FIELD_EX64(env->CSR_ESTAT, CSR_ESTAT, IS)) {
cpu_interrupt(cs, CPU_INTERRUPT_HARD);
} else {
cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
}
}
static inline bool cpu_loongarch_hw_interrupts_enabled(CPULoongArchState *env)
{
bool ret = 0;
ret = (FIELD_EX64(env->CSR_CRMD, CSR_CRMD, IE) &&
!(FIELD_EX64(env->CSR_DBG, CSR_DBG, DST)));
return ret;
}
/* Check if there is pending and not masked out interrupt */
static inline bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env)
{
uint32_t pending;
uint32_t status;
bool r;
pending = FIELD_EX64(env->CSR_ESTAT, CSR_ESTAT, IS);
status = FIELD_EX64(env->CSR_ECFG, CSR_ECFG, LIE);
r = (pending & status) != 0;
return r;
}
static void loongarch_cpu_do_interrupt(CPUState *cs)
{
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
CPULoongArchState *env = &cpu->env;
bool update_badinstr = 1;
int cause = -1;
const char *name;
bool tlbfill = FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR);
uint32_t vec_size = FIELD_EX64(env->CSR_ECFG, CSR_ECFG, VS);
if (cs->exception_index != EXCCODE_INT) {
if (cs->exception_index < 0 ||
cs->exception_index > ARRAY_SIZE(excp_names)) {
name = "unknown";
} else {
name = excp_names[cs->exception_index];
}
qemu_log_mask(CPU_LOG_INT,
"%s enter: pc " TARGET_FMT_lx " ERA " TARGET_FMT_lx
" TLBRERA " TARGET_FMT_lx " %s exception\n", __func__,
env->pc, env->CSR_ERA, env->CSR_TLBRERA, name);
}
switch (cs->exception_index) {
case EXCCODE_DBP:
env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, DCL, 1);
env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, ECODE, 0xC);
goto set_DERA;
set_DERA:
env->CSR_DERA = env->pc;
env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, DST, 1);
env->pc = env->CSR_EENTRY + 0x480;
break;
case EXCCODE_INT:
if (FIELD_EX64(env->CSR_DBG, CSR_DBG, DST)) {
env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, DEI, 1);
goto set_DERA;
}
QEMU_FALLTHROUGH;
case EXCCODE_PIF:
cause = cs->exception_index;
update_badinstr = 0;
break;
case EXCCODE_ADEM:
case EXCCODE_SYS:
case EXCCODE_BRK:
case EXCCODE_PIL:
case EXCCODE_PIS:
case EXCCODE_PME:
case EXCCODE_PNR:
case EXCCODE_PNX:
case EXCCODE_PPI:
case EXCCODE_INE:
case EXCCODE_IPE:
case EXCCODE_FPE:
cause = cs->exception_index;
break;
default:
qemu_log("Error: exception(%d) '%s' has not been supported\n",
cs->exception_index, excp_names[cs->exception_index]);
abort();
}
if (update_badinstr) {
env->CSR_BADI = cpu_ldl_code(env, env->pc);
}
/* Save PLV and IE */
if (tlbfill) {
env->CSR_TLBRPRMD = FIELD_DP64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PPLV,
FIELD_EX64(env->CSR_CRMD,
CSR_CRMD, PLV));
env->CSR_TLBRPRMD = FIELD_DP64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PIE,
FIELD_EX64(env->CSR_CRMD, CSR_CRMD, IE));
/* set the DA mode */
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DA, 1);
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PG, 0);
env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA,
PC, (env->pc >> 2));
} else {
env->CSR_ESTAT = FIELD_DP64(env->CSR_ESTAT, CSR_ESTAT, ECODE, cause);
env->CSR_PRMD = FIELD_DP64(env->CSR_PRMD, CSR_PRMD, PPLV,
FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV));
env->CSR_PRMD = FIELD_DP64(env->CSR_PRMD, CSR_PRMD, PIE,
FIELD_EX64(env->CSR_CRMD, CSR_CRMD, IE));
env->CSR_ERA = env->pc;
}
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PLV, 0);
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, IE, 0);
if (cs->exception_index == EXCCODE_INT) {
/* Interrupt */
uint32_t vector = 0;
uint32_t pending = FIELD_EX64(env->CSR_ESTAT, CSR_ESTAT, IS);
pending &= FIELD_EX64(env->CSR_ECFG, CSR_ECFG, LIE);
/* Find the highest-priority interrupt. */
vector = 31 - clz32(pending);
env->pc = env->CSR_EENTRY + (EXCCODE_EXTERNAL_INT + vector) * vec_size;
qemu_log_mask(CPU_LOG_INT,
"%s: PC " TARGET_FMT_lx " ERA " TARGET_FMT_lx
" cause %d\n" " A " TARGET_FMT_lx " D "
TARGET_FMT_lx " vector = %d ExC " TARGET_FMT_lx "ExS"
TARGET_FMT_lx "\n",
__func__, env->pc, env->CSR_ERA,
cause, env->CSR_BADV, env->CSR_DERA, vector,
env->CSR_ECFG, env->CSR_ESTAT);
} else {
if (tlbfill) {
env->pc = env->CSR_TLBRENTRY;
} else {
env->pc = env->CSR_EENTRY;
env->pc += cause * vec_size;
}
qemu_log_mask(CPU_LOG_INT,
"%s: PC " TARGET_FMT_lx " ERA " TARGET_FMT_lx
" cause %d%s\n, ESTAT " TARGET_FMT_lx
" EXCFG " TARGET_FMT_lx " BADVA " TARGET_FMT_lx
"BADI " TARGET_FMT_lx " SYS_NUM " TARGET_FMT_lu
" cpu %d asid " TARGET_FMT_lx "\n", __func__, env->pc,
tlbfill ? env->CSR_TLBRERA : env->CSR_ERA,
cause, tlbfill ? "(refill)" : "", env->CSR_ESTAT,
env->CSR_ECFG,
tlbfill ? env->CSR_TLBRBADV : env->CSR_BADV,
env->CSR_BADI, env->gpr[11], cs->cpu_index,
env->CSR_ASID);
}
cs->exception_index = -1;
}
static void loongarch_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
vaddr addr, unsigned size,
MMUAccessType access_type,
int mmu_idx, MemTxAttrs attrs,
MemTxResult response,
uintptr_t retaddr)
{
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
CPULoongArchState *env = &cpu->env;
if (access_type == MMU_INST_FETCH) {
do_raise_exception(env, EXCCODE_ADEF, retaddr);
} else {
do_raise_exception(env, EXCCODE_ADEM, retaddr);
}
}
static bool loongarch_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
{
if (interrupt_request & CPU_INTERRUPT_HARD) {
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
CPULoongArchState *env = &cpu->env;
if (cpu_loongarch_hw_interrupts_enabled(env) &&
cpu_loongarch_hw_interrupts_pending(env)) {
/* Raise it */
cs->exception_index = EXCCODE_INT;
loongarch_cpu_do_interrupt(cs);
return true;
}
}
return false;
}
#ifdef CONFIG_TCG
static void loongarch_cpu_synchronize_from_tb(CPUState *cs,
const TranslationBlock *tb)
{
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
CPULoongArchState *env = &cpu->env;
env->pc = tb->pc;
}
#endif /* CONFIG_TCG */
static bool loongarch_cpu_has_work(CPUState *cs)
{
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
CPULoongArchState *env = &cpu->env;
bool has_work = false;
if ((cs->interrupt_request & CPU_INTERRUPT_HARD) &&
cpu_loongarch_hw_interrupts_pending(env)) {
has_work = true;
}
return has_work;
}
static void loongarch_la464_initfn(Object *obj)
{
LoongArchCPU *cpu = LOONGARCH_CPU(obj);
CPULoongArchState *env = &cpu->env;
int i;
for (i = 0; i < 21; i++) {
env->cpucfg[i] = 0x0;
}
env->cpucfg[0] = 0x14c010; /* PRID */
uint32_t data = 0;
data = FIELD_DP32(data, CPUCFG1, ARCH, 2);
data = FIELD_DP32(data, CPUCFG1, PGMMU, 1);
data = FIELD_DP32(data, CPUCFG1, IOCSR, 1);
data = FIELD_DP32(data, CPUCFG1, PALEN, 0x2f);
data = FIELD_DP32(data, CPUCFG1, VALEN, 0x2f);
data = FIELD_DP32(data, CPUCFG1, UAL, 1);
data = FIELD_DP32(data, CPUCFG1, RI, 1);
data = FIELD_DP32(data, CPUCFG1, EP, 1);
data = FIELD_DP32(data, CPUCFG1, RPLV, 1);
data = FIELD_DP32(data, CPUCFG1, HP, 1);
data = FIELD_DP32(data, CPUCFG1, IOCSR_BRD, 1);
env->cpucfg[1] = data;
data = 0;
data = FIELD_DP32(data, CPUCFG2, FP, 1);
data = FIELD_DP32(data, CPUCFG2, FP_SP, 1);
data = FIELD_DP32(data, CPUCFG2, FP_DP, 1);
data = FIELD_DP32(data, CPUCFG2, FP_VER, 1);
data = FIELD_DP32(data, CPUCFG2, LLFTP, 1);
data = FIELD_DP32(data, CPUCFG2, LLFTP_VER, 1);
data = FIELD_DP32(data, CPUCFG2, LAM, 1);
env->cpucfg[2] = data;
env->cpucfg[4] = 100 * 1000 * 1000; /* Crystal frequency */
data = 0;
data = FIELD_DP32(data, CPUCFG5, CC_MUL, 1);
data = FIELD_DP32(data, CPUCFG5, CC_DIV, 1);
env->cpucfg[5] = data;
data = 0;
data = FIELD_DP32(data, CPUCFG16, L1_IUPRE, 1);
data = FIELD_DP32(data, CPUCFG16, L1_DPRE, 1);
data = FIELD_DP32(data, CPUCFG16, L2_IUPRE, 1);
data = FIELD_DP32(data, CPUCFG16, L2_IUUNIFY, 1);
data = FIELD_DP32(data, CPUCFG16, L2_IUPRIV, 1);
data = FIELD_DP32(data, CPUCFG16, L3_IUPRE, 1);
data = FIELD_DP32(data, CPUCFG16, L3_IUUNIFY, 1);
data = FIELD_DP32(data, CPUCFG16, L3_IUINCL, 1);
env->cpucfg[16] = data;
data = 0;
data = FIELD_DP32(data, CPUCFG17, L1IU_WAYS, 3);
data = FIELD_DP32(data, CPUCFG17, L1IU_SETS, 8);
data = FIELD_DP32(data, CPUCFG17, L1IU_SIZE, 6);
env->cpucfg[17] = data;
data = 0;
data = FIELD_DP32(data, CPUCFG18, L1D_WAYS, 3);
data = FIELD_DP32(data, CPUCFG18, L1D_SETS, 8);
data = FIELD_DP32(data, CPUCFG18, L1D_SIZE, 6);
env->cpucfg[18] = data;
data = 0;
data = FIELD_DP32(data, CPUCFG19, L2IU_WAYS, 15);
data = FIELD_DP32(data, CPUCFG19, L2IU_SETS, 8);
data = FIELD_DP32(data, CPUCFG19, L2IU_SIZE, 6);
env->cpucfg[19] = data;
data = 0;
data = FIELD_DP32(data, CPUCFG20, L3IU_WAYS, 15);
data = FIELD_DP32(data, CPUCFG20, L3IU_SETS, 14);
data = FIELD_DP32(data, CPUCFG20, L3IU_SETS, 6);
env->cpucfg[20] = data;
env->CSR_ASID = FIELD_DP64(0, CSR_ASID, ASIDBITS, 0xa);
}
static void loongarch_cpu_list_entry(gpointer data, gpointer user_data)
{
const char *typename = object_class_get_name(OBJECT_CLASS(data));
qemu_printf("%s\n", typename);
}
void loongarch_cpu_list(void)
{
GSList *list;
list = object_class_get_list_sorted(TYPE_LOONGARCH_CPU, false);
g_slist_foreach(list, loongarch_cpu_list_entry, NULL);
g_slist_free(list);
}
static void loongarch_cpu_reset(DeviceState *dev)
{
CPUState *cs = CPU(dev);
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(cpu);
CPULoongArchState *env = &cpu->env;
lacc->parent_reset(dev);
env->fcsr0_mask = FCSR0_M1 | FCSR0_M2 | FCSR0_M3;
env->fcsr0 = 0x0;
int n;
/* Set csr registers value after reset */
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PLV, 0);
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, IE, 0);
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DA, 1);
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PG, 0);
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DATF, 1);
env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DATM, 1);
env->CSR_EUEN = FIELD_DP64(env->CSR_EUEN, CSR_EUEN, FPE, 0);
env->CSR_EUEN = FIELD_DP64(env->CSR_EUEN, CSR_EUEN, SXE, 0);
env->CSR_EUEN = FIELD_DP64(env->CSR_EUEN, CSR_EUEN, ASXE, 0);
env->CSR_EUEN = FIELD_DP64(env->CSR_EUEN, CSR_EUEN, BTE, 0);
env->CSR_MISC = 0;
env->CSR_ECFG = FIELD_DP64(env->CSR_ECFG, CSR_ECFG, VS, 0);
env->CSR_ECFG = FIELD_DP64(env->CSR_ECFG, CSR_ECFG, LIE, 0);
env->CSR_ESTAT = env->CSR_ESTAT & (~MAKE_64BIT_MASK(0, 2));
env->CSR_RVACFG = FIELD_DP64(env->CSR_RVACFG, CSR_RVACFG, RBITS, 0);
env->CSR_TCFG = FIELD_DP64(env->CSR_TCFG, CSR_TCFG, EN, 0);
env->CSR_LLBCTL = FIELD_DP64(env->CSR_LLBCTL, CSR_LLBCTL, KLO, 0);
env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 0);
env->CSR_MERRCTL = FIELD_DP64(env->CSR_MERRCTL, CSR_MERRCTL, ISMERR, 0);
env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, TLB_TYPE, 2);
env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, MTLB_ENTRY, 63);
env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_WAYS, 7);
env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_SETS, 8);
for (n = 0; n < 4; n++) {
env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV0, 0);
env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV1, 0);
env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV2, 0);
env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV3, 0);
}
env->pc = 0x1c000000;
restore_fp_status(env);
cs->exception_index = -1;
}
static void loongarch_cpu_disas_set_info(CPUState *s, disassemble_info *info)
{
info->print_insn = print_insn_loongarch;
}
static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp)
{
CPUState *cs = CPU(dev);
LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(dev);
Error *local_err = NULL;
cpu_exec_realizefn(cs, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
return;
}
cpu_reset(cs);
qemu_init_vcpu(cs);
lacc->parent_realize(dev, errp);
}
static void loongarch_qemu_write(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
}
static uint64_t loongarch_qemu_read(void *opaque, hwaddr addr, unsigned size)
{
switch (addr) {
case FEATURE_REG:
return 1ULL << IOCSRF_MSI | 1ULL << IOCSRF_EXTIOI |
1ULL << IOCSRF_CSRIPI;
case VENDOR_REG:
return 0x6e6f73676e6f6f4cULL; /* "Loongson" */
case CPUNAME_REG:
return 0x303030354133ULL; /* "3A5000" */
case MISC_FUNC_REG:
return 1ULL << IOCSRM_EXTIOI_EN;
}
return 0ULL;
}
static const MemoryRegionOps loongarch_qemu_ops = {
.read = loongarch_qemu_read,
.write = loongarch_qemu_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 8,
},
.impl = {
.min_access_size = 8,
.max_access_size = 8,
},
};
static void loongarch_cpu_init(Object *obj)
{
LoongArchCPU *cpu = LOONGARCH_CPU(obj);
CPULoongArchState *env = &cpu->env;
cpu_set_cpustate_pointers(cpu);
qdev_init_gpio_in(DEVICE(cpu), loongarch_cpu_set_irq, N_IRQS);
timer_init_ns(&cpu->timer, QEMU_CLOCK_VIRTUAL,
&loongarch_constant_timer_cb, cpu);
memory_region_init_io(&env->system_iocsr, OBJECT(cpu), NULL,
env, "iocsr", UINT64_MAX);
address_space_init(&env->address_space_iocsr, &env->system_iocsr, "IOCSR");
memory_region_init_io(&env->iocsr_mem, OBJECT(cpu), &loongarch_qemu_ops,
NULL, "iocsr_misc", 0x428);
memory_region_add_subregion(&env->system_iocsr, 0, &env->iocsr_mem);
}
static ObjectClass *loongarch_cpu_class_by_name(const char *cpu_model)
{
ObjectClass *oc;
char *typename;
typename = g_strdup_printf(LOONGARCH_CPU_TYPE_NAME("%s"), cpu_model);
oc = object_class_by_name(typename);
g_free(typename);
return oc;
}
void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags)
{
LoongArchCPU *cpu = LOONGARCH_CPU(cs);
CPULoongArchState *env = &cpu->env;
int i;
qemu_fprintf(f, " PC=%016" PRIx64 " ", env->pc);
qemu_fprintf(f, " FCSR0 0x%08x fp_status 0x%02x\n", env->fcsr0,
get_float_exception_flags(&env->fp_status));
/* gpr */
for (i = 0; i < 32; i++) {
if ((i & 3) == 0) {
qemu_fprintf(f, " GPR%02d:", i);
}
qemu_fprintf(f, " %s %016" PRIx64, regnames[i], env->gpr[i]);
if ((i & 3) == 3) {
qemu_fprintf(f, "\n");
}
}
qemu_fprintf(f, "CRMD=%016" PRIx64 "\n", env->CSR_CRMD);
qemu_fprintf(f, "PRMD=%016" PRIx64 "\n", env->CSR_PRMD);
qemu_fprintf(f, "EUEN=%016" PRIx64 "\n", env->CSR_EUEN);
qemu_fprintf(f, "ESTAT=%016" PRIx64 "\n", env->CSR_ESTAT);
qemu_fprintf(f, "ERA=%016" PRIx64 "\n", env->CSR_ERA);
qemu_fprintf(f, "BADV=%016" PRIx64 "\n", env->CSR_BADV);
qemu_fprintf(f, "BADI=%016" PRIx64 "\n", env->CSR_BADI);
qemu_fprintf(f, "EENTRY=%016" PRIx64 "\n", env->CSR_EENTRY);
qemu_fprintf(f, "PRCFG1=%016" PRIx64 ", PRCFG2=%016" PRIx64 ","
" PRCFG3=%016" PRIx64 "\n",
env->CSR_PRCFG1, env->CSR_PRCFG3, env->CSR_PRCFG3);
qemu_fprintf(f, "TLBRENTRY=%016" PRIx64 "\n", env->CSR_TLBRENTRY);
qemu_fprintf(f, "TLBRBADV=%016" PRIx64 "\n", env->CSR_TLBRBADV);
qemu_fprintf(f, "TLBRERA=%016" PRIx64 "\n", env->CSR_TLBRERA);
/* fpr */
if (flags & CPU_DUMP_FPU) {
for (i = 0; i < 32; i++) {
qemu_fprintf(f, " %s %016" PRIx64, fregnames[i], env->fpr[i]);
if ((i & 3) == 3) {
qemu_fprintf(f, "\n");
}
}
}
}
#ifdef CONFIG_TCG
#include "hw/core/tcg-cpu-ops.h"
static struct TCGCPUOps loongarch_tcg_ops = {
.initialize = loongarch_translate_init,
.synchronize_from_tb = loongarch_cpu_synchronize_from_tb,
.tlb_fill = loongarch_cpu_tlb_fill,
.cpu_exec_interrupt = loongarch_cpu_exec_interrupt,
.do_interrupt = loongarch_cpu_do_interrupt,
.do_transaction_failed = loongarch_cpu_do_transaction_failed,
};
#endif /* CONFIG_TCG */
#include "hw/core/sysemu-cpu-ops.h"
static const struct SysemuCPUOps loongarch_sysemu_ops = {
.get_phys_page_debug = loongarch_cpu_get_phys_page_debug,
};
static void loongarch_cpu_class_init(ObjectClass *c, void *data)
{
LoongArchCPUClass *lacc = LOONGARCH_CPU_CLASS(c);
CPUClass *cc = CPU_CLASS(c);
DeviceClass *dc = DEVICE_CLASS(c);
device_class_set_parent_realize(dc, loongarch_cpu_realizefn,
&lacc->parent_realize);
device_class_set_parent_reset(dc, loongarch_cpu_reset, &lacc->parent_reset);
cc->class_by_name = loongarch_cpu_class_by_name;
cc->has_work = loongarch_cpu_has_work;
cc->dump_state = loongarch_cpu_dump_state;
cc->set_pc = loongarch_cpu_set_pc;
dc->vmsd = &vmstate_loongarch_cpu;
cc->sysemu_ops = &loongarch_sysemu_ops;
cc->disas_set_info = loongarch_cpu_disas_set_info;
#ifdef CONFIG_TCG
cc->tcg_ops = &loongarch_tcg_ops;
#endif
}
#define DEFINE_LOONGARCH_CPU_TYPE(model, initfn) \
{ \
.parent = TYPE_LOONGARCH_CPU, \
.instance_init = initfn, \
.name = LOONGARCH_CPU_TYPE_NAME(model), \
}
static const TypeInfo loongarch_cpu_type_infos[] = {
{
.name = TYPE_LOONGARCH_CPU,
.parent = TYPE_CPU,
.instance_size = sizeof(LoongArchCPU),
.instance_init = loongarch_cpu_init,
.abstract = true,
.class_size = sizeof(LoongArchCPUClass),
.class_init = loongarch_cpu_class_init,
},
DEFINE_LOONGARCH_CPU_TYPE("la464", loongarch_la464_initfn),
};
DEFINE_TYPES(loongarch_cpu_type_infos)
static void loongarch_cpu_add_definition(gpointer data, gpointer user_data)
{
ObjectClass *oc = data;
CpuDefinitionInfoList **cpu_list = user_data;
CpuDefinitionInfo *info = g_new0(CpuDefinitionInfo, 1);
const char *typename = object_class_get_name(oc);
info->name = g_strndup(typename,
strlen(typename) - strlen("-" TYPE_LOONGARCH_CPU));
info->q_typename = g_strdup(typename);
QAPI_LIST_PREPEND(*cpu_list, info);
}
CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp)
{
CpuDefinitionInfoList *cpu_list = NULL;
GSList *list;
list = object_class_get_list(TYPE_LOONGARCH_CPU, false);
g_slist_foreach(list, loongarch_cpu_add_definition, &cpu_list);
g_slist_free(list);
return cpu_list;
}