Message ID | 20220106094200.1801206-3-gaosong@loongson.cn |
---|---|
State | New |
Headers | show |
Series | Add LoongArch linux-user emulation support | expand |
On 1/6/22 17:41, Song Gao wrote: > This patch adds target state header, target definitions > and initialization routines. > > Signed-off-by: Song Gao<gaosong@loongson.cn> > Signed-off-by: Xiaojuan Yang<yangxiaojuan@loongson.cn> > Reviewed-by: Richard Henderson<richard.henderson@linaro.org> > Reviewed-by: Philippe Mathieu-Daudé<f4bug@amsat.org> > --- > target/loongarch/cpu-param.h | 18 ++ > target/loongarch/cpu.c | 314 +++++++++++++++++++++++++++++++++++ > target/loongarch/cpu.h | 252 ++++++++++++++++++++++++++++ > target/loongarch/internals.h | 21 +++ > 4 files changed, 605 insertions(+) > create mode 100644 target/loongarch/cpu-param.h > create mode 100644 target/loongarch/cpu.c > create mode 100644 target/loongarch/cpu.h > create mode 100644 target/loongarch/internals.h > > diff --git a/target/loongarch/cpu-param.h b/target/loongarch/cpu-param.h > new file mode 100644 > index 0000000000..9a769b67e0 > --- /dev/null > +++ b/target/loongarch/cpu-param.h > @@ -0,0 +1,18 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* > + * LoongArch CPU parameters for QEMU. > + * > + * Copyright (c) 2021 Loongson Technology Corporation Limited > + */ > + > +#ifndef LOONGARCH_CPU_PARAM_H > +#define LOONGARCH_CPU_PARAM_H > + > +#define TARGET_LONG_BITS 64 > +#define TARGET_PHYS_ADDR_SPACE_BITS 48 > +#define TARGET_VIRT_ADDR_SPACE_BITS 48 > + > +#define TARGET_PAGE_BITS 14 Aren't we capable of page sizes up to 64KiB? Minimal feasible page size is indeed 16KiB though (due to cache aliasing, although 4KiB pages are supported in hardware, they don't work in practice). > +#define NB_MMU_MODES 4 > + > +#endif > diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c > new file mode 100644 > index 0000000000..76b89d1606 > --- /dev/null > +++ b/target/loongarch/cpu.c > @@ -0,0 +1,314 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* > + * QEMU LoongArch CPU > + * > + * Copyright (c) 2021 Loongson Technology Corporation Limited > + */ > + > +#include "qemu/osdep.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" > + > +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[EXCP_LAST + 1] = { > + [EXCP_SYSCALL] = "Syscall", > + [EXCP_BREAK] = "Break", > + [EXCP_INE] = "Instruction Non-existent", Nit: "Instruction Non-Existent" (or is there any authoritative source for this spelling? the English translation of the manual?) > + [EXCP_FPE] = "Floating Point Exception", > +}; > + > +const char *loongarch_exception_name(int32_t exception) > +{ > + assert(excp_names[exception]); > + return excp_names[exception]; > +} > + > +void QEMU_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; > +} > + > +#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) > +{ > + return true; Note: this is only applicable to CONFIG_USER_ONLY, and needs to be changed in the following commits adding system emulation. To better convey your intention it may be better to use an #ifdef guard, something like this: #ifndef CONFIG_USER_ONLY #error System emulation TODO #else return true; #endif (I'm not sure if this is okay in QEMU coding style, so please correct me if this isn't the case.) > +} > + > +static void loongarch_3a5000_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, LSPW, 1); Do you support the SPW extension in this series? If not you probably don't want to set this bit. > + 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, 0x8003); This seems out-of-place, according to the manual this field is Way-1 for the L1I cache, so you have 0x8004=32772 ways in this cache? Same for all similar constructions below. > + data = FIELD_DP32(data, CPUCFG17, L1IU_SETS, 0x60); > + env->cpucfg[17] = data; Extra space after the "=" operator. Also it seems the L1IU_SIZE field is not provided initialization value; same for all similar constructions below. > + > + data = 0; > + data = FIELD_DP32(data, CPUCFG18, L1D_WAYS, 0x8003); > + data = FIELD_DP32(data, CPUCFG18, L1D_SETS, 0x60); > + env->cpucfg[18] = data; > + > + data = 0; > + data = FIELD_DP32(data, CPUCFG19, L2IU_WAYS, 0x800f); > + data = FIELD_DP32(data, CPUCFG19, L2IU_SETS, 0x60); > + env->cpucfg[19] = data; > + > + data = 0; > + data = FIELD_DP32(data, CPUCFG20, L3IU_WAYS, 0xf00f); > + data = FIELD_DP32(data, CPUCFG20, L3IU_SETS, 0x60); > + env->cpucfg[20] = data; > +} > + > +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; > + > + cs->exception_index = EXCP_NONE; > +} > + > +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_cpu_initfn(Object *obj) > +{ > + LoongArchCPU *cpu = LOONGARCH_CPU(obj); > + > + cpu_set_cpustate_pointers(cpu); > +} > + > +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"); > + } > + } > + > + /* 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, > +}; > +#endif /* CONFIG_TCG */ > + > +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; > + 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_initfn, > + > + .abstract = true, > + .class_size = sizeof(LoongArchCPUClass), > + .class_init = loongarch_cpu_class_init, > + }, > + DEFINE_LOONGARCH_CPU_TYPE("Loongson-3A5000", loongarch_3a5000_initfn), > +}; > + > +DEFINE_TYPES(loongarch_cpu_type_infos) > diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h > new file mode 100644 > index 0000000000..b036cdee5f > --- /dev/null > +++ b/target/loongarch/cpu.h > @@ -0,0 +1,252 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* > + * QEMU LoongArch CPU > + * > + * Copyright (c) 2021 Loongson Technology Corporation Limited > + */ > + > +#ifndef LOONGARCH_CPU_H > +#define LOONGARCH_CPU_H > + > +#include "exec/cpu-defs.h" > +#include "fpu/softfloat-types.h" > +#include "hw/registerfields.h" > + > +#define TCG_GUEST_DEFAULT_MO (0) > + > +#define FCSR0_M1 0x1f /* FCSR1 mask, Enables */ > +#define FCSR0_M2 0x1f1f0000 /* FCSR2 mask, Cause and Flags */ > +#define FCSR0_M3 0x300 /* FCSR3 mask, Round Mode */ > +#define FCSR0_RM 8 /* Round Mode bit num on fcsr0 */ > + > +FIELD(FCSR0, ENABLES, 0, 5) > +FIELD(FCSR0, RM, 8, 2) > +FIELD(FCSR0, FLAGS, 16, 5) > +FIELD(FCSR0, CAUSE, 24, 5) > + > +#define GET_FP_CAUSE(REG) FIELD_EX32(REG, FCSR0, CAUSE) > +#define SET_FP_CAUSE(REG, V) FIELD_DP32(REG, FCSR0, CAUSE, V) > +#define GET_FP_ENABLES(REG) FIELD_EX32(REG, FCSR0, ENABLES) > +#define SET_FP_ENABLES(REG, V) FIELD_DP32(REG, FCSR0, ENABLES, V) > +#define GET_FP_FLAGS(REG) FIELD_EX32(REG, FCSR0, FLAGS) > +#define SET_FP_FLAGS(REG, V) FIELD_DP32(REG, FCSR0, FLAGS, V) > +#define UPDATE_FP_FLAGS(REG, V) \ > + do { \ > + (REG) |= FIELD_DP32(0, FCSR0, FLAGS, V); \ > + } while (0) > + > +#define FP_INEXACT 1 > +#define FP_UNDERFLOW 2 > +#define FP_OVERFLOW 4 > +#define FP_DIV0 8 > +#define FP_INVALID 16 > + > +/* cpucfg[0] bits */ > +FIELD(CPUCFG0, PRID, 0, 32) > + > +/* cpucfg[1] bits */ > +FIELD(CPUCFG1, ARCH, 0, 2) > +FIELD(CPUCFG1, PGMMU, 2, 1) > +FIELD(CPUCFG1, IOCSR, 3, 1) > +FIELD(CPUCFG1, PALEN, 4, 8) > +FIELD(CPUCFG1, VALEN, 12, 8) > +FIELD(CPUCFG1, UAL, 20, 1) > +FIELD(CPUCFG1, RI, 21, 1) > +FIELD(CPUCFG1, EP, 22, 1) > +FIELD(CPUCFG1, RPLV, 23, 1) > +FIELD(CPUCFG1, HP, 24, 1) > +FIELD(CPUCFG1, IOCSR_BRD, 25, 1) > +FIELD(CPUCFG1, MSG_INT, 26, 1) > + > +/* cpucfg[2] bits */ > +FIELD(CPUCFG2, FP, 0, 1) > +FIELD(CPUCFG2, FP_SP, 1, 1) > +FIELD(CPUCFG2, FP_DP, 2, 1) > +FIELD(CPUCFG2, FP_VER, 3, 3) > +FIELD(CPUCFG2, LSX, 6, 1) > +FIELD(CPUCFG2, LASX, 7, 1) > +FIELD(CPUCFG2, COMPLEX, 8, 1) > +FIELD(CPUCFG2, CRYPTO, 9, 1) > +FIELD(CPUCFG2, LVZ, 10, 1) > +FIELD(CPUCFG2, LVZ_VER, 11, 3) > +FIELD(CPUCFG2, LLFTP, 14, 1) > +FIELD(CPUCFG2, LLFTP_VER, 15, 3) > +FIELD(CPUCFG2, LBT_X86, 18, 1) > +FIELD(CPUCFG2, LBT_ARM, 19, 1) > +FIELD(CPUCFG2, LBT_MIPS, 20, 1) > +FIELD(CPUCFG2, LSPW, 21, 1) > +FIELD(CPUCFG2, LAM, 22, 1) > + > +/* cpucfg[3] bits */ > +FIELD(CPUCFG3, CCDMA, 0, 1) > +FIELD(CPUCFG3, SFB, 1, 1) > +FIELD(CPUCFG3, UCACC, 2, 1) > +FIELD(CPUCFG3, LLEXC, 3, 1) > +FIELD(CPUCFG3, SCDLY, 4, 1) > +FIELD(CPUCFG3, LLDBAR, 5, 1) > +FIELD(CPUCFG3, ITLBHMC, 6, 1) > +FIELD(CPUCFG3, ICHMC, 7, 1) > +FIELD(CPUCFG3, SPW_LVL, 8, 3) > +FIELD(CPUCFG3, SPW_HP_HF, 11, 1) > +FIELD(CPUCFG3, RVA, 12, 1) > +FIELD(CPUCFG3, RVAMAX, 13, 4) > + > +/* cpucfg[4] bits */ > +FIELD(CPUCFG4, CC_FREQ, 0, 32) > + > +/* cpucfg[5] bits */ > +FIELD(CPUCFG5, CC_MUL, 0, 16) > +FIELD(CPUCFG5, CC_DIV, 16, 16) > + > +/* cpucfg[6] bits */ > +FIELD(CPUCFG6, PMP, 0, 1) > +FIELD(CPUCFG6, PMVER, 1, 3) > +FIELD(CPUCFG6, PMNUM, 4, 4) > +FIELD(CPUCFG6, PMBITS, 8, 6) > +FIELD(CPUCFG6, UPM, 14, 1) > + > +/* cpucfg[16] bits */ > +FIELD(CPUCFG16, L1_IUPRE, 0, 1) > +FIELD(CPUCFG16, L1_IUUNIFY, 1, 1) > +FIELD(CPUCFG16, L1_DPRE, 2, 1) > +FIELD(CPUCFG16, L2_IUPRE, 3, 1) > +FIELD(CPUCFG16, L2_IUUNIFY, 4, 1) > +FIELD(CPUCFG16, L2_IUPRIV, 5, 1) > +FIELD(CPUCFG16, L2_IUINCL, 6, 1) > +FIELD(CPUCFG16, L2_DPRE, 7, 1) > +FIELD(CPUCFG16, L2_DPRIV, 8, 1) > +FIELD(CPUCFG16, L2_DINCL, 9, 1) > +FIELD(CPUCFG16, L3_IUPRE, 10, 1) > +FIELD(CPUCFG16, L3_IUUNIFY, 11, 1) > +FIELD(CPUCFG16, L3_IUPRIV, 12, 1) > +FIELD(CPUCFG16, L3_IUINCL, 13, 1) > +FIELD(CPUCFG16, L3_DPRE, 14, 1) > +FIELD(CPUCFG16, L3_DPRIV, 15, 1) > +FIELD(CPUCFG16, L3_DINCL, 16, 1) > + > +/* cpucfg[17] bits */ > +FIELD(CPUCFG17, L1IU_WAYS, 0, 16) > +FIELD(CPUCFG17, L1IU_SETS, 16, 8) > +FIELD(CPUCFG17, L1IU_SIZE, 24, 7) > + > +/* cpucfg[18] bits */ > +FIELD(CPUCFG18, L1D_WAYS, 0, 16) > +FIELD(CPUCFG18, L1D_SETS, 16, 8) > +FIELD(CPUCFG18, L1D_SIZE, 24, 7) > + > +/* cpucfg[19] bits */ > +FIELD(CPUCFG19, L2IU_WAYS, 0, 16) > +FIELD(CPUCFG19, L2IU_SETS, 16, 8) > +FIELD(CPUCFG19, L2IU_SIZE, 24, 7) > + > +/* cpucfg[20] bits */ > +FIELD(CPUCFG20, L3IU_WAYS, 0, 16) > +FIELD(CPUCFG20, L3IU_SETS, 16, 8) > +FIELD(CPUCFG20, L3IU_SIZE, 24, 7) > + > +extern const char * const regnames[32]; > +extern const char * const fregnames[32]; > + > +typedef struct CPULoongArchState CPULoongArchState; > +struct CPULoongArchState { > + uint64_t gpr[32]; > + uint64_t pc; > + > + uint64_t fpr[32]; > + float_status fp_status; > + bool cf[8]; > + > + /* > + * fcsr0 > + * 31:29 |28:24 |23:21 |20:16 |15:10 |9:8 |7:5 |4:0 > + * Cause Flags RM Enables > + */ > + uint32_t fcsr0; > + uint32_t fcsr0_mask; With the field definition somewhere above, is this "infographic" necessary? > + > + uint32_t cpucfg[21]; > + > + uint64_t lladdr; /* LL virtual address compared against SC */ Similarly, do we explain every field with comments *here*? I think if fields are named according to the manuals, people will naturally look up names there so there's no worry for misunderstanding. > + uint64_t llval; > + > + uint64_t badaddr; > +}; > + > +/** > + * LoongArchCPU: > + * @env: #CPULoongArchState > + * > + * A LoongArch CPU. > + */ > +struct LoongArchCPU { > + /*< private >*/ > + CPUState parent_obj; > + /*< public >*/ > + > + CPUNegativeOffsetState neg; > + CPULoongArchState env; > +}; > + > +#define TYPE_LOONGARCH_CPU "loongarch-cpu" > + > +OBJECT_DECLARE_TYPE(LoongArchCPU, LoongArchCPUClass, > + LOONGARCH_CPU) > + > +/** > + * LoongArchCPUClass: > + * @parent_realize: The parent class' realize handler. > + * @parent_reset: The parent class' reset handler. > + * > + * A LoongArch CPU model. > + */ > +struct LoongArchCPUClass { > + /*< private >*/ > + CPUClass parent_class; > + /*< public >*/ > + > + DeviceRealize parent_realize; > + DeviceReset parent_reset; > +}; > + > +#define MMU_USER_IDX 3 > + > +static inline int cpu_mmu_index(CPULoongArchState *env, bool ifetch) > +{ > + return MMU_USER_IDX; > +} > + > +static inline void cpu_get_tb_cpu_state(CPULoongArchState *env, > + target_ulong *pc, > + target_ulong *cs_base, > + uint32_t *flags) > +{ > + *pc = env->pc; > + *cs_base = 0; > + *flags = cpu_mmu_index(env, false); > +} > + > +void loongarch_cpu_list(void); > + > +#define cpu_list loongarch_cpu_list > + > +typedef CPULoongArchState CPUArchState; > +typedef LoongArchCPU ArchCPU; > + > +#include "exec/cpu-all.h" > + > +/* Exceptions */ > +enum { > + EXCP_NONE = -1, > + EXCP_SYSCALL = 0, > + EXCP_BREAK, > + EXCP_INE, > + EXCP_FPE, > + > + EXCP_LAST = EXCP_FPE, > +}; > + > +#define LOONGARCH_CPU_TYPE_SUFFIX "-" TYPE_LOONGARCH_CPU > +#define LOONGARCH_CPU_TYPE_NAME(model) model LOONGARCH_CPU_TYPE_SUFFIX > +#define CPU_RESOLVING_TYPE TYPE_LOONGARCH_CPU > + > +#endif /* LOONGARCH_CPU_H */ > diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h > new file mode 100644 > index 0000000000..1e69e7d9d9 > --- /dev/null > +++ b/target/loongarch/internals.h > @@ -0,0 +1,21 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* > + * QEMU LoongArch CPU -- internal functions and types > + * > + * Copyright (c) 2021 Loongson Technology Corporation Limited > + */ > + > +#ifndef LOONGARCH_INTERNALS_H > +#define LOONGARCH_INTERNALS_H > + > +void loongarch_translate_init(void); > + > +void loongarch_cpu_dump_state(CPUState *cpu, FILE *f, int flags); > + > +void QEMU_NORETURN do_raise_exception(CPULoongArchState *env, > + uint32_t exception, > + uintptr_t pc); > + > +const char *loongarch_exception_name(int32_t exception); > + > +#endif
On 1/9/22 1:25 AM, WANG Xuerui wrote: >> +#define TARGET_PAGE_BITS 14 > Aren't we capable of page sizes up to 64KiB? Minimal feasible page size is indeed 16KiB > though (due to cache aliasing, although 4KiB pages are supported in hardware, they don't > work in practice). You must use the minimal page size here. 64k pages would be handled solely within tlb_fill, as multiples of the minimal page size. >> +static bool loongarch_cpu_has_work(CPUState *cs) >> +{ >> + return true; > > Note: this is only applicable to CONFIG_USER_ONLY, and needs to be changed in the > following commits adding system emulation. To better convey your intention it may be > better to use an #ifdef guard, something like this: > > #ifndef CONFIG_USER_ONLY > #error System emulation TODO > #else > return true; > #endif > > (I'm not sure if this is okay in QEMU coding style, so please correct me if this isn't the > case.) Prefer positive tests over negative tests, so #ifdef CONFIG_USER_ONLY return true; #else #error #endif >> + data = FIELD_DP32(data, CPUCFG2, LSPW, 1); > Do you support the SPW extension in this series? If not you probably don't want to set > this bit. Correct, you can't expose features that you don't implement. >> + uint64_t lladdr; /* LL virtual address compared against SC */ > Similarly, do we explain every field with comments *here*? I think if fields are named > according to the manuals, people will naturally look up names there so there's no worry > for misunderstanding. >> + uint64_t llval; These two fields are not architectural, so they do require explanation. Not that there aren't other targets that lack this documentation... r~
Hi, On 2022/1/10 上午2:49, Richard Henderson wrote: >>> +static bool loongarch_cpu_has_work(CPUState *cs) >>> +{ >>> + return true; >> >> Note: this is only applicable to CONFIG_USER_ONLY, and needs to be >> changed in the following commits adding system emulation. To better >> convey your intention it may be better to use an #ifdef guard, >> something like this: >> >> #ifndef CONFIG_USER_ONLY >> #error System emulation TODO >> #else >> return true; >> #endif >> >> (I'm not sure if this is okay in QEMU coding style, so please correct >> me if this isn't the case.) > In my opinion, we don't need to do this. As you pointed out below, SPW shouldn't appear in this series. All CONFIG_USER_ONLY macors should appear in the system emulation series. Liks this: https://patchew.org/QEMU/20220108091419.2027710-1-yangxiaojuan@loongson.cn/20220108091419.2027710-12-yangxiaojuan@loongson.cn/ static bool loongarch_cpu_has_work(CPUState *cs) { +#ifdef CONFIG_USER_ONLY return true; +#else + 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; +#endif } > Prefer positive tests over negative tests, so > > #ifdef CONFIG_USER_ONLY > return true; > #else > #error > #endif > >>> + data = FIELD_DP32(data, CPUCFG2, LSPW, 1); >> Do you support the SPW extension in this series? If not you probably >> don't want to set this bit. > > Correct, you can't expose features that you don't implement. Accept this suggesstions. Thanks Song
Hi, On 2022/1/9 下午5:25, WANG Xuerui wrote: >> + >> +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[EXCP_LAST + 1] = { >> + [EXCP_SYSCALL] = "Syscall", >> + [EXCP_BREAK] = "Break", >> + [EXCP_INE] = "Instruction Non-existent", > Nit: "Instruction Non-Existent" (or is there any authoritative source > for this spelling? the English translation of the manual?) I must admit that your English is really good, But 'Instruction Non-existent' is what you pointed out in v7. I have a history [1], [1] https://patchew.org/QEMU/1634561247-25499-1-git-send-email-gaosong@loongson.cn/1634561247-25499-3-git-send-email-gaosong@loongson.cn/ > +static const char * const excp_names[EXCP_LAST + 1] = { > + [EXCP_ADE] = "Address error", > + [EXCP_SYSCALL] = "Syscall", > + [EXCP_BREAK] = "Break", > + [EXCP_INE] = "Inst. Not Exist", Nit: "Instruction Non-existent", no need to shorten "instruction" like this IMO; no other similar usages exist so this would not be consistent. In any case thank you for your other suggestions. Thanks Song
Hi, On 1/10/22 21:00, gaosong wrote: > > Hi, > > On 2022/1/9 下午5:25, WANG Xuerui wrote: >>> + >>> +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[EXCP_LAST + 1] = { >>> + [EXCP_SYSCALL] = "Syscall", >>> + [EXCP_BREAK] = "Break", >>> + [EXCP_INE] = "Instruction Non-existent", >> Nit: "Instruction Non-Existent" (or is there any authoritative source >> for this spelling? the English translation of the manual?) > I must admit that your English is really good, But 'Instruction Non-existent' is what you pointed out in v7. I have a history [1], > [1]https://patchew.org/QEMU/1634561247-25499-1-git-send-email-gaosong@loongson.cn/1634561247-25499-3-git-send-email-gaosong@loongson.cn/ > > +static const char * const excp_names[EXCP_LAST + 1] = { > > + [EXCP_ADE] = "Address error", > > + [EXCP_SYSCALL] = "Syscall", > > + [EXCP_BREAK] = "Break", > > + [EXCP_INE] = "Inst. Not Exist", > Nit: "Instruction Non-existent", no need to shorten "instruction" like > this IMO; no other similar usages exist so this would not be consistent. > In any case thank you for your other suggestions. Well, I do make my mistakes sometimes, and I'll explain a bit: in the former review what I noticed was the broken English, and I pointed that out, but meanwhile I've forgotten I had done that, and why I didn't write "Non-Existent" was because the "Address error" wasn't in full Title Case. However, in this revision, what I noticed is the inconsistent casing (with EXCP_ADE removed, all other strings are in Title Case now), hence the reply. No need to go full self-defense over this; I think what matters after all is just consistency. > > Thanks > Song
Hi, On 1/10/22 20:34, gaosong wrote: > > Hi, > > On 2022/1/10 上午2:49, Richard Henderson wrote: >>>> +static bool loongarch_cpu_has_work(CPUState *cs) >>>> +{ >>>> + return true; >>> >>> Note: this is only applicable to CONFIG_USER_ONLY, and needs to be >>> changed in the following commits adding system emulation. To better >>> convey your intention it may be better to use an #ifdef guard, >>> something like this: >>> >>> #ifndef CONFIG_USER_ONLY >>> #error System emulation TODO >>> #else >>> return true; >>> #endif >>> >>> (I'm not sure if this is okay in QEMU coding style, so please >>> correct me if this isn't the case.) >> > In my opinion, we don't need to do this. As you pointed out below, SPW shouldn't appear in this series. All CONFIG_USER_ONLY macors should appear in the system emulation series. IMO, preemptively including the guard serves as "paving the road" to the full system emulation; in your argument, I feel we're deferring too much information to the context (this patch being inside the "linux-user" series, and getting merged earlier than full system emulation), while context would gradually fade out in the months/years ahead; being precise in writing down what you mean doesn't do any harm. Additionally, we might have to re-order the patches in order to unblock as much progress as possible, and in that case, this patch and the other patch adding the !CONFIG_USER_ONLY part would probably get squashed into one, so this code has to be touched too.
Hi, On 2022/1/9 下午5:25, WANG Xuerui wrote: >> + 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, 0x8003); > > This seems out-of-place, according to the manual this field is Way-1 > for the L1I cache, so you have 0x8004=32772 ways in this cache? > > Same for all similar constructions below. > I have time to reply to your comment now. As in the previous comments, I don't remember which one,these values should be the same as the values of the physical environment. I dumped 'CPUCFG17' value again, the value is no problem. Maybe you didn't think about dumping these values when you commented. The value of 'L11U_SIZE' is dumped to be 0. and cpucfg[i] has been initialized to 0 before. There is no need to set it again. Thanks Song
Hi, On 2022/1/12 下午5:28, gaosong wrote: >>> + 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, 0x8003); >> >> This seems out-of-place, according to the manual this field is Way-1 >> for the L1I cache, so you have 0x8004=32772 ways in this cache? >> >> Same for all similar constructions below. >> > I have time to reply to your comment now. > As in the previous comments, I don't remember which one,these values should be the same as the values of the physical environment. I dumped 'CPUCFG17' value again, > the value is no problem. Maybe you didn't think about dumping these values when you commented. The value of 'L11U_SIZE' is dumped to be 0. and cpucfg[i] has been > initialized to 0 before. There is no need to set it again. Not quite right, cpucfg[17] is '0x60800003', I missed a '0', I don't know from which version it's wrong Thank you very much, if I hadn't dumped the value today, I wouldn't have realized the wrong. > Thanks > Song
Hi, On 2022/1/12 18:17, gaosong wrote: > > Hi, > > On 2022/1/12 下午5:28, gaosong wrote: >>>> + 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, 0x8003); >>> >>> This seems out-of-place, according to the manual this field is Way-1 >>> for the L1I cache, so you have 0x8004=32772 ways in this cache? >>> >>> Same for all similar constructions below. >>> >> I have time to reply to your comment now. >> As in the previous comments, I don't remember which one,these values should be the same as the values of the physical environment. I dumped 'CPUCFG17' value again, >> the value is no problem. Maybe you didn't think about dumping these values when you commented. The value of 'L11U_SIZE' is dumped to be 0. and cpucfg[i] has been >> initialized to 0 before. There is no need to set it again. > Not quite right, cpucfg[17] is '0x60800003', I missed a '0', I don't know from which version it's wrong > Thank you very much, if I hadn't dumped the value today, I wouldn't have realized the wrong. Still not quite right; maybe you made a typo there as the value is `0x06080003` (the fields are all whole bytes/shorts, so very easy to recognize). I used the following snippet to get real values from the 3A5000 system: #include <stdio.h> int cpucfg(const int sel) { int ret; __asm__ __volatile__("cpucfg %0, %1" : "=r"(ret) : "r"(sel)); return ret; } int main(void) { int i; int c; for (i = 0; i < 64; i++) { c = cpucfg(i); if (!c) { continue; } printf("CPUCFG.0x%-2x = 0x%08x\n", i, c); } return 0; } And I got the following output so we can cross-check: CPUCFG.0x0 = 0x0014c010 CPUCFG.0x1 = 0x03f2f2fe CPUCFG.0x2 = 0x007ccfc7 CPUCFG.0x3 = 0x0000fcff CPUCFG.0x4 = 0x05f5e100 CPUCFG.0x5 = 0x00010001 CPUCFG.0x6 = 0x00007f33 CPUCFG.0x10 = 0x00002c3d CPUCFG.0x11 = 0x06080003 CPUCFG.0x12 = 0x06080003 CPUCFG.0x13 = 0x0608000f CPUCFG.0x14 = 0x060e000f CPUCFG.0x30 = 0x0000000e Obviously the 0x30 leaf is undocumented, but not implementing it shouldn't matter either, as userspace has no way to make use of that when people aren't even aware of its existence. The other fields are of course to be checked to only leave the implemented bits set in the QEMU implementation. Hope that helps!
diff --git a/target/loongarch/cpu-param.h b/target/loongarch/cpu-param.h new file mode 100644 index 0000000000..9a769b67e0 --- /dev/null +++ b/target/loongarch/cpu-param.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch CPU parameters for QEMU. + * + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_CPU_PARAM_H +#define LOONGARCH_CPU_PARAM_H + +#define TARGET_LONG_BITS 64 +#define TARGET_PHYS_ADDR_SPACE_BITS 48 +#define TARGET_VIRT_ADDR_SPACE_BITS 48 + +#define TARGET_PAGE_BITS 14 +#define NB_MMU_MODES 4 + +#endif diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c new file mode 100644 index 0000000000..76b89d1606 --- /dev/null +++ b/target/loongarch/cpu.c @@ -0,0 +1,314 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU LoongArch CPU + * + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.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" + +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[EXCP_LAST + 1] = { + [EXCP_SYSCALL] = "Syscall", + [EXCP_BREAK] = "Break", + [EXCP_INE] = "Instruction Non-existent", + [EXCP_FPE] = "Floating Point Exception", +}; + +const char *loongarch_exception_name(int32_t exception) +{ + assert(excp_names[exception]); + return excp_names[exception]; +} + +void QEMU_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; +} + +#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) +{ + return true; +} + +static void loongarch_3a5000_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, LSPW, 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, 0x8003); + data = FIELD_DP32(data, CPUCFG17, L1IU_SETS, 0x60); + env->cpucfg[17] = data; + + data = 0; + data = FIELD_DP32(data, CPUCFG18, L1D_WAYS, 0x8003); + data = FIELD_DP32(data, CPUCFG18, L1D_SETS, 0x60); + env->cpucfg[18] = data; + + data = 0; + data = FIELD_DP32(data, CPUCFG19, L2IU_WAYS, 0x800f); + data = FIELD_DP32(data, CPUCFG19, L2IU_SETS, 0x60); + env->cpucfg[19] = data; + + data = 0; + data = FIELD_DP32(data, CPUCFG20, L3IU_WAYS, 0xf00f); + data = FIELD_DP32(data, CPUCFG20, L3IU_SETS, 0x60); + env->cpucfg[20] = data; +} + +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; + + cs->exception_index = EXCP_NONE; +} + +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_cpu_initfn(Object *obj) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + cpu_set_cpustate_pointers(cpu); +} + +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"); + } + } + + /* 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, +}; +#endif /* CONFIG_TCG */ + +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; + 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_initfn, + + .abstract = true, + .class_size = sizeof(LoongArchCPUClass), + .class_init = loongarch_cpu_class_init, + }, + DEFINE_LOONGARCH_CPU_TYPE("Loongson-3A5000", loongarch_3a5000_initfn), +}; + +DEFINE_TYPES(loongarch_cpu_type_infos) diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h new file mode 100644 index 0000000000..b036cdee5f --- /dev/null +++ b/target/loongarch/cpu.h @@ -0,0 +1,252 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU LoongArch CPU + * + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_CPU_H +#define LOONGARCH_CPU_H + +#include "exec/cpu-defs.h" +#include "fpu/softfloat-types.h" +#include "hw/registerfields.h" + +#define TCG_GUEST_DEFAULT_MO (0) + +#define FCSR0_M1 0x1f /* FCSR1 mask, Enables */ +#define FCSR0_M2 0x1f1f0000 /* FCSR2 mask, Cause and Flags */ +#define FCSR0_M3 0x300 /* FCSR3 mask, Round Mode */ +#define FCSR0_RM 8 /* Round Mode bit num on fcsr0 */ + +FIELD(FCSR0, ENABLES, 0, 5) +FIELD(FCSR0, RM, 8, 2) +FIELD(FCSR0, FLAGS, 16, 5) +FIELD(FCSR0, CAUSE, 24, 5) + +#define GET_FP_CAUSE(REG) FIELD_EX32(REG, FCSR0, CAUSE) +#define SET_FP_CAUSE(REG, V) FIELD_DP32(REG, FCSR0, CAUSE, V) +#define GET_FP_ENABLES(REG) FIELD_EX32(REG, FCSR0, ENABLES) +#define SET_FP_ENABLES(REG, V) FIELD_DP32(REG, FCSR0, ENABLES, V) +#define GET_FP_FLAGS(REG) FIELD_EX32(REG, FCSR0, FLAGS) +#define SET_FP_FLAGS(REG, V) FIELD_DP32(REG, FCSR0, FLAGS, V) +#define UPDATE_FP_FLAGS(REG, V) \ + do { \ + (REG) |= FIELD_DP32(0, FCSR0, FLAGS, V); \ + } while (0) + +#define FP_INEXACT 1 +#define FP_UNDERFLOW 2 +#define FP_OVERFLOW 4 +#define FP_DIV0 8 +#define FP_INVALID 16 + +/* cpucfg[0] bits */ +FIELD(CPUCFG0, PRID, 0, 32) + +/* cpucfg[1] bits */ +FIELD(CPUCFG1, ARCH, 0, 2) +FIELD(CPUCFG1, PGMMU, 2, 1) +FIELD(CPUCFG1, IOCSR, 3, 1) +FIELD(CPUCFG1, PALEN, 4, 8) +FIELD(CPUCFG1, VALEN, 12, 8) +FIELD(CPUCFG1, UAL, 20, 1) +FIELD(CPUCFG1, RI, 21, 1) +FIELD(CPUCFG1, EP, 22, 1) +FIELD(CPUCFG1, RPLV, 23, 1) +FIELD(CPUCFG1, HP, 24, 1) +FIELD(CPUCFG1, IOCSR_BRD, 25, 1) +FIELD(CPUCFG1, MSG_INT, 26, 1) + +/* cpucfg[2] bits */ +FIELD(CPUCFG2, FP, 0, 1) +FIELD(CPUCFG2, FP_SP, 1, 1) +FIELD(CPUCFG2, FP_DP, 2, 1) +FIELD(CPUCFG2, FP_VER, 3, 3) +FIELD(CPUCFG2, LSX, 6, 1) +FIELD(CPUCFG2, LASX, 7, 1) +FIELD(CPUCFG2, COMPLEX, 8, 1) +FIELD(CPUCFG2, CRYPTO, 9, 1) +FIELD(CPUCFG2, LVZ, 10, 1) +FIELD(CPUCFG2, LVZ_VER, 11, 3) +FIELD(CPUCFG2, LLFTP, 14, 1) +FIELD(CPUCFG2, LLFTP_VER, 15, 3) +FIELD(CPUCFG2, LBT_X86, 18, 1) +FIELD(CPUCFG2, LBT_ARM, 19, 1) +FIELD(CPUCFG2, LBT_MIPS, 20, 1) +FIELD(CPUCFG2, LSPW, 21, 1) +FIELD(CPUCFG2, LAM, 22, 1) + +/* cpucfg[3] bits */ +FIELD(CPUCFG3, CCDMA, 0, 1) +FIELD(CPUCFG3, SFB, 1, 1) +FIELD(CPUCFG3, UCACC, 2, 1) +FIELD(CPUCFG3, LLEXC, 3, 1) +FIELD(CPUCFG3, SCDLY, 4, 1) +FIELD(CPUCFG3, LLDBAR, 5, 1) +FIELD(CPUCFG3, ITLBHMC, 6, 1) +FIELD(CPUCFG3, ICHMC, 7, 1) +FIELD(CPUCFG3, SPW_LVL, 8, 3) +FIELD(CPUCFG3, SPW_HP_HF, 11, 1) +FIELD(CPUCFG3, RVA, 12, 1) +FIELD(CPUCFG3, RVAMAX, 13, 4) + +/* cpucfg[4] bits */ +FIELD(CPUCFG4, CC_FREQ, 0, 32) + +/* cpucfg[5] bits */ +FIELD(CPUCFG5, CC_MUL, 0, 16) +FIELD(CPUCFG5, CC_DIV, 16, 16) + +/* cpucfg[6] bits */ +FIELD(CPUCFG6, PMP, 0, 1) +FIELD(CPUCFG6, PMVER, 1, 3) +FIELD(CPUCFG6, PMNUM, 4, 4) +FIELD(CPUCFG6, PMBITS, 8, 6) +FIELD(CPUCFG6, UPM, 14, 1) + +/* cpucfg[16] bits */ +FIELD(CPUCFG16, L1_IUPRE, 0, 1) +FIELD(CPUCFG16, L1_IUUNIFY, 1, 1) +FIELD(CPUCFG16, L1_DPRE, 2, 1) +FIELD(CPUCFG16, L2_IUPRE, 3, 1) +FIELD(CPUCFG16, L2_IUUNIFY, 4, 1) +FIELD(CPUCFG16, L2_IUPRIV, 5, 1) +FIELD(CPUCFG16, L2_IUINCL, 6, 1) +FIELD(CPUCFG16, L2_DPRE, 7, 1) +FIELD(CPUCFG16, L2_DPRIV, 8, 1) +FIELD(CPUCFG16, L2_DINCL, 9, 1) +FIELD(CPUCFG16, L3_IUPRE, 10, 1) +FIELD(CPUCFG16, L3_IUUNIFY, 11, 1) +FIELD(CPUCFG16, L3_IUPRIV, 12, 1) +FIELD(CPUCFG16, L3_IUINCL, 13, 1) +FIELD(CPUCFG16, L3_DPRE, 14, 1) +FIELD(CPUCFG16, L3_DPRIV, 15, 1) +FIELD(CPUCFG16, L3_DINCL, 16, 1) + +/* cpucfg[17] bits */ +FIELD(CPUCFG17, L1IU_WAYS, 0, 16) +FIELD(CPUCFG17, L1IU_SETS, 16, 8) +FIELD(CPUCFG17, L1IU_SIZE, 24, 7) + +/* cpucfg[18] bits */ +FIELD(CPUCFG18, L1D_WAYS, 0, 16) +FIELD(CPUCFG18, L1D_SETS, 16, 8) +FIELD(CPUCFG18, L1D_SIZE, 24, 7) + +/* cpucfg[19] bits */ +FIELD(CPUCFG19, L2IU_WAYS, 0, 16) +FIELD(CPUCFG19, L2IU_SETS, 16, 8) +FIELD(CPUCFG19, L2IU_SIZE, 24, 7) + +/* cpucfg[20] bits */ +FIELD(CPUCFG20, L3IU_WAYS, 0, 16) +FIELD(CPUCFG20, L3IU_SETS, 16, 8) +FIELD(CPUCFG20, L3IU_SIZE, 24, 7) + +extern const char * const regnames[32]; +extern const char * const fregnames[32]; + +typedef struct CPULoongArchState CPULoongArchState; +struct CPULoongArchState { + uint64_t gpr[32]; + uint64_t pc; + + uint64_t fpr[32]; + float_status fp_status; + bool cf[8]; + + /* + * fcsr0 + * 31:29 |28:24 |23:21 |20:16 |15:10 |9:8 |7:5 |4:0 + * Cause Flags RM Enables + */ + uint32_t fcsr0; + uint32_t fcsr0_mask; + + uint32_t cpucfg[21]; + + uint64_t lladdr; /* LL virtual address compared against SC */ + uint64_t llval; + + uint64_t badaddr; +}; + +/** + * LoongArchCPU: + * @env: #CPULoongArchState + * + * A LoongArch CPU. + */ +struct LoongArchCPU { + /*< private >*/ + CPUState parent_obj; + /*< public >*/ + + CPUNegativeOffsetState neg; + CPULoongArchState env; +}; + +#define TYPE_LOONGARCH_CPU "loongarch-cpu" + +OBJECT_DECLARE_TYPE(LoongArchCPU, LoongArchCPUClass, + LOONGARCH_CPU) + +/** + * LoongArchCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_reset: The parent class' reset handler. + * + * A LoongArch CPU model. + */ +struct LoongArchCPUClass { + /*< private >*/ + CPUClass parent_class; + /*< public >*/ + + DeviceRealize parent_realize; + DeviceReset parent_reset; +}; + +#define MMU_USER_IDX 3 + +static inline int cpu_mmu_index(CPULoongArchState *env, bool ifetch) +{ + return MMU_USER_IDX; +} + +static inline void cpu_get_tb_cpu_state(CPULoongArchState *env, + target_ulong *pc, + target_ulong *cs_base, + uint32_t *flags) +{ + *pc = env->pc; + *cs_base = 0; + *flags = cpu_mmu_index(env, false); +} + +void loongarch_cpu_list(void); + +#define cpu_list loongarch_cpu_list + +typedef CPULoongArchState CPUArchState; +typedef LoongArchCPU ArchCPU; + +#include "exec/cpu-all.h" + +/* Exceptions */ +enum { + EXCP_NONE = -1, + EXCP_SYSCALL = 0, + EXCP_BREAK, + EXCP_INE, + EXCP_FPE, + + EXCP_LAST = EXCP_FPE, +}; + +#define LOONGARCH_CPU_TYPE_SUFFIX "-" TYPE_LOONGARCH_CPU +#define LOONGARCH_CPU_TYPE_NAME(model) model LOONGARCH_CPU_TYPE_SUFFIX +#define CPU_RESOLVING_TYPE TYPE_LOONGARCH_CPU + +#endif /* LOONGARCH_CPU_H */ diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h new file mode 100644 index 0000000000..1e69e7d9d9 --- /dev/null +++ b/target/loongarch/internals.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU LoongArch CPU -- internal functions and types + * + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_INTERNALS_H +#define LOONGARCH_INTERNALS_H + +void loongarch_translate_init(void); + +void loongarch_cpu_dump_state(CPUState *cpu, FILE *f, int flags); + +void QEMU_NORETURN do_raise_exception(CPULoongArchState *env, + uint32_t exception, + uintptr_t pc); + +const char *loongarch_exception_name(int32_t exception); + +#endif