Posts filed under 'BSP'

OS の起動時間短縮(1/2)~RAM レジストリの初期化時間を短縮する

■はじめに
今回と次回は、WEC の起動時間を短縮する方策を、二つ紹介します。WEC 2013 では、Snapshot Boot が導入され、適切に実装・チューニングすれば、OS の起動時間を数秒以内に抑えることも可能になりました。しかし、WEC 2013 よりも前の版では、Snapshot Boot を行うことができません。そこで、WEC 7 でも対応可能な起動時間短縮の方策を紹介してみることにしました。なお、Snapshot Boot については、MSDN の次のページをご覧ください。

 Snapshot Boot Development (Compact 2013)
 http://msdn.microsoft.com/en-us/library/dn169259.aspx

日本国内の Windows Embedded MVP 有志が主催して不定期に開催している、「Windows Embedded Community Day」の第1回で僕が担当したプレゼンでも、Snapshot Boot について簡単に紹介しています:

 組み込みでもマルチコア。WEC2013 での対応
 http://www.slideshare.net/ShinyaKoga/2013-0719wemvp-kansai

 Windows Embedded Community Day 第 1 回 – 真夏の組み込み Windows 技術セミナー2013
 http://msdn.microsoft.com/ja-jp/dn521045

■RAM レジストリの初期化
2011/08/02 に書いたエントリ(「レジストリの永続化~RAM-Based の場合」)では、レジストリの永続化対応を行う場合、Hive-Based のレジストリよりも、RAM-Based のレジストリの方が、より堅牢だと述べました。しかし、RAM-Based のレジストリには、起動時間が長くなるという短所があります。これは、レジストリの初期化に要する時間が、RAM-Based のレジストリの方が長いことが要因のようです。

Hive-Based のレジストリは、レジストリの Hive ファイルを filesys.dll が memory mapped file としてオープンして内容にアクセスします。つまり、Hive ファイルの内容は、filesys.dll が RAM 上に構築するレジストリ(一種のオンメモリデータベース)と同じではないかと思われます。従って、OS の起動時にレジストリを初期化する動作というのは、OS イメージに収録されている Hive ファイルや、内容更新されて永続化記憶域(ストレージ)に配置された Hive ファイルを、単に memory mapped file としてオープンするだけなのでしょう。

一方、RAM-Based のレジストリは、filesys.dll が RAM 上に構築するレジストリとは内容(フォーマット)が異なるようです。このため、OS の起動時にレジストリを初期化する際、ストレージからレジストリデータを読み出し、その内容を解析して RAM 上にレジストリを構築する、という処理が必要になります。このため、レジストリの初期化に要する時間が長くなるのです。この時間は、レジストリ全体のサイズに依存しますから、一般的には、OS イメージに組み込む機能が多いほど、つまり、OS Design のカタログ項目の選択が多いほど長くなります。10秒以上に達することがあることを確認していますし、永続化した場合は、レジストリの初期化時間の合計で20秒を超える場合もあります。

■永続化された場合の RAM レジストリの初期化
上で、「永続化した場合は」と書きました。そうです。RAM レジストリを永続化した場合、永続化しない場合よりも、レジストリの初期化に要する時間が長くなるのです。これについては、2011/08/02 に書いたエントリでも紹介した、WinCE 6.0 のリファレンスにある、次のページの説明にヒントがあります。

 File System Initialization of RAM-based Registry (Windows Embedded CE 6.0)
 http://msdn.microsoft.com/en-US/library/ee490364(v=winembedded.60).aspx

このページには、RAM-based レジストリの初期化の流れが説明されているのですが、1から8まであるステップのうち、5と6は、次の通りです:

  1. Filesys.dll initializes the registry by checking for a registry in RAM.
    If Filesys.dll does not find a registry in RAM, it restores the registry from ROM. Filesys.dll loads \Windows\Default.fdf, which was created using Makeimg.exe.
  2. Filesys.dll checks for the ReadRegistryFromOEM OEM adaptation layer (OAL) function. If Filesys.dll does not find ReadRegistryFromOEM, it keeps the registry restored from ROM.

つまり、OS イメージ(ROM イメージ)に収録されている Default.fdf というファイル内容をロードして初期化した後、OAL の ReadRegistryFromOEM() を(それが実装されていれば)呼び出し、ストレージに保存されたレジストリデータをロードして初期化し直す、というわけです。つまり、RAM-Based のレジストリを永続化した場合、レジストリの初期化処理が実質二回行われるのです。

■永続化された RAM レジストリの初期化時間を短縮
RAM-Based のレジストリを永続化した場合に、永続化しない場合よりもレジストリの初期化に要する時間が長くなるのは、レジストリの初期化処理が二回行われるからなのです。これを、一回にすることができれば、そのぶん起動時間を短縮できることになります。何かよい方法はないでしょうか?

あります。それは、2012/01/13 に書いたエントリ(「OS の部分アップデート方策~その2(2/2)」)で述べた、ROM イメージを複数に分割することです。このエントリで、ROM イメージを複数の region に分割した場合、同じ名前(パス)のファイルに対して、region 間で shadowing が作用すると書きました(※「Region ごとの部分アップデート」という項をご覧ください)。この shadowing を利用するのです。

方策は、次の通りです:

1.) OSDesign.reg において、最小限の内容(たとえば、[HKLM\init] キーだけ)が有効になるように設定して OS イメージをビルドして、生成された Default.fdf ファイルを採取する。

2.) (1) で採取した Default.fdf ファイルのみを収録する ROM イメージの region を作成する。
 これは、config.bib によるメモリマップの設定と、ブートローダによる複数 region のロード、および、OAL による複数 region の連結処理(OEMRomChain のリスト構築処理)が必要です。

3.) 通常のビルド時は、(1) の設定を解除して OS イメージをビルドする。
 この結果、本来のレジストリ設定内容は、OS 本体を収録した region(NK region)に Default.fdf という内容で収録され、(1) で作成した(最少サイズの)Default.fdf は、それ専用の region に収録されます。

4.) OAL における OEMRomChain のリスト構築処理を実行する前に、ストレージ上にレジストリデータが存在するかどうかをチェックし、存在しない場合は、OEMRomChain のリスト構築処理において、Default.fdf のみを収録した region を無視する。ストレージ上にレジストリデータが存在する場合は、Default.fdf のみを収録した region を、OEMRomChain のリストにおいて、OS 本体を収録した region よりも前に配置する。

この方策により、ストレージ上にレジストリデータが存在する場合は、(1) で作成した最小限の Default.fdf ファイルの内容で一回目のレジストリ初期化が実行され、その時間を最小限(相対的に 0)にできます。その後、ストレージに保存されたレジストリデータがロードされ、レジストリが初期化される、というわけです。

ストレージ上にレジストリデータが存在しない場合は、(1) で作成した最小限の Default.fdf ファイルを収録した region は OEMRomChain のリストには挿入されませんから、shadowing が行われず、OS 本体を収録した region(NK region)に収録された、通常の Default.fdf がロードされてレジストリが初期化されます。従って、レジストリを一度もストレージに保存していない場合や、ストレージに保存したレジストリデータを無効化した場合にも、問題なく動作します。上記方策を適用しない場合の動作と比べて、起動時間が短縮される以外は、見た目の動作は変わりません。

Add comment 2014/09/23 koga

WEC 7/WinCE 6.0 のブートシーケンス~init に至るまで

■はじめに
前々回のエントリ(「Telnet サーバと udevice.exe(2/2)」)で、User Mode Driver Framework の Reflector が、カーネルランドからユーザランドを呼び出す仕組みを紹介した際、カーネルのソースコードを少しだけ覗いてみました。今回は、WEC 7/WinCE 6.0 のブートシーケンスについて、再びカーネルのソースコードを覗いてみます。

もしあなたが、WEC 7/WinCE 6.0 のカーネル移植をこれから始めてみようと思っていらっしゃるのであれば、少しだけ参考になるかも知れません。あるいは、カーネル移植を自分で手がけることはないけれど、どのような手順で WEC 7/WinCE 6.0 がブートするのか興味がある、という方にも、楽しんでもらえると良いなと思います。Linux などの UNIX 系 OS では、カーネルが最初に init プロセスを生成・起動し、init が全てのプロセスの母となりますが、WEC 7/WinCE 6.0 で init に相当するものは何なのかも、見てみます。

