はじめに
Unikernelを組込みシステムへいい具合に統合してやろうと思ったらRTOSとLinuxが同時に動く機材が必要となったので、タイトルの通り移植してみました。
Raspberry Pi向けのポーティングだとFreeRTOS単独起動構成が多い気がしていますが、今回のFreeRTOSはu-bootからキックさせた後にLinuxと同時起動出来るようにしています。
既にRTOSとLinuxが同時に動く実装が存在するi.MX8MやZynqMP SoCなどを持ってくればとても簡単なのですが、ラズパイにしておけばお金の節約と知識習得に繋がるはずなのです。
リポジトリ
ここにあります。ビルドや使い方はREADME.md
を参照してください。
ベースの実装は@eggmanさんのRaspberry Pi 3向けFreeRTOSです。先人のご尽力に感謝ですm(_ _)m。
移植について
ベースの実装から変えたところ
- 「u-bootからの起動」および「Linuxとの同時起動」を前提にしているため、EL2からスタートアップルーチンが動くように変更
- FreeRTOSのバイナリがCPUコア#3で動作するように変更 (CPUコア#0-2はLinux用)
- Raspberry Pi 4Bでは割込みコントローラとしてGICを使えるようになったので、それを使うように変更
- UARTはUART2(by PL011)を使用するように変更 (UART1はLinux用)
- Linuxと同時起動できるように、LinuxによるGIC構成変更に上手く対処するためのルーチンを追加
- MMUを有効化 (ただし仮想アドレス=物理アドレス、2MBページサイズ)
- FreeRTOS kernelのバージョンをv10.3.0にアップデート
実装tips
CPUコア#3でのバイナリキック
Raspberry Piのstubコード解説記事に詳しく書いてある通り、0xD8 - 0xF7までのメモリ領域は8byte毎にspin_cpuX : X=0,1,2,3
とラベルがつけられていています。CPUコア#3でバイナリをキックさせたければ、spin_cpu3 (=アドレス0xF0)
にリセットハンドラの先頭アドレスを書き込んでSEV命令を発行すればCPUコア#3が適切に動き出します。
今回のポーティングではアドレス0xF0へのリセットハンドラアドレス書き込みをu-bootのmw
コマンドにお任せしたのですが、同コマンドをCPUコア#0で実行するとその書き込み結果がキャッシュに留まって物理メモリに反映されないという事態が起こりました(アレェェ...)。CPUコア#3はアドレス0xF0の値を見てプログラムカウンタを設定しますが、物理メモリに値が反映されていないとアドレス0xF0の値は0x0のままとなります。このとき、CPUコア#3はWFE
命令を発効してお昼寝するようにstubコードが実装されています。
この問題を解消しようとするとu-bootのdcache flush
コマンドを利用できればいいのですが、Raspberry Pi 4B向けUbuntu 20.04 LTSに収録されているu-bootには同コマンドがなかったのでu-bootを再コンパイルする羽目に...。
Linuxとの同時起動
u-bootプロンプトからFreeRTOSサンプルをキックさせた後にLinuxをキックさせると、LinuxがGICをLinux用に設定してくるので先にFreeRTOSで設定したGIC構成がオーバーライトされてしまいます(特に問題なのがLinuxによるGIC Distributor設定の書き換えでした)。そうなってしまうと、tick用タイマとUARTの割込みが入ってこなくなってしまいます。
なので今回はLinuxがGICのDistributor設定を終えるタイミングを検知するための機構をFreeRTOS側に実装し、LinuxがGIC設定を終えた後にFreeRTOSが改めてGIC設定し直すようにしました。検知機構はビジーループx2という地球に優しくない処理なので、そのうち方法を見直さなければなりません。
MMU有効化
remoteproc/rpmsgの移植をしていたら、MMUが有効になっていないと使えない排他アクセス命令を使うコードになっていたので泣く泣くMMU周りのコードを書く羽目に...。ということで、ベースとなるUARTサンプルにまず適用。
めんどいので「仮想アドレス=物理アドレス」なアドレス無変換のページテーブルを作成するように実装。2-levelのページテーブルで、1st levelが1GBブロックサイズ、2nd levelが2MBページサイズです。
UART2(PL011)用のコード実装
勘のよい方は既に気付いているかと思います。実はUART1(mini UART)をFreeRTOS側で利用するようにすれば、UART1は既にベース実装にあったのでわざわざUART2の箇所を実装する必要は無いのです。
ええ、もちろん知っていましたがあえてやりました。ニッコリ。
次のマイルストーン
LinuxとFreeRTOSが通信できるように、remoteproc/rpmsgを移植。