FreeBSDで動くAMD SVM専用仮想マシン「DVM(だめVM)」を実装してみた

…タイトルは釣りなんだ。すまない。(´・ω・`)


筑波大 Softlab Hackathlonに参加してきました - かーねる・う゛いえむにっき
で「VM作る!」と宣言してみたはいいものの時間が足りず、結局作れたのはKVMのコードからAMD SVMのGuestモードを開始するのに最低限必要な機能のみを抜き出しFreeBSDへ乗せたものです。


削れるものは全て削ったので、ゲストOSを起動するような実用的な機能は一切無し。
今出来るのは、アドレス決め打ちでメモリ上にGuest用のコードを展開し、インターセプトを起こさせてHostモードへ戻ってくる事のみ。
しかし、Hostモードへ戻る瞬間にtriple fault起こして止まる(^^;;;
未だバグ有りな未完成状態です。

削ったもの


Q. それ全部じゃねーかよ!
A. だいたい全部


Q. そんなに削って動くの?
A. 無理

どんな動きをするの?

AMD SVMについてはこっちを見てね↓
AMD SVM(AMD-V)についてちょっと勉強してみた 再まとめ - かーねる・う゛いえむにっき


AMD SVMではGuest上でリアルモードの時でもページングを使えるようにする(Paged Real Mode)事でページング機構のみでHost-Guest間のメモリマッピングを解決しているみたいです。
では、ページングを切ってしまうとGuestから見えるメモリ空間は物理メモリ上のどこになるのか?
qemuで試した限りでは、Hostと全く同じものが見えているようです。(access violation的な扱いになるべきな気もしますが)


KVMの実装ではGuestはリアルモードで0x0番地から実行されるので、じゃあHostの0x0番地から数バイトGuest向けのプログラムを書き込んでしまえ。というのが現状の実装です。なんて乱暴な。
敢えてそうなっているのは、Paged Real Modeを実装する気力がなかった。という大変個人的な理由に基づいています。


まぁそんな訳で、

  • 0x0〜0x6へGuest向けのプログラムを書き込み
  • VMCBがアロケート/初期化して
  • VMRUN命令を実行しGuestモードへ切り替え
  • #VMEXITで戻ってくる
  • exit codeをprintfで確認

という処理を行うのが本プログラムの内容となっています。

動作環境

qemux86_64版でAMD SVMがエミュレーション出来る事が条件です(qemu 0.9.1以降)。
http://www.rcis.aist.go.jp/project/knoppix/vmknoppix/で配布しているVMKnoppixに入っているqemu-system-x86_64で動作確認を行っています。
Mac OS Xでビルドしたqemu 0.10.5では何故かSVM拡張命令が実行出来ませんでした。
理由は分からないけどそういう現象が起こる事があるみたいです。

ソースコード

http://syuu.dokukino.com/dvm.diff
FreeBSD 7.2/i386へのパッチになっています。


ライセンス云々とか言う奴は居ないと思うけど、そこに書いてあるようにGPL 2だよ。BSDライセンスにはなりません、残念残念。

動作シーケンス

FreeBSD起動開始 i386/i386/locore.s:btext()

cpu_startup()からdvm_run()をコール i386/i386/machdep.c:cpu_startup()

0x0-0x6をGuest用のコードへ書き換え i386/dvm/dvm.c:dvm_run()

VMCBをアロケート i386/dvm/dvm.c:svm_create_vcpu()

VMCBへ初期値(各レジスタの初期値とインターセプトするイベントの指定)を設定 i386/dvm/dvm.c:init_vmcb()

ホストレジスタ退避/ゲストレジスタ復帰 i386/dvm/dvm.c:svm_vcpu_run()

VMLOAD命令でVMCBからレジスタをロード i386/dvm/dvm.c:svm_vcpu_run()

VMRUN命令を実行 i386/dvm/dvm.c:svm_vcpu_run()

Guestモードで0x0からプログラムが実行される i386/dvm/dvm.c:svm_vcpu_run()

int 0x80を実行した所でインターセプト(#VMEXIT) i386/dvm/dvm.c:svm_vcpu_run()

VMSAVE命令でVMCBへレジスタをセーブ i386/dvm/dvm.c:svm_vcpu_run()

ホストレジスタ復帰/ゲストレジスタ退避 i386/dvm/dvm.c:svm_vcpu_run()

exit_codeをprintf() i386/dvm/dvm.c:svm_vcpu_run()

FreeBSD起動処理へ戻る i386/i386/machdep.c:cpu_startup()


ソースコードへコメント入れて解説しようかとも思ったけど、シンドイのでまた次の機会にする。