■WEC 7/WinCE 6.0 のブートシーケンス(ARM の場合)
分かりやすく書く、というのは、難しいことです。今回は、簡潔に、カーネルのソースコードの具体的な個所を示して、ブートシーケンスの大枠だけを書いてみます。興味のある方は、ご自分でソースコードを追ってみて下さい。正確に理解するには、それが一番だと思います。説明の都合上、ここでは ARM プロセッサの場合のブートシーケンスについて記します。他のプロセッサ(x86, MIPS)の場合も、大枠は違わないでしょう。

まず最初に実行されるのは、OS イメージの先頭に配置されるエントリルーチンの、StartUp() です。StartUp() は、startup.s というソースファイルにアセンブラで実装されており、プロセッサの必要最小限の初期化処理を行います。具体的には、(プロセッサをスーパバイザモードに設定した後で)割り込みコントローラと MMU をディゼーブルにして、キャッシュと RAM を初期化します。また、デバッグ用の LED 点灯などのために、必要最小限の周辺機器コントローラの初期化も行います。startup.s は、WEC 7/WinCE 6.0 に付属する BSP ですと、たとえば次の場所にあります:

・Freescale i.MX27 の BSP(WEC 7)
 %_WINCEROOT%/platform/3DS_iMX27/src/OAL/oallib/startup.s

・デバイスエミュレータの BSP(WinCE 6.0)
 %_WINCEROOT%/platform/DEVICEEMULATOR/src/oal/oallib/startup.s

エントリルーチンの StartUp() を実装している startup.s は、実はカーネルとブートローダに共通のソースファイルとなっています。StartUp() は、初期化処理を行った後に KernelStart() という手続きを呼び出すのですが、カーネルとブートローダのそれぞれにおいて、KernelStart() が実装されています。つまり、カーネルとブートローダでは異なる起動処理の本体を、同じ名前の KernelStart() という手続きで実装することにより、それを呼び出すエントリルーチンを共通化している、というわけです。

カーネルの方の KernelStart() は、nkldr.lilb という静的リンクライブラリに所属する armstart.s で実装されており、armstart.s は、
 %_WINCEROOT%/private/winceos/COREOS/nk/ldr/arm/
に収録されています。ここで、nkldr.lib は oal.exe にリンクされるライブラリです。oal.exe は、
 %_WINCEROOT%/public/common/oak/files/common.bib
にある次の行の設定により、nk.exe という名前で OS イメージに格納されます。この nk.exe(oal.exe)が、カーネル移植レイヤを含む中核であり、ブートシーケンスに最初から関わるのです。


MODULES
;  Name            Path                                           Memory Type
;  --------------  ---------------------------------------------  -----------
; @CESYSGEN IF CE_MODULES_NK
IF IMGNOKITL
    nk.exe          $(_FLATRELEASEDIR)\oal.exe                  NK  SHZ
ENDIF IMGNOKITL

IF IMGNOKITL !
IF IMGNOKITLDLL
    nk.exe          $(_FLATRELEASEDIR)\oalkitl.exe              NK  SHZ
ENDIF IMGNOKITLDLL
IF IMGNOKITLDLL !
    nk.exe          $(_FLATRELEASEDIR)\oal.exe                  NK  SHZ
    kitl.dll        $(_FLATRELEASEDIR)\kitl.dll                 NK  SHZ
ENDIF IMGNOKITLDLL !
ENDIF IMGNOKITL !

KernelStart() が呼び出された後のブートシーケンスは、ARM プロセッサの場合、次の通りです:

1.) KernelStart() は、OEMAddressTable に記述されたカーネル仮想アドレスのマップに従って、MMU のページテーブルを設定したのち、MMU とキャッシュをイネーブルにして仮想アドレスに遷移する。また、割り込みハンドラのスタックを設定する。

2.) その後、KernelStart() は、ARMInit() を呼び出して kernel.dll のエントリルーチンのアドレスを取得する。そして、そのアドレス(kernel.dll のエントリルーチンである NKStartup() の開始アドレス)へジャンプする。
  ここで、ARMInit() は FindKernelEntry() という関数を使って kernel.dll のエントリルーチンのアドレスを取得します。ARMInit() と FindKernelEntry()、そして NKStartup() は、それぞれ次のソースファイルで実装されています。

 - ARMInit() :nk.exe に所属
  %_WINCEROOT%/private/winceos/COREOS/nk/ldr/arm/arminit.c

 - FindKernelEntry() :nk.exe に所属
  %_WINCEROOT%/private/winceos/COREOS/nk/ldr/ldrcmn.c

 - NKStartup() :kernel.dll に所属
  %_WINCEROOT%/private/winceos/COREOS/nk/kernel/arm/mdarm.c

3.) NKStartup() が、(nk.exe のではなく)kernel.dll の KernelStart() を呼び出す。
  kernel.dll の KernelStart() を実装しているソースファイルは、
   %_WINCEROOT%/private/winceos/COREOS/nk/kernel/arm/armtrap.s
  です。

4.) KernelStart() が、KernelInit() を呼び出す。KernelInit() によって、カーネルのヒープと仮想記憶管理構造、および、プロセス管理構造とスレッド管理構造が初期化される。
  ここで、スレッド管理構造を初期化する THRDInit() という関数では、SystemStartupFunc() という関数を、最初に起動するスレッドが実行する手続きとしてセットします。KernelInit() と THRDInit() は、それぞれ次のソースファイルで実装されています。

 - KernelInit()
  %_WINCEROOT%/private/winceos/COREOS/nk/kernel/nkinit.c

 - THRDInit()
  %_WINCEROOT%/private/winceos/COREOS/nk/kernel/thread.c

5.) その後、KernelStart() は Reschedule() を呼び出す。Reschedule() によって、最初のスレッドに実行が移って動き出し、(4) で設定された SystemStartupFunc() を実行する。
  Reschedule() も、armtrap.s で実装されています。THRDInit() によってマルチスレッド機構が構築されて、最初の Reschedule() の呼び出しによって、マルチスレッドモードへ遷移するというわけです。

6.) SystemStartupFunc() が、KernelInit2() を呼び出して、マルチスレッドモードでなければ行えないカーネルの初期化処理を実行する。
  SystemStartupFunc() は、さらに、ローダーとページプールの初期化、および、(それらが組込まれていれば)カーネルのロギング機構やカーネルデバッガの初期化も行います。メッセージキューやウォッチドッグタイマの初期化も、ここで行われます。SystemStartupFunc() を実装しているソースファイルは、
   %_WINCEROOT%/private/winceos/COREOS/nk/kernel/schedule.c
です。KernelInit2() のソースファイルは、nkinit.c です。

7.) SystemStartupFunc() は、各種初期化動作を行った後、ブートシーケンスの最終段階として、RunApps() という関数を実行するスレッドを起動する。
  RunApps() を実行するスレッドを起動した後、SystemStartupFunc() は、RTC のアラーム発火を待つ無限ループを実行します。

8.) RunApps() が、filesys.dll の WinMain() を実行するスレッドを起動する。
  その名前から連想する動作とは違い、RunApps() は、プロセス(アプリケーション)を起動するのではなく、スレッドを起動します。純粋マイクロカーネル構造だった WinCE 5.0 までは、filesys は DLL ではなく EXE でしたから、その名残なのかも知れません。
  filesys.dll のメインルーチン(WinMain())を実行するスレッドを起動した後、RunApps() は、CleanPagesInTheBackground() という関数を実行します。CleanPagesInTheBackground() は、
   %_WINCEROOT%/private/winceos/COREOS/nk/kernel/physmem.c
  で実装されており、自身の優先度を最低優先度値(255)に変更した後、不要になったページを破棄する無限ループを実行します。つまり、Linux でいえば swapper に相当するアイドルスレッドとなります。

以上が、WCE/WinCE カーネルのブートシーケンスの大枠です。ブートの最終段階として、filesys.dll のメインルーチンが実行されることが分かりました。
filesys.dll は、起動後(つまり、メインルーチンの実行開始後)、レジストリの初期化とファイルシステム API の初期化を行い、続いて、Storage Manager と Device Manager (device.dll) を起動します。device.dll によってデバイスドライバがロードされ、Storage Manager によって、ファイルシステム上のファイルを読み書きできるようになります。その後、filesys.dll は、レジストリの HKEY_LOCAL_MACHINE\Init キー配下に記述された設定内容に従って、プロセスを起動します。標準シェル(explorer.exe)が起動されるのは、このタイミングです。

