メモリマップの設定 (MMUの設定)
概要
SOLID の目標の一つに、RTOS での MMU の有効活用があります。
MMU の設定は複雑で、何か設定ミスがあった時のデバッグも難しいため、(ARM Cortex-A の場合、コード/データキャッシュを有効にするには MMU の設定が必須なので)とりあえず物理アドレスと仮想アドレスを同一に設定、といった最低限の初期化を行うだけで、有効活用されていないことが多いのではないでしょうか。一方 Linux 等のリッチ OS を使用すれば MMU は有効活用できるわけですが、リアルタイム性と見通しの良さを重視する SOLID とは目標が異なります。
SOLID はこの問題に対して、以下のようなソリューションを提供します。
- IDE の GUI でメモリ空間を簡単設定
メモリマップデザイナというエディタを使用して、簡単にメモリ空間の設定・確認ができます。 メモリマップデザイナが扱う smm (SolidMemoryMapDesigner 形式) ファイルから、設定プログラムがビルド時に自動生成され組み込まれるので、ユーザがプログラムを記述する必要はありません。
- メモリ領域の属性を設定する API を提供
MMU には多くの設定項目が存在しますが、RTOS で必要な設定は限られています。 SOLID では直接 MMU を設定するのではなく、よりわかりやすく整理された API を使用し、例えば書き込み不可属性や実行可能属性など、簡単にメモリ領域の属性の設定が可能です。
MMU を活用すれば、仮想アドレス機能により、ターゲットボードの仕様に縛られることが無くなるため、プログラムのモジュール化や再利用が容易になります。また、メモリ保護機能により、プログラムの不正なメモリアクセス等を素早く検出・デバッグすることが可能になります。そしてソストウェアの開発効率や品質を高められるだけではなく、今後 IoT 等でますます重要になると思われるセキュリティも高めることができます。 (ただし、リアルタイム性を重視する SOLID では、Linux のような仮想アドレス空間の多重化や仮想メモリ、デマンドページング等は使用しません。) さらに SOLID では、仮想アドレス空間を前提とすることで、従来はリッチ OS に限定されていた、アドレスサニタイザという動的解析技術を RTOS でも使用可能にしました。この技術により、実行速度をほとんど落とすこと無く、様々なプログラムの不具合を実行時に検出することが可能になります。
メモリマップデザイナ
SOLID の提供物には、ターゲットボードごとにデフォルトの smm ファイルが存在するので、必要に応じてそれを改変して行くことになりますが、ここでは説明のため、空の smm ファイル(test.smm)をプロジェクトに追加し、メモリマップデザイナで編集してみます。
空のファイルを開くと、何も設定が存在しません。左上の Add ボタンで新規設定を追加し、メモリ空間の名前や属性、物理/仮想アドレスなどを設定して行きます。
ここでは例として、仮想アドレスを指定しない(NO_ADDRESS) RAM 空間を定義しました。このメモリ空間は SOLID コアサービスにより管理され、動的メモリ割り当て時等に使用されます。 物理/仮想アドレスとサイズを変更するたびにメモリレイアウトの表示が更新されるので、確認しながら設定が可能です。
今回は例として、RAM 空間 (SOLID_RAM)、IO 空間 (SOLID_IO)、SOLID のコアサービスが使用するメモリ空間 (SOLID_CORE)、予約済み仮想アドレス空間(SOLID_RESERVE)を一つずつ定義してみました。(RESERVE 空間内の仮想アドレスは、後述する IO 領域の自動マッピングの際などに使用されます。)
設定が終わり、保存すると、以下のような XML ファイルとして保存されます。
<?xml version="1.0" encoding="utf-8"?>
<MemoryMap xmlns:dm0="http://schemas.microsoft.com/VisualStudio/2008/DslTools/Core" dslVersion="1.0.0.0" Id="26026338-f676-4e22-84e6-1ff4f96404f8" xmlns="http://schemas.microsoft.com/dsltools/SolidMemoryMap">
<MemoryConfigurations>
<MemoryConfiguration Name="RAM" PhysicalAddress="0x20100000" Size="0x00180000" />
<MemoryConfiguration Name="IO" PhysicalAddress="0xf0000000" Size="0x00002000" Attribute="SOLID_IO" VirtualAddress="0xf1000000" />
<MemoryConfiguration Name="CORE" PhysicalAddress="0x20800000" Size="0x00200000" Attribute="SOLID_CORE" VirtualAddress="0xf8000000" />
<MemoryConfiguration Name="RESERVED" Size="0x01000000" Attribute="SOLID_RESERVE" VirtualAddress="0xfa000000" />
</MemoryConfigurations>
<AddressSanitizerSettings />
</MemoryMap>
この smm ファイルから、ビルド時に以下のような C 言語の配列定義が生成され、ターゲットプログラムに組み込まれます。
/****************************************************************************************************
This source code was generated by Solid Memory Map tool.
****************************************************************************************************/
const static SOLID_MEM_CONF myMemories[] =
{
{ 0x20100000, 0x180000, SOLID_RAM, "RAM", NO_ADDRESS, },
{ 0x20800000, 0x200000, SOLID_CORE, "CORE", 0xF8000000 },
{ 0xF0000000, 0x2000, SOLID_IO, "IO", 0xF1000000 },
{ NO_ADDRESS, 0x1000000, SOLID_RESERVE, "RESERVED", 0xFA000000 },
};
具体的には、要実装関数である IMPL_MEM_GetConfig()
関数のデフォルト実装がこの配列を使用します。メモリマップデザイナを使用する場合、IMPL_MEM_GetConfig()
関数をユーザが修正する必要はありません。
SOLIDで予約されているメモリ空間名
一部のメモリ空間名は、特定用途のために予約されています。
- IOAREA
I/Oリソースマネージャが使用する空間です。(属性 SOLID_RESERVE)
- OSSTACK
OSのスタックを自動で確保する際に使用する空間です。(属性 SOLID_RESERVE)
SOLID MEM API
メモリ関係の API は、関数名が SOLID_MEM で開始する関数となります。
メモリ空間のレイアウトはメモリマップデザイナで設定できましたが、それぞれのメモリ空間の中にも用途に応じて様々な属性を持つメモリ領域を設定できます。例えばプログラムコードが配置されるメモリ領域は、実行可能(SOLID_MEM_ATTR_EXECUTABLE
)だが書き込み不可(SOLID_MEM_ATTR_READONLY
)、データが配置されるメモリ領域は実行不可(SOLID_MEM_ATTR_EXECUTABLE
属性無し)、IO メモリ領域はキャッシュしない(SOLID_MEM_ATTR_CACHEABLE
属性無し)等です。メモリ属性の取得は SOLID_MEM_GetAttr()
関数を、変更は SOLID_MEM_SetAttr()
関数を使用します。
SOLID は仮想アドレス空間を前提にしていますが、仮想アドレス空間内にメモリ領域を割り当てる際には、SOLID_MEM_Alloc()
/SOLID_MEM_Realloc()
関数を、メモリ領域の解放には SOLID_MEM_Free()
関数を使用します。仮想アドレス空間内に物理アドレス領域をマッピングするには SOLID_MEM_Map()
関数を、アンマップには SOLID_MEM_Unmap()
関数を使用します。特定のアドレス範囲がアクセス可能かどうかは SOLID_MEM_IsValid()
関数を、仮想アドレスと物理アドレスの変換には SOLID_MEM_VA2PA()
関数を使用します。メモリマップデザイナで設定したメモリ空間の名前を用いて仮想アドレスとサイズを取得する SOLID_MEM_GetAddress()
関数も提供されます。
マッピングは SOLID_MEM_Map()
関数だけでも可能なのですが、デバイスごとに異なる IO メモリ領域を、それぞれのドライバでバラバラに仮想アドレス空間にマッピングしていくと、どのデバイスがどのメモリ領域を使うかの管理が煩雑になるので、SOLID はメモリマップデザイナで設定した SOLID_RESERVE
空間内に自動的に一意な IO メモリ領域を割り当てる SOLID_MEM_AllocIO()
関数、解放する SOLID_MEM_FreeIO()
関数、IO メモリ領域を確認する SOLID_MEM_CheckIO()
関数を提供します。
コード/データキャッシュの制御も、SOLID_MEM_CACHE
で開始する関数で可能です。それぞれ SOLID_MEM_CACHE_InvalidateCode()
関数(コードキャッシュ破棄)、SOLID_MEM_CACHE_Invalidate()
関数(データキャッシュ破棄)、SOLID_MEM_CACHE_Clean()
関数(データキャッシュ書き戻し)、SOLID_MEM_CACHE_Flush()
関数(データキャッシュを書き戻してから破棄)が提供されます。
属性や各関数の詳細は Core Service メモリ API リファレンス を参照してください。
用語
- メモリアドレス
文脈が明らかな場合、メモリを省略して単にアドレスと呼びます。
- 物理(メモリ)アドレス
MMU 無効時、あるいは MMU でアドレス変換後のメモリアドレス。
- 仮想(メモリ)アドレス
MMU 有効時の、アドレス変換前のメモリアドレス。 (注釈:ドキュメントは現在「論理アドレス」になっている所があるが、仮想アドレスに統一予定。)
- 物理(メモリ)アドレス空間
CPU がアクセス可能な物理アドレス範囲。 ARM Cortex-A の場合は 0x00000000-0xffffffff の範囲となります。
- 仮想(メモリ)アドレス空間
CPU がアクセス可能な仮想アドレス範囲。 ARM Cortex-A の場合は 0x00000000-0xffffffff の範囲となります。
- (物理/仮想)アドレス空間
文脈が明らかな場合、物理/仮想を省略して単にアドレス空間と呼びます。
- (物理/仮想)メモリ領域
(物理/仮想)アドレス空間内の、先頭アドレスとサイズで指定されるアドレス範囲。
- メモリ空間
メモリマップデザイナで設定したメモリ領域のこと。名前、先頭の物理アドレス、属性、サイズ、マッピングする仮想アドレスを情報として持ちます。
- メモリマップ
メモリ空間が、物理アドレス空間と論理アドレス空間において、それぞれどのようにメモリ領域を割り当てられているのか(マッピング)を示した図。
- メモリマップデザイナ
SOLID-IDE が提供するエディタ。メモリマップを GUI で編集(設定、確認)できます。