AMD Ryzenプロセッサで楽しくBitVisorにさわりましょう。っていうのを去年書きましたが、今年版です。
差分の概要
去年は以下の記事でした:
https://qiita.com/hdk_2/items/1111d6c981a7328cf146
今回は、プロセッサを変えたのと、ハードディスクドライブを変えたのと、ソリッドステートドライブを追加したのと、カーネル起動オプションnomodesetを外したのと、sysctlのkernel.randomize_va_space=0を外しました。
環境
- マザーボード: ASUS PRIME B350M-A
- プロセッサ: AMD Ryzen 7 2700 BOX (AM4/3.2/20M/C8/T16/65W)
- メモリー: G.SKILL F4-2400C16D-16GFX (DDR4 PC4-19200 8GB 2枚組)
- ビデオカード: 玄人志向 GF-GT710-E1GB/LP (GeForce GT710 1GB LP)
- ハードディスクドライブ: HGST HDN726040ALE614
- ソリッドステートドライブ: Crucial M4-CT064M4SSD2
- オペレーティングシステム: Debian GNU/Linux 9 amd64
昨年からのソフトウェア面の大きな差異として、Spectre対策がLinuxカーネルに入ったことがあげられます。
$ dmesg|grep Spectre
[ 0.015582] Spectre V2 : Mitigation: Full AMD retpoline
[ 0.015583] Spectre V2 : Spectre v2 / SpectreRSB mitigation: Filling RSB on context switch
[ 0.015583] Spectre V2 : Spectre v2 mitigation: Enabling Indirect Branch Prediction Barrier
$ grep bugs /proc/cpuinfo|uniq
bugs : fxsave_leak sysret_ss_attrs null_seg spectre_v1 spectre_v2 spec_store_bypass
ビルドする
a@best:/dev/shm/bitvisor-2.0$ for i in 1 2 3 4 5
> do time make -j > /dev/null 2>&1;make clean >/dev/null;done
real 0m5.356s
user 1m2.160s
sys 0m3.996s
real 0m5.218s
user 1m2.152s
sys 0m3.952s
real 0m5.236s
user 1m2.340s
sys 0m3.784s
real 0m5.256s
user 1m2.432s
sys 0m3.764s
real 0m5.228s
user 1m2.272s
sys 0m3.940s
ビルド時間が0.5秒ほど短縮しています。Spectre対策も入ってこれですから、確実に速くなっているかと思います。tmpfsなので、純粋にCPUの性能向上のおかげと思われます。
動かす
Starting BitVisor...
Copyright (c) 2007, 2008 University of Tsukuba
All rights reserved.
ACPI DMAR not found.
FACS address 0xDAB82E00 0xDAB82DC0
Module not found.
Old log found in RAM
Processor 0 (BSP)
Using SVM.
Processor 0 3194078208 Hz (Invariant TSC)
Loading drivers.
AES/AES-XTS Encryption Engine initialized (AES=openssl)
Copyright (c) 1998-2002 The OpenSSL Project. All rights reserved.
Generic ATA/ATAPI para pass-through driver 0.4 registered
Generic AHCI para pass-through driver registered
Generic RAID para pass-through driver registered
Generic IEEE1394 para pass-through driver 0.1 registered
Broadcom NetXtreme Gigabit Ethernet Driver registered
VPN for Intel PRO/100 registered
Intel PRO/1000 driver registered
Realtek Ethernet Driver registered
NVMe para pass-through driver registered
NVMe para pass-through driver registered
PCI device concealer registered
PCI device monitor registered
Generic EHCI para pass-through driver 0.9 registered
Generic EHCI para pass-through driver 0.9 registered
Generic UHCI para pass-through driver 1.0 registered
xHCI para pass-through driver 0.1 registered
Intel Corporation Ethernet Controller 10 Gigabit X540 Driver registered
PCI: finding devices...
PCI: 40 devices found
MCFG [0] 0000:00-3F (F8000000,4000000)
Starting a virtual machine.
Processor 1 (AP)
Processor 2 (AP)
Processor 3 (AP)
Processor 4 (AP)
Processor 5 (AP)
Processor 6 (AP)
Processor 7 (AP)
Processor 8 (AP)
Processor 9 (AP)
Processor 10 (AP)
Processor 11 (AP)
Processor 12 (AP)
Processor 13 (AP)
Processor 14 (AP)
Processor 15 (AP)
Processor 10 3194170880 Hz (Invariant TSC)
Processor 9 3194179584 Hz (Invariant TSC)
Processor 1 3194171392 Hz (Invariant TSC)
Processor 3 3194182656 Hz (Invariant TSC)
Processor 4 3194171392 Hz (Invariant TSC)
Processor 8 3194179584 Hz (Invariant TSC)
Processor 7 3194182656 Hz (Invariant TSC)
Processor 2 3194182144 Hz (Invariant TSC)
Processor 5 3194182656 Hz (Invariant TSC)
Processor 13 3194179072 Hz (Invariant TSC)
Processor 14 3194179072 Hz (Invariant TSC)
Processor 6 3194166784 Hz (Invariant TSC)
Processor 12 3194173440 Hz (Invariant TSC)
Processor 15 3194173440 Hz (Invariant TSC)
Processor 11 3194165760 Hz (Invariant TSC)
普通に動いていますね?
待った
Old log found in RAM
鋭い皆さんはこれに気づいているかも知れません。これは、ネットワーク等でうまくログを得られない場合のデバッグ用の機能によるもので、RAMが消されずに再びBitVisorが同じアドレスに読み込まれることを期待して、panic時にRAM上にログとチェック用のキーワードとCRCを残し、次回起動時にそこからログが取り出された時に残るメッセージです。dbgshのlogコマンドで参照できます。このログの手前には以下のpanicが残されていました:
panic(CPU7): Assertion failed!(n < NUM_OF_ALLOCSIZE) function mm_page_alloc file core/mm.c line 546
CR0 80000019 CR2 00000000 CR3 D08C8000 CR4 000006A0
RFLAGS 00200012 GDTR 428A6808+000000FF IDTR 40F296E0+00001000
stackdump: 7 428A6B68 0 40126014 7 40138CFA 40138D18 0 D D 0 7 428A6B68 4011F26D 202 401268C7 7 40126DA6 706EF7F3528E5277 926E3CADDD6B28A9 AC5ACE48F512BE5E BD784B7A00000028 428BFB88 428BFB28 364DE5B9684CA7B0 402D7847 40294CB0 402D782C 222 41085000 9E4F4BB6DAA0F1B2 D
backtrace: 40126014 40138CFA 40138D18 4011F26D 401268C7 40126DA6 4010F4E1 40120FA0 40120FD1 40120FD1 40120FD1 40120FD1 40120FD1 40120FD1 40120FD1 40120FD1 40120FD1 40120FD1 40120FD1 40120FD1 40120FD1 40121421 40121454 4012D5F5 4012D8FF 4012AF89 40120AB0 4011D41A 4012030D 4012032A 4010EEA7
Guest state and registers of cpu 7 ------------
RAX 00000000 RCX 00000000 RDX 00000000 RBX 00000000
RSP 00000000 RBP 00000000 RSI 00000000 RDI 00000000
R8 00000000 R9 00000000 R10 00000000 R11 00000000
R12 00000000 R13 00000000 R14 00000000 R15 00000000
Exception 0x0E
ACR ES 00000093 CS 0000009B SS 00000093 DS 00000093 FS 00000093 GS 00000093
LIMIT ES 0000FFFF CS 0000FFFF SS 0000FFFF DS 0000FFFF FS 0000FFFF GS 0000FFFF
BASE ES 00000000 CS 00000000 SS 00000000 DS 00000000 FS 00000000 GS 00000000
SEL ES 00000000 CS 00000000 SS 00000000 DS 00000000 FS 00000000 GS 00000000
RIP 00000000 RFLAGS 00000002 GDTR 00000000+0000FFFF IDTR 00000000+0000FFFF
EFER 00000000
VMCB 0x42a70000 (phys D0A70000)
EXITCODE: 0=0x0 (VMEXIT_CR0_READ)
EXITINFO1 00000000 EXITINFO2 00000000 EXITINTINFO 00000000
------------------------------------------------
panic(CPU7): Assertion failed!(n < NUM_OF_ALLOCSIZE) function mm_page_alloc file core/mm.c line 546
これはメモリーが確保できなかった時のpanicなのです。ヤバイ。
せっかくなので
少し追っかけてみましょう。
backtrace: 40126014 40138CFA 40138D18 4011F26D 401268C7 40126DA6 4010F4E1 40120FA0 40120FD1 40120FD1 40120FD1 40120FD1 40120FD1 40120FD1 40120FD1 40120FD1 40120FD1 40120FD1 40120FD1 40120FD1 40120FD1 40121421 40121454 4012D5F5 4012D8FF 4012AF89 40120AB0 4011D41A 4012030D 4012032A 4010EEA7
これは、CONFIG_BACKTRACE=0にしていたので正確なバックトレースではなく、スタックの中から.textセクションのアドレス範囲の値を最大32個抽出してそれっぽく並べているだけです。なのでスタック上のゴミが混ざっていることがよくあります。真面目にデバッグの必要があるならCONFIG_BACKTRACE=1にしておきましょう。そうするとバックトレースのアドレスは矢印<-で結ばれて最後まで出力されます。
それはいいとして、このアドレスからコード位置を引く方法としてはaddr2lineコマンドを使う方法があります。
a@best:~/bitvisor$ addr2line -e bitvisor.elf 0x40120FD1
/home/a/bitvisor/core/mm.c:551
こんな風にコード位置を示してくれます。これでも物足りない時はobjdumpコマンドで逆アセンブルしてしまいましょう。64ビットの場合はこうなります:
$ objdump -d bitvisor.elf -m i386:x86-64 | pager
あるいは-dの代わりに-Sを使えば、ソースコードと混ぜた逆アセンブル結果が出ますので、好みに合わせて選びましょう。
さて、40120FD1はメモリー確保処理内の再帰呼び出し、40121421はalloc_pages()、40121454はalloc_page(), 4012D5F5はsvm_np_init()の中のようです。つまりnested page tablesの準備のところ? 1ページも確保できないとはよっぽどです。そんなにメモリーはギリギリなんでしょうか。ちゃんと起動した時のBitVisorの空きメモリーをdbgshでチェックします。
> sendint
sendint> free
send 0 to free
sendint>
> log
(省略)
2529 pages (10116 KiB) free
うーん、一応9.8MiBはあるようですね... 起動時にそんなにメモリーを解放する場面もなさそうですし、どうして動いたり動かなかったりするのか、謎は深まるばかりです。
なお、今回引っ掛かったnested page tablesの割当処理を見ると、論理プロセッサーごとに1024ページ、つまり4MiBを消費しています。Ryzen 7は8コア16スレッド、つまり論理プロセッサーが16個ありますので、これだけで64MiBです。そして、VMM全体のサイズは128MiBです。冷静に考えると、ThreadripperやCore i9のほとんどのモデルでは動かないんだなぁ