~derf / interblag / entry / Avoiding accidental bricks of MSP430FR launchpads
dark mode

The MSP430FR launchpad series is a pretty nifty tool both for research and teaching. You get an ultra-low-power 16-bit microcontroller, persistent FRAM, and energy measurement capabilities, all for under $20.

Unfortunately, especially when it comes to teaching, there's one major drawback: Out of bound memory accesses which are off by several thousand bytes can permanently brick the CPU. This typically happens either due to a buffer overflow in FRAM or a stack pointer underflow (i.e., stack overflow) in SRAM.

This issue recently bit one of my students and it turns out that it could have been avoided. So I'll give a quick overview of symptoms, cause, and protection against it, both as a reference for myself and for others.

Symptoms

A bricked MSP430FR launchpad is no longer flashable or erasable via JTAG or BSL. Attempts to control it via MSP Flasher fail with error 16: "The Debug Interface to the device has been secured".

* -----/|-------------------------------------------------------------------- *
*     / |__                                                                   *
*    /_   /   MSP Flasher v1.3.20                                             *
*      | /                                                                    *
* -----|/-------------------------------------------------------------------- *
*
* Evaluating triggers...
* Invalid argument for -i trigger. Default used (USB).
* Checking for available FET debuggers:
* Found USB FET @ ttyACM0 <- Selected
* Initializing interface @ ttyACM0...done
* Checking firmware compatibility:
* FET firmware is up to date.
* Reading FW version...done
* Setting VCC to 3000 mV...done
* Accessing device...
# Exit: 16
# ERROR: The Debug Interface to the device has been secured
* Starting target code execution...done
* Disconnecting from device...done
*
* ----------------------------------------------------------------------------
* Driver      : closed (Internal error)
* ----------------------------------------------------------------------------
*/

Unless you know the exact memory pattern written by the buffer overflow (and it specifies a reasonable password length), there is no remedy I'm aware of. The CPU is permanently bricked.

Cause

MSP430FR CPUs use a unified memory architecture: Registers, volatile SRAM, and persistent FRAM are all part of the same address space. This includes fuses (“JTAG signatures”) used to secure the device by either disabling JTAG access altogether or protecting it with a user-defined password.

While write access to several CPU registers requires specific passwords and timing sequences to be observed, this is not the case for the JTAG signatures. Change them, reset the CPU, and it's game over.

The JTAG signatures reside next to the reset vector and interrupt vector at the 16-bit address boundary, within the address range from 0xff80 to 0xffff. On MSP430FR5994 CPUs, the (writable!) text segment ends at 0xff7f and SRAM is located in 0x1c00 to 0x3bff. So, a small buffer overflow in a persistent variable (located in FRAM) or a significant stack pointer underflow (starting in SRAM, growing down, and wrapping from 0x0000 to 0xffff) may overwrite the JTAG signatures with arbitrary data.

Protection

MSP430FR CPUs contain a bare-bones Memory Protection Unit. It can partition the address space into up to three distinct regions with 1kB granularity and enforce RWX settings for each region. So, if we disallow writes to the 1kB region from 0xfc00 to 0xffff, we no longer have to worry about accidentally overwriting the JTAG signatures. To do so, place the following lines in your startup code:

MPUCTL0 = MPUPW;
MPUSEGB2 = 0x1000; // memory address 0x10000
MPUSEGB1 = 0x0fc0; // memory address 0x0fc00
MPUSAM &= ~MPUSEG2WE; // disallow writes
MPUSAM |= MPUSEG2VS;  // reset CPU on violation
MPUCTL0 = MPUPW | MPUENA;
MPUCTL0_H = 0;

Note that this disallows writes not just to the JTAG signatures, but also to part of the text segment as well as the interrupt vector table. If an application dynamically alters interrupt vector table entries or uses persistent FRAM variables at addresses beyond 0xfbff, this method will break the application. Most practical use cases shouldn't run into this issue.