filesys.dll のブート時動作については、WinCE 6.0 のリファレンスで説明されています:

 File System Boot Process (Windows Embedded CE 6.0)
 http://msdn.microsoft.com/en-us/library/ee490357(v=winembedded.60).aspx

この Blog の以前のエントリでも、Storage Manager と device.dll の起動順序について書きました。興味のある方は、こちらもどうぞ:

 レジストリ変更内容の永続化(2/2)
 http://www.stprec.co.jp/ceblog/2011/02/21/

さて、filesys.dll が、ブートの完了時に、レジストリの HKEY_LOCAL_MACHINE\Init キー配下の記述に従ってプロセスを起動すると書きました。ということは、filesys.dll が全てのプロセスの親となるわけです。つまり、UNIX 系 OS の init プロセスに相当します。init プロセスとは異なり、filesys.dll は、カーネルにロードされる DLL ですが、WinCE 5.0 までは、filesys もプロセスでした。こうやって見てみると、ブートシーケンスの大枠は、UNIX 系 OS と *大きくは* 違わないことが分かります。

■KernelRelocate() について
WEC/WinCE のブートシーケンスについて、もう少しだけ補足します。上に書いたブートシーケンスの (2) に登場した、ARMInit() です。

ARMInit() は、FindKernelEntry() を呼び出す前に、KernelRelocate() という関数を呼び出します。この KernelRelocate() を呼び出すことによって、初めて大域変数へアクセスできるようになるのです。大域変数は、実行形式ファイル(.exe と .dll)の data セクションに配置されますが、OS イメージは ROM 領域にあるので、それを RAM 領域へ再配置(relocate)する必要があります。この再配置は、LoadLibrary() や CreateProcess() によってロードされる場合は、カーネルのローダーによって行われます。しかし、上で述べたブートシーケンスにおける、nk.exe(oal.exe)と kernel.dll の呼び出しの流れでは、(6) で SystemStartupFunc() がローダーを初期化する前に行われます。従って、ローダーは介在できず、自分自身で再配置処理を行われなければいけません。そのための関数が KernelRelocate() なのです。

KernelRelocate() による再配置処理の詳細は、MSDN の Blog サイトにある、WinCE の開発チームの人による Blog エントリに書かれています:

 How does Windows Embedded CE 6.0 Start?
 http://blogs.msdn.com/b/ce_base/archive/2007/11/26/how-does-windows-embedded-ce-6.0-start_3f00_.aspx

上のページで ‘KernelRelocate’ を検索してみて下さい。なお、ここの説明には、そこまでは書かれていないのですが、おそらく、romimage.exe が OS イメージファイル(nk.bin)を生成する際、”copy entries” として TOC に記述されるのは(※この copy entries を KernelRelocate() が参照します)、oal.exe と kernel.dll の data セクションだけではないかと思います。それら以外の EXE や DLL の data セクションは、ローダーによってロード時に再配置が行われるはずですし、必ずロードされるとは限らないものを、ブート時に全て RAM に配置するのは無駄があるからです。

ローダーによる、ROM 領域の実行形式ファイルに対する再配置処理は、
 %_WINCEROOT%/private/winceos/COREOS/nk/kernel/loader.c
で実装されている PageInOnePage() の中で行われるように見えます。もし興味のある方は、ご自分で追ってみて下さい。WEC/WinCE の実行形式ファイルフォーマットについては、PE (Portable Executable) のフォーマットを解説したページが参考になります:

 EXEファイルの内部構造(PEヘッダ)
 http://codezine.jp/article/detail/412?p=2

 Peering Inside the PE: A Tour of the Win32 Portable Executable File Format
 http://msdn.microsoft.com/en-us/library/ms809762.aspx

Add comment 2012/12/21 koga

デバイスエミュレータで WEC 7 を動かす

最初にことわっておきますが、今回の内容は、WinCE 6.0 (Windows Embedded CE 6.0) と WEC 7 (Windows Embedded Compact 7 ) を両方お使いの方が主な対象です。WinCE 6.0 しか使っていらっしゃらない方には、特に関係ない内容ですし、また、WEC 7 だけをお使いの方にも、必要性は低い内容だと思います。もし、あなたが、WinCE 6.0 の ARM ベースのデバイスエミュレータをご利用になっていて、同じものを WEC 7 でも使えないだろうか?と思ったことがあるのなら、今回の内容は、あなたのためのものです。お急ぎなら、途中を飛ばして、今回のエントリの最後の方にある「DeviceEmulator BSP を WEC 7 へ移植する」を読んでみて下さい。

■WinCE 6.0 と Windows Mobile のデバイスエミュレータ
Visal Studio 2008 までは、Pro 以上の版に含まれる Smart Device Development 機能の一部として、ARM ベースのデバイスエミュレータが付属しています。このデバイスエミュレータは、単体でも提供されており、最新版の 3.0(VS 2008 付属のものと同じ版)を、以下のページからダウンロードできます:

 Microsoft Device Emulator 3.0
 http://www.microsoft.com/ja-jp/download/details.aspx?id=5352

デバイスエミュレータがエミュレートしているのは、Samsung 製の、S3C2410 という ARM9 コアのプロセッサを搭載したリファレンスボードです。WinCE 6.0 の Platform Builder をお使いの方ならご存知の通り、WinCE 6.0 には、デバイスエミュレータの BSP が付属しています。そのため、WinCE 6.0 を動かす実機がなくても、デバイスエミュレータの BSP を使って OS イメージをビルドすれば、その OS イメージをデバイスエミュレータで動かし、実機に依存しない部分の開発が可能です。

WinCE 5.0 までは、デバイスエミュレータには Virtual PC が使われていました。つまり、ターゲットプロセッサは x86 でした。WinCE 6.0 では、それが ARM プロセッサになった、というわけです。しかし、後述するように、WEC 7 では、再び Virtual PC がデバイスエミュレータとして採用されました。

以下に、WinCE 6.0 のデバイスエミュレータ、つまり、S3C2410 リファレンスボードのエミュレータの特徴をまとめます:

  • Windows Mobile のエミュレータとしても利用されている。
  • 設定ファイルを作成することにより、スキン画像と入力ボタン/キーパッドを設定できる。
  • ARM プロセッサをエミュレートしている。

どれも、Virtual PC ベースのエミュレータには無いものです。専用の BSP を使って作成した OS イメージをロードして動かせるのは、どちらのエミュレータも同じです。一方、エミュレータから利用できるホスト PC の周辺機器機能は、Virtual PC ベースのエミュレータの方が豊富です。WinCE 6.0 のデバイスエミュレータの方は、実質、Ethernet とシリアルポートのみです。

WinCE 6.0 のデバイスエミュレータでは、周辺機器のエミュレート機能として、ホスト PC のマウス入力に対する、タッチパネル入力のエミュレートや、ホスト PC のディレクトリをメモリカードとしてマウント/エミュレートする、というものがあります。Virtual PC に比べると、利用できるホスト PC の周辺機器機能は貧弱ですが、入力ボタン/キーパッドのエミュレートが可能なことと、ARM プロセッサをエミュレートしているのは、便利な場合があります。

さて、WinCE 6.0 のデバイスエミュレータがエミュレート(シミュレート)しているのは、上述したように、Samsung の ARM9 コアのプロセッサ(S3C2410)のリファレンスボードです。S3C2410 の ARM9 コアは、ARM920T、つまり、命令セットが v4T である ARM9TDMI ファミリです。WinCE 6.0 までの ARM コンパイラは、ARMv4 の命令セットにしか対応していませんでしたので、それで十分でした。しかし、WEC 7 の ARM コンパイラでは、ARMv5/v6/v7 のサポートが追加された代わりに、ARMv4 には対応していません。このことは、このデバイスエミュレータで WEC 7 を動かそうとする場合に、問題となるように思われます。

しかし、心配はいりません。VS 2008 の Smart Device Development 機能に付属する、最新版(3.0)のデバイスエミュレータ(※上述したページから、単体でダウンロードできます)では、ARMv5 の命令セットにも対応しています。デバイスエミュレータのコマンドラインオプションのリファレンスを見ると、/cpucore オプションで ARMv5 を指定することにより、ARMv5 命令セットが有効になると説明されています(デフォルトは、ARMv4):

 デバイス エミュレータのコマンド ライン リファレンス
 http://msdn.microsoft.com/ja-jp/library/aa188169(v=VS.90).aspx

