こんにちは。技術部 手島です。
今回は Bus Pirateを用いてSPI接続するフラッシュメモリの読出方法を記載します。
背景
組込機器を解析する際、まず間違いなく記憶媒体に遭遇します。その記憶媒体の中でも、小型の機器に頻繁に使われている小型のフラッシュメモリがこのSPIフラッシュメモリです。
こんなの。
近頃はプライベートで身近なIoT製品を解析する方も増えているように感じています。その際に、お手軽で使い勝手が良いBusPirateとflashromコマンドを用いてフラッシュメモリから直にファームウェアの入手を試みる方法がよく用いられているようです。しかしながら、このSPI接続するフラッシュメモリに対し、flashromコマンドで読み出しを行うと失敗することがあります。また、そもそも読みたいフラッシュメモリがサポートされてなかったりします。そこで、flashromコマンドに頼らず自分でフラッシュメモリとSPIでお話しする手順を記載します。
今回のターゲット
先日、お仕事とは関係ない、とある家庭用機器を開封したところ、このようなパッケージのフラッシュメモリがありましたので検証に用います。下の赤い定規の1目盛りが1mmです。
デジタル顕微鏡で拡大した画面を携帯で撮影したため、画像が荒くてごめんなさい
Macronix社のMX25L8006Eというフラッシュメモリで、パッケージの横にピンが出ています。
引用: http://www.macronix.com/Lists/Datasheet/Attachments/6764/MX25L8006E,%203V,%208Mb,%20v1.6.pdf
普段の案件時にはROMライターを用いていますが、適合するアダプタが残念ながら社内に無く、到着を待っている余裕がありませんでした。このような場合、手軽に使えるBusPirateやRaspberry Piを用いて中身を読み出しています。
変換基板のはんだづけと接続
今回はピン間隔が0.8mmと珍しいピッチだったので、千石電商さんで変換基板を購入しました。はんだづけし、各ピンがジャンプしていないことを確認します。
その後、BusPirateとフラッシュメモリをこのように接続します。
ピン名が CS# または /CS のように記載されているものは、アイドル時にはHigh、アクティブ時にLowとなるピンです。
BusPirateの設定
設定設定時のログを記載します。SPI通信に関わる各項目は、大抵はデフォルト値でよいのですが、正確にはフラッシュメモリのデータシートを見ながら設定する必要があります。
例1:ビットの読出タイミングはクロックの立ち上がり or 立ち下がり?
例2:クロックはActive High or Active Low?
今回は、以下の変更以外はデフォルトの値を用いています。
- SPIモード・1MHzの通信を選択
- Highレベルの電圧を3.3V固定
HiZ>i
Bus Pirate v3.5
Firmware v6.1 r1676 Bootloader v4.4
DEVID:0x0447 REVID:0x3046 (24FJ64GA002 B8)
http://dangerousprototypes.com
HiZ>m
1. HiZ
2. 1-WIRE
3. UART
4. I2C
5. SPI
6. 2WIRE
7. 3WIRE
8. LCD
x. exit(without change)
(1)>5
Set speed:
1. 30KHz
2. 125KHz
3. 250KHz
4. 1MHz
(1)>4
Clock polarity:
1. Idle low *default
2. Idle high
(1)>
Output clock edge:
1. Idle to active
2. Active to idle *default
(2)>
Input sample phase:
1. Middle *default
2. End
(1)>
CS:
1. CS
2. /CS *default
(2)>
Select output type:
1. Open drain (H=Hi-Z, L=GND)
2. Normal (H=3.3V, L=GND)
(1)>2
Ready
SPI>W
POWER SUPPLIES ON
SPI>v
Pinstates:
1.(BR) 2.(RD) 3.(OR) 4.(YW) 5.(GN) 6.(BL) 7.(PU) 8.(GR) 9.(WT) 0.(Blk)
GND 3.3V 5.0V ADC VPU AUX CLK MOSI CS MISO
P P P I I I O O O I
GND 3.34V 5.00V 0.00V 3.33V L L H H L
接続が正しいことを確認するテスト
フラッシュメモリの中身を読み出す前に、物理的な接続やSPIのモードが正しいことを確認します。予め既知である値が正しく読み出せなければ、フラッシュメモリから読み出したファームウェアやデータも正しくありません。今回は既知の値として、JEDEC IDを使用します。
JEDEC IDとは、JEDECという半導体製造メーカーの団体が、各ベンダやICチップごとに固有の値を設定したものです。今回のフラッシュメモリのデータシートによると、JEDEC IDは 0xC2 0x20 0x14、読み出しコマンドは 0x9F と分かりました。0x9Fを送信すると、その後3バイトの情報を送信してくれます。
使用したコマンドとログを記載します。今回はBusPirateがSPIのMaster、MX25L8006EがSPIのSlaveです。記載のようにJEDEC IDが読めたので、物理的な接続、およびSPIの動作モードは正しいようです。
ログ:
SPI>{ 0x9f r:3 ]
/CS ENABLED
WRITE: 0x9F READ: 0x00
READ: 0xC2 0x20 0x14
/CS DISABLED
コマンドの解説
{ : Start with read, /CSをLowにし、Slaveとの通信を開始する。この際、Slaveからの読出も行う
0x9f : BusPirateからMX25L8006Eに対するRDID(Read Identification)コマンド
r:3 : 3バイト読み出す。この時、クロックはBusPirateが自動で送信してくれる。
] : /CSをHighにし、Slaveとの通信を終了する。
読み出し
ついにフラッシュメモリに記録された情報を読み出します。読み出しコマンドはデータシートによると 0x03、その後にアドレスを24ビット指定します。その後、クロックを送ると、アドレスをインクリメントしながら返信してくれます。
以下に、試しに16バイト読み出した際のコマンドとログを記載します。何か読めました。
ログ:
SPI>{ 0x03 0 0 0 r:16 ]
/CS ENABLED
WRITE: 0x03 READ: 0x00
WRITE: 0x00 READ: 0x00
WRITE: 0x00 READ: 0x00
WRITE: 0x00 READ: 0x00
READ: 0x58 0x00 0x00 0x00 0x35 0x10 0x00 0x00 0x36 0x10 0x00 0x00 0xB0 0x27 0x00 0x00
/CS DISABLED
コマンドの解説:
{ : Start with read
0x03 : BusPirateからMX25L8006Eに対するREADコマンド
0 0 0 : 読み出すアドレス24ビット。アドレスマップの先頭を指す。
r:16 : 16バイト読み出す。この時、クロックはBusPirateが自動で送信する。
] : /CSをHighにし、Slaveとの通信を終了する。
BusPirate v3.6のバッファが48kバイト程度しかないため、私はログを取りながら32kバイトずつ読み、バイナリに固めます。
ログ:
SPI>{ 0x03 0 0 0 r:32768 ]
/CS ENABLED
WRITE: 0x03 READ: 0x00
WRITE: 0x00 READ: 0x00
WRITE: 0x00 READ: 0x00
WRITE: 0x00 READ: 0x00
READ: 0x58 0x00 0x00 0x00 0x35 0x10 0x00 0x00 0x36 0x10 0x00 0x00 0xB0 0x27 0x00 0x00 0xB1 0x27 0x00 0x00 0xB8 0x4A 0x00 0x00 0xB9 0x4A 0x00 0x00 0xDE 0x66 0x00 0x00 0xDF 0x66 0x00 0x00 0xCC 0x82 0x00 0x00 0xCD ...(以下省略)
トラブルシューティング
本当はロジックアナライザで確認するのが最も確実なのですが、手元のBusPirateを用いて、意図的に正しく動作しない状態で動作させてみました。
JEDEC IDを読んでも 0x00 しか返ってこない
MOSIピン、MISOピン、HOLD# ピンの接続を確認してください。試しに、HOLD# ピンに何も接続していない状態だとこうなりました。
SPI>{ 0x9f r:3 ]
/CS ENABLED
WRITE: 0x9F READ: 0x00
READ: 0x00 0x00 0x00
/CS DISABLED
READコマンドを実行しても、全部 0xFF なんだけど
フラッシュメモリのその領域が空なのかもしれません。もしフラッシュメモリに何も書き込まれていない場合、0xFFになります。
正しく動作している BusPirateの設定
私の手元で正しく動作していた際のBusPirateの状態はこんな感じです。i コマンドで状態を表示し、お手元のBusPirateの状態と比較してください。
SPI>i
Bus Pirate v3.5
Firmware v6.1 r1676 Bootloader v4.4
DEVID:0x0447 REVID:0x3046 (24FJ64GA002 B8)
http://dangerousprototypes.com
CFG1:0xFFDF CFG2:0xFF7F
*----------*
Pinstates:
1.(BR) 2.(RD) 3.(OR) 4.(YW) 5.(GN) 6.(BL) 7.(PU) 8.(GR) 9.(WT) 0.(Blk)
GND 3.3V 5.0V ADC VPU AUX CLK MOSI CS MISO
P P P I I I O O O I
GND 3.34V 5.00V 0.00V 3.33V L L H H L
POWER SUPPLIES ON, Pull-up resistors OFF, Normal outputs (H=3.3v, L=GND)
MSB set: MOST sig bit first, Number of bits read/write: 8
a/A/@ controls AUX pin
SPI (spd ckp ske smp csl hiz)=( 4 0 1 0 1 0 )
*----------*
最後に
今回紹介したBusPirateでの読出は、仕事で用いる場合には最終手段として用いています。フラッシュメモリとのコンタクトの問題、およびBusPirate側の設定ミスにより、読み出した情報が正確でない場合があるためです。
以下のような場合を除き、弊社での診断時にはROMライタを使用して、フラッシュメモリとのコンタクト不良による作業ミスを防止しています。
- 適合するアダプタが無い、もしくは入手困難である
- アダプタは入手可能だが、配送期間を待てない
- アダプタの使用頻度と価格のバランスが悪い
- ROMライタでサポートされていない
一方で、ROMライタがサポートしていないデバイスもあるため、稀にどうしても自らの手で読み出す必要があります。このようなレアケースに備え、ハードウェア解析の担当者は自力で読み出す手法を試行錯誤しています。