Slide 1

Slide 1 text

1 BHyVe code reading @syuu1228

Slide 2

Slide 2 text

2 お手元にソースコードをご 用意下さい https://github.com/lattera/b hyve

Slide 3

Slide 3 text

3 1, BHyVe 概要

Slide 4

Slide 4 text

4 BHyVe とは ● FreeBSD 版の Linux KVM のようなもの ● Intel VT を用いたハイパーバイザ ● 開発の初期段階でごく限定的な機能が実装されている             ↓ 最低限のハイパーバイザ実装のよいサンプルになりそう

Slide 5

Slide 5 text

5 Web Site http://www.bhyve.org/

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

7 割り込み ● MSI 割り込みのみ対応 ● 割り込みコントローラは Local APIC のみ実装

Slide 8

Slide 8 text

8 2,動作の流れ

Slide 9

Slide 9 text

9 おさらい: Linux KVM

Slide 10

Slide 10 text

10 Intel VT 向け VMM の動作の流れ 1.VMCS にゲスト環境の設定をロード 2.CPU に VMCS をセット 3.VMLAUNCH でゲストモードに切り替え 4 .ゲスト環境実行 5 .何らかの trap 要因が発生、 VMExit する 6.VMExit 要因を調べ、要因に合わせたエミュレーション処 理を行う 7.3 に戻る エミュレータが欲しいなら、 QEMU を使えばいいじゃない

Slide 11

Slide 11 text

11 QEMU KVM Linux kernel IOCTL Guest kernel User program VMLAUNCH VMExit Linux KVM 動作イメージ

Slide 12

Slide 12 text

12 Intel VT 向け VMM の動作の流れ 1.VMCS にゲスト環境の設定をロード 2.CPU に VMCS をセット 3.VMLAUNCH でゲストモードに切り替え 4 .ゲスト環境実行 5 .何らかの trap 要因が発生、 VMExit する 6.VMExit 要因を調べ、要因に合わせたエミュレーション処 理を行う 7.3 に戻る 白いところを KVM がやる。黄色いところを QEMU がやる。

Slide 13

Slide 13 text

13 BHyVe は?

Slide 14

Slide 14 text

14 /usr/sbin/bhyve vmm.ko BSD kernel IOCTL(VM_RUN) Guest kernel User program VMLAUNCH VMExit BHyVe 動作イメージ

Slide 15

Slide 15 text

15 Intel VT 向け VMM の動作の流れ 1.VMCS にゲスト環境の設定をロード 2.CPU に VMCS をセット 3.VMLAUNCH でゲストモードに切り替え 4 .ゲスト環境実行 5 .何らかの trap 要因が発生、 VMExit する 6.VMExit 要因を調べ、要因に合わせたエミュレーション処 理を行う 7.3 に戻る 白いところを vmm.ko がやる。黄色いところを /usr/sbin/bhyve が やる。

Slide 16

Slide 16 text

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();}

Slide 17

Slide 17 text

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 に対応するエミュレー ション処理を呼び出し

Slide 18

Slide 18 text

18 3,ゲスト OS ローダ

Slide 19

Slide 19 text

19 何故ゲスト OS ローダが必要か ● BHyVe には BIOS がない ● HDD のブートセクタから起動されるブートローダは BIOS に依存 している為、例え /usr/sbin/bhyve が起動時にブートセクタをロー ドして実行しても動作しない ● BIOS のエミュレーションを実装する代わりにゲストの FreeBSD カーネルをゲストメモリ空間にロードして、いきなりカーネルを 実行している → Xen の domU と同じ ● ロードを行うプログラムとゲストの実行を行うプログラムを分離 している – ロード: /usr/sbin/bhyveload – 実行: /usr/sbin/bhyve

Slide 20

Slide 20 text

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 に依頼

Slide 21

Slide 21 text

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 へマップ

Slide 22

Slide 22 text

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()

Slide 23

Slide 23 text

23 bhyveload の動作 – userboot.so ● usr.sbin/bhyveload/bhyveload.c:589 – FreeBSD のブートローダをユーザ空間で動くように移植したも の – メモリやレジスタへの読み書きを wrap 、ゲストのメモリ空間 /レジスタへアクセス – (メモリ空間は mmap 、レジスタの読み書きは ioctl 経由で VMM が管理するゲストのデスクリプタへ) – これを利用して kload が実装されている – ( Linux における kexec と同じ)

Slide 24

Slide 24 text

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 関数を実行

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

26 bhyveload の動作 – cb_copyin, cb_copyout ● src/usr.sbin/bhyveload/bhyveload.c:297 membase へ memcpy ● src/usr.sbin/bhyveload/bhyveload.c:313 membase から memcpy

Slide 27

Slide 27 text

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 で各種レジスタを初期化

Slide 28

Slide 28 text

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 初期化 – エントリポイント設定

Slide 29

Slide 29 text

29 4, IO デバイス エミュレーション

Slide 30

Slide 30 text

30 ゲストカーネルのコンフィグレーション device pci device bvmconsole device bvmdebug device mptable ACPI や多くのデバイスは無効 virtio.ko, if_vtnet.ko, virtio_pci.ko, virtio_blk.ko はモジュール としてビルド パラバーチャルなデバイスしか無い点は Xen の domU に 近い

Slide 31

Slide 31 text

31 /usr/sbin/bhyve vmm.ko BSD kernel IOCTL return Guest kernel VMExit IO エミュレーション IO 命令 console PCI net blk io emulation 実行

Slide 32

Slide 32 text

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()

Slide 33

Slide 33 text

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 から一文字読んで画面に書く

Slide 34

Slide 34 text

34 3, vmm.ko の提供するイ ンタフェース

Slide 35

Slide 35 text

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 インスタンスを削除

Slide 36

Slide 36 text

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番地)

Slide 37

Slide 37 text

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: ゲストレジス タの読み書き

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

39 /dev/vmm/${name} への ioctl (3) ● VM_PPTDEV_MSI: PCI passthorugh ● VM_INJECT_NMI: 未調査 ● VM_STATS: 未調査 ● VM_STAT_DESC: 未調査