]>
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" | |
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 | cs->exception_index = -1; | |
200 | } | |
201 | ||
202 | static void loongarch_cpu_disas_set_info(CPUState *s, disassemble_info *info) | |
203 | { | |
204 | info->print_insn = print_insn_loongarch; | |
205 | } | |
206 | ||
207 | static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp) | |
208 | { | |
209 | CPUState *cs = CPU(dev); | |
210 | LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(dev); | |
211 | Error *local_err = NULL; | |
212 | ||
213 | cpu_exec_realizefn(cs, &local_err); | |
214 | if (local_err != NULL) { | |
215 | error_propagate(errp, local_err); | |
216 | return; | |
217 | } | |
218 | ||
219 | cpu_reset(cs); | |
220 | qemu_init_vcpu(cs); | |
221 | ||
222 | lacc->parent_realize(dev, errp); | |
223 | } | |
224 | ||
225 | static void loongarch_cpu_init(Object *obj) | |
226 | { | |
227 | LoongArchCPU *cpu = LOONGARCH_CPU(obj); | |
228 | ||
229 | cpu_set_cpustate_pointers(cpu); | |
230 | } | |
231 | ||
232 | static ObjectClass *loongarch_cpu_class_by_name(const char *cpu_model) | |
233 | { | |
234 | ObjectClass *oc; | |
235 | char *typename; | |
236 | ||
237 | typename = g_strdup_printf(LOONGARCH_CPU_TYPE_NAME("%s"), cpu_model); | |
238 | oc = object_class_by_name(typename); | |
239 | g_free(typename); | |
240 | return oc; | |
241 | } | |
242 | ||
243 | void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) | |
244 | { | |
245 | LoongArchCPU *cpu = LOONGARCH_CPU(cs); | |
246 | CPULoongArchState *env = &cpu->env; | |
247 | int i; | |
248 | ||
249 | qemu_fprintf(f, " PC=%016" PRIx64 " ", env->pc); | |
250 | qemu_fprintf(f, " FCSR0 0x%08x fp_status 0x%02x\n", env->fcsr0, | |
251 | get_float_exception_flags(&env->fp_status)); | |
252 | ||
253 | /* gpr */ | |
254 | for (i = 0; i < 32; i++) { | |
255 | if ((i & 3) == 0) { | |
256 | qemu_fprintf(f, " GPR%02d:", i); | |
257 | } | |
258 | qemu_fprintf(f, " %s %016" PRIx64, regnames[i], env->gpr[i]); | |
259 | if ((i & 3) == 3) { | |
260 | qemu_fprintf(f, "\n"); | |
261 | } | |
262 | } | |
263 | ||
264 | /* fpr */ | |
265 | if (flags & CPU_DUMP_FPU) { | |
266 | for (i = 0; i < 32; i++) { | |
267 | qemu_fprintf(f, " %s %016" PRIx64, fregnames[i], env->fpr[i]); | |
268 | if ((i & 3) == 3) { | |
269 | qemu_fprintf(f, "\n"); | |
270 | } | |
271 | } | |
272 | } | |
273 | } | |
274 | ||
275 | #ifdef CONFIG_TCG | |
276 | #include "hw/core/tcg-cpu-ops.h" | |
277 | ||
278 | static struct TCGCPUOps loongarch_tcg_ops = { | |
279 | .initialize = loongarch_translate_init, | |
280 | .synchronize_from_tb = loongarch_cpu_synchronize_from_tb, | |
281 | }; | |
282 | #endif /* CONFIG_TCG */ | |
283 | ||
284 | static void loongarch_cpu_class_init(ObjectClass *c, void *data) | |
285 | { | |
286 | LoongArchCPUClass *lacc = LOONGARCH_CPU_CLASS(c); | |
287 | CPUClass *cc = CPU_CLASS(c); | |
288 | DeviceClass *dc = DEVICE_CLASS(c); | |
289 | ||
290 | device_class_set_parent_realize(dc, loongarch_cpu_realizefn, | |
291 | &lacc->parent_realize); | |
292 | device_class_set_parent_reset(dc, loongarch_cpu_reset, &lacc->parent_reset); | |
293 | ||
294 | cc->class_by_name = loongarch_cpu_class_by_name; | |
295 | cc->dump_state = loongarch_cpu_dump_state; | |
296 | cc->set_pc = loongarch_cpu_set_pc; | |
297 | cc->disas_set_info = loongarch_cpu_disas_set_info; | |
298 | #ifdef CONFIG_TCG | |
299 | cc->tcg_ops = &loongarch_tcg_ops; | |
300 | #endif | |
301 | } | |
302 | ||
303 | #define DEFINE_LOONGARCH_CPU_TYPE(model, initfn) \ | |
304 | { \ | |
305 | .parent = TYPE_LOONGARCH_CPU, \ | |
306 | .instance_init = initfn, \ | |
307 | .name = LOONGARCH_CPU_TYPE_NAME(model), \ | |
308 | } | |
309 | ||
310 | static const TypeInfo loongarch_cpu_type_infos[] = { | |
311 | { | |
312 | .name = TYPE_LOONGARCH_CPU, | |
313 | .parent = TYPE_CPU, | |
314 | .instance_size = sizeof(LoongArchCPU), | |
315 | .instance_init = loongarch_cpu_init, | |
316 | ||
317 | .abstract = true, | |
318 | .class_size = sizeof(LoongArchCPUClass), | |
319 | .class_init = loongarch_cpu_class_init, | |
320 | }, | |
321 | DEFINE_LOONGARCH_CPU_TYPE("la464", loongarch_la464_initfn), | |
322 | }; | |
323 | ||
324 | DEFINE_TYPES(loongarch_cpu_type_infos) |