WS007SHのNORフラッシュメモリ解析 †
サービスメニューによって吸い出した1MBのSYSNO_C2.DBKを眺めて,WS007SH(W-ZERO3 [es])がいかに起動するか,ハードウェアがどうなっているかを深く理解することを目的とします。
注意とお願い †
この内容にはかなり推測が含まれています。
また,作業量が膨大なため,よろしければ協力をお願いします。
利便のため,次のような表記をします。
- NORメモリのアドレス@0x00000 - @0xFFFFF
- 物理メモリアドレス$0x00000000 - $0xFFFFFFFF
- 仮想メモリアドレスV0x00000000 - V0xFFFFFFFF
参考になるもの・必要になるもの †
- まずはWS007SHの分解レポート
W-ZERO3は,CPUとしてARM v5TEベースのCPUであるXscaleシリーズのPXA270というプロセッサを採用しています。元々はIntelが開発したのですが,現在Xscaleの事業はマーベル・テクノロジー・グループに譲渡されています。ただ,XScaleのマニュアルはIntelのサイトに置いてあります。
- ARMについて何も知らない場合,以下のページが分かりやすく解説されているので一見するとよいでしょう。
【絵で分かるキーワードARM(あーむ)】
- ARMのマニュアルとしては,英語ですがARMのサイトのここからARM v5TE Architecture Reference Manualをダウンロードするとよいでしょう。
- また,PXA27xのマニュアル(英語)
も必要です。たくさんありますが,とりあえずManualsにあるIntel(R) PXA27x Processor Family Developer's ManualとIntel XScale Core Developer's Manualだけで十分です。
現在では,intelのWEBサイトにPXA関連のマニュアルは置いてありません。XScale FreakのPXA27xの項目からダウンロードするとよいでしょう。
- ARMのディスアセンブラも必要になります。ディスアセンブルするだけならこのサイトにdisarmというARM用のディスアセンブラがあります。
- また,MSDNライブラリのWindows CE 5.0の情報も参考になります。こことか。
基本的にはこれらの資料を参考にしつつ根気よくSYSNO_C2.DBKを読んでいけばいいわけですが,どのようにGPIOピンを繋ぐか,などは製造元が決めることですし,内蔵されているICにはドキュメントが存在しないものもあるため,そのあたりはチップを想像しつつ行うことになります。例えばCPLDがキーボードとIO拡張の制御に使用されている(情報源はここ)ので,キー制御にPXA270の標準の機能が使用されておらず非常にやっかいなことになります。
PXA270のはじめの動作 †
上記PXA27xのマニュアル2.10に書いてあるように,CPUに電力が供給されるとCPUは$0x00000000番地から実行を開始します。
おそらくこの$0x00000000番地(PXA270ではスタティックメモリエリア0)にこのNORメモリが接続されてるんじゃないかと思われますが,確証はありません。
ここではNORは$0x04000000に接続されてるとか書かれているけど,情報源はどこだろう?
追記:情報源と思われるページが見つかりました。このブログの2006年1月20日の日記です。しかし,解析する限り,仮想メモリの$0x04000000に実アドレス$0x00000000が割り当てられていることはありますが,実アドレス$0x04000000にNORメモリがメモリマップされている,ということはなさそうです。もしくは,この情報はWS003/004のものなので,WS007では変更されているのかもしれません。
しかし,途中,仮想メモリを割り当てるときに物理アドレス$0x00000000から(1MBでなく)8MBがまとめて割り振られていることや,物理アドレス$0x04000000には1MB割り振られていることから,もしかしたら$0x04000000に接続されているのかも?
追記:これについても,物理アドレス$0x04000000(スタティックメモリエリア1)だけでなく,$0x08000000(スタティックメモリエリア2)と$0x10000000(スタティックメモリエリア4)にも1MBが割り当てられているので,おそらくこれらはメモリマップI/Oであると思われる。
そうだとしたら,$0x00000000に何かが接続されていて,NORメモリのプログラムコードの前に何かが実行されることになるのだけれど。(追記に従い削除)
NORフラッシュメモリのおおまかな構造 †
| @0x00000-@0x1FFFF | ブートローダ |
| @0x20000-@0x7FFFF | サービスメニューのプログラム? |
| @0x80000-@0xCFFFF | 通常起動時の初期化関数?(たぶんWILLCOMロゴとかもここにある?) |
| @0xD0000-@0xFFFFF | サービスメニューで利用されるフラグとか,UniqueIDとか |
解析メイン †
とりあえず,NORメモリは$0x00000000に接続されているものとします
- 最初,$0x00001020(=@0x01020)にジャンプする。この間@0x01000バイトまでのデータはほとんど無効データ(0x00)であるが,0x0040から11バイトだけ何らかのデータが書かれている(これが何をあらわしているのかは不明)。
@0x01000から@0x0101Fまでは例外ベクタの値と思われる。
@0x01020からは主にリセットの種類を判別し,クロックやGPIOの初期設定を行っている。
リセットがスリープモードからの復帰でないとき,$0x00003BB0番地へジャンプする。
($0x00003BB0番地は,NORメモリのバージョンによって変わるかもしれません。)
- $0x00003BB0番地からの処理では,まず$0x00000000から$0x0001FFFFまでを$0xA00A0000から$0xA00BFFFFまで(上位メモリ)にコピーし,その上位メモリにジャンプする。
$0xA0000000はPXA27xの仕様ではSDRAM領域であり,$0xA0000000に64MBのSDRAMが接続されていると思ってよいだろう。
それから,サービスメニューに入るか,通常の起動を行うかの判定を行う。
判定については,まずGPIOの83と84を見る。GPIO84がLOW(PL84が0)なら,サービスメニューへ。このときPL83がLOW(PL83が0)ならリモート診断モードへ入ると思われる。
PL83がhighのときはよく分かっていない。
知りたい点:このGPIO83と84が何に接続されているのか?
次に,PL84がHIGHのとき$0x08000000に向かってなにかゴチャゴチャした処理を行っているのだが,たぶんキーを検出しているのだろう。すると,$0x08000000にキー制御用のCPLDが接続されているのか?
追記:キー検出の処理が整理できました(謎はありますが)ので,下にまとめておきます。
また,このゴチャゴチャした処理を終えて,サービスメニューに入るか通常の起動を行うか判定するらしいのだが,もしサービスメニューに入るキーが押されていなくても,@0xF0000にある4バイトがDIAG,DIAR,DIAS(ASCIIコード)のいずれかである場合,それぞれ診断メニュー・リモート診断メニュー・サービスメニューに入る。
追記:また,このときD+Cキーの同時押しを判定しています。これはSDカードを基にフラッシュメモリを復旧させる(しかしもちろん,NORフラッシュが完全にダメになっていればこの復旧もできないが)ものである。
キー検出 †
- $0x08000000に0x00をバイトストア
- $0x08000004に0x00をバイトストア
- $0x08000008に0x01をバイトストア
- 10ミリ秒待つ
- $0x08000008に0x00をバイトストア
- $0~11ビットいずれか1つのビットを立てた,16ビットの値を$0x08000000と$0x08000004に2分割してそれぞれバイトストアする。立てたビットの重みをaとする。
- 1ミリ秒待つ
- $0x08000008に0x00をバイトストア
- $0x08000008からバイトロード
- ロードした値を,0~6ビットいずれか1つをセットした8ビットの値(ビットの重みをbとする)と比べ,合致したらaとbの値を保存しておく。
- bを1増やして8へループする。bは最初0。
- aを1増やしてbを0にして1へループする。aは最初0
- 12×b+aで以下のテーブル(左上が0,右上が11,値は16進数)を引く。これが押されたキーのスキャンコードになる。
| 84 | 31 | 33 | 35 | 36 | 37 | 39 | 30 | 08 | 00 | DB | D6 |
| 00 | 32 | 34 | 52 | 59 | 38 | 49 | 4F | 50 | 00 | D8 | D7 |
| 09 | 51 | 45 | 54 | 47 | 55 | 4A | 4B | 00 | 00 | 00 | 00 |
| 00 | 57 | 53 | 46 | 56 | 48 | 4D | 4C | 00 | 00 | D5 | 00 |
| DE | 41 | 44 | 43 | 42 | 4E | 2E | 00 | 0D | 00 | DC | 00 |
| AB | 5A | 58 | 2D | 20 | 2F | 00 | 8B | 00 | 00 | D9 | 85 |
| DD | CF | 00 | A7 | A8 | 2C | 8C | 8A | 8D | 00 | DA | 00 |
- GPIOのPL95が1のときにはスキャンコード173(=0xAD)のキーが押されたことにする。
- $0x08000000に0x00をバイトストア
- $0x08000004に0x00をバイトストア
- $0x08000008に0x01をバイトストア
- 10ミリ秒待つ
- 0x08000008に0x00をバイトストア
通常の起動処理 †
$0x40F00008番地に$0xA00A3CA0を(これは初期化関数から帰ってくるときのアドレスかな?),$0xA0001600番地に$0xA00A4160を($04160はなんらかの関数なのだが,よく分からない。サービスメニューに入る場合でもこの処理がある)ストアする。
その後,$0x00080000番地へジャンプする(正確には,cr0に0x00080000を代入した後,cr0の値をpcに代入している)。
@0x80000からの処理の長さがはんぱないので,解析を中断している。
サービスメニュー開始(@0x20000)での処理 †
- IRQとFIQ割り込みを禁止したスーパーバイザモードに入る。
- 命令キャッシュ・分岐対象バッファ(Branch Target Buffer)をイネーブルにする。
- 仮想メモリの設定を行う。テーブルはV0xA1FF0000~V0xA1FFFFFFまで。
| V0x00000000 - V0x03FFFFFF | $0xA0000000 - $0xA3FFFFFF | SDRAM |
| V0x04000000 - V0x047FFFFF | $0x00000000 - $0x007FFFFF | NORフラッシュメモリ |
| V0xD0000000 - V0xD07FFFFF | $0x00000000 - $0x007FFFFF | 同上 |
| V0xDA000000 - V0xDA0FFFFF | $0x08000000 - $0x080FFFFF | おそらくキー制御関連を含めたのメモリマップドI/O |
| V0xDC000000 - V0xDC0FFFFF | $0x04000000 - $0x040FFFFF | おそらくメモリマップドI/O |
| V0xD6000000 - V0xD60FFFFF | $0x10000000 - $0x100FFFFF | おそらくメモリマップドI/O |
| V0xA0000000 - V0xA3FFFFFF | $0xA0000000 - $0xA3FFFFFF | SDRAM |
| V0x80000000 - V0x80FFFFFF | $0x08000000 - $0x08FFFFFF | 上とかぶっている… |
| V0x40000000 - V0x480FFFFF | $0x40000000 - $0x480FFFFF | PXA270付属のレジスタ |
| V0x5C000000 - V0x5C0FFFFF | $0x5C000000 - $0x5C0FFFFF | PXAの内部メモリ? |
| V0xB0000000 - V0xCFFFFFFF | $0x20000000 - $0x3FFFFFFF | PC Card/CompactFlash? Slot 0&1 |
| V0x4C000000 - V0x4C0FFFFF | $0x4C000000 - $0x4C0FFFFF | USBホストコントローラ? |
| V0x50000000 - V0x500FFFFF | $0x50000000 - $0x500FFFFF | カメラコントローラ? |
- ドメイン0のみクライアントに設定
- プロセスIDレジスタに0を書き込む
- ミニデータキャッシュの設定
- 分岐予測をイネーブル
- V0x00008000にV0x04020000~V0x0407FFFFまでの内容をコピーする。
(つまり,NORメモリをSDRAMにコピーする)
- SDRAM領域にジャンプする。
- スタックの設定
| モード | スタックポインタ(SP)=r13の値 |
| システム&ユーザー | 0x003E0000 |
| 未定義 | 0x003C0000 |
| アボート | 0x003A0000 |
| FIQ | 0x00380000 |
| IRQ | 0x00360000 |
| スーパーバイザ | 0x00340000 |
- v0x00059d64から0x6974バイトをv0x00300000までに転送
(つまり,@0x71d64から0x6974バイトを$0xA0300000に転送)
v0x00306974からv0x00309c84までゼロクリア
v0x00309c84からv0x003e0000までを0x55で埋める
- (私のNORメモリでは)@0x33628に対応するSDRAMの位置にジャンプする。
@0x33628での処理 †
この処理に突入したとき,仮想メモリマッピングとNORメモリをSDRAMにコピーしていたりする関係で,実際のPC(プログラムカウンタ)の値は0x0001b628である。
- v0x00301a60に0x00000000か0x00000001を書き込む。
このルーチンに突入したとき,PCの値が0x04000000より大きくなっているはずなら0,小さいなら1。普通は1になるはず。
- v0x00301a5cにCPUの種類を書き込む。1(PXA255)か2(PXA27x)か0(Unknown)である。
普通は2になるはず。実際の判定処理としては,コプロセッサ15にある,プロセッサIDレジスタを読み取って判定している。(ちなみにW-ZERO3とW-ZERO3[es]のプロセッサIDレジスタの値は0x69054117)
- メモリ容量をv0x00301a64に書き込む。
- 続きはまだ解析中……