Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
target/loongarch: Add core definition
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> Message-Id: <20220606124333.2060567-3-yangxiaojuan@loongson.cn> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
- Loading branch information
1 parent
64baad6
commit 228021f
Showing
4 changed files
with
606 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,324 @@ | ||
/* 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" | ||
|
||
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; | ||
} | ||
|
||
#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 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; | ||
} | ||
|
||
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 = -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_cpu_init(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->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_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) |
Oops, something went wrong.