Skip to content

Commit b1391c6

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

File tree

5 files changed

+281
-14
lines changed

5 files changed

+281
-14
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: 257 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,68 @@
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 */
8-
#define NRF91_NVMC 0x50039000U
9-
#define NRF91_NVMC_READY (NRF91_NVMC + 0x400U)
10-
#define NRF91_NVMC_CONFIG (NRF91_NVMC + 0x504U)
11-
#define NRF91_NVMC_ERASEALL (NRF91_NVMC + 0x50cU)
9+
#define NRF91_NVMC 0x50039000U
10+
#define NRF91_NVMC_READY (NRF91_NVMC + 0x400U)
11+
#define NRF91_NVMC_READYNEXT (NRF91_NVMC + 0x408U)
12+
#define NRF91_NVMC_CONFIG (NRF91_NVMC + 0x504U)
13+
#define NRF91_NVMC_ERASEALL (NRF91_NVMC + 0x50cU)
14+
15+
#define NVMC_TIMEOUT_MS 300U
1216

1317
#define NRF91_NVMC_CONFIG_REN 0x0U // Read only access
1418
#define NRF91_NVMC_CONFIG_WEN 0x1U // Write enable
1519
#define NRF91_NVMC_CONFIG_EEN 0x2U // Erase enable
1620
#define NRF91_NVMC_CONFIG_PEEN 0x3U // Partial erase enable
1721

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