ちなみに、VS 2005 に付属していた版(1.0)、つまり、WinCE 6.0 用としても使われるデバイスエミュレータは、ソースコードを入手可能です:

 Shared Source Microsoft Device Emulator 1.0 Release
 http://www.microsoft.com/en-us/download/details.aspx?id=10865

デバイスエミュレータ 1.0 のソースコードは、上のページに書かれているように、Shared Source ライセンスで提供されていますので、興味がある方は、ご覧になってみて下さい。QEMU など、他のエミュレータと同様なところはあると思いますので、両者を比較しながら読んでみるのも、面白いでしょう。

■WEC 7 での Virtual PC 対応
WEC 7 では、WinCE 6.0 にあった ARM ベースのデバイスエミュレータの BSP は付属せず、代わりに、Virtual PC 用の BSP が付属しています。この BSP に対応した、Virtual PC の仮想マシンイメージ(cevm.vmc)も付属しており、BSP を使って OS イメージを作成すれば、すぐに動かせるようになっています。この仮想マシンイメージを有効にすると、Windows 7 で Windows XP Mode をお使いの場合、Windows Virtual PC の仮想マシンとして、Windows XP Mode に加えて cevm が表示されるようになる筈です。cevm には、WEC 7 のブートローダが組み込まれていますので、起動してブートローダのメニュー画面を表示させ、ネットワーク設定を行えば、Platform Builder を使って OS イメージをダウンロードできます。

WEC 7 での Virtual PC ベースのデバイスエミュレータを使う手順については、リファレンスの次のページをご覧下さい。

 Develop with Virtual CEPC (Windows Embedded Compact 7)
 http://msdn.microsoft.com/en-us/library/jj200433

お使いの PC に WEC 7 をインストール済みの方であれば、
 C:/Program Files/Windows Embedded Compact 7/Documentation/
ディレクトリの中に入っている
 Getting Started with Virtual CEPC.pdf
というドキュメントも、参考になるでしょう。

Virtual PC ベースのデバイスエミュレータを、WinCE 6.0 のデバイスエミュレータと比べると、上述したように、エミュレータから利用できるホスト PC の周辺機器機能は、Virtual PC ベース(前者)の方が豊富です。また、原理上、前者の方が高速でもあります。一見すると、Virtual PC ベースのエミュレータの方が良いことずくめで、WinCE 6.0 のデバイスエミュレータ(ARM ベースのエミュレータ)を使う理由は、ありません。

しかし、ARM ベースのエミュレータの方が便利な場合も、あります。たとえば、次の場合です。

  • 実機のプロセッサが ARM の場合。
  • 実機にハードウェアボタンを搭載予定であり、そのモック動作を、エミュレータで行いたい場合。

ハードウェアボタンのシミュレートについては、上述したように、設定ファイル(スキンファイル)へ記述することにより任意に設定できます。ARM ベースのデバイスエミュレータの設定ファイルの記述仕様は、リファレンスの次のページで説明されています:

 デバイス エミュレータのスキンの XML スキーマ リファレンス
 http://msdn.microsoft.com/ja-jp/library/aa188144(v=vs.90)

 デバイス エミュレータ構成の XML スキーマ リファレンス
 http://msdn.microsoft.com/ja-jp/library/bb531167(v=vs.90)


2012-09-05 追記:
デバイスエミュレータのスキン設定ファイルにおいて、ボタン/キーパッドに割り当てるキーコードについて、上記のリファレンスでは、特に説明されていません。割り当て可能なキーコードの説明は、WinCE 5.0 のリファレンスにある、次のページをご覧下さい:

 http://msdn.microsoft.com/en-us/library/ms905130.aspx

実機のプロセッサが ARM の場合ですが、Virtual PC ベースのデバイスエミュレータ、つまり、x86 のエミュレータで開発すると、エミュレータでの動作中には起きなかった例外送出が、実機の ARM プロセッサでは発生する、という可能性があります。これが起きる典型的なケースは、TCP/IP 通信や USB 通信でデータ転送を行う際に、転送プロトコルのパケットのペイロードを、構造体に cast して、構造体のメンバにアクセスする場合です。この時、構造体のメンバが 2Byte 以上のサイズの整数値型([unsigned] short, [unsigned] long など)であり、かつ、構造体に cast したメモリ領域のアドレスのアラインメントが、それらの整数値型のサイズに対して揃っていなければ、ARM プロセッサの場合は例外が発生します。しかし、x86 の場合には、アラインメントが揃っていなくても、例外が発生しません。

従って、Virtual PC ベースのデバイスエミュレータでは起きなかった、アプリケーションや汎用デバイスドライバ(USB のクラスドライバなど)の不具合が、ARM プロセッサの実機へ移植すると発生する、という可能性があります。同様のことは、x86 PC でしかテストされていないソフトウェアを、ARM や MIPS などのプロセッサへ移植する場合にも問題となります。これら、ワードアラインメントに関わる不具合を、デバイスエミュレータでの開発時に検出することができるのは、WinCE 6.0 において、ARM ベースのエミュレータが導入された利点の一つだったと思います。

ちなみに、WEC 7 の ARM コンパイラでは、WinCE 6.0 までの ARM コンパイラに比べて Compiler Intrinsics が強化されているため、WinCE 6.0 では発生しなかった例外送出が、WEC 7 では発生する、というケースもあります。つまり、WinCE 6.0 では、バイト単位のアクセス実行にしかコンパイルされなかったソースコードが、ワード単位でのアクセス実行にコンパイルされる場合がある、ということです。同じソースコードから生成されるバイナリの実行効率が上がるようにコンパイラが改善された一方で、その「副作用」として、不用意に書かれたソースコードによって新たに例外送出が発生する場合もある、というわけですね。

ところで、WEC 7 のデバイスエミュレータが、WinCE 6.0 での ARM ベースから Virtual PC ベースに戻ったのは、使用できるホスト PC の周辺機器や実行速度の違いが、主な要因だと思われます。それに加えて、WEC 7 からは PCMCIA がサポートされなくなったことも、要因の一つではないかと思います。WinCE 6.0 のデバイスエミュレータは、NE2000 互換の PCMCIA カードをエミュレートしているのですが、WEC 7 では PCMCIA がサポートされないため、その PCMCIA カード(NE2000 互換のネットワークカード)を利用できないのです。

このことは、Device Emulator の BSP を WinCE 6.0 から WEC 7 へ移植する際に、問題となります。さて、いよいよ、今回のエントリの本題です。

■Device Emulator BSP を WEC 7 へ移植する
これまでの説明で、WinCE 6.0 と WEC 7 では、デバイスエミュレータが異なっており、一方は、ARM プロセッサベース(Samsung S3C2410 のリファレンスボード)、他方は、Virtual PC ベースだということを述べました。また、WinCE 6.0 には付属していた、ARM プロセッサベースのデバイスエミュレータの BSP が、WEC 7 には付属していないことも述べました。そのため、Visual Studio 2008 の Smart Device Development 機能に含まれる、ARM ベースのデバイスエミュレータ(3.0; ARMv5 対応)で WEC 7 を動かそうとしても、OS イメージを作成する手段がないということについても、お分かり頂けたのではないかと思います。

では、Visual Studio 2008 付属のデバイスエミュレータで WEC 7 を動かすには、どうすればよいのでしょうか?答えは簡単です。WinCE 6.0 に付属するデバイスエミュレータの BSP を、WEC 7 に移植して、移植した BSP を使って OS イメージを作成すればよいのです。必要な作業は、以下の通りです:

  1. WinCE 6.0 の %_WINCEROOT%/platform/DEVICEEMULATOR/ ディレクトリを、WEC 7 の %_WINCEROOT%/platform/ ディレクトリへコピーする。
  2. WinCE 6.0 の %_WINCEROOT%/PUBLIC/COMMON/OAK/DRIVERS/PCCARD/ ディレクトリを、WEC 7 にコピーした DEVICEEMULATOR/ ディレクトリの中の SRC/DRIVERS/ ディレクトリへコピーする(※その際、DRIVERS/ の下に PCMCIA/ というディレクトリを作り、その中に PCCARD/ ディレクトリの内容をコピーするのが良いでしょう)。
  3. コピーした DEVICEEMULATOR/ ディレクトリと PCCARD/ ディレクトリの内容、つまり、WinCE 6.0 のデバイスエミュレータの BSP と PCMCIA スタックに対して、WEC 7 への移植(必要な改訂)を施す。
  4. WinCE 6.0 の %_WINCEROOT%/PUBLIC/COMMON/DDK/INC/ ディレクトリから、PCMCIA 関連のヘッダファイル(tuple.h, cardsv2.h, socksv2.h, cardserv.h)を、WEC 7 にコピーした DEVICEEMULATOR/ ディレクトリの中の SRC/INC/ へコピーする。
  5. WinCE 6.0 の %_WINCEROOT%/PUBLIC/COMMON/OAK/files/common.reg ファイルから、PCMCIA の NE2000 互換カードに関するレジストリ項目を、WEC 7 にコピーしたデバイスエミュレータの platform.reg へコピーする。

