Skip to content

Commit 2d81846

Browse files
committed
target/nrf91: add mass_erase and recovery probe
Signed-off-by: Maximilian Deubel <[email protected]>
1 parent e52eafa commit 2d81846

File tree

5 files changed

+252
-6
lines changed

5 files changed

+252
-6
lines changed

src/target/adiv5.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,8 @@ void adiv5_dp_init(adiv5_debug_port_s *const dp)
954954

955955
dp->target_partno = (targetid & ADIV5_DP_TARGETID_TPARTNO_MASK) >> ADIV5_DP_TARGETID_TPARTNO_OFFSET;
956956

957+
dp->target_revision = (targetid & ADIV5_DP_TARGETID_TREVISION_MASK) >> ADIV5_DP_TARGETID_TREVISION_OFFSET;
958+
957959
DEBUG_INFO("TARGETID 0x%08" PRIx32 " designer 0x%x partno 0x%x\n", targetid, dp->target_designer_code,
958960
dp->target_partno);
959961

@@ -1003,6 +1005,13 @@ void adiv5_dp_init(adiv5_debug_port_s *const dp)
10031005
if (dp->target_designer_code == JEP106_MANUFACTURER_NXP)
10041006
lpc55_dp_prepare(dp);
10051007

1008+
if (dp->target_designer_code == JEP106_MANUFACTURER_NORDIC && dp->target_partno == 0x90U) {
1009+
if (!nrf91_dp_prepare(dp)) {
1010+
/* device is in secure state, only show rescue target */
1011+
return;
1012+
}
1013+
}
1014+
10061015
/* Probe for APs on this DP */
10071016
size_t invalid_aps = 0;
10081017
dp->refcnt++;

src/target/adiv5.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ struct adiv5_debug_port {
274274
/* TARGETID designer and partno, present on DPv2 */
275275
uint16_t target_designer_code;
276276
uint16_t target_partno;
277+
uint8_t target_revision;
277278
};
278279

