こちらはRISC-Vでハイパーバイザを開発する際に参考になりそうなプロセスをまとめたものです。自分のハイパーバイザの開発の為に書いたので、親切に書いてある箇所もあれば、説明を大幅に端折っている箇所もあるため、対象読者はよくわかりません。RISC-VでOS書いたことがある人向けでしょうか。要するに備忘録だと思ってください。 仕様書と格闘しながら書き上げているので、間違っている箇所などもあると思います。仕様書はネット上で公開されており、このページで解説しているものはThe RISC-V Instruction Set Manual: Volume II Privileged Architecture Version 20240411を元にしています。
開発はqemu-9.0.2で行っています。仮想化支援機能を搭載したRISC-Vの実機は2024年12月29日現在ではようやく登場したばかりでまだ値段も高いので、 当分はエミュレータで開発することになりそうです。
ハイパーバイザ拡張では、"V"で表される、現在の仮想化モードという概念が追加されます。V=0は仮想化していないことを示し、ソフトウェアは通常のMモード、Sモード、Uモードに新たなモードであるHSモードを加えた4つのモードのどれかで動作しています。 V=1は仮想化している状態を示し、ソフトウェアはVUモードもしくはVSモードで動作しています。ハイパーバイザ拡張は、あくまでも拡張であり、V=0としてMモード、Sモード、Uモードを使用することで、通常の動作を行うことが可能になっています。 HSモードの追加により複数のHSモード専用のレジスタが追加されており、 そのレジスタでハイパーバイザの設定を行うことで、通常の動作とハイパーバイザの動作を分離することが可能になっています 。分離されていることを強調するために、以後このページでは、通常のソフトウェアスタック(MモードやSモード、Uモードで動作する)での動作を通常モードと呼びます。
上図は、仮想マシン、ハイパーバイザ、ファームウェアがRISC-V上で動作していると仮定したときに、それぞれのソフトウェアがどの特権モードで動作するかを示したものです。ファームウェアとハイパーバイザは、それぞれMモード、 HSモードで動作しており、これはV=0です。そして、その上で2つ動作している仮想マシンを見ると、ゲストOSはVSモード、ゲストOSの上で動くアプリケーションはVUモードで動作していることが分かります。
ハイパーバイザでは、まずホスト側で各デバイスの仮想化やメモリ割り当てなどのゲストのための諸々の設定を行い、その後ゲストに制御を渡すことで仮想マシンの動作が開始します。
ここでは、その遷移方法について扱います。
特権モードでも説明した通り、ハイパーバイザはHSモード、仮想マシンはVSモード及びVUモードで動作します。"ゲストに制御を渡す"とは、具体的に言えばHSモードからVSモードに遷移することです。 特権モードの遷移は、通常それ専用の命令があるということは少なく、"トラップからの復帰"という名目で行われることが多いです。どういうことかというと、例えば仮想マシンでトラップが発生した場合には、 それに対応するために特権モードがHSモードに遷移します。そしてトラップに対する何らかの処理を行った後、HSモードから仮想マシンに処理を返します。通常のハイパーバイザは、このトラップからの復帰動作を利用して、ゲストに制御を渡します。 もちろん、実際にはトラップは起こっていないのですが、制御をゲストに渡した後も、ハイパーバイザとゲストはトラップを介してやり取りするので、そこまで不自然じゃないのだと思います。 原理が分かったところで、コードを見てみます。RISC-Vでは、以下のようなアセンブリ言語をHSモードで実行することで、VSモードに遷移します(entry_pointは適切なアドレスに変換する必要があります)。
csrs sstatus, 0x100
csrs hstatus, 0x80
csrw sepc, entry_point
sret
1行目では、sstatusレジスタのSPPビットをセットしています。SPPビットは、トラップが起こる前の特権モードを格納しています。SPP=1ならSモードを表し、SPP=0ならUモードを表します。sstatusレジスタは通常モードで使用されるSモードのレジスタですが、 HSモードと共用しているため、設定します。次に2行目では、hstatusのSPVビットをセットしています。SPVビットは、トラップが起こる前の仮想化モードを表しています。
ここまでの2行で、トラップが起こる前のモードが、Sモード + 仮想化モード、つまりVSモードである、ということを明示出来ました。後はリターン命令でトラップから復帰することで特権モードの遷移を行えば良いのですが、遷移先のアドレスを設定する必要があります。 続く3行目では、その設定を行っています。sepcとは、トラップが起こったアドレスを格納しているレジスタです。例によって、Sモードで使用されるレジスタですが、HSモードと共用しています。entry_pointを仮想マシンのバイナリがロードされたアドレスに置き換えると、 4行目のsret命令によってそのアドレスにジャンプし、仮想マシンの動作が始まります。
PMPをセットしていないメモリ領域から命令フェッチをするとinstruction access fault例外が起こります。しっかり設定しましょう
さてPMPとは物理メモリ保護であり、あるアドレス範囲について、読み書き可能か、実行可能かという風に設定することで意図しないメモリ領域へのアクセスを防ぎます。 複数のモードがありますがここではTOR(Top of range)モードのみ説明します。 TORモードでは、2つのレジスタにアドレスの上底と下底を書き込んでアドレス範囲を指定し、権限の有無は設定用のレジスタに書き込みます。アドレス範囲と権限をまとめて1セットとすると、何セット分 設定ができるかはレジスタの数によります。詳しくは仕様書を見てください。
工事中