1f8da88d7SSong Gao /* SPDX-License-Identifier: GPL-2.0-or-later */ 2f8da88d7SSong Gao /* 3f8da88d7SSong Gao * LoongArch emulation for QEMU - main translation routines. 4f8da88d7SSong Gao * 5f8da88d7SSong Gao * Copyright (c) 2021 Loongson Technology Corporation Limited 6f8da88d7SSong Gao */ 7f8da88d7SSong Gao 8f8da88d7SSong Gao #include "qemu/osdep.h" 9f8da88d7SSong Gao #include "cpu.h" 10f8da88d7SSong Gao #include "tcg/tcg-op.h" 11f8da88d7SSong Gao #include "exec/translator.h" 12f8da88d7SSong Gao #include "exec/helper-proto.h" 13f8da88d7SSong Gao #include "exec/helper-gen.h" 14f8da88d7SSong Gao 15f8da88d7SSong Gao #include "exec/log.h" 16f8da88d7SSong Gao #include "qemu/qemu-print.h" 17d578ca6cSSong Gao #include "fpu/softfloat.h" 18f8da88d7SSong Gao #include "translate.h" 19f8da88d7SSong Gao #include "internals.h" 20f8da88d7SSong Gao 21f8da88d7SSong Gao /* Global register indices */ 22f8da88d7SSong Gao TCGv cpu_gpr[32], cpu_pc; 23f8da88d7SSong Gao static TCGv cpu_lladdr, cpu_llval; 24f8da88d7SSong Gao TCGv_i64 cpu_fpr[32]; 25f8da88d7SSong Gao 26f9bf5074SXiaojuan Yang #include "exec/gen-icount.h" 27f9bf5074SXiaojuan Yang 28f8da88d7SSong Gao #define DISAS_STOP DISAS_TARGET_0 295b1dedfeSXiaojuan Yang #define DISAS_EXIT DISAS_TARGET_1 305b1dedfeSXiaojuan Yang #define DISAS_EXIT_UPDATE DISAS_TARGET_2 31f8da88d7SSong Gao 32143d6785SSong Gao static inline int plus_1(DisasContext *ctx, int x) 33143d6785SSong Gao { 34143d6785SSong Gao return x + 1; 35143d6785SSong Gao } 36143d6785SSong Gao 37bb79174dSSong Gao static inline int shl_2(DisasContext *ctx, int x) 38bb79174dSSong Gao { 39bb79174dSSong Gao return x << 2; 40bb79174dSSong Gao } 41bb79174dSSong Gao 42d578ca6cSSong Gao /* 43d578ca6cSSong Gao * LoongArch the upper 32 bits are undefined ("can be any value"). 44d578ca6cSSong Gao * QEMU chooses to nanbox, because it is most likely to show guest bugs early. 45d578ca6cSSong Gao */ 46d578ca6cSSong Gao static void gen_nanbox_s(TCGv_i64 out, TCGv_i64 in) 47d578ca6cSSong Gao { 48d578ca6cSSong Gao tcg_gen_ori_i64(out, in, MAKE_64BIT_MASK(32, 32)); 49d578ca6cSSong Gao } 50d578ca6cSSong Gao 51f8da88d7SSong Gao void generate_exception(DisasContext *ctx, int excp) 52f8da88d7SSong Gao { 53f8da88d7SSong Gao tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); 54f8da88d7SSong Gao gen_helper_raise_exception(cpu_env, tcg_constant_i32(excp)); 55f8da88d7SSong Gao ctx->base.is_jmp = DISAS_NORETURN; 56f8da88d7SSong Gao } 57f8da88d7SSong Gao 58f8da88d7SSong Gao static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) 59f8da88d7SSong Gao { 60f8da88d7SSong Gao if (translator_use_goto_tb(&ctx->base, dest)) { 61f8da88d7SSong Gao tcg_gen_goto_tb(n); 62f8da88d7SSong Gao tcg_gen_movi_tl(cpu_pc, dest); 63f8da88d7SSong Gao tcg_gen_exit_tb(ctx->base.tb, n); 64f8da88d7SSong Gao } else { 65f8da88d7SSong Gao tcg_gen_movi_tl(cpu_pc, dest); 66f8da88d7SSong Gao tcg_gen_lookup_and_goto_ptr(); 67f8da88d7SSong Gao } 68f8da88d7SSong Gao } 69f8da88d7SSong Gao 70f8da88d7SSong Gao static void loongarch_tr_init_disas_context(DisasContextBase *dcbase, 71f8da88d7SSong Gao CPUState *cs) 72f8da88d7SSong Gao { 73f8da88d7SSong Gao int64_t bound; 74f8da88d7SSong Gao DisasContext *ctx = container_of(dcbase, DisasContext, base); 75f8da88d7SSong Gao 76f8da88d7SSong Gao ctx->page_start = ctx->base.pc_first & TARGET_PAGE_MASK; 77c8885b88SRui Wang ctx->plv = ctx->base.tb->flags & HW_FLAGS_PLV_MASK; 78b4bda200SRui Wang if (ctx->base.tb->flags & HW_FLAGS_CRMD_PG) { 79c8885b88SRui Wang ctx->mem_idx = ctx->plv; 80b4bda200SRui Wang } else { 81c8885b88SRui Wang ctx->mem_idx = MMU_IDX_DA; 82b4bda200SRui Wang } 83f8da88d7SSong Gao 84f8da88d7SSong Gao /* Bound the number of insns to execute to those left on the page. */ 85f8da88d7SSong Gao bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4; 86f8da88d7SSong Gao ctx->base.max_insns = MIN(ctx->base.max_insns, bound); 87143d6785SSong Gao 88143d6785SSong Gao ctx->zero = tcg_constant_tl(0); 89f8da88d7SSong Gao } 90f8da88d7SSong Gao 91f8da88d7SSong Gao static void loongarch_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) 92f8da88d7SSong Gao { 93f8da88d7SSong Gao } 94f8da88d7SSong Gao 95f8da88d7SSong Gao static void loongarch_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) 96f8da88d7SSong Gao { 97f8da88d7SSong Gao DisasContext *ctx = container_of(dcbase, DisasContext, base); 98f8da88d7SSong Gao 99f8da88d7SSong Gao tcg_gen_insn_start(ctx->base.pc_next); 100f8da88d7SSong Gao } 101f8da88d7SSong Gao 102143d6785SSong Gao /* 103143d6785SSong Gao * Wrappers for getting reg values. 104143d6785SSong Gao * 105143d6785SSong Gao * The $zero register does not have cpu_gpr[0] allocated -- we supply the 106143d6785SSong Gao * constant zero as a source, and an uninitialized sink as destination. 107143d6785SSong Gao * 108143d6785SSong Gao * Further, we may provide an extension for word operations. 109143d6785SSong Gao */ 110143d6785SSong Gao static TCGv gpr_src(DisasContext *ctx, int reg_num, DisasExtend src_ext) 111143d6785SSong Gao { 112143d6785SSong Gao TCGv t; 113143d6785SSong Gao 114143d6785SSong Gao if (reg_num == 0) { 115143d6785SSong Gao return ctx->zero; 116143d6785SSong Gao } 117143d6785SSong Gao 118143d6785SSong Gao switch (src_ext) { 119143d6785SSong Gao case EXT_NONE: 120143d6785SSong Gao return cpu_gpr[reg_num]; 121143d6785SSong Gao case EXT_SIGN: 12260a7e25eSRichard Henderson t = tcg_temp_new(); 123143d6785SSong Gao tcg_gen_ext32s_tl(t, cpu_gpr[reg_num]); 124143d6785SSong Gao return t; 125143d6785SSong Gao case EXT_ZERO: 12660a7e25eSRichard Henderson t = tcg_temp_new(); 127143d6785SSong Gao tcg_gen_ext32u_tl(t, cpu_gpr[reg_num]); 128143d6785SSong Gao return t; 129143d6785SSong Gao } 130143d6785SSong Gao g_assert_not_reached(); 131143d6785SSong Gao } 132143d6785SSong Gao 133143d6785SSong Gao static TCGv gpr_dst(DisasContext *ctx, int reg_num, DisasExtend dst_ext) 134143d6785SSong Gao { 135143d6785SSong Gao if (reg_num == 0 || dst_ext) { 13660a7e25eSRichard Henderson return tcg_temp_new(); 137143d6785SSong Gao } 138143d6785SSong Gao return cpu_gpr[reg_num]; 139143d6785SSong Gao } 140143d6785SSong Gao 141143d6785SSong Gao static void gen_set_gpr(int reg_num, TCGv t, DisasExtend dst_ext) 142143d6785SSong Gao { 143143d6785SSong Gao if (reg_num != 0) { 144143d6785SSong Gao switch (dst_ext) { 145143d6785SSong Gao case EXT_NONE: 146143d6785SSong Gao tcg_gen_mov_tl(cpu_gpr[reg_num], t); 147143d6785SSong Gao break; 148143d6785SSong Gao case EXT_SIGN: 149143d6785SSong Gao tcg_gen_ext32s_tl(cpu_gpr[reg_num], t); 150143d6785SSong Gao break; 151143d6785SSong Gao case EXT_ZERO: 152143d6785SSong Gao tcg_gen_ext32u_tl(cpu_gpr[reg_num], t); 153143d6785SSong Gao break; 154143d6785SSong Gao default: 155143d6785SSong Gao g_assert_not_reached(); 156143d6785SSong Gao } 157143d6785SSong Gao } 158143d6785SSong Gao } 159143d6785SSong Gao 160143d6785SSong Gao #include "decode-insns.c.inc" 161143d6785SSong Gao #include "insn_trans/trans_arith.c.inc" 16263cfcd47SSong Gao #include "insn_trans/trans_shift.c.inc" 163ad08cb3fSSong Gao #include "insn_trans/trans_bit.c.inc" 164bb79174dSSong Gao #include "insn_trans/trans_memory.c.inc" 16594b02d57SSong Gao #include "insn_trans/trans_atomic.c.inc" 1668708a04aSSong Gao #include "insn_trans/trans_extra.c.inc" 167d578ca6cSSong Gao #include "insn_trans/trans_farith.c.inc" 1689b741076SSong Gao #include "insn_trans/trans_fcmp.c.inc" 1697c1f8870SSong Gao #include "insn_trans/trans_fcnv.c.inc" 170b7dabd56SSong Gao #include "insn_trans/trans_fmov.c.inc" 171e616bdfdSSong Gao #include "insn_trans/trans_fmemory.c.inc" 172ee86bd58SSong Gao #include "insn_trans/trans_branch.c.inc" 1735b1dedfeSXiaojuan Yang #include "insn_trans/trans_privileged.c.inc" 174143d6785SSong Gao 175f8da88d7SSong Gao static void loongarch_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) 176f8da88d7SSong Gao { 177f8da88d7SSong Gao CPULoongArchState *env = cs->env_ptr; 178f8da88d7SSong Gao DisasContext *ctx = container_of(dcbase, DisasContext, base); 179f8da88d7SSong Gao 180f8da88d7SSong Gao ctx->opcode = cpu_ldl_code(env, ctx->base.pc_next); 181f8da88d7SSong Gao 182f8da88d7SSong Gao if (!decode(ctx, ctx->opcode)) { 183f8da88d7SSong Gao qemu_log_mask(LOG_UNIMP, "Error: unknown opcode. " 184f8da88d7SSong Gao TARGET_FMT_lx ": 0x%x\n", 185f8da88d7SSong Gao ctx->base.pc_next, ctx->opcode); 186f8da88d7SSong Gao generate_exception(ctx, EXCCODE_INE); 187f8da88d7SSong Gao } 188f8da88d7SSong Gao 189f8da88d7SSong Gao ctx->base.pc_next += 4; 190f8da88d7SSong Gao } 191f8da88d7SSong Gao 192f8da88d7SSong Gao static void loongarch_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) 193f8da88d7SSong Gao { 194f8da88d7SSong Gao DisasContext *ctx = container_of(dcbase, DisasContext, base); 195f8da88d7SSong Gao 196f8da88d7SSong Gao switch (ctx->base.is_jmp) { 197f8da88d7SSong Gao case DISAS_STOP: 198f8da88d7SSong Gao tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); 199f8da88d7SSong Gao tcg_gen_lookup_and_goto_ptr(); 200f8da88d7SSong Gao break; 201f8da88d7SSong Gao case DISAS_TOO_MANY: 202f8da88d7SSong Gao gen_goto_tb(ctx, 0, ctx->base.pc_next); 203f8da88d7SSong Gao break; 204f8da88d7SSong Gao case DISAS_NORETURN: 205f8da88d7SSong Gao break; 2065b1dedfeSXiaojuan Yang case DISAS_EXIT_UPDATE: 2075b1dedfeSXiaojuan Yang tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); 2085b1dedfeSXiaojuan Yang QEMU_FALLTHROUGH; 2095b1dedfeSXiaojuan Yang case DISAS_EXIT: 2105b1dedfeSXiaojuan Yang tcg_gen_exit_tb(NULL, 0); 2115b1dedfeSXiaojuan Yang break; 212f8da88d7SSong Gao default: 213f8da88d7SSong Gao g_assert_not_reached(); 214f8da88d7SSong Gao } 215f8da88d7SSong Gao } 216f8da88d7SSong Gao 217f8da88d7SSong Gao static void loongarch_tr_disas_log(const DisasContextBase *dcbase, 218f8da88d7SSong Gao CPUState *cpu, FILE *logfile) 219f8da88d7SSong Gao { 220f8da88d7SSong Gao qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); 221f8da88d7SSong Gao target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); 222f8da88d7SSong Gao } 223f8da88d7SSong Gao 224f8da88d7SSong Gao static const TranslatorOps loongarch_tr_ops = { 225f8da88d7SSong Gao .init_disas_context = loongarch_tr_init_disas_context, 226f8da88d7SSong Gao .tb_start = loongarch_tr_tb_start, 227f8da88d7SSong Gao .insn_start = loongarch_tr_insn_start, 228f8da88d7SSong Gao .translate_insn = loongarch_tr_translate_insn, 229f8da88d7SSong Gao .tb_stop = loongarch_tr_tb_stop, 230f8da88d7SSong Gao .disas_log = loongarch_tr_disas_log, 231f8da88d7SSong Gao }; 232f8da88d7SSong Gao 233597f9b2dSRichard Henderson void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, 234306c8721SRichard Henderson target_ulong pc, void *host_pc) 235f8da88d7SSong Gao { 236f8da88d7SSong Gao DisasContext ctx; 237f8da88d7SSong Gao 238306c8721SRichard Henderson translator_loop(cs, tb, max_insns, pc, host_pc, 239306c8721SRichard Henderson &loongarch_tr_ops, &ctx.base); 240f8da88d7SSong Gao } 241f8da88d7SSong Gao 242f8da88d7SSong Gao void loongarch_translate_init(void) 243f8da88d7SSong Gao { 244f8da88d7SSong Gao int i; 245f8da88d7SSong Gao 246f8da88d7SSong Gao cpu_gpr[0] = NULL; 247f8da88d7SSong Gao for (i = 1; i < 32; i++) { 248f8da88d7SSong Gao cpu_gpr[i] = tcg_global_mem_new(cpu_env, 249f8da88d7SSong Gao offsetof(CPULoongArchState, gpr[i]), 250f8da88d7SSong Gao regnames[i]); 251f8da88d7SSong Gao } 252f8da88d7SSong Gao 253f8da88d7SSong Gao for (i = 0; i < 32; i++) { 254f8da88d7SSong Gao int off = offsetof(CPULoongArchState, fpr[i]); 255f8da88d7SSong Gao cpu_fpr[i] = tcg_global_mem_new_i64(cpu_env, off, fregnames[i]); 256f8da88d7SSong Gao } 257f8da88d7SSong Gao 258f8da88d7SSong Gao cpu_pc = tcg_global_mem_new(cpu_env, offsetof(CPULoongArchState, pc), "pc"); 259f8da88d7SSong Gao cpu_lladdr = tcg_global_mem_new(cpu_env, 260f8da88d7SSong Gao offsetof(CPULoongArchState, lladdr), "lladdr"); 261f8da88d7SSong Gao cpu_llval = tcg_global_mem_new(cpu_env, 262f8da88d7SSong Gao offsetof(CPULoongArchState, llval), "llval"); 263f8da88d7SSong Gao } 264