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