]>
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" |
f84a2aac | 20 | #include "sysemu/reset.h" |
228021f0 SG |
21 | |
22 | const char * const regnames[32] = { | |
23 | "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", | |
24 | "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", | |
25 | "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", | |
26 | "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", | |
27 | }; | |
28 | ||
29 | const char * const fregnames[32] = { | |
30 | "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", | |
31 | "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", | |
32 | "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", | |
33 | "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", | |
34 | }; | |
35 | ||
36 | static const char * const excp_names[] = { | |
37 | [EXCCODE_INT] = "Interrupt", | |
38 | [EXCCODE_PIL] = "Page invalid exception for load", | |
39 | [EXCCODE_PIS] = "Page invalid exception for store", | |
40 | [EXCCODE_PIF] = "Page invalid exception for fetch", | |
41 | [EXCCODE_PME] = "Page modified exception", | |
42 | [EXCCODE_PNR] = "Page Not Readable exception", | |
43 | [EXCCODE_PNX] = "Page Not Executable exception", | |
44 | [EXCCODE_PPI] = "Page Privilege error", | |
45 | [EXCCODE_ADEF] = "Address error for instruction fetch", | |
46 | [EXCCODE_ADEM] = "Address error for Memory access", | |
47 | [EXCCODE_SYS] = "Syscall", | |
48 | [EXCCODE_BRK] = "Break", | |
49 | [EXCCODE_INE] = "Instruction Non-Existent", | |
50 | [EXCCODE_IPE] = "Instruction privilege error", | |
51 | [EXCCODE_FPE] = "Floating Point Exception", | |
52 | [EXCCODE_DBP] = "Debug breakpoint", | |
7fe7eea6 | 53 | [EXCCODE_BCE] = "Bound Check Exception", |
228021f0 SG |
54 | }; |
55 | ||
56 | const char *loongarch_exception_name(int32_t exception) | |
57 | { | |
58 | assert(excp_names[exception]); | |
59 | return excp_names[exception]; | |
60 | } | |
61 | ||
62 | void G_NORETURN do_raise_exception(CPULoongArchState *env, | |
63 | uint32_t exception, | |
64 | uintptr_t pc) | |
65 | { | |
66 | CPUState *cs = env_cpu(env); | |
67 | ||
68 | qemu_log_mask(CPU_LOG_INT, "%s: %d (%s)\n", | |
69 | __func__, | |
70 | exception, | |
71 | loongarch_exception_name(exception)); | |
72 | cs->exception_index = exception; | |
73 | ||
74 | cpu_loop_exit_restore(cs, pc); | |
75 | } | |
76 | ||
77 | static void loongarch_cpu_set_pc(CPUState *cs, vaddr value) | |
78 | { | |
79 | LoongArchCPU *cpu = LOONGARCH_CPU(cs); | |
80 | CPULoongArchState *env = &cpu->env; | |
81 | ||
82 | env->pc = value; | |
83 | } | |
84 | ||
a8a506c3 XY |
85 | #include "hw/loongarch/virt.h" |
86 | ||
f757a2cd XY |
87 | void loongarch_cpu_set_irq(void *opaque, int irq, int level) |
88 | { | |
89 | LoongArchCPU *cpu = opaque; | |
90 | CPULoongArchState *env = &cpu->env; | |
91 | CPUState *cs = CPU(cpu); | |
92 | ||
93 | if (irq < 0 || irq >= N_IRQS) { | |
94 | return; | |
95 | } | |
96 | ||
97 | env->CSR_ESTAT = deposit64(env->CSR_ESTAT, irq, 1, level != 0); | |
98 | ||
99 | if (FIELD_EX64(env->CSR_ESTAT, CSR_ESTAT, IS)) { | |
100 | cpu_interrupt(cs, CPU_INTERRUPT_HARD); | |
101 | } else { | |
102 | cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); | |
103 | } | |
104 | } | |
105 | ||
106 | static inline bool cpu_loongarch_hw_interrupts_enabled(CPULoongArchState *env) | |
107 | { | |
108 | bool ret = 0; | |
109 | ||
110 | ret = (FIELD_EX64(env->CSR_CRMD, CSR_CRMD, IE) && | |
111 | !(FIELD_EX64(env->CSR_DBG, CSR_DBG, DST))); | |
112 | ||
113 | return ret; | |
114 | } | |
115 | ||
116 | /* Check if there is pending and not masked out interrupt */ | |
117 | static inline bool cpu_loongarch_hw_interrupts_pending(CPULoongArchState *env) | |
118 | { | |
119 | uint32_t pending; | |
120 | uint32_t status; | |
121 | bool r; | |
122 | ||
123 | pending = FIELD_EX64(env->CSR_ESTAT, CSR_ESTAT, IS); | |
124 | status = FIELD_EX64(env->CSR_ECFG, CSR_ECFG, LIE); | |
125 | ||
126 | r = (pending & status) != 0; | |
127 | return r; | |
128 | } | |
129 | ||
130 | static void loongarch_cpu_do_interrupt(CPUState *cs) | |
131 | { | |
132 | LoongArchCPU *cpu = LOONGARCH_CPU(cs); | |
133 | CPULoongArchState *env = &cpu->env; | |
134 | bool update_badinstr = 1; | |
135 | int cause = -1; | |
136 | const char *name; | |
137 | bool tlbfill = FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR); | |
138 | uint32_t vec_size = FIELD_EX64(env->CSR_ECFG, CSR_ECFG, VS); | |
139 | ||
140 | if (cs->exception_index != EXCCODE_INT) { | |
141 | if (cs->exception_index < 0 || | |
142 | cs->exception_index > ARRAY_SIZE(excp_names)) { | |
143 | name = "unknown"; | |
144 | } else { | |
145 | name = excp_names[cs->exception_index]; | |
146 | } | |
147 | ||
148 | qemu_log_mask(CPU_LOG_INT, | |
149 | "%s enter: pc " TARGET_FMT_lx " ERA " TARGET_FMT_lx | |
150 | " TLBRERA " TARGET_FMT_lx " %s exception\n", __func__, | |
151 | env->pc, env->CSR_ERA, env->CSR_TLBRERA, name); | |
152 | } | |
153 | ||
154 | switch (cs->exception_index) { | |
155 | case EXCCODE_DBP: | |
156 | env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, DCL, 1); | |
157 | env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, ECODE, 0xC); | |
158 | goto set_DERA; | |
159 | set_DERA: | |
160 | env->CSR_DERA = env->pc; | |
161 | env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, DST, 1); | |
162 | env->pc = env->CSR_EENTRY + 0x480; | |
163 | break; | |
164 | case EXCCODE_INT: | |
165 | if (FIELD_EX64(env->CSR_DBG, CSR_DBG, DST)) { | |
166 | env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, DEI, 1); | |
167 | goto set_DERA; | |
168 | } | |
169 | QEMU_FALLTHROUGH; | |
170 | case EXCCODE_PIF: | |
171 | cause = cs->exception_index; | |
172 | update_badinstr = 0; | |
173 | break; | |
f757a2cd XY |
174 | case EXCCODE_SYS: |
175 | case EXCCODE_BRK: | |
7d552f0e SG |
176 | case EXCCODE_INE: |
177 | case EXCCODE_IPE: | |
178 | case EXCCODE_FPE: | |
7fe7eea6 | 179 | case EXCCODE_BCE: |
7d552f0e SG |
180 | env->CSR_BADV = env->pc; |
181 | QEMU_FALLTHROUGH; | |
182 | case EXCCODE_ADEM: | |
f757a2cd XY |
183 | case EXCCODE_PIL: |
184 | case EXCCODE_PIS: | |
185 | case EXCCODE_PME: | |
186 | case EXCCODE_PNR: | |
187 | case EXCCODE_PNX: | |
188 | case EXCCODE_PPI: | |
f757a2cd XY |
189 | cause = cs->exception_index; |
190 | break; | |
191 | default: | |
192 | qemu_log("Error: exception(%d) '%s' has not been supported\n", | |
193 | cs->exception_index, excp_names[cs->exception_index]); | |
194 | abort(); | |
195 | } | |
196 | ||
197 | if (update_badinstr) { | |
198 | env->CSR_BADI = cpu_ldl_code(env, env->pc); | |
199 | } | |
200 | ||
201 | /* Save PLV and IE */ | |
202 | if (tlbfill) { | |
203 | env->CSR_TLBRPRMD = FIELD_DP64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PPLV, | |
204 | FIELD_EX64(env->CSR_CRMD, | |
205 | CSR_CRMD, PLV)); | |
206 | env->CSR_TLBRPRMD = FIELD_DP64(env->CSR_TLBRPRMD, CSR_TLBRPRMD, PIE, | |
207 | FIELD_EX64(env->CSR_CRMD, CSR_CRMD, IE)); | |
208 | /* set the DA mode */ | |
209 | env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DA, 1); | |
210 | env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PG, 0); | |
211 | env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, | |
212 | PC, (env->pc >> 2)); | |
213 | } else { | |
214 | env->CSR_ESTAT = FIELD_DP64(env->CSR_ESTAT, CSR_ESTAT, ECODE, cause); | |
215 | env->CSR_PRMD = FIELD_DP64(env->CSR_PRMD, CSR_PRMD, PPLV, | |
216 | FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PLV)); | |
217 | env->CSR_PRMD = FIELD_DP64(env->CSR_PRMD, CSR_PRMD, PIE, | |
218 | FIELD_EX64(env->CSR_CRMD, CSR_CRMD, IE)); | |
219 | env->CSR_ERA = env->pc; | |
220 | } | |
221 | ||
222 | env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PLV, 0); | |
223 | env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, IE, 0); | |
224 | ||
225 | if (cs->exception_index == EXCCODE_INT) { | |
226 | /* Interrupt */ | |
227 | uint32_t vector = 0; | |
228 | uint32_t pending = FIELD_EX64(env->CSR_ESTAT, CSR_ESTAT, IS); | |
229 | pending &= FIELD_EX64(env->CSR_ECFG, CSR_ECFG, LIE); | |
230 | ||
231 | /* Find the highest-priority interrupt. */ | |
232 | vector = 31 - clz32(pending); | |
233 | env->pc = env->CSR_EENTRY + (EXCCODE_EXTERNAL_INT + vector) * vec_size; | |
234 | qemu_log_mask(CPU_LOG_INT, | |
235 | "%s: PC " TARGET_FMT_lx " ERA " TARGET_FMT_lx | |
236 | " cause %d\n" " A " TARGET_FMT_lx " D " | |
237 | TARGET_FMT_lx " vector = %d ExC " TARGET_FMT_lx "ExS" | |
238 | TARGET_FMT_lx "\n", | |
239 | __func__, env->pc, env->CSR_ERA, | |
240 | cause, env->CSR_BADV, env->CSR_DERA, vector, | |
241 | env->CSR_ECFG, env->CSR_ESTAT); | |
242 | } else { | |
243 | if (tlbfill) { | |
244 | env->pc = env->CSR_TLBRENTRY; | |
245 | } else { | |
246 | env->pc = env->CSR_EENTRY; | |
247 | env->pc += cause * vec_size; | |
248 | } | |
249 | qemu_log_mask(CPU_LOG_INT, | |
250 | "%s: PC " TARGET_FMT_lx " ERA " TARGET_FMT_lx | |
251 | " cause %d%s\n, ESTAT " TARGET_FMT_lx | |
252 | " EXCFG " TARGET_FMT_lx " BADVA " TARGET_FMT_lx | |
253 | "BADI " TARGET_FMT_lx " SYS_NUM " TARGET_FMT_lu | |
254 | " cpu %d asid " TARGET_FMT_lx "\n", __func__, env->pc, | |
255 | tlbfill ? env->CSR_TLBRERA : env->CSR_ERA, | |
256 | cause, tlbfill ? "(refill)" : "", env->CSR_ESTAT, | |
257 | env->CSR_ECFG, | |
258 | tlbfill ? env->CSR_TLBRBADV : env->CSR_BADV, | |
259 | env->CSR_BADI, env->gpr[11], cs->cpu_index, | |
260 | env->CSR_ASID); | |
261 | } | |
262 | cs->exception_index = -1; | |
263 | } | |
264 | ||
265 | static void loongarch_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, | |
266 | vaddr addr, unsigned size, | |
267 | MMUAccessType access_type, | |
268 | int mmu_idx, MemTxAttrs attrs, | |
269 | MemTxResult response, | |
270 | uintptr_t retaddr) | |
271 | { | |
272 | LoongArchCPU *cpu = LOONGARCH_CPU(cs); | |
273 | CPULoongArchState *env = &cpu->env; | |
274 | ||
275 | if (access_type == MMU_INST_FETCH) { | |
276 | do_raise_exception(env, EXCCODE_ADEF, retaddr); | |
277 | } else { | |
278 | do_raise_exception(env, EXCCODE_ADEM, retaddr); | |
279 | } | |
280 | } | |
281 | ||
282 | static bool loongarch_cpu_exec_interrupt(CPUState *cs, int interrupt_request) | |
283 | { | |
284 | if (interrupt_request & CPU_INTERRUPT_HARD) { | |
285 | LoongArchCPU *cpu = LOONGARCH_CPU(cs); | |
286 | CPULoongArchState *env = &cpu->env; | |
287 | ||
288 | if (cpu_loongarch_hw_interrupts_enabled(env) && | |
289 | cpu_loongarch_hw_interrupts_pending(env)) { | |
290 | /* Raise it */ | |
291 | cs->exception_index = EXCCODE_INT; | |
292 | loongarch_cpu_do_interrupt(cs); | |
293 | return true; | |
294 | } | |
295 | } | |
296 | return false; | |
297 | } | |
298 | ||
228021f0 SG |
299 | #ifdef CONFIG_TCG |
300 | static void loongarch_cpu_synchronize_from_tb(CPUState *cs, | |
301 | const TranslationBlock *tb) | |
302 | { | |
303 | LoongArchCPU *cpu = LOONGARCH_CPU(cs); | |
304 | CPULoongArchState *env = &cpu->env; | |
305 | ||
306 | env->pc = tb->pc; | |
307 | } | |
308 | #endif /* CONFIG_TCG */ | |
309 | ||
f757a2cd XY |
310 | static bool loongarch_cpu_has_work(CPUState *cs) |
311 | { | |
312 | LoongArchCPU *cpu = LOONGARCH_CPU(cs); | |
313 | CPULoongArchState *env = &cpu->env; | |
314 | bool has_work = false; | |
315 | ||
316 | if ((cs->interrupt_request & CPU_INTERRUPT_HARD) && | |
317 | cpu_loongarch_hw_interrupts_pending(env)) { | |
318 | has_work = true; | |
319 | } | |
320 | ||
321 | return has_work; | |
322 | } | |
323 | ||
228021f0 SG |
324 | static void loongarch_la464_initfn(Object *obj) |
325 | { | |
326 | LoongArchCPU *cpu = LOONGARCH_CPU(obj); | |
327 | CPULoongArchState *env = &cpu->env; | |
328 | int i; | |
329 | ||
330 | for (i = 0; i < 21; i++) { | |
331 | env->cpucfg[i] = 0x0; | |
332 | } | |
333 | ||
334 | env->cpucfg[0] = 0x14c010; /* PRID */ | |
335 | ||
336 | uint32_t data = 0; | |
337 | data = FIELD_DP32(data, CPUCFG1, ARCH, 2); | |
338 | data = FIELD_DP32(data, CPUCFG1, PGMMU, 1); | |
339 | data = FIELD_DP32(data, CPUCFG1, IOCSR, 1); | |
340 | data = FIELD_DP32(data, CPUCFG1, PALEN, 0x2f); | |
341 | data = FIELD_DP32(data, CPUCFG1, VALEN, 0x2f); | |
342 | data = FIELD_DP32(data, CPUCFG1, UAL, 1); | |
343 | data = FIELD_DP32(data, CPUCFG1, RI, 1); | |
344 | data = FIELD_DP32(data, CPUCFG1, EP, 1); | |
345 | data = FIELD_DP32(data, CPUCFG1, RPLV, 1); | |
346 | data = FIELD_DP32(data, CPUCFG1, HP, 1); | |
347 | data = FIELD_DP32(data, CPUCFG1, IOCSR_BRD, 1); | |
348 | env->cpucfg[1] = data; | |
349 | ||
350 | data = 0; | |
351 | data = FIELD_DP32(data, CPUCFG2, FP, 1); | |
352 | data = FIELD_DP32(data, CPUCFG2, FP_SP, 1); | |
353 | data = FIELD_DP32(data, CPUCFG2, FP_DP, 1); | |
354 | data = FIELD_DP32(data, CPUCFG2, FP_VER, 1); | |
355 | data = FIELD_DP32(data, CPUCFG2, LLFTP, 1); | |
356 | data = FIELD_DP32(data, CPUCFG2, LLFTP_VER, 1); | |
357 | data = FIELD_DP32(data, CPUCFG2, LAM, 1); | |
358 | env->cpucfg[2] = data; | |
359 | ||
360 | env->cpucfg[4] = 100 * 1000 * 1000; /* Crystal frequency */ | |
361 | ||
362 | data = 0; | |
363 | data = FIELD_DP32(data, CPUCFG5, CC_MUL, 1); | |
364 | data = FIELD_DP32(data, CPUCFG5, CC_DIV, 1); | |
365 | env->cpucfg[5] = data; | |
366 | ||
367 | data = 0; | |
368 | data = FIELD_DP32(data, CPUCFG16, L1_IUPRE, 1); | |
369 | data = FIELD_DP32(data, CPUCFG16, L1_DPRE, 1); | |
370 | data = FIELD_DP32(data, CPUCFG16, L2_IUPRE, 1); | |
371 | data = FIELD_DP32(data, CPUCFG16, L2_IUUNIFY, 1); | |
372 | data = FIELD_DP32(data, CPUCFG16, L2_IUPRIV, 1); | |
373 | data = FIELD_DP32(data, CPUCFG16, L3_IUPRE, 1); | |
374 | data = FIELD_DP32(data, CPUCFG16, L3_IUUNIFY, 1); | |
375 | data = FIELD_DP32(data, CPUCFG16, L3_IUINCL, 1); | |
376 | env->cpucfg[16] = data; | |
377 | ||
378 | data = 0; | |
379 | data = FIELD_DP32(data, CPUCFG17, L1IU_WAYS, 3); | |
380 | data = FIELD_DP32(data, CPUCFG17, L1IU_SETS, 8); | |
381 | data = FIELD_DP32(data, CPUCFG17, L1IU_SIZE, 6); | |
382 | env->cpucfg[17] = data; | |
383 | ||
384 | data = 0; | |
385 | data = FIELD_DP32(data, CPUCFG18, L1D_WAYS, 3); | |
386 | data = FIELD_DP32(data, CPUCFG18, L1D_SETS, 8); | |
387 | data = FIELD_DP32(data, CPUCFG18, L1D_SIZE, 6); | |
388 | env->cpucfg[18] = data; | |
389 | ||
390 | data = 0; | |
391 | data = FIELD_DP32(data, CPUCFG19, L2IU_WAYS, 15); | |
392 | data = FIELD_DP32(data, CPUCFG19, L2IU_SETS, 8); | |
393 | data = FIELD_DP32(data, CPUCFG19, L2IU_SIZE, 6); | |
394 | env->cpucfg[19] = data; | |
395 | ||
396 | data = 0; | |
397 | data = FIELD_DP32(data, CPUCFG20, L3IU_WAYS, 15); | |
398 | data = FIELD_DP32(data, CPUCFG20, L3IU_SETS, 14); | |
399 | data = FIELD_DP32(data, CPUCFG20, L3IU_SETS, 6); | |
400 | env->cpucfg[20] = data; | |
398cecb9 XY |
401 | |
402 | env->CSR_ASID = FIELD_DP64(0, CSR_ASID, ASIDBITS, 0xa); | |
228021f0 SG |
403 | } |
404 | ||
405 | static void loongarch_cpu_list_entry(gpointer data, gpointer user_data) | |
406 | { | |
407 | const char *typename = object_class_get_name(OBJECT_CLASS(data)); | |
408 | ||
409 | qemu_printf("%s\n", typename); | |
410 | } | |
411 | ||
412 | void loongarch_cpu_list(void) | |
413 | { | |
414 | GSList *list; | |
415 | list = object_class_get_list_sorted(TYPE_LOONGARCH_CPU, false); | |
416 | g_slist_foreach(list, loongarch_cpu_list_entry, NULL); | |
417 | g_slist_free(list); | |
418 | } | |
419 | ||
420 | static void loongarch_cpu_reset(DeviceState *dev) | |
421 | { | |
422 | CPUState *cs = CPU(dev); | |
423 | LoongArchCPU *cpu = LOONGARCH_CPU(cs); | |
424 | LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(cpu); | |
425 | CPULoongArchState *env = &cpu->env; | |
426 | ||
427 | lacc->parent_reset(dev); | |
428 | ||
429 | env->fcsr0_mask = FCSR0_M1 | FCSR0_M2 | FCSR0_M3; | |
430 | env->fcsr0 = 0x0; | |
431 | ||
398cecb9 XY |
432 | int n; |
433 | /* Set csr registers value after reset */ | |
434 | env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PLV, 0); | |
435 | env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, IE, 0); | |
436 | env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DA, 1); | |
437 | env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, PG, 0); | |
438 | env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DATF, 1); | |
439 | env->CSR_CRMD = FIELD_DP64(env->CSR_CRMD, CSR_CRMD, DATM, 1); | |
440 | ||
441 | env->CSR_EUEN = FIELD_DP64(env->CSR_EUEN, CSR_EUEN, FPE, 0); | |
442 | env->CSR_EUEN = FIELD_DP64(env->CSR_EUEN, CSR_EUEN, SXE, 0); | |
443 | env->CSR_EUEN = FIELD_DP64(env->CSR_EUEN, CSR_EUEN, ASXE, 0); | |
444 | env->CSR_EUEN = FIELD_DP64(env->CSR_EUEN, CSR_EUEN, BTE, 0); | |
445 | ||
446 | env->CSR_MISC = 0; | |
447 | ||
448 | env->CSR_ECFG = FIELD_DP64(env->CSR_ECFG, CSR_ECFG, VS, 0); | |
449 | env->CSR_ECFG = FIELD_DP64(env->CSR_ECFG, CSR_ECFG, LIE, 0); | |
450 | ||
451 | env->CSR_ESTAT = env->CSR_ESTAT & (~MAKE_64BIT_MASK(0, 2)); | |
452 | env->CSR_RVACFG = FIELD_DP64(env->CSR_RVACFG, CSR_RVACFG, RBITS, 0); | |
453 | env->CSR_TCFG = FIELD_DP64(env->CSR_TCFG, CSR_TCFG, EN, 0); | |
454 | env->CSR_LLBCTL = FIELD_DP64(env->CSR_LLBCTL, CSR_LLBCTL, KLO, 0); | |
455 | env->CSR_TLBRERA = FIELD_DP64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR, 0); | |
456 | env->CSR_MERRCTL = FIELD_DP64(env->CSR_MERRCTL, CSR_MERRCTL, ISMERR, 0); | |
457 | ||
458 | env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, TLB_TYPE, 2); | |
459 | env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, MTLB_ENTRY, 63); | |
460 | env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_WAYS, 7); | |
461 | env->CSR_PRCFG3 = FIELD_DP64(env->CSR_PRCFG3, CSR_PRCFG3, STLB_SETS, 8); | |
462 | ||
463 | for (n = 0; n < 4; n++) { | |
464 | env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV0, 0); | |
465 | env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV1, 0); | |
466 | env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV2, 0); | |
467 | env->CSR_DMW[n] = FIELD_DP64(env->CSR_DMW[n], CSR_DMW, PLV3, 0); | |
468 | } | |
469 | ||
f757a2cd XY |
470 | env->pc = 0x1c000000; |
471 | ||
d578ca6c | 472 | restore_fp_status(env); |
228021f0 SG |
473 | cs->exception_index = -1; |
474 | } | |
475 | ||
476 | static void loongarch_cpu_disas_set_info(CPUState *s, disassemble_info *info) | |
477 | { | |
478 | info->print_insn = print_insn_loongarch; | |
479 | } | |
480 | ||
481 | static void loongarch_cpu_realizefn(DeviceState *dev, Error **errp) | |
482 | { | |
483 | CPUState *cs = CPU(dev); | |
484 | LoongArchCPUClass *lacc = LOONGARCH_CPU_GET_CLASS(dev); | |
485 | Error *local_err = NULL; | |
486 | ||
487 | cpu_exec_realizefn(cs, &local_err); | |
488 | if (local_err != NULL) { | |
489 | error_propagate(errp, local_err); | |
490 | return; | |
491 | } | |
492 | ||
ca61e750 XY |
493 | loongarch_cpu_register_gdb_regs_for_features(cs); |
494 | ||
228021f0 SG |
495 | cpu_reset(cs); |
496 | qemu_init_vcpu(cs); | |
497 | ||
498 | lacc->parent_realize(dev, errp); | |
499 | } | |
500 | ||
f84a2aac XY |
501 | static void loongarch_qemu_write(void *opaque, hwaddr addr, |
502 | uint64_t val, unsigned size) | |
503 | { | |
504 | } | |
505 | ||
506 | static uint64_t loongarch_qemu_read(void *opaque, hwaddr addr, unsigned size) | |
507 | { | |
508 | switch (addr) { | |
509 | case FEATURE_REG: | |
510 | return 1ULL << IOCSRF_MSI | 1ULL << IOCSRF_EXTIOI | | |
511 | 1ULL << IOCSRF_CSRIPI; | |
512 | case VENDOR_REG: | |
513 | return 0x6e6f73676e6f6f4cULL; /* "Loongson" */ | |
514 | case CPUNAME_REG: | |
515 | return 0x303030354133ULL; /* "3A5000" */ | |
516 | case MISC_FUNC_REG: | |
517 | return 1ULL << IOCSRM_EXTIOI_EN; | |
518 | } | |
519 | return 0ULL; | |
520 | } | |
521 | ||
522 | static const MemoryRegionOps loongarch_qemu_ops = { | |
523 | .read = loongarch_qemu_read, | |
524 | .write = loongarch_qemu_write, | |
525 | .endianness = DEVICE_LITTLE_ENDIAN, | |
526 | .valid = { | |
527 | .min_access_size = 4, | |
528 | .max_access_size = 8, | |
529 | }, | |
530 | .impl = { | |
531 | .min_access_size = 8, | |
532 | .max_access_size = 8, | |
533 | }, | |
534 | }; | |
535 | ||
228021f0 SG |
536 | static void loongarch_cpu_init(Object *obj) |
537 | { | |
538 | LoongArchCPU *cpu = LOONGARCH_CPU(obj); | |
f84a2aac | 539 | CPULoongArchState *env = &cpu->env; |
228021f0 SG |
540 | |
541 | cpu_set_cpustate_pointers(cpu); | |
f757a2cd | 542 | qdev_init_gpio_in(DEVICE(cpu), loongarch_cpu_set_irq, N_IRQS); |
dd615fa4 XY |
543 | timer_init_ns(&cpu->timer, QEMU_CLOCK_VIRTUAL, |
544 | &loongarch_constant_timer_cb, cpu); | |
f84a2aac XY |
545 | memory_region_init_io(&env->system_iocsr, OBJECT(cpu), NULL, |
546 | env, "iocsr", UINT64_MAX); | |
547 | address_space_init(&env->address_space_iocsr, &env->system_iocsr, "IOCSR"); | |
548 | memory_region_init_io(&env->iocsr_mem, OBJECT(cpu), &loongarch_qemu_ops, | |
549 | NULL, "iocsr_misc", 0x428); | |
550 | memory_region_add_subregion(&env->system_iocsr, 0, &env->iocsr_mem); | |
228021f0 SG |
551 | } |
552 | ||
553 | static ObjectClass *loongarch_cpu_class_by_name(const char *cpu_model) | |
554 | { | |
555 | ObjectClass *oc; | |
556 | char *typename; | |
557 | ||
558 | typename = g_strdup_printf(LOONGARCH_CPU_TYPE_NAME("%s"), cpu_model); | |
559 | oc = object_class_by_name(typename); | |
560 | g_free(typename); | |
561 | return oc; | |
562 | } | |
563 | ||
564 | void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) | |
565 | { | |
566 | LoongArchCPU *cpu = LOONGARCH_CPU(cs); | |
567 | CPULoongArchState *env = &cpu->env; | |
568 | int i; | |
569 | ||
570 | qemu_fprintf(f, " PC=%016" PRIx64 " ", env->pc); | |
571 | qemu_fprintf(f, " FCSR0 0x%08x fp_status 0x%02x\n", env->fcsr0, | |
572 | get_float_exception_flags(&env->fp_status)); | |
573 | ||
574 | /* gpr */ | |
575 | for (i = 0; i < 32; i++) { | |
576 | if ((i & 3) == 0) { | |
577 | qemu_fprintf(f, " GPR%02d:", i); | |
578 | } | |
579 | qemu_fprintf(f, " %s %016" PRIx64, regnames[i], env->gpr[i]); | |
580 | if ((i & 3) == 3) { | |
581 | qemu_fprintf(f, "\n"); | |
582 | } | |
583 | } | |
584 | ||
7e1c521e XY |
585 | qemu_fprintf(f, "CRMD=%016" PRIx64 "\n", env->CSR_CRMD); |
586 | qemu_fprintf(f, "PRMD=%016" PRIx64 "\n", env->CSR_PRMD); | |
587 | qemu_fprintf(f, "EUEN=%016" PRIx64 "\n", env->CSR_EUEN); | |
588 | qemu_fprintf(f, "ESTAT=%016" PRIx64 "\n", env->CSR_ESTAT); | |
589 | qemu_fprintf(f, "ERA=%016" PRIx64 "\n", env->CSR_ERA); | |
590 | qemu_fprintf(f, "BADV=%016" PRIx64 "\n", env->CSR_BADV); | |
591 | qemu_fprintf(f, "BADI=%016" PRIx64 "\n", env->CSR_BADI); | |
592 | qemu_fprintf(f, "EENTRY=%016" PRIx64 "\n", env->CSR_EENTRY); | |
593 | qemu_fprintf(f, "PRCFG1=%016" PRIx64 ", PRCFG2=%016" PRIx64 "," | |
594 | " PRCFG3=%016" PRIx64 "\n", | |
595 | env->CSR_PRCFG1, env->CSR_PRCFG3, env->CSR_PRCFG3); | |
596 | qemu_fprintf(f, "TLBRENTRY=%016" PRIx64 "\n", env->CSR_TLBRENTRY); | |
597 | qemu_fprintf(f, "TLBRBADV=%016" PRIx64 "\n", env->CSR_TLBRBADV); | |
598 | qemu_fprintf(f, "TLBRERA=%016" PRIx64 "\n", env->CSR_TLBRERA); | |
599 | ||
228021f0 SG |
600 | /* fpr */ |
601 | if (flags & CPU_DUMP_FPU) { | |
602 | for (i = 0; i < 32; i++) { | |
603 | qemu_fprintf(f, " %s %016" PRIx64, fregnames[i], env->fpr[i]); | |
604 | if ((i & 3) == 3) { | |
605 | qemu_fprintf(f, "\n"); | |
606 | } | |
607 | } | |
608 | } | |
609 | } | |
610 | ||
611 | #ifdef CONFIG_TCG | |
612 | #include "hw/core/tcg-cpu-ops.h" | |
613 | ||
614 | static struct TCGCPUOps loongarch_tcg_ops = { | |
615 | .initialize = loongarch_translate_init, | |
616 | .synchronize_from_tb = loongarch_cpu_synchronize_from_tb, | |
7e1c521e XY |
617 | |
618 | .tlb_fill = loongarch_cpu_tlb_fill, | |
f757a2cd XY |
619 | .cpu_exec_interrupt = loongarch_cpu_exec_interrupt, |
620 | .do_interrupt = loongarch_cpu_do_interrupt, | |
621 | .do_transaction_failed = loongarch_cpu_do_transaction_failed, | |
228021f0 SG |
622 | }; |
623 | #endif /* CONFIG_TCG */ | |
624 | ||
7e1c521e XY |
625 | #include "hw/core/sysemu-cpu-ops.h" |
626 | ||
627 | static const struct SysemuCPUOps loongarch_sysemu_ops = { | |
628 | .get_phys_page_debug = loongarch_cpu_get_phys_page_debug, | |
629 | }; | |
630 | ||
228021f0 SG |
631 | static void loongarch_cpu_class_init(ObjectClass *c, void *data) |
632 | { | |
633 | LoongArchCPUClass *lacc = LOONGARCH_CPU_CLASS(c); | |
634 | CPUClass *cc = CPU_CLASS(c); | |
635 | DeviceClass *dc = DEVICE_CLASS(c); | |
636 | ||
637 | device_class_set_parent_realize(dc, loongarch_cpu_realizefn, | |
638 | &lacc->parent_realize); | |
639 | device_class_set_parent_reset(dc, loongarch_cpu_reset, &lacc->parent_reset); | |
640 | ||
641 | cc->class_by_name = loongarch_cpu_class_by_name; | |
f757a2cd | 642 | cc->has_work = loongarch_cpu_has_work; |
228021f0 SG |
643 | cc->dump_state = loongarch_cpu_dump_state; |
644 | cc->set_pc = loongarch_cpu_set_pc; | |
67ebd42a | 645 | dc->vmsd = &vmstate_loongarch_cpu; |
7e1c521e | 646 | cc->sysemu_ops = &loongarch_sysemu_ops; |
228021f0 | 647 | cc->disas_set_info = loongarch_cpu_disas_set_info; |
ca61e750 XY |
648 | cc->gdb_read_register = loongarch_cpu_gdb_read_register; |
649 | cc->gdb_write_register = loongarch_cpu_gdb_write_register; | |
650 | cc->disas_set_info = loongarch_cpu_disas_set_info; | |
651 | cc->gdb_num_core_regs = 34; | |
652 | cc->gdb_core_xml_file = "loongarch-base64.xml"; | |
653 | cc->gdb_stop_before_watchpoint = true; | |
654 | ||
228021f0 SG |
655 | #ifdef CONFIG_TCG |
656 | cc->tcg_ops = &loongarch_tcg_ops; | |
657 | #endif | |
658 | } | |
659 | ||
660 | #define DEFINE_LOONGARCH_CPU_TYPE(model, initfn) \ | |
661 | { \ | |
662 | .parent = TYPE_LOONGARCH_CPU, \ | |
663 | .instance_init = initfn, \ | |
664 | .name = LOONGARCH_CPU_TYPE_NAME(model), \ | |
665 | } | |
666 | ||
667 | static const TypeInfo loongarch_cpu_type_infos[] = { | |
668 | { | |
669 | .name = TYPE_LOONGARCH_CPU, | |
670 | .parent = TYPE_CPU, | |
671 | .instance_size = sizeof(LoongArchCPU), | |
672 | .instance_init = loongarch_cpu_init, | |
673 | ||
674 | .abstract = true, | |
675 | .class_size = sizeof(LoongArchCPUClass), | |
676 | .class_init = loongarch_cpu_class_init, | |
677 | }, | |
678 | DEFINE_LOONGARCH_CPU_TYPE("la464", loongarch_la464_initfn), | |
679 | }; | |
680 | ||
681 | DEFINE_TYPES(loongarch_cpu_type_infos) | |
425876f5 XY |
682 | |
683 | static void loongarch_cpu_add_definition(gpointer data, gpointer user_data) | |
684 | { | |
685 | ObjectClass *oc = data; | |
686 | CpuDefinitionInfoList **cpu_list = user_data; | |
687 | CpuDefinitionInfo *info = g_new0(CpuDefinitionInfo, 1); | |
688 | const char *typename = object_class_get_name(oc); | |
689 | ||
690 | info->name = g_strndup(typename, | |
691 | strlen(typename) - strlen("-" TYPE_LOONGARCH_CPU)); | |
692 | info->q_typename = g_strdup(typename); | |
693 | ||
694 | QAPI_LIST_PREPEND(*cpu_list, info); | |
695 | } | |
696 | ||
697 | CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) | |
698 | { | |
699 | CpuDefinitionInfoList *cpu_list = NULL; | |
700 | GSList *list; | |
701 | ||
702 | list = object_class_get_list(TYPE_LOONGARCH_CPU, false); | |
703 | g_slist_foreach(list, loongarch_cpu_add_definition, &cpu_list); | |
704 | g_slist_free(list); | |
705 | ||
706 | return cpu_list; | |
707 | } |