FPGA開発日記

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

RISC-V ハイパーバイザー拡張の勉強 (ハイパーバイザー向けに新設されたレジスタの実装を確認する)

Spikeの実装を読みながらハイパーバイザー向けのレジスタ実装を確認する。

ハイパーバイザーステータスレジスタ (hstatus)

VSモードでのゲストの例外動作および制御の記録を行っている。

  • 命令での使用:ハイパーバイザーメモリアクセス命令において、HSTATUS.HUビットに応じて実行許可条件が変わる。

  • riscv-isa-sim/riscv/insns/hlv_b.h

  require_extension('H');
  require_novirt();
  require_privilege(get_field(STATE.hstatus, HSTATUS_HU) ? PRV_U : PRV_S);
  WRITE_RD(MMU.guest_load_int8(RS1));
  • レジスタへの書き込み:書き込み可能な場所にのみ通常通り書き込む
  • 例外・割り込み発生時:mideleg, medelegによってHSレベルに例外・割り込みが移譲される場合、現在のステートをHSTATUSに書き込む。
  void processor_t::take_trap(trap_t& t, reg_t epc)
  {
  /* ... 中略 ... */
    } else if (state.prv <= PRV_S && bit < max_xlen && ((hsdeleg >> bit) & 1)) {
      // Handle the trap in HS-mode
      set_virt(false);
      reg_t vector = (state.stvec & 1) && interrupt ? 4*bit : 0;
      state.pc = (state.stvec & ~(reg_t)1) + vector;
      state.scause = t.cause();
  /* ... 中略 ... */
      set_csr(CSR_MSTATUS, s);
      s = state.hstatus;
      s = set_field(s, HSTATUS_SPVP, state.prv);
      s = set_field(s, HSTATUS_SPV, curr_virt);
      s = set_field(s, HSTATUS_GVA, t.has_gva());
      set_csr(CSR_HSTATUS, s);
      set_privilege(PRV_S);
    } else {
      // Handle the trap in M-mode
  
  • 例外からの復帰:SRET命令の場合、HSTATUS.SPVをチェックして仮想化モードに戻るかどうかを設定している。
  • riscv-isa-sim/riscv/insns/sret.h
  if (!STATE.v) {
    reg_t prev_virt = get_field(STATE.hstatus, HSTATUS_SPV);
    p->set_virt(prev_virt);
  }

ハイパーバイザーTrap移譲レジスタ (hedeleg / hideleg)

割り込み・例外移譲レジスタは割り込み・例外発生時の移譲条件の確認に使用されている。

  • riscv-isa-sim/riscv/processor.cc
void processor_t::take_interrupt(reg_t pending_interrupts)
{
  reg_t enabled_interrupts, deleg, status, mie, m_enabled;
  reg_t hsie, hs_enabled, vsie, vs_enabled;
/* ... 中略 ... */
  if (enabled_interrupts == 0) {
    // HS-ints have higher priority over VS-ints
    deleg = state.mideleg & ~state.hideleg;
    status = (state.v) ? state.vsstatus : state.mstatus;
    hsie = get_field(status, MSTATUS_SIE);
    hs_enabled = state.prv < PRV_S || (state.prv == PRV_S && hsie);
/* ... 中略 ... */
  }
}

void processor_t::take_trap(trap_t& t, reg_t epc)
{
  if (debug) {
    fprintf(log_file, "core %3d: exception %s, epc 0x%016" PRIx64 "\n",
            id, t.name(), epc);
/* ... 中略 ... */
  if (interrupt) {
    vsdeleg = (curr_virt && state.prv <= PRV_S) ? (state.mideleg & state.hideleg) : 0;
    hsdeleg = (state.prv <= PRV_S) ? state.mideleg : 0;
    bit &= ~((reg_t)1 << (max_xlen-1));
  } else {
    vsdeleg = (curr_virt && state.prv <= PRV_S) ? (state.medeleg & state.hedeleg) : 0;
    hsdeleg = (state.prv <= PRV_S) ? state.medeleg : 0;
  }