WSL2 と QEMU と Vim で xv6 のステップ実行環境をつくる

##はじめに


[Tech Commit](https://www.tech-commit.jp/) の LT発表会 で OS のコードリ－ディングをやってらっしゃる方のお話を聞いて、自分もまた UNIX V6 のコード読みたい欲が高まり、[積ん読してた書籍](https://www.amazon.co.jp/dp/B0821XY1QJ) をパラパラめくってみたのですがやはり難しい。
記載されている古い書き方の C言語やアセンブリを解読しつつ OS の仕組みを理解するのは自分の場合茨の道だなぁとあらためて感じました。
エミュレータ で実際にコードを動かしながら読めば分かりやすいのかな？と思い色々調べているうちに、**xv6** というマサチューセッツ工科大(MIT) の Operating System Engineering のクラスで使用される UNIX V6 を ANSI の C で書き換えて x86 (またはRISC-V) ベースに作りなおされた OS がある！という事を知ったので、早速動かしてみようと思い立ちました。
## xv6 について

UNIX V6 がベースであるため OS の基本的なアイディアがほぼ全て詰まっているけどコード量は1万行弱（Linuxの場合2200万行以上）で、OS の学習にはもってこいだと思います。

- [MIT での講義用の資料](https://pdos.csail.mit.edu/6.828/2020/xv6.html) 
- [ドラフトPDF](https://pdos.csail.mit.edu/6.828/2020/xv6/book-riscv-rev1.pdf)
- [上記ドラフトを電気通信大学の菅原先生という方が翻訳されたPDF](https://www.sugawara-lab.jp/fig/xv6-riscv-jpn.pdf)
-  [ソースコード](https://github.com/mit-pdos/xv6-riscv)

## xv6 が起動する仮想環境の準備

xv6 は仮想マシン上で動かすことが想定されており、bochs や QEMU で動くように環境が整備されています。以前は x86 の仮想環境で動作しましたが、最近の版では RISC-V に変わっているためクロスコンパイル用の開発ツールをインストールする必要があります。Linux の場合は比較的簡単です。
今回は WSL2 でやりましたが、Ubuntu20.04 でも全く同じ手順で大丈夫だと思います。

まずは必要なパッケージをインストールします。

```shell
$ sudo apt-get install qemu-kvm git build-essential gdb-multiarch qemu-system-misc gcc-riscv64-linux-gnu binutils-riscv64-linux-gnu texinfo
```

次に xv6 のソースコードを取得しビルドします。
 
```shell
$ git clone git://github.com/mit-pdos/xv6-riscv.git
$ cd xv6-riscv
$ make
```
ここまで出来たら QEMU で xv6 を起動してみます。 同ディレクトリ上で `make qemu` と打ちます。

```shell
$ make qemu
qemu-system-riscv64 -machine virt -bios none -kernel kernel/kernel -m 128M -smp 3 -nographic -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0

xv6 kernel is booting

hart 1 starting
hart 2 starting
init: starting sh
$
```

上記の様に起動し、ターミナル上で xv6 のシェルが起動し入力を受け付ける様になります。
xv6 から抜けるには [ctrl-A][x]の順にタイプします。

## GDB のインストール

[GDB（GNUデバッガ）](https://ja.wikipedia.org/wiki/GNU%E3%83%87%E3%83%90%E3%83%83%E3%82%AC)は、プログラムの不具合解析や値を参照・一時的に変更する等して動作をテストする際に使用するツールです。ここでは RISC-V 用にクロスデバッグ可能な gdb をインストールします。
[こちら](https://ftp.gnu.org/gnu/gdb/)からソースコードをダウンロードし、以下の手順でインストールします。

```shell
$ tar xvzf gdb-10.1.tar.gz
$ cd gdb-10.1
$ ./configure --program-prefix=riscv- --target=riscv
$ make
$ sudo make install
```
(※上記でパスが通ったディレクトリにインストールされなかった為、 `sudo cp gdb/gdb /usr/local/bin` でバイナリを配置しました。ここ後で修正します。)

## Vim によるステップ実行

Vim8.1 から、標準で GDB を扱える用になっています。これがものすごく便利です。

まずは xv6 を gdb モードで起動します。

```shell

$ make qemu-gdb
*** Now run 'gdb' in another window.
qemu-system-riscv64 -machine virt -bios none -kernel kernel/kernel -m 128M -smp 3 -nographic -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 -S -gdb tcp::26000
```

もう一つ別なターミナルを起動後 `xv6-riscv` ディレクトリに移動し vim を起動、コマンドモードで `:Termdebug kernel/kernel` とタイプすると、下図の様な画面になります。
![fig1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13048/573433eb-08af-b7ab-df76-610848eef407.jpeg)

左上の gdb のウィンドウで下記の様にタイプします。

```shell
(gdb) target remote localhost:26000
(gdb) cd kernel/
(gdb) directory .
(gdb) b main
(gdb) c
```

すると、`main.c` の 13行目にブレークポイントが設定され、画面が下図の様になります。
![fig2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13048/6bc9c965-92c9-afca-2be4-82286d1c3f14.jpeg)

あとは右ウィンドウの任意の行でマウスを右クリックすることによりブレークポイントを設置したり、変数のマウスオーバーによる値の表示、上部メニュー (` Step    Next    Finish    Cont    Stop    Eval`) のクリックでステップ実行などの操作が自由に実施できます！

![gif1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/13048/407ef649-223b-f7ab-2c22-be93230fdf81.gif)

## おわりに
vim の `:Termdebug` は初めて使いましたが、こんなに便利だとは思いませんでした笑
これで OS コードリーディングがめっちゃ捗りますね！

興味があったら是非試してみてくださーい、ではでは！