]>
Commit | Line | Data |
---|---|---|
032c76bc CW |
1 | /* |
2 | * Altera Nios II emulation for qemu: main translation routines. | |
3 | * | |
4 | * Copyright (C) 2016 Marek Vasut <marex@denx.de> | |
5 | * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com> | |
6 | * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch> | |
7 | * (Portions of this file that were originally from nios2sim-ng.) | |
8 | * | |
9 | * This library is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU Lesser General Public | |
11 | * License as published by the Free Software Foundation; either | |
12 | * version 2.1 of the License, or (at your option) any later version. | |
13 | * | |
14 | * This library is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * Lesser General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU Lesser General Public | |
20 | * License along with this library; if not, see | |
21 | * <http://www.gnu.org/licenses/lgpl-2.1.html> | |
22 | */ | |
23 | ||
24 | #include "cpu.h" | |
25 | #include "tcg-op.h" | |
26 | #include "exec/exec-all.h" | |
27 | #include "disas/disas.h" | |
28 | #include "exec/helper-proto.h" | |
29 | #include "exec/helper-gen.h" | |
30 | #include "exec/log.h" | |
31 | #include "exec/cpu_ldst.h" | |
32 | ||
33 | #define INSTRUCTION_FLG(func, flags) { (func), (flags) } | |
34 | #define INSTRUCTION(func) \ | |
35 | INSTRUCTION_FLG(func, 0) | |
36 | #define INSTRUCTION_NOP() \ | |
37 | INSTRUCTION_FLG(nop, 0) | |
38 | #define INSTRUCTION_UNIMPLEMENTED() \ | |
39 | INSTRUCTION_FLG(gen_excp, EXCP_UNIMPL) | |
40 | #define INSTRUCTION_ILLEGAL() \ | |
41 | INSTRUCTION_FLG(gen_excp, EXCP_ILLEGAL) | |
42 | ||
43 | /* Special R-Type instruction opcode */ | |
44 | #define INSN_R_TYPE 0x3A | |
45 | ||
46 | /* I-Type instruction parsing */ | |
47 | #define I_TYPE(instr, code) \ | |
48 | struct { \ | |
49 | uint8_t op; \ | |
50 | union { \ | |
4ae4b609 PB |
51 | uint16_t u; \ |
52 | int16_t s; \ | |
53 | } imm16; \ | |
032c76bc CW |
54 | uint8_t b; \ |
55 | uint8_t a; \ | |
56 | } (instr) = { \ | |
57 | .op = extract32((code), 0, 6), \ | |
4ae4b609 | 58 | .imm16.u = extract32((code), 6, 16), \ |
032c76bc CW |
59 | .b = extract32((code), 22, 5), \ |
60 | .a = extract32((code), 27, 5), \ | |
61 | } | |
62 | ||
63 | /* R-Type instruction parsing */ | |
64 | #define R_TYPE(instr, code) \ | |
65 | struct { \ | |
66 | uint8_t op; \ | |
67 | uint8_t imm5; \ | |
68 | uint8_t opx; \ | |
69 | uint8_t c; \ | |
70 | uint8_t b; \ | |
71 | uint8_t a; \ | |
72 | } (instr) = { \ | |
73 | .op = extract32((code), 0, 6), \ | |
74 | .imm5 = extract32((code), 6, 5), \ | |
75 | .opx = extract32((code), 11, 6), \ | |
76 | .c = extract32((code), 17, 5), \ | |
77 | .b = extract32((code), 22, 5), \ | |
78 | .a = extract32((code), 27, 5), \ | |
79 | } | |
80 | ||
81 | /* J-Type instruction parsing */ | |
82 | #define J_TYPE(instr, code) \ | |
83 | struct { \ | |
84 | uint8_t op; \ | |
85 | uint32_t imm26; \ | |
86 | } (instr) = { \ | |
87 | .op = extract32((code), 0, 6), \ | |
88 | .imm26 = extract32((code), 6, 26), \ | |
89 | } | |
90 | ||
91 | typedef struct DisasContext { | |
92 | TCGv_ptr cpu_env; | |
93 | TCGv *cpu_R; | |
94 | TCGv_i32 zero; | |
95 | int is_jmp; | |
96 | target_ulong pc; | |
97 | TranslationBlock *tb; | |
98 | int mem_idx; | |
99 | bool singlestep_enabled; | |
100 | } DisasContext; | |
101 | ||
102 | typedef struct Nios2Instruction { | |
103 | void (*handler)(DisasContext *dc, uint32_t code, uint32_t flags); | |
104 | uint32_t flags; | |
105 | } Nios2Instruction; | |
106 | ||
107 | static uint8_t get_opcode(uint32_t code) | |
108 | { | |
109 | I_TYPE(instr, code); | |
110 | return instr.op; | |
111 | } | |
112 | ||
113 | static uint8_t get_opxcode(uint32_t code) | |
114 | { | |
115 | R_TYPE(instr, code); | |
116 | return instr.opx; | |
117 | } | |
118 | ||
119 | static TCGv load_zero(DisasContext *dc) | |
120 | { | |
121 | if (TCGV_IS_UNUSED_I32(dc->zero)) { | |
122 | dc->zero = tcg_const_i32(0); | |
123 | } | |
124 | return dc->zero; | |
125 | } | |
126 | ||
127 | static TCGv load_gpr(DisasContext *dc, uint8_t reg) | |
128 | { | |
129 | if (likely(reg != R_ZERO)) { | |
130 | return dc->cpu_R[reg]; | |
131 | } else { | |
132 | return load_zero(dc); | |
133 | } | |
134 | } | |
135 | ||
136 | static void t_gen_helper_raise_exception(DisasContext *dc, | |
137 | uint32_t index) | |
138 | { | |
139 | TCGv_i32 tmp = tcg_const_i32(index); | |
140 | ||
141 | tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc); | |
142 | gen_helper_raise_exception(dc->cpu_env, tmp); | |
143 | tcg_temp_free_i32(tmp); | |
144 | dc->is_jmp = DISAS_UPDATE; | |
145 | } | |
146 | ||
147 | static bool use_goto_tb(DisasContext *dc, uint32_t dest) | |
148 | { | |
149 | if (unlikely(dc->singlestep_enabled)) { | |
150 | return false; | |
151 | } | |
152 | ||
153 | #ifndef CONFIG_USER_ONLY | |
154 | return (dc->tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); | |
155 | #else | |
156 | return true; | |
157 | #endif | |
158 | } | |
159 | ||
160 | static void gen_goto_tb(DisasContext *dc, int n, uint32_t dest) | |
161 | { | |
162 | TranslationBlock *tb = dc->tb; | |
163 | ||
164 | if (use_goto_tb(dc, dest)) { | |
165 | tcg_gen_goto_tb(n); | |
166 | tcg_gen_movi_tl(dc->cpu_R[R_PC], dest); | |
1639a965 | 167 | tcg_gen_exit_tb((uintptr_t)tb + n); |
032c76bc CW |
168 | } else { |
169 | tcg_gen_movi_tl(dc->cpu_R[R_PC], dest); | |
170 | tcg_gen_exit_tb(0); | |
171 | } | |
172 | } | |
173 | ||
174 | static void gen_excp(DisasContext *dc, uint32_t code, uint32_t flags) | |
175 | { | |
176 | t_gen_helper_raise_exception(dc, flags); | |
177 | } | |
178 | ||
179 | static void gen_check_supervisor(DisasContext *dc) | |
180 | { | |
181 | if (dc->tb->flags & CR_STATUS_U) { | |
182 | /* CPU in user mode, privileged instruction called, stop. */ | |
183 | t_gen_helper_raise_exception(dc, EXCP_SUPERI); | |
184 | } | |
185 | } | |
186 | ||
187 | /* | |
188 | * Used as a placeholder for all instructions which do not have | |
189 | * an effect on the simulator (e.g. flush, sync) | |
190 | */ | |
191 | static void nop(DisasContext *dc, uint32_t code, uint32_t flags) | |
192 | { | |
193 | /* Nothing to do here */ | |
194 | } | |
195 | ||
196 | /* | |
197 | * J-Type instructions | |
198 | */ | |
199 | static void jmpi(DisasContext *dc, uint32_t code, uint32_t flags) | |
200 | { | |
201 | J_TYPE(instr, code); | |
202 | gen_goto_tb(dc, 0, (dc->pc & 0xF0000000) | (instr.imm26 << 2)); | |
203 | dc->is_jmp = DISAS_TB_JUMP; | |
204 | } | |
205 | ||
206 | static void call(DisasContext *dc, uint32_t code, uint32_t flags) | |
207 | { | |
208 | tcg_gen_movi_tl(dc->cpu_R[R_RA], dc->pc + 4); | |
209 | jmpi(dc, code, flags); | |
210 | } | |
211 | ||
212 | /* | |
213 | * I-Type instructions | |
214 | */ | |
215 | /* Load instructions */ | |
216 | static void gen_ldx(DisasContext *dc, uint32_t code, uint32_t flags) | |
217 | { | |
218 | I_TYPE(instr, code); | |
219 | ||
220 | TCGv addr = tcg_temp_new(); | |
221 | TCGv data; | |
222 | ||
223 | /* | |
224 | * WARNING: Loads into R_ZERO are ignored, but we must generate the | |
225 | * memory access itself to emulate the CPU precisely. Load | |
226 | * from a protected page to R_ZERO will cause SIGSEGV on | |
227 | * the Nios2 CPU. | |
228 | */ | |
229 | if (likely(instr.b != R_ZERO)) { | |
230 | data = dc->cpu_R[instr.b]; | |
231 | } else { | |
232 | data = tcg_temp_new(); | |
233 | } | |
234 | ||
4ae4b609 | 235 | tcg_gen_addi_tl(addr, load_gpr(dc, instr.a), instr.imm16.s); |
032c76bc CW |
236 | tcg_gen_qemu_ld_tl(data, addr, dc->mem_idx, flags); |
237 | ||
238 | if (unlikely(instr.b == R_ZERO)) { | |
239 | tcg_temp_free(data); | |
240 | } | |
241 | ||
242 | tcg_temp_free(addr); | |
243 | } | |
244 | ||
245 | /* Store instructions */ | |
246 | static void gen_stx(DisasContext *dc, uint32_t code, uint32_t flags) | |
247 | { | |
248 | I_TYPE(instr, code); | |
249 | TCGv val = load_gpr(dc, instr.b); | |
250 | ||
251 | TCGv addr = tcg_temp_new(); | |
4ae4b609 | 252 | tcg_gen_addi_tl(addr, load_gpr(dc, instr.a), instr.imm16.s); |
032c76bc CW |
253 | tcg_gen_qemu_st_tl(val, addr, dc->mem_idx, flags); |
254 | tcg_temp_free(addr); | |
255 | } | |
256 | ||
257 | /* Branch instructions */ | |
258 | static void br(DisasContext *dc, uint32_t code, uint32_t flags) | |
259 | { | |
260 | I_TYPE(instr, code); | |
261 | ||
4ae4b609 | 262 | gen_goto_tb(dc, 0, dc->pc + 4 + (instr.imm16.s & -4)); |
032c76bc CW |
263 | dc->is_jmp = DISAS_TB_JUMP; |
264 | } | |
265 | ||
266 | static void gen_bxx(DisasContext *dc, uint32_t code, uint32_t flags) | |
267 | { | |
268 | I_TYPE(instr, code); | |
269 | ||
270 | TCGLabel *l1 = gen_new_label(); | |
271 | tcg_gen_brcond_tl(flags, dc->cpu_R[instr.a], dc->cpu_R[instr.b], l1); | |
272 | gen_goto_tb(dc, 0, dc->pc + 4); | |
273 | gen_set_label(l1); | |
4ae4b609 | 274 | gen_goto_tb(dc, 1, dc->pc + 4 + (instr.imm16.s & -4)); |
032c76bc CW |
275 | dc->is_jmp = DISAS_TB_JUMP; |
276 | } | |
277 | ||
278 | /* Comparison instructions */ | |
279 | #define gen_i_cmpxx(fname, op3) \ | |
280 | static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ | |
281 | { \ | |
282 | I_TYPE(instr, (code)); \ | |
283 | tcg_gen_setcondi_tl(flags, (dc)->cpu_R[instr.b], (dc)->cpu_R[instr.a], \ | |
284 | (op3)); \ | |
285 | } | |
286 | ||
4ae4b609 PB |
287 | gen_i_cmpxx(gen_cmpxxsi, instr.imm16.s) |
288 | gen_i_cmpxx(gen_cmpxxui, instr.imm16.u) | |
032c76bc CW |
289 | |
290 | /* Math/logic instructions */ | |
291 | #define gen_i_math_logic(fname, insn, resimm, op3) \ | |
292 | static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ | |
293 | { \ | |
294 | I_TYPE(instr, (code)); \ | |
295 | if (unlikely(instr.b == R_ZERO)) { /* Store to R_ZERO is ignored */ \ | |
296 | return; \ | |
297 | } else if (instr.a == R_ZERO) { /* MOVxI optimizations */ \ | |
298 | tcg_gen_movi_tl(dc->cpu_R[instr.b], (resimm) ? (op3) : 0); \ | |
299 | } else { \ | |
300 | tcg_gen_##insn##_tl((dc)->cpu_R[instr.b], (dc)->cpu_R[instr.a], \ | |
301 | (op3)); \ | |
302 | } \ | |
303 | } | |
304 | ||
4ae4b609 PB |
305 | gen_i_math_logic(addi, addi, 1, instr.imm16.s) |
306 | gen_i_math_logic(muli, muli, 0, instr.imm16.s) | |
032c76bc | 307 | |
4ae4b609 PB |
308 | gen_i_math_logic(andi, andi, 0, instr.imm16.u) |
309 | gen_i_math_logic(ori, ori, 1, instr.imm16.u) | |
310 | gen_i_math_logic(xori, xori, 1, instr.imm16.u) | |
032c76bc | 311 | |
4ae4b609 PB |
312 | gen_i_math_logic(andhi, andi, 0, instr.imm16.u << 16) |
313 | gen_i_math_logic(orhi , ori, 1, instr.imm16.u << 16) | |
314 | gen_i_math_logic(xorhi, xori, 1, instr.imm16.u << 16) | |
032c76bc CW |
315 | |
316 | /* Prototype only, defined below */ | |
317 | static void handle_r_type_instr(DisasContext *dc, uint32_t code, | |
318 | uint32_t flags); | |
319 | ||
320 | static const Nios2Instruction i_type_instructions[] = { | |
321 | INSTRUCTION(call), /* call */ | |
322 | INSTRUCTION(jmpi), /* jmpi */ | |
323 | INSTRUCTION_ILLEGAL(), | |
324 | INSTRUCTION_FLG(gen_ldx, MO_UB), /* ldbu */ | |
325 | INSTRUCTION(addi), /* addi */ | |
326 | INSTRUCTION_FLG(gen_stx, MO_UB), /* stb */ | |
327 | INSTRUCTION(br), /* br */ | |
328 | INSTRUCTION_FLG(gen_ldx, MO_SB), /* ldb */ | |
329 | INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_GE), /* cmpgei */ | |
330 | INSTRUCTION_ILLEGAL(), | |
331 | INSTRUCTION_ILLEGAL(), | |
332 | INSTRUCTION_FLG(gen_ldx, MO_UW), /* ldhu */ | |
333 | INSTRUCTION(andi), /* andi */ | |
334 | INSTRUCTION_FLG(gen_stx, MO_UW), /* sth */ | |
335 | INSTRUCTION_FLG(gen_bxx, TCG_COND_GE), /* bge */ | |
336 | INSTRUCTION_FLG(gen_ldx, MO_SW), /* ldh */ | |
337 | INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_LT), /* cmplti */ | |
338 | INSTRUCTION_ILLEGAL(), | |
339 | INSTRUCTION_ILLEGAL(), | |
340 | INSTRUCTION_NOP(), /* initda */ | |
341 | INSTRUCTION(ori), /* ori */ | |
342 | INSTRUCTION_FLG(gen_stx, MO_UL), /* stw */ | |
343 | INSTRUCTION_FLG(gen_bxx, TCG_COND_LT), /* blt */ | |
344 | INSTRUCTION_FLG(gen_ldx, MO_UL), /* ldw */ | |
345 | INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_NE), /* cmpnei */ | |
346 | INSTRUCTION_ILLEGAL(), | |
347 | INSTRUCTION_ILLEGAL(), | |
348 | INSTRUCTION_NOP(), /* flushda */ | |
349 | INSTRUCTION(xori), /* xori */ | |
350 | INSTRUCTION_ILLEGAL(), | |
351 | INSTRUCTION_FLG(gen_bxx, TCG_COND_NE), /* bne */ | |
352 | INSTRUCTION_ILLEGAL(), | |
353 | INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_EQ), /* cmpeqi */ | |
354 | INSTRUCTION_ILLEGAL(), | |
355 | INSTRUCTION_ILLEGAL(), | |
356 | INSTRUCTION_FLG(gen_ldx, MO_UB), /* ldbuio */ | |
357 | INSTRUCTION(muli), /* muli */ | |
358 | INSTRUCTION_FLG(gen_stx, MO_UB), /* stbio */ | |
359 | INSTRUCTION_FLG(gen_bxx, TCG_COND_EQ), /* beq */ | |
360 | INSTRUCTION_FLG(gen_ldx, MO_SB), /* ldbio */ | |
361 | INSTRUCTION_FLG(gen_cmpxxui, TCG_COND_GEU), /* cmpgeui */ | |
362 | INSTRUCTION_ILLEGAL(), | |
363 | INSTRUCTION_ILLEGAL(), | |
364 | INSTRUCTION_FLG(gen_ldx, MO_UW), /* ldhuio */ | |
365 | INSTRUCTION(andhi), /* andhi */ | |
366 | INSTRUCTION_FLG(gen_stx, MO_UW), /* sthio */ | |
367 | INSTRUCTION_FLG(gen_bxx, TCG_COND_GEU), /* bgeu */ | |
368 | INSTRUCTION_FLG(gen_ldx, MO_SW), /* ldhio */ | |
369 | INSTRUCTION_FLG(gen_cmpxxui, TCG_COND_LTU), /* cmpltui */ | |
370 | INSTRUCTION_ILLEGAL(), | |
371 | INSTRUCTION_UNIMPLEMENTED(), /* custom */ | |
372 | INSTRUCTION_NOP(), /* initd */ | |
373 | INSTRUCTION(orhi), /* orhi */ | |
374 | INSTRUCTION_FLG(gen_stx, MO_SL), /* stwio */ | |
375 | INSTRUCTION_FLG(gen_bxx, TCG_COND_LTU), /* bltu */ | |
376 | INSTRUCTION_FLG(gen_ldx, MO_UL), /* ldwio */ | |
377 | INSTRUCTION_UNIMPLEMENTED(), /* rdprs */ | |
378 | INSTRUCTION_ILLEGAL(), | |
379 | INSTRUCTION_FLG(handle_r_type_instr, 0), /* R-Type */ | |
380 | INSTRUCTION_NOP(), /* flushd */ | |
381 | INSTRUCTION(xorhi), /* xorhi */ | |
382 | INSTRUCTION_ILLEGAL(), | |
383 | INSTRUCTION_ILLEGAL(), | |
384 | INSTRUCTION_ILLEGAL(), | |
385 | }; | |
386 | ||
387 | /* | |
388 | * R-Type instructions | |
389 | */ | |
390 | /* | |
391 | * status <- estatus | |
392 | * PC <- ea | |
393 | */ | |
394 | static void eret(DisasContext *dc, uint32_t code, uint32_t flags) | |
395 | { | |
396 | tcg_gen_mov_tl(dc->cpu_R[CR_STATUS], dc->cpu_R[CR_ESTATUS]); | |
397 | tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_EA]); | |
398 | ||
399 | dc->is_jmp = DISAS_JUMP; | |
400 | } | |
401 | ||
402 | /* PC <- ra */ | |
403 | static void ret(DisasContext *dc, uint32_t code, uint32_t flags) | |
404 | { | |
405 | tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_RA]); | |
406 | ||
407 | dc->is_jmp = DISAS_JUMP; | |
408 | } | |
409 | ||
410 | /* PC <- ba */ | |
411 | static void bret(DisasContext *dc, uint32_t code, uint32_t flags) | |
412 | { | |
413 | tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_BA]); | |
414 | ||
415 | dc->is_jmp = DISAS_JUMP; | |
416 | } | |
417 | ||
418 | /* PC <- rA */ | |
419 | static void jmp(DisasContext *dc, uint32_t code, uint32_t flags) | |
420 | { | |
421 | R_TYPE(instr, code); | |
422 | ||
423 | tcg_gen_mov_tl(dc->cpu_R[R_PC], load_gpr(dc, instr.a)); | |
424 | ||
425 | dc->is_jmp = DISAS_JUMP; | |
426 | } | |
427 | ||
428 | /* rC <- PC + 4 */ | |
429 | static void nextpc(DisasContext *dc, uint32_t code, uint32_t flags) | |
430 | { | |
431 | R_TYPE(instr, code); | |
432 | ||
433 | if (likely(instr.c != R_ZERO)) { | |
434 | tcg_gen_movi_tl(dc->cpu_R[instr.c], dc->pc + 4); | |
435 | } | |
436 | } | |
437 | ||
438 | /* | |
439 | * ra <- PC + 4 | |
440 | * PC <- rA | |
441 | */ | |
442 | static void callr(DisasContext *dc, uint32_t code, uint32_t flags) | |
443 | { | |
444 | R_TYPE(instr, code); | |
445 | ||
446 | tcg_gen_mov_tl(dc->cpu_R[R_PC], load_gpr(dc, instr.a)); | |
447 | tcg_gen_movi_tl(dc->cpu_R[R_RA], dc->pc + 4); | |
448 | ||
449 | dc->is_jmp = DISAS_JUMP; | |
450 | } | |
451 | ||
452 | /* rC <- ctlN */ | |
453 | static void rdctl(DisasContext *dc, uint32_t code, uint32_t flags) | |
454 | { | |
455 | R_TYPE(instr, code); | |
456 | ||
457 | gen_check_supervisor(dc); | |
458 | ||
459 | switch (instr.imm5 + CR_BASE) { | |
460 | case CR_PTEADDR: | |
461 | case CR_TLBACC: | |
462 | case CR_TLBMISC: | |
463 | { | |
464 | #if !defined(CONFIG_USER_ONLY) | |
465 | if (likely(instr.c != R_ZERO)) { | |
466 | tcg_gen_mov_tl(dc->cpu_R[instr.c], dc->cpu_R[instr.imm5 + CR_BASE]); | |
467 | #ifdef DEBUG_MMU | |
468 | TCGv_i32 tmp = tcg_const_i32(instr.imm5 + CR_BASE); | |
469 | gen_helper_mmu_read_debug(dc->cpu_R[instr.c], dc->cpu_env, tmp); | |
470 | tcg_temp_free_i32(tmp); | |
471 | #endif | |
472 | } | |
473 | #endif | |
474 | break; | |
475 | } | |
476 | ||
477 | default: | |
478 | if (likely(instr.c != R_ZERO)) { | |
479 | tcg_gen_mov_tl(dc->cpu_R[instr.c], dc->cpu_R[instr.imm5 + CR_BASE]); | |
480 | } | |
481 | break; | |
482 | } | |
483 | } | |
484 | ||
485 | /* ctlN <- rA */ | |
486 | static void wrctl(DisasContext *dc, uint32_t code, uint32_t flags) | |
487 | { | |
488 | R_TYPE(instr, code); | |
489 | ||
490 | gen_check_supervisor(dc); | |
491 | ||
492 | switch (instr.imm5 + CR_BASE) { | |
493 | case CR_PTEADDR: | |
494 | case CR_TLBACC: | |
495 | case CR_TLBMISC: | |
496 | { | |
497 | #if !defined(CONFIG_USER_ONLY) | |
498 | TCGv_i32 tmp = tcg_const_i32(instr.imm5 + CR_BASE); | |
499 | gen_helper_mmu_write(dc->cpu_env, tmp, load_gpr(dc, instr.a)); | |
500 | tcg_temp_free_i32(tmp); | |
501 | #endif | |
502 | break; | |
503 | } | |
504 | ||
505 | default: | |
506 | tcg_gen_mov_tl(dc->cpu_R[instr.imm5 + CR_BASE], load_gpr(dc, instr.a)); | |
507 | break; | |
508 | } | |
509 | ||
510 | /* If interrupts were enabled using WRCTL, trigger them. */ | |
511 | #if !defined(CONFIG_USER_ONLY) | |
512 | if ((instr.imm5 + CR_BASE) == CR_STATUS) { | |
513 | gen_helper_check_interrupts(dc->cpu_env); | |
514 | } | |
515 | #endif | |
516 | } | |
517 | ||
518 | /* Comparison instructions */ | |
519 | static void gen_cmpxx(DisasContext *dc, uint32_t code, uint32_t flags) | |
520 | { | |
521 | R_TYPE(instr, code); | |
522 | if (likely(instr.c != R_ZERO)) { | |
523 | tcg_gen_setcond_tl(flags, dc->cpu_R[instr.c], dc->cpu_R[instr.a], | |
524 | dc->cpu_R[instr.b]); | |
525 | } | |
526 | } | |
527 | ||
528 | /* Math/logic instructions */ | |
529 | #define gen_r_math_logic(fname, insn, op3) \ | |
530 | static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ | |
531 | { \ | |
532 | R_TYPE(instr, (code)); \ | |
533 | if (likely(instr.c != R_ZERO)) { \ | |
534 | tcg_gen_##insn((dc)->cpu_R[instr.c], load_gpr((dc), instr.a), \ | |
535 | (op3)); \ | |
536 | } \ | |
537 | } | |
538 | ||
539 | gen_r_math_logic(add, add_tl, load_gpr(dc, instr.b)) | |
540 | gen_r_math_logic(sub, sub_tl, load_gpr(dc, instr.b)) | |
541 | gen_r_math_logic(mul, mul_tl, load_gpr(dc, instr.b)) | |
542 | ||
543 | gen_r_math_logic(and, and_tl, load_gpr(dc, instr.b)) | |
544 | gen_r_math_logic(or, or_tl, load_gpr(dc, instr.b)) | |
545 | gen_r_math_logic(xor, xor_tl, load_gpr(dc, instr.b)) | |
546 | gen_r_math_logic(nor, nor_tl, load_gpr(dc, instr.b)) | |
547 | ||
548 | gen_r_math_logic(srai, sari_tl, instr.imm5) | |
549 | gen_r_math_logic(srli, shri_tl, instr.imm5) | |
550 | gen_r_math_logic(slli, shli_tl, instr.imm5) | |
551 | gen_r_math_logic(roli, rotli_tl, instr.imm5) | |
552 | ||
553 | #define gen_r_mul(fname, insn) \ | |
554 | static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ | |
555 | { \ | |
556 | R_TYPE(instr, (code)); \ | |
557 | if (likely(instr.c != R_ZERO)) { \ | |
558 | TCGv t0 = tcg_temp_new(); \ | |
559 | tcg_gen_##insn(t0, dc->cpu_R[instr.c], \ | |
560 | load_gpr(dc, instr.a), load_gpr(dc, instr.b)); \ | |
561 | tcg_temp_free(t0); \ | |
562 | } \ | |
563 | } | |
564 | ||
565 | gen_r_mul(mulxss, muls2_tl) | |
566 | gen_r_mul(mulxuu, mulu2_tl) | |
567 | gen_r_mul(mulxsu, mulsu2_tl) | |
568 | ||
569 | #define gen_r_shift_s(fname, insn) \ | |
570 | static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ | |
571 | { \ | |
572 | R_TYPE(instr, (code)); \ | |
573 | if (likely(instr.c != R_ZERO)) { \ | |
574 | TCGv t0 = tcg_temp_new(); \ | |
575 | tcg_gen_andi_tl(t0, load_gpr((dc), instr.b), 31); \ | |
576 | tcg_gen_##insn((dc)->cpu_R[instr.c], load_gpr((dc), instr.a), t0); \ | |
577 | tcg_temp_free(t0); \ | |
578 | } \ | |
579 | } | |
580 | ||
581 | gen_r_shift_s(sra, sar_tl) | |
582 | gen_r_shift_s(srl, shr_tl) | |
583 | gen_r_shift_s(sll, shl_tl) | |
584 | gen_r_shift_s(rol, rotl_tl) | |
585 | gen_r_shift_s(ror, rotr_tl) | |
586 | ||
587 | static void divs(DisasContext *dc, uint32_t code, uint32_t flags) | |
588 | { | |
589 | R_TYPE(instr, (code)); | |
590 | ||
591 | /* Stores into R_ZERO are ignored */ | |
592 | if (unlikely(instr.c == R_ZERO)) { | |
593 | return; | |
594 | } | |
595 | ||
596 | TCGv t0 = tcg_temp_new(); | |
597 | TCGv t1 = tcg_temp_new(); | |
598 | TCGv t2 = tcg_temp_new(); | |
599 | TCGv t3 = tcg_temp_new(); | |
600 | ||
601 | tcg_gen_ext32s_tl(t0, load_gpr(dc, instr.a)); | |
602 | tcg_gen_ext32s_tl(t1, load_gpr(dc, instr.b)); | |
603 | tcg_gen_setcondi_tl(TCG_COND_EQ, t2, t0, INT_MIN); | |
604 | tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, -1); | |
605 | tcg_gen_and_tl(t2, t2, t3); | |
606 | tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0); | |
607 | tcg_gen_or_tl(t2, t2, t3); | |
608 | tcg_gen_movi_tl(t3, 0); | |
609 | tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); | |
610 | tcg_gen_div_tl(dc->cpu_R[instr.c], t0, t1); | |
611 | tcg_gen_ext32s_tl(dc->cpu_R[instr.c], dc->cpu_R[instr.c]); | |
612 | ||
613 | tcg_temp_free(t3); | |
614 | tcg_temp_free(t2); | |
615 | tcg_temp_free(t1); | |
616 | tcg_temp_free(t0); | |
617 | } | |
618 | ||
619 | static void divu(DisasContext *dc, uint32_t code, uint32_t flags) | |
620 | { | |
621 | R_TYPE(instr, (code)); | |
622 | ||
623 | /* Stores into R_ZERO are ignored */ | |
624 | if (unlikely(instr.c == R_ZERO)) { | |
625 | return; | |
626 | } | |
627 | ||
628 | TCGv t0 = tcg_temp_new(); | |
629 | TCGv t1 = tcg_temp_new(); | |
630 | TCGv t2 = tcg_const_tl(0); | |
631 | TCGv t3 = tcg_const_tl(1); | |
632 | ||
633 | tcg_gen_ext32u_tl(t0, load_gpr(dc, instr.a)); | |
634 | tcg_gen_ext32u_tl(t1, load_gpr(dc, instr.b)); | |
635 | tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); | |
636 | tcg_gen_divu_tl(dc->cpu_R[instr.c], t0, t1); | |
637 | tcg_gen_ext32s_tl(dc->cpu_R[instr.c], dc->cpu_R[instr.c]); | |
638 | ||
639 | tcg_temp_free(t3); | |
640 | tcg_temp_free(t2); | |
641 | tcg_temp_free(t1); | |
642 | tcg_temp_free(t0); | |
643 | } | |
644 | ||
645 | static const Nios2Instruction r_type_instructions[] = { | |
646 | INSTRUCTION_ILLEGAL(), | |
647 | INSTRUCTION(eret), /* eret */ | |
648 | INSTRUCTION(roli), /* roli */ | |
649 | INSTRUCTION(rol), /* rol */ | |
650 | INSTRUCTION_NOP(), /* flushp */ | |
651 | INSTRUCTION(ret), /* ret */ | |
652 | INSTRUCTION(nor), /* nor */ | |
653 | INSTRUCTION(mulxuu), /* mulxuu */ | |
654 | INSTRUCTION_FLG(gen_cmpxx, TCG_COND_GE), /* cmpge */ | |
655 | INSTRUCTION(bret), /* bret */ | |
656 | INSTRUCTION_ILLEGAL(), | |
657 | INSTRUCTION(ror), /* ror */ | |
658 | INSTRUCTION_NOP(), /* flushi */ | |
659 | INSTRUCTION(jmp), /* jmp */ | |
660 | INSTRUCTION(and), /* and */ | |
661 | INSTRUCTION_ILLEGAL(), | |
662 | INSTRUCTION_FLG(gen_cmpxx, TCG_COND_LT), /* cmplt */ | |
663 | INSTRUCTION_ILLEGAL(), | |
664 | INSTRUCTION(slli), /* slli */ | |
665 | INSTRUCTION(sll), /* sll */ | |
666 | INSTRUCTION_UNIMPLEMENTED(), /* wrprs */ | |
667 | INSTRUCTION_ILLEGAL(), | |
668 | INSTRUCTION(or), /* or */ | |
669 | INSTRUCTION(mulxsu), /* mulxsu */ | |
670 | INSTRUCTION_FLG(gen_cmpxx, TCG_COND_NE), /* cmpne */ | |
671 | INSTRUCTION_ILLEGAL(), | |
672 | INSTRUCTION(srli), /* srli */ | |
673 | INSTRUCTION(srl), /* srl */ | |
674 | INSTRUCTION(nextpc), /* nextpc */ | |
675 | INSTRUCTION(callr), /* callr */ | |
676 | INSTRUCTION(xor), /* xor */ | |
677 | INSTRUCTION(mulxss), /* mulxss */ | |
678 | INSTRUCTION_FLG(gen_cmpxx, TCG_COND_EQ), /* cmpeq */ | |
679 | INSTRUCTION_ILLEGAL(), | |
680 | INSTRUCTION_ILLEGAL(), | |
681 | INSTRUCTION_ILLEGAL(), | |
682 | INSTRUCTION(divu), /* divu */ | |
683 | INSTRUCTION(divs), /* div */ | |
684 | INSTRUCTION(rdctl), /* rdctl */ | |
685 | INSTRUCTION(mul), /* mul */ | |
686 | INSTRUCTION_FLG(gen_cmpxx, TCG_COND_GEU), /* cmpgeu */ | |
687 | INSTRUCTION_NOP(), /* initi */ | |
688 | INSTRUCTION_ILLEGAL(), | |
689 | INSTRUCTION_ILLEGAL(), | |
690 | INSTRUCTION_ILLEGAL(), | |
691 | INSTRUCTION_FLG(gen_excp, EXCP_TRAP), /* trap */ | |
692 | INSTRUCTION(wrctl), /* wrctl */ | |
693 | INSTRUCTION_ILLEGAL(), | |
694 | INSTRUCTION_FLG(gen_cmpxx, TCG_COND_LTU), /* cmpltu */ | |
695 | INSTRUCTION(add), /* add */ | |
696 | INSTRUCTION_ILLEGAL(), | |
697 | INSTRUCTION_ILLEGAL(), | |
698 | INSTRUCTION_FLG(gen_excp, EXCP_BREAK), /* break */ | |
699 | INSTRUCTION_ILLEGAL(), | |
700 | INSTRUCTION(nop), /* nop */ | |
701 | INSTRUCTION_ILLEGAL(), | |
702 | INSTRUCTION_ILLEGAL(), | |
703 | INSTRUCTION(sub), /* sub */ | |
704 | INSTRUCTION(srai), /* srai */ | |
705 | INSTRUCTION(sra), /* sra */ | |
706 | INSTRUCTION_ILLEGAL(), | |
707 | INSTRUCTION_ILLEGAL(), | |
708 | INSTRUCTION_ILLEGAL(), | |
709 | INSTRUCTION_ILLEGAL(), | |
710 | }; | |
711 | ||
712 | static void handle_r_type_instr(DisasContext *dc, uint32_t code, uint32_t flags) | |
713 | { | |
714 | uint8_t opx; | |
715 | const Nios2Instruction *instr; | |
716 | ||
717 | opx = get_opxcode(code); | |
718 | if (unlikely(opx >= ARRAY_SIZE(r_type_instructions))) { | |
719 | goto illegal_op; | |
720 | } | |
721 | ||
722 | instr = &r_type_instructions[opx]; | |
723 | instr->handler(dc, code, instr->flags); | |
724 | ||
725 | return; | |
726 | ||
727 | illegal_op: | |
728 | t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); | |
729 | } | |
730 | ||
731 | static void handle_instruction(DisasContext *dc, CPUNios2State *env) | |
732 | { | |
733 | uint32_t code; | |
734 | uint8_t op; | |
735 | const Nios2Instruction *instr; | |
736 | #if defined(CONFIG_USER_ONLY) | |
737 | /* FIXME: Is this needed ? */ | |
738 | if (dc->pc >= 0x1000 && dc->pc < 0x2000) { | |
739 | env->regs[R_PC] = dc->pc; | |
740 | t_gen_helper_raise_exception(dc, 0xaa); | |
741 | return; | |
742 | } | |
743 | #endif | |
744 | code = cpu_ldl_code(env, dc->pc); | |
745 | op = get_opcode(code); | |
746 | ||
747 | if (unlikely(op >= ARRAY_SIZE(i_type_instructions))) { | |
748 | goto illegal_op; | |
749 | } | |
750 | ||
751 | TCGV_UNUSED_I32(dc->zero); | |
752 | ||
753 | instr = &i_type_instructions[op]; | |
754 | instr->handler(dc, code, instr->flags); | |
755 | ||
756 | if (!TCGV_IS_UNUSED_I32(dc->zero)) { | |
757 | tcg_temp_free(dc->zero); | |
758 | } | |
759 | ||
760 | return; | |
761 | ||
762 | illegal_op: | |
763 | t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); | |
764 | } | |
765 | ||
766 | static const char * const regnames[] = { | |
767 | "zero", "at", "r2", "r3", | |
768 | "r4", "r5", "r6", "r7", | |
769 | "r8", "r9", "r10", "r11", | |
770 | "r12", "r13", "r14", "r15", | |
771 | "r16", "r17", "r18", "r19", | |
772 | "r20", "r21", "r22", "r23", | |
773 | "et", "bt", "gp", "sp", | |
774 | "fp", "ea", "ba", "ra", | |
775 | "status", "estatus", "bstatus", "ienable", | |
776 | "ipending", "cpuid", "reserved0", "exception", | |
777 | "pteaddr", "tlbacc", "tlbmisc", "reserved1", | |
778 | "badaddr", "config", "mpubase", "mpuacc", | |
779 | "reserved2", "reserved3", "reserved4", "reserved5", | |
780 | "reserved6", "reserved7", "reserved8", "reserved9", | |
781 | "reserved10", "reserved11", "reserved12", "reserved13", | |
782 | "reserved14", "reserved15", "reserved16", "reserved17", | |
783 | "rpc" | |
784 | }; | |
785 | ||
786 | static TCGv_ptr cpu_env; | |
787 | static TCGv cpu_R[NUM_CORE_REGS]; | |
788 | ||
789 | #include "exec/gen-icount.h" | |
790 | ||
791 | static void gen_exception(DisasContext *dc, uint32_t excp) | |
792 | { | |
793 | TCGv_i32 tmp = tcg_const_i32(excp); | |
794 | ||
795 | tcg_gen_movi_tl(cpu_R[R_PC], dc->pc); | |
796 | gen_helper_raise_exception(cpu_env, tmp); | |
797 | tcg_temp_free_i32(tmp); | |
798 | dc->is_jmp = DISAS_UPDATE; | |
799 | } | |
800 | ||
801 | /* generate intermediate code for basic block 'tb'. */ | |
802 | void gen_intermediate_code(CPUNios2State *env, TranslationBlock *tb) | |
803 | { | |
804 | Nios2CPU *cpu = nios2_env_get_cpu(env); | |
805 | CPUState *cs = CPU(cpu); | |
806 | DisasContext dc1, *dc = &dc1; | |
807 | int num_insns; | |
808 | int max_insns; | |
809 | ||
810 | /* Initialize DC */ | |
811 | dc->cpu_env = cpu_env; | |
812 | dc->cpu_R = cpu_R; | |
813 | dc->is_jmp = DISAS_NEXT; | |
814 | dc->pc = tb->pc; | |
815 | dc->tb = tb; | |
816 | dc->mem_idx = cpu_mmu_index(env, false); | |
817 | dc->singlestep_enabled = cs->singlestep_enabled; | |
818 | ||
819 | /* Set up instruction counts */ | |
820 | num_insns = 0; | |
821 | if (cs->singlestep_enabled || singlestep) { | |
822 | max_insns = 1; | |
823 | } else { | |
824 | int page_insns = (TARGET_PAGE_SIZE - (tb->pc & TARGET_PAGE_MASK)) / 4; | |
825 | max_insns = tb->cflags & CF_COUNT_MASK; | |
826 | if (max_insns == 0) { | |
827 | max_insns = CF_COUNT_MASK; | |
828 | } | |
829 | if (max_insns > page_insns) { | |
830 | max_insns = page_insns; | |
831 | } | |
832 | if (max_insns > TCG_MAX_INSNS) { | |
833 | max_insns = TCG_MAX_INSNS; | |
834 | } | |
835 | } | |
836 | ||
837 | gen_tb_start(tb); | |
838 | do { | |
839 | tcg_gen_insn_start(dc->pc); | |
840 | num_insns++; | |
841 | ||
842 | if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) { | |
843 | gen_exception(dc, EXCP_DEBUG); | |
844 | /* The address covered by the breakpoint must be included in | |
845 | [tb->pc, tb->pc + tb->size) in order to for it to be | |
846 | properly cleared -- thus we increment the PC here so that | |
847 | the logic setting tb->size below does the right thing. */ | |
848 | dc->pc += 4; | |
849 | break; | |
850 | } | |
851 | ||
852 | if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) { | |
853 | gen_io_start(); | |
854 | } | |
855 | ||
856 | /* Decode an instruction */ | |
857 | handle_instruction(dc, env); | |
858 | ||
859 | dc->pc += 4; | |
860 | ||
861 | /* Translation stops when a conditional branch is encountered. | |
862 | * Otherwise the subsequent code could get translated several times. | |
863 | * Also stop translation when a page boundary is reached. This | |
864 | * ensures prefetch aborts occur at the right place. */ | |
865 | } while (!dc->is_jmp && | |
866 | !tcg_op_buf_full() && | |
867 | num_insns < max_insns); | |
868 | ||
869 | if (tb->cflags & CF_LAST_IO) { | |
870 | gen_io_end(); | |
871 | } | |
872 | ||
873 | /* Indicate where the next block should start */ | |
874 | switch (dc->is_jmp) { | |
875 | case DISAS_NEXT: | |
876 | /* Save the current PC back into the CPU register */ | |
877 | tcg_gen_movi_tl(cpu_R[R_PC], dc->pc); | |
878 | tcg_gen_exit_tb(0); | |
879 | break; | |
880 | ||
881 | default: | |
882 | case DISAS_JUMP: | |
883 | case DISAS_UPDATE: | |
884 | /* The jump will already have updated the PC register */ | |
885 | tcg_gen_exit_tb(0); | |
886 | break; | |
887 | ||
888 | case DISAS_TB_JUMP: | |
889 | /* nothing more to generate */ | |
890 | break; | |
891 | } | |
892 | ||
893 | /* End off the block */ | |
894 | gen_tb_end(tb, num_insns); | |
895 | ||
896 | /* Mark instruction starts for the final generated instruction */ | |
897 | tb->size = dc->pc - tb->pc; | |
898 | tb->icount = num_insns; | |
899 | ||
900 | #ifdef DEBUG_DISAS | |
901 | if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) | |
902 | && qemu_log_in_addr_range(tb->pc)) { | |
903 | qemu_log_lock(); | |
904 | qemu_log("IN: %s\n", lookup_symbol(tb->pc)); | |
905 | log_target_disas(cs, tb->pc, dc->pc - tb->pc, 0); | |
906 | qemu_log("\n"); | |
907 | qemu_log_unlock(); | |
908 | } | |
909 | #endif | |
910 | } | |
911 | ||
912 | void nios2_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, | |
913 | int flags) | |
914 | { | |
915 | Nios2CPU *cpu = NIOS2_CPU(cs); | |
916 | CPUNios2State *env = &cpu->env; | |
917 | int i; | |
918 | ||
919 | if (!env || !f) { | |
920 | return; | |
921 | } | |
922 | ||
923 | cpu_fprintf(f, "IN: PC=%x %s\n", | |
924 | env->regs[R_PC], lookup_symbol(env->regs[R_PC])); | |
925 | ||
926 | for (i = 0; i < NUM_CORE_REGS; i++) { | |
927 | cpu_fprintf(f, "%9s=%8.8x ", regnames[i], env->regs[i]); | |
928 | if ((i + 1) % 4 == 0) { | |
929 | cpu_fprintf(f, "\n"); | |
930 | } | |
931 | } | |
932 | #if !defined(CONFIG_USER_ONLY) | |
933 | cpu_fprintf(f, " mmu write: VPN=%05X PID %02X TLBACC %08X\n", | |
934 | env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK, | |
935 | (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4, | |
936 | env->mmu.tlbacc_wr); | |
937 | #endif | |
938 | cpu_fprintf(f, "\n\n"); | |
939 | } | |
940 | ||
941 | void nios2_tcg_init(void) | |
942 | { | |
943 | int i; | |
944 | ||
945 | cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); | |
946 | ||
947 | for (i = 0; i < NUM_CORE_REGS; i++) { | |
948 | cpu_R[i] = tcg_global_mem_new(cpu_env, | |
949 | offsetof(CPUNios2State, regs[i]), | |
950 | regnames[i]); | |
951 | } | |
952 | } | |
953 | ||
954 | void restore_state_to_opc(CPUNios2State *env, TranslationBlock *tb, | |
955 | target_ulong *data) | |
956 | { | |
957 | env->regs[R_PC] = data[0]; | |
958 | } |