athrill2のTOPPERS/ATK2-SC3ハードウェア要求仕様準拠についてサンプルプログラムで確認する

#概要
athrill2のメモリ保護機能に対して，TOPPERS/ATK2-SC3のハードウェア要求で求められる機能について動作確認を実施します．

#対象とする要求仕様
athrill2がTOPPERS/ATK2-SC3の実機レス環境として最低限満たすべき要件は以下の通りです．

|要件番号|要件|
|---|---|
|【HW029】|メモリ保護を目的に搭載するハードウェアは，リアルタイム性に優れたメモリ保護機能ユニット(MPU)であること|
|【HW030】|保護領域レジスタは，ソフトウェアによって動的に設定内容を変更できること|
|【HW031】|プロセッサの動作モードが非特権の状態において， MPU は，以下のいずれかに該当するメモリアクセスをメモリ保護違反として検出できること【HW031】．<br>・ 対象メモリアドレスがいずれの保護領域レジスタにもマッチしないアクセス<br>・ 対象メモリアドレスがいずれかの保護領域レジスタにマッチしている場合で，その保護領域のアクセス許可設定で禁止されている種類のアクセス|
|【HW032】|MPU がメモリ保護違反を検出した際に発生する割込みは， OS 管理下の割込みより高い優先度の割込みか， NMI(ノンマスカブル割込み)であること|
|【HW033】|プロセッサが特権モードで実行している間は，以下のいずれかの振舞いが可能であること【HW033】．<br>・ MPU の機能を停止<br>・ すべてのメモリ領域に対するすべての種類のアクセスを許可|
|【HW034】|保護領域レジスタにて設定する保護領域に対して，書込みアクセスを禁止することができること|
|【HW035】|すべてのメモリアドレスに対して，読出しアクセスと実行アクセスを許可するように設定できること|
|【HW036】|MPU に設定可能な保護領域の情報の組が，以下に規定する数以上であること【HW036】．<br>・ コードMPU とデータ MPU の区別が無い場合は 3 組以上<br>・ コードMPU とデータ MPU が別の場合には，データ MPU に対して 3 組以上|
|【HW038】|保護領域レジスタにて設定する保護領域に対して，読出しアクセス，書込みアクセスを個別に禁止することができること|

#メモリ保護構成
上記要件を確認するために設計したメモリ保護構成は以下としました．

