]>
Commit | Line | Data |
---|---|---|
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" | |
19 | ||
20 | const char * const regnames[32] = { | |
21 | "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", | |
22 | "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", | |
23 | "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", | |
24 | "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", | |
25 | }; | |
26 | ||
27 | const char * const fregnames[32] = { | |
28 | "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", | |
29 | "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", | |
30 | "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", | |
31 | "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", | |
32 | }; | |
33 | ||
34 | static const char * const excp_names[] = { | |
35 | [EXCCODE_INT] = "Interrupt", | |
36 | [EXCCODE_PIL] = "Page invalid exception for load", | |
37 | [EXCCODE_PIS] = "Page invalid exception for store", | |
38 | [EXCCODE_PIF] = "Page invalid exception for fetch", | |
39 | [EXCCODE_PME] = "Page modified exception", | |
40 | [EXCCODE_PNR] = "Page Not Readable exception", | |
41 | [EXCCODE_PNX] = "Page Not Executable exception", | |
42 | [EXCCODE_PPI] = "Page Privilege error", | |
43 | [EXCCODE_ADEF] = "Address error for instruction fetch", | |
44 | [EXCCODE_ADEM] = "Address error for Memory access", | |
45 | [EXCCODE_SYS] = "Syscall", | |
46 | [EXCCODE_BRK] = "Break", | |
47 | [EXCCODE_INE] = "Instruction Non-Existent", | |
48 | [EXCCODE_IPE] = "Instruction privilege error", | |
49 | [EXCCODE_FPE] = "Floating Point Exception", | |
50 | [EXCCODE_DBP] = "Debug breakpoint", | |
51 | }; | |
52 | ||
53 | const char *loongarch_exception_name(int32_t exception) | |
54 | { | |
55 | assert(excp_names[exception]); | |
56 | return excp_names[exception]; | |
57 | } | |
58 | ||
59 | void G_NORETURN do_raise_exception(CPULoongArchState *env, | |
60 | uint32_t exception, | |
61 | uintptr_t pc) | |
62 | { | |
63 | CPUState *cs = env_cpu(env); | |
64 | ||
65 | qemu_log_mask(CPU_LOG_INT, "%s: %d (%s)\n", | |
66 | __func__, | |
67 | exception, | |
68 | loongarch_exception_name(exception)); | |
69 | cs->exception_index = exception; | |
70 | ||
71 | cpu_loop_exit_restore(cs, pc); | |
72 | } | |
73 | ||
74 | static void loongarch_cpu_set_pc(CPUState *cs, vaddr value) | |
75 | { | |
76 | LoongArchCPU *cpu = LOONGARCH_CPU(cs); | |
77 | CPULoongArchState *env = &cpu->env; | |
78 | ||
79 | env->pc = value; | |
80 | } | |
81 | ||
82 | #ifdef CONFIG_TCG | |
83 | static void loongarch_cpu_synchronize_from_tb(CPUState *cs, | |
84 | const TranslationBlock *tb) | |
85 | { | |
86 | LoongArchCPU *cpu = LOONGARCH_CPU(cs); | |
87 | CPULoongArchState *env = &cpu->env; | |
88 | ||
89 | env->pc = tb->pc; | |
90 | } | |
91 | #endif /* CONFIG_TCG */ | |
92 | ||
93 | static void loongarch_la464_initfn(Object *obj) | |
94 | { | |
95 | LoongArchCPU *cpu = LOONGARCH_CPU(obj); | |
96 | CPULoongArchState *env = &cpu->env; | |
97 | int i; | |
98 | ||
99 | for (i = 0; i < 21; i++) { | |
100 | env->cpucfg[i] = 0x0; | |
101 | } | |
102 | ||
103 | env->cpucfg[0] = 0x14c010; /* PRID */ | |
104 | ||
105 | uint32_t data = 0; | |
106 | data = FIELD_DP32(data, CPUCFG1, ARCH, 2); | |
107 | data = FIELD_DP32(data, CPUCFG1, PGMMU, 1); | |
108 | data = FIELD_DP32(data, CPUCFG1, IOCSR, 1); | |
109 | data = FIELD_DP32(data, CPUCFG1, PALEN, 0x2f); | |
110 | data = FIELD_DP32(data, CPUCFG1, VALEN, 0x2f); | |
111 | data = FIELD_DP32(data, CPUCFG1, UAL, 1); | |
112 | data = FIELD_DP32(data, CPUCFG1, RI, 1); | |
113 | data = FIELD_DP32(data, CPUCFG1, EP, 1); | |
114 | data = FIELD_DP32(data, CPUCFG1, RPLV, 1); | |
115 | data = FIELD_DP32(data, CPUCFG1, HP, 1); | |
116 | data = FIELD_DP32(data, CPUCFG1, IOCSR_BRD, 1); | |
117 | env->cpucfg[1] = data; | |
118 | ||
119 | data = 0; | |
120 | data = FIELD_DP32(data, CPUCFG2, FP, 1); | |
121 | data = FIELD_DP32(data, CPUCFG2, FP_SP, 1); | |
122 | data = FIELD_DP32(data, CPUCFG2, FP_DP, 1); | |
123 | data = FIELD_DP32(data, CPUCFG2, FP_VER, 1); | |
124 | data = FIELD_DP32(data, CPUCFG2, LLFTP, 1); | |
125 | data = FIELD_DP32(data, CPUCFG2, LLFTP_VER, 1); | |
126 | data = FIELD_DP32(data, CPUCFG2, LAM, 1); | |
127 | env->cpucfg[2] = data; | |
128 | ||
129 | env->cpucfg[4] = 100 * 1000 * 1000; /* Crystal frequency */ | |
130 | ||
131 | data = 0; | |
132 | data = FIELD_DP32(data, CPUCFG5, CC_MUL, 1); | |
133 | data = FIELD_DP32(data, CPUCFG5, CC_DIV, 1); | |
134 | env->cpucfg[5] = data; | |
135 | ||
136 | data = 0; | |
137 | data = FIELD_DP32(data, CPUCFG16, L1_IUPRE, 1); | |
138 | data = FIELD_DP32(data, CPUCFG16, L1_DPRE, 1); | |
139 | data = FIELD_DP32(data, CPUCFG16, L2_IUPRE, 1); | |
140 | data = FIELD_DP32(data, CPUCFG16, L2_IUUNIFY, 1); | |
141 | data = FIELD_DP32(data, CPUCFG16, L2_IUPRIV, 1); | |
142 | data = FIELD_DP32(data, CPUCFG16, L3_IUPRE, 1); | |
143 | data = FIELD_DP32(data, CPUCFG16, L3_IUUNIFY, 1); | |
144 | data = FIELD_DP32(data, CPUCFG16, L3_IUINCL, 1); | |
145 | env->cpucfg[16] = data; | |
146 | ||
147 | data = 0; | |
148 | data = FIELD_DP32(data, CPUCFG17, L1IU_WAYS, 3); | |
149 | data = FIELD_DP32(data, CPUCFG17, L1IU_SETS, 8); | |
150 | data = FIELD_DP32(data, CPUCFG17, L1IU_SIZE, 6); | |
151 | env->cpucfg[17] = data; | |
152 | ||
153 | data = 0; | |
154 | data = FIELD_DP32(data, CPUCFG18, L1D_WAYS, 3); | |
155 | data = FIELD_DP32(data, CPUCFG18, L1D_SETS, 8); | |
156 | data = FIELD_DP32(data, CPUCFG18, L1D_SIZE, 6); | |
157 | env->cpucfg[18] = data; | |
158 | ||
159 | data = 0; | |
160 | data = FIELD_DP32(data, CPUCFG19, L2IU_WAYS, 15); | |
161 | data = FIELD_DP32(data, CPUCFG19, L2IU_SETS, 8); | |
162 | data = FIELD_DP32(data, CPUCFG19, L2IU_SIZE, 6); | |
163 | env->cpucfg[19] = data; | |
164 | ||
165 | data = 0; | |
166 | data = FIELD_DP32(data, CPUCFG20, L3IU_WAYS, 15); | |
167 | data = FIELD_DP32(data, CPUCFG20, L3IU_SETS, 14); | |
168 | data = FIELD_DP32(data, CPUCFG20, L3IU_SETS, 6); | |
169 | env->cpucfg[20] = data; | |
170 | } | |
171 | ||
172 | static void loongarch_cpu_list_entry(gpointer data, gpointer user_data) | |
173 | { | |
174 | const char *typename = object_class_get_name(OBJECT_CLASS(data)); | |
175 | ||
176 | qemu_printf("%s\n", typename); | |
177 | } | |
178 | ||
179 | void loongarch_cpu_list(void) | |
180 | { | |
181 | GSList *list; | |
182 | list = object_class_get_list_sorted(TYPE_LOONGARCH_CPU, false); | |
183 | g_slist_foreach(list, loongarch_cpu_list_entry, NULL); | |
184 | g_slist_free(list); | |
185 | } | |
186 | ||
187 | static void loongarch_cpu_reset(DeviceState *dev) | |
188 | { | |
189 | CPUState *cs = CPU(dev); | |
190 | LoongArchCPU *cpu = LOONGARCH_CPU(cs); | |
191 | LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(cpu); | |
192 | CPULoongArchState *env = &cpu->env; | |
193 | ||
194 | lacc->parent_reset(dev); | |
195 | ||
196 | env->fcsr0_mask = FCSR0_M1 | FCSR0_M2 | FCSR0_M3; | |
197 | env->fcsr0 = 0x0; | |
198 | ||
199 | restore_fp_status(env); | |
200 | cs->exception_index = -1; | |
201 | } | |
202 | ||
203 | static void loongarch_cpu_disas_set_info(CPUState *s, disassemble_info *info) | |
204 | { | |
205 | info->print_insn = print_insn_loongarch; | |
206 | } | |
207 | ||
208 | static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp) | |
209 | { | |
210 | CPUState *cs = CPU(dev); | |
211 | LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(dev); | |
212 | Error *local_err = NULL; | |
213 | ||
214 | cpu_exec_realizefn(cs, &local_err); | |
215 | if (local_err != NULL) { | |
216 | error_propagate(errp, local_err); | |
217 | return; | |
218 | } | |
219 | ||
220 | cpu_reset(cs); | |
221 | qemu_init_vcpu(cs); | |
222 | ||
223 | lacc->parent_realize(dev, errp); | |
224 | } | |
225 | ||
226 | static void loongarch_cpu_init(Object *obj) | |
227 | { | |
228 | LoongArchCPU *cpu = LOONGARCH_CPU(obj); | |
229 | ||
230 | cpu_set_cpustate_pointers(cpu); | |
231 | } | |
232 | ||
233 | static ObjectClass *loongarch_cpu_class_by_name(const char *cpu_model) | |
234 | { | |
235 | ObjectClass *oc; | |
236 | char *typename; | |
237 | ||
238 | typename = g_strdup_printf(LOONGARCH_CPU_TYPE_NAME("%s"), cpu_model); | |
239 | oc = object_class_by_name(typename); | |
240 | g_free(typename); | |
241 | return oc; | |
242 | } | |
243 | ||
244 | void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) | |
245 | { | |
246 | LoongArchCPU *cpu = LOONGARCH_CPU(cs); | |
247 | CPULoongArchState *env = &cpu->env; | |
248 | int i; | |
249 | ||
250 | qemu_fprintf(f, " PC=%016" PRIx64 " ", env->pc); | |
251 | qemu_fprintf(f, " FCSR0 0x%08x fp_status 0x%02x\n", env->fcsr0, | |
252 | get_float_exception_flags(&env->fp_status)); | |
253 | ||
254 | /* gpr */ | |
255 | for (i = 0; i < 32; i++) { | |
256 | if ((i & 3) == 0) { | |
257 | qemu_fprintf(f, " GPR%02d:", i); | |
258 | } | |
259 | qemu_fprintf(f, " %s %016" PRIx64, regnames[i], env->gpr[i]); | |
260 | if ((i & 3) == 3) { | |
261 | qemu_fprintf(f, "\n"); | |
262 | } | |
263 | } | |
264 | ||
265 | /* fpr */ | |
266 | if (flags & CPU_DUMP_FPU) { | |
267 | for (i = 0; i < 32; i++) { | |
268 | qemu_fprintf(f, " %s %016" PRIx64, fregnames[i], env->fpr[i]); | |
269 | if ((i & 3) == 3) { | |
270 | qemu_fprintf(f, "\n"); | |
271 | } | |
272 | } | |
273 | } | |
274 | } | |
275 | ||
276 | #ifdef CONFIG_TCG | |
277 | #include "hw/core/tcg-cpu-ops.h" | |
278 | ||
279 | static struct TCGCPUOps loongarch_tcg_ops = { | |
280 | .initialize = loongarch_translate_init, | |
281 | .synchronize_from_tb = loongarch_cpu_synchronize_from_tb, | |
282 | }; | |
283 | #endif /* CONFIG_TCG */ | |
284 | ||
285 | static void loongarch_cpu_class_init(ObjectClass *c, void *data) | |
286 | { | |
287 | LoongArchCPUClass *lacc = LOONGARCH_CPU_CLASS(c); | |
288 | CPUClass *cc = CPU_CLASS(c); | |
289 | DeviceClass *dc = DEVICE_CLASS(c); | |
290 | ||
291 | device_class_set_parent_realize(dc, loongarch_cpu_realizefn, | |
292 | &lacc->parent_realize); | |
293 | device_class_set_parent_reset(dc, loongarch_cpu_reset, &lacc->parent_reset); | |
294 | ||
295 | cc->class_by_name = loongarch_cpu_class_by_name; | |
296 | cc->dump_state = loongarch_cpu_dump_state; | |
297 | cc->set_pc = loongarch_cpu_set_pc; | |
298 | cc->disas_set_info = loongarch_cpu_disas_set_info; | |
299 | #ifdef CONFIG_TCG | |
300 | cc->tcg_ops = &loongarch_tcg_ops; | |
301 | #endif | |
302 | } | |
303 | ||
304 | #define DEFINE_LOONGARCH_CPU_TYPE(model, initfn) \ | |
305 | { \ | |
306 | .parent = TYPE_LOONGARCH_CPU, \ | |
307 | .instance_init = initfn, \ | |
308 | .name = LOONGARCH_CPU_TYPE_NAME(model), \ | |
309 | } | |
310 | ||
311 | static const TypeInfo loongarch_cpu_type_infos[] = { | |
312 | { | |
313 | .name = TYPE_LOONGARCH_CPU, | |
314 | .parent = TYPE_CPU, | |
315 | .instance_size = sizeof(LoongArchCPU), | |
316 | .instance_init = loongarch_cpu_init, | |
317 | ||
318 | .abstract = true, | |
319 | .class_size = sizeof(LoongArchCPUClass), | |
320 | .class_init = loongarch_cpu_class_init, | |
321 | }, | |
322 | DEFINE_LOONGARCH_CPU_TYPE("la464", loongarch_la464_initfn), | |
323 | }; | |
324 | ||
325 | DEFINE_TYPES(loongarch_cpu_type_infos) |