/* * Altera Nios II emulation for qemu: main translation routines. * * Copyright (C) 2016 Marek Vasut * Copyright (C) 2012 Chris Wulff * Copyright (C) 2010 Tobias Klauser * (Portions of this file that were originally from nios2sim-ng.) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see * */ #include "qemu/osdep.h" #include "cpu.h" #include "tcg-op.h" #include "exec/exec-all.h" #include "disas/disas.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" #include "exec/log.h" #include "exec/cpu_ldst.h" #include "exec/translator.h" #include "qemu/qemu-print.h" /* is_jmp field values */ #define DISAS_JUMP DISAS_TARGET_0 /* only pc was modified dynamically */ #define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */ #define DISAS_TB_JUMP DISAS_TARGET_2 /* only pc was modified statically */ #define INSTRUCTION_FLG(func, flags) { (func), (flags) } #define INSTRUCTION(func) \ INSTRUCTION_FLG(func, 0) #define INSTRUCTION_NOP() \ INSTRUCTION_FLG(nop, 0) #define INSTRUCTION_UNIMPLEMENTED() \ INSTRUCTION_FLG(gen_excp, EXCP_UNIMPL) #define INSTRUCTION_ILLEGAL() \ INSTRUCTION_FLG(gen_excp, EXCP_ILLEGAL) /* Special R-Type instruction opcode */ #define INSN_R_TYPE 0x3A /* I-Type instruction parsing */ #define I_TYPE(instr, code) \ struct { \ uint8_t op; \ union { \ uint16_t u; \ int16_t s; \ } imm16; \ uint8_t b; \ uint8_t a; \ } (instr) = { \ .op = extract32((code), 0, 6), \ .imm16.u = extract32((code), 6, 16), \ .b = extract32((code), 22, 5), \ .a = extract32((code), 27, 5), \ } /* R-Type instruction parsing */ #define R_TYPE(instr, code) \ struct { \ uint8_t op; \ uint8_t imm5; \ uint8_t opx; \ uint8_t c; \ uint8_t b; \ uint8_t a; \ } (instr) = { \ .op = extract32((code), 0, 6), \ .imm5 = extract32((code), 6, 5), \ .opx = extract32((code), 11, 6), \ .c = extract32((code), 17, 5), \ .b = extract32((code), 22, 5), \ .a = extract32((code), 27, 5), \ } /* J-Type instruction parsing */ #define J_TYPE(instr, code) \ struct { \ uint8_t op; \ uint32_t imm26; \ } (instr) = { \ .op = extract32((code), 0, 6), \ .imm26 = extract32((code), 6, 26), \ } typedef struct DisasContext { TCGv_ptr cpu_env; TCGv *cpu_R; TCGv_i32 zero; int is_jmp; target_ulong pc; TranslationBlock *tb; int mem_idx; bool singlestep_enabled; } DisasContext; typedef struct Nios2Instruction { void (*handler)(DisasContext *dc, uint32_t code, uint32_t flags); uint32_t flags; } Nios2Instruction; static uint8_t get_opcode(uint32_t code) { I_TYPE(instr, code); return instr.op; } static uint8_t get_opxcode(uint32_t code) { R_TYPE(instr, code); return instr.opx; } static TCGv load_zero(DisasContext *dc) { if (!dc->zero) { dc->zero = tcg_const_i32(0); } return dc->zero; } static TCGv load_gpr(DisasContext *dc, uint8_t reg) { if (likely(reg != R_ZERO)) { return dc->cpu_R[reg]; } else { return load_zero(dc); } } static void t_gen_helper_raise_exception(DisasContext *dc, uint32_t index) { TCGv_i32 tmp = tcg_const_i32(index); tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc); gen_helper_raise_exception(dc->cpu_env, tmp); tcg_temp_free_i32(tmp); dc->is_jmp = DISAS_UPDATE; } static bool use_goto_tb(DisasContext *dc, uint32_t dest) { if (unlikely(dc->singlestep_enabled)) { return false; } #ifndef CONFIG_USER_ONLY return (dc->tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); #else return true; #endif } static void gen_goto_tb(DisasContext *dc, int n, uint32_t dest) { TranslationBlock *tb = dc->tb; if (use_goto_tb(dc, dest)) { tcg_gen_goto_tb(n); tcg_gen_movi_tl(dc->cpu_R[R_PC], dest); tcg_gen_exit_tb(tb, n); } else { tcg_gen_movi_tl(dc->cpu_R[R_PC], dest); tcg_gen_exit_tb(NULL, 0); } } static void gen_excp(DisasContext *dc, uint32_t code, uint32_t flags) { t_gen_helper_raise_exception(dc, flags); } static void gen_check_supervisor(DisasContext *dc) { if (dc->tb->flags & CR_STATUS_U) { /* CPU in user mode, privileged instruction called, stop. */ t_gen_helper_raise_exception(dc, EXCP_SUPERI); } } /* * Used as a placeholder for all instructions which do not have * an effect on the simulator (e.g. flush, sync) */ static void nop(DisasContext *dc, uint32_t code, uint32_t flags) { /* Nothing to do here */ } /* * J-Type instructions */ static void jmpi(DisasContext *dc, uint32_t code, uint32_t flags) { J_TYPE(instr, code); gen_goto_tb(dc, 0, (dc->pc & 0xF0000000) | (instr.imm26 << 2)); dc->is_jmp = DISAS_TB_JUMP; } static void call(DisasContext *dc, uint32_t code, uint32_t flags) { tcg_gen_movi_tl(dc->cpu_R[R_RA], dc->pc + 4); jmpi(dc, code, flags); } /* * I-Type instructions */ /* Load instructions */ static void gen_ldx(DisasContext *dc, uint32_t code, uint32_t flags) { I_TYPE(instr, code); TCGv addr = tcg_temp_new(); TCGv data; /* * WARNING: Loads into R_ZERO are ignored, but we must generate the * memory access itself to emulate the CPU precisely. Load * from a protected page to R_ZERO will cause SIGSEGV on * the Nios2 CPU. */ if (likely(instr.b != R_ZERO)) { data = dc->cpu_R[instr.b]; } else { data = tcg_temp_new(); } tcg_gen_addi_tl(addr, load_gpr(dc, instr.a), instr.imm16.s); tcg_gen_qemu_ld_tl(data, addr, dc->mem_idx, flags); if (unlikely(instr.b == R_ZERO)) { tcg_temp_free(data); } tcg_temp_free(addr); } /* Store instructions */ static void gen_stx(DisasContext *dc, uint32_t code, uint32_t flags) { I_TYPE(instr, code); TCGv val = load_gpr(dc, instr.b); TCGv addr = tcg_temp_new(); tcg_gen_addi_tl(addr, load_gpr(dc, instr.a), instr.imm16.s); tcg_gen_qemu_st_tl(val, addr, dc->mem_idx, flags); tcg_temp_free(addr); } /* Branch instructions */ static void br(DisasContext *dc, uint32_t code, uint32_t flags) { I_TYPE(instr, code); gen_goto_tb(dc, 0, dc->pc + 4 + (instr.imm16.s & -4)); dc->is_jmp = DISAS_TB_JUMP; } static void gen_bxx(DisasContext *dc, uint32_t code, uint32_t flags) { I_TYPE(instr, code); TCGLabel *l1 = gen_new_label(); tcg_gen_brcond_tl(flags, dc->cpu_R[instr.a], dc->cpu_R[instr.b], l1); gen_goto_tb(dc, 0, dc->pc + 4); gen_set_label(l1); gen_goto_tb(dc, 1, dc->pc + 4 + (instr.imm16.s & -4)); dc->is_jmp = DISAS_TB_JUMP; } /* Comparison instructions */ #define gen_i_cmpxx(fname, op3) \ static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ { \ I_TYPE(instr, (code)); \ tcg_gen_setcondi_tl(flags, (dc)->cpu_R[instr.b], (dc)->cpu_R[instr.a], \ (op3)); \ } gen_i_cmpxx(gen_cmpxxsi, instr.imm16.s) gen_i_cmpxx(gen_cmpxxui, instr.imm16.u) /* Math/logic instructions */ #define gen_i_math_logic(fname, insn, resimm, op3) \ static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ { \ I_TYPE(instr, (code)); \ if (unlikely(instr.b == R_ZERO)) { /* Store to R_ZERO is ignored */ \ return; \ } else if (instr.a == R_ZERO) { /* MOVxI optimizations */ \ tcg_gen_movi_tl(dc->cpu_R[instr.b], (resimm) ? (op3) : 0); \ } else { \ tcg_gen_##insn##_tl((dc)->cpu_R[instr.b], (dc)->cpu_R[instr.a], \ (op3)); \ } \ } gen_i_math_logic(addi, addi, 1, instr.imm16.s) gen_i_math_logic(muli, muli, 0, instr.imm16.s) gen_i_math_logic(andi, andi, 0, instr.imm16.u) gen_i_math_logic(ori, ori, 1, instr.imm16.u) gen_i_math_logic(xori, xori, 1, instr.imm16.u) gen_i_math_logic(andhi, andi, 0, instr.imm16.u << 16) gen_i_math_logic(orhi , ori, 1, instr.imm16.u << 16) gen_i_math_logic(xorhi, xori, 1, instr.imm16.u << 16) /* Prototype only, defined below */ static void handle_r_type_instr(DisasContext *dc, uint32_t code, uint32_t flags); static const Nios2Instruction i_type_instructions[] = { INSTRUCTION(call), /* call */ INSTRUCTION(jmpi), /* jmpi */ INSTRUCTION_ILLEGAL(), INSTRUCTION_FLG(gen_ldx, MO_UB), /* ldbu */ INSTRUCTION(addi), /* addi */ INSTRUCTION_FLG(gen_stx, MO_UB), /* stb */ INSTRUCTION(br), /* br */ INSTRUCTION_FLG(gen_ldx, MO_SB), /* ldb */ INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_GE), /* cmpgei */ INSTRUCTION_ILLEGAL(), INSTRUCTION_ILLEGAL(), INSTRUCTION_FLG(gen_ldx, MO_UW), /* ldhu */ INSTRUCTION(andi), /* andi */ INSTRUCTION_FLG(gen_stx, MO_UW), /* sth */ INSTRUCTION_FLG(gen_bxx, TCG_COND_GE), /* bge */ INSTRUCTION_FLG(gen_ldx, MO_SW), /* ldh */ INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_LT), /* cmplti */ INSTRUCTION_ILLEGAL(), INSTRUCTION_ILLEGAL(), INSTRUCTION_NOP(), /* initda */ INSTRUCTION(ori), /* ori */ INSTRUCTION_FLG(gen_stx, MO_UL), /* stw */ INSTRUCTION_FLG(gen_bxx, TCG_COND_LT), /* blt */ INSTRUCTION_FLG(gen_ldx, MO_UL), /* ldw */ INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_NE), /* cmpnei */ INSTRUCTION_ILLEGAL(), INSTRUCTION_ILLEGAL(), INSTRUCTION_NOP(), /* flushda */ INSTRUCTION(xori), /* xori */ INSTRUCTION_ILLEGAL(), INSTRUCTION_FLG(gen_bxx, TCG_COND_NE), /* bne */ INSTRUCTION_ILLEGAL(), INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_EQ), /* cmpeqi */ INSTRUCTION_ILLEGAL(), INSTRUCTION_ILLEGAL(), INSTRUCTION_FLG(gen_ldx, MO_UB), /* ldbuio */ INSTRUCTION(muli), /* muli */ INSTRUCTION_FLG(gen_stx, MO_UB), /* stbio */ INSTRUCTION_FLG(gen_bxx, TCG_COND_EQ), /* beq */ INSTRUCTION_FLG(gen_ldx, MO_SB), /* ldbio */ INSTRUCTION_FLG(gen_cmpxxui, TCG_COND_GEU), /* cmpgeui */ INSTRUCTION_ILLEGAL(), INSTRUCTION_ILLEGAL(), INSTRUCTION_FLG(gen_ldx, MO_UW), /* ldhuio */ INSTRUCTION(andhi), /* andhi */ INSTRUCTION_FLG(gen_stx, MO_UW), /* sthio */ INSTRUCTION_FLG(gen_bxx, TCG_COND_GEU), /* bgeu */ INSTRUCTION_FLG(gen_ldx, MO_SW), /* ldhio */ INSTRUCTION_FLG(gen_cmpxxui, TCG_COND_LTU), /* cmpltui */ INSTRUCTION_ILLEGAL(), INSTRUCTION_UNIMPLEMENTED(), /* custom */ INSTRUCTION_NOP(), /* initd */ INSTRUCTION(orhi), /* orhi */ INSTRUCTION_FLG(gen_stx, MO_SL), /* stwio */ INSTRUCTION_FLG(gen_bxx, TCG_COND_LTU), /* bltu */ INSTRUCTION_FLG(gen_ldx, MO_UL), /* ldwio */ INSTRUCTION_UNIMPLEMENTED(), /* rdprs */ INSTRUCTION_ILLEGAL(), INSTRUCTION_FLG(handle_r_type_instr, 0), /* R-Type */ INSTRUCTION_NOP(), /* flushd */ INSTRUCTION(xorhi), /* xorhi */ INSTRUCTION_ILLEGAL(), INSTRUCTION_ILLEGAL(), INSTRUCTION_ILLEGAL(), }; /* * R-Type instructions */ /* * status <- estatus * PC <- ea */ static void eret(DisasContext *dc, uint32_t code, uint32_t flags) { tcg_gen_mov_tl(dc->cpu_R[CR_STATUS], dc->cpu_R[CR_ESTATUS]); tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_EA]); dc->is_jmp = DISAS_JUMP; } /* PC <- ra */ static void ret(DisasContext *dc, uint32_t code, uint32_t flags) { tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_RA]); dc->is_jmp = DISAS_JUMP; } /* PC <- ba */ static void bret(DisasContext *dc, uint32_t code, uint32_t flags) { tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_BA]); dc->is_jmp = DISAS_JUMP; } /* PC <- rA */ static void jmp(DisasContext *dc, uint32_t code, uint32_t flags) { R_TYPE(instr, code); tcg_gen_mov_tl(dc->cpu_R[R_PC], load_gpr(dc, instr.a)); dc->is_jmp = DISAS_JUMP; } /* rC <- PC + 4 */ static void nextpc(DisasContext *dc, uint32_t code, uint32_t flags) { R_TYPE(instr, code); if (likely(instr.c != R_ZERO)) { tcg_gen_movi_tl(dc->cpu_R[instr.c], dc->pc + 4); } } /* * ra <- PC + 4 * PC <- rA */ static void callr(DisasContext *dc, uint32_t code, uint32_t flags) { R_TYPE(instr, code); tcg_gen_mov_tl(dc->cpu_R[R_PC], load_gpr(dc, instr.a)); tcg_gen_movi_tl(dc->cpu_R[R_RA], dc->pc + 4); dc->is_jmp = DISAS_JUMP; } /* rC <- ctlN */ static void rdctl(DisasContext *dc, uint32_t code, uint32_t flags) { R_TYPE(instr, code); gen_check_supervisor(dc); switch (instr.imm5 + CR_BASE) { case CR_PTEADDR: case CR_TLBACC: case CR_TLBMISC: { #if !defined(CONFIG_USER_ONLY) if (likely(instr.c != R_ZERO)) { tcg_gen_mov_tl(dc->cpu_R[instr.c], dc->cpu_R[instr.imm5 + CR_BASE]); #ifdef DEBUG_MMU TCGv_i32 tmp = tcg_const_i32(instr.imm5 + CR_BASE); gen_helper_mmu_read_debug(dc->cpu_R[instr.c], dc->cpu_env, tmp); tcg_temp_free_i32(tmp); #endif } #endif break; } default: if (likely(instr.c != R_ZERO)) { tcg_gen_mov_tl(dc->cpu_R[instr.c], dc->cpu_R[instr.imm5 + CR_BASE]); } break; } } /* ctlN <- rA */ static void wrctl(DisasContext *dc, uint32_t code, uint32_t flags) { R_TYPE(instr, code); gen_check_supervisor(dc); switch (instr.imm5 + CR_BASE) { case CR_PTEADDR: case CR_TLBACC: case CR_TLBMISC: { #if !defined(CONFIG_USER_ONLY) TCGv_i32 tmp = tcg_const_i32(instr.imm5 + CR_BASE); gen_helper_mmu_write(dc->cpu_env, tmp, load_gpr(dc, instr.a)); tcg_temp_free_i32(tmp); #endif break; } default: tcg_gen_mov_tl(dc->cpu_R[instr.imm5 + CR_BASE], load_gpr(dc, instr.a)); break; } /* If interrupts were enabled using WRCTL, trigger them. */ #if !defined(CONFIG_USER_ONLY) if ((instr.imm5 + CR_BASE) == CR_STATUS) { gen_helper_check_interrupts(dc->cpu_env); } #endif } /* Comparison instructions */ static void gen_cmpxx(DisasContext *dc, uint32_t code, uint32_t flags) { R_TYPE(instr, code); if (likely(instr.c != R_ZERO)) { tcg_gen_setcond_tl(flags, dc->cpu_R[instr.c], dc->cpu_R[instr.a], dc->cpu_R[instr.b]); } } /* Math/logic instructions */ #define gen_r_math_logic(fname, insn, op3) \ static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ { \ R_TYPE(instr, (code)); \ if (likely(instr.c != R_ZERO)) { \ tcg_gen_##insn((dc)->cpu_R[instr.c], load_gpr((dc), instr.a), \ (op3)); \ } \ } gen_r_math_logic(add, add_tl, load_gpr(dc, instr.b)) gen_r_math_logic(sub, sub_tl, load_gpr(dc, instr.b)) gen_r_math_logic(mul, mul_tl, load_gpr(dc, instr.b)) gen_r_math_logic(and, and_tl, load_gpr(dc, instr.b)) gen_r_math_logic(or, or_tl, load_gpr(dc, instr.b)) gen_r_math_logic(xor, xor_tl, load_gpr(dc, instr.b)) gen_r_math_logic(nor, nor_tl, load_gpr(dc, instr.b)) gen_r_math_logic(srai, sari_tl, instr.imm5) gen_r_math_logic(srli, shri_tl, instr.imm5) gen_r_math_logic(slli, shli_tl, instr.imm5) gen_r_math_logic(roli, rotli_tl, instr.imm5) #define gen_r_mul(fname, insn) \ static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ { \ R_TYPE(instr, (code)); \ if (likely(instr.c != R_ZERO)) { \ TCGv t0 = tcg_temp_new(); \ tcg_gen_##insn(t0, dc->cpu_R[instr.c], \ load_gpr(dc, instr.a), load_gpr(dc, instr.b)); \ tcg_temp_free(t0); \ } \ } gen_r_mul(mulxss, muls2_tl) gen_r_mul(mulxuu, mulu2_tl) gen_r_mul(mulxsu, mulsu2_tl) #define gen_r_shift_s(fname, insn) \ static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ { \ R_TYPE(instr, (code)); \ if (likely(instr.c != R_ZERO)) { \ TCGv t0 = tcg_temp_new(); \ tcg_gen_andi_tl(t0, load_gpr((dc), instr.b), 31); \ tcg_gen_##insn((dc)->cpu_R[instr.c], load_gpr((dc), instr.a), t0); \ tcg_temp_free(t0); \ } \ } gen_r_shift_s(sra, sar_tl) gen_r_shift_s(srl, shr_tl) gen_r_shift_s(sll, shl_tl) gen_r_shift_s(rol, rotl_tl) gen_r_shift_s(ror, rotr_tl) static void divs(DisasContext *dc, uint32_t code, uint32_t flags) { R_TYPE(instr, (code)); /* Stores into R_ZERO are ignored */ if (unlikely(instr.c == R_ZERO)) { return; } TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); TCGv t2 = tcg_temp_new(); TCGv t3 = tcg_temp_new(); tcg_gen_ext32s_tl(t0, load_gpr(dc, instr.a)); tcg_gen_ext32s_tl(t1, load_gpr(dc, instr.b)); tcg_gen_setcondi_tl(TCG_COND_EQ, t2, t0, INT_MIN); tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, -1); tcg_gen_and_tl(t2, t2, t3); tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0); tcg_gen_or_tl(t2, t2, t3); tcg_gen_movi_tl(t3, 0); tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); tcg_gen_div_tl(dc->cpu_R[instr.c], t0, t1); tcg_gen_ext32s_tl(dc->cpu_R[instr.c], dc->cpu_R[instr.c]); tcg_temp_free(t3); tcg_temp_free(t2); tcg_temp_free(t1); tcg_temp_free(t0); } static void divu(DisasContext *dc, uint32_t code, uint32_t flags) { R_TYPE(instr, (code)); /* Stores into R_ZERO are ignored */ if (unlikely(instr.c == R_ZERO)) { return; } TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); TCGv t2 = tcg_const_tl(0); TCGv t3 = tcg_const_tl(1); tcg_gen_ext32u_tl(t0, load_gpr(dc, instr.a)); tcg_gen_ext32u_tl(t1, load_gpr(dc, instr.b)); tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); tcg_gen_divu_tl(dc->cpu_R[instr.c], t0, t1); tcg_gen_ext32s_tl(dc->cpu_R[instr.c], dc->cpu_R[instr.c]); tcg_temp_free(t3); tcg_temp_free(t2); tcg_temp_free(t1); tcg_temp_free(t0); } static const Nios2Instruction r_type_instructions[] = { INSTRUCTION_ILLEGAL(), INSTRUCTION(eret), /* eret */ INSTRUCTION(roli), /* roli */ INSTRUCTION(rol), /* rol */ INSTRUCTION_NOP(), /* flushp */ INSTRUCTION(ret), /* ret */ INSTRUCTION(nor), /* nor */ INSTRUCTION(mulxuu), /* mulxuu */ INSTRUCTION_FLG(gen_cmpxx, TCG_COND_GE), /* cmpge */ INSTRUCTION(bret), /* bret */ INSTRUCTION_ILLEGAL(), INSTRUCTION(ror), /* ror */ INSTRUCTION_NOP(), /* flushi */ INSTRUCTION(jmp), /* jmp */ INSTRUCTION(and), /* and */ INSTRUCTION_ILLEGAL(), INSTRUCTION_FLG(gen_cmpxx, TCG_COND_LT), /* cmplt */ INSTRUCTION_ILLEGAL(), INSTRUCTION(slli), /* slli */ INSTRUCTION(sll), /* sll */ INSTRUCTION_UNIMPLEMENTED(), /* wrprs */ INSTRUCTION_ILLEGAL(), INSTRUCTION(or), /* or */ INSTRUCTION(mulxsu), /* mulxsu */ INSTRUCTION_FLG(gen_cmpxx, TCG_COND_NE), /* cmpne */ INSTRUCTION_ILLEGAL(), INSTRUCTION(srli), /* srli */ INSTRUCTION(srl), /* srl */ INSTRUCTION(nextpc), /* nextpc */ INSTRUCTION(callr), /* callr */ INSTRUCTION(xor), /* xor */ INSTRUCTION(mulxss), /* mulxss */ INSTRUCTION_FLG(gen_cmpxx, TCG_COND_EQ), /* cmpeq */ INSTRUCTION_ILLEGAL(), INSTRUCTION_ILLEGAL(), INSTRUCTION_ILLEGAL(), INSTRUCTION(divu), /* divu */ INSTRUCTION(divs), /* div */ INSTRUCTION(rdctl), /* rdctl */ INSTRUCTION(mul), /* mul */ INSTRUCTION_FLG(gen_cmpxx, TCG_COND_GEU), /* cmpgeu */ INSTRUCTION_NOP(), /* initi */ INSTRUCTION_ILLEGAL(), INSTRUCTION_ILLEGAL(), INSTRUCTION_ILLEGAL(), INSTRUCTION_FLG(gen_excp, EXCP_TRAP), /* trap */ INSTRUCTION(wrctl), /* wrctl */ INSTRUCTION_ILLEGAL(), INSTRUCTION_FLG(gen_cmpxx, TCG_COND_LTU), /* cmpltu */ INSTRUCTION(add), /* add */ INSTRUCTION_ILLEGAL(), INSTRUCTION_ILLEGAL(), INSTRUCTION_FLG(gen_excp, EXCP_BREAK), /* break */ INSTRUCTION_ILLEGAL(), INSTRUCTION(nop), /* nop */ INSTRUCTION_ILLEGAL(), INSTRUCTION_ILLEGAL(), INSTRUCTION(sub), /* sub */ INSTRUCTION(srai), /* srai */ INSTRUCTION(sra), /* sra */ INSTRUCTION_ILLEGAL(), INSTRUCTION_ILLEGAL(), INSTRUCTION_ILLEGAL(), INSTRUCTION_ILLEGAL(), }; static void handle_r_type_instr(DisasContext *dc, uint32_t code, uint32_t flags) { uint8_t opx; const Nios2Instruction *instr; opx = get_opxcode(code); if (unlikely(opx >= ARRAY_SIZE(r_type_instructions))) { goto illegal_op; } instr = &r_type_instructions[opx]; instr->handler(dc, code, instr->flags); return; illegal_op: t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); } static void handle_instruction(DisasContext *dc, CPUNios2State *env) { uint32_t code; uint8_t op; const Nios2Instruction *instr; #if defined(CONFIG_USER_ONLY) /* FIXME: Is this needed ? */ if (dc->pc >= 0x1000 && dc->pc < 0x2000) { env->regs[R_PC] = dc->pc; t_gen_helper_raise_exception(dc, 0xaa); return; } #endif code = cpu_ldl_code(env, dc->pc); op = get_opcode(code); if (unlikely(op >= ARRAY_SIZE(i_type_instructions))) { goto illegal_op; } dc->zero = NULL; instr = &i_type_instructions[op]; instr->handler(dc, code, instr->flags); if (dc->zero) { tcg_temp_free(dc->zero); } return; illegal_op: t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); } static const char * const regnames[] = { "zero", "at", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", "et", "bt", "gp", "sp", "fp", "ea", "ba", "ra", "status", "estatus", "bstatus", "ienable", "ipending", "cpuid", "reserved0", "exception", "pteaddr", "tlbacc", "tlbmisc", "reserved1", "badaddr", "config", "mpubase", "mpuacc", "reserved2", "reserved3", "reserved4", "reserved5", "reserved6", "reserved7", "reserved8", "reserved9", "reserved10", "reserved11", "reserved12", "reserved13", "reserved14", "reserved15", "reserved16", "reserved17", "rpc" }; static TCGv cpu_R[NUM_CORE_REGS]; #include "exec/gen-icount.h" static void gen_exception(DisasContext *dc, uint32_t excp) { TCGv_i32 tmp = tcg_const_i32(excp); tcg_gen_movi_tl(cpu_R[R_PC], dc->pc); gen_helper_raise_exception(cpu_env, tmp); tcg_temp_free_i32(tmp); dc->is_jmp = DISAS_UPDATE; } /* generate intermediate code for basic block 'tb'. */ void gen_intermediate_code(CPUState *cs, TranslationBlock *tb) { CPUNios2State *env = cs->env_ptr; DisasContext dc1, *dc = &dc1; int num_insns; int max_insns; /* Initialize DC */ dc->cpu_env = cpu_env; dc->cpu_R = cpu_R; dc->is_jmp = DISAS_NEXT; dc->pc = tb->pc; dc->tb = tb; dc->mem_idx = cpu_mmu_index(env, false); dc->singlestep_enabled = cs->singlestep_enabled; /* Set up instruction counts */ num_insns = 0; if (cs->singlestep_enabled || singlestep) { max_insns = 1; } else { int page_insns = (TARGET_PAGE_SIZE - (tb->pc & TARGET_PAGE_MASK)) / 4; max_insns = tb_cflags(tb) & CF_COUNT_MASK; if (max_insns == 0) { max_insns = CF_COUNT_MASK; } if (max_insns > page_insns) { max_insns = page_insns; } if (max_insns > TCG_MAX_INSNS) { max_insns = TCG_MAX_INSNS; } } gen_tb_start(tb); do { tcg_gen_insn_start(dc->pc); num_insns++; if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) { gen_exception(dc, EXCP_DEBUG); /* The address covered by the breakpoint must be included in [tb->pc, tb->pc + tb->size) in order to for it to be properly cleared -- thus we increment the PC here so that the logic setting tb->size below does the right thing. */ dc->pc += 4; break; } if (num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { gen_io_start(); } /* Decode an instruction */ handle_instruction(dc, env); dc->pc += 4; /* Translation stops when a conditional branch is encountered. * Otherwise the subsequent code could get translated several times. * Also stop translation when a page boundary is reached. This * ensures prefetch aborts occur at the right place. */ } while (!dc->is_jmp && !tcg_op_buf_full() && num_insns < max_insns); if (tb_cflags(tb) & CF_LAST_IO) { gen_io_end(); } /* Indicate where the next block should start */ switch (dc->is_jmp) { case DISAS_NEXT: /* Save the current PC back into the CPU register */ tcg_gen_movi_tl(cpu_R[R_PC], dc->pc); tcg_gen_exit_tb(NULL, 0); break; default: case DISAS_JUMP: case DISAS_UPDATE: /* The jump will already have updated the PC register */ tcg_gen_exit_tb(NULL, 0); break; case DISAS_TB_JUMP: /* nothing more to generate */ break; } /* End off the block */ gen_tb_end(tb, num_insns); /* Mark instruction starts for the final generated instruction */ tb->size = dc->pc - tb->pc; tb->icount = num_insns; #ifdef DEBUG_DISAS if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) && qemu_log_in_addr_range(tb->pc)) { qemu_log_lock(); qemu_log("IN: %s\n", lookup_symbol(tb->pc)); log_target_disas(cs, tb->pc, dc->pc - tb->pc); qemu_log("\n"); qemu_log_unlock(); } #endif } void nios2_cpu_dump_state(CPUState *cs, FILE *f, int flags) { Nios2CPU *cpu = NIOS2_CPU(cs); CPUNios2State *env = &cpu->env; int i; if (!env) { return; } qemu_fprintf(f, "IN: PC=%x %s\n", env->regs[R_PC], lookup_symbol(env->regs[R_PC])); for (i = 0; i < NUM_CORE_REGS; i++) { qemu_fprintf(f, "%9s=%8.8x ", regnames[i], env->regs[i]); if ((i + 1) % 4 == 0) { qemu_fprintf(f, "\n"); } } #if !defined(CONFIG_USER_ONLY) qemu_fprintf(f, " mmu write: VPN=%05X PID %02X TLBACC %08X\n", env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK, (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4, env->mmu.tlbacc_wr); #endif qemu_fprintf(f, "\n\n"); } void nios2_tcg_init(void) { int i; for (i = 0; i < NUM_CORE_REGS; i++) { cpu_R[i] = tcg_global_mem_new(cpu_env, offsetof(CPUNios2State, regs[i]), regnames[i]); } } void restore_state_to_opc(CPUNios2State *env, TranslationBlock *tb, target_ulong *data) { env->regs[R_PC] = data[0]; }