FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

QEMUに入門してみる(4. 独自コンフィグレーションを用意して必要なファイルを確認)

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);
}