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