FPGA開発日記

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

GDBリモートサーバを自作ISSに実装する(レジスタ、メモリ値取得とKillコマンド)

msyksphinz.hatenablog.com

ブレークポイントとステップ実行が何となく動くようになったので、データの取得のためのレジスタ値取得コマンドとメモリ設定コマンドを調整していこう。

現在対象としているアーキテクチャはMIPS64のため、これに合うように、まずはレジスタのダンプコマンド(gコマンド)を修正するのと、メモリのバイトーオーダーを調整する。

github.com

Info Registerコマンドで取得されるレジスタ

info registerコマンドにより、汎用レジスタの値を取得できる。

Breakpoint 1, main () at core_main.c:89
(gdb) info registers
                  zero               at               v0               v1
 R0   0000000000000000 ffffffff800025f8 0000000000000000 0000000000000000
                    a0               a1               a2               a3
 R4   0000000000000000 0000000000000000 0000000000000000 0000000000000000
                    a4               a5               a6               a7
 R8   0000000000000000 0000000000000000 0000000000000000 0000000000000000
                    t0               t1               t2               t3
 R12  0000000000000000 0000000000000000 0000000000000000 0000000000000000
                    s0               s1               s2               s3
 R16  0000000000000000 0000000000000000 0000000000000000 0000000000000000
                    s4               s5               s6               s7
 R20  0000000000000000 0000000000000000 0000000000000000 0000000000000000
                    t8               t9               k0               k1
 R24  ffffffff800025f8 0000000000000000 0000000000000000 0000000000000000
                    gp               sp               s8               ra
 R28  ffffffff7f000020 ffffffff7f004018 0000000000000000 ffffffffbfc000c4
                    sr               lo               hi              bad
      0000000000000000 0000000000000000 0000000000000000 0000000000000000
                 cause               pc
      0000000000000000 ffffffff800025f8
                   fsr              fir
              00000000         00000000
(gdb)

実際にはGDBクライアントとGDBサーバの間でgコマンドが発行されており、汎用レジスタの値を全て取得するようなコマンドが走っている。

void GdbEnv::DumpGeneralRegisters (void)
{
    std::stringstream str;
    for (Addr_t idx = 0; idx < 32; idx++) {
        DWord_t val = m_env->GRegRead<DWord_t>(idx);
        DWord_t reversed_val = ReverseByte<DWord_t>(val);

        str << std::hex << std::setw(16) << std::setfill('0') << reversed_val;
    }
    SendPacketString (str.str().c_str());
}

気をつけなければならないのはバイトアラインだ。GDBの返答としては、バイト単位で返さなければらないらしく、そのままレジスタの表記で返してしまうとバイトが反転してしまう。 従って、ReverseByte関数を実装してバイト単位で反転し、その結果を返すようにしている。

template <class T> T GdbEnv::ReverseByte (T data)
{
    T reversed_data = 0;
    const int byte_size = sizeof (T);
    for (int idx = 0; idx < byte_size; idx++) {
        Byte_t byte = data & 0x0FF;
        reversed_data = reversed_data << 8 | (byte & 0x0FF);
        data >>= 8;
    }
    return reversed_data;
}


template Word_t  GdbEnv::ReverseByte (Word_t data);
template DWord_t GdbEnv::ReverseByte (DWord_t data);

Killコマンドの実装

現在、GDBクライアント側を強制終了させると、GDBサーバ側(ISS)が強制終了されてしまう。これを防ぐために、GDBプロトコルの'k'コマンドをサポートしよう。

    case 'k' : {
        return GDB_KILL;
    }

単純にStepExecutionをせずに、GDB_KILL通知をISSの制御部に返す。これによりGDBの通信チャネルを切断し、終了するように変更した。

    if (FLAGS_gdb != -1) {
#ifdef ARCH_MIPS64
        Mips64Env *env = new Mips64Env (g_debug_fp, g_uart_fp, en_stop_sim, FLAGS_debug);
#endif // ARCH_MIPS64
        std::unique_ptr<GdbEnv> p_gdb = std::unique_ptr<GdbEnv>(new GdbEnv(env, FLAGS_gdb));
        p_gdb->HandleGdb();
        while (p_gdb->HandleClientRequest () == GDB_NORMAL);

        p_gdb->CloseRSPClient();

ポート番号をオプションで指定できるようにする

ついでに、--gdbオプションによりポート番号を指定できるようにした。

DEFINE_int32 (gdb, -1, "Wait GDB port");
  • 以下のコマンドでポート番号を指定可能だ。
$ ./swimmer_mips64 -gdb 3000