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