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で確認
という処理を行うのが本プログラムの内容となっています。
動作環境
qemuのx86_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()
ソースコードへコメント入れて解説しようかとも思ったけど、シンドイのでまた次の機会にする。