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