UART and JTAG Debug Access

UART and JTAG Debug Access

TL;DR: UART gives you a console, JTAG gives you the CPU — find the pins with a multimeter and a logic analyser, then drive them with a USB adapter and openocd.

What it is

Most embedded devices leave debug headers on the board: UART for the boot/serial console and JTAG (or SWD on Cortex-M) for halt-mode CPU debugging. UART is the cheapest win — a working console often exposes a U-Boot prompt that lets you dump memory, boot from TFTP, or drop to a root shell. JTAG is heavier artillery: with openocd attached you can halt the CPU, read/write memory, set breakpoints, and single-step the kernel.

Preconditions / where it applies

  • Physical access to the PCB and the ability to solder or hold pogo pins
  • A 3.3V USB-UART adapter (FT232, CP2102, or TIGARD), and a JTAG probe (TIGARD, FT2232H, Segger J-Link, or Bus Blaster)
  • A logic analyser (Saleae Logic 8 or a cheap CY7C68013A clone) and a multimeter
  • Optional but huge: a JTAGulator for blind pin discovery on unknown headers

Technique

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 1. UART pin discovery with a multimeter
# - GND: continuity to the chassis / shield
# - VCC: steady 3.3V or 1.8V relative to GND
# - TX:  idles high (~3.3V) and dips during boot output
# - RX:  idles high but floats — pulled low only when host transmits

# 2. Baudrate hunt — capture TX with a logic analyser, measure the
#    shortest pulse width, then baud = 1 / pulse_width_seconds
# Common values: 9600, 38400, 57600, 115200, 921600
picocom -b 115200 /dev/ttyUSB0
# If output is garbled, try: 57600, 38400, 9600 — or use sigrok-cli + uart decoder

# 3. Catch the U-Boot autoboot countdown
#    Spam Enter / Ctrl-C / 'x' during the "Hit any key to stop autoboot" window
=> printenv
=> md.b 0x80000000 0x100        # memory dump
=> setenv bootargs 'init=/bin/sh console=ttyS0,115200'
=> boot
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 4. JTAG pin mapping with a JTAGulator
#    Connect all 24 channels to the candidate header, then run BYPASS scan
#    JTAGulator reports the TDI/TDO/TCK/TMS/TRST mapping and IDCODE

# 5. openocd config — example for an FT2232H probe and an ARM926EJ-S target
openocd -f interface/ftdi/tigard.cfg -f target/arm926ejs.cfg
# In another terminal:
telnet localhost 4444
> halt
> reg
> mdw 0x00000000 16
> load_image vmlinux 0x80008000

# 6. Single-step the kernel after attach
arm-none-eabi-gdb vmlinux
(gdb) target remote :3333
(gdb) hbreak start_kernel
(gdb) continue
(gdb) stepi

If JTAG is fused off but UART still works, the U-Boot console is often the path of least resistance — md and mw let you read and write physical memory before the kernel takes over.

Detection and defence

  • Production builds should strip the kernel console (CONFIG_PRINTK=n or quiet in bootargs) and disable the U-Boot autoboot key
  • JTAG can be permanently disabled by blowing the SoC’s debug-disable OTP fuse (TI JTAG_DIS, NXP JTAG_SECURITY)
  • Secure boot rooted in mask ROM stops a modified kernel from running even after a JTAG memory write — the next reset re-verifies signatures
  • Anti-tamper switches and conformal coating raise the cost of board access; epoxy on debug headers is a cheap deterrent
  • Glitch-resistant fuse readout (Intel CSME, NXP HABv4) prevents fuse-bypass via voltage / clock glitching

References

See also: firmware-extraction, executable-files-pe-elf.