はじめに
この記事では,BitVisor でのI/O のフックの方法をざっくりと解説します.
I/O のフックはBitVisor をはじめ,VMMやハイパバイザの肝になる処理なので,なんとなくでも知ってると良いかなと思います.
コードと対応付けた解説などは特にないので,悪しからず.
I/O って何?
I/O とはCPU とデバイスの間でデータをやり取りすることです.
I/O はデバイス内にあるレジスタからデータを読んだり,レジスタにデータを書いたりする操作になります.
I/O のフックって何?
ここでいうI/O のフックとは,OS がI/O をしようとしたときに,一度VMMやハイパバイザに処理が遷移させることです.
例えば,BitVisor では,ディスクへのI/Oをフックし,読み書きしているデータを暗号化,複合化することで,OSから透過的にディスクを暗号化する機能を実現しています.
I/O の種類
I/O にはPIO とMMIO があります.
それぞれについて,説明とフックの方法を説明します.
PIO って何?
PIO はPort mapped I/O の略(だったと思う,うろ覚え)です.
PIO では,I/O のために特殊なアセンブラ命令を使います.
x86ではin 命令とout 命令です.
in命令やout命令でポートと呼ばれる番号を引数に渡してやると,そのポートに対応するレジスタへの読み書きができます.
Linux では,どのポートのどのデバイスのレジスタがマップされているかは以下のコマンドで簡単に確認できます(Windows やMac でもできるでしょうが,僕はやり方を知りません).
$ cat /proc/ioports
より詳細なレジスタの配置が知りたい場合は,デバイスの仕様書やデバイスドライバのコードを読みましょう.
#PIO のフック
PIO のフックは,VT-x で提供されている機能を使えば簡単にできます.
VT-x で指定されている方法で,ポートを指定すれば,そのポートをフックすることができます.
MMIO って何?
MMIO とはMemory Mapped I/O の略
メモリアクセスと同じ命令でI/O を行える機能.
x86 のアセンブラで言えば,mov 命令でI/Oが行える.
デバイスのレジスタがメモリ空間上にマップされている.
このメモリアドレスにmov 命令で読み書きすることで,デバイスのレジスタから読み書きすることができます.
Linux では,どのアドレスにどのデバイスのレジスタがマップされているかは以下のコマンドで簡単に確認できます(Windows やMac でもできるでしょうが,僕はやり方を知りません).
$ cat /proc/iomems
こちらも,より詳細なレジスタの配置が知りたい場合は,デバイスの仕様書やデバイスドライバのコードを読みましょう.
MMIO のフック
MMIO のフックはVT-x で機能が提供されているわけではありません.
MMIO のフックは,意図的にEPT Violation を起こしてやることで実現しています.
EPT Violation とは,ページングのPage Fault に似たようなものです.
EPT やページングについては,ここでは解説しません(これやると疲れるので...)
どうやって意図的にEPT Violation を起こすかというと,フックしたいレジスタがマップされているメモリページをEPT にマップしないことで,実現しています.
おわりに
ざっくりとI/Oのフック処理についての解説っぽいものを書いてみました.