SlideShare a Scribd company logo
第七回 カーネル/VM探検隊発表
x86-64/Linuxに独自メモリ空間を勝手増設

                 中村 実
        nminoru1975@gmail.com
          Twitter @nminoru_jp
   http://www.nminoru.jp/~nminoru/

                                     1
自己紹介
• 某電機メーカーのサラリーマンです。

• 01~05 Java VM
• 06~10 メインフレームのソフトエミュ
  レータ
• 11    ファイルシステム



                         2
エミュレータを高速化するに
      は?
• ゲストとホストでCPUが異なる計算機を想
  定
 – ARM、MIPS、SPARC、Alphaをx86-64上で実行


• ゲストの仮想メモリがネック
 – ソフトウェアでエミュレーションすると絶対
   的に遅い
 – ホストのMMUを使ってゲストのメモリ空間を
   エミュレーションしましょう。
                                     3
ホストMMUをどうやって使う
      か?
• どうすればいい?
 1. OSを自前で作る
 2. OSから一部のCPUを切り離して独自処理
 3. 既存のOSを改造



 このLTではx86-64/Linux上で32ビットCPUを
 エミュレーションする勝手改造を紹介しま
                 す
                                 4
オレオレVM
• Linux仮想メモリ(VM)中に自分専用のVMを!
  – https://github.com/nminoru/oleolevm/
    x86風の2段のページテーブルを持つCPUを仮定

• 使い方
  – /proc/oleolevm を mmap
  – CPU 命令のインタプリータがロード・ストア
  – ゲストのTLB ミス・保護違反は SIGSEGV シグナルで通知
                          Memory
                                            Load/Store
                 mmap
                                                         CPU Interpreter
/proc/oleolevm             oleolevm
                                            SIGSEGV


                        ioctol or syscall                              5
どのように改造するか?
• Hugetlb を参考に (`・ω・´)
• vm_area_structのvm_flagsを勝手に追加
  – /include/linux/mm.h
     #define VM_HUGETLB 0x00400000
     #define VM_OLEOLETLB 0x100000000UL

• VM_HUGETLBとis_vm_hugetlb_pageを手掛
  かりに追加
     if (is_vm_hugetlb_page(vma))
           return copy_hugetlb_page_range(dst_mm, src_mm, vma);

     if (is_vm_oleoletlb_page(vma))
           return -ENOMEM;                                        6
Shadow Page Table(SPT)
  • ゲストの仮想メモリ空間をホストのページテーブルに
    組み込む。
                                          CR3
            oleolevm      0x3,000,000

       Guest Virt Space                                 PGD

                Page                            512GB
                          0x2,000,000                   PUD

            空き4GB                                1GB
写像                        0x1,000,000                   PMD
       Guest Phy Space
                                                 2MB
                Page                                     PTE

gCR3   ST        PT                              4KB
                          0x0,000,000
                                        Page
                                                               7
サンプルはここまでで力尽きま
      した




                 8
ゲストCR3を切り替え対応
 • ゲストのコンテキストスイッチに応じてSPTの書き換
   えが必要
 • 毎回破棄するとコスト高なので過去のSPTも保存
      – PUDをゲスト仮想空間1つに割り当てると効率がイイ!!
         CR3

         PGD                          PMD   PMD   PMD   PMD
                           CR3が変更され
                           たら入れ替え
  PUD                                 PTE   PTE   PTE   PTE

                     1GB

                                      PMD   PMD   PMD   PMD
PMD     PMD    PMD         PMD
                                      PTE   PTE   PTE   PTE
PTE     PTE    PTE         PTE
                                                              9
ゲストモード対応
• ゲストにもuser modeとsupervisor modeがあり
  アクセス禁止領域が違う
• User mode用とsupervisor mode用の空間を分
  けてしまえば解決
        Guest Virt Space
         (Supervisor)
             空き            CPU Interpreter
        Guest Virt Space
            (User)
             空き

        Guest Phy Space
                                             10
JITコンパイラ対応
• 高速化のためのJIT Compilation
  – Dynamic Binary Translationとも言う

• 分岐回数テーブル
  – 普通はハッシュテーブル
                                     Branch Counter Table
  – オレオレVMならMMUで用意
• 翻訳コードへのlookup table                   Page    カウン
                                                 タ
  – これもMMUで用意できる
• 翻訳元コードの書込み保護                                 空き
  – 書込み保護違反時にカーネル側                     Guest Virt Space
    で翻訳コードの破棄が可能
                                        Page
                                                命令


                                                            11
ゲストデバッグ機能対応
• Watchpoint Debug
  – 指定したメモリ範囲にアクセスがあったことを検
    出
• 問題
  – アクセスの補足方法
     • x86のdebug registerはエミュは使い辛い
     • ゲストのwatchpoint範囲をカバーするようにSPTのペー
       ジにプロテクションを付ける
  – アドレスの判定
  – Watchpointに引っかかったx86命令の再実行方法
                                         12
アドレス判定
• #PF例外はCR2に例外を起こしたメモリアドレ
  スを返すが不正確
 – movq %rax, [0x0FFE] で #PF がでてもCR2は0x1000に
   なるかも。
• x86命令をデコードしてアドレスを判定
 – struct pt_regsに#PFが起きた時点のレジスタが格納
   されている
 – %ripから16バイトを読み込んで命令をデコード
 – でもオペコードレベルで400命令以上のデコード
   が必要 (´;ω;`)

                                           13
