A Relic Reloaded
For more than 30 years, JTAG has been a method of interacting with the digital I/O pins on devices that has been baked into most MCUs and FPGAs you use. JTAG is classically used for testing production boards, but it also presents a powerful reverse engineering tool. In this article, Colin discusses how to drive a JTAG boundary scan chain from Python, using open-source tools.
In many of my articles I try to bring you some of the most recent embedded security work from the past few years. But in this article, I’m coming back to something older—something that’s even a year older than me—and that some of you may have used years ago and forgotten about. I’m talking about JTAG—that ubiquitous interface that most often comes up as a way to debug devices, and, from a security perspective, is a giant bullseye an attacker will use to read memory and code out of your device.
All that said, this article isn’t even about the usage of JTAG as a debug interface. Instead, I’m going all the way back to the initial purpose of JTAG, which was a way to toggle bits on a board as part of automated testing. This feature came up a few times for me, because we needed ways here at NewAE Technology to perform some simple testing of assembled boards, and the JTAG interface has the potential to provide this. It also turns out to be a wonderful tool for security analysis.
If you’re not familiar with how JTAG works, a basic summary is shown in Figure 1. Devices have a scan chain that is effectively a big shift register, and on each I/O pin of the device you have the ability to read the status of the device pin, or to set the pin to a certain state. This is called a “boundary scan” because you are simply probing the outer pins of the device itself. Nothing the JTAG chain has access to is a secret—you could get all the same information with an oscilloscope and small probe on the device pins.
But this gets interesting when you consider the fact that many devices are very difficult to probe—the small wafer level chip scale package (WLCSP) devices have pitches of 0.4mm or smaller. And if the board uses blind vias, you’ll have no easy way to access signals. The JTAG boundary scan is a bit of magic that gives you total access to the device pins without difficult soldering. It also means you can do tasks such as dumping the contents of a memory chip by simply driving the required SPI signals out the I/O pins of the microcontroller (MCU).
REVERSE ENGINEERING
Because JTAG boundary scan doesn’t allow access to secret information, the boundary scan mode often survives the debug disable fuses. This means that even on a fully secure device you might find that boundary scan is still enabled—a nice “feature” for reverse engineering of a PCB or system. It’s also a handy feature during your production or development. I’m going to be using it for checking soldering connections, but you can use it for tasks such as programming attached SPI flash chips (attached to any pin of the device).
Programming of SPI flash chips is especially useful when the device boots off SPI flash, and you need to rely on some magic bootloader for the initial programming (which may or may not support the SPI flash chip you’ve attached). With a JTAG interface, once you have a working JTAG chain and SPI programming script on one device, it should work on almost any other device you try it on. The main downside is it’s relatively slow—it’s bit banging the SPI interface over another serial interface.
— ADVERTISMENT—
—Advertise Here—
Despite this incredible power, and the relatively old age of JTAG boundary scan, I found tools were surprisingly expensive and inaccessible. If you are used to using JTAG for debug purposes you’ll know there is a wide range of options for JTAG tooling—from higher-end commercial tools such as from Segger Microcontroller (including the hardware and software), to open-source software tools such as OpenOCD and low-cost hardware based on FTDI USB chips.
For this reason, I decided to detail for you how I was able to get an open-source JTAG boundary scan setup to successfully work with my standard Python tooling, and hopefully I can prevent you from taking a number of the same dead-ends that I went down in trying to get this solution working.
JTAG HARDWARE TOOLS
To actually talk to your hardware device, you’ll need some sort of JTAG interface. I re-used my Segger J-Link tool, which is my preferred tool for working with Arm MCUs during development, so I already had a few of the units here. If you want to pick up a Segger tool, you can follow along even with their low-cost Segger EDU. Normally the different Segger units have different host licenses and features. Since we are only using the physical interface, and none of the Segger host software, we don’t need to worry about the host software side. The Segger EDU hardware is strictly for non-commercial use however—I’m even using several of the fully licensed Segger tools in writing this article!
The other option in JTAG interface is FTDI-based solutions, normally using the FT2232H chip from FTDI Chip that offers a high-speed SPI/JTAG/serial protocol engine. A recent tool in this area is the Tigard (shown in Figure 2), which has the advantage of voltage translation to work with any target system, and as a nice bonus, this translator has a selector switch for a forced output level. This is useful when you are just connecting to test points on the PCB—it saves you the extra connection, which would be solely to drive the voltage translator.
Another worthwhile tool is the JTAGulator by Joe Grand. This tool is fully self-contained and doesn’t require a software library to drive it. It has extra features like an ability to discover unknown JTAG connector pinouts. It supports a mode to toggle the I/O pins on the target device using the JTAG modes I’ll discuss here.
BSDL
The giant shift register that is part of the JTAG chain of every device needs a description to be useful. Most likely you want to just toggle a specific pin on the package, be it a package pin number or a MCU pin name (such as PORTA11). This information is communication in a file format called Boundary Scan Description Language (BSDL), which is based on VHDL and describes each pin on the scan chain along with the input or output capability of it.
You’ll find BSDL files available from most (but not all) vendors. Typically, these are widely available from FPGA vendors, and available from some MCU vendors. For example, you can find BSDL files from STMicroelectronics (ST) under their “CAD Resources” section of each product page (at least for the STM32 series devices I am using). But looking at another device I was testing, the NXP MPC5676R showed that boundary scan files weren’t posted on the website, but were available on request, even via their public support forum, so no special NDA or relationship was required.
JTAG GUI—PIN & PACKAGE VIEW
If you explore JTAG software tools, one tool that comes up is called TopJTAG, and is relatively low cost with a $100 price tag. This tool has a Windows-only GUI, but it does a good job of parsing the BSDL files and showing the package pinout. An example of this on a tiny WLCSP device (the one from my article “IC Packaging and Physical Security: Do They Affect One Another?” in Circuit Cellar 362, September 2020 [1]) is shown in Figure 3.
This WLCSP package was actually the original reason I began exploring this JTAG testing. We needed a way to validate small-scale production boards, without the cost of X-ray inspection. For this purpose, we used the setup in Figure 4—the small blue daughterboard in the center is the target of the test. The red carrier board (we call our UFO board) provides power and connections to the board. The NewAE ChipWhisperer-Lite (black board at bottom) is used to set up clock and communicate with the target during regular usage.
— ADVERTISMENT—
—Advertise Here—
For this test, the FPGA on the ChipWhisperer-Lite is configured to be part of the same JTAG scan chain as the target device. The JTAG controller can then set each pin on the target chip as an output, and toggle that pin high and low while setting all other pins as inputs. By observing for pins toggling on the ChipWhisperer-Lite, we can confirm the solder connections on the board (and also look for unintended connections caused by solder bridges). A block diagram of this photo is shown in Figure 5. While this is all great—it would imply manually setting each pin high through the GUI (or some basic scripting). Instead let’s look at how we could set a pin high and low programmatically, via a Python library.
THE JTAG SCRIPT
Using JTAG boundary scan with scripting is also old news—but the problem is many of these languages and tools were created long before the prevalence of Python and other modern scripting languages become popular.
For JTAG you will often see the Serial Vector Format (SVF) files describe, which allow you to output various patterns while also having certain expected inputs you will validate. For the purpose I described (checking electrical connectivity), it would work great. But then we need to support another scripting language, and if we want live interaction between our JTAG boundary scan and other tools, it becomes even more of a mess. For reverse engineering we are certain to want control over external tools, so I’d would prefer to use a modern scripting language like Python.
Using JTAG boundary scan directly from Python means we could set pins high or low, while running tests on other software tools that depend on the state of such pins. It also means we can skip over needing the SVF file format—because we use Python extensively in our other projects at the company that means no one person needs to be the SVF wizard, and everyone can easily modify or fix our test program.
To accomplish this, I’ll first introduce the Viveris JTAG Boundary Scanner library [2]. This is a more recently released library, that is compiled in C and also includes a Windows GUI. This Windows GUI offers many interesting features, such as the ability to send commands to an attached SPI or I2C device. For hardware, it supports either FTDI FT2232-based cables, or a Segger J-Link device.
THE JTAG SNAKE
The Viveris JTAG Boundary Scanner library is written in C and compiles to a library for your system. So, to interface it with Python, I started a ctypes project called “pyjtagbs” (for Python JTAG Boundary Scan) that you can find on GitHub [3]. This library also works with a native Python implementation of JTAG (which is slower), but either work to give you basic boundary scan “magic.”
For the example setup of Figure 4 and Figure 5, the code from Listing 1 shows connecting to a scan chain and printing the detected devices. This will actually print three devices even though there are only two physically on the board—the STM32 chip has both the boundary scan and Debug core present. To use this scan chain, we load information about the boundary scan device and Arm debug core, as shown in Listing 2.
Listing 1
Connecting to a scan chain and enumerating detected devices
from jtagbs import jtagbs
from jtagbs.viveris import jtagcore
interface = jtagcore.JTAGCore()
jtag = jtagbs.JTAGBS(interface)
probes = jtag.list_available_probes()
jtag.open_probe('JLINK')
jtag.init_scanchain()
devices = jtag.list_devices()
for i, dev in enumerate(devices):
print("Device {} is from {}".format(i, dev['manufacturer']))
Listing 2
Assigning BSDL files to each of the 3 devices/cores.
s6lx9 = jtag.get_device(0)
arm = jtag.get_device(1)
stm32 = jtag.get_device(2)
s6lx9.set_bsdl(r"../bsdl_files/xilinx/xc6slx9l_tqg144.bsd")
arm.set_bsdl(r"../bsdl_files/st/stm32f4/CortexMx.bsd")
stm32.set_bsdl(r"../bsdl_files/st/stm32f4/STM32F405_415_407_417_LQFP64.bsd")
One important note is that it needs information on each device loaded—ST provides a “dummy” file for the Arm debug interface when using it in scan chain mode. It’s important to load this file, otherwise some of the pins for other devices in the scan tool won’t be read correctly depending on the mode it is set to. Remember you are just constructing a large shift register, so at the lower layer all the tool is doing is setting a specific bit in the scan chain. It needs to understand what is present in the scan chain for our mapping of the name of the bit to the actual bit to be accurate.
With the BSDL file loaded, it’s now possible to set and clear pin names by more human friendly names. For the STM32 this means using the normal name of the GPIO pins—for example PC12. For the FPGA this means using the physical pin numbers (which are what is normally used in the FPGA constraints file). The format used in the BSDL file is IO_P81, which means pin 81 on the TQFP-144 package of the Xilinx Spartan 6 LX9 FPGA on my board.
WALKING MY BIT
To demonstrate a simple “walking ‘1’s” test that sets each I/O line to “1” in sequence, see Listing 3. I’ve done a “lazy mapping” where I’ve simply listed each pin on the STM32 and listed each pin on the Spartan 6 LX9 that could be interconnected. The actual interconnection will depend on my PCB layout—some pins on the headers are actually constant voltages (always “1” or “0”) or used for bootstrapping and have pull-ups present (so read as “1” but could be set to “0”).
Listing 3
Toggling a pin on the STM32 chip and watching the result on the S6LX9 chip
s6lx9testpins = [80, 81, 82, 83, 84, 85, 87, 88, 92, 93, 94, 95, 111, 112, 114, 115, 116, 117, 118, 119, 120]
s6lx9testpins = ['IO_P%d'%p for p in s6lx9testpins]
stm32testpins = ['PA9', 'PA10', 'PA11', 'PA12', 'PB13', 'PB14', 'PB15', 'PB12', 'PC0', 'PC1', 'PC2', 'PC3']
s6lx9.set_scan_mode('passive')
stm32.set_scan_mode('active')
base_pin_state = s6lx9.get_pin_state(s6lx9testpins)
for p in stm32testpins:
stm32.set_pin_state(stm32testpins, ['high-z']*len(stm32testpins))
stm32.set_pin_state(p, 'high')
new_pin_state = s6lx9.get_pin_state(s6lx9testpins)
print(new_pin_state)
stm32.set_pin_state(stm32testpins, ['high-z']*len(stm32testpins))
The output in Listing 4 shows a series of rows of 1s and 0s, each row corresponding to the state of each of the FPGA pins listed in the s6lx9testpins
variable of Listing 3. For most of the rows a single bit change is present between the previous row. Note there are some constant “1” bits as mentioned, due to the headers having constant voltages on some of the pins. But you can see that toggling a single bit on the STM32 results in a bit toggling on the FPGA. With this we can then build a full test setup, by checking for the expected bit only going high, and also setting bits low to test lines with pull-ups (which show as 1 either way).
Listing 4
The output of Listing 3, showing different pins on the S6LX9 chip going to “1” during the test
000000110010010100000
000010110010010100000
001000110010010100000
100000110010010100000
000001100010010100000
000000101010010100000
000000110010010100000
010000110010010100000
000000110010010110000
000000110010010101000
000000110010010100100
000000110010010100010
This same setup can also help with figuring out connections between chips—we can simply toggle every pin and see if it toggles on another chip. Of course, this requires some care on a powered board—if you are testing a motor driver for example you could get into invalid states, such as setting the high-side and low-side of a MOSFET to “on” at the same time.
But this unique capability has a lot of potential for both regular and reverse engineers. You can use JTAG boundary scan to read the state of I/O pins during initial board debugging (no LED required), and you can check solder joints and other features of your board without even needing to touch a compiler. This is great for checking early prototypes when you are trying to debug both the soldering, the code and the PCB layout all at once. I’m going to be continuing to improve my pyjtagbs project, and I hope you find it a useful tool for your board bring up or reverse engineering work.
RESOURCES
References:
[1] “IC Packaging and Physical Security: Do They Affect One Another?” in Circuit Cellar 362, September 2020
[2] Viveris JTAG Boundary Scanner library (https://github.com/viveris/jtag-boundary-scanner
[3] pyjtagbs (Python JTAG Boundary Scan) project on GITHub https://github.com/colinoflynn/pyjtagbs
— ADVERTISMENT—
—Advertise Here—
FTDI Chip | www.ftdichip.com
NewAE Technology | www.newae.com
OpenOCD | www.openocd.org
SEGGER Microcontroller | www.segger.com
STMicroelectronics | www.st.com
TopJTAG | www.topjtag.com
Xilinx | www.xilinx.com
PUBLISHED IN CIRCUIT CELLAR MAGAZINE • MAY 2021 #370 – Get a PDF of the issue
Sponsor this ArticleColin O’Flynn has been building and breaking electronic devices for many years. He is an assistant professor at Dalhousie University, and also CTO of NewAE Technology both based in Halifax, NS, Canada. Some of his work is posted on his website (see link above).