QEMUの続き。独自ターゲットでビルドしてみる。static const TranslatorOps myriscvx_tr_ops
で必要な関数を調査する。
一つずつ見ていこう。myriscvx_tr_init_disas_context()
を見てみる。Disas
ということから、これはディスアセンブル(というかログ?)を出力するためのコンテキストなのか?
qemu/target/myriscvx/translate.c
typedef struct DisasContext { DisasContextBase base; /* pc_succ_insn points to the instruction following base.pc_next */ target_ulong pc_succ_insn; target_ulong priv_ver; bool virt_enabled; uint32_t opcode; uint32_t mstatus_fs; uint32_t misa; uint32_t mem_idx; /* Remember the rounding mode encoded in the previous fp instruction, which we have already installed into env->fp_status. Or -1 for no previous fp instruction. Note that we exit the TB when writing to any system register, which includes CSR_FRM, so we do not have to reset this known value. */ int frm; bool ext_ifencei; } DisasContext;
static void myriscvx_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); CPUMYRISCVXState *env = cs->env_ptr; ctx->pc_succ_insn = ctx->base.pc_first; ctx->mem_idx = ctx->base.tb->flags & TB_FLAGS_MMU_MASK; ctx->mstatus_fs = ctx->base.tb->flags & TB_FLAGS_MSTATUS_FS; // ctx->priv_ver = env->priv_ver; ctx->virt_enabled = false; ctx->misa = env->misa; ctx->frm = -1; /* unknown rounding mode */ // ctx->ext_ifencei = cpu->cfg.ext_ifencei; }
コンテキストの初期化を行っているようだ。
次は.tb_start
だ。これは空白になっている。
static void myriscvx_tr_tb_start(DisasContextBase *db, CPUState *cpu) { }
次は.insn_start
だ。これはオペコードを出力するための関数か?
static void myriscvx_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *ctx = container_of(dcbase, DisasContext, base); tcg_gen_insn_start(ctx->base.pc_next); }
次は.breakpoint_check
だ。ブレークポイントにヒット数かどうかをチェックするための関数?
static bool myriscvx_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu, const CPUBreakpoint *bp) { DisasContext *ctx = container_of(dcbase, DisasContext, base); tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); ctx->base.is_jmp = DISAS_NORETURN; gen_exception_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. */ ctx->base.pc_next += 4; return true; }
.translate_insn
は命令の変換に当たる。これがメインの処理なのか?
static void myriscvx_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *ctx = container_of(dcbase, DisasContext, base); CPUMYRISCVXState *env = cpu->env_ptr; uint16_t opcode16 = translator_lduw(env, ctx->base.pc_next); decode_opc(env, ctx, opcode16); ctx->base.pc_next = ctx->pc_succ_insn; if (ctx->base.is_jmp == DISAS_NEXT) { target_ulong page_start; page_start = ctx->base.pc_first & TARGET_PAGE_MASK; if (ctx->base.pc_next - page_start >= TARGET_PAGE_SIZE) { ctx->base.is_jmp = DISAS_TOO_MANY; } } }
.tb_stop
はTBを終了させるためのコードとなっている。これも使い方はまだ良く分からない。
static void myriscvx_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *ctx = container_of(dcbase, DisasContext, base); switch (ctx->base.is_jmp) { case DISAS_TOO_MANY: gen_goto_tb(ctx, 0, ctx->base.pc_next); break; case DISAS_NORETURN: break; default: g_assert_not_reached(); } }
disas_log
はディスアセンブリをログに出力するための関数らしい。
static void myriscvx_tr_disas_log(const DisasContextBase *dcbase, CPUState *cpu) { MYRISCVXCPU *rvcpu = MYRISCVX_CPU(cpu); CPUMYRISCVXState *env = &rvcpu->env; qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first)); qemu_log("Priv: "TARGET_FMT_ld"; Virt: "TARGET_FMT_ld"\n", env->priv, env->virt); log_target_disas(cpu, dcbase->pc_first, dcbase->tb->size); }