Everything

第3章  メモリ保護機能


本章では,RI600PXが提供しているメモリ保護機能について解説しています。
3.1 概  要
RI600PXは,マイコンに搭載されたMPU(Memory Protection Unit)を利用して,タスク・コンテキストからのメモリ・アクセスに関して,以下の保護機能を実現しています。なお,ハンドラは本機能に関係なく,すべてのアドレス空間にアクセスできます。
1 ) タスクおよびタスク例外処理ルーチンによる不正メモリ・アクセスの検出
タスクおよびタスク例外処理ルーチンは,アクセス許可された領域(メモリ・オブジェクト)のみにアクセスできます。許可されていない領域をアクセスすると,アクセス例外ハンドラが起動されます。
2 ) ユーザ・スタック保護
各タスクのユーザ・スタックは,他のタスクからはアクセスできません。ユーザ・スタックがオーバフローしたり,タスクが他のタスクのユーザ・スタックをアクセスすると,アクセス例外ハンドラが起動されます。
3 ) RI600PXによる不正メモリ・アクセスの検出
いくつかのサービス・コールは,引数としてポインタを受け取ります。RI600PXは,サービス・コールを呼び出したタスクがポインタで指定された領域をアクセス可能かを検査します。アクセス許可がない場合には,サービス・コールはエラーE_MACV を返します。
また,いくつかのサービス・コールはユーザ・スタックにタスクのコンテキスト・レジスタを保存します。このときにユーザ・スタックがオーバフローする場合には,サービス・コールはエラーE_MACV を返します。
この機能は,タスク・コンテキストから呼び出されたサービス・コールでのみ行われます。
3.2 ドメイン,メモリオブジェクト,アクセス許可ベクタ
メモリ保護機能は,以下を制御することで実現しています。
- だれが
- どこに対して
- どのアクセスが許可されるか
「だれが」に相当するものが「ドメイン」です。タスクとそのタスク例外処理ルーチンは,必ずいずれかのドメインに所属します。ドメインは1~15 のドメインID で識別されます。ドメインは,システム・コンフィギュレーション・ファイルで静的に生成されます。
「どこに対して」に相当するものが「メモリ・オブジェクト」で,「どのアクセスが許可されるか」に相当するものが「アクセス許可ベクタ」です。
メモリ・オブジェクトは,通常はシステム・コンフィギュレーション・ファイルで静的に登録しますが,動的に登録(ata_mem サービス・コール),登録解除(det_mem サービス・コール)することもできます。メモリ・オブジェクトは,先頭アドレスが16 バイト境界で,かつサイズが16 バイトの整数倍でなければなりません。
アクセス許可ベクタは,各ドメインに所属するタスクからのオペランド・リード・アクセス,オペランド・ライト・アクセス,および実行アクセスの可否を表します。
タスクから,メモリ・オブジェクトに対して許可されていないアクセスをした場合,およびメモリ・オブジェクトでも自タスクのユーザ・スタックでもない領域をアクセスした場合,アクセス例外ハンドラが起動されます。
一方,ハンドラ(割り込みハンド,周期ハンドラ,アラーム・ハンドラ,およびアクセス例外ハンドラ)にはドメイン所属の概念はなく,全アドレス空間にアクセスできます。
図3-1  メモリ保護の概要
表3-1にメモリ・オブジェクトに関する操作をまとめます。
表3-1  メモリ・オブジェクト操作
操作
静的(システム・コンフィギュレーション・ファイル)
動的(サービス・コール)
登録
memory_object[]
登録解除

アクセス許可変更

アクセス権チェック(メモリ・オブジェクト以外の領域もチェック可能)

状態参照


