メモリマップの設定 (MMUの設定)

概要

SOLID の目標の一つに、RTOS での MMU の有効活用があります。

MMU の設定は複雑で、何か設定ミスがあった時のデバッグも難しいため、(ARM Cortex-A の場合、コード/データキャッシュを有効にするには MMU の設定が必須なので)とりあえず物理アドレスと仮想アドレスを同一に設定、といった最低限の初期化を行うだけで、有効活用されていないことが多いのではないでしょうか。一方 Linux 等のリッチ OS を使用すれば MMU は有効活用できるわけですが、リアルタイム性と見通しの良さを重視する SOLID とは目標が異なります。

SOLID はこの問題に対して、以下のようなソリューションを提供します。

IDE の GUI でメモリ空間を簡単設定

メモリマップデザイナというエディタを使用して、簡単にメモリ空間の設定・確認ができます。 メモリマップデザイナが扱う .smm (SolidMemoryMapDesigner 形式) ファイルから、設定プログラムがビルド時に自動生成され組み込まれるので、ユーザがプログラムを記述する必要はありません。

リンカスクリプトとの自動連係

リンカスクリプト中に .text, .data, .rodata, .bss の各セクションの先頭と終端を識別する為の特定のシンボルを定義することで、これらのセクションについて、 実行可不可、書き換え可不可の属性が自動的に設定されます。これにより、実行コードの破壊や、コード以外の部分の実行等を例外として捕捉することができます。

メモリ領域の属性を設定する API を提供

MMU には多くの設定項目が存在しますが、RTOS で必要な設定は限られています。 SOLID では直接 MMU を設定するのではなく、よりわかりやすく整理された API を使用し、例えば書き込み不可属性や実行可能属性など、簡単にメモリ領域の属性の設定が可能です。

MMU を活用すれば、仮想アドレス機能により、ターゲットボードの仕様に縛られることが無くなるため、プログラムのモジュール化や再利用が容易になります。また、メモリ保護機能により、プログラムの不正なメモリアクセス等を素早く検出・デバッグすることが可能になります。そしてソフトウェアの開発効率や品質を高められるだけではなく、今後 IoT 等でますます重要になると思われるセキュリティも高めることができます。(ただし、リアルタイム性を重視する SOLID では、Linux のような仮想アドレス空間の多重化や仮想メモリ、デマンドページング等は使用しません。)

さらに SOLID では、仮想アドレス空間を前提とすることで、従来はリッチ OS に限定されていた、アドレスサニタイザという動的解析技術を RTOS でも使用可能にしました。この技術により、実行速度をほとんど落とすこと無く、様々なプログラムの不具合を実行時に検出することが可能になります。

メモリマップデザイナ

SOLID の提供物には、ターゲットボードごとにデフォルトの .smm ファイルが存在するので、必要に応じてそれを改変して行くことになりますが、ここでは説明のため、空の .smm ファイル (test.smm) をプロジェクトに追加し、メモリマップデザイナで編集してみます。

空のファイルを開くと、何も設定が存在しません。左上の Add ボタンで新規設定を追加し、メモリ空間の名前や属性、物理/仮想アドレスなどを設定して行きます。

参考

属性については メモリ空間 を参照してください。予約済みメモリ空間については 定義済みのメモリマップ名 を参照してください

../_images/solid-tutorial-mmu-0.png

ここでは例として、仮想アドレスを指定しない (NO_ADDRESS) RAM 空間を定義しました。このメモリ空間は SOLID コアサービスにより管理され、動的メモリ割り当て時等に使用されます。 物理/仮想アドレスとサイズを変更するたびにメモリレイアウトの表示が更新されるので、確認しながら設定が可能です。

../_images/solid-tutorial-mmu-1.png

今回は例として、RAM 空間 (SOLID_RAM)、IO 空間 (SOLID_IO)、SOLID のコアサービスが使用するメモリ空間 (SOLID_CORE)、予約済み仮想アドレス空間 (SOLID_RESERVE) を一つずつ定義してみました。(SOLID_RESERVE 空間内の仮想アドレスは、後述する IO 領域の自動マッピングの際などに使用されます。)

../_images/solid-tutorial-mmu-2.png

設定が終わり、保存すると、以下のような XML ファイルとして保存されます。

<?xml version="1.0" encoding="utf-8"?>
<MemoryMap xmlns:dm0="http://schemas.microsoft.com/VisualStudio/2008/DslTools/Core" dslVersion="1.1.0.0" Id="32d50636-0859-4da8-8dd3-be592e8fb68a" 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="SOLID" PhysicalAddress="0x20800000" Size="0x00200000" Attribute="SOLID_CORE" VirtualAddress="0xf8000000" />
    <MemoryConfiguration Name="RESERVED" Size="0x01000000" Attribute="SOLID_RESERVE" VirtualAddress="0xfa000000" />
  </MemoryConfigurations>
  <AddressSanitizerSettings />
  <MappablePhysicalAreas>
    <MappablePhysicalArea Name="Area1" Size="0x100000000" />
  </MappablePhysicalAreas>
