master
c 335 lines 11.5 KB
Raw
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3 * LoongArch CPU parameters for QEMU.
4 *
5 * Copyright (c) 2025 Loongson Technology Corporation Limited
6 */
7 #include "qemu/osdep.h"
8 #include "qemu/accel.h"
9 #include "qemu/error-report.h"
10 #include "qemu/log.h"
11 #include "qemu/plugin.h"
12 #include "accel/tcg/cpu-ldst.h"
13 #include "accel/tcg/cpu-loop.h"
14 #include "accel/tcg/cpu-ops.h"
15 #include "exec/translation-block.h"
16 #include "exec/target_page.h"
17 #include "tcg/debug-assert.h"
18 #include "tcg_loongarch.h"
19 #include "internals.h"
20
21 struct TypeExcp {
22 int32_t exccode;
23 const char * const name;
24 };
25
26 static const struct TypeExcp excp_names[] = {
27 {EXCCODE_INT, "Interrupt"},
28 {EXCCODE_PIL, "Page invalid exception for load"},
29 {EXCCODE_PIS, "Page invalid exception for store"},
30 {EXCCODE_PIF, "Page invalid exception for fetch"},
31 {EXCCODE_PME, "Page modified exception"},
32 {EXCCODE_PNR, "Page Not Readable exception"},
33 {EXCCODE_PNX, "Page Not Executable exception"},
34 {EXCCODE_PPI, "Page Privilege error"},
35 {EXCCODE_ADEF, "Address error for instruction fetch"},
36 {EXCCODE_ADEM, "Address error for Memory access"},
37 {EXCCODE_SYS, "Syscall"},
38 {EXCCODE_BRK, "Break"},
39 {EXCCODE_INE, "Instruction Non-Existent"},
40 {EXCCODE_IPE, "Instruction privilege error"},
41 {EXCCODE_FPD, "Floating Point Disabled"},
42 {EXCCODE_FPE, "Floating Point Exception"},
43 {EXCCODE_DBP, "Debug breakpoint"},
44 {EXCCODE_BCE, "Bound Check Exception"},
45 {EXCCODE_SXD, "128 bit vector instructions Disable exception"},
46 {EXCCODE_ASXD, "256 bit vector instructions Disable exception"},
47 {EXCP_HLT, "EXCP_HLT"},
48 };
49
50 static const char *loongarch_exception_name(int32_t exception)
51 {
52 int i;
53
54 for (i = 0; i < ARRAY_SIZE(excp_names); i++) {
55 if (excp_names[i].exccode == exception) {
56 return excp_names[i].name;
57 }
58 }
59 return "Unknown";
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: exception: %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 #ifndef CONFIG_USER_ONLY
78 static void loongarch_cpu_do_interrupt(CPUState *cs)
79 {
80 CPULoongArchState *env = cpu_env(cs);
81 CPUSysState *sys = env_sys(env);
82 bool update_badinstr = 1;
83 int cause = -1;
84 bool tlbfill = FIELD_EX64(sys->CSR_TLBRERA, CSR_TLBRERA, ISTLBR);
85 uint32_t vec_size = FIELD_EX64(sys->CSR_ECFG, CSR_ECFG, VS);
86 uint64_t last_pc = env->pc;
87
88 if (cs->exception_index != EXCCODE_INT) {
89 qemu_log_mask(CPU_LOG_INT,
90 "%s enter: pc " TARGET_FMT_lx " ERA " TARGET_FMT_lx
91 " TLBRERA " TARGET_FMT_lx " exception: %d (%s)\n",
92 __func__, env->pc, sys->CSR_ERA, sys->CSR_TLBRERA,
93 cs->exception_index,
94 loongarch_exception_name(cs->exception_index));
95 }
96
97 switch (cs->exception_index) {
98 case EXCCODE_DBP:
99 sys->CSR_DBG = FIELD_DP64(sys->CSR_DBG, CSR_DBG, DCL, 1);
100 sys->CSR_DBG = FIELD_DP64(sys->CSR_DBG, CSR_DBG, ECODE, 0xC);
101 goto set_DERA;
102 set_DERA:
103 sys->CSR_DERA = env->pc;
104 sys->CSR_DBG = FIELD_DP64(sys->CSR_DBG, CSR_DBG, DST, 1);
105 set_pc(env, sys->CSR_EENTRY + 0x480);
106 break;
107 case EXCCODE_INT:
108 if (FIELD_EX64(sys->CSR_DBG, CSR_DBG, DST)) {
109 sys->CSR_DBG = FIELD_DP64(sys->CSR_DBG, CSR_DBG, DEI, 1);
110 goto set_DERA;
111 }
112 QEMU_FALLTHROUGH;
113 case EXCCODE_PIF:
114 case EXCCODE_PNX:
115 case EXCCODE_ADEF:
116 cause = cs->exception_index;
117 update_badinstr = 0;
118 break;
119 case EXCCODE_BCE:
120 sys->CSR_BADV = env->pc;
121 QEMU_FALLTHROUGH;
122 case EXCCODE_SYS:
123 case EXCCODE_BRK:
124 case EXCCODE_INE:
125 case EXCCODE_IPE:
126 case EXCCODE_FPD:
127 case EXCCODE_FPE:
128 case EXCCODE_SXD:
129 case EXCCODE_ASXD:
130 case EXCCODE_ADEM:
131 case EXCCODE_PIL:
132 case EXCCODE_PIS:
133 case EXCCODE_PME:
134 case EXCCODE_PNR:
135 case EXCCODE_PPI:
136 cause = cs->exception_index;
137 break;
138 default:
139 qemu_log("Error: exception(%d) has not been supported\n",
140 cs->exception_index);
141 abort();
142 }
143
144 if (update_badinstr) {
145 MemOpIdx oi = make_memop_idx(MO_LEUL, cpu_mmu_index(cs, true));
146
147 sys->CSR_BADI = cpu_ldl_code_mmu(env, env->pc, oi, 0);
148 }
149
150 /* Save PLV and IE */
151 if (tlbfill) {
152 sys->CSR_TLBRPRMD = FIELD_DP64(sys->CSR_TLBRPRMD, CSR_TLBRPRMD, PPLV,
153 FIELD_EX64(sys->CSR_CRMD,
154 CSR_CRMD, PLV));
155 sys->CSR_TLBRPRMD = FIELD_DP64(sys->CSR_TLBRPRMD, CSR_TLBRPRMD, PIE,
156 FIELD_EX64(sys->CSR_CRMD, CSR_CRMD, IE));
157 /* set the DA mode */
158 sys->CSR_CRMD = FIELD_DP64(sys->CSR_CRMD, CSR_CRMD, DA, 1);
159 sys->CSR_CRMD = FIELD_DP64(sys->CSR_CRMD, CSR_CRMD, PG, 0);
160 sys->CSR_TLBRERA = FIELD_DP64(sys->CSR_TLBRERA, CSR_TLBRERA,
161 PC, (env->pc >> 2));
162 } else {
163 sys->CSR_ESTAT = FIELD_DP64(sys->CSR_ESTAT, CSR_ESTAT, ECODE,
164 EXCODE_MCODE(cause));
165 sys->CSR_ESTAT = FIELD_DP64(sys->CSR_ESTAT, CSR_ESTAT, ESUBCODE,
166 EXCODE_SUBCODE(cause));
167 sys->CSR_PRMD = FIELD_DP64(sys->CSR_PRMD, CSR_PRMD, PPLV,
168 FIELD_EX64(sys->CSR_CRMD, CSR_CRMD, PLV));
169 sys->CSR_PRMD = FIELD_DP64(sys->CSR_PRMD, CSR_PRMD, PIE,
170 FIELD_EX64(sys->CSR_CRMD, CSR_CRMD, IE));
171 sys->CSR_ERA = env->pc;
172 }
173
174 sys->CSR_CRMD = FIELD_DP64(sys->CSR_CRMD, CSR_CRMD, PLV, 0);
175 sys->CSR_CRMD = FIELD_DP64(sys->CSR_CRMD, CSR_CRMD, IE, 0);
176
177 if (vec_size) {
178 vec_size = (1 << vec_size) * 4;
179 }
180
181 if (cs->exception_index == EXCCODE_INT) {
182 /* Interrupt */
183 uint32_t vector = 0;
184 uint32_t pending = FIELD_EX64(sys->CSR_ESTAT, CSR_ESTAT, IS);
185 pending &= FIELD_EX64(sys->CSR_ECFG, CSR_ECFG, LIE);
186
187 /* Find the highest-priority interrupt. */
188 vector = 31 - clz32(pending);
189 set_pc(env, sys->CSR_EENTRY + \
190 (EXCCODE_EXTERNAL_INT + vector) * vec_size);
191 qemu_log_mask(CPU_LOG_INT,
192 "%s: PC " TARGET_FMT_lx " ERA " TARGET_FMT_lx
193 " cause %d\n" " A " TARGET_FMT_lx " D "
194 TARGET_FMT_lx " vector = %d ExC " TARGET_FMT_lx "ExS"
195 TARGET_FMT_lx "\n",
196 __func__, env->pc, sys->CSR_ERA,
197 cause, sys->CSR_BADV, sys->CSR_DERA, vector,
198 sys->CSR_ECFG, sys->CSR_ESTAT);
199 qemu_plugin_vcpu_interrupt_cb(cs, last_pc);
200 } else {
201 if (tlbfill) {
202 set_pc(env, sys->CSR_TLBRENTRY);
203 } else {
204 set_pc(env, sys->CSR_EENTRY + EXCODE_MCODE(cause) * vec_size);
205 }
206 qemu_log_mask(CPU_LOG_INT,
207 "%s: PC " TARGET_FMT_lx " ERA " TARGET_FMT_lx
208 " cause %d%s\n, ESTAT " TARGET_FMT_lx
209 " EXCFG " TARGET_FMT_lx " BADVA " TARGET_FMT_lx
210 "BADI " TARGET_FMT_lx " SYS_NUM " TARGET_FMT_lu
211 " cpu %d asid " TARGET_FMT_lx "\n", __func__, env->pc,
212 tlbfill ? sys->CSR_TLBRERA : sys->CSR_ERA,
213 cause, tlbfill ? "(refill)" : "", sys->CSR_ESTAT,
214 sys->CSR_ECFG,
215 tlbfill ? sys->CSR_TLBRBADV : sys->CSR_BADV,
216 sys->CSR_BADI, env->gpr[11], cs->cpu_index,
217 sys->CSR_ASID);
218 qemu_plugin_vcpu_exception_cb(cs, last_pc);
219 }
220 cs->exception_index = -1;
221 }
222
223 static void loongarch_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
224 vaddr addr, unsigned size,
225 MMUAccessType access_type,
226 int mmu_idx, MemTxAttrs attrs,
227 MemTxResult response,
228 uintptr_t retaddr)
229 {
230 CPULoongArchState *env = cpu_env(cs);
231 CPUSysState *sys = env_sys(env);
232
233 sys->CSR_BADV = addr;
234 if (access_type == MMU_INST_FETCH) {
235 do_raise_exception(env, EXCCODE_ADEF, retaddr);
236 } else {
237 do_raise_exception(env, EXCCODE_ADEM, retaddr);
238 }
239 }
240
241 static inline bool cpu_loongarch_hw_interrupts_enabled(CPULoongArchState *env)
242 {
243 bool ret = 0;
244 CPUSysState *sys = env_sys(env);
245
246 ret = (FIELD_EX64(sys->CSR_CRMD, CSR_CRMD, IE) &&
247 !(FIELD_EX64(sys->CSR_DBG, CSR_DBG, DST)));
248
249 return ret;
250 }
251
252 static bool loongarch_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
253 {
254 if (interrupt_request & CPU_INTERRUPT_HARD) {
255 CPULoongArchState *env = cpu_env(cs);
256
257 if (cpu_loongarch_hw_interrupts_enabled(env) &&
258 cpu_loongarch_hw_interrupts_pending(env)) {
259 /* Raise it */
260 cs->exception_index = EXCCODE_INT;
261 loongarch_cpu_do_interrupt(cs);
262 return true;
263 }
264 }
265 return false;
266 }
267
268 static vaddr loongarch_pointer_wrap(CPUState *cs, int mmu_idx,
269 vaddr result, vaddr base)
270 {
271 return is_va32(cpu_env(cs)) ? (uint32_t)result : result;
272 }
273 #endif
274
275 static TCGTBCPUState loongarch_get_tb_cpu_state(CPUState *cs)
276 {
277 CPULoongArchState *env = cpu_env(cs);
278 CPUSysState *sys = env_sys(env);
279 uint32_t flags;
280
281 flags = sys->CSR_CRMD & (R_CSR_CRMD_PLV_MASK | R_CSR_CRMD_PG_MASK);
282 flags |= FIELD_EX64(sys->CSR_EUEN, CSR_EUEN, FPE) * HW_FLAGS_EUEN_FPE;
283 flags |= FIELD_EX64(sys->CSR_EUEN, CSR_EUEN, SXE) * HW_FLAGS_EUEN_SXE;
284 flags |= FIELD_EX64(sys->CSR_EUEN, CSR_EUEN, ASXE) * HW_FLAGS_EUEN_ASXE;
285 flags |= is_va32(env) * HW_FLAGS_VA32;
286
287 return (TCGTBCPUState){ .pc = env->pc, .flags = flags };
288 }
289
290 static void loongarch_cpu_synchronize_from_tb(CPUState *cs,
291 const TranslationBlock *tb)
292 {
293 tcg_debug_assert(!tcg_cflags_has(cs, CF_PCREL));
294 set_pc(cpu_env(cs), tb->pc);
295 }
296
297 static void loongarch_restore_state_to_opc(CPUState *cs,
298 const TranslationBlock *tb,
299 const uint64_t *data)
300 {
301 set_pc(cpu_env(cs), data[0]);
302 }
303
304 static int loongarch_cpu_mmu_index(CPUState *cs, bool ifetch)
305 {
306 CPULoongArchState *env = cpu_env(cs);
307 CPUSysState *sys = env_sys(env);
308
309 if (FIELD_EX64(sys->CSR_CRMD, CSR_CRMD, PG)) {
310 return FIELD_EX64(sys->CSR_CRMD, CSR_CRMD, PLV);
311 }
312 return MMU_DA_IDX;
313 }
314
315 const TCGCPUOps loongarch_tcg_ops = {
316 .guest_default_memory_order = 0,
317 .mttcg_supported = true,
318
319 .initialize = loongarch_translate_init,
320 .translate_code = loongarch_translate_code,
321 .get_tb_cpu_state = loongarch_get_tb_cpu_state,
322 .synchronize_from_tb = loongarch_cpu_synchronize_from_tb,
323 .restore_state_to_opc = loongarch_restore_state_to_opc,
324 .mmu_index = loongarch_cpu_mmu_index,
325
326 #ifndef CONFIG_USER_ONLY
327 .tlb_fill = loongarch_cpu_tlb_fill,
328 .pointer_wrap = loongarch_pointer_wrap,
329 .cpu_exec_interrupt = loongarch_cpu_exec_interrupt,
330 .cpu_exec_halt = loongarch_cpu_has_work,
331 .cpu_exec_reset = cpu_reset,
332 .do_interrupt = loongarch_cpu_do_interrupt,
333 .do_transaction_failed = loongarch_cpu_do_transaction_failed,
334 #endif
335 };