2010年04月19日

USB-BOOT

そろそろ USB-BOOT をやってみたい。

まずは、USB-BOOT の stage1/stage2 を動かすこと自体が目標になる。

Jz4725 を使っている SAKC も ブートはできているようだから

を理解して 実行してみることが最初のステップか。

とりあえず、ここに書いてある通りにして git で取ってきてまとめたもの

をベースにすることにする。

まず USB-BOOT させてみる。→ キーを押しながら USB に差し込むと Linux から次のように見える。

    T: Bus=01 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 7 Spd=480 MxCh= 0
    D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1
    P: Vendor=601a ProdID=4750 Rev= 1.00
    S: Manufacturer=Ingenic
    S: Product=JZ4750 USB Boot Device
    C:* #Ifs= 1 Cfg#= 1 Atr=c0 MxPwr= 2mA
    I:* If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=50 Driver=(none)
    E: Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
    E: Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms

Jz4725B にも関わらず ProdID=4750 。String も "JZ4750 USB Boot Device" 。

あと Bulk エンドポイントが In/Out の 2 つあることがわかる。

さて 次のようにビルドした usbboot を動かしてみる。

    export PATH=$PATH:/usr/mipsel-linux-uclibc/bin
    export PATH=$PATH:/usr/mipsel-linux-uclibc/usr/bin
    export CROSS_COMPILE=mipsel-linux-
    export ARCH=mips
    ./autogen.sh
    ./configure --prefix=/usr --sysconfdir=/etc
    make
    make install
    cd xburst_stage1
    make
    cp xburst_stage1.bin /usr/share/xburst-tools
    cd ..
    cd xburst_stage2
    make
    cp xburst_stage2.bin /usr/share/xburst-tools
    cd ..


で動かすといきなりエラー。src/ingenic_usb.h の PRODUCT_ID が 0x4740 になっているためだった。0x4750 にしないと少なくとも動かない。

ここでかなり不安になったのだが、とりあえずは 修正して 動かしてみると...

    # ./usbboot
    usbboot - Ingenic XBurst USB Boot Utility
    (c) 2009 Ingenic Semiconductor Inc., Qi Hardware Inc., Xiangfu Liu, Marek Lindner
    This program is Free Software and comes with ABSOLUTELY NO WARRANTY.

    Now checking whether all configure args valid: YES
    Current device information:
    CPU type is Ingenic XBurst Jz4740
    Crystal work at 12MHz, the CCLK up to 252MHz and PMH_CLK up to 84MHz
    SDRAM Total size is 32 MB, work in 4 bank and 16 bit mode
    Nand page per block 128, Nand page size 4096, ECC offset in OOB 12, bad block offset in OOB 0, bad block page 127, use 1 plane mode
    usbboot :>

お! とか思ったが、/etc/xburst-tools/usbboot.cfg をチェックするのを忘れていた。

だいたい Ben-NanoNote と同じはずで、ちょっとみた範囲では修正する必要はないように見える。

で、Now checking ... のメッセージは usbboot.cfg をチェックしているだけ なのがわかりちょっとがっかり。

ちょっと コードをチェックして bulk に read したときメッセージを出すようにしてみた。

    usbboot :> boot
    CPU data: JZ4750V1
    CPU not yet booted, now booting...
    Loading stage1 from '/usr/share/xburst-tools/xburst_stage1.bin'
    Download stage 1 program and execute at 0x80002000
    CPU data: JZ4750V1
    Loading stage2 from '/usr/share/xburst-tools/xburst_stage2.bin'
    Download stage 2 program and execute at 0x81c00000
    CPU data: JZ4750V1
    CPU data: Boot4750
    Booted successfully!
    CPU data: Boot4750
    #bulk_read: 8
    #00 00 00 00 00 00 00 00
    Configuring XBurst CPU succeeded.
    usbboot :>


CPU data: Boot4750 というメッセージは stage2 が出している。
要するに stage2 が動いて メッセージを変えたということらしい。あと bulk_read が その後初めて動作する。それまでは control エンドポイントでの通信。

