]>
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 | ||
9d808657 | 24 | #include "qemu/osdep.h" |
032c76bc CW |
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" | |
77fc6f5e | 33 | #include "exec/translator.h" |
90c84c56 | 34 | #include "qemu/qemu-print.h" |
77fc6f5e LV |
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 */ | |
032c76bc CW |
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 { \ | |
4ae4b609 PB |
59 | uint16_t u; \ |
60 | int16_t s; \ | |
61 | } imm16; \ | |
032c76bc CW |
62 | uint8_t b; \ |
63 | uint8_t a; \ | |
64 | } (instr) = { \ | |
65 | .op = extract32((code), 0, 6), \ | |
4ae4b609 | 66 | .imm16.u = extract32((code), 6, 16), \ |
032c76bc CW |
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 | { | |
f764718d | 129 | if (!dc->zero) { |
032c76bc CW |
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); | |
07ea28b4 | 175 | tcg_gen_exit_tb(tb, n); |
032c76bc CW |
176 | } else { |
177 | tcg_gen_movi_tl(dc->cpu_R[R_PC], dest); | |
07ea28b4 | 178 | tcg_gen_exit_tb(NULL, 0); |
032c76bc CW |
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 | ||
4ae4b609 | 243 | tcg_gen_addi_tl(addr, load_gpr(dc, instr.a), instr.imm16.s); |
032c76bc CW |
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(); | |
4ae4b609 | 260 | tcg_gen_addi_tl(addr, load_gpr(dc, instr.a), instr.imm16.s); |
032c76bc CW |
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 | ||
4ae4b609 | 270 | gen_goto_tb(dc, 0, dc->pc + 4 + (instr.imm16.s & -4)); |
032c76bc CW |
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); | |
4ae4b609 | 282 | gen_goto_tb(dc, 1, dc->pc + 4 + (instr.imm16.s & -4)); |
032c76bc CW |
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 | ||
4ae4b609 PB |
295 | gen_i_cmpxx(gen_cmpxxsi, instr.imm16.s) |
296 | gen_i_cmpxx(gen_cmpxxui, instr.imm16.u) | |
032c76bc CW |
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 | ||
4ae4b609 PB |
313 | gen_i_math_logic(addi, addi, 1, instr.imm16.s) |
314 | gen_i_math_logic(muli, muli, 0, instr.imm16.s) | |
032c76bc | 315 | |
4ae4b609 PB |
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) | |
032c76bc | 319 | |
4ae4b609 PB |
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) | |
032c76bc CW |
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 | ||
f764718d | 759 | dc->zero = NULL; |
032c76bc CW |
760 | |
761 | instr = &i_type_instructions[op]; | |
762 | instr->handler(dc, code, instr->flags); | |
763 | ||
f764718d | 764 | if (dc->zero) { |
032c76bc CW |
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 | ||
032c76bc CW |
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'. */ | |
8b86d6d2 | 809 | void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) |
032c76bc | 810 | { |
9c489ea6 | 811 | CPUNios2State *env = cs->env_ptr; |
032c76bc CW |
812 | DisasContext dc1, *dc = &dc1; |
813 | int num_insns; | |
032c76bc CW |
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; | |
8b86d6d2 | 826 | if (max_insns > 1) { |
032c76bc | 827 | int page_insns = (TARGET_PAGE_SIZE - (tb->pc & TARGET_PAGE_MASK)) / 4; |
032c76bc CW |
828 | if (max_insns > page_insns) { |
829 | max_insns = page_insns; | |
830 | } | |
032c76bc CW |
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 | ||
c5a49c63 | 848 | if (num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { |
032c76bc CW |
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 | ||
c5a49c63 | 865 | if (tb_cflags(tb) & CF_LAST_IO) { |
032c76bc CW |
866 | gen_io_end(); |
867 | } | |
868 | ||
869 | /* Indicate where the next block should start */ | |
870 | switch (dc->is_jmp) { | |
871 | case DISAS_NEXT: | |
872 | /* Save the current PC back into the CPU register */ | |
873 | tcg_gen_movi_tl(cpu_R[R_PC], dc->pc); | |
07ea28b4 | 874 | tcg_gen_exit_tb(NULL, 0); |
032c76bc CW |
875 | break; |
876 | ||
877 | default: | |
878 | case DISAS_JUMP: | |
879 | case DISAS_UPDATE: | |
880 | /* The jump will already have updated the PC register */ | |
07ea28b4 | 881 | tcg_gen_exit_tb(NULL, 0); |
032c76bc CW |
882 | break; |
883 | ||
884 | case DISAS_TB_JUMP: | |
885 | /* nothing more to generate */ | |
886 | break; | |
887 | } | |
888 | ||
889 | /* End off the block */ | |
890 | gen_tb_end(tb, num_insns); | |
891 | ||
892 | /* Mark instruction starts for the final generated instruction */ | |
893 | tb->size = dc->pc - tb->pc; | |
894 | tb->icount = num_insns; | |
895 | ||
896 | #ifdef DEBUG_DISAS | |
897 | if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) | |
898 | && qemu_log_in_addr_range(tb->pc)) { | |
899 | qemu_log_lock(); | |
900 | qemu_log("IN: %s\n", lookup_symbol(tb->pc)); | |
1d48474d | 901 | log_target_disas(cs, tb->pc, dc->pc - tb->pc); |
032c76bc CW |
902 | qemu_log("\n"); |
903 | qemu_log_unlock(); | |
904 | } | |
905 | #endif | |
906 | } | |
907 | ||
90c84c56 | 908 | void nios2_cpu_dump_state(CPUState *cs, FILE *f, int flags) |
032c76bc CW |
909 | { |
910 | Nios2CPU *cpu = NIOS2_CPU(cs); | |
911 | CPUNios2State *env = &cpu->env; | |
912 | int i; | |
913 | ||
90c84c56 | 914 | if (!env) { |
032c76bc CW |
915 | return; |
916 | } | |
917 | ||
90c84c56 MA |
918 | qemu_fprintf(f, "IN: PC=%x %s\n", |
919 | env->regs[R_PC], lookup_symbol(env->regs[R_PC])); | |
032c76bc CW |
920 | |
921 | for (i = 0; i < NUM_CORE_REGS; i++) { | |
90c84c56 | 922 | qemu_fprintf(f, "%9s=%8.8x ", regnames[i], env->regs[i]); |
032c76bc | 923 | if ((i + 1) % 4 == 0) { |
90c84c56 | 924 | qemu_fprintf(f, "\n"); |
032c76bc CW |
925 | } |
926 | } | |
927 | #if !defined(CONFIG_USER_ONLY) | |
90c84c56 MA |
928 | qemu_fprintf(f, " mmu write: VPN=%05X PID %02X TLBACC %08X\n", |
929 | env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK, | |
930 | (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4, | |
931 | env->mmu.tlbacc_wr); | |
032c76bc | 932 | #endif |
90c84c56 | 933 | qemu_fprintf(f, "\n\n"); |
032c76bc CW |
934 | } |
935 | ||
936 | void nios2_tcg_init(void) | |
937 | { | |
938 | int i; | |
939 | ||
032c76bc CW |
940 | for (i = 0; i < NUM_CORE_REGS; i++) { |
941 | cpu_R[i] = tcg_global_mem_new(cpu_env, | |
942 | offsetof(CPUNios2State, regs[i]), | |
943 | regnames[i]); | |
944 | } | |
945 | } | |
946 | ||
947 | void restore_state_to_opc(CPUNios2State *env, TranslationBlock *tb, | |
948 | target_ulong *data) | |
949 | { | |
950 | env->regs[R_PC] = data[0]; | |
951 | } |