FPGA開発日記

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

自作Binary Translation型RISC-Vエミュレータのデバッグ(Jumpの条件変更)

自作Binary Translation型RISC-Vエミュレータの開発、いい加減本命のTCG Block Chainingを実装しろと言いたいところだが、まだ本来のテストパタンがPASSできなくて地味にデバッグを続けていた。

一つ問題になったのは、本当に時々エミュレーション中にSegmentation Faultで落ちてしまうときがある。Dhrystone実行中にも頻繁に発生し始めたので何故だろうこれはと思い解析してみた。

どうもTBのEXIT条件がおかしくなっているっぽい。

00007F1D789402AA 488B5510             mov       0x10(%rbp),%rdx
00007F1D789402AE 488B9510000000       mov       0x10(%rbp),%rdx
00007F1D789402B5 488B5D08             mov       8(%rbp),%rbx
00007F1D789402B9 488B9D08000000       mov       8(%rbp),%rbx
00007F1D789402C0 488BC2               mov       %rdx,%rax
00007F1D789402C3 480500000000         add       $0,%rax
00007F1D789402C9 48898508020000       mov       %rax,0x208(%rbp)
00007F1D789402D0 E93AFDD678           jmp       0x0000_7F1D_F16B_000F
========= BLOCK START =========
9550: Guest PC Address = 800012c8
 00000000800012c8:00000000800012c8 Hostcode 15e30405 : c.addi  s0, 1
 00000000800012ca:00000000800012ca Hostcode ff4515e3 : bne     a0, s4, pc + 4074
label found 2
label found. offset = 4e
replacement target is 34, data = 16
00007F1D6D850000 488B5548             mov       0x48(%rbp),%rdx
00007F1D6D850004 488B9548000000       mov       0x48(%rbp),%rdx
00007F1D6D85000B 4881C201000000       add       $1,%rdx
00007F1D6D850012 48895548             mov       %rdx,0x48(%rbp)
00007F1D6D850016 488B5558             mov       0x58(%rbp),%rdx
00007F1D6D85001A 488B9558000000       mov       0x58(%rbp),%rdx
00007F1D6D850021 488B9DA8000000       mov       0xA8(%rbp),%rbx
00007F1D6D850028 488B9DA8000000       mov       0xA8(%rbp),%rbx
00007F1D6D85002F 483BD3               cmp       %rbx,%rdx
00007F1D6D850032 0F8516000000         jne       0x0000_7F1D_6D85_004E
00007F1D6D850038 48B8CE12008000000000 movabs    $0x8000_12CE,%rax
00007F1D6D850042 48898508020000       mov       %rax,0x208(%rbp)
00007F1D6D850049 E9C1FFE583           jmp       0x0000_7F1C_F16B_000F
00007F1D6D85004E 48B8B412008000000000 movabs    $0x8000_12B4,%rax
00007F1D6D850058 48898508020000       mov       %rax,0x208(%rbp)
00007F1D6D85005F E9ABFFE583           jmp       0x0000_7F1C_F16B_000F
zsh: segmentation fault (core dumped)  cargo run --release -- --debug --dump-host --dump-guest --machine sifive_u

あれ、最後のJMPのアドレスがずれている気がする。最初は0x0000_7F1D_F16B_000Fなのに、0x0000_7F1C_F16B_000Fに変わっている。これはなんでだ?

よく見てみると、これはジャンプに必要な相対距離が32ビットの範囲を超えてしまっているために発生しているっぽい。確かにジャンプの相対距離が32ビットに収まるかどうかはについては確認していなかった。これはTCGの生成方法に問題があるので、ジャンプの方式を変えて、相対アドレスではなく絶対アドレスでジャンプするように変更した。

TCGの生成アルゴリズムを変更して、以下のようにした。

00007F51919B020B 48FFE0               jmp       *%rax
00007F51919B020E 48B8DA11008000000000 movabs    $0x8000_11DA,%rax
00007F51919B0218 48898508020000       mov       %rax,0x208(%rbp)
00007F51919B021F 48B80F00D8E6517F0000 movabs    $0x7F51_E6D8_000F,%rax
00007F51919B0229 48FFE0               jmp       *%rax
00007F51919B022C 48B80F00D8E6517F0000 movabs    $0x7F51_E6D8_000F,%rax
00007F51919B0236 48FFE0               jmp       *%rax
========= BLOCK START =========
6360: Guest PC Address = 800011da
 00000000800011da:00000000800011da Hostcode e70ce30c : c.sd    a1, 0(a4)
 00000000800011dc:00000000800011dc Hostcode 0741e70c : c.sd    a1, 8(a4)
 00000000800011de:00000000800011de Hostcode 6de30741 : c.addi  a4, 16
 00000000800011e0:00000000800011e0 Hostcode fed76de3 : bltu    a4, a3, pc + 4090
...

一度絶対アドレスを生成して%RAXに格納して、レジスタジャンプを行う。これで一応Segmentation Faultは出なくなった。

QEMUの終了条件

ちなみに、QEMUの終了条件についても調査した。複数スレッドが立ち上がっている状態でどのようにモニタスレッドがすべてのスレッドを停止させるのか監視したかった。

が、qemu-system-riscv64を見ていると、--machine=virtの場合はTESTデバイスにアクセスするとすぐに強制的に終了している。これでいいのかなあ...

  • qemu-5.1.0/hw/riscv/sifive_test.c
static void sifive_test_write(void *opaque, hwaddr addr,
           uint64_t val64, unsigned int size)
{
    if (addr == 0) {
        int status = val64 & 0xffff;
        int code = (val64 >> 16) & 0xffff;
        switch (status) {
        case FINISHER_FAIL:
            exit(code);
        case FINISHER_PASS:
            exit(0);
        case FINISHER_RESET:
            qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
            return;
        default:
            break;
        }
    }
    qemu_log_mask(LOG_GUEST_ERROR, "%s: write: addr=0x%x val=0x%016" PRIx64 "\n",
                  __func__, (int)addr, val64);
}