Author:Hcamael@Zhidao Chuangyu 404 Laboratory
Recently, while working on a router, I accidentally bricked the CFE and found that I could recover it via JTAG, so I conducted some research on JTAG.
Initially, I just wanted to recover the device and didn’t intend to delve deeper into the research.
Recovery Attempts
The bricked router model is:LinkSys wrt54g v8
CPU model:BCM5354
Flash model:K8D6316UBM
First, I obtained the pinout for the JTAG interface on the device using JTAGulator.
Fortunately, the company had a J-Link, but attempts to use it failed as it couldn’t recognize the device.
Then I found a tool called:tjtag-pi
which allows control of JTAG via Raspberry Pi, after which I learned how to operate the Raspberry Pi.
Raspberry Pi Pins
I used the RPi3, and the pinout diagram is as follows:
Alternatively, you can check the status of each pin in Raspberry Pi 3 using gpio readall:
The RPi3 has a Python module called RPi.GPIO to control these pins.
For example:
>>> from RPi import GPIO>>> GPIO.setmode(GPIO.BCM)>>> GPIO.setup(2, GPIO.OUT)>>> GPIO.setup(3, GPIO.IN)
First, you need to initialize the GPIO mode, where the BCM mode corresponds to the pin arrangement shown in orange in the above diagram.
Then you can configure each pin individually; for example, in the above diagram, set pin 2 as output and pin 3 as input.
>>> GPIO.output(2, 1)>>> GPIO.output(2, 0)
Use the output function for binary output.
>>> GPIO.input(3)1
Use the input function to get the input from the pin.
We can connect the two pins with a wire to test the code above.
After connecting the corresponding pins of the Raspberry Pi to the router, you can run the tjtag-pi program.However, during the execution, I encountered issues where it often got stuck while writing to flash.By adjusting the configuration, it sometimes succeeded, but the CFE was not recovered, and checking the backup flash data revealed that the data was not successfully written.
Since using existing tools failed, I had to try to research and create my own solutions.
JTAG
First, regarding the pins, devices I have seen generally provide more than 5 * 2 JTAG pins.Among these, there are usually some ground pins, and the other half only needs to know the four most important pins.
The typical order of these four pins is:
TDI
TDO
TMS
TCK
TDI represents input, TDO represents output, TMS is the control bit, and TCK is the clock input.
The basic structure of JTAG is shown in the above image, and the architecture of the TAP-Controller is shown in the following image:
Based on these two structures, let’s explain the principles of JTAG.
The core of JTAG is the TAP-Controller, which determines the input-output relationship by parsing TMS data.So first, let’s look at the architecture of the TAP-Controller.
From the above diagram, we can see that in any state, outputting five 1s will return to the TEST LOGIC RESET state.Thus, before using JTAG, we first send five 1s through the TMS port, and the JTAG state machine will enter the RESET recovery state.
When TAP enters the SHIFT-IR state, the Instruction Register will start receiving data from TDI; after the input ends and it enters the UPDATE-IR state, it will parse the value of the instruction register and then decide what data to output.
SHIFT-DR controls the data register, which is generally used during read-write operations.
At this point, a question arises: there is only one TMS port, how does JTAG know the value of TMS input each time?This is where the TCK port comes into play, which can be referred to as the clock signal.When TCK transitions from low to high frequency, it captures one bit of TMS/TDI input and outputs one bit of TDO.
For example, if we want to perform a reset operation with TAP:
for x in range(5): TCK 0 TMS 1 TCK 1
For instance, if we need to input 0b10 into the instruction register:
1. Reset
2. Enter RUN-TEST/IDLE state
TCK 0TMS 0TCK 1
3. Enter SELECT-DR-SCAN state
TCK 0TMS 1TCK 1
4. Enter SELECT-IR-SCAN state
TCK 0TMS 1TCK 1
5. Enter CAPTURE-IR state
TCK 0TMS 0TCK 1
6. Enter SHIFT-IR state
TCK 0TMS 0TCK 1
7. Input 0b10
TCK 0TMS 0TDI 0TCK 1TCK 0TMS 1TDI 1TCK 0
Then we move to EXIT-IR -> UPDATE-IR.
Based on the theory above, we can write a function to set the 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)
Once you understand the above code, you essentially grasp the logic of TAP.Next comes the question of instructions: what is the length of the instruction register?What values are meaningful for the instruction register?
Different CPUs have different answers to the above questions; based on my online research, each CPU should have a bsd (boundary scan description) file.The CPU model studied in this article is BCM5354, but I couldn’t find the bsd file for this model online.I could only find a bsd file for a different model from the same manufacturer for reference.
bcm53101m.bsd
In this file, we can see the location of the JTAG port on the CPU port:
"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);
We can find the definition of the instruction length:
attribute INSTRUCTION_LENGTH of top: entity is 32;
We can also find the valid values for the instruction register:
attribute INSTRUCTION_OPCODE of top: entity is "IDCODE (11111111111111111111111111111110)," & "BYPASS (00000000000000000000000000000000, 11111111111111111111111111111111)," & "EXTEST (11111111111111111111111111101000)," & "SAMPLE (11111111111111111111111111111000)," & "PRELOAD (11111111111111111111111111111000)," & "HIGHZ (11111111111111111111111111001111)," & "CLAMP (11111111111111111111111111101111) ";
When the value of the instruction register is IDCODE, the output channel of the IDCODE register is enabled; let’s take a look at the IDCODE register:
attribute IDCODE_REGISTER of top: entity is "0000" & -- version "0000000011011111" & -- part number "00101111111" & -- manufacturer's identity "1"; -- required by 1149.1
From here we can see that the fixed output of the IDCODE register is: 0b00000000000011011111001011111111. How do we obtain the TDO output? This is where the data register DR comes into play. The TAP state machine switches to SHIFT-IR to output IDCODE to the IR and then switches to SHIFT-DR to obtain the TDO output value of INSTRUCTION_LENGTH length. The exit can be represented in code form as:
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_data
def ReadData(): return ReadWriteData(0)
def WriteData(data): ReadWriteData(data)
def idcode(): set_instr(INSTR_IDCODE) print(hex(self.ReadData()))
INSTR_ADDRESS = 0x08
INSTR_DATA = 0x09
INSTR_CONTROL = 0x0A