* カーネル領域　：１個(コード/データ)
* ユーザ領域　　：２個(コード/データ)
* ユーザ共有領域：１個(データ）

![image.png](https://qiita-image-store.s3.amazonaws.com/0/244147/ad67f9dd-7b43-f59c-b12c-713b6cbb4333.png)

#サンプルプログラム構成
サンプルプログラムでは，以下の２つのユーザモード(非特権モード)とカーネルモード(非特権モード)を持ちます．なお，カーネルモードから各ユーザモードへの切り替えは，動的にMPUの設定を変更することで実現します．

* ユーザモード(USER1)
  * USER1のデータ/ROM領域およびUSER共有データ領域のみアクセスできる
* ユーザモード(USER2)
  * USER2のデータ/ROM領域およびUSER共有データ領域のみアクセスできる
* カーネルモード
  * すべてのデータ/ROM領域をアクセスできる

上記のイメージ図を以下に示します．

![image.png](https://qiita-image-store.s3.amazonaws.com/0/244147/98820ec6-4faf-c49c-2cd5-a26c71290451.png)

#テストプログラム設計
##テストプログラム全体設計
MPUテストでは，メモリ保護が発生するとCPU例外が発生します．
そのため，CPU例外発生してから次のテストを実行できる仕組み・設計が必要となります．

今回は，下図のように，「テスト制御データ」をグローバル領域で確保しておき，テスト実行状況を更新・参照することで，任意のコンテキストからテスト継続を実施できる設計としました．

![image.png](https://qiita-image-store.s3.amazonaws.com/0/244147/4f5b88a7-6925-d428-cdad-4df1a057104e.png)

##データ読み書きテスト
MPUテストとして，データ読み書きのテストは，各モードに対してすべて実施します(下図)．
なお，NG発生した場合はCPU例外が発生し，次のテストを実行します．

![image.png](https://qiita-image-store.s3.amazonaws.com/0/244147/83bc4a17-1540-abdd-a595-d83fb4aa4bae.png)

##命令実行テスト
MPUテストとして，命令実行のテストは，各モードに対してすべて実施します(下図)．
なお，NG発生した場合はCPU例外が発生し，次のテストを実行します．

![image.png](https://qiita-image-store.s3.amazonaws.com/0/244147/77919aa9-59fe-bd3c-0377-2b0066adff46.png)

#テストプログラム実装
本テストプログラムは実装は以下で公開しています．

https://github.com/tmori/athrill/tree/master/sample/athrill2/mputest2

#テストプログラム実行方法
* テストプログラムのビルド方法

```
$ make clean;make
```

* ビルド成功するとmputest.elfファイルが作成されます

* テストプログラムの起動方法

```
$ athrill2 -i -d device_config.txt -m memory.txt -c1 mputest.elf
```

 * 正しく起動すると以下のメッセージが出力されます．
 * 
```
core id num=1
ROM : START=0x0 SIZE=512
ROM : START=0x100000 SIZE=512
ROM : START=0x200000 SIZE=512
RAM : START=0x6000000 SIZE=512
RAM : START=0x7000000 SIZE=512
RAM : START=0x8000000 SIZE=512
RAM : START=0x9000000 SIZE=512
Elf loading was succeeded:0x0 - 0x29ac : 10.428 KB
Elf loading was succeeded:0x29ac - 0x2a4c : 0.12 KB
Elf loading was succeeded:0x100000 - 0x100320 : 0.800 KB
Elf loading was succeeded:0x200000 - 0x2003c0 : 0.960 KB
ELF SYMBOL SECTION LOADED:index=25
ELF SYMBOL SECTION LOADED:sym_num=136
ELF STRING TABLE SECTION LOADED:index=26
[DBG>
HIT break:0x0
[NEXT> pc=0x0 vector.S 9
```

* 「c」コマンドを入力しテストプログラムを実行

```
[NEXT> pc=0x0 vector.S 9
c
```

* テスト実行ログが以下のように出力されます

```
[CPU>
****Start: Kernel Test:User1!****
 START:KernelMpuTestNo_1
 Expect: EXCEPTION for do [other user<id>_task_data = 10]
Exec Error code[0]=0x5f6a code[1]=0x1 type_id=0x0 code_id=113
 PASSED:Exception happened!!
 START:KernelMpuTestNo_2
 Expect: EXCEPTION for do [kernel_task_data = 10]
Exec Error code[0]=0x5f6a code[1]=0x1 type_id=0x0 code_id=113
 PASSED:Exception happened!!
 START:KernelMpuTestNo_3
 Expect: EXCEPTION for do [other user<id>_task_data = user2_task_data]
ERROR:can not load data:addr=0x8000000 size=4byte
Exec Error code[0]=0x5f2a code[1]=0x1 type_id=0x0 code_id=49
 PASSED:Exception happened!!
 START:KernelMpuTestNo_4
 Expect: EXCEPTION for do [other user<id>_task_data = kernel_task_data]
ERROR:can not load data:addr=0x60004a0 size=4byte
Exec Error code[0]=0x5f2a code[1]=0x1 type_id=0x0 code_id=49
 PASSED:Exception happened!!
 START:KernelMpuTestNo_5
 Expect: EXCEPTION for do [other user2_internal_func()]
 PASSED:Exception happened!!
 START:KernelMpuTestNo_6
 Expect: EXCEPTION for do [kernel_internal_func()]
 PASSED:Exception happened!!
 START:KernelMpuTestNo_7
 Expect: not happen EXCEPTION for do [internal user read/write/exec]
ERROR:can not load data:addr=0x60004a0 size=4byte
Exec Error code[0]=0x5f2a code[1]=0x1 type_id=0x0 code_id=49
PASSED <user1_test_result[KernelMpuTestNo_0] == TRUE> ../kernel/main.c:134
PASSED <user1_test_result[KernelMpuTestNo_1] == TRUE> ../kernel/main.c:135
PASSED <user1_test_result[KernelMpuTestNo_2] == TRUE> ../kernel/main.c:136
PASSED <user1_test_result[KernelMpuTestNo_3] == TRUE> ../kernel/main.c:137
PASSED <user1_test_result[KernelMpuTestNo_4] == TRUE> ../kernel/main.c:138
PASSED <user1_test_result[KernelMpuTestNo_5] == TRUE> ../kernel/main.c:139
PASSED <user1_test_result[KernelMpuTestNo_6] == TRUE> ../kernel/main.c:140
PASSED <user1_task_data == 100> ../kernel/main.c:142
PASSED <user_shared_data == 100> ../kernel/main.c:143
PASSED <user2_task_data == 99> ../kernel/main.c:144
PASSED <kernel_task_data == 99> ../kernel/main.c:145

****Start: Kernel Test:User2!****
 START:KernelMpuTestNo_1
 Expect: EXCEPTION for do [other user<id>_task_data = 10]
Exec Error code[0]=0x5f6a code[1]=0x1 type_id=0x0 code_id=113
 PASSED:Exception happened!!
 START:KernelMpuTestNo_2
 Expect: EXCEPTION for do [kernel_task_data = 10]
Exec Error code[0]=0x5f6a code[1]=0x1 type_id=0x0 code_id=113
 PASSED:Exception happened!!
 START:KernelMpuTestNo_3
 Expect: EXCEPTION for do [other user<id>_task_data = user2_task_data]
ERROR:can not load data:addr=0x7000000 size=4byte
Exec Error code[0]=0x5f2a code[1]=0x1 type_id=0x0 code_id=49
 PASSED:Exception happened!!
 START:KernelMpuTestNo_4
 Expect: EXCEPTION for do [other user<id>_task_data = kernel_task_data]
ERROR:can not load data:addr=0x60004a0 size=4byte
Exec Error code[0]=0x5f2a code[1]=0x1 type_id=0x0 code_id=49
 PASSED:Exception happened!!
 START:KernelMpuTestNo_5
 Expect: EXCEPTION for do [other user2_internal_func()]
 PASSED:Exception happened!!
 START:KernelMpuTestNo_6
 Expect: EXCEPTION for do [kernel_internal_func()]
 PASSED:Exception happened!!
 START:KernelMpuTestNo_7
 Expect: not happen EXCEPTION for do [internal user read/write/exec]
ERROR:can not load data:addr=0x60004a0 size=4byte
Exec Error code[0]=0x5f2a code[1]=0x1 type_id=0x0 code_id=49
PASSED <user2_test_result[KernelMpuTestNo_0] == TRUE> ../kernel/main.c:150
PASSED <user2_test_result[KernelMpuTestNo_1] == TRUE> ../kernel/main.c:151
PASSED <user2_test_result[KernelMpuTestNo_2] == TRUE> ../kernel/main.c:152
PASSED <user2_test_result[KernelMpuTestNo_3] == TRUE> ../kernel/main.c:153
PASSED <user2_test_result[KernelMpuTestNo_4] == TRUE> ../kernel/main.c:154
PASSED <user2_test_result[KernelMpuTestNo_5] == TRUE> ../kernel/main.c:155
PASSED <user2_test_result[KernelMpuTestNo_6] == TRUE> ../kernel/main.c:156
PASSED <user1_task_data == 100> ../kernel/main.c:158
PASSED <user_shared_data == 101> ../kernel/main.c:159
PASSED <user2_task_data == 100> ../kernel/main.c:160
PASSED <kernel_task_data == 99> ../kernel/main.c:161

****Start: Kernel Test:Kernel!****
PASSED <user1_task_data == 1000> ../kernel/main.c:34
PASSED <user2_task_data == 1000> ../kernel/main.c:35
PASSED <kernel_task_data == 1000> ../kernel/main.c:36
PASSED <user_shared_data == 1001> ../kernel/main.c:37
****End: Kernel Test!
```

※テスト実行中にCPU例外が発生しますので，ERRORメッセージが頻繁にでますが，期待したものです．

#まとめ
上記結果により，athrill2がTOPPERS/ATK2-SC3の実機レス環境として，最低限必要となるハードウェア要件を満たしていることを確認できました．