上で述べたように、デバイスエミュレータがエミュレートするネットワークインタフェースは、NE2000 互換の PCMCIA カードなのですが、WEC 7 では PCMCIA がサポートされず、PCMCIA スタックが付属していません。そのため、WinCE から PCMCIA スタックを移植する必要があります。PCMCIA スタックを移植するにあたっては、%_WINCEROOT%/PUBLIC/COMMON/OAK/DRIVERS/ ディレクトリへコピーせず、デバイスエミュレータ専用のコンポーネントとして、デバイスエミュレータの BSP ディレクトリへコピーするのが良いでしょう。そうすれば、WEC 7 のソースツリー全体を「汚さずに」済みます。なお、デバイスエミュレータの BSP では、DRIVERS/ の下に PCCARD/ というディレクトリがあります。そのため、WinCE 6.0 の PCMCIA スタックのディレクトリ(PCCARD/)を、そのまま DRIVERS/ の下にコピーするのではなく、DRIVERS/ の下に PCMCIA/ というディレクトリを作って、その下に、WinCE 6.0 の PCARD/ ディレクトリの内容をコピーするのが良いでしょう。

デバイスエミュレータの BSP の移植作業は、他の BSP を WinCE 6.0 から WEC 7 へ移植する場合と同様です。中間ディレクトリのパスが WEC 7 で変わったことに伴う、各コンポーネントの sources ファイルの修正や、WinCE 6.0 時点で古くなっていた型定義が WEC 7 では削られてしまったことへの対応などが必要です。たとえば、デバイスエミュレータのディスプレイドライバでは、GPERotate や GPESurfRotate という型を参照していますが、これらは、WEC 7 のヘッダファイルでは削られてしまっているため、WinCE 6.0 のヘッダファイルからドライバのソースファイルへコピーする必要があります。

また、PCMCIA スタックの移植では、%_WINCEROOT%/PUBLIC/COMMON/DDK/INC/ にある devload.h や cebuscfg.h において、PCMCIA 関連の定数の定義が削られてしまっていることへの対応(WinCE 6.0 の同じ名前のファイルから、PCMCIA 関連の定数の定義を PCMCIA スタックのソースファイルへコピーする)も必要です。

上記のように、それなりの移植の手間は必要ですが、WEC 7 のコアのソース(PRIVATE/ および PUBLIC/ ディレクトリ配下)に手を加えずに、BSP および BSP 固有のコンポーネントとして追加するだけで済みます。これは、Windows Embedded Compact が、明確にモジュール化されていることの証だと思います。

移植後の BSP の品質を確保するためには、より多くの作業が必要ですが、「とりあえず動かす」レベルであれば、それほど大変な作業では、ありません。今回は、デバイスエミュレータで WEC 7 が起動して、ホスト PC でのマウス操作でタッチパネル入力をシミュレートでき、さらに、デバイスエミュレータのネットワークアダプタを使えるようになるまでの作業で分かったことを書いています。

最後に、デバイスエミュレータのネットワークアダプタを使えるようにするための移植作業に関して、注意点を二つ書きます。移植作業の細かい説明は省きましたが、次の二点は、見落としがちな要点なので、自分でも移植してみようと思った方のために書いておきます。

  • PCMCIA の NE2000 互換カードに関するレジストリ項目を、WinCE 6.0 の common.reg からコピーする際、BusType を 0 に変更する。
  • PCMCIA ホストコントローラのドライバ(pcc_smdk2410.dll)のロード順序を、後にする。

一番目の注意点ですが、[HKEY_LOCAL_MACHINE\Comm\NE20001\Parms] キーの下にある BusType キー値のことです。WinCE 6.0 の common.reg では、8、つまり PCMCIA の bus type 値が設定されています。しかし、WEC 7 では、PCMCIA がサポートされないため、この値を指定すると、「未知/未定義の bus type」というエラーになってしまい、NE2000 ミニポートドライバの初期化に失敗します。このキー値は、NE2000 ミニポートドライバがロードされた際に、初期化動作の一つとして NdisMRegisterIoPortRange() を呼び出す際に参照されるのですが(※NdisMSetAttributes() を使って、NDIS_HANDLE に対して bus type を設定したのち、NdisMRegisterIoPortRange() を呼び出します)、その呼び出しがエラーとなってしまいます。

このエラーは、bus type を 0 (Internal) に変更することで回避できます。今回、デバイスエミュレータで WEC 7 を動かすために上記の移植を行った際、当初は、NE2000 ミニポートドライバを自前実装し、NdisMRegisterIoPortRange() を呼び出さない仕組みで実装し直す必要があるのではないかと考えました。しかし、分析してみたところ、bus type の設定変更だけで済むことが分かり、移植の手間を減らすことができました。

最後に、二番目の注意点です。オリジナルの実装では、[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\PCC_SMDK2410] キーの下にある Order キー値が 1 になっています。しかし、その設定では、デバイスエミュレータの PCMCIA ホストコントローラがネットワークカードを認識して、NE2000 ミニポートドライバをロードする際に、エラーになってしまうのです。その原因は、解明できていないのですが、pcc_smdk2410.dll がロードされる順序を遅らせることにより、エラーを回避できるようです。設定する値は、dword:10 などにしてみて下さい。

二番目の点については、今後、もし調査の時間がとれたら、さらに追ってみたいと思います。もし、正確な原因や、より根本的な対処策をご存じの方がいらしたら、ぜひ教えて下さい。

1 comment 2012/07/04 koga

Lilas-am440-6/7 の Armadillo-420 対応版

Armadillo-400 シリーズ用の弊社の WEC 7/WinCE 6.0 BSP (Lilas-am440-6/7) の、Armadillo-420 に対応した最新版が、アットマークテクノ社のサイトで公開されました:

 「Armadillo-420 ・ Armadillo-WLAN(AWL13) がWindows Embedded Compact 7に対応
  ~Armadillo-400シリーズすべてで BSP「Lilas」が利用可能に~」
 http://www.atmark-techno.com/news/notices/201205_lilas

両者のバージョンは、WEC 7 用 (Lilas-am440-7) が 1.0.4 で、WinCE 6.0 用 (Lilas-am440-6) が 1.1.2 です。三機種ある Armadillo-400 シリーズの中で、今回対応を追加した Armadillo-420 は、メモリの搭載容量が他の二つの半分です(RAM: 64MB, NOR Flash: 16MB)。このため、カーネルのメモリマップも異なります。Armadillo-420 で動く OS イメージを作成するためには、OS Design で、BSP のカタログ項目の “A420 CPU Board” を選択して頂く必要があります。この点、ご留意下さい。

また、Armadillo-420 には LCD インタフェースがないため、ヘッドレス(ディスプレイ無し)で使うことになりますが、ヘッドレスのデバイスで WEC 7/WinCE 6.0 を動かす場合のヒントとして、しばらく前に書いた、以下のエントリを参考にして頂ければ幸いです:

 WinCE デバイスのリモート操作~その1(1/2)
 http://www.stprec.co.jp/ceblog/2011/12/29/

 WinCE デバイスのリモート操作~その2(2/2)
 http://www.stprec.co.jp/ceblog/2012/01/04/

 WinCE/WEC でシリアルコンソール
 http://www.stprec.co.jp/ceblog/2012/03/15/

Armadillo-420 対応の追加によって、より多くの皆さんに、Armadillo-400 シリーズで WEC 7/WinCE 6.0 を評価してみて頂けることを期待しています。
Let’s Enjoy it! :-)

Add comment 2012/06/08 koga

OS の部分アップデート方策~その2(2/2)

前回のエントリでは、ファイルシステム用の永続記憶域を使った部分アップデートの方法を紹介しました。今回は、ROM イメージを複数に分割する方式の部分アップデートについて紹介します。