再実行
• ゲストのLOAD/STOREは#PF例外補足後に再実
  行できないとダメ
  – でもSPTにプロテクションをかけているよ
• x86を1命令エミュレーション
  – #PFを起こしたx86命令をカーネルランドで1命令
    エミュし、pt_regsに結果を書き戻す。%ripも1命
    令分続ける。
• x86のSingle Step実行を使う
  1. #PFハンドラでSPTのプロテクションを解除し、
     EFLAGSにTFを立ててユーザランドに戻る
  2. 1命令を実行したら#DB例外が発生
  3. SPTのプロテクションを元に戻す
                                   14
まとめ
• オレオレVMを使えばx86-64のMMUを全部
  使える (`・ω・´)

• でもカーネルのリベースが大変 (´;ω;`)

• なにかうまい手はありませんか?



                            15
参考文献
• Jim Smith, Ravi Nair, Virtual Machines:
  Versatile Platforms for Systems and Processes




                                                  16
清聴ありがとうございまし
     た。




               17
ここから未使用




          18
予約領域を使う
• x86/x86-64は#PF例外の種類が少ない
• #PF例外時のエラーコード
 – Present bit
 – W/R bit
 – U/S bit
• そうだ!! 予約領域を使おう
 – RSVD


                            19
SPTの一貫性(1/2)
• ゲストのページテーブルとSPTの一貫性の維
  持するには?
 1. ゲストのページテーブル領域に書込み保護をか
    ける
 2. 毎回SPTを破棄
 3. 毎回SPTをゲストのページテーブルとチェック
• x86-64の予約領域を使ってSPTのチェックを
  遅延させる方法があるよ
 – 特許4897578「仮想計算機の制御プログラムおよ
   び仮想計算機システム」服部 直也ほか

                               20
SPTの一貫性(2/2)
• 予約領域へビットを打った場合「SPTを
  チェックしろ」という意味
• RSVD=1の#PF例外をハンドルして検査
• 最初はPGDのreserved bitを打つ。
• #PF例外があがるとPGDのreserved bitを解除し
  て直下のPUDの512エントリにresreved bitを打
  つ。
• 中位のエントリのreserved bitを下位のテーブ
  ルに写してゆく。
• 最後のPTEのreserved bitを解除する時にゲス
  トPTEとチェックを行う
                               21
改造なしで独自メモリ管理
• vm_area_structにvm_opsを設定
 static int foo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
 {
     vmf->page = page; // ここにロジックを入れる
 }

 static struct vm_operations_struct foo_vm_ops = {
      .fault = foo_vm_fault,
 };

 static int foo_mmap(struct file *filp, struct vm_area_struct *vma)
 {
      vma->vm_ops = &foo_vm_ops;
 }

 static struct file_operations foo_fops = {
      .mmap = foo_mmap,
 };

                                                                             22
