]>
Commit | Line | Data |
---|---|---|
228021f0 SG |
1 | /* SPDX-License-Identifier: GPL-2.0-or-later */ |
2 | /* | |
3 | * QEMU LoongArch CPU | |
4 | * | |
5 | * Copyright (c) 2021 Loongson Technology Corporation Limited | |
6 | */ | |
7 | ||
8 | #include "qemu/osdep.h" | |
9 | #include "qemu/log.h" | |
10 | #include "qemu/qemu-print.h" | |
11 | #include "qapi/error.h" | |
12 | #include "qemu/module.h" | |
13 | #include "sysemu/qtest.h" | |
14 | #include "exec/exec-all.h" | |
15 | #include "qapi/qapi-commands-machine-target.h" | |
16 | #include "cpu.h" | |
17 | #include "internals.h" | |
18 | #include "fpu/softfloat-helpers.h" | |
398cecb9 | 19 | #include "cpu-csr.h" |
228021f0 SG |
20 | |
21 | const char * const regnames[32] = { | |
22 | "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", | |
23 | "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", | |
24 | "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", | |
25 | "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", | |
26 | }; | |
27 | ||
28 | const char * const fregnames[32] = { | |
29 | "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", | |
30 | "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", | |
31 | "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", | |
32 | "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", | |
33 | }; | |
34 | ||
35 | static const char * const excp_names[] = { | |
36 | [EXCCODE_INT] = "Interrupt", | |
37 | [EXCCODE_PIL] = "Page invalid exception for load", | |
38 | [EXCCODE_PIS] = "Page invalid exception for store", | |
39 | [EXCCODE_PIF] = "Page invalid exception for fetch", | |
40 | [EXCCODE_PME] = "Page modified exception", | |
41 | [EXCCODE_PNR] = "Page Not Readable exception", | |
42 | [EXCCODE_PNX] = "Page Not Executable exception", | |
43 | [EXCCODE_PPI] = "Page Privilege error", | |
44 | [EXCCODE_ADEF] = "Address error for instruction fetch", | |
45 | [EXCCODE_ADEM] = "Address error for Memory access", | |
46 | [EXCCODE_SYS] = "Syscall", | |
47 | [EXCCODE_BRK] = "Break", | |
48 | [EXCCODE_INE] = "Instruction Non-Existent", | |
49 | [EXCCODE_IPE] = "Instruction privilege error", | |
50 | [EXCCODE_FPE] = "Floating Point Exception", | |
51 | [EXCCODE_DBP] = "Debug breakpoint", | |
52 | }; | |
53 | ||
54 | const char *loongarch_exception_name(int32_t exception) | |
55 | { | |
56 | assert(excp_names[exception]); | |
57 | return excp_names[exception]; | |
58 | } | |
59 | ||
60 | void G_NORETURN do_raise_exception(CPULoongArchState *env, | |
61 | uint32_t exception, | |
62 | uintptr_t pc) | |
63 | { | |
64 | CPUState *cs = env_cpu(env); | |
65 | ||
66 | qemu_log_mask(CPU_LOG_INT, "%s: %d (%s)\n", | |
67 | __func__, | |
68 | exception, | |
69 | loongarch_exception_name(exception)); | |
70 | cs->exception_index = exception; | |
71 | ||
72 | cpu_loop_exit_restore(cs, pc); | |
73 | } | |
74 | ||
75 | static void loongarch_cpu_set_pc(CPUState *cs, vaddr value) | |
76 | { | |
77 | LoongArchCPU *cpu = LOONGARCH_CPU(cs); | |
78 | CPULoongArchState *env = &cpu->env; | |
79 | ||
80 | env->pc = value; | |
81 | } | |
82 | ||
83 | #ifdef CONFIG_TCG | |
84 | static void loongarch_cpu_synchronize_from_tb(CPUState *cs, | |
85 | const TranslationBlock *tb) | |
86 | { | |
87 | LoongArchCPU *cpu = LOONGARCH_CPU(cs); | |
88 | CPULoongArchState *env = &cpu->env; | |
89 | ||
90 | env->pc = tb->pc; | |
91 | } | |
92 | #endif /* CONFIG_TCG */ | |
93 | ||
94 | static void loongarch_la464_initfn(Object *obj) | |
95 | { | |
96 | LoongArchCPU *cpu = LOONGARCH_CPU(obj); | |
97 | CPULoongArchState *env = &cpu->env; | |
98 | int i; | |
99 | ||
100 | for (i = 0; i < 21; i++) { | |
101 | env->cpucfg[i] = 0x0; | |
102 | } | |
103 | ||
104 | env->cpucfg[0] = 0x14c010; /* PRID */ | |
105 | ||
106 | uint32_t data = 0; | |
107 | data = FIELD_DP32(data, CPUCFG1, ARCH, 2); | |
108 | data = FIELD_DP32(data, CPUCFG1, PGMMU, 1); | |
109 | data = FIELD_DP32(data, CPUCFG1, IOCSR, 1); | |
110 | data = FIELD_DP32(data, CPUCFG1, PALEN, 0x2f); | |
111 | data = FIELD_DP32(data, CPUCFG1, VALEN, 0x2f); | |
112 | data = FIELD_DP32(data, CPUCFG1, UAL, 1); | |
113 | data = FIELD_DP32(data, CPUCFG1, RI, 1); | |
114 | data = FIELD_DP32(data, CPUCFG1, EP, 1); | |
115 | data = FIELD_DP32(data, CPUCFG1, RPLV, 1); | |
116 | data = FIELD_DP32(data, CPUCFG1, HP, 1); | |
117 | data = FIELD_DP32(data, CPUCFG1, IOCSR_BRD, 1); | |
118 | env->cpucfg[1] = data; | |
119 | ||
120 | data = 0; | |
121 | data = FIELD_DP32(data, CPUCFG2, FP, 1); | |
122 | data = FIELD_DP32(data, CPUCFG2, FP_SP, 1); | |
123 | data = FIELD_DP32(data, CPUCFG2, FP_DP, 1); | |
124 | data = FIELD_DP32(data, CPUCFG2, FP_VER, 1); | |
125 | data = FIELD_DP32(data, CPUCFG2, LLFTP, 1); | |
126 | data = FIELD_DP32(data, CPUCFG2, LLFTP_VER, 1); | |
127 | data = FIELD_DP32(data, CPUCFG2, LAM, 1); | |
128 | env->cpucfg[2] = data; | |
129 | ||
130 | env->cpucfg[4] = 100 * 1000 * 1000; /* Crystal frequency */ | |
131 | ||
132 | data = 0; | |
133 | data = FIELD_DP32(data, CPUCFG5, CC_MUL, 1); | |
134 | data = FIELD_DP32(data, CPUCFG5, CC_DIV, 1); | |
135 | env->cpucfg[5] = data; | |
136 | ||
137 | data = 0; | |
138 | data = FIELD_DP32(data, CPUCFG16, L1_IUPRE, 1); | |
139 | data = FIELD_DP32(data, CPUCFG16, L1_DPRE, 1); | |
140 | data = FIELD_DP32(data, CPUCFG16, L2_IUPRE, 1); | |
141 | data = FIELD_DP32(data, CPUCFG16, L2_IUUNIFY, 1); | |
142 | data = FIELD_DP32(data, CPUCFG16, L2_IUPRIV, 1); | |
143 | data = FIELD_DP32(data, CPUCFG16, L3_IUPRE, 1); | |
144 | data = FIELD_DP32(data, CPUCFG16, L3_IUUNIFY, 1); | |
145 | data = FIELD_DP32(data, CPUCFG16, L3_IUINCL, 1); | |
146 | env->cpucfg[16] = data; | |
147 | ||
148 | data = 0; | |
149 | data = FIELD_DP32(data, CPUCFG17, L1IU_WAYS, 3); | |
150 | data = FIELD_DP32(data, CPUCFG17, L1IU_SETS, 8); | |
151 | data = FIELD_DP32(data, CPUCFG17, L1IU_SIZE, 6); | |
152 | env->cpucfg[17] = data; | |
153 | ||
154 | data = 0; | |
155 | data = FIELD_DP32(data, CPUCFG18, L1D_WAYS, 3); | |
156 | data = FIELD_DP32(data, CPUCFG18, L1D_SETS, 8); | |
157 | data = FIELD_DP32(data, CPUCFG18, L1D_SIZE, 6); | |
158 | env->cpucfg[18] = data; | |
159 | ||
160 | data = 0; | |
161 | data = FIELD_DP32(data, CPUCFG19, L2IU_WAYS, 15); | |
162 | data = FIELD_DP32(data, CPUCFG19, L2IU_SETS, 8); | |
163 | data = FIELD_DP32(data, CPUCFG19, L2IU_SIZE, 6); | |
164 | env->cpucfg[19] = data; | |
165 | ||
166 | data = 0; | |
167 | data = FIELD_DP32(data, CPUCFG20, L3IU_WAYS, 15); | |
168 | data = FIELD_DP32(data, CPUCFG20, L3IU_SETS, 14); | |
169 | data = FIELD_DP32(data, CPUCFG20, L3IU_SETS, 6); | |
170 | env->cpucfg[20] = data; | |
398cecb9 XY |
171 | |
172 | env->CSR_ASID = FIELD_DP64(0, CSR_ASID, ASIDBITS, 0xa); | |
228021f0 SG |
173 | } |
174 | ||
175 | static void loongarch_cpu_list_entry(gpointer data, gpointer user_data) | |
176 | { | |
177 | const char *typename = object_class_get_name(OBJECT_CLASS(data)); | |
178 | ||
179 | qemu_printf("%s\n", typename); | |
180 | } | |
181 | ||
182 | void loongarch_cpu_list(void) | |
183 | { | |
184 | GSList *list; | |
185 | list = object_class_get_list_sorted(TYPE_LOONGARCH_CPU, false); | |
186 | g_slist_foreach(list, loongarch_cpu_list_entry, NULL); | |
187 | g_slist_free(list); | |
188 | } | |
189 | ||
190 | static void loongarch_cpu_reset(DeviceState *dev) | |
191 | { | |
192 | CPUState *cs = CPU(dev); | |
193 | LoongArchCPU *cpu = LOONGARCH_CPU(cs); | |
194 | LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(cpu); | |
195 | CPULoongArchState *env = &cpu->env; | |
196 | ||
197 | lacc->parent_reset(dev); | |
198 | ||
199 | env->fcsr0_mask = FCSR0_M1 | FCSR0_M2 | FCSR0_M3; | |
200 | env->fcsr0 = 0x0; | |
201 | ||
398cecb9 XY |
202 | int n; |
203 | /* Set csr registers value after reset */ | |
204 | env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PLV, 0); | |
205 | env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, IE, 0); | |
206 | env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DA, 1); | |
207 | env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PG, 0); | |
208 | env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DATF, 1); | |
209 | env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DATM, 1); | |
210 | ||
211 | env->CSR_EUEN = FIELD_DP64(env->CSR_EUEN, CSR_EUEN, FPE, 0); | |
212 | env->CSR_EUEN = FIELD_DP64(env->CSR_EUEN, CSR_EUEN, SXE, 0); | |
213 | env->CSR_EUEN = FIELD_DP64(env->CSR_EUEN, CSR_EUEN, ASXE, 0); | |
214 | env->CSR_EUEN = FIELD_DP64(env->CSR_EUEN, CSR_EUEN, BTE, 0); | |
215 | ||
216 | env->CSR_MISC = 0; | |
217 | ||
218 | env->CSR_ECFG = FIELD_DP64(env->CSR_ECFG, CSR_ECFG, VS, 0); | |
219 | env->CSR_ECFG = FIELD_DP64(env->CSR_ECFG, CSR_ECFG, LIE, 0); | |
220 | ||
221 | env->CSR_ESTAT = env->CSR_ESTAT & (~MAKE_64BIT_MASK(0, 2)); | |
222 | env->CSR_RVACFG = FIELD_DP64(env->CSR_RVACFG, CSR_RVACFG, RBITS, 0); | |
223 | env->CSR_TCFG = FIELD_DP64(env->CSR_TCFG, CSR_TCFG, EN, 0); | |
224 | env->CSR_LLBCTL = FIELD_DP64(env->CSR_LLBCTL, CSR_LLBCTL, KLO, 0); | |
225 | env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 0); | |
226 | env->CSR_MERRCTL = FIELD_DP64(env->CSR_MERRCTL, CSR_MERRCTL, ISMERR, 0); | |
227 | ||
228 | env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, TLB_TYPE, 2); | |
229 | env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, MTLB_ENTRY, 63); | |
230 | env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_WAYS, 7); | |
231 | env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_SETS, 8); | |
232 | ||
233 | for (n = 0; n < 4; n++) { | |
234 | env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV0, 0); | |
235 | env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV1, 0); | |
236 | env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV2, 0); | |
237 | env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV3, 0); | |
238 | } | |
239 | ||
d578ca6c | 240 | restore_fp_status(env); |
228021f0 SG |
241 | cs->exception_index = -1; |
242 | } | |
243 | ||
244 | static void loongarch_cpu_disas_set_info(CPUState *s, disassemble_info *info) | |
245 | { | |
246 | info->print_insn = print_insn_loongarch; | |
247 | } | |
248 | ||
249 | static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp) | |
250 | { | |
251 | CPUState *cs = CPU(dev); | |
252 | LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(dev); | |
253 | Error *local_err = NULL; | |
254 | ||
255 | cpu_exec_realizefn(cs, &local_err); | |
256 | if (local_err != NULL) { | |
257 | error_propagate(errp, local_err); | |
258 | return; | |
259 | } | |
260 | ||
261 | cpu_reset(cs); | |
262 | qemu_init_vcpu(cs); | |
263 | ||
264 | lacc->parent_realize(dev, errp); | |
265 | } | |
266 | ||
267 | static void loongarch_cpu_init(Object *obj) | |
268 | { | |
269 | LoongArchCPU *cpu = LOONGARCH_CPU(obj); | |
270 | ||
271 | cpu_set_cpustate_pointers(cpu); | |
272 | } | |
273 | ||
274 | static ObjectClass *loongarch_cpu_class_by_name(const char *cpu_model) | |
275 | { | |
276 | ObjectClass *oc; | |
277 | char *typename; | |
278 | ||
279 | typename = g_strdup_printf(LOONGARCH_CPU_TYPE_NAME("%s"), cpu_model); | |
280 | oc = object_class_by_name(typename); | |
281 | g_free(typename); | |
282 | return oc; | |
283 | } | |
284 | ||
285 | void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) | |
286 | { | |
287 | LoongArchCPU *cpu = LOONGARCH_CPU(cs); | |
288 | CPULoongArchState *env = &cpu->env; | |
289 | int i; | |
290 | ||
291 | qemu_fprintf(f, " PC=%016" PRIx64 " ", env->pc); | |
292 | qemu_fprintf(f, " FCSR0 0x%08x fp_status 0x%02x\n", env->fcsr0, | |
293 | get_float_exception_flags(&env->fp_status)); | |
294 | ||
295 | /* gpr */ | |
296 | for (i = 0; i < 32; i++) { | |
297 | if ((i & 3) == 0) { | |
298 | qemu_fprintf(f, " GPR%02d:", i); | |
299 | } | |
300 | qemu_fprintf(f, " %s %016" PRIx64, regnames[i], env->gpr[i]); | |
301 | if ((i & 3) == 3) { | |
302 | qemu_fprintf(f, "\n"); | |
303 | } | |
304 | } | |
305 | ||
306 | /* fpr */ | |
307 | if (flags & CPU_DUMP_FPU) { | |
308 | for (i = 0; i < 32; i++) { | |
309 | qemu_fprintf(f, " %s %016" PRIx64, fregnames[i], env->fpr[i]); | |
310 | if ((i & 3) == 3) { | |
311 | qemu_fprintf(f, "\n"); | |
312 | } | |
313 | } | |
314 | } | |
315 | } | |
316 | ||
317 | #ifdef CONFIG_TCG | |
318 | #include "hw/core/tcg-cpu-ops.h" | |
319 | ||
320 | static struct TCGCPUOps loongarch_tcg_ops = { | |
321 | .initialize = loongarch_translate_init, | |
322 | .synchronize_from_tb = loongarch_cpu_synchronize_from_tb, | |
323 | }; | |
324 | #endif /* CONFIG_TCG */ | |
325 | ||
326 | static void loongarch_cpu_class_init(ObjectClass *c, void *data) | |
327 | { | |
328 | LoongArchCPUClass *lacc = LOONGARCH_CPU_CLASS(c); | |
329 | CPUClass *cc = CPU_CLASS(c); | |
330 | DeviceClass *dc = DEVICE_CLASS(c); | |
331 | ||
332 | device_class_set_parent_realize(dc, loongarch_cpu_realizefn, | |
333 | &lacc->parent_realize); | |
334 | device_class_set_parent_reset(dc, loongarch_cpu_reset, &lacc->parent_reset); | |
335 | ||
336 | cc->class_by_name = loongarch_cpu_class_by_name; | |
337 | cc->dump_state = loongarch_cpu_dump_state; | |
338 | cc->set_pc = loongarch_cpu_set_pc; | |
67ebd42a | 339 | dc->vmsd = &vmstate_loongarch_cpu; |
228021f0 SG |
340 | cc->disas_set_info = loongarch_cpu_disas_set_info; |
341 | #ifdef CONFIG_TCG | |
342 | cc->tcg_ops = &loongarch_tcg_ops; | |
343 | #endif | |
344 | } | |
345 | ||
346 | #define DEFINE_LOONGARCH_CPU_TYPE(model, initfn) \ | |
347 | { \ | |
348 | .parent = TYPE_LOONGARCH_CPU, \ | |
349 | .instance_init = initfn, \ | |
350 | .name = LOONGARCH_CPU_TYPE_NAME(model), \ | |
351 | } | |
352 | ||
353 | static const TypeInfo loongarch_cpu_type_infos[] = { | |
354 | { | |
355 | .name = TYPE_LOONGARCH_CPU, | |
356 | .parent = TYPE_CPU, | |
357 | .instance_size = sizeof(LoongArchCPU), | |
358 | .instance_init = loongarch_cpu_init, | |
359 | ||
360 | .abstract = true, | |
361 | .class_size = sizeof(LoongArchCPUClass), | |
362 | .class_init = loongarch_cpu_class_init, | |
363 | }, | |
364 | DEFINE_LOONGARCH_CPU_TYPE("la464", loongarch_la464_initfn), | |
365 | }; | |
366 | ||
367 | DEFINE_TYPES(loongarch_cpu_type_infos) | |
425876f5 XY |
368 | |
369 | static void loongarch_cpu_add_definition(gpointer data, gpointer user_data) | |
370 | { | |
371 | ObjectClass *oc = data; | |
372 | CpuDefinitionInfoList **cpu_list = user_data; | |
373 | CpuDefinitionInfo *info = g_new0(CpuDefinitionInfo, 1); | |
374 | const char *typename = object_class_get_name(oc); | |
375 | ||
376 | info->name = g_strndup(typename, | |
377 | strlen(typename) - strlen("-" TYPE_LOONGARCH_CPU)); | |
378 | info->q_typename = g_strdup(typename); | |
379 | ||
380 | QAPI_LIST_PREPEND(*cpu_list, info); | |
381 | } | |
382 | ||
383 | CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) | |
384 | { | |
385 | CpuDefinitionInfoList *cpu_list = NULL; | |
386 | GSList *list; | |
387 | ||
388 | list = object_class_get_list(TYPE_LOONGARCH_CPU, false); | |
389 | g_slist_foreach(list, loongarch_cpu_add_definition, &cpu_list); | |
390 | g_slist_free(list); | |
391 | ||
392 | return cpu_list; | |
393 | } |