BitVisor+FreeDOS+dbgsh

BitVisor上でFreeDOSを起動して、dbgshでBitVisorのログを確認する方法を紹介します。

# 環境

ギリギリBIOS環境が残っているiMac Mid 2014で試すつもりだったのですが、なぜかBitVisor上でFreeDOSを起動しようとしたところでフリーズしたり再起動したりしたので、ASUS AM1I-A + AMD Athlon 5350のPCを使うことにしました。

1GBのUSBメモリーをフォーマットしてFreeDOS 1.2をインストールし、FDIMPLESコマンドでDJGPPの各種パッケージをインストールした上で、さらに、Debian GNU/Linux環境からGNU GRUB 2.02+dfsg1-20をインストールして使っています。AUTOEXEC.BATに書き加えた環境変数の設定は以下の2つです:

```shell
PATH=%PATH%;C:\DEVEL\DJGPP\BIN
set DJGPP=C:\DEVEL\DJGPP\DJGPP.ENV
```

# BitVisor起動

とりあえず、普通の手順でGNU GRUBからBitVisorを起動し、その上でFreeDOSを起動するところまでは通常通りでした。(なぜかクロック周波数測定のあたりで10秒以上止まりますが...)

# dbgsh

dbgshのコンパイルは仮想マシン上のFreeDOS環境でDJGPPを使用しました。幸いdbgshまわりに長いファイル名は使われていないのですが、Makefileは使用できないようでしたので、以下のようにコマンドを手入力します:

```cmd
C:\DBGSH>gcc -s -o dbgsh.exe dbgsh.c ../common/call_vmm.c
```

とりあえずエラー無くコンパイルは通ります。vmcallやvmmcall命令のアセンブルに引っ掛かるかなと思っていたのですが、ツール類がちゃんと新しいものになっているようです。出来上がったバイナリは、BitVisor上ではなくて別の仮想マシン上なので動かないはずですが、試しに実行したところ以下のようになりました:

```shell
C:\DBGSH>dbgsh
Bad command or filename - "stty".
```

これで止まってしまうのですが、とりあえず、止まってしまうのは別の仮想マシン上だからいいとして、sttyがないというメッセージはかっこ悪いですね。これの対策は簡単で、以下のようにしてWindows用の実装を使えば大丈夫です:

```shell
C:\DBGSH>gcc -s -D__MINGW32__ -o dbgsh.exe dbgsh.c ../common/call_vmm.c
```

これをBitVisor上のFreeDOS環境に持ち込んだところ、エラーになりました:
![_IMG_20191207_161846.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103733/e5a16f5f-420d-586a-f684-c1a4ac1e590d.jpeg)

えっ... というわけでgdbを使ってこれのデバッグをしたわけですが、原因は__セグメントベース__でした... DJGPPはセグメントベースを0にしていないそうです。アドレスが0x2000より小さいところで気づければよかったのですが、DOSなので小さいアドレスにも違和感がなかったことと、32bitのCコンパイラーはセグメントベース0が当たり前だと思っていたというか完全に意識から飛んでいました。(ﾉ∀`)ｱﾁｬｰ

そういうわけなので、対策が必要です。以下のようにしました:

```diff

--- tools/common/call_vmm.c     2017-12-17 21:17:45.000000000 +0900
+++ /media/a/FREEDOS2016/common/call_vmm.c      2019-12-07 17:14:02.000000000 +0900
@@ -87,6 +87,8 @@
                      : "a" (0), "b" (p->arg), "r" (p->addr), "S" (done));
 }
 
+#include <sys/segments.h>
+#include <dpmi.h>
 void
 call_vmm_get_function (intptr_t addr0, intptr_t addr1, int aoff,
                       int off, call_vmm_function_t *function)
@@ -98,6 +100,9 @@
        else
                data.addr = addr1;
        data.arg = data.addr + off;
+unsigned long segment_base = 0;
+__dpmi_get_segment_base_address (_my_ds (), &segment_base);
+data.arg += segment_base;
        if (!call_vmm_docall (call_vmm_get_function_sub, &data)) {
                function->vmmcall_number = data.ret;
                function->vmmcall_type = VMMCALL_TYPE_VMCALL;
```

セグメントベースをDPMIのAPIで取得して加えます。BitVisorのvmmcallインターフェイスは物理アドレスではなくリニアアドレスを使っているので、これで問題ありません。これで動くようになったのですが、改行の表示が正しく行われません:
![_IMG_20191207_171315.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103733/85e64a4d-2bb1-b3c4-56d2-8b3a1121a4e4.jpeg)

これはputch()の仕様なのかな、Windowsではこうはならないのですが、とりあえず適当に修正を加えました。ついでで`__MINGW32__`も定義してあります:

```diff
--- tools/dbgsh/dbgsh.c 2015-10-12 21:05:34.000000000 +0900
+++ /media/a/FREEDOS2016/dbgsh/dbgsh.c  2019-12-07 17:16:22.000000000 +0900
@@ -29,6 +29,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#define __MINGW32__
 #ifdef __MINGW32__
 #include <conio.h>
 #define NOECHO()
@@ -98,6 +99,7 @@
                                fprintf (fp, "%c", r);
                                fflush (fp);
                        }
+if (r == '\n') PUTCHAR ('\r');
                        PUTCHAR (r);
                        s = 0;
                }
```

これでバッチリです!
![_IMG_20191207_171806.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103733/a9e04ab8-60c3-4080-f479-c80863914ac9.jpeg)