</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, "SOLID", 0xF8000000 },
    { 0xF0000000, 0x2000, SOLID_IO, "IO", 0xF1000000 },
    { NO_ADDRESS, 0x1000000, SOLID_RESERVE, "RESERVED", 0xFA000000 },
};

具体的には、要実装関数である IMPL_MEM_GetConfig 関数のデフォルト実装がこの配列を使用します。メモリマップデザイナを使用する場合、IMPL_MEM_GetConfig 関数をユーザが修正する必要はありません。

メモリマップデザイナの詳細は メモリマップデザイナ を参照してください。

メモリ領域の間の依存関係

メモリマップデザイナで設定するメモリ領域には、SOLIDコアサービスの仕様により、以下の依存関係が発生します。

「Physical Address (物理アドレス) のみが設定され、Virtual Address (仮想アドレス) が NO_ADDRESS で 属性が SOLID_RAM のメモリ領域」(複数設定可) は物理メモリのプールとして管理され、 「Physical Address が NO_ADDRESS で、Virtual Address のみが設定されていて、属性が SOLID_RESERVED のメモリ領域」(複数設定可) へ物理メモリをマップする際に切り出して使用されます。

そのため、前者の総合計サイズよりも、後者の総合計サイズが上回らないように設定する必要があります。後者が前者を上回った場合には、ターゲットプログラムの実行中にメモリマップに 失敗することがあります。この場合、 malloc() の失敗、タスク生成の失敗、ローダブルアプリケーションのロード失敗、といった現象が発生します。

前者のメモリ領域の例としては、上の例にもある領域名 RAM の領域。後者のメモリ領域の例としては、物理アドレスを指定しなかった場合の領域名 SOLID_HEAPOSSTACK の領域、 その他領域名 DLLAREAFTRACE の領域になります。

注釈

仮想アドレスへのメモリマッピングは 4KB アラインされた(下位12bitが0)アドレスから 4KB 単位に行われますので、 各領域の先頭アドレスが 4KB アラインされていない場合や領域サイズが4KB単位になっていない場合には、 先頭番地アドレスの丸めや、サイズ自体の切り上げを考慮した上で、総合計サイズを計算する必要があるという点に注意して下さい。

リンカスクリプトとMMU設定の連携

リンカスクリプト中には、 .text (実行コード), .rodata (書換え不可データ), .data (書換え可能データ), .bss (初期値なし書換え可能データ) のセクションがあります。 SOLID-OS で使用するリンカスクリプトでは、各セクションの先頭と末尾を識別するための固有シンボル (.text ならば、 _solid_text_start_solid_text_end) を定義しています。 これらのシンボルを参照して、各セクションの MMU 属性を以下の表のように自動設定します。

セクション

実行可不可

書換え可不可

.text

不可

.rodata

不可

不可

.data

不可

.bss

不可

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 有効時の、アドレス変換前のメモリアドレス。

プロセッサ

指定可能なメモリ範囲

備考

ARMv7-A/R, ARMv8-A(AArch32)

0x00000000 から 0xffffffff

ARMv8-A (AArch64)

0x0000000000000000 から 0x0000ffffffffffff

物理(メモリ)アドレス空間

CPU がアクセス可能な物理アドレス範囲。

プロセッサ

指定可能なメモリ範囲

備考

ARMv7-A/R, ARMv8-A(AArch32)

0x00000000 から 0xffffffff

ARMv7-A (LPAE有効)

0x0000000000000000 から 0x000000ffffffffff

SOLID_CORE は32-bitアドレス空間内に配置する必要があります

ARMv8-A (AArch64)

0x0000000000000000 から 0x0000ffffffffffff

(物理/仮想)アドレス空間

文脈が明らかな場合、物理/仮想を省略して単にアドレス空間と呼びます。

(物理/仮想)メモリ領域

(物理/仮想)アドレス空間内の、先頭アドレスとサイズで指定されるアドレス範囲。

メモリ空間

メモリマップデザイナで設定したメモリ領域のこと。名前、先頭の物理アドレス、属性、サイズ、マッピングする仮想アドレスを情報として持ちます。

メモリマップ

メモリ空間が、物理アドレス空間と仮想アドレス空間において、それぞれどのようにメモリ領域を割り当てられているのか(マッピング)を示した図。

メモリマップデザイナ

SOLID-IDE が提供するエディタ。メモリマップを GUI で編集(設定、確認)できます。