最初に断っておきますが、この方法は、WinCE/WEC の OS Design のコンフィグレーションでは対応できません。カーネル移植レイヤ(OAL)とブートローダのカスタマイズが必要です。

■複数の ROM イメージ(Multiple Region)
ROM イメージを複数に分割する仕組みは、もともと、WinCE/WEC のカーネルと Platform Builder 内蔵の ROM イメージ生成コマンド(makeimg.exe, romimage.exe)に組み込まれています。ただし、それを有効にするためには、OAL とブートローダが、複数に分割された ROM イメージに対応する必要があるのです。この対応は、必須ではないため、全ての BSP が対応しているわけではありません。対応していない BSP の場合は、OAL とブートローダの実装にカスタマイズを加えなければいけない、というわけです。

複数に分割された ROM イメージに対応した OAL とブートローダは、それぞれ、次の機能を持ちます。

・OAL
 ブートローダが Flash メモリなどから RAM にロードした(XIP の場合は、NOR Flash 上の)、複数に分割された ROM イメージのリストを、カーネルに伝える。この通知は、OAL の初期化時に(つまり、OEMInit() の中で)OEMRomChain を構築することで行う。

・ブートローダ
 Flash メモリなどから、複数に分割された ROM イメージを RAM へロードする(XIP の場合は、ロードしない)。また、ROM イメージをホスト(開発用 PC)から転送された時は、分割された ROM イメージを各々認識し、それらを、Flash メモリなどに書き込む。

ここで、「分割された ROM イメージ」というのは、ROM イメージファイルを単純に分割したものでは、ありません。「分割された ROM イメージ」の一つ一つは、それぞれが ROM イメージのヘッダを持っていて、個別の ROM イメージとなっています。これらを、region と呼びます。どのように region を構成するのかは、config.bib で設定します。makeimg.exe は、config.bib で複数の region が設定されていれば、それに従って、region ごとに ROM イメージファイル(.bin)を生成するのです。

複数の region を WinCE/WEC カーネルに認識させるために、OAL は、初期化時に region のリストを構築します。このリストが、上で述べた OEMRomChain です。OEMRomChain は、ROMChain_t 構造体のリストで、カーネルのスタートアップルーチンによって、スタートアップルーチンが所属する ROM イメージを指すように初期化されます。つまり、デフォルトでは、region が一つだけとなります。その後、スタートアップルーチンが OAL の OEMInit() を呼び出し、OEMInit() が、OEMRomChain に複数の region を登録する(OEMRomChain を初期化し直す)というわけです。

より正確には、OEMInit() が OEMRomChain をどう構築するかは、OAL における OEMInit() の実装次第です。カーネルのスタートアップルーチンは、OEMInit() を呼び出した後、OEMRomChain の内容をチェックして、自身が所属する region が登録されていない場合には、自身が所属する region を OEMRomChain の末尾に連結します。

ROMChain_t 構造体の説明は、WinCE 6.0/WEC 7 のリファレンスの、次のページをご覧ください:

 OEMRomChain
 http://msdn.microsoft.com/en-US/library/ee479246(v=WinEmbedded.60).aspx
 http://msdn.microsoft.com/en-US/library/ee479246.aspx

■Region ごとの部分アップデート
複数の ROM イメージ(Multiple Region)を使うと、部分アップデートする対象(アプリケーションなど)を収録した ROM イメージと、OS 本体を収録した ROM イメージを分けることができます。そして、ブートローダが対応していれば、アップデートしたい ROM イメージ(region)だけをブートローダへ転送し、書き換えることができます。これが、ROM イメージファイルを複数に分割する方式の部分アップデートです。

この方法の利点は、次の通りです:

・永続記憶域上にルートファイルシステムを構築する必要がないため、より耐障害性が高い。
・OS イメージ中の全てのファイルを、アップデートすることが可能。

ここで、「OS イメージ中の全てのファイルをアップデート可能」というのは、ROM イメージの Region 間で shadowing が作用することによるものです。つまり、二つの region に同じファイルが収録されている場合、上述した OEMRomChain のリストにおいて、より先頭に近い方の region 内のファイルが優先され、その後ろにある region 内のファイルが隠されるのです。

OEMRomChain の region 間での shadowin は、前回紹介した、ルートファイルシステム上のファイルによる ROM イメージ内のファイルの shadowing の仕組みと、本質的には同じものです。どちらも、FSD Manager と、カーネルのローダーによって実現されています。前回、カーネルのローダーが、OpenExecutable() から OpenFileFromFilesys() と OpenFileFromROM() を呼び出し、先に呼び出す OpenFileFromFilesys() で executable ファイル(.exe または .dll)が見つかれば、そちらを優先する、と説明しました。実は、OEMRomChain に複数の region が登録されている場合、OpenFileFromROM() は、OEMRomChain のリスト(つまり、region のリスト)を先頭から順に辿り、最初に見つかったファイルをオープンします。従って、よりリストの先頭に近い region 内のファイルが優先される、というわけです。

たとえば、OS 本体を収録した region(※この region に、カーネルのスタートアップルーチンが含まれます)と部分アップデートする対象を収録した region の二つがある場合について考えましょう。OEMRomChain のリストを構築する際、OS 本体を収録した region がリストの末尾要素となり、部分アップデートする対象を収録した region がリストの先頭要素となるようにすれば、両者に同じファイルが存在する場合、部分アップデート対象を収録した region 内のファイルが優先されます。このため、OS 本体を収録した region に収録したデバイスドライバなどをアップデートする際に、その region を書き換える代わりに、部分アップデート用の region の ROM イメージにアップデートしたいファイルを追加して、部分アップデート用の region だけを書き換える、という方策が可能です。

OS 本体を収録した region のサイズが、部分アップデートする対象を収録した region のサイズよりも、ずっと大きければ、小さい方の region(つまり、部分アップデートする対象を収録した region)の方を書き換える方が、より小さなコストで済みます。

部分アップデート用に、複数の ROM イメージ、つまり region の分割を行う方策としては、region を二つに分ける以外に、次のように、四つの region を設定する方策も考えられます(どちらかといえば、極端な例ですが):

 - NK: OS 本体を収録
 - EXT: デバイス固有(BSP 固有)のデバイスドライバやアプリケーションなどを収録
 - UNK: NK region 内のファイルの更新版を収録する(※出荷時は空)
 - UEXT: EXT region 内のファイルの更新版を収録する(※出荷時は空)

このように region を設定すれば、OS 本体(WinCE/WEC のカーネル及び、標準のデバイスドライバやアプリケーション、API の DLL など)と、デバイス(あなたが開発する製品)固有のドライバやアプリケーションを、それぞれ独立してアップデートできます。OS 本体用の ROM イメージと、デバイス固有の ROM イメージは、全体的なアップデートが必要となれば、各々の region(NK, EXT)を書き換えればよいですし、特定のファイルだけをアップデートしたい場合は、各々の部分アップデート用の region(UNK, UEXT)を書き換えればよい、というわけです。

WinCE/WEC は、月例アップデートが公開されます。もし、出荷後に重要な修正がリリースされた場合は、その修正を、出荷済みの製品の OS に反映しなければならないこともあるでしょう。そのような場合に、上の例のような region 分割を設定しておくと、OS 本体の ROM イメージを全て書き換えなくても、修正が加わった .dll や .exe を収録した、部分アップデートの ROM イメージを作り(そのイメージサイズは、OS 本体の ROM イメージに比べて、非常に小さいでしょう)、対応する region を書き換えることでアップデートできます。なお、部分アップデートを繰り返す場合、部分アップデートの ROM イメージには、最新のアップデート対象の .exe や .dll に加えて、以前の部分アップデートの際に収録した .exe や .dll も収録しなければならないことに注意して下さい。そうしなければ、部分アップデートした際に、以前の部分アップデートの内容が消えてしまうからです。

ところで、もし、部分アップデートを繰り返した結果、アップデート対象のファイルが累積して、部分アップデートの ROM イメージが大きくなった場合は、古い部分アップデート内容を破棄して、全体をアップデートする必要が生じる場合も、あると思います。つまり、上の例で言うと、UNK または UEXT region に収録するファイルが増えた場合は、それらの region を空にして、代わりに、NK または EXT region を作り直し、そちらを書き換える必要が生じるでしょう。もちろん、どのように region を分割し、部分アップデートを行うかということは、ケースバイケースですよね。