X86-64の仮想メモリは将来拡張さ
          れるか?
• U.S. Patent 6,671,791 Processor including a translation
  unit for selectively translating virtual addresses of
  different sizes using a plurality of paging tables and
  mapping mechanisms




                                                        23

More Related Content

x86-64/Linuxに独自メモリ空間を勝手増設

  • 1. 第七回 カーネル/VM探検隊発表 x86-64/Linuxに独自メモリ空間を勝手増設 中村 実 [email protected] Twitter @nminoru_jp http://www.nminoru.jp/~nminoru/ 1
  • 2. 自己紹介 • 某電機メーカーのサラリーマンです。 • 01~05 Java VM • 06~10 メインフレームのソフトエミュ レータ • 11 ファイルシステム 2
  • 3. エミュレータを高速化するに は? • ゲストとホストでCPUが異なる計算機を想 定 – ARM、MIPS、SPARC、Alphaをx86-64上で実行 • ゲストの仮想メモリがネック – ソフトウェアでエミュレーションすると絶対 的に遅い – ホストのMMUを使ってゲストのメモリ空間を エミュレーションしましょう。 3
  • 4. ホストMMUをどうやって使う か? • どうすればいい? 1. OSを自前で作る 2. OSから一部のCPUを切り離して独自処理 3. 既存のOSを改造 このLTではx86-64/Linux上で32ビットCPUを エミュレーションする勝手改造を紹介しま す 4
  • 5. オレオレVM • Linux仮想メモリ(VM)中に自分専用のVMを! – https://github.com/nminoru/oleolevm/ x86風の2段のページテーブルを持つCPUを仮定 • 使い方 – /proc/oleolevm を mmap – CPU 命令のインタプリータがロード・ストア – ゲストのTLB ミス・保護違反は SIGSEGV シグナルで通知 Memory Load/Store mmap CPU Interpreter /proc/oleolevm oleolevm SIGSEGV ioctol or syscall 5
  • 6. どのように改造するか? • Hugetlb を参考に (`・ω・´) • vm_area_structのvm_flagsを勝手に追加 – /include/linux/mm.h #define VM_HUGETLB 0x00400000 #define VM_OLEOLETLB 0x100000000UL • VM_HUGETLBとis_vm_hugetlb_pageを手掛 かりに追加 if (is_vm_hugetlb_page(vma)) return copy_hugetlb_page_range(dst_mm, src_mm, vma); if (is_vm_oleoletlb_page(vma)) return -ENOMEM; 6
  • 7. Shadow Page Table(SPT) • ゲストの仮想メモリ空間をホストのページテーブルに 組み込む。 CR3 oleolevm 0x3,000,000 Guest Virt Space PGD Page 512GB 0x2,000,000 PUD 空き4GB 1GB 写像 0x1,000,000 PMD Guest Phy Space 2MB Page PTE gCR3 ST PT 4KB 0x0,000,000 Page 7
  • 9. ゲストCR3を切り替え対応 • ゲストのコンテキストスイッチに応じてSPTの書き換 えが必要 • 毎回破棄するとコスト高なので過去のSPTも保存 – PUDをゲスト仮想空間1つに割り当てると効率がイイ!! CR3 PGD PMD PMD PMD PMD CR3が変更され たら入れ替え PUD PTE PTE PTE PTE 1GB PMD PMD PMD PMD PMD PMD PMD PMD PTE PTE PTE PTE PTE PTE PTE PTE 9
  • 10. ゲストモード対応 • ゲストにもuser modeとsupervisor modeがあり アクセス禁止領域が違う • User mode用とsupervisor mode用の空間を分 けてしまえば解決 Guest Virt Space (Supervisor) 空き CPU Interpreter Guest Virt Space (User) 空き Guest Phy Space 10
  • 11. JITコンパイラ対応 • 高速化のためのJIT Compilation – Dynamic Binary Translationとも言う • 分岐回数テーブル – 普通はハッシュテーブル Branch Counter Table – オレオレVMならMMUで用意 • 翻訳コードへのlookup table Page カウン タ – これもMMUで用意できる • 翻訳元コードの書込み保護 空き – 書込み保護違反時にカーネル側 Guest Virt Space で翻訳コードの破棄が可能 Page 命令 11
  • 12. ゲストデバッグ機能対応 • Watchpoint Debug – 指定したメモリ範囲にアクセスがあったことを検 出 • 問題 – アクセスの補足方法 • x86のdebug registerはエミュは使い辛い • ゲストのwatchpoint範囲をカバーするようにSPTのペー ジにプロテクションを付ける – アドレスの判定 – Watchpointに引っかかったx86命令の再実行方法 12
  • 13. アドレス判定 • #PF例外はCR2に例外を起こしたメモリアドレ スを返すが不正確 – movq %rax, [0x0FFE] で #PF がでてもCR2は0x1000に なるかも。 • x86命令をデコードしてアドレスを判定 – struct pt_regsに#PFが起きた時点のレジスタが格納 されている – %ripから16バイトを読み込んで命令をデコード – でもオペコードレベルで400命令以上のデコード が必要 (´;ω;`) 13
  • 14. 再実行 • ゲストのLOAD/STOREは#PF例外補足後に再実 行できないとダメ – でもSPTにプロテクションをかけているよ • x86を1命令エミュレーション – #PFを起こしたx86命令をカーネルランドで1命令 エミュし、pt_regsに結果を書き戻す。%ripも1命 令分続ける。 • x86のSingle Step実行を使う 1. #PFハンドラでSPTのプロテクションを解除し、 EFLAGSにTFを立ててユーザランドに戻る 2. 1命令を実行したら#DB例外が発生 3. SPTのプロテクションを元に戻す 14
  • 15. まとめ • オレオレVMを使えばx86-64のMMUを全部 使える (`・ω・´) • でもカーネルのリベースが大変 (´;ω;`) • なにかうまい手はありませんか? 15
  • 16. 参考文献 • Jim Smith, Ravi Nair, Virtual Machines: Versatile Platforms for Systems and Processes 16
  • 19. 予約領域を使う • x86/x86-64は#PF例外の種類が少ない • #PF例外時のエラーコード – Present bit – W/R bit – U/S bit • そうだ!! 予約領域を使おう – RSVD 19
  • 20. SPTの一貫性(1/2) • ゲストのページテーブルとSPTの一貫性の維 持するには? 1. ゲストのページテーブル領域に書込み保護をか ける 2. 毎回SPTを破棄 3. 毎回SPTをゲストのページテーブルとチェック • x86-64の予約領域を使ってSPTのチェックを 遅延させる方法があるよ – 特許4897578「仮想計算機の制御プログラムおよ び仮想計算機システム」服部 直也ほか 20
  • 21. SPTの一貫性(2/2) • 予約領域へビットを打った場合「SPTを チェックしろ」という意味 • RSVD=1の#PF例外をハンドルして検査 • 最初はPGDのreserved bitを打つ。 • #PF例外があがるとPGDのreserved bitを解除し て直下のPUDの512エントリにresreved bitを打 つ。 • 中位のエントリのreserved bitを下位のテーブ ルに写してゆく。 • 最後のPTEのreserved bitを解除する時にゲス トPTEとチェックを行う 21
  • 22. 改造なしで独自メモリ管理 • vm_area_structにvm_opsを設定 static int foo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { vmf->page = page; // ここにロジックを入れる } static struct vm_operations_struct foo_vm_ops = { .fault = foo_vm_fault, }; static int foo_mmap(struct file *filp, struct vm_area_struct *vma) { vma->vm_ops = &foo_vm_ops; } static struct file_operations foo_fops = { .mmap = foo_mmap, }; 22
  • 23. X86-64の仮想メモリは将来拡張さ れるか? • U.S. Patent 6,671,791 Processor including a translation unit for selectively translating virtual addresses of different sizes using a plurality of paging tables and mapping mechanisms 23