それは良いとして、4750 用のコードが動いてしまっているようだ。これはまずいかも。Ben-Nanonote など 4740 のコードが動くはずで、動作を確認できていないコードが動作しているっぽい。

さて、4750 かどうかは、fw_args で判断している。要するに usbboot のプログラムが送り込んだ情報をもとに している。

さらに調べると、その情報は 結局 JZ4750V1 の情報を 引き継いでいる。

で、JZ4750V1 のまま動けば良いのだが ... stage2 の処理は なんかいろいろ違うのだった。

    それは置いておいて... USB device はどうやって使うのか調べようとしたのだが ... Programming Manual に 記載がないことが判った。まぁ Linux のドライバ見れば判ることだからあまり気にしないが ...

    ところで、stage2 に切り替わっても別段 USB をリセットしたりはしないのかも知れない。stage2 で USB を乗っ取るということか。ポーリングベースなら、切り替えの手続きもなしでいけそうだし。

    そうすると→ブートローダ→カーネルと処理を切り替えても最初の USB コンフィグのまま使えるのかも知れない。

    Linux の USB デバイスドライバを組み込まなければ そのまま使えるんじゃないだろうか? だったらコンソールとして使えるかも知れない。

で 無理やり 0x4740 に変更してみたら .. stage2 が動かない。-- ううむ 困った。

(続き)

実をいうと、NAND FLASH にアクセスできない問題が出ている。

nquery -- query NAND flash info

というのが動かないと先はないのだが、

    usbboot :> nquery 0 0
    CPU data: Boot4750
    ID of No.0 device No.0 flash:
    Vendor ID :0x0
    Product ID :0x0
    Chip ID :0x0
    Page ID :0x0
    Plane ID :0x0
    Operation status: Success!
    usbboot :>

となってしまうのだ。

stage2 が動いている以上、なんとかできるはずなので、調査から。

まず、ブートシーケンスの理解から

    1. NAND Boot の場合

  • 0x1FC00000 - 0x1FC01000(内部ROM)
    から起動して、NAND FLASH から Boot する場合、FLASH の最初のエリア(size 0x2000) を SDRAM 0x8000000 にロードする。もし ECC が正しくなければ、次のエリア(offset 0x2000) を SDRAM にロード 。これの ECC も正しくなければ STOP 。ロードが OK なら 0x80000004 に JUMP 。

  • 0x8000000 - 0x80002000 (First Stage Loader)
    U-BOOT を 0x80100000 に ロードして 先頭(0x80100000) に JUMP。

    2. USB Boot の場合

  • stage1 program and execute at 0x80002000
  • stage2 program and execute at 0x81c00000


ところで SDRAM の領域のベースアドレスは、ソフトで決めることができる。初期状態は、0x20000000 にマップされている?とかするので、要注意。

さて、どうするのか正しいのか? 困ってしまったのだが ... ググってみると

ingenic usbboot は対応済みなことがわかった。

たとえば stage1/fw/board_4750.c

ソース的には board_4750.c を変更して対応している。その中で 4725b かどうかを 特定の場所を参照して判断している。

( ( * (volatile unsigned int *) 0xB00F0000 ) & 0x80000000 )

どうも qi-hardware のものは 基本的に Ingenic オリジナルから再構成しているようだ。だいぶ別物になっているような印象。

ただ、jz4725b 対応は上記だけのようなので、ちょっとマージしてやってみる。

    usbboot :> nquery 0 0
    CPU data: Boot4750
    ID of No.0 device No.0 flash:
    # bulk_read: 8
    # 20 d5 94 25 44 00 00 00
    Vendor ID :0x20
    Product ID :0xd5
    Chip ID :0x94
    Page ID :0x25
    Plane ID :0x44
    # bulk_read: 8
    # 00 00 00 00 00 00 00 00
    Operation status: Success!
    usbboot :>