■Region 分割の手順
ここまでで、ROM イメージを複数に分割する方式の部分アップデートについて、その仕組みと、部分アップデートの方策について説明しました。残りは、実際の手順です。

複数の region に分割した ROM イメージを作成する手順の例は、WinCE 6.0 のリファレンスに載っています。現時点では、WEC 7 のリファレンスには対応するページが見当たりません。

 How to Create a Run-Time Image with Multiple XIP Regions
 http://msdn.microsoft.com/en-US/library/ee482739.aspx

このページでは、CEPC の場合の手順が載っていますが、他のボード(デバイス)の場合も、基本的には同じです。必要な作業を簡単にまとめると、次の通りです:

・config.bib ファイルの MEMORY セクションの記述を書き換える。その際、環境変数 IMGMULTIBIN に対する条件分岐を書き加えて、複数 region 対応するかどうかを、環境変数(IMGMULTIBIN)の設定有無で切り換えできるようにすると便利。
 複数 region 対応用の MEMORY セクションには、NK の他に定義する region を、RAMIMAGE 型の memory region として設定する。また、それらの region の連結内容を記録するための CHAIN region を、RESERVED の memory region として設定する。
 http://msdn.microsoft.com/en-US/library/ee482800.aspx (Modifying the Binary Image Builder Files)

・platform.bib ファイルの MODULES または FILES セクションに、NK 以外の region に収録するファイルを記述する。
 BSP 固有のデバイスドライバやアプリケーションなどを、NK 以外の region に収録する場合は、環境変数 IMGMULTIBIN に対する条件分岐を書き加えて、複数 region 対応の場合のみ、当該 region(「Region ごとの部分アップデート」で示した例で言えば、EXT region)を指定する。
 http://msdn.microsoft.com/en-US/library/ee482800.aspx (Modifying the Binary Image Builder Files)

・OS イメージをビルドする。

・ビルドしてできた .bin ファイルのうち、chain.bib を、Platform Builder から WinCE/WEC デバイスへ転送するファイルとして設定する。
 デバイスへ転送するファイルは、Platform Builder のプロパティダイアログで設定できる。Platform Builder の「プロジェクト」 > 「プロパティ」メニューで、プロジェクトのプロパティダイアログを開き、左側にあるツリービューから、「構成プロパティ」 > 「全般」を選択して表示される画面の、「デバッガ用のターゲットファイル名」というラベルのドロップダウンリストで、転送したい .bin ファイルを選択する。
 http://msdn.microsoft.com/en-US/library/ee482967.aspx (Building the Run-Time Image and Opening a Workspace with Multiple XIP Regions)

以上が、複数の region に分割した ROM イメージを作成して、WinCE/WEC デバイスのブートローダへ転送するために、Platform Builder で行う手順です。なお、上の最後のページ(“Building the Run-Time Image and Opening a Workspace with Multiple XIP Regions”)の説明では、複数に分割した ROM イメージを全て転送する場合、xip.bin を指定すると書かれていますが、これは、うまくいきません。xip.bin ではなく、CAHIN region の内容を収録した chain.bin を指定して下さい。

xip.bin は、全ての region の内容を一つに連結したファイルなのですが、WinCE 6.0 付属のブートローダのライブラリ(blcommon)の実装は、この構造に対応していないように思われます。xip.bin が、残りの .bin ファイルの内容を単純に連結した内容になっており、その結果、blcommon ライブラリでは、先頭の region しか認識されないのです。blcommon ライブラリのソースを読むと、ROM イメージのヘッダに記すマジックナンバーの値として、xip.bin 専用の値が定義されていたような形跡があるのですが、その値(X000FF)が設定されている場合、blcommon ライブラリは、エラーメッセージを出力して停止するように実装されています。

■OAL とブートローダのカスタマイズ
さて、今回の最初の方で、複数に分割した ROM イメージに対応するには、OAL とブートローダのカスタマイズが必要だと述べました。複数に分割した ROM イメージに対応している OAL とブートローダが、それぞれ、どのような働きをするのかについても、ごく簡単に説明しました。OAL とブートローダに対するカスタマイズの内容について、最後に、もう少しだけ補足します。

・OAL のカスタマイズ
 OEMInit() 中で、OEMRomChain を構築しなければならないことは、既に述べました。このことは、次のページに書かれています。

 Booting an Image with Multiple XIP Regions
 http://msdn.microsoft.com/en-US/library/ee483010.aspx

 しかし、このページの説明だけでは、あっさりし過ぎていて、具体的にどうすればよいのか、分かりません。OEMRomChain を構築する例は、x86 用の BSP 共通ソースをご覧になるのが良いでしょう。このソースは、次の場所にあります:

 %_WINCEROOT%/PLATFORM/COMMON/src/x86/COMMON/startup/oeminit.c

 oeminit.c の中にある、x86InitRomChain() が、OEMRomChain を構築する関数です。なお、x86InitRomChain() では、OEMRomChain という名前の変数の値を設定していますが、実は、OEMRomChain はマクロで、その実体は、OEMGLOBAL 構造体のメンバ pROMChain なのです。

 OEMGLOBAL
 http://msdn.microsoft.com/en-us/library/ee478176(v=WinEmbedded.60).aspx  
 http://msdn.microsoft.com/en-us/library/ee478176.aspx

 OEMRomChain を定義したヘッダファイルは、次の場所にあります:

 %_WINCEROOT%/PUBLIC/COMMON/OAK/INC/bcoemglobal.h

 OEMRomChain を構築する処理では、region の連結内容を記録した CHAIN region と、各 regionの ROM イメージヘッダ内容をアクセスする必要がありますが、それらは、以下のヘッダファイルで定義されています:

 %_WINCEROOT%/PUBLIC/COMMON/OAK/INC/romldr.h
 %_WINCEROOT%/PUBLIC/COMMON/OAK/INC/pehdr.h

 CHAIN region の構造については、次のページでも説明されています:

 XIP Chain
 http://msdn.microsoft.com/en-us/library/ee482877.aspx

・ブートローダのカスタマイズ
 ブートローダのカスタマイズについては、次のページをご覧ください。

 Adding Support for Multiple-BIN Image Notification
 http://msdn.microsoft.com/en-US/library/ee479207.aspx

 OEMMultiBINNotify
 http://msdn.microsoft.com/en-US/library/ee478943(v=WinEmbedded.60).aspx
 http://msdn.microsoft.com/en-US/library/ee478943.aspx

 OEMMultiBINNotify() を実装すると共に、CHAIN region の内容および他の region をホストから受け取って Flash メモリなどに書き込む動作、および、region が一つだけ転送された場合(部分アップデートの場合)に、それを正しい場所へ書き込む機能を実装しなければいけません。さらに、ブート動作では、Flash メモリなどに書き込んだ、CHAIN region と他の全ての region を RAM へロードする処理(XIP の場合は、ロードしない)を実装する必要があります。

Add comment 2012/01/13 koga

CESYSGEN 条件文での ‘Component’ 名

WinCE のビルドシステムでは、.bib ファイルや .reg ファイルなどで、@CESYSGEN IF … @CESYSGEN ENDIF という構文を使って、条件指定を記述できます。たとえば、WinCE のカタログ項目で「SD バス ドライバー」が選択されたら、BSP 固有の SD ホストコントローラのドライバが OS Design へ組み込まれるようにするには、BSP の platform.bib において、次のように記述します:

; @CESYSGEN IF CE_MODULES_SDBUS2
    <SD ホストドライバ名>.dll       $(_FLATRELEASEDIR)\<SD ホストドライバ名>.dll            NK  SHK
; @CESYSGEN ENDIF CE_MODULES_SDBUS2


ここで、条件指定に使っている ‘CE_MODULES_SDBUS2′ という変数名は、どうやって決まるのでしょうか?カタログ項目「SD バス ドライバー」の Sysgen 変数(システム生成変数)は ‘SYSGEN_SDBUS’ ですから、Sysgen 変数の名前を使っているわけでは、ありません。今回は、この条件指定に使う変数名について調べてみて分かったことを記します。

まず、WinCE のリファレンスの CESYSG 条件文に対する説明は、次のページです:

 http://msdn.microsoft.com/en-US/library/ee478869(v=WinEmbedded.60).aspx (Preprocessing Using Cesysgen Conditionals)

このページでは、CESYSGEN 条件文で指定する変数名(’Component’ 名)について、次のように説明しています。

・対応するものが module の場合は、<OS tree name>_MODULES_<module_name>。

