pyocd / pyocd/pyOCD

Recent ST-Link firmwares (J33,J34,J35) cannot access APs with APSEL>0

Open
#775 7 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

fyi probe
Dominant language
Python
Stars
1.5k
Forks
560
Avg merge
1h 41m
Merged PRs (30d)
3

Description

I am enabling APPROTECT (lockout main AP function in nrf52) in a production programming setup using pyOCD:

from pyocd.core.helpers import ConnectHelper
from pyocd.core.memory_map import FlashRegion
from pyocd.flash.flash import Flash

with ConnectHelper.session_with_chosen_probe(target_override="nrf52") as session:
    target = session.board.target
    target.reset_and_halt()
    fl = FlashLoader(session)
    mmap = target.memory_map

    uicr2 = FlashRegion(start=0x10001200, end=0x1000120f, blocksize=0x10, is_testable=False)
    uicr2.flash = Flash(target, mmap.get_boot_memory().algo)
    uicr2.flash.region = uicr2
    mmap.add_region(uicr2)
    fl.add_data(APPROTECT_REG, b'\x00')
    fl.commit()

It's a little dance to dynamically add the second page (?) of the UICR to the memory map. I wasn't sure how to upstream this (poorly documented) memory region. It's not clear if it is a second page / can be erased separately, why the address gap with the rest of the UICR, what happens if pyOCD tries to write/erase the gap, &c. This hack works well enough for a write once operation.

I also added a way to check if the chip is locked out:

def nrf52_locked():
    session = start_uninit_session()
    target = session.board.target
    locked = not target.dp.read_ap(1 << 24 | 0xc)
    session.close()
    return locked

and several functions that should work while the nrf52 is locked (via CTRL-AP @1):

def recover():
    session = start_uninit_session()
    target = session.board.target
    target.dp.init()
    target.dp.power_up_debug()
    target.dp.write_ap(1 << 24 | 4, 1)
    while target.dp.read_ap(1 << 24 | 8):
        sleep(0.1)
    _ctrl_ap_reset(session)
    session.close()

def _ctrl_ap_reset(session=None):
    if not session:
        session = start_uninit_session()
    target = session.board.target
    target.dp.write_ap(1 << 24 | 0, 1)
    sleep(0.001)
    target.dp.write_ap(1 << 24 | 0, 0)

This works as intended with ST-Link v2 firmware 2J29S7, but with later firmwares (including all of those still available for download from ST (2J33xx, 2J34xx, 2J35xx) have stopped working.

The ST-Link has started returning JTAG_NO_DEVICE_CONNECTED when running either JTAG_WRITE_DAP_REG or JTAG_READ_DAP_REG even when the address is on another unlocked AP.

I looked into the pyOCD probe driver, but the ST-Link protocol is very high level. I can't imagine there's a way to work around this. It's still submitting the correct commands to the probe... I think it's a regression in the firmware; but I don't know how to proceed (I'm not a big ST customer).

It's currently sufficient for us to remain at 2J29S7 for our v2 probes, but v3 probes are similarly broken and I'm unaware of a working firmware version for that hardware.

Please advise.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading the pyOCD probe driver and its high-level ST-Link protocol handling. Reproduce the DAP register failures with firmware 2J29S7 and later 2J33–2J35 versions, including access to APSEL>0. Done would be a confirmed regression analysis with a documented workaround or an actionable upstream path.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
devtools, embedded-iot
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.