Weitere ähnliche Inhalte
Ähnlich wie Bhyve code reading (20)
Mehr von Takuya ASADA (20)
Bhyve code reading
- 4. BHyVe とは
●
FreeBSD 版の Linux KVM のようなもの
●
Intel VT を用いたハイパーバイザ
●
開発の初期段階でごく限定的な機能が実装されている
↓
最低限のハイパーバイザ実装のよいサンプルになりそう
4
- 6. 実装状況
●
Intel VT-x, EPT 必須 (= Nehalem 以降必須 )
●
BIOS 非対応 (disk ブート出来ない )
●
対応デバイス :
– PCI
● virtio-net, virtio-blk
● pci passthrough(VT-d)
● pci UART
– paravirtual console/debug port
6
●
- 7. 割り込み
●
MSI 割り込みのみ対応
●
割り込みコントローラは Local APIC のみ実装
7
- 10. Intel VT 向け VMM の動作の流れ
1.VMCS にゲスト環境の設定をロード
2.CPU に VMCS をセット
3.VMLAUNCH でゲストモードに切り替え
4.ゲスト環境実行
trap 要因が発生、 VMExit する
5.何らかのエミュレー タが欲 しいなら、 QEMU を使 えばいいじゃない
10
- 11. Linux KVM 動作イメージ
QEMU User
program
IOCTL
VMExit
Linux kernel KVM Guest kernel
VMLAUNCH 11
- 12. Intel VT 向け VMM の動作の流れ
1.VMCS にゲスト環境の設定をロード
2.CPU に VMCS をセット
3.VMLAUNCH でゲストモードに切り替え
4.ゲスト環境実行
白 いところを 要因が発生、 VMExit する
5.何らかの trap KVM がやる。黄色 いところを QEMU がやる。
12
- 15. Intel VT 向け VMM の動作の流れ
1.VMCS にゲスト環境の設定をロード
2.CPU に VMCS をセット
3.VMLAUNCH でゲストモードに切り替え
4.ゲスト環境実行
白 いところを 要因が発生、 VMExit する
5.何らかの trap vmm.ko がやる。黄色 いところを /usr/sbin/bhyve が
やる。 15
- 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();}
16
- 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 に対応するエミュレー
ション処理を呼び出し
17
- 19. 何故ゲスト OS ローダが必要か
●
BHyVe には BIOS がない
●
HDD のブートセクタから起動されるブートローダは BIOS に依存
している為、例え /usr/sbin/bhyve が起動時にブートセクタをロー
ドして実行しても動作しない
●
BIOS のエミュレーションを実装する代わりにゲストの FreeBSD
カーネルをゲストメモリ空間にロードして、いきなりカーネルを
実行している 19
- 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 に依頼
20
- 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 へマップ
21
- 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()
22
- 23. bhyveload の動作 – userboot.so
● usr.sbin/bhyveload/bhyveload.c:589
– FreeBSD のブートローダをユーザ空間で動くように移植したも
の
– メモリやレジスタへの読み書きを wrap 、ゲストのメモリ空間
/レジスタへアクセス
– (メモリ空間は mmap 、レジスタの読み書きは ioctl 経由で
VMM が管理するゲストのデスクリプタへ) 23
- 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 関数を実行
24
- 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
25
- 26. bhyveload の動作 – cb_copyin, cb_copyout
● src/usr.sbin/bhyveload/bhyveload.c:297
membase へ memcpy
● src/usr.sbin/bhyveload/bhyveload.c:313
membase から memcpy
26
- 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 で各種レジスタを初期化
27
- 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 初期化 28
- 30. ゲストカーネルのコンフィグレーション
device pci
device bvmconsole
device bvmdebug
device mptable
ACPI や多くのデバイスは無効
virtio.ko, if_vtnet.ko, virtio_pci.ko, virtio_blk.ko はモジュール
としてビルド 30
- 31. IO エミュレーション
io emulation 実行
console net blk
PCI
/usr/sbin/bhyve
IOCTL return
IO 命令
VMExit
BSD kernel vmm.ko Guest kernel
31
- 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()
32
- 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 から一文字読んで画面に書く
33
- 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 インスタンスを削除
35
- 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番地)
36
- 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: ゲストレジス 37
- 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 のどの
機能を使うか(調査中) 38
- 39. /dev/vmm/${name} への ioctl (3)
● VM_PPTDEV_MSI: PCI passthorugh
●
VM_INJECT_NMI: 未調査
●
VM_STATS: 未調査
●
VM_STAT_DESC: 未調査
39