なんということだ。あっさり動いてしまった。で、Page ID を見ると 消去ブロックサイズ 256KB / ページサイズ 2KB だとわかる。

あと usbboot.cfg が 違うので 修正。

これで NAND FLASH が覗けるようになったかも。上記の bulk_read の 表示を削って使ってみることにした。


    usbboot :> nreadraw 0 2048 0 0
    ==== CPU data: Boot4750
    Reading from No.0 device No.0 flash....

    0x00000000 : ff 55 55 55 55 55 55 55 ff ff ff ff 01 00 11 04
    0x00000010 : 00 00 00 00 21 e0 e0 03 00 00 e9 8f 21 e0 20 01
    0x00000020 : 00 80 1d 3c 00 40 bd 37 00 80 19 3c b8 06 39 27
    0x00000030 : 08 00 20 03 00 00 00 00 00 00 00 00 00 00 00 00
    0x00000040 : 1b 43 02 3c 83 de 42 34 19 00 82 00 0f 00 02 3c
    0x00000050 : 10 20 00 00 40 42 42 34 82 24 04 00 1b 00 44 00
    :
    :
    0x000007e0 : 21 20 04 02 ff ff 84 24 5c 02 00 0c b0 15 45 24
    0x000007f0 : 00 80 02 3c b0 15 43 90 54 00 73 14 00 80 03 3c
    Operation end position : 0
    usbboot :>

コマンドには、nread/nreadraw/nreadoob がある。読み出す位置の指定はページ。

nread と nreadraw の違いがよくわかっていないのだが .. とりあえずあちこちダンプしてみた。


    nquery 0 0
    nreadraw 0 2048 0 0 ff 55 55 55 55 55 55 55 ff ff ff ff 01 00 11 04
    nreadraw 1 2048 0 0 7c 15 62 8c 18 00 40 10 21 80 00 00 00 80 14 3c
    nreadraw 2 2048 0 0 21 10 80 00 c3 28 02 00 2b 10 aa 00 0a 00 40 10
    nreadraw 3 2048 0 0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
    nreadraw 4 2048 0 0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
    nreadraw 5 2048 0 0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
    nreadraw 6 2048 0 0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
    nreadraw 7 2048 0 0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
    nreadraw 127 2048 0 0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
    nreadraw 128 2048 0 0 80 80 1f 3c 00 00 ff 27 00 90 80 40 00 98 80 40
    nreadraw 129 2048 0 0 10 00 b0 8f 08 00 e0 03 28 00 bd 27 57 03 20 0c
    nreadraw 130 2048 0 0 01 00 42 34 25 20 82 00 00 00 e4 ac 00 01 03 3c
    nreadraw 131 2048 0 0 00 00 43 90 06 00 6a 91 26 18 69 00 ff 00 63 30
    nreadraw 132 2048 0 0 44 03 0c 36 48 03 42 34 54 03 10 36 00 00 a5 ae

    nreadraw 150 2048 0 0 03 16 02 00 a2 a0 62 a0 0b 00 03 24 03 00 43 10

    nreadraw 160 2048 0 0 2b 20 a6 00 02 00 00 15 1b 00 68 00 0d 00 07 00
    nreadraw 161 2048 0 0 08 25 82 80 d3 ad 00 00 c1 95 00 01 01 00 00 00
    nreadraw 162 2048 0 0 1c 00 00 00 18 00 00 00 04 00 00 00 05 00 00 00
    nreadraw 163 2048 0 0 00 00 00 00 00 00 00 00 80 80 00 00 00 00 00 00

    nreadraw 164 2048 0 0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
    nreadraw 165 2048 0 0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
    nreadraw 175 2048 0 0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
    nreadraw 200 2048 0 0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
    nreadraw 255 2048 0 0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff

    nreadraw 256 2048 0 0 00 02 00 00 00 04 00 00 00 20 00 00 66 03 00 00

    nreadraw 511 2048 0 0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
    nreadraw 512 2048 0 0 00 02 00 ff 00 02 00 ff 00 02 00 ff 00 02 00 ff
    nreadraw 513 2048 0 0 00 02 00 ff 00 02 00 ff 00 02 00 ff 00 02 00 ff
    nreadraw 514 2048 0 0 00 02 00 ff 00 02 00 ff 00 02 00 ff 00 02 00 ff
    nreadraw 515 2048 0 0 00 02 00 ff 00 02 00 ff 00 02 00 ff 00 02 00 ff

    nreadraw 1024 2048 0 0 00 02 00 ff 00 02 00 ff 00 02 00 ff 00 02 00 ff
    nreadraw 2048 2048 0 0 00 02 00 ff 00 02 00 ff 00 02 00 ff 00 02 00 ff
    nreadraw 4096 2048 0 0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
    nreadraw 8192 2048 0 0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
    nreadraw 16384 2048 0 0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff

    nreadraw 32768 2048 0 0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
    nreadraw 32769 2048 0 0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
    nreadraw 32770 2048 0 0 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff

頭から 6KB 弱?の stage1 ブートローダがある。ブートの仕組みからいうと 8KB x 2 にしそうなものだが、1つしかない。

128 ページから 163 ページまで(72KB) 何かが入っている。stage2 ブートローダ?

あとは、256 ページから(offset 512KB) にも。カーネル? 

さらに 512 ページ (offset 1024KB) からもなにか。.. あとはよくわからない。

それはともかく、0xff が続くデータは ファームウェアのファイルには入っていなかった。RAW イメージではなかったのだろう。

さて、なにをするにも NAND FLASH のバックアップを取っておきたい。だが、いまあるコマンドは ページ単位で データをダンプするのみ。バックアップ専用のコマンドなどないようだ。

作るのは簡単そうなのだが、意図的にそうしているような気もするし ... どうしよう。

とりあえずは、

  • 情報量を減らすため、nreadraw (など) で同じデータがあれば、続く状態は表示しないように変更。
  • アドレスの値が ページ内オフセットだったのを 絶対アドレスに変更 (ただし 4GB まで)
  • 繰り返しを指定できるように変更。

という改造をしてみた。

で、やってみたのだが、なんだがすごく遅い。1 秒に 2 ページぐらいの速度。これで 頭の 128MB を バックアップしようとすれば 10 時間ぐらいかかる計算。

ところで、nread と nreadraw の違いがわからない。他に readoob というのがあって、冗長データが読めるのは分かるのだが ...
説明には、

  • nread read NAND flash data with checking bad block and ECC
  • nreadraw read NAND flash data without checking bad block and ECC

nreadraw は、ECC をみない生のデータで、nread は、ECC を見て? bad block と代替したデータ? それともエラーになる?

どうもよくわからないが、同じ内容のようなので OK か。

ところで、OOB は Out of band というそうだ。2K ページ毎に 64 バイトある。ECC を このエリアに置くことになっているらしい。-- 内部ROM もチェックしているようだからどう置くかも決まっているのだろう。

... というわけで nreadoob でダンプしてみた。

  • パターン1) 頭の方

    0x00000000 : ff ff ff fa 75 f3 2c d5 cb 81 ac f6 84 c2 78 46
    0x00000010 : 13 c5 c4 08 38 3e d9 69 e7 59 d6 4c be e0 2f f1
    0x00000020 : 2c 58 fc ec 32 c7 b2 5c f8 25 50 ec ea 86 0b a5
    0x00000030 : 9d 40 44 27 31 b2 d4 ff ff ff ff ff ff ff ff ff

  • パターン2) 未使用

    0x00001800 : ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
    *

  • パターン3) 後ろの方

    0x00110000 : 00 5f 02 ff 00 5e 01 ff 00 5d 00 ff 00 5c 00 ff
    0x00110010 : 00 5c 00 ff 00 5c 00 ff 00 5b 00 ff 00 5a 01 ff
    0x00110020 : 00 59 03 ff 00 58 04 ff 01 56 02 ff 02 55 00 ff
    0x00110030 : 02 55 00 ff 01 54 00 ff 01 54 00 ff 00 53 00 ff

  • パターン4) 後ろの方-- 未使用?

    0x0014c800 : 00 02 00 ff 00 02 00 ff 00 02 00 ff 00 02 00 ff