3.3 メモリ・オブジェクト数の制約
あるドメインにリード・アクセス,ライト・アクセス,実行アクセスの少なくともひとつのアクセスを許可されているメモリ・オブジェクトの数は,最大7つです。このことに留意して,メモリ・マップ設計を行ってください。ata_memまたは sac_memによってこの制約を満たさなくなる場合には,E_OACVエラーが検出されます。
3.4 信頼されたドメイン
メモリ・アクセス以外の保護機能をサポートしていないシステムでは,例えば以下のような不正アクセスの可能性が考えられます。
1 ) 悪意のあるタスクAは,メモリ・オブジェクトMに対するアクセス許可がない。
2 ) タスクAの作成者は,メモリ・オブジェクトMに対するアクセス許可があるドメインに所属するタスクBを生成して起動するように,タスクAを実装する。
3 ) タスクBがメモリ・オブジェクトMにアクセスしても,不正アクセスは検出されない。
RI600PXでは,このような悪意による不正メモリ・アクセスを防ぐために,「信頼されたドメイン」と呼ぶ機能をサポートしています。以下に示すソフトウエア構成に変更を与えるサービス・コールは,信頼されたドメインに所属するタスクからのみ呼出し可能となっています。これらのサービス・コールを信頼されていないドメインから呼び出した場合は,E_OACVエラーが検出されます。
- cre_???, acre_???, del_???, def_tex, ata_mem, det_mem, sac_mem
3.5 アクセス許可の変更
メモリ・オブジェクトに対するアクセス許可は,動的に変更(sac_memサービス・コール)することができます。例えば,以下のような使い方が想定されます。
アプリケーションを外部からダウンロードし,別のドメインに所属するタスクとして実行させるケースを考えます。
1 ) ダウンロードする領域をメモリ・オブジェクトとして登録(ata_mem)する。その際,そのメモリ・オブジェクトに対し,ダウンロードを実行するタスクが所属するドメインAからのライトアクセスを許可する。その後,メモリ・オブジェクト領域にダウンロードを行う。
2 ) ダウンロード完了後,そのメモリ・オブジェクトをドメインBからアクセスできるように設定(sac_mem)する。そして,ダウンロードしたコードを,ドメインBに所属するタスクとして生成し,起動する。
3.6 ユーザ・スタック保護
各タスクのユーザ・スタックは,そのタスクのみがアクセスできます。ユーザ・スタックがオーバフローしたり,他のタスクからアクセスすると,アクセス例外ハンドラが起動されます。
また,タスクから呼び出されたサービス・コールでカーネルがユーザ・スタックを使用する場合,カーネルはスタック・ポインタがそのタスクのユーザ・スタック領域内にあるかどうかを検査します。範囲外の場合にはエラーE_MACVを返します。
3.7 アクセス許可のチェック
共通ライブラリ関数など,複数のドメインから呼び出されるプログラムでは,メモリ・アクセスが可能かどうかを判定したい場合があります。このような場合は,vprb_memサービス・コールを利用してください。vprb_memサービス・コールは,指定したタスクが指定した領域に対して指定したアクセスが可能かを検査します。
3.8 プロセッサのモード
MPU(Memory Protection Unit)によるメモリ保護は,ユーザ・モードのときにのみ機能します。
RI600PXでは,タスク・コンテキストはユーザ・モード,非タスク・コンテキストはスーパバイザ・モードで動作する仕様となっています。
3.9 MPU(Memory Protection Unit)の有効化
RI600PXは,起動時(vsta_knlivsta_knl)に常にMPUを有効化します。システム動作中は,MPUを無効にしてはなりません。無効にした場合の動作は保証されません。
3.10 アクセス例外ハンドラ(_RI_sys_access_exception( ))
タスクまたはタスク例外処理ルーチンが,許可されていないメモリにアクセスした時には,アクセス例外ハンドラが起動されます。アクセス例外ハンドラでは,アクセス違反の要因を取り除いて復帰するなどの処理を行うことができます。あるいは,デバッグ目的のみで使用しても構いません。
3.10.1 ユーザ・オウン・コーディング部
アクセス例外ハンドラは,ユーザ・オウン・コーディング部として実装する必要があります。
備考 RI600PXで提供するサンプルのアクセス例外ハンドラのソース・ファイルは,“access_exc.c”です。
- アクセス例外ハンドラの基本型
以下に,アクセス例外ハンドラの基本型を示します。
 #include        "kernel.h"              /*標準ヘッダ・ファイルの定義*/
 #include        "kernel_id.h"           /*cfg600pxが出力するヘッダ・ファイルの定義*/
 
 ////////////////////////////////////////////////////////////
 // Access exception handler
 ////////////////////////////////////////////////////////////
 void _RI_sys_access_exception(UW pc ,UW psw, UW sts, UW addr);
 void _RI_sys_access_exception(UW pc ,UW psw, UW sts, UW addr)
 {
         ............
         ............
 }

