第一回は概要、第二回はセキュアカーネルの作成を説明してきました。
第三回では非セキュアカーネルの作成および起動について説明します。
非セキュアカーネルの作成
非セキュアカーネルは基本的にセキュアカーネルと同じものを使用しますが、以下に示す項目をセキュアカーネルから変更して非セキュアカーネルを作成します。
- 初期化
- 両カーネル間の通信
初期化
M2351のclock設定やセキュアカーネルが制御するperipheralデバイスのアクセスが発生すると、エラー例外が発生してしまうため、セキュア領域へのアクセスを行っている部分をスキップするようにします。
両カーネル間の通信
セキュアカーネルと非セキュアカーネルとの通信を行うために、非セキュアカーネル側でも以下の準備が必要となります。
- カーネル間割込み
- カーネル間通信用イベントフラグ
カーネル間割込み
相手カーネルにカーネル間通信テーブルを更新したことを通知するために使用する割込みで、非セキュアカーネルは外部割込みint1を使用しています。(デモでは外部割込みint1を本来の用途である外部割込みとして使用していないため、割り当てています。)
準備としては以下の例のようにハンドラ登録を行います。割込み番号以外はセキュアカーネルと同様です。
#define REQ_CALL_INTNUM 27 /* 外部割込みint1 */
extern void req_call_handler(UINT dintno);
ER rc;
T_DINT dint;
dint.intatr = TA_HLNG;
dint.inthdr = req_call_handler;
rc = tk_def_int(REQ_CALL_INTNUM, &dint); /* ハンドラ登録 */
if(rc < E_OK)
return rc;
ClearInt(REQ_CALL_INTNUM);
EnableInt(REQ_CALL_INTNUM);
カーネル間通信用イベントフラグ
相手カーネルからのレスポンス待ちなどに使用するイベントフラグをセキュアカーネルと同様、以下の例のように作成しておきます。
ID sec_kernel_flgid;
T_CFLG cflg;
cflg.exinf = NULL;
cflg.flgatr = (TA_TPRI | TA_WMUL);
cflg.iflgptn = 0;
sec_kernel_flgid = tk_cre_flg(&cflg);
if(sec_kernel_flgid < E_OK)
return sec_kernel_flgid;
非セキュアカーネルの配置と実行
非セキュアカーネルをビルドして、第一回で示した非セキュアカーネルのロードモジュール置場(0x00010000~0x0001ffff)に配置しておくと、セキュアカーネルが実行する仕組みにしています。
セキュアカーネルは初期化時に以下のことを行います。
- 非セキュアカーネルをロードモジュール置場から非セキュア領域へコピー
- 非セキュアモードへの切替え、および非セキュアカーネルの呼出し
非セキュアカーネルの初期化完了通知
非セキュアカーネルの初期化は、セキュアカーネルの初期化から呼び出されます。呼び出したセキュアカーネルは非セキュアカーネルの初期化が完了するのを待つようにしています。
そのため、非セキュアカーネルは初期化完了をセキュアカーネルに通知する必要があり、カーネル間通信を用いて通知を行います。次節にカーネル間通信について説明します。
カーネル間通信
共有メモリに設けたカーネル間通信テーブルに通知したい情報を書き、カーネル間通信用に割り当てた割込みを発生させて相手カーネルに制御を渡すことで実現しています。
デモでは以下のような実装で行っています。
- カーネル間通信テーブルの構造
- カーネル間割込み発生部
- カーネル間割込みハンドラ
カーネル間通信テーブルの構造
共有メモリの先頭部分に以下の構造のテーブルを定義します。
offset | 内容 |
---|---|
0 | セキュアカーネル用フラグ(0:未初期化、1:使用中、2:使用可能) |
4 | セキュアカーネル用sequence番号 |
8 | セキュアカーネル用ack番号 |
12 | セキュアカーネル用通知コード番号 |
16 | セキュアカーネル用引数 |
20 | reserve |
24 | reserve |
28 | reserve |
32 | 非セキュアカーネル用フラグ(0:未初期化、1:使用中、2:使用可能) |
36 | 非セキュアカーネル用sequence番号 |
40 | 非セキュアカーネル用ack番号 |
44 | 非セキュアカーネル用通知コード番号 |
48 | 非セキュアカーネル用引数 |
52 | reserve |
56 | resreve |
60 | reserve |
ソースコードでは以下のように実装しています。
struct req_call_table {
UW flag;
UW seq;
UW ack;
UW code;
UW data1;
UW reserve2;
UW reserve3;
UW reserve4;
};
struct req_call_table req_call_table[2];
カーネル間割込み発生部
非セキュアカーネルからセキュアカーネルに割込みを発生させ情報を通知したい場合、NVICのpendingレジスタを操作して、外部割込みint0に割込みを発生させます。
セキュア用の割込みが発生することで、セキュアモードに遷移させてセキュアカーネルのハンドラに制御を渡します。
#define REQ_CALL_INTNUM_SEC 26
__cmse_nonsecure_entry ER req_call_sec(ID num, UW data1)
{
if(req_call_table[0].flag != 2){
return(E_BUSY);
}
req_call_table[0].flag = 1;
req_call_table[0].code = num;
req_call_table[0].data1 = data1;
req_call_table[0].seq++;
out_w(NVIC_BASE + NVIC_SETPEND, (1 << (REQ_CALL_INTNUM_SEC - 16)));
}
セキュアカーネルから非セキュアカーネルに割込みを発生させ情報を通知したい場合、NVICのpendingレジスタを操作して、外部割込みint1に割込みを発生させます。
非セキュア用の割込みが発生することで、非セキュアモードに遷移させて非セキュアカーネルのハンドラに制御を渡します。
#define REQ_CALL_INTNUM_NOSEC 27
LOCAL ER req_call_nonsec(ID num, UW data1)
{
if(req_call_table[1].flag != 2){
return(E_BUSY);
}
req_call_table[1].flag = 1;
req_call_table[1].code = num;
req_call_table[1].data1 = data1;
req_call_table[1].seq++;
out_w(NVIC_BASE + NVIC_SETPEND, (1 << (REQ_CALL_INTNUM_NONSEC - 16)));
}
カーネル間割込みハンドラ
カーネル間割込みが発生したとき、通知の重複がないことを確認した後、イベントフラグやメールボックスでタスクへ通知しています。
#define SEC_KERNEL_NUMBER /* セキュアカーネル=0、非セキュアカーネル=1 */
void req_call_handler(UINT dintno)
{
struct req_call_table *tbl = &req_call_table[SEC_KERNEL_NUMBER];
if(tbl->seq != tbl->ack){
tbl->ack++;
if(tbl->code & 0x0000ffff){
tk_set_flg(sec_kernel_flgid, tbl->code);
}
else{
tk_snd_mbx(sec_kernel_mbxid, (T_MSG *)(tbl->data1));
}
}
tbl->flag = 2;
}
次回はディスパッチの制御について記述予定です。
『各種製品名は、各社の製品名称、商標または登録商標です。本記事に記載されているシステム名、製品名には、必ずしも商標表示((R)、TM)を付記していません。』