279280
struct adiv5_access_port {

src/target/nrf91.c

Lines changed: 228 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "target_internal.h"
44
#include "cortexm.h"
55
#include "adiv5.h"
6+
#include "gdb_packet.h"
67

78
/* Non-Volatile Memory Controller (NVMC) Registers */
89
#define NRF91_NVMC 0x50039000U
@@ -15,6 +16,52 @@
1516
#define NRF91_NVMC_CONFIG_EEN 0x2U // Erase enable
1617
#define NRF91_NVMC_CONFIG_PEEN 0x3U // Partial erase enable
1718

19+
/* https://infocenter.nordicsemi.com/topic/ps_nrf9160/dif.html */
20+
#define NRF91_PARTNO 0x90U
21+
22+
#define NRF91_CTRL_AP_RESET ADIV5_AP_REG(0x000)
23+
#define NRF91_CTRL_AP_ERASEALL ADIV5_AP_REG(0x004)
24+
#define NRF91_CTRL_IDR_EXPECTED 0x12880000
25+
#define NRF91_AHB_AP_IDR_EXPECTED 0x84770001
26+
#define NRF91_CTRL_AP_ERASEALLSTATUS ADIV5_AP_REG(0x008)
27+
28+
/* https://infocenter.nordicsemi.com/topic/ps_nrf9161/uicr.html */
29+
#define NRF91_UICR_APPROTECT 0x00FF8000U
30+
#define NRF91_UICR_SECUREAPPROTECT 0x00FF802CU
31+
#define NRF91_UICR_APPROTECT_UNPROTECT_VAL 0x50FA50FAU
32+
#define NRF91_UICR_ERASED_VAL 0xFFFFFFFFU
33+
34+
static bool nrf91_ctrl_ap_mass_erase(adiv5_access_port_s *ap)
35+
{
36+
adiv5_ap_write(ap, NRF91_CTRL_AP_ERASEALL, 1);
37+
platform_timeout_s timeout;
38+
platform_timeout_set(&timeout, 300);
39+
40+
bool ret = false;
41+
42+
while (true) {
43+
uint32_t status = adiv5_ap_read(ap, NRF91_CTRL_AP_ERASEALLSTATUS);
44+
if (status == 0) {
45+
ret = true;
46+
DEBUG_INFO("nRF91 mass erase succeeded.\n");
47+
break;
48+
}
49+
if (platform_timeout_is_expired(&timeout)) {
50+
DEBUG_INFO("nRF91 mass erase failed.\n");
51+
break;
52+
}
53+
}
54+
55+
platform_delay(10);
56+
57+
adiv5_ap_write(ap, NRF91_CTRL_AP_RESET, 1);
58+
adiv5_ap_write(ap, NRF91_CTRL_AP_RESET, 0);
59+
60+
platform_delay(200);
61+
62+
return ret;
63+
}
64+
1865
static bool nrf91_wait_ready(target_s *const target, platform_timeout_s *const timeout)
1966
{
2067
/* Poll for NVMC_READY */
@@ -49,6 +96,25 @@ static bool nrf91_flash_erase(target_flash_s *flash, target_addr_t addr, size_t
4996
return nrf91_wait_ready(target, NULL);
5097
}
5198

99+
static bool nrf91_uicr_flash_erase(target_flash_s *flash, target_addr_t addr, size_t len)
100+
{
101+
target_s *target = flash->t;
102+
103+
bool erase_needed = false;
104+
105+
for (size_t offset = 0; offset < len; offset += 4) {
106+
if (target_mem_read32(target, addr + offset) != NRF91_UICR_ERASED_VAL) {
107+
erase_needed = true;
108+
break;
109+
}
110+
}
111+
112+
if (erase_needed) {
113+
gdb_out("Skipping UICR erase, mass erase might be needed\n");
114+
}
115+
return true;
116+
}
117+
52118
static bool nrf91_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t len)
53119
{
54120
target_s *target = flash->t;
@@ -68,6 +134,7 @@ static bool nrf91_flash_write(target_flash_s *flash, target_addr_t dest, const v
68134

69135
static void nrf91_add_flash(target_s *target, uint32_t addr, size_t length, size_t erasesize)
70136
{
137+
/* add main flash */
71138
target_flash_s *flash = calloc(1, sizeof(*flash));
72139
if (!flash) { /* calloc failed: heap exhaustion */
73140
DEBUG_WARN("calloc: failed in %s\n", __func__);
@@ -81,25 +148,180 @@ static void nrf91_add_flash(target_s *target, uint32_t addr, size_t length, size
81148
flash->write = nrf91_flash_write;
82149
flash->erased = 0xff;
83150
target_add_flash(target, flash);
151+
152+
/* add separate UICR flash */
153+
target_flash_s *flash_uicr = calloc(1, sizeof(*flash_uicr));
154+
if (!flash_uicr) { /* calloc failed: heap exhaustion */
155+
DEBUG_WARN("calloc: failed in %s\n", __func__);
156+
return;
157+
}
158+
159+
flash_uicr->start = 0xff8000U;
160+
flash_uicr->length = 0x1000U;
161+
flash_uicr->blocksize = 0x4U;
162+
flash_uicr->erase = nrf91_uicr_flash_erase;
163+
flash_uicr->write = nrf91_flash_write;
164+
flash_uicr->erased = 0xff;
165+
target_add_flash(target, flash_uicr);
166+
}
167+
168+
static bool nrf91_mass_erase(target_s *target)
169+
{
170+
adiv5_access_port_s *ap = cortex_ap(target);
171+
adiv5_access_port_s ctrl_ap = {
172+
.dp = ap->dp,
173+
.apsel = 0x4U,
174+
};
175+
176+
return nrf91_ctrl_ap_mass_erase(&ctrl_ap);
177+
}
178+
179+
static bool nrf91_exit_flash_mode(target_s *const target)
180+
{
181+
adiv5_access_port_s *ap = cortex_ap(target);
182+
/* Persist AP access if uninitialized (only needed for devices with hardenend APPROTECT) */
183+
if (ap->dp->target_revision > 2) {
184+
bool approtect_erased = target_mem_read32(target, NRF91_UICR_APPROTECT) == NRF91_UICR_ERASED_VAL;
185+
bool secureapprotect_erased = target_mem_read32(target, NRF91_UICR_SECUREAPPROTECT) == NRF91_UICR_ERASED_VAL;
186+
187+
target_mem_write32(target, NRF91_NVMC_CONFIG, NRF91_NVMC_CONFIG_WEN);
188+
189+
while (target_mem_read32(target, NRF91_NVMC_READY) == 0) {
190+
platform_delay(1);
191+
DEBUG_INFO("Waiting for NVMC to become ready\n");
192+
}
193+
194+
if (approtect_erased) {
195+
target_mem_write32(target, NRF91_UICR_APPROTECT, NRF91_UICR_APPROTECT_UNPROTECT_VAL);
196+
}
197+
if (secureapprotect_erased) {
198+
target_mem_write32(target, NRF91_UICR_SECUREAPPROTECT, NRF91_UICR_APPROTECT_UNPROTECT_VAL);
199+
}
200+
201+
target_mem_write32(target, NRF91_NVMC_CONFIG, NRF91_NVMC_CONFIG_REN);
202+
}
203+
return true;
84204
}
85205

86206
bool nrf91_probe(target_s *target)
87207
{
88208
adiv5_access_port_s *ap = cortex_ap(target);
89209

90-
if (ap->dp->version < 2U)
210+
if (ap->dp->version < 2U || ap->dp->target_partno != NRF91_PARTNO)
91211
return false;
92212

93-
switch (ap->dp->target_partno) {
94-
case 0x90:
213+
uint32_t partno = target_mem_read32(target, 0x00FF0140);
214+
uint32_t hwrevision = target_mem_read32(target, 0x00FF0144);
215+
uint32_t variant = target_mem_read32(target, 0x00FF0148);
216+
217+
DEBUG_INFO("nRF%04" PRIx32 " %4s%4s detected!\n", partno, (const char *)&variant, (const char *)&hwrevision);
218+
219+
switch (ap->dp->target_revision) {
220+
case 0:
221+
case 1:
222+
case 2:
95223
target->driver = "Nordic nRF9160";
96-
target->target_options |= TOPT_INHIBIT_NRST;
97-
target_add_ram(target, 0x20000000, 256U * 1024U);
98-
nrf91_add_flash(target, 0, 4096U * 256U, 4096U);
224+
break;
225+
case 3:
226+
target->driver = "Nordic nRF91x1";
99227
break;
100228
default:
229+
target->driver = "Nordic nRF91";
230+
}
231+
232+
target->target_options |= TOPT_INHIBIT_NRST;
233+
target_add_ram(target, 0x20000000, 256U * 1024U);
234+
nrf91_add_flash(target, 0, 4096U * 256U, 4096U);
235+
236+
target->mass_erase = nrf91_mass_erase;
237+
target->exit_flash_mode = nrf91_exit_flash_mode;
238+
239+
return true;
240+
}
241+
242+
static bool nrf91_rescue_do_recover(target_s *target)
243+
{
244+
adiv5_access_port_s *ap = (adiv5_access_port_s *)target->priv;
245+
246+
const bool hardened_approtect = ap->dp->target_revision > 2;
247+
248+
/* on some revisions, this needs to be repeated */
249+
for (size_t i = 0; i < 3; ++i) {
250+
if (!nrf91_ctrl_ap_mass_erase(ap))
251+
continue;
252+
if (!hardened_approtect) {
253+
/* pin reset is needed on older devices */
254+
platform_nrst_set_val(true);
255+
platform_delay(100);
256+
platform_nrst_set_val(false);
257+
258+
/* repetition not needed and debug port inactive at this point */
259+
return false;
260+
}
261+
262+
//check if CSW DEVICEEN is set
263+
struct adiv5_access_port ahb_ap = *ap;
264+
ahb_ap.apsel = 0x0U;
265+
const uint32_t csw = ap->dp->ap_read(&ahb_ap, ADIV5_AP_CSW);
266+
if (csw & ADIV5_AP_CSW_DEVICEEN) {
267+
DEBUG_INFO("nRF91 Rescue succeeded.\n");
268+
break;
269+
}
270+
}
271+
272+
return false;
273+
}
274+
275+
bool nrf91_rescue_probe(adiv5_access_port_s *ap)
276+
{
277+
target_s *target = target_new();
278+
if (!target) {
101279
return false;
102280
}
281+
adiv5_ap_ref(ap);
282+
target->attach = (void *)nrf91_rescue_do_recover;
283+
target->priv = ap;
284+
target->priv_free = (void *)adiv5_ap_unref;
285+
target->driver = "nRF91 Rescue (Attach, then scan again!)";
286+
287+
return true;
288+
}
103289

290+
/* check if nRF91 target is in secure state, return false if device is protected */
291+
bool nrf91_dp_prepare(adiv5_debug_port_s *const dp)
292+
{
293+
adiv5_access_port_s ahb_ap = {
294+
.dp = dp,
295+
.apsel = 0x0U,
296+
};
297+
adiv5_access_port_s ctrl_ap = {
298+
.dp = dp,
299+
.apsel = 0x4U,
300+
};
301+
ahb_ap.idr = adiv5_ap_read(&ahb_ap, ADIV5_AP_IDR);
302+
ahb_ap.csw = adiv5_ap_read(&ahb_ap, ADIV5_AP_CSW);
303+
ctrl_ap.idr = adiv5_ap_read(&ctrl_ap, ADIV5_AP_IDR);
304+
305+
if (ahb_ap.idr != NRF91_AHB_AP_IDR_EXPECTED) {
306+
DEBUG_ERROR(
307+
"nRF91: AHB-AP IDR is 0x%08" PRIx32 ", expected 0x%08" PRIx32 "\n", ahb_ap.idr, NRF91_AHB_AP_IDR_EXPECTED);
308+
}
309+
310+
if (ctrl_ap.idr != NRF91_CTRL_IDR_EXPECTED) {
311+
DEBUG_ERROR(
312+
"nRF91: CTRL-AP IDR is 0x%08" PRIx32 ", expected 0x%08" PRIx32 "\n", ctrl_ap.idr, NRF91_CTRL_IDR_EXPECTED);
313+
}
314+
315+
if (!(ahb_ap.csw & ADIV5_AP_CSW_DEVICEEN)) {
316+
DEBUG_INFO("nRF91 is in secure state, creating rescue target\n");
317+
adiv5_access_port_s *ap = calloc(1, sizeof(*ap));
318+
if (!ap) { /* calloc failed: heap exhaustion */
319+
DEBUG_ERROR("calloc: failed in %s\n", __func__);
320+
return false;
321+
}
322+
memcpy(ap, &ctrl_ap, sizeof(*ap));
323+
nrf91_rescue_probe(ap);
324+
return false;
325+
}
104326
return true;
105327
}

src/target/target_probe.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@
4848
{ \
4949
lpc55_dp_prepare_nop(debug_port); \
5050
};
51+
#define NRF91_DP_PREPARE_WEAK_NOP(name) \
52+
__attribute__((weak)) bool name(adiv5_debug_port_s *const debug_port) \
53+
{ \
54+
return nrf91_dp_prepare_nop(debug_port); \
55+
};
5156
#else
5257
#define CORTEXAR_PROBE_WEAK_NOP(name) \
5358
extern bool name(adiv5_access_port_s *, target_addr_t) __attribute__((weak, alias("cortexar_probe_nop")));
@@ -56,6 +61,8 @@
5661
#define TARGET_PROBE_WEAK_NOP(name) extern bool name(target_s *) __attribute__((weak, alias("target_probe_nop")));
5762
#define LPC55_DP_PREPARE_WEAK_NOP(name) \
5863
extern void name(adiv5_debug_port_s *) __attribute__((weak, alias("lpc55_dp_prepare_nop")));
64+
#define NRF91_DP_PREPARE_WEAK_NOP(name) \
65+
extern bool name(adiv5_debug_port_s *) __attribute__((weak, alias("nrf91_dp_prepare_nop")));
5966
#endif
6067

6168
static inline bool cortexar_probe_nop(adiv5_access_port_s *const access_port, const target_addr_t base_address)
@@ -82,6 +89,12 @@ static inline void lpc55_dp_prepare_nop(adiv5_debug_port_s *const debug_port)
8289
(void)debug_port;
8390
}
8491

92+
static inline bool nrf91_dp_prepare_nop(adiv5_debug_port_s *const debug_port)
93+
{
94+
(void)debug_port;
95+
return true;
96+
}
97+
8598
/*
8699
* nop alias functions to allow support for target probe methods
87100
* to be disabled by not compiling/linking them in.

src/target/target_probe.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,5 +98,6 @@ bool stm32mp15_cm4_probe(target_s *target);
9898
bool zynq7_probe(target_s *target);
9999

100100
void lpc55_dp_prepare(adiv5_debug_port_s *dp);
101+
bool nrf91_dp_prepare(adiv5_debug_port_s * dp);
101102

102103
#endif /* TARGET_TARGET_PROBE_H */

0 commit comments

Comments
 (0)