備考 アクセス例外ハンドラの関数名は“_RI_sys_access_exception”です。
- パラメータ
I/O
パラメータ
レジスタ
説明
I
UW pc;
R1
アクセス例外を発生させた命令のアドレス
I
UW psw;
R2
アクセス例外発生時のPSW
I
UW sts;
R3
アクセス違反要因
MPU(Memory Protection Unit)のMPESTSレジスタ値が設定されます。
I
UW addr;
R4
オペランド・アクセス・エラーの場合は,そのアクセス・アドレス(=MPUのMPDEAレジスタの値)が設定されます。
実行アクセス・エラーの場合は,不定です。

- スタック
アクセス例外ハンドラは,システム・スタックを使用します。
- サービス・コールの発行
アクセス例外ハンドラでは,“発行有効範囲”が“非タスク”のサービスコールを発行可能です。
- 処理開始時のPSW
表3-2  アクセス例外ハンドラ処理開始時のPSW
ビット

備考
I
0
全割り込み禁止
IPL
アクセス例外発生前と同じ
処理開始時より下げてはなりません。
PM
0
スーパバイザ・モード
U
0
システム・スタック
C, Z, S, O
不定
その他
0

3.11 メモリ・マップ設計
本節では,メモリ・マップ設計に必要な情報を説明します。本節を参考に,セクション分割,メモリ・オブジェクト定義,リンカでのセクション配置を行ってください。
3.11.1 メモリ・オブジェクトのアドレス制約
メモリ・オブジェクトの先頭アドレスは16バイト境界,サイズは16の倍数でなければなりません。
システム・コンフィギュレーション・ファイルで,静的API“memory_object[]”によってメモリ・オブジェクトを登録するとき,メモリ・オブジェクトのアドレスを絶対アドレスまたはセクション名で指定することができます。
絶対アドレス指定は,I/Oレジスタ領域などの指定するときに使用します。
セクション名指定では,メモリ・オブジェクトを構成する先頭のセクション名と最後のセクション名を指定します。この場合,リンク時に必ず想定通りのセクション配置となるように注意してください。たとえば,メモリ・オブジェクトの先頭は16バイト境界でなければならないので,メモリ・オブジェクトの先頭(memory_object[].start_address)に指定したセクションに対し,リンカの“aligned_section”オプションを指定してください。
また,メモリ・オブジェクトのサイズは16の整数倍,すなわちメモリ・オブジェクトの終端アドレスは16の整数倍+15でなければなりません。しかし,メモリ・オブジェクトの最後のセクション(memory_object[].end_address)がこの通りになるとは限りません。最後のセクションの終端が16の倍数+15でない場合には,終端+1から次の16の倍数+15までの範囲も,そのメモリ・オブジェクトの一部と扱われます。したがって,リンク時には終端セクションの終端+1から16の倍数+15の範囲には他のセクションを配置してはなりません。
- 例
memory_object[].end_addressに“CU_DOM1”を指定し,CU_DOM1セクションの終端アドレスが0xFFFF1003の場合,0xFFFF1004~0xFFFF100Fの範囲に他のセクションを配置してはなりません。リンク時に,CU_DOM1の後続のセクションに“aligned_section”オプションを指定することで,0xFFFF1004~0xFFFF100Fにはどのセクションも配置されなくなります。
3.11.2 メモリ・オブジェクト化すべき領域
1 ) タスクがアクセスする領域
タスクがアクセスできるのは,そのタスク自身のユーザ・スタックを除くと,アクセス許可が適切に設定されたメモリ・オブジェクトのみです。したがって,タスクがアクセスするプログラムセクション,定数セクション,初期化データ・セクション,未初期化データ・セクションは,メモリ・オブジェクト内に配置する必要があります。また,タスクでI/O領域をアクセスする場合には,その領域もメモリ・オブジェクトとする必要があります。
2 ) メールボックスで扱うメッセージ
メッセージは,送受信双方のタスクからアクセス可能なメモリ・オブジェクト内に作成する必要があります。
ただし,メッセージ先頭には,カーネルの管理テーブルがあります。カーネル管理テーブルが破壊されると,システムの正常な動作は保証されません。この理由から,メッセージ通信には,データ・キューまたはメッセージ・バッファの使用を推奨します。
3 ) 固定長・可変長メモリ・プール領域
メモリ・プール領域は,メモリ・ブロックを使用するタスクからアクセス可能なメモリ・オブジェクト内とする必要があります。
ただし,RI600PXはメモリ・プール領域内に管理テーブルを生成します。カーネル管理テーブルが破壊されると,システムの正常な動作は保証されません。
‐ システム・コンフィギュレーション・ファイルで固定長メモリ・プールを生成する場合
固定長メモリ・プール領域はmemorypool[].sectionで指定したセクションに生成されます。memorypool[].sectionを省略した場合,固定長メモリ・プール領域はBURI_HEAPセクションに生成されます。
cre_mpfacre_mpfで固定長メモリ・プールを生成する場合
固定長メモリ・プール領域はアプリケーション側で確保し,これらのサービス・コールでその先頭アドレスを指定します。
‐ システム・コンフィギュレーション・ファイルで可変長メモリ・プールを生成する場合
可変長メモリ・プール領域はvariable_memorypool[].mpl_sectionで指定したセクションに生成されます。variable_memorypool[].mpl_sectionを省略した場合,可変長メモリ・プール領域はBURI_HEAPセクションに生成されます。
cre_mplacre_mplで可変長メモリ・プールを生成する場合
可変長メモリ・プール領域はアプリケーション側で確保し,これらのサービス・コールで先頭そのアドレスを指定します。
3.11.3 メモリ・オブジェクトにしてはならない領域
1 ) BURI_HEAP以外のRI600PXのセクション
BURI_HEAP以外のRI600PXのセクションはRI600PXのみがアクセスするため,メモリ・オブジェクトにしてはなりません。RI600PXのセクションについては,「2.6.4 セクション配置」を参照してください。
2 ) タスクのユーザ・スタック領域
タスクのユーザ・スタック領域はメモリ・オブジェクト外でなければなりません。他のユーザ・スタックおよびメモリ・オブジェクトと重なっていた場合,システムの正常な動作は保証されません。
‐ システム・コンフィギュレーション・ファイルでタスクを生成する場合
ユーザ・スタック領域はtask[].stack_sectionで指定したセクションに生成されます。task[].stack_sectionを省略した場合,ユーザ・スタック領域はSURI_STACKセクションに生成されます。
cre_tskacre_tskでタスクを生成する場合
ユーザ・スタック領域はアプリケーション側で確保し,これらのサービス・コールでそのアドレスを指定します。
3 ) データ・キュー領域
データ・キュー領域はメモリ・オブジェクト外でなければなりません。ユーザ・スタックおよびメモリ・オブジェクトと重なっていた場合,システムの正常な動作は保証されません。
‐ システム・コンフィギュレーション・ファイルでデータ・キューを生成する場合
データ・キュー領域はRI600PXのBRI_RAMセクションに生成されます。
cre_dtqacre_dtqでデータ・キューを生成する場合
データ・キュー領域はアプリケーション側で確保し,これらのサービス・コールでそのアドレスを指定します。
4 ) メッセージ・バッファ領域
メッセージ・バッファ領域はメモリ・オブジェクト外でなければなりません。ユーザ・スタックおよびメモリ・オブジェクトと重なっていた場合,システムの正常な動作は保証されません。
‐ システム・コンフィギュレーション・ファイルでメッセージ・バッファを生成する場合
データ・キュー領域はRI600PXのBRI_RAMセクションに生成されます。
cre_mbfacre_mbfでメッセージ・バッファを生成する場合
メッセージ・バッファ領域はアプリケーション側で確保し,これらのサービス・コールでそのアドレスを指定します。
5 ) 固定長メモリ・プール管理領域
固定長メモリ・プール管理領域はメモリ・オブジェクト外でなければなりません。ユーザ・スタックおよびメモリ・オブジェクトと重なっていた場合,システムの正常な動作は保証されません。
‐ システム・コンフィギュレーション・ファイルで固定長メモリ・プールを生成する場合
固定長メモリ・プール管理領域はRI600PXのBRI_RAMセクションに生成されます。
cre_mpfacre_mpfで固定長メモリ・プールを生成する場合
固定長メモリ・プール管理領域はアプリケーション側で確保し,これらのサービス・コールでそのアドレスを指定します。