STM32L552ZE — -rdu at RDP Level 0 with TZEN=1 leaves the device with secure debug permanently disabled
On an STM32L552ZE with TZEN=1, RDP=0xAA (Level 0) and BOOT_LOCK=1, the STM32CubeProgrammer command -rdu reported success while explicitly stating that no option byte was modified. From that command onward the device is permanently inaccessible: DAUTHSTATUS shows secure invasive and non-invasive debug disabled, every FLASH register (secure and non-secure alias) fails even on read, and the documented RDP regression path (RDP=0xBB → RDP=0xAA TZEN=0x0) can no longer be executed because option byte programming requires those registers.
We would like to understand whether this is expected behaviour, and whether any recovery path exists.
Hardware and tools
| Item | Value |
|---|---|
| Device | STM32L552ZE-Q on NUCLEO-L552ZE-Q |
| Device ID | 0x472, Revision ID Rev Z (DBGMCU_IDCODE = 0x20016472) |
| ST-LINK | onboard, firmware V2J48M35 (also reproduced with V2J47M34) |
| STM32CubeProgrammer | 2.23.0 (also reproduced with 2.22.0) |
| Supply | 3.26–3.27 V, separate USB port |
| BOOT0 (PH3) | tied to VCC during all operations described below |
Option byte state before the failing command
Read successfully via the secure alias 0x50022040 (40 bytes):
RDP : 0xAA (Level 0, no protection)
TZEN : 0x1
BOOT_LOCK : 0x1
HDP1EN : 0x1 HDP1_PEND : 0x22
SECWM1_PSTRT : 0x0 SECWM1_PEND : 0x7F
SECWM2_PSTRT : 0x7F SECWM2_PEND : 0x0
IWDG_SW : 0x0 (hardware independent watchdog)
nSWBOOT0 : 0x1 nBOOT0 : 0x1Flash was empty at this point (a previous -e all reported Mass erase successfully achieved). With IWDG_SW=0 and no firmware to refresh the watchdog, the device resets roughly every 512 ms. With BOOT_LOCK=1 the BOOT0 pin has no effect — BL Version in the CubeProgrammer banner changed from 0x92 (ROM bootloader running) to 0x0 once BOOT_LOCK was set.
Sequence
All commands used -c port=SWD mode=HotPlug, BOOT0 tied to VCC, full power cycle between steps.
Step 1 — attempt to escalate RDP as the first half of the documented regression:
STM32_Programmer_CLI.exe -c port=SWD mode=HotPlug -ob RDP=0xBBResult: programming was attempted, verification failed. Option bytes remained readable via 0x50022040 afterwards.
Error: Expected value for Option Byte "rdp": 0xBB, found: 0xAA
Error: Option Byte Programming failed Or modified by application after OB_LAUNCHStep 2 — readout unprotect:
STM32_Programmer_CLI.exe -c port=SWD mode=HotPlug -rduResult: reported as successful, with an explicit statement that nothing was changed:
Warning: Option Byte: rdp, value: 0xAA, was not modified.
Warning: Option Bytes are unchanged, Data won't be downloadedFrom this point on the device is inaccessible. In all subsequent invocations CubeProgrammer switches from the secure option byte alias 0x50022040 (40 bytes) to the non-secure alias 0x40022040 (32 bytes) and fails. This transition is visible across our logs: 12 consecutive runs before Step 2 used 0x50022040 exclusively; every run after it uses 0x40022040.
Resulting state — register level evidence
Reads that succeed (non-secure address space, debug domain):
| Address | Register | Value |
|---|---|---|
0xE0044000 | DBGMCU_IDCODE | 0x20016472 |
0xE0044008 | DBGMCU_APB1FZR1 | writable — wrote 0x00001000, reads back 0x00001000 |
0xE000EDFC | DEMCR | writable — wrote 0x00000001, reads back 0x00000001 |
0xE000EDF0 | DHCSR | 0x03080000 → S_LOCKUP=1, S_RESET_ST=1, C_DEBUGEN=0 |
0xE000EFB8 | DAUTHSTATUS | 0x000000AF |
DAUTHSTATUS = 0xAF decodes to:
| Field | Bits | Value | Meaning |
|---|---|---|---|
NSID | [1:0] | 0b11 | non-secure invasive debug enabled |
NSNID | [3:2] | 0b11 | non-secure non-invasive debug enabled |
SID | [5:4] | 0b10 | secure invasive debug disabled |
SNID | [7:6] | 0b10 | secure non-invasive debug disabled |
Reads that fail with Error: failed to read the requested memory content:
| Address | Register |
|---|---|
0x50022040 | FLASH_OPTR, secure alias |
0x40022040 | FLASH_OPTR, non-secure alias |
0x5002204C | FLASH_SECBOOTADD0R (contains BOOT_LOCK) |
Command level failures:
-ob displ -> Error: Uploading Option Bytes bank: 0 failed
Error: Initializing the Option Bytes failed
-e all -> Error: Mass Erase : Config SAU Failed !
mode=UR -> Error: ST-LINK error (DEV_TARGET_NOT_HALTED)
mode=UR --hardRst -> Error: ST-LINK error (DEV_TARGET_NOT_HALTED)
mode=Normal -> Error: ST-LINK error (DEV_TARGET_NOT_HALTED)
mode=Powerdown -> Error: ST-LINK error (DEV_TARGET_NOT_HALTED)DEV_TARGET_NOT_HALTED is consistent with SID being disabled: the core is in secure lockup (S_LOCKUP=1, forced by BOOT_LOCK=1 booting into erased flash), and halting a core executing in secure state requires secure invasive debug.
We also armed DEMCR.VC_CORERESET together with DBGMCU_APB1FZR1.DBG_IWDG_STOP to catch the core at the reset vector and freeze the watchdog. Both bits read back correctly and persist across the watchdog resets, but the vector catch never takes effect because DHCSR.C_DEBUGEN cannot be made to stick — consistent with the same SID restriction.
Why we believe this is not the documented behaviour
RDP Level 1 does not normally block option byte programming — that is precisely why the RDP=0xBB → RDP=0xAA TZEN=0x0 regression is the documented way out of TrustZone, and we have used it successfully many times on this same board type. Here the FLASH registers cannot even be read, which is a stronger restriction than Level 1.
Two readings seem possible and we cannot distinguish them, because FLASH_OPTR is unreadable:
- RDP is now Level 2 — which we never requested; no command in the sequence writes
0xCC. -rduinitiated an RSS-based TZEN/RDP regression that did not complete, leaving the device in a state where secure debug is closed while RDP is not at a level from which regression is offered.
In either case, the command that caused it reported success and simultaneously reported that no option byte had been modified.
Questions
- What does
-rdudo on an STM32L552 withTZEN=1andRDP=0xAA, given that it reportsOption Bytes are unchanged? Is it expected to alter the debug authentication state? - Is there any supported recovery for a device with
DAUTHSTATUSshowingSID/SNIDdisabled andFLASH_OPTRunreadable — for example a specific RSS entry sequence, or a factory procedure? - Should
-rdube rejected, or should it warn, when the device is already at RDP Level 0? Running it there has no useful effect by its own report, yet it appears to be destructive. - For product design: with
IWDG_SW=0(hardware watchdog) andBOOT_LOCK=1, a device whose firmware stops refreshing the watchdog resets every ~512 ms and BOOT0 has no effect, so no programmer operation longer than that window can complete. Is there a recommended recovery route for this combination, which we intend to use in production?
