ARMv7-Mで動作モードを利用してコンテキストスイッチ

Cortex-M4のボードで自作OSに挑戦している。
Cortex-M4の準拠するARMv7-Mアーキテクチャでは2つの「動作モード」(Operating Mode)が存在する。

  • スレッドモード
  • ハンドラモード

リセット状態ではスレッドモードで、割り込みが発生するとハンドラモードに移行して割り込みハンドラが実行される。
スレッドモードは特権が存在し、特権・非特権の2つの状態があり、リセット状態では特権状態である。ハンドラモードでは常に特権状態となる。
特権の状態を切り替えるにはハンドラモードに突入してから切り替える、もしくは特権状態ならばCONTROLレジスタに書き込むことで非特権状態になれる。

この特権の状態を利用することで、カーネル(スケジューラ)部分は特権状態で動かして、通常のプロセスに相当する部分は非特権状態で動かすということができる。
では、どうやってリセット状態からプロセスを起動するか。
ARMv7-Mでは割り込みが発生するとスタックポインタに自動的に一部のレジスタ状態をプッシュし、
ハンドラモードからの復帰時にスタックポインタに保存されたレジスタを復元する。
スタックポインタはメインとプロセスの2種類あり、通常のスレッドモードでは、特権状態ではメインを、非特権状態ではプロセスを使う。
つまりプロセスを初期状態から起動させるには

  1. プロセススタックポインタにプロセスの初期状態をプッシュしておく
  2. svc命令でSVCの割り込みハンドラをハンドラモードで実行
  3. ハンドラから非特権状態のスレッドモードに復帰
  4. プロセスが起動される

という流れになる。

注意するべき点としては

  • 浮動小数点の機能が有効化されている場合はより多くの状態を保存するため、スタックのフレームサイズが代わる
  • スタックはCCR.STKALIGNがセットされていない場合は4バイトに、セットされている場合は8バイトにアラインされている必要がある
  • xPSRレジスタのうちThumbモードを有効にするビットは立てる必要がある(ARMv7-Mでは常にThumbモードで実行されなければならない)。よって初期値は0x01000000である必要がある
  • スタックに保存されないレジスタに関しては別途なんらかの手段で保存しておかないと複数プロセス間の切り替えができない

細かい動作はリファレンスマニュアルを参照すること。以下に自分が参考にした章を示す(マニュアルのバージョンはE.b)

  • B1.3 Overview of system level terminology and operation
    • 動作モードの定義
  • B1.4.2 The special-purpose program status registers, xPSR
    • xPSRレジスタの詳細
  • B1.5.6 Exception entry behavior
    • 例外発生時の動作について。スタックのレイアウトについても書かれている