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