・対応するものが component の場合は、<module_name>_<component_name>。

ただし、この説明では、’module’ と ‘component’ について具体的な説明がなく、また、<OS tree name>, <module_name>, <component_name> について、説明されていません。これらについては、%_WINCEROOT%/PUBLIC/ 配下の .bat ファイルに記述された、ビルド用の変数設定を見るのが確実だと思います。

たとえば、カタログ項目「SD バス ドライバー」の例でいえば、プロパティの「モジュール」が sdbus2.dll ですが、’sdbus2′ をキーにして %_WINCEROOT%/PUBLIC/ 配下の .bat ファイルを検索すると、%_WINCEROOT%/PUBLIC/CEBASE/OAK/MISC/winceos.bat がヒットします。ヒットするのは、以下の行です:

        set CE_MODULES=%CE_MODULES% sdbus2


ここで、CE_MODULES というビルド変数は、%_WINCEROOT%/PUBLIC/common/CESYSGEN/makefile で参照されており、ターゲット ‘preproc’ の依存ファイルリストの一部として記述されています。つまり、ビルドの preproc ステージで、CE_MODULES にセットされたターゲット群が、依存ファイルとしてビルドされるというわけです。

さて、上で述べた、CESYSGEN 条件文で指定する Component 名の ‘module’/'component’ は、このビルド変数の名前の末尾が ‘_MODULES’ と ‘_COMPONENTS’ のどちらなのか、に対応するようです。sdbus2 の場合は、CE_MODULES というビルド変数の値の一部としてセットされますので、’module’ です。そして、Component 名は、ビルド変数の名前に ‘_’ と、.dll/.exe の名前(つまり、当該 module/component のビルドターゲット名)を大文字にしたものを連結した形式となるようです。sdbus2 の場合は、CE_MODULES_SDBUS2 ですね。

Component 名が ‘component’ の場合の例は、pmif です。pmif は、%_WINCEROOT%/PUBLIC/CEBASE/OAK/MISC/winceos.bat の中で、次のように DEVICE_COMPONENTS というビルド変数の値の一部としてセットされます。

          set DEVICE_COMPONENTS=%DEVICE_COMPONENTS% pmif


pmif は、対応するカタログ項目が選択された場合に、DeviceManager (devmgr.dll) にリンクされるライブラリ(pmif.lib)に対するビルドターゲットです。CESYSGEN 条件文の変数名は、Component 名が ‘component’ の場合、ビルド変数の名前から末尾の ‘_COMPONENTS’ を削って、’_’ とビルドターゲット名を大文字にしたものを連結した形式となるようです。つまり、pmif に対しては、DEVICE_PMIF となります。DEVICE_PMIF は、たとえば %_WINCEROOT%/PUBLIC/common/OAK/FILES/common.bib で参照されており、common.bib には次の行があります:

; @CESYSGEN IF DEVICE_PMIF
   pm.dll       $(_FLATRELEASEDIR)\pm.dll                      NK  SHMK
; @CESYSGEN ENDIF


ちなみに、pmif に対応するカタログ項目は、「電源管理 (完全)」(Sysgen 変数:SYSGEN_PM)と「電源管理 (最小)」(Sysgen 変数:SYSGEN_PMSTUBS)です。これらのカタログ項目が、どちらも選択されていない場合は、pmif.lib が devmgr.dll にリンクされません。pmif が DEVICE_COMPONENTS の値の一部にセットされることにより、devmgr.dll のリンクライブラリに pmif.lib が追加される様子は、%_WINCEROOT%/PUBLIC/common/CESYSGEN/makefile をご覧ください。

さらに補足すると、pmif.lib のソースは、WinCE のカーネルソースの一部となっており、%_WINCEROOT%/PRIVATE/winceos/COREOS/device/pmif/ に配置されています。

さて、CESYSGEN 条件文で指定する Component 名の命名規約の説明における、’module’ と ‘component’ の違いは、上で述べた実例を振り返ると、おそらく、次のような使い分けなのだと思います:

・module
 ビルドターゲットが、.dll または .exe、つまり、executable であるもの。

・component
 ビルドターゲットが、.lib などの、ビルドの中間生成ファイルであるもの。

また、module については、Platform Builder のカタログ項目ビューで対応する項目のプロパティを見ると、「モジュール」という項目があり、component については、そうではない、ということも言えそうです。

module と component の概念については、WinCE のリファレンスの、次のページにも記述があります:

 http://msdn.microsoft.com/en-US/library/ee482113(v=WinEmbedded.60).aspx (Windows Embedded CE Modules and Components)

このページの説明には、component は、module の構成要素であり、module によっては、(コンフィグレーションにより組込むかどうかを切り替えできる)オプションの component を含む、と書かれています。上で述べた解釈は、この説明にも合っていると思います。

蛇足ですが、.bat ファイルにおける、OS Design に module を含める記述の例(ビルド変数 XXX_MODULES の設定例)が、WinCE のリファレンスの次のページに載っています:

 http://msdn.microsoft.com/en-US/library/ee478860(v=WinEmbedded.60).aspx (Cesysgen Batch File)

Add comment 2011/08/14 koga

Armadillo-440 用 WEC7 BSP 無償版をリリース

アットマークテクノ社の i.MX25 プロセッサ搭載ボード Armadillo-440 用の、Windows Embedded Compact 7 の BSP 無償版をリリースしました:

http://www.stprec.co.jp/news/20100917.html

お気軽に御利用下さい。

Add comment 2010/09/21 koga

Armadiilo-440 用 WinCE 6.0 BSP の修正版リリース

Armadillo-440 用 WinCE 6.0 BSP (Lilas-am440-6) の修正第一版をリリースしました。修正内容は、以下の通りです:

    ディスプレイコントロールパネルの、バックライト設定の調節が適切でない不具合を修正。
    Ethernet ドライバが、link up/link down を正しく通知しない不具合を修正。
    ブートローダ(EBoot)において、OS イメージの NOR Flash書き込み速度が遅かったのを改善。

無償版(Basic Version)の最新版は、ここからダウンロードできます。お気軽にご利用下さい。

1 comment 2010/08/18 koga

OSC Hokkaido 2010でWinCEを展示

6/26(土)に、札幌市産業振興センターで開催される「オープンソースカンファレンス北海道(OSC Hokkaido)2010」で、アットマークテクノ社の Armadillo-440 に弊社で移植した WinCE 6.0 R3 を展示します。この Armadillo-440 移植版は、5/12-14 に開催された ESEC 2010 において、アットマークテクノ社のブースで展示して頂いたものの完成版となる予定です。

当日までには、アットマークテクノ社の Armadillo サイトにて、評価用のデモ版 OS イメージを公開する予定です。また、弊社 Web サイトにおいて、Armadillo-440 用の BSP (”Lilas-am440-6″) の無償評価版も公開する予定です。いましばらくお待ち下さい。

なお、6/26 の OSC Hokkaido 2010 では、.NET Micro Framework のセミナーも担当します。こちらは、事前登録が必要ですので、ご興味のある方は、早めの登録をお願いします。トップページにてユーザ登録のうえ、トップページの「セミナー事前登録の方法」に記載の手順で事前登録して下さいませ。

Add comment 2010/05/18 koga

“Windows Embedded” 組み込みセミナー(札幌編)

12/17(木)に、JR 札幌駅隣接の JR タワーにあるマイクロソフト北海道支社で、「”Windows Embedded” 組み込みセミナー」が開催されます。この無料セミナーは、5または7つのセッションで構成される予定ですが、二番目のセッションを担当することになりました。

僕が担当するセッションのタイトルは、「Armadillo-500 で広がる Windows Embedded CE 6.0 の開発」です。

参加登録の受け付けが始まっていますので、Windows Embedded CE や、その他の Windows Embedded 製品での開発に興味がある方は、是非いらして下さい。定員は40名、全セッションの終了後には、無料で参加できる懇親会も予定されています。各セッションの内容は、
 http://www.microsoft.com/windowsembedded/ja-jp/news/events/embedded_kumikomi_Seminar.mspx#sapporo
をどうぞ。参加登録は、
 http://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032435447&Culture=ja-JP
です。

Add comment 2009/11/27 koga

Previous Posts


Categories

Links

Posts by Authors

Recent Posts

Calendar

2017年4月
« 9月    
 1
2345678
9101112131415
16171819202122
23242526272829
30  

Posts by Month

Posts by Category

Meta