Share
Embed
BHyVe Code Reading
Takuya ASADA
May 26, 2012
Transcript
39 slides
-
4 BHyVe とは • FreeBSD 版の Linux KVM のようなもの •
Intel VT を用いたハイパーバイザ • 開発の初期段階でごく限定的な機能が実装されている ↓ 最低限のハイパーバイザ実装のよいサンプルになりそう -
6 実装状況 • Intel VT-x, EPT 必須 (= Nehalem 以降必須
) • BIOS 非対応 (disk ブート出来ない ) • 対応デバイス : – PCI • virtio-net, virtio-blk • pci passthrough(VT-d) • pci UART – paravirtual console/debug port • 対応 OS: FreeBSD 8, 9, 10 -
10 Intel VT 向け VMM の動作の流れ 1.VMCS にゲスト環境の設定をロード 2.CPU に
VMCS をセット 3.VMLAUNCH でゲストモードに切り替え 4 .ゲスト環境実行 5 .何らかの trap 要因が発生、 VMExit する 6.VMExit 要因を調べ、要因に合わせたエミュレーション処 理を行う 7.3 に戻る エミュレータが欲しいなら、 QEMU を使えばいいじゃない -
11 QEMU KVM Linux kernel IOCTL Guest kernel User program
VMLAUNCH VMExit Linux KVM 動作イメージ -
12 Intel VT 向け VMM の動作の流れ 1.VMCS にゲスト環境の設定をロード 2.CPU に
VMCS をセット 3.VMLAUNCH でゲストモードに切り替え 4 .ゲスト環境実行 5 .何らかの trap 要因が発生、 VMExit する 6.VMExit 要因を調べ、要因に合わせたエミュレーション処 理を行う 7.3 に戻る 白いところを KVM がやる。黄色いところを QEMU がやる。 -
14 /usr/sbin/bhyve vmm.ko BSD kernel IOCTL(VM_RUN) Guest kernel User program
VMLAUNCH VMExit BHyVe 動作イメージ -
15 Intel VT 向け VMM の動作の流れ 1.VMCS にゲスト環境の設定をロード 2.CPU に
VMCS をセット 3.VMLAUNCH でゲストモードに切り替え 4 .ゲスト環境実行 5 .何らかの trap 要因が発生、 VMExit する 6.VMExit 要因を調べ、要因に合わせたエミュレーション処 理を行う 7.3 に戻る 白いところを vmm.ko がやる。黄色いところを /usr/sbin/bhyve が やる。 -
16 /usr/sbin/bhyve の動作(1) • src/usr.sbin/bhyve/fbsdrun.c:669 fbsdrun_addcpu() で CPU0 のスレッドを作成 –
src/usr.sbin/bhyve/fbsdrun.c:209 pthread_create(fbsdrun_start_thread) • src/usr.sbin/bhyve/fbsdrun.c:195 vm_loop() – src/usr.sbin/bhyve/fbsdrun.c:476 while(1) {vm_run();} -
17 /usr/sbin/bhyve の動作(2) • src/usr.sbin/bhyve/fbsdrun.c:476 while(1) { vm_run(); – src/lib/libvmmapi/vmmapi.c:265
ioctl(VM_RUN) vmm.ko に VMX non root mode への切り替 えを依頼 • src/usr.sbin/bhyve/fbsdrun.c:494 handler[exitcode]() EXIT_REASON に対応するエミュレー ション処理を呼び出し -
19 何故ゲスト OS ローダが必要か • BHyVe には BIOS がない •
HDD のブートセクタから起動されるブートローダは BIOS に依存 している為、例え /usr/sbin/bhyve が起動時にブートセクタをロー ドして実行しても動作しない • BIOS のエミュレーションを実装する代わりにゲストの FreeBSD カーネルをゲストメモリ空間にロードして、いきなりカーネルを 実行している → Xen の domU と同じ • ロードを行うプログラムとゲストの実行を行うプログラムを分離 している – ロード: /usr/sbin/bhyveload – 実行: /usr/sbin/bhyve -
20 bhyveload の動作 - vm_create • src/usr.sbin/bhyveload/bhyveload.c:557 vm_create(vmname) で /dev/vmm/%s
に device file を作成 – src/lib/libvmmapi/vmmapi.c:85 sysctl 経由で device file 作成を vmm.ko に依頼 -
21 bhyveload の動作 - vm_setup_memory • src/usr.sbin/bhyveload/bhyveload.c:570 vm_setup_memory() で membase
へゲスト空間を mmap – src/lib/libvmmapi/vmmapi.c:139 vmm.ko へ ioctl(VM_MAP_MEMORY) でゲスト空間をアロケート vmm.ko への mmap でゲスト空間を membase へマップ -
22 bhyveload の動作 - vm_open • usr.sbin/bhyveload/bhyveload.c:564 vm_open(vmname) で /dev/vmm/%s
を open() – src/lib/libvmmapi/vmmapi.c:92 vm_open() • src/lib/libvmmapi/vmmapi.c:67 vm_device_open() -
23 bhyveload の動作 – userboot.so • usr.sbin/bhyveload/bhyveload.c:589 – FreeBSD のブートローダをユーザ空間で動くように移植したも
の – メモリやレジスタへの読み書きを wrap 、ゲストのメモリ空間 /レジスタへアクセス – (メモリ空間は mmap 、レジスタの読み書きは ioctl 経由で VMM が管理するゲストのデスクリプタへ) – これを利用して kload が実装されている – ( Linux における kexec と同じ) -
24 bhyveload の動作 – userboot.so • usr.sbin/bhyveload/bhyveload.c:589 dlopen で userboot.so
を開く • usr.sbin/bhyveload/bhyveload.c:594 dlsym で loader_main 関数のアドレスを取得 • usr.sbin/bhyveload/bhyveload.c:603 loader_main 関数を実行 -
25 bhyveload の動作 – userboot.so • loader_main 関数は boot2 とほぼ同じ動作を行うが、引数で渡
しているコールバック関数で以下のような処理を仮想化してい る – コンソールの読み書き cb_putc, cb_getc, cb_poll – ファイルの操作 cb_open, cb_close, cb_isdir, cb_read, cb_readdir, cb_seek, cb_stat – ディスクの読み書き cb_diskread – メモリの読み書き cb_copyin, cb_copyout, cb_getmem – レジスタの読み書き cb_setreg, cb_setmsr, cb_setcr, cb_setgdt, cb_exec -
26 bhyveload の動作 – cb_copyin, cb_copyout • src/usr.sbin/bhyveload/bhyveload.c:297 membase へ
memcpy • src/usr.sbin/bhyveload/bhyveload.c:313 membase から memcpy -
27 bhyveload の動作 – cb_setreg, cb_exec • src/usr.sbin/bhyveload/bhyveload.c:327 vm_set_register でレジスタセット
• src/usr.sbin/bhyveload/bhyveload.c:434 vm_setup_freebsd_registers でレジスタなどの初期化 – src/lib/libvmmapi/vmmapi_freebsd.c:63 vm_set_register, vm_set_desc で各種レジスタを初期化 -
28 bhyveload の動作 – vm_setup_freebsd_registers • src/lib/libvmmapi/vmmapi_freebsd.c:63 – CR0 =
PE | PG | NE # ページング、プロテクトモード – CR4 = PAE | VMXE # PAE 、 VMX 有効 – EFER = LME | LMA # long mode 有効 – GDT 初期化&セグメントレジスタ初期化 – タスクレジスタ初期化 – ページテーブル& CR3 初期化 – RSP 初期化 – エントリポイント設定 -
30 ゲストカーネルのコンフィグレーション device pci device bvmconsole device bvmdebug device mptable
ACPI や多くのデバイスは無効 virtio.ko, if_vtnet.ko, virtio_pci.ko, virtio_blk.ko はモジュール としてビルド パラバーチャルなデバイスしか無い点は Xen の domU に 近い -
31 /usr/sbin/bhyve vmm.ko BSD kernel IOCTL return Guest kernel VMExit
IO エミュレーション IO 命令 console PCI net blk io emulation 実行 -
32 /usr/sbin/bhyve の動作 – IO emulation • src/usr.sbin/bhyve/fbsdrun.c:494 handler[exitcode]() EXIT_REASON
に対応するエミュレー ション処理を呼び出し – src/usr.sbin/bhyve/fbsdrun.c:465 IO の場合は VM_EXITCODE_INOUT なので vmexit_inout • src/usr.sbin/bhyve/fbsdrun.c:281 EAX の値を取得して emulate_inout() -
33 /usr/sbin/bhyve の動作 – IO emulation • src/usr.sbin/bhyve/inout.c:72 inout_handers[port].handler(in, port,
bytes, eax) port = 0x220 なら console ( src/usr.sbin/bhyve/consport.c:127 で定義) – src/usr.sbin/bhyve/consport.c:101 in = 1 ならキーボードから一文字を読んで eax に書く in = 0 なら eax から一文字読んで画面に書く -
35 sysctl • src/sys/amd64/vmm/vmm_dev.c:387 – hw.vmm.create(name) /dev/vmm/${name} に新しい VM インスタンスを指す
デバイスファイルを作成 – hw.vmm.destroy(name) /dev/vmm/${name} の VM インスタンスを削除 -
36 /dev/vmm/${name} へのファイル API • read/write – src/sys/amd64/vmm/vmm_dev.c:184 ゲスト空間の読み書き( offset
= ゲストの物理アドレス) • mmap – src/sys/amd64/vmm/vmm_dev.c:347 ゲスト空間のマップ (先頭ポインタがゲストの物理アドレス0番地) -
37 /dev/vmm/${name} への ioctl (1) • src/sys/amd64/vmm/vmm_dev.c:144 • VM_RUN: VMLAUNCH
させる • VM_SET_PINNING/VM_GET_PINNING: CPU の固定割当 • VM_MAP_MEMORY: ゲストのメモリ空間割当 • VM_GET_MEMORY_SEG: 未調査 • VM_SET_REGISTER/VM_GET_REGISTER: ゲストレジス タの読み書き -
38 /dev/vmm/${name} への ioctl(2) • VM_SET_SEGMENT_DESCRIPTOR/VM_GET_SEGMENT_D ESCRIPTOR: セグメントレジスタの読み書き • VM_INJECT_EVENT:
未調査 • VM_LAPIC_IRQ: 未調査 • VM_SET_CAPABILITY/VM_GET_CAPABILITY: VT-x のどの 機能を使うか(調査中) • VM_BIND_PPTDEV/VM_UNBIND_PPTDEV: PCI passthrough • VM_MAP_PPTDEV_MMIO: PCI passthrough -
39 /dev/vmm/${name} への ioctl (3) • VM_PPTDEV_MSI: PCI passthorugh •
VM_INJECT_NMI: 未調査 • VM_STATS: 未調査 • VM_STAT_DESC: 未調査