您好,登錄后才能下訂單哦!
最近在搞路由器的時候,不小心把CFE給刷掛了,然后發現能通過jtag進行救磚,所以就對jtag進行了一波研究。
最開始只是想救磚,并沒有想深入研究的想法。
變磚的路由器型號為:LinkSys wrt54g v8
CPU 型號為:BCM5354
Flash型號為:K8D6316UBM
首先通過jtagulator得到了設備上jtag接口的順序。
正好公司有一個jlink,但是參試了一波失敗,識別不了設備。
隨后通過Google搜到發現了一個工具叫: tjtag-pi
可以通樹莓派來控制jtag,隨后學習了一波樹莓派的操作。
我使用的是rpi3,其接口編號圖如下:
或者在樹莓派3中可以使用
gpio readall
查看各個接口的狀態:
rpi3中的Python有一個
RPi.GPIO
模塊,可以控制這些接口。
舉個例子:
>>> from RPi import GPIO >>> GPIO.setmode(GPIO.BCM)>>> GPIO.setup(2, GPIO.OUT)>>> GPIO.setup(3, GPIO.IN)
首先是需要進行初始化GPIO的模式,BCM模式對應的針腳排序是上面圖中橙色的部門。
然后可以對各個針腳進行單獨設置,比如上圖中,把2號針腳設置為輸出,3號針腳設置為輸入。
>>> GPIO.output(2, 1)>>> GPIO.output(2, 0)
使用output函數進行二進制輸出
>>> GPIO.input(3)1
使用input函數獲取針腳的輸入。
我們可以用線把兩個針腳連起來測試上面的代碼。
將樹莓派對應針腳和路由器的連起來以后,可以運行tjtag-pi程序。但是在運行的過程中卻遇到了問題,經常會卡在寫flash的時候。通過調整配置,有時是可以寫成功的,但是CFE并沒有被救回來,備份flash的數據,發現并沒有成功寫入數據。
因為使用輪子失敗,所以我只能自己嘗試研究和造輪子了。
首先是針腳,我見過的設備給jtag一般是提供了5 * 2以上的引腳。其中有一般都是接地引腳,另一半只要知道4個最重要的引腳。
這四個引腳一般情況下的排序是:
TDI TDO TMS TCK
TDI表示輸入,TDO表示輸出,TMS控制位,TCK時鐘輸入。
jtag大致架構如上圖所示,其中TAP-Controller的架構如下圖所示:
根據上面這兩個架構,對jtag的原理進行講解。
jtag的核心是TAP-Controller,通過解析TMS數據,來決定輸入和輸出的關系。所以我們先來看看TAP-Controller的架構。
從上面的圖中我們可以發現,在任何狀態下,輸出5次1,都會回到
TEST LOGIC RESET
狀態下。所以在使用jtag前,我們先通過TMS端口,發送5次為1的數據,jtag的狀態機將會進入到RESET的復原狀態。
當TAP進入到
SHIFT-IR
的狀態時,
Instruction Register
將會開始接收TDI傳入的數據,當輸入結束后,進入到
UPDATE-IR
狀態時將會解析指令寄存器的值,隨后決定輸出什么數據。
SHIFT-DR
則是控制數據寄存器,一般是在讀寫數據的時候需要使用。
講到這里,就出現一個問題了,TMS就一個端口,jtag如何知道TMS每次輸入的值是多少呢?這個時候就需要用到TCK端口了,該端口可以稱為時鐘指令。當TCK從低頻變到高頻時,獲取一比特TMS/TDI輸入,TDO輸出1比特。
比如我們讓TAP進行一次復位操作:
for x in range(5): TCK 0 TMS 1 TCK 1
再比如,我們需要給指令寄存器傳入0b10:
1.復位
2.進入RUN-TEST/IDLE狀態
TCK 0 TMS 0 TCK 1
3.進入SELECT-DR-SCAN狀態
TCK 0 TMS 1 TCK 1
4.進入SELECT-IR-SCAN狀態
TCK 0 TMS 1 TCK 1
5.進入CAPTURE-IR狀態
TCK 0 TMS 0 TCK 1
6.進入SHIFT-IR狀態
TCK 0 TMS 0 TCK 1
7.輸入0b10
TCK 0 TMS 0 TDI 0 TCK 1 TCK 0 TMS 1 TDI 1 TCK 0
隨后就是進入
EXIT-IR -> UPDATE-IR
根據上面的理論我們就可以通過寫一個設置IR的函數:
def clock(tms, tdi): tms = 1 if tms else 0 tdi = 1 if tdi else 0 GPIO.output(TCK, 0) GPIO.output(TMS, tms) GPIO.output(TDI, tdi) GPIO.output(TCK, 1) return GPIO.input(TDO)def reset(): clock(1, 0) clock(1, 0) clock(1, 0) clock(1, 0) clock(1, 0) clock(0, 0)def set_instr(instr): clock(1, 0) clock(1, 0) clock(0, 0) clock(0, 0) for i in range(INSTR_LENGTH): clock(i==(INSTR_LENGTH - 1), (instr>>i)&1) clock(1, 0) clock(0, 0)
把上面的代碼理解清楚后,基本就理解了TAP的邏輯。接下來就是指令的問題了,指令寄存器的長度是多少?指令寄存器的值為多少時是有意義的?
不同的CPU對于上面的答案都不一樣,通過我在網上搜索的結果,每個CPU應該都有一個bsd(boundary scan description)文件。本篇文章研究的CPU型號是
BCM5354
,但是我并沒有在網上找到該型號CPU的bsd文件。我只能找了一個相同廠商不同型號的CPU的bsd文件進行參考。
bcm53101m.bsd
在該文件中我們能看到jtag端口在cpu端口的位置:
"tck : B46 , " & "tdi : A57 , " & "tdo : B47 , " & "tms : A58 , " & "trst_b : A59 , " & attribute TAP_SCAN_RESET of trst_b : signal is true; attribute TAP_SCAN_IN of tdi : signal is true; attribute TAP_SCAN_MODE of tms : signal is true; attribute TAP_SCAN_OUT of tdo : signal is true; attribute TAP_SCAN_CLOCK of tck : signal is (2.5000000000000000000e+07, BOTH);
能找到指令長度的定義:
attribute INSTRUCTION_LENGTH of top: entity is 32;
能找到指令寄存器的有效值:
attribute INSTRUCTION_OPCODE of top: entity is "IDCODE (11111111111111111111111111111110)," & "BYPASS (00000000000000000000000000000000, 11111111111111111111111111111111)," & "EXTEST (11111111111111111111111111101000)," & "SAMPLE (11111111111111111111111111111000)," & "PRELOAD (11111111111111111111111111111000)," & "HIGHZ (11111111111111111111111111001111)," & "CLAMP (11111111111111111111111111101111) " ;
當指令寄存器的值為
IDCODE
的時候,IDCODE寄存器的輸出通道開啟,我們來看看IDCODE寄存器:
attribute IDCODE_REGISTER of top: entity is "0000" & -- version "0000000011011111" & -- part number "00101111111" & -- manufacturer's identity "1"; -- required by 1149.1
從這里我們能看出IDCODE寄存器的固定輸出為:
0b00000000000011011111001011111111
那我們怎么獲取TDO的輸出呢?這個時候數據寄存器DR就發揮作用了。
用代碼形式的表示如下:
def ReadWriteData(data): out_data = 0 clock(1, 0) clock(0, 0) clock(0, 0) for i in range(32): out_bit = clock((i == 31), ((data >> i) & 1)) out_data = out_data | (out_bit << i) clock(1,0) clock(0,0) return out_datadef ReadData(): return ReadWriteData(0)def WriteData(data): ReadWriteData(data)def idcode(): set_instr(INSTR_IDCODE) print(hex(self.ReadData()))
因為我也是個初學者,邊界掃描描述文件中的內容并不是都能看得懂,比如在邊界掃描文件中并不能看出BYPASS指令是做什么的。但是在其他文檔中,得知BYPASS寄存器一般是用來做測試的,在該寄存器中,輸入和輸出是直連,可以通過比較輸入和輸出的值,來判斷端口是否連接正確。
另外還有邊界掃描寄存器一大堆數據,也沒完全研究透,相關的資料少的可憐。而且也找不到對應CPU的文檔。
當研究到這里的時候,我只了解了jtag的基本原理,只會使用兩個基本的指令(IDCODE, BYPASS)。但是對我修磚沒任何幫助。
沒辦法,我又回頭來看tjtag的源碼,在tjtag中定義了幾個指令寄存器的OPCODE:
INSTR_ADDRESS = 0x08INSTR_DATA = 0x09INSTR_CONTROL = 0x0A
照抄著tjtag中flash AMD的操作,可以成功對flash進行擦除,寫入操作讀取操作。但是卻不知其原理。
這里分享下我的腳本: jtag.py
flash 文檔: https://www.dataman.com/media/datasheet/Samsung/K8D6x16UTM_K8D6x16UBM_rev16.pdf
接下來將會對該flash 文檔進行研究,并在之后的文章中分享我后續的研究成果。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。