===================================================================== ARM64プロセッサ依存部設計メモ Last Modified: 16 Apr 2019 ===================================================================== ○ このドキュメントの位置づけ このドキュメントは,TOPPERS/SSPカーネルをARMv8-Aプロセッサに移植する際 の設計メモである. ○ ARMv8-Aの仕様まとめ ARMv8-Aの仕様のうち,カーネルの設計に関係する事項についてまとめる. ● レジスタ 汎用レジスタはr0〜r30 (特に64ビットレジスタはX0..X30で表現)の31種類からなる. (参考)「Procedure Call Standard for the ARM 64-bit Architecture」 r0...r7 Parameter/result registers(引数および返値の受け渡し用) r8 Indirect result location register (大きなデータ構造を 返値として返す場合など,呼び出し側が結果を取り出すために 間接アドレッシングを多用する際,そのベースアドレスを受け渡し するために使われる) r9..r15 Caller-saved Temporary registers(呼び出し側で保存すべきレジスタ) r16(IP0) The first intra-procedure-call scratch register リンカによって veneer(注1) および PLT(Procedure Linkage Table,注2) コード の呼び出しで使われる.それ以外の時はテンポラリレジスタとして使われることもある. (注1)リンカによって挿入される小さなコード片で, 分岐命令のターゲットが範囲外の場合などに使われる (注2)共有ライブラリの呼び出しでシンボル解決を行うために使用される 関数テーブル r17(IP1) The second intra-procedure-call temporary register(r16と役割は同じ) r18 The Platform Register(プラットフォームABIで使用されるレジスタ. ABIで使われない場合はテンポラリレジスタとして使われる) r19..r28 Callee-saved registers(呼び出された側で保存して使うレジスタ) r29(FP) The Frame Pointer(フレームポインタ) r30(LR) The Link Register(リンクレジスタ) SSPカーネルのアセンブラによる実装ではr8, r16...r18は使用していない. ● コーリングコンベンション r0...r7 が引数および返値に使われる. ARMにより規定されているため,コンパイラに依存せずこのルールとなる ● PSTATE AArch64ではプロセッサの状態を表すフラグの集まりをまとめてPSTATEと呼び, それぞれのフラグへ別々にアクセスするための特別なレジスタ名が定義されている. PSTATEの詳細は,ARM Architecture Reference Manual ARMv8, for ARMv8-A architecture profile の D1.7 などを参照のこと. ・spsel EL1より上の例外レベルでは,spsel という特別な名称のレジスタにアクセスし, PSTATE.SPフラグの値をセットして使用スタックの切り替えを行う. ・daif daif という名称のレジスタを使用してPSTATEのD,A,I,Fビットを操作し, 各種例外および割込みの禁止/許可を制御する SSPカーネルの実装では,スタックはSP_EL1のみを使用する. また,CPUロック状態および割込みロック状態の実装にPSTATEのIおよびFフラグを 利用している. ● 割込みベクタ ベクタテーブルのアドレスはリセット時に,システムレジスタの一つVector Base Address Register(VBAR, システムレジスタ)にアドレスをセットすることで, 2048バイト境界の任意のアドレスに配置可能である. SSPカーネルの実装ではスタートアップルーチンで設定している. ● 割込み ここではGICを搭載するプロセッサの場合について述べる. それ以外の場合についてはチップ依存部のドキュメントに記載する. ●割込み優先度 GICにおいては割込み優先度は設定値の小さい方が高優先度となる. 優先度は最大8bitであり,SoC毎に実装されているビット幅が異なる.実装さ れるビットが8bit以下の場合は,LSBから無効になる.例えば,実装されてい るビット幅が7bitの場合は,ビット0が無効となる. 優先度のビットフィールドのLSBから数ビットをサブ優先度と呼ぶフィールド に設定することが可能である.残りの上位ビットをプリエンプション優先度と 呼ぶ.プリエンプション優先度が同じで,サブ優先度が異なる優先度のグルー プは,お互いをプリエンプトすることができない. 例として,QEMU Virtボード向けSSPの実装では Cortex-A53プロセッサを ターゲットとしており,優先度は16段階(4bit)でビット0から3が無効である. ●割込み/例外の受付 GICの場合,割込みを受け付けると受け付けた割込みの番号がGICC_IARにセットされる. 割込み番号はこのレジスタにセットされる値を使用する. 例外番号は,例外発生時のプロセッサ状態に応じて決定される,ベクタテーブル中の ジャンプ先オフセット毎に異なる番号を割り振ることにしている. 例外発生時の状態 例外の種類 例外番号  例外ベクタ先頭からのオフセット AArch64, EL1t(EL1, SP_EL0) Synchronous 0 0x0000 AArch64, EL1t(EL1, SP_EL0) SError 1 0x0180 AArch64, EL1h(EL1, SP_EL1) Synchronous 2 0x0200 AArch64, EL1h(EL1, SP_EL1) SError 3 0x0380 AArch64, EL0 (EL0, SP_EL0) Synchronous 4 0x0400 AArch64, EL0 (EL0, SP_EL0) SError 5 0x0580 AArch32, EL0 (EL0, SP_EL0) Synchronous 6 0x0600 AArch32, EL0 (EL0, SP_EL0) SError 7 0x0780 割込みを受け付けた際,受け付けた割込みに設定された優先度より低い割込みを 禁止するため,GICC_RPRから取得した割込み要因の割込み優先度を GICC_PMRへ セットしている.そしてハンドラ終了後に割込み発生前の割り込み優先度に戻す. [CPUモード] プロセッサは,EL0からEL3までの例外レベルのいずれかで動作する. またそれぞれのレベルで64ビットモード(AArch64) または32ビットモード(AArch32)を 選択することができる.ただし,ある例外レベルが32ビットモードで動作する場合は それより低い例外レベルでは64ビットモードを選択することができない. また,セキュリティ拡張機能を搭載するプロセッサではセキュアモードおよび 非セキュアモードを選択することができる. SSPの実装では,64ビットモード(AArch64),非セキュア,例外レベル1(EL1) で動作する. ●リセット時の状態 リセット時はプロセッサチップがサポートする最大の例外レベルおよびそのレベルの スタック(SP_ELx)が有効となっている. 例としてQEMU向けVirtボード(Cortex-A53) では,既定でEL1が最大例外レベルのため リセット直後はSP_EL1が有効となっている. ● スタックポインタ(SP_EL0とSP_ELx) スタックポインタは,例外レベル0のスタックポインタ(SP_EL0)および実行中の 例外レベルのスタックポインタ(SP_ELx)が選択可能である. スタックの選択はspselレジスタへ1を設定するとSP_ELxを,0を設定すると SP_EL0 を選択する. SSPの実装では,EL1 で SP_EL1 のみを使用して動作する. ●例外レベルの遷移 例外レベル(EL)の遷移は割込み/例外の受付および例外リターン命令(eret)により行う. 割込みおよび例外を受け付けることで発生前と同じまたはより高い例外レベルへ遷移する. 一方,同じまたはより低い例外レベルへの遷移は例外リターン命令(eret)により行う. 受付時の遷移先となる例外レベルの指定はシステムレジスタにより設定することで行う. 割込み/例外リターン時の遷移先となる例外レベルは,SPSR_ELxのビット2および3で 指定する.割込み/例外受付時,PSTATEの状態がSPSR_ELxに,リターンアドレスがELR_ELxに, それぞれ保存されeret命令の実行によりその格納値が復帰されるため,eret実行前に あらかじめSPSR_ELxにセットしておくことで指定した例外レベルへ遷移することが可能である. 同じタイミングで使用スタックや64ビット/32ビットモードの指定も行うことができる. QEMU Virt向けSSPの実装ではEL1のみを使用するため,例外レベル間の遷移処理は行っていない. ●例外レベルの判定 現状の例外レベルを判定するには,CPSRのビット2およびビット3の値を使用する. bit[3:2]の値が '11'の場合:EL3 '10'の場合:EL2 '01'の場合:EL1 '00'の場合:EL0 ●GICC_PMRレジスタ 設定した優先度以下(値としては以上)の優先度の割込みの受付を禁止する. 設定可能な最大値を設定すると,全ての割込みを許可する. 例えば優先度が16段階の場合は0xf0(=0x0f << 4)をセットするとすべての割込み許可 となる.値は例外/割込みの受付とリターンにより変化しないため,受け付けた割込み の優先度を割込みの入口処理で設定する必要がある. ●例外/割込みの受付 ・例外/割込みを受付けると,受付け時にアクティブなスタック上に以下のコ ンテキストを保存する. --------------- <- new SP | GICC_PMR | ---------------- | 割込み/例外番号| ---------------- | ESR_ELx | ---------------- | ELR_ELx | ---------------- | SPSR_ELx | ---------------- | X30 | ---------------- | X29 | ---------------- | : | | : | ---------------- | X1 | ---------------- | X0 | ---------------- <- old SP | | 割込み/例外発生時の主なシーケンスは次のようになる(遷移先の例外レベルをELxと表現する) (ハードウェアの処理) ・PSTATEをSPSR_ELxに保存する ・リターンアドレスをELR_ELxに保存する ・PSTATE.{D,A,I,F}を1にセットする ・同期例外およびSError割込みのとき,例外要因情報をESR_ELxに保存する ・スタックポインタをSP_ELxに切り替える ・ELxへ遷移する (ソフトウェアでの処理) ・(発生時にSP_EL0を使用していた場合)スタックを割込み発生前(SP_EL0)に戻す ・汎用レジスタ(X0-X30)をスタックに保存 ・SPSR_ELx, ESR_ELx, ELR_ELxをスタックに保存 ・スタックポインタのアライメントを調整 ・割込み番号(GICC_IARから取得),例外番号(オフセット毎に定義した値)をスタックに保存 ・割込み発生前の割込み優先度マスクをスタックに保存 ・GICC_PMRに受け付けた割込みの割込み優先度をセット ・割込み/例外ネストカウンタのインクリメント ・(割込みの場合)CPUロック解除 ・ベクタテーブルを読み込みハンドラを実行する ●例外/割込みからのリターン (ソフトウェアでの処理) ・CPUロック状態へ移行 ・割込み/例外ネスとカウンタのデクリメント ・GICC_PMRに割込み発生前の優先度マスクを戻す ・スタックポインタのアライメント調整分を戻す ・戻り先のコンテキストやシステム状態に応じて割込み/例外発生元へリターン または遅延ディスパッチを実行する ・ELR_ELxに戻り先アドレス,SPSR_ELxにプロセッサ状態がそれぞれ セットされている状態でeret命令を実行し,割込み/例外からリターン ○OSの実装 1.コア依存部名称: arm64 ARMv8 では,64ビットモード(AArch64)および32ビットモード(AArch32)の両方をサポートするが 本実装は64ビットモードのみをサポートすることを明示するためプロセッサ依存部名称を arm64 とした 2. 例外モードの使い分け 本実装ではタスクコンテキスト,非タスクコンテキストいずれも例外レベル1(EL1)で動作する. タスクコンテキストをEL0で動作する案も考えられる.その場合は割込み/例外の出口処理において 遅延ディスパッチの前に例外レベルの切り替えが必要となる. 3.ディスパッチャの実行モード 本実装ではすべて例外レベル1で動作するため,ディスパッチャは例外レベル1(EL1)で動作する. 4.スタックの使い分け SSPではタスクコンテキスト,非タスクコンテキスト共に一つのスタックを共用する. 本実装ではSP_ELxを使用している. この他にSP_EL0を使用する実装も考えられる.その場合は割込み/例外の入口処理にて レジスタを保存する前にSP_EL0へのスタック切り替えを行う. 5.コンテキストの判定 割込み/例外のネスト回数を保持する変数(intnest)が0ならタスクタスクコンテキスト, 1以上なら非タスクコンテキストとする. コンテキスト毎に例外レベルを分ける実装を取る場合はPSTATEのビット2および3で 例外レベルを読み出すことにより判別するという方法も考えられる. 本実装ではコンテキストによらず同じ例外レベルを使用し,プロセッサの ステータスレジスタでは判別ができないため変数で判別する. 6. CPUロック CPUロックPSTATE.IによりCPUロックを実現する. 7. 割込みロックとCPU例外の関係 FIQをカーネル管理外の割込みとするため,割込みロックはPSTATE.Iに加えて PSTATE.Fも用いて実現する.ただし,フラグをお互いに独立させるため, 割込みロック状態の設定/解除の際は,ロック前にCPUロックフラグ(=PSTATE.I)の 状態を保存し,解除前に元の状態に戻すようにしている. 8. 外部優先度と内部優先度 外部優先度とはAPIで指定する割込み優先度(PRI型)のことであり,値が小さい ほど優先度が高い.割込みハンドラには,-1から連続した負の値を設定可能で ある.GICの場合,内部優先度は,GICC_PMRレジスタに設定する値である. 外部優先度と内部優先度の変換は以下のマクロで表現される. /* 外部表現への変換 */ #define EXT_IPM(pri) \ (((PRI)((pri) >> GIC_PRI_SHIFT)) - (GIC_PRI_LEVEL - 1)) /* 内部表現への変換 */ #define INT_IPM(ipm) \ (((uint_t)((ipm) + (GIC_PRI_LEVEL - 1))) << GIC_PRI_SHIFT) ここでGIC_PRI_LEVELはサポートする割込み優先度の数,GIC_PRI_SHIFTはGICC_PMRレジスタ内 の,内部優先度の値が格納される位置(ビット0からのオフセット)を表す. 9. カーネル管理内の最高優先度(CPUロック状態での優先度マスク) GICの場合,カーネル管理内の割込みの最高優先度は設定可能な外部優先度の最高値と同じとする. 以上.