セキュアカーネルの作成
ここでは最初に起動するセキュアカーネルの作成について説明しますが、M2351のclock設定やperipheral設定などTrustZoneとは関係のない対応は割愛させて頂きます。カーネルの初期化時に前回(第一回)で示したデモの構成に従い以下の設定を行います。
- SAU(Security Attribution Uint)によるメモリ構成の設定
- M2351のSCU(Secure Configuration Unit)の設定
- NVIC(Nested Vectored Interrupt Controller)の設定
- 両カーネル間の通信のための準備
- 非セキュアカーネルのロードと実行
SAUによるメモリ構成の設定
SAUはregionというレジスタ群でメモリ領域のアクセス属性を設定します。M2351は8つのregionを持っており、最大8つのメモリ領域を設定することができます。
デモでは以下の表に示す3つのregionを使用します。
region番号 | 先頭アドレス | 最終アドレス | 属性 |
---|---|---|---|
0 | 0x0000fc00 | 0x0000ffff | NSC(Non Secure Callable) |
1 | 0x30008000 | 0x30017fff | 非セキュア領域 |
2 | 0x50000000 | 0x5fffffff | 非セキュア領域 |
region0番には、べニア領域と言われる非セキュアモードからセキュアモードに遷移するために設けられた非セキュアモードが唯一callできる(NSC)セキュア領域を設定します。デモではセキュアカーネルROM実行空間の最後の1Kbytesを設定しています。
region1番には、前回(第一回)で示した非セキュアカーネルROM実行空間、非セキュアカーネルRAM空間、および共有メモリ空間を、非セキュアカーネルがアクセスできるように設定します。
region2番には、M2351の非セキュアモードでアクセスできるperipheral I/Oレジスタ領域を、非セキュアカーネルがアクセスできるように設定します。
以下にソースコードの例を示します。(out_w関数は指定I/Oアドレスに値を書き込む関数)
#define UTK_SAU_BASE 0xe000edd0
#define SAU_CTRL 0x0000
#define SAU_TYPE 0x0004
#define SAU_RNR 0x0008
#define SAU_RBAR 0x000c
#define SAU_RLAR 0x0010
#define SAU_CTRL_ALLNS 0x00000002
#define SAU_CTRL_ENABLE 0x00000001
/* SAU enable */
out_w(UTK_SAU_BASE + SAU_CTRL, SAU_CTRL_ENABLE);
/* region #0: non-secure callable, 0x0000fc00 - 0x0000ffff */
out_w(UTK_SAU_BASE + SAU_RNR, 0);
out_w(UTK_SAU_BASE + SAU_RBAR, 0x0000fc00);
out_w(UTK_SAU_BASE + SAU_RLAR, 0x0000ffe3);
/* region #1: non-secure, 0x30008000 - 0x30017fff */
out_w(UTK_SAU_BASE + SAU_RNR, 1);
out_w(UTK_SAU_BASE + SAU_RBAR, 0x30008000);
out_w(UTK_SAU_BASE + SAU_RLAR, 0x30017fe1);
/* region #2: non-secure, 0x50000000 - 0x5fffffff */
out_w(UTK_SAU_BASE + SAU_RNR, 2);
out_w(UTK_SAU_BASE + SAU_RBAR, 0x50000000);
out_w(UTK_SAU_BASE + SAU_RLAR, 0x5fffffe1);
SAUを有効にした後、region番号を変えながらSAU_RBARに先頭アドレス、SAU_RLARに最終アドレスを設定しています。SAUに設定できる領域は32byteアライメントであるため、SAU_RLARには属性情報も含まれています。SAU_RLARは以下の構造になっています。
M2351のSCUの設定
M2351が持っているSecure Configuration Unitに対して、非セキュアカーネルでアクセスできるSRAM領域やperipheralデバイスを設定します。
#define UTK_SCU_BASE 0x4002f000 /* SCU Base Address */
#define SCU_PNSSET6 0x00000018 /* Peripheral secure attribution set */
#define SCU_SRAMNSSET 0x00000024 /* SRAM secure attribution set */
/* SRAM non-secure area */
out_w(UTK_SCU_BASE + SCU_SRAMNSSET, 0x00000ff0);
/* USBD is non-secure */
out_w(UTK_SCU_BASE + SCU_PNSSET6, 0x00000001);
M2351はSCU_SRAMNSSETでSRAM領域をセキュア用領域と非セキュア用領域に分けることができます。SCU_SRAMNSSETの1bitが2Kbyteに相当し、1を設定した箇所が非セキュア用領域となります。デモでは0x00000ff0と設定しているので、SRAMの0x00008000~0x00017fffのオフセット部分が非セキュア用領域となります。M2351では非セキュアモードでSRAMにアクセスする場合は0x30000000がベースアドレスとなるため、0x30008000~0x30017fffが非セキュア用SRAM領域となります。
M2351はSCU_PNSSETでperipheralデバイスをセキュアモードで使用するのか非セキュアモードで使用するのかを設定することができます。デモではUSBDデバイスを非セキュアカーネルが操作できる設定にしています。
NVICの設定
割込みコントローラのNVIC_ITNSで、割込み発生時に非セキュアカーネルがハンドリングする割込みを設定します。他のNVICレジスタと同様に、割込み番号に対応したbitが並んでおり、対応するbitを1にすることで、その割込みは非セキュア用となります。
#define NVIC_BASE 0xe000e000
#define NVIC_ITNS 0x0380
/* USBD interrupt is non-secure */
out_w(NVIC_BASE + NVIC_ITNS + 4, 0x00200000);
/* External int1(No.11) is non-secure */
out_w(NVIC_BASE + NVIC_ITNS, 0x00000800);
デモではUSBDデバイスの割込みを非セキュア用にして、両カーネル間の通信用に外部割込みint1を非セキュア用に設定しています。(デモでは外部割込みint1を本来の用途である外部割込みとして使用していないため、両カーネル間の通信用に割り当てています。)
両カーネル間の通信のための準備
セキュアカーネルと非セキュアカーネルとの通信には以下の要素を用いて行うため、これらの準備を行います。
- カーネル間通信テーブル
- カーネル間割込み
- カーネル間通信用イベントフラグ
カーネル間通信テーブル
カーネル間通信テーブルは共有メモリ領域に配置し、セキュアカーネルが初期化時に0クリアします。
カーネル間割込み
相手カーネルにカーネル間通信テーブルを更新したことを通知するために使用する割込みで、デモではセキュアカーネルは外部割込みint0を、非セキュアカーネルは外部割込みint1を使用しています。(デモでは外部割込みint0およびint1を本来の用途である外部割込みとして使用していないため、割り当てています。)
準備としてはカーネルに以下の例のようにハンドラ登録を行います。
#define REQ_CALL_INTNUM 26 /* 外部割込みint0 */
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;
非セキュアカーネルのロードと実行
第三回で説明する非セキュアカーネルのコードをflash領域から、非セキュア領域のSRAMへコピーを行います。
セキュアカーネルでは、非セキュアカーネルを呼び出すタスクを生成し、その呼出しタスクにディスパッチすることで非セキュアカーネルに制御を渡します。
セキュアカーネルは非セキュアカーネルの初期化が終了したことを通知するイベントフラグを待ちます。以下に例を示します。
extern void non_sec_calltsk();
ID non_sec_call_tskid;
ER rc;
T_CTSK ctsk;
ctsk.exinf = NULL;
ctsk.tskatr = (TA_HLNG | TA_USERBUF | TA_RNG0);
ctsk.task = non_sec_calltsk;
ctsk.itskpri = MAX_PRI; /* 最低優先度 */
ctsk.stksz = 256;
ctsk.bufptr = non_sec_call_stack;
non_sec_call_tskid = tk_cre_tsk(&ctsk); /* 呼出しタスクを生成 */
if(non_sec_call_tskid < E_OK)
return rc;
rc = tk_sta_tsk(non_sec_call_tskid, 0);
if(rc < E_OK)
return rc;
/* カーネル間通信イベントフラグで非セキュアカーネルの初期化完了を待つ */
rc = req_wait(REQ_CODE_INIT, TMO_FEVR);
if(rc < E_OK)
return rc;
非セキュアカーネル呼出しタスクでは、以下の処理を行い非セキュアモードに遷移して、非セキュアカーネルに制御を渡します。
- sp_nsレジスタ(非セキュアモードでのスタックポインタ)の設定
- blxns命令による非セキュアモードへの遷移
以下に例を示します。
void non_sec_calltsk()
{
UB *non_sec_vector;
UB *non_sec_sp, *non_sec_pc;
non_sec_vector = &NSCODE_start; /* 非セキュアカーネルの先頭ベクタアドレス */
non_sec_sp = (UB *)(*((UW *)non_sec_vector));
non_sec_pc = (UB *)(*(UW *)(non_sec_vector + 4) & ~0x00000001);
asm("msr sp_ns, %0" :: "r"(non_sec_sp));
asm("blxns %0" :: "r"(non_sec_pc));
/* 復帰してくることはない */
}
次回(第三回)は非セキュアカーネルの作成について記述予定です。
『各種製品名は、各社の製品名称、商標または登録商標です。本記事に記載されているシステム名、製品名には、必ずしも商標表示((R)、TM)を付記していません。』