未使用のページは、0xff のままになっている。これで書き込んだことがあるかどうか判断できそうだ。

dual-boot を考えて そういうページに、自作のデータを置くようにすれば良いのだろう。

あと、あるところから 4 バイト単位で最後が 0xff になる特徴あるパターンになっている。そういう特徴があるものの中でも "0x00 0x02 0x00 0xff" で埋められているものもある。これは何だろう?

ちなみに、nreadraw で取ったデータにも 同じようなデータが見受けられる。

    使っているページを調べるために、nreadraw で取ったデータを見ていたのだが、文字列が入っていたりした。

    どうも OOB のデータではないようだ。使い方を間違っているのか?
    ... ただ、違ったとしても ページを使っているかどうかの判断には使える。


追記:JZ4750L VOLANS board

JZ4750 というのがどうにも気になっていたのだが ...

Ingenic の VOLANS 評価ボードというのは JZ4725B らしい。

当然のように対応済みなのだが、u-boot や linux では JZ4750L として扱っている。

キーワードだけ並べるとこんなかんじ。

  • VOLANS development board(ver:1.1, Jz4725B based)
  • linux : arch/mips/f4750l_defconfig
  • linux : arch/mips/include/asm/mach-jz4750l/board-f4750l.h
  • U-Boot(1.1.6) : board/volans

dingoo-linux と qi の両方とも JZ4750L の扱いは適当そうなので、Ingenic のカーネルとU-BOOT をちゃんと取ってこないとダメっぽい。

linux の方は取ってこれているので、board-f4750l.h を見てみると...

    GPIO_SD1_VCC_EN GPE4
    GPIO_SD1_CD_N GPC14
    GPIO_USB_DETE GPD6
    GPIO_DC_DETE_N GPD7
    GPIO_CHARG_STAT_N GPD15 (使っていない.. かも)
    GPIO_DISP_OFF_N GPD25 (LCD_REV ?)
    GPIO_LCD_PWM GPE14 (PWM4)

SD0 を除くと 上記の GPIO を定義している。別のボードの定義なわけだが、リファレンスだし、変更する理由がなければ変更しないかも知れないので参考にはなる。また、ピンアサインは変えても機能そのものを削ると ソースも直さないといけないので 素直に 対応しているかも知れない。ちなみに、想定していなかったピンは、

  • USB_DETE:

    USB に電源が供給されているかどうかを判断する(USB_UDC_HOTPLUG)のに必要。

  • GPIO_DISP_OFF_N:

    ディスプレーを OFF するのは LCD_DE ではなかったのか? あるいは(MOS FET の)電源スイッチ用?

  • GPIO_CHARG_STAT_N

    充電IC の LED 用のピン(/CHRG) に接続する。L で充電中。赤LED は充電を示すのではなく 外部電源の意味なので関係ない。

    どこにも接続されていないように見えたが GPIO につながっているのかも。

ぐらいか。

いろいろやらないといけないとしても、どのようにやるのが正しいのかということが分かったのは収穫。

ところで、f4750l_defconfig の中に CONFIG_JZ_FPGA=y なんて記述があり、FPGA を載せたボードなのか! .. なんて思ったのだが、ググると

    support f4750l(on fpga) board for jz4750l

なんて記述を見つけた。FPGA を載せたのではなく、LSI の開発前に FPGA に 載せたということらしい。

追記:本当に正しく読めているのだろうか?


0x00000000 : ff 55 55 55 55 55 55 55 ff ff ff ff


これは先頭のイメージで、0x80000000 にロードされて、0x80000004 から実行されるはず ... なのだが どう見ても命令のパターンには見えない。