80+
static bool nrf91_wait_readynext(target_s *const target, platform_timeout_s *const timeout)
81+
{
82+
/* Poll for NVMC_READY */
83+
while (target_mem_read32(target, NRF91_NVMC_READYNEXT) == 0) {
84+
if (target_check_error(target))
85+
return false;
86+
if (timeout)
87+
target_print_progress(timeout);
88+
}
89+
return true;
90+
}
91+
3092
static bool nrf91_flash_erase(target_flash_s *flash, target_addr_t addr, size_t len)
3193
{
3294
target_s *target = flash->t;
@@ -49,17 +111,42 @@ static bool nrf91_flash_erase(target_flash_s *flash, target_addr_t addr, size_t
49111
return nrf91_wait_ready(target, NULL);
50112
}
51113

52-
static bool nrf91_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t len)
114+
static bool nrf91_uicr_flash_erase(target_flash_s *flash, target_addr_t addr, size_t len)
53115
{
54116
target_s *target = flash->t;
55117

118+
bool erase_needed = false;
119+
120+
for (size_t offset = 0; offset < len; offset += 4) {
121+
if (target_mem_read32(target, addr + offset) != NRF91_UICR_ERASED_VAL) {
122+
erase_needed = true;
123+
break;
124+
}
125+
}
126+
127+
if (erase_needed) {
128+
gdb_out("Skipping UICR erase, mass erase might be needed\n");
129+
}
130+
return true;
131+
}
132+
133+
static bool nrf91_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t len)
134+
{
135+
target_s *target = flash->t;
136+
platform_timeout_s timeout;
56137
/* Enable write */
57138
target_mem_write32(target, NRF91_NVMC_CONFIG, NRF91_NVMC_CONFIG_WEN);
58-
if (!nrf91_wait_ready(target, NULL))
139+
140+
if (!nrf91_wait_ready(target, &timeout))
59141
return false;
60142
/* Write the data */
61-
target_mem_write(target, dest, src, len);
62-
if (!nrf91_wait_ready(target, NULL))
143+
for (size_t offset = 0; offset < len; offset += 4) {
144+
target_mem_write32(target, dest + offset, src + offset);
145+
if (!nrf91_wait_readynext(target, &timeout))
146+
return false;
147+
}
148+
//target_mem_write(target, dest, src, len);
149+
if (!nrf91_wait_ready(target, &timeout))
63150
return false;
64151
/* Return to read-only */
65152
target_mem_write32(target, NRF91_NVMC_CONFIG, NRF91_NVMC_CONFIG_REN);
@@ -68,6 +155,7 @@ static bool nrf91_flash_write(target_flash_s *flash, target_addr_t dest, const v
68155

69156
static void nrf91_add_flash(target_s *target, uint32_t addr, size_t length, size_t erasesize)
70157
{
158+
/* add main flash */
71159
target_flash_s *flash = calloc(1, sizeof(*flash));
72160
if (!flash) { /* calloc failed: heap exhaustion */
73161
DEBUG_WARN("calloc: failed in %s\n", __func__);
@@ -81,25 +169,180 @@ static void nrf91_add_flash(target_s *target, uint32_t addr, size_t length, size
81169
flash->write = nrf91_flash_write;
82170
flash->erased = 0xff;
83171
target_add_flash(target, flash);
172+
173+
/* add separate UICR flash */
174+
target_flash_s *flash_uicr = calloc(1, sizeof(*flash_uicr));
175+
if (!flash_uicr) { /* calloc failed: heap exhaustion */
176+
DEBUG_WARN("calloc: failed in %s\n", __func__);
177+
return;
178+
}
179+
180+
flash_uicr->start = 0xff8000U;
181+
flash_uicr->length = 0x1000U;
182+
flash_uicr->blocksize = 0x4U;
183+
flash_uicr->erase = nrf91_uicr_flash_erase;
184+
flash_uicr->write = nrf91_flash_write;
185+
flash_uicr->erased = 0xff;
186+
target_add_flash(target, flash_uicr);
187+
}
188+
189+
static bool nrf91_mass_erase(target_s *target)
190+
{
191+
adiv5_access_port_s *ap = cortex_ap(target);
192+
adiv5_access_port_s ctrl_ap = {
193+
.dp = ap->dp,
194+
.apsel = 0x4U,
195+
};
196+
197+
return nrf91_ctrl_ap_mass_erase(&ctrl_ap);
198+
}
199+
200+
static bool nrf91_exit_flash_mode(target_s *const target)
201+
{
202+
adiv5_access_port_s *ap = cortex_ap(target);
203+
/* Persist AP access if uninitialized (only needed for devices with hardenend APPROTECT) */
204+
if (ap->dp->target_revision > 2) {
205+
bool approtect_erased = target_mem_read32(target, NRF91_UICR_APPROTECT) == NRF91_UICR_ERASED_VAL;
206+
bool secureapprotect_erased = target_mem_read32(target, NRF91_UICR_SECUREAPPROTECT) == NRF91_UICR_ERASED_VAL;
207+
208+
target_mem_write32(target, NRF91_NVMC_CONFIG, NRF91_NVMC_CONFIG_WEN);
209+
210+
while (target_mem_read32(target, NRF91_NVMC_READY) == 0) {
211+
platform_delay(1);
212+
DEBUG_INFO("Waiting for NVMC to become ready\n");
213+
}
214+
215+
if (approtect_erased) {
216+
target_mem_write32(target, NRF91_UICR_APPROTECT, NRF91_UICR_APPROTECT_UNPROTECT_VAL);
217+
}
218+
if (secureapprotect_erased) {
219+
target_mem_write32(target, NRF91_UICR_SECUREAPPROTECT, NRF91_UICR_APPROTECT_UNPROTECT_VAL);
220+
}
221+
222+
target_mem_write32(target, NRF91_NVMC_CONFIG, NRF91_NVMC_CONFIG_REN);
223+
}
224+
return true;
84225
}
85226

86227
bool nrf91_probe(target_s *target)
87228
{
88229
adiv5_access_port_s *ap = cortex_ap(target);
89230

90-
if (ap->dp->version < 2U)
231+
if (ap->dp->version < 2U || ap->dp->target_partno != NRF91_PARTNO)
91232
return false;
92233

93-
switch (ap->dp->target_partno) {
94-
case 0x90:
234+
uint32_t partno = target_mem_read32(target, 0x00FF0140);
235+
uint32_t hwrevision = target_mem_read32(target, 0x00FF0144);
236+
uint32_t variant = target_mem_read32(target, 0x00FF0148);
237+
238+
DEBUG_INFO("nRF%04" PRIx32 " %4s%4s detected!\n", partno, (const char *)&variant, (const char *)&hwrevision);
239+
240+
switch (ap->dp->target_revision) {
241+
case 0:
242+
case 1:
243+
case 2:
95244
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);
245+
break;
246+
case 3:
247+
target->driver = "Nordic nRF91x1";
99248
break;
100249
default:
250+
target->driver = "Nordic nRF91";
251+
}
252+
253+
target->target_options |= TOPT_INHIBIT_NRST;
254+
target_add_ram(target, 0x20000000, 256U * 1024U);
255+
nrf91_add_flash(target, 0, 4096U * 256U, 4096U);
256+
257+
target->mass_erase = nrf91_mass_erase;
258+
target->exit_flash_mode = nrf91_exit_flash_mode;
259+
260+
return true;
261+
}
262+
263+
static bool nrf91_rescue_do_recover(target_s *target)
264+
{
265+
adiv5_access_port_s *ap = (adiv5_access_port_s *)target->priv;
266+
267+
const bool hardened_approtect = ap->dp->target_revision > 2;
268+
269+
/* on some revisions, this needs to be repeated */
270+
for (size_t i = 0; i < 3; ++i) {
271+
if (!nrf91_ctrl_ap_mass_erase(ap))
272+
continue;
273+
if (!hardened_approtect) {
274+
/* pin reset is needed on older devices */
275+
platform_nrst_set_val(true);
276+
platform_delay(100);
277+
platform_nrst_set_val(false);
278+
279+
/* repetition not needed and debug port inactive at this point */
280+
return false;
281+
}
282+
283+
//check if CSW DEVICEEN is set
284+
struct adiv5_access_port ahb_ap = *ap;
285+
ahb_ap.apsel = 0x0U;
286+
const uint32_t csw = ap->dp->ap_read(&ahb_ap, ADIV5_AP_CSW);
287+
if (csw & ADIV5_AP_CSW_DEVICEEN) {
288+
DEBUG_INFO("nRF91 Rescue succeeded.\n");
289+
break;
290+
}
291+
}
292+
293+
return false;
294+
}
295+
296+
bool nrf91_rescue_probe(adiv5_access_port_s *ap)
297+
{
298+
target_s *target = target_new();
299+
if (!target) {
101300
return false;
102301
}
302+
adiv5_ap_ref(ap);
303+
target->attach = (void *)nrf91_rescue_do_recover;
304+
target->priv = ap;
305+
target->priv_free = (void *)adiv5_ap_unref;
306+
target->driver = "nRF91 Rescue (Attach, then scan again!)";
103307

104308
return true;
105309
}
310+
311+
/* check if nRF91 target is in secure state, return false if device is protected */
312+
bool nrf91_dp_prepare(adiv5_debug_port_s *const dp)
313+
{
314+
adiv5_access_port_s ahb_ap = {
315+
.dp = dp,
316+
.apsel = 0x0U,
317+
};
318+
adiv5_access_port_s ctrl_ap = {
319+
.dp = dp,
320+
.apsel = 0x4U,
321+
};
322+
ahb_ap.idr = adiv5_ap_read(&ahb_ap, ADIV5_AP_IDR);
323+
ahb_ap.csw = adiv5_ap_read(&ahb_ap, ADIV5_AP_CSW);
324+
ctrl_ap.idr = adiv5_ap_read(&ctrl_ap, ADIV5_AP_IDR);
325+
326+
if (ahb_ap.idr != NRF91_AHB_AP_IDR_EXPECTED) {
327+
DEBUG_ERROR(
328+
"nRF91: AHB-AP IDR is 0x%08" PRIx32 ", expected 0x%08" PRIx32 "\n", ahb_ap.idr, NRF91_AHB_AP_IDR_EXPECTED);
329+
}
330+
331+
if (ctrl_ap.idr != NRF91_CTRL_IDR_EXPECTED) {
332+
DEBUG_ERROR(
333+
"nRF91: CTRL-AP IDR is 0x%08" PRIx32 ", expected 0x%08" PRIx32 "\n", ctrl_ap.idr, NRF91_CTRL_IDR_EXPECTED);
334+
}
335+
336+
if (!(ahb_ap.csw & ADIV5_AP_CSW_DEVICEEN)) {
337+
DEBUG_INFO("nRF91 is in secure state, creating rescue target\n");
338+
adiv5_access_port_s *ap = calloc(1, sizeof(*ap));
339+
if (!ap) { /* calloc failed: heap exhaustion */
340+
DEBUG_ERROR("calloc: failed in %s\n", __func__);
341+
return false;
342+
}
343+
memcpy(ap, &ctrl_ap, sizeof(*ap));
344+
nrf91_rescue_probe(ap);
345+
return false;
346+
}
347+
return true;
348+
}

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.

0 commit comments

Comments
 (0)