1 /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 /* 3 * LoongArch emulation for QEMU - main translation routines. 4 * 5 * Copyright (c) 2021 Loongson Technology Corporation Limited 6 */ 7 8 #include "qemu/osdep.h" 9 #include "cpu.h" 10 #include "tcg/tcg-op.h" 11 #include "exec/translator.h" 12 #include "exec/helper-proto.h" 13 #include "exec/helper-gen.h" 14 15 #include "exec/log.h" 16 #include "qemu/qemu-print.h" 17 #include "fpu/softfloat.h" 18 #include "translate.h" 19 #include "internals.h" 20 21 /* Global register indices */ 22 TCGv cpu_gpr[32], cpu_pc; 23 static TCGv cpu_lladdr, cpu_llval; 24 TCGv_i64 cpu_fpr[32]; 25 26 #include "exec/gen-icount.h" 27 28 #define DISAS_STOP DISAS_TARGET_0 29 #define DISAS_EXIT DISAS_TARGET_1 30 #define DISAS_EXIT_UPDATE DISAS_TARGET_2 31 32 static inline int plus_1(DisasContext *ctx, int x) 33 { 34 return x + 1; 35 } 36 37 static inline int shl_2(DisasContext *ctx, int x) 38 { 39 return x << 2; 40 } 41 42 /* 43 * LoongArch the upper 32 bits are undefined ("can be any value"). 44 * QEMU chooses to nanbox, because it is most likely to show guest bugs early. 45 */ 46 static void gen_nanbox_s(TCGv_i64 out, TCGv_i64 in) 47 { 48 tcg_gen_ori_i64(out, in, MAKE_64BIT_MASK(32, 32)); 49 } 50 51 void generate_exception(DisasContext *ctx, int excp) 52 { 53 tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); 54 gen_helper_raise_exception(cpu_env, tcg_constant_i32(excp)); 55 ctx->base.is_jmp = DISAS_NORETURN; 56 } 57 58 static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) 59 { 60 if (translator_use_goto_tb(&ctx->base, dest)) { 61 tcg_gen_goto_tb(n); 62 tcg_gen_movi_tl(cpu_pc, dest); 63 tcg_gen_exit_tb(ctx->base.tb, n); 64 } else { 65 tcg_gen_movi_tl(cpu_pc, dest); 66 tcg_gen_lookup_and_goto_ptr(); 67 } 68 } 69 70 static void loongarch_tr_init_disas_context(DisasContextBase *dcbase, 71 CPUState *cs) 72 { 73 int64_t bound; 74 DisasContext *ctx = container_of(dcbase, DisasContext, base); 75 76 ctx->page_start = ctx->base.pc_first & TARGET_PAGE_MASK; 77 ctx->plv = ctx->base.tb->flags & HW_FLAGS_PLV_MASK; 78 if (ctx->base.tb->flags & HW_FLAGS_CRMD_PG) { 79 ctx->mem_idx = ctx->plv; 80 } else { 81 ctx->mem_idx = MMU_IDX_DA; 82 } 83 84 /* Bound the number of insns to execute to those left on the page. */ 85 bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4; 86 ctx->base.max_insns = MIN(ctx->base.max_insns, bound); 87 88 ctx->zero = tcg_constant_tl(0); 89 } 90 91 static void loongarch_tr_tb_start(DisasContextBase *dcbase, CPUState *cs) 92 { 93 } 94 95 static void loongarch_tr_insn_start(DisasContextBase *dcbase, CPUState *cs) 96 { 97 DisasContext *ctx = container_of(dcbase, DisasContext, base); 98 99 tcg_gen_insn_start(ctx->base.pc_next); 100 } 101 102 /* 103 * Wrappers for getting reg values. 104 * 105 * The $zero register does not have cpu_gpr[0] allocated -- we supply the 106 * constant zero as a source, and an uninitialized sink as destination. 107 * 108 * Further, we may provide an extension for word operations. 109 */ 110 static TCGv gpr_src(DisasContext *ctx, int reg_num, DisasExtend src_ext) 111 { 112 TCGv t; 113 114 if (reg_num == 0) { 115 return ctx->zero; 116 } 117 118 switch (src_ext) { 119 case EXT_NONE: 120 return cpu_gpr[reg_num]; 121 case EXT_SIGN: 122 t = tcg_temp_new(); 123 tcg_gen_ext32s_tl(t, cpu_gpr[reg_num]); 124 return t; 125 case EXT_ZERO: 126 t = tcg_temp_new(); 127 tcg_gen_ext32u_tl(t, cpu_gpr[reg_num]); 128 return t; 129 } 130 g_assert_not_reached(); 131 } 132 133 static TCGv gpr_dst(DisasContext *ctx, int reg_num, DisasExtend dst_ext) 134 { 135 if (reg_num == 0 || dst_ext) { 136 return tcg_temp_new(); 137 } 138 return cpu_gpr[reg_num]; 139 } 140 141 static void gen_set_gpr(int reg_num, TCGv t, DisasExtend dst_ext) 142 { 143 if (reg_num != 0) { 144 switch (dst_ext) { 145 case EXT_NONE: 146 tcg_gen_mov_tl(cpu_gpr[reg_num], t); 147 break; 148 case EXT_SIGN: 149 tcg_gen_ext32s_tl(cpu_gpr[reg_num], t); 150 break; 151 case EXT_ZERO: 152 tcg_gen_ext32u_tl(cpu_gpr[reg_num], t); 153 break; 154 default: 155 g_assert_not_reached(); 156 } 157 } 158 } 159 160 #include "decode-insns.c.inc" 161 #include "insn_trans/trans_arith.c.inc" 162 #include "insn_trans/trans_shift.c.inc" 163 #include "insn_trans/trans_bit.c.inc" 164 #include "insn_trans/trans_memory.c.inc" 165 #include "insn_trans/trans_atomic.c.inc" 166 #include "insn_trans/trans_extra.c.inc" 167 #include "insn_trans/trans_farith.c.inc" 168 #include "insn_trans/trans_fcmp.c.inc" 169 #include "insn_trans/trans_fcnv.c.inc" 170 #include "insn_trans/trans_fmov.c.inc" 171 #include "insn_trans/trans_fmemory.c.inc" 172 #include "insn_trans/trans_branch.c.inc" 173 #include "insn_trans/trans_privileged.c.inc" 174 175 static void loongarch_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) 176 { 177 CPULoongArchState *env = cs->env_ptr; 178 DisasContext *ctx = container_of(dcbase, DisasContext, base); 179 180 ctx->opcode = translator_ldl(env, &ctx->base, ctx->base.pc_next); 181 182 if (!decode(ctx, ctx->opcode)) { 183 qemu_log_mask(LOG_UNIMP, "Error: unknown opcode. " 184 TARGET_FMT_lx ": 0x%x\n", 185 ctx->base.pc_next, ctx->opcode); 186 generate_exception(ctx, EXCCODE_INE); 187 } 188 189 ctx->base.pc_next += 4; 190 } 191 192 static void loongarch_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) 193 { 194 DisasContext *ctx = container_of(dcbase, DisasContext, base); 195 196 switch (ctx->base.is_jmp) { 197 case DISAS_STOP: 198 tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); 199 tcg_gen_lookup_and_goto_ptr(); 200 break; 201 case DISAS_TOO_MANY: 202 gen_goto_tb(ctx, 0, ctx->base.pc_next); 203 break; 204 case DISAS_NORETURN: 205 break; 206 case DISAS_EXIT_UPDATE: 207 tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); 208 QEMU_FALLTHROUGH; 209 case DISAS_EXIT: 210 tcg_gen_exit_tb(NULL, 0); 211 break; 212 default: 213 g_assert_not_reached(); 214 } 215 } 216 217 static void loongarch_tr_disas_log(const DisasContextBase *dcbase, 218 CPUState *cpu, FILE *logfile) 219 { 220 qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); 221 target_disas(logfile, cpu, dcbase->pc_first, dcbase->tb->size); 222 } 223 224 static const TranslatorOps loongarch_tr_ops = { 225 .init_disas_context = loongarch_tr_init_disas_context, 226 .tb_start = loongarch_tr_tb_start, 227 .insn_start = loongarch_tr_insn_start, 228 .translate_insn = loongarch_tr_translate_insn, 229 .tb_stop = loongarch_tr_tb_stop, 230 .disas_log = loongarch_tr_disas_log, 231 }; 232 233 void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, 234 target_ulong pc, void *host_pc) 235 { 236 DisasContext ctx; 237 238 translator_loop(cs, tb, max_insns, pc, host_pc, 239 &loongarch_tr_ops, &ctx.base); 240 } 241 242 void loongarch_translate_init(void) 243 { 244 int i; 245 246 cpu_gpr[0] = NULL; 247 for (i = 1; i < 32; i++) { 248 cpu_gpr[i] = tcg_global_mem_new(cpu_env, 249 offsetof(CPULoongArchState, gpr[i]), 250 regnames[i]); 251 } 252 253 for (i = 0; i < 32; i++) { 254 int off = offsetof(CPULoongArchState, fpr[i]); 255 cpu_fpr[i] = tcg_global_mem_new_i64(cpu_env, off, fregnames[i]); 256 } 257 258 cpu_pc = tcg_global_mem_new(cpu_env, offsetof(CPULoongArchState, pc), "pc"); 259 cpu_lladdr = tcg_global_mem_new(cpu_env, 260 offsetof(CPULoongArchState, lladdr), "lladdr"); 261 cpu_llval = tcg_global_mem_new(cpu_env, 262 offsetof(CPULoongArchState, llval), "llval"); 263 } 264