とりあえず (いろいろやって) objdump で 逆アセンブルしてみた。


0: 555555ff 0x555555ff
4: 55555555 0x55555555
8: ffffffff 0xffffffff
c: 04110001 bal 14
10: 00000000 nop
14: 03e0e021 move gp,ra
18: 8fe90000 lw t1,0(ra)
1c: 0120e021 move gp,t1
20: 3c1d8000 lui sp,0x8000
24: 37bd4000 ori sp,sp,0x4000
28: 3c198000 lui t9,0x8000
2c: 273906b8 addiu t9,t9,1720
30: 03200008 jr t9
34: 00000000 nop
...


0xc 番地ぐらいからは 命令っぽいが、やはり 0x4 とか 0x8 番地は命令ではない。

あと、これはブートコードの先頭かというとそれらしい。スタックを 0x80004000 に設定しているみたいだし。

ひょっとしたら、0x80000004 番地に jump するのは古いチップだけで、JZ4750 以降は 0x80000010 番地とかに変わっているのかも知れない。

stage2 の調査



usbboot の stage2 のコードを見てみることにする。

main.c は 初期化を行ったあと usb_main() を call している。

usb_main() は、udc.c (USB Device Controller) にあり、ここですべてのコマンド処理をしている。

コマンド処理を見るまえに、ディスクリプタを見てみる。

  • ここでは、Vendor ID = x0601a , Product ID = 0x4740 固定で Product ID = 0x4750 は使っていない。
  • エンドポイントは、bulk エンドポイントが 2 つ。最大パケットサイズは 512B 。
  • String は、"A00A00A00A00" を返す ... とっても適当。


自分では usb を reset していない。HOST が USB を reset しない限り この ディスクリプタに切り替わらない。そして 0x4750 専用にした usbboot の方が動かなくなるはず。(ココ重要)

要するに一応は定義してあるもののダミー。

次に USB の処理を見てみる。

usb_main() では、udc4740Proc() を繰り返し call しているだけ。udc4740Proc では、USB_REG_INTRUSB, USB_REG_INTRIN, USB_REG_INTROUT というレジスタの内容を見て、処理を行う。

処理を行う関数は 次の 3 つ。

  • EP0_Handler()

    コントロール エンドポイントの処理。ベンダーリクエストの パケットに対してそれぞれの処理をする関数を呼び出すようになっている。

  • EPIN_Handler()

    Host から見て IN だから 送信のハンドラ。
    強制的に バッファのデータを送出するのは Handshake_PKT() ?

  • EPOUT_Handler()

    受信のハンドラ。

    送受信は、Bulk_buf というバッファに対して行われる。

    半二重でしか使っていないということ。Bulk_buf の操作は boothandler.c でしか行われていない。

    何に使っているかというと ...

      NAND_OPS_Handle()
      NAND_QUERY:
      NAND_READ_OOB:
      NAND_READ_RAW:
      NAND_READ:
      NAND_PROGRAM:
      SDRAM_OPS_Handle()
      SDRAM_LOAD:

    大量のデータ送受信は、バルク転送を使うらしい。


さて、仕組みをしってなにをしたいのか? ... というと、シリアルコンソールの代わりをするものを作りたいわけだ。

USBの処理は go した時点で用済みなわけだが、新たに動作するプログラムでも stage2 のようにして USB デバイスのコードを入れてやれば バルクエンドポイントをシリアルのように使って通信できるはず。ただし、usbboot の方も改造して go したあと 端末モードのように動作させる必要がある。
posted by すz at 20:42| Comment(0) | TrackBack(0) | Jz47xx
この記事へのコメント
コメントを書く
お名前: [必須入力]

メールアドレス: [必須入力]

ホームページアドレス: [必須入力]

コメント: [必須入力]

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
この記事へのトラックバックURL
http://blog.sakura.ne.jp/tb/37233690
※ブログオーナーが承認したトラックバックのみ表示されます。

この記事へのトラックバック