Skip to content

Commit c0f4903

Browse files
committed
target/nrf91: add mass_erase and recovery probe
Signed-off-by: Maximilian Deubel <maximilian.deubel@nordicsemi.no>
1 parent d9a4030 commit c0f4903

File tree

5 files changed

+316
-14
lines changed

5 files changed

+316
-14
lines changed

src/target/adiv5.c

+9
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

+1
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

+292-14
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,75 @@
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+
unsigned char empty_app[] = {
38+
0x00, 0x10, 0x00, 0x20, 0x09, 0x00, 0x00, 0x00, 0x05, 0x4b, 0x4f, 0xf0,
39+
0x5a, 0x02, 0xc3, 0xf8, 0x10, 0x2e, 0x03, 0x4b, 0x4f, 0xf0, 0x5a, 0x02,
40+
0xc3, 0xf8, 0x00, 0x2e, 0xfe, 0xe7, 0x00, 0x00, 0x00, 0x90, 0x03, 0x50
41+
};
42+
unsigned int empty_app_len = 36;
43+
44+
static bool nrf91_ctrl_ap_mass_erase(adiv5_access_port_s *ap)
45+
{
46+
adiv5_ap_write(ap, NRF91_CTRL_AP_ERASEALL, 1);
47+
platform_timeout_s timeout;
48+
platform_timeout_set(&timeout, NVMC_TIMEOUT_MS);
49+
50+
bool ret = false;
51+
52+
while (true) {
53+
uint32_t status = adiv5_ap_read(ap, NRF91_CTRL_AP_ERASEALLSTATUS);
54+
if (status == 0) {
55+
ret = true;
56+
DEBUG_INFO("nRF91 mass erase succeeded.\n");
57+
break;
58+
}
59+
if (platform_timeout_is_expired(&timeout)) {
60+
DEBUG_INFO("nRF91 mass erase failed.\n");
61+
break;
62+
}
63+
}
64+
65+
platform_delay(10);
66+
67+
adiv5_ap_write(ap, NRF91_CTRL_AP_RESET, 1);
68+
adiv5_ap_write(ap, NRF91_CTRL_AP_RESET, 0);
69+
70+
platform_delay(200);
71+
72+
return ret;
73+
}
74+
1875
static bool nrf91_wait_ready(target_s *const target, platform_timeout_s *const timeout)
1976
{
2077
/* Poll for NVMC_READY */
@@ -27,6 +84,18 @@ static bool nrf91_wait_ready(target_s *const target, platform_timeout_s *const t
2784
return true;
2885
}
2986

87+
static bool nrf91_wait_readynext(target_s *const target, platform_timeout_s *const timeout)
88+
{
89+
/* Poll for NVMC_READY */
90+
while (target_mem_read32(target, NRF91_NVMC_READYNEXT) == 0) {
91+
if (target_check_error(target))
92+
return false;
93+
if (timeout)
94+
target_print_progress(timeout);
95+
}
96+
return true;
97+
}
98+
3099
static bool nrf91_flash_erase(target_flash_s *flash, target_addr_t addr, size_t len)
31100
{
32101
target_s *target = flash->t;
@@ -49,17 +118,42 @@ static bool nrf91_flash_erase(target_flash_s *flash, target_addr_t addr, size_t
49118
return nrf91_wait_ready(target, NULL);
50119
}
51120

52-
static bool nrf91_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t len)
121+
static bool nrf91_uicr_flash_erase(target_flash_s *flash, target_addr_t addr, size_t len)
53122
{
54123
target_s *target = flash->t;
55124

125+
bool erase_needed = false;
126+
127+
for (size_t offset = 0; offset < len; offset += 4) {
128+
if (target_mem_read32(target, addr + offset) != NRF91_UICR_ERASED_VAL) {
129+
erase_needed = true;
130+
break;
131+
}
132+
}
133+
134+
if (erase_needed) {
135+
gdb_out("Skipping UICR erase, mass erase might be needed\n");
136+
}
137+
return true;
138+
}
139+
140+
static bool nrf91_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t len)
141+
{
142+
target_s *target = flash->t;
143+
platform_timeout_s timeout;
56144
/* Enable write */
57145
target_mem_write32(target, NRF91_NVMC_CONFIG, NRF91_NVMC_CONFIG_WEN);
58-
if (!nrf91_wait_ready(target, NULL))
146+
147+
if (!nrf91_wait_ready(target, &timeout))
59148
return false;
60149
/* Write the data */
61-
target_mem_write(target, dest, src, len);
62-
if (!nrf91_wait_ready(target, NULL))
150+
for (size_t offset = 0; offset < len; offset += 4) {
151+
target_mem_write32(target, dest + offset, src + offset);
152+
if (!nrf91_wait_readynext(target, &timeout))
153+
return false;
154+
}
155+
//target_mem_write(target, dest, src, len);
156+
if (!nrf91_wait_ready(target, &timeout))
63157
return false;
64158
/* Return to read-only */
65159
target_mem_write32(target, NRF91_NVMC_CONFIG, NRF91_NVMC_CONFIG_REN);
@@ -68,6 +162,7 @@ static bool nrf91_flash_write(target_flash_s *flash, target_addr_t dest, const v
68162

69163
static void nrf91_add_flash(target_s *target, uint32_t addr, size_t length, size_t erasesize)
70164
{
165+
/* add main flash */
71166
target_flash_s *flash = calloc(1, sizeof(*flash));
72167
if (!flash) { /* calloc failed: heap exhaustion */
73168
DEBUG_WARN("calloc: failed in %s\n", __func__);
@@ -81,25 +176,208 @@ static void nrf91_add_flash(target_s *target, uint32_t addr, size_t length, size
81176
flash->write = nrf91_flash_write;
82177
flash->erased = 0xff;
83178
target_add_flash(target, flash);
179+
180+
/* add separate UICR flash */
181+
target_flash_s *flash_uicr = calloc(1, sizeof(*flash_uicr));
182+
if (!flash_uicr) { /* calloc failed: heap exhaustion */
183+
DEBUG_WARN("calloc: failed in %s\n", __func__);
184+
return;
185+
}
186+
187+
flash_uicr->start = 0xff8000U;
188+
flash_uicr->length = 0x1000U;
189+
flash_uicr->blocksize = 0x4U;
190+
flash_uicr->erase = nrf91_uicr_flash_erase;
191+
flash_uicr->write = nrf91_flash_write;
192+
flash_uicr->erased = 0xff;
193+
target_add_flash(target, flash_uicr);
194+
}
195+
196+
static bool nrf91_mass_erase(target_s *target)
197+
{
198+
adiv5_access_port_s *ap = cortex_ap(target);
199+
adiv5_access_port_s ctrl_ap = {
200+
.dp = ap->dp,
201+
.apsel = 0x4U,
202+
};
203+
204+
if (!nrf91_ctrl_ap_mass_erase(&ctrl_ap)) {
205+
return false;
206+
}
207+
208+
if (ap->dp->target_revision > 2) {
209+
target_mem_write32(target, NRF91_NVMC_CONFIG, NRF91_NVMC_CONFIG_WEN);
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+
target_mem_write(target, 0, empty_app, empty_app_len);
216+
target_mem_write32(target, NRF91_UICR_APPROTECT, NRF91_UICR_APPROTECT_UNPROTECT_VAL);
217+
target_mem_write32(target, NRF91_UICR_SECUREAPPROTECT, NRF91_UICR_APPROTECT_UNPROTECT_VAL);
218+
219+
while (target_mem_read32(target, NRF91_NVMC_READY) == 0) {
220+
platform_delay(1);
221+
DEBUG_INFO("Waiting for NVMC to become ready\n");
222+
}
223+
224+
target_mem_write32(target, NRF91_NVMC_CONFIG, NRF91_NVMC_CONFIG_REN);
225+
}
226+
227+
return true;
228+
}
229+
230+
static bool nrf91_exit_flash_mode(target_s *const target)
231+
{
232+
adiv5_access_port_s *ap = cortex_ap(target);
233+
/* Persist AP access if uninitialized (only needed for devices with hardenend APPROTECT) */
234+
if (ap->dp->target_revision > 2) {
235+
bool approtect_erased = target_mem_read32(target, NRF91_UICR_APPROTECT) == NRF91_UICR_ERASED_VAL;
236+
bool secureapprotect_erased = target_mem_read32(target, NRF91_UICR_SECUREAPPROTECT) == NRF91_UICR_ERASED_VAL;
237+
238+
target_mem_write32(target, NRF91_NVMC_CONFIG, NRF91_NVMC_CONFIG_WEN);
239+
240+
while (target_mem_read32(target, NRF91_NVMC_READY) == 0) {
241+
platform_delay(1);
242+
DEBUG_INFO("Waiting for NVMC to become ready\n");
243+
}
244+
245+
if (approtect_erased) {
246+
target_mem_write32(target, NRF91_UICR_APPROTECT, NRF91_UICR_APPROTECT_UNPROTECT_VAL);
247+
}
248+
if (secureapprotect_erased) {
249+
target_mem_write32(target, NRF91_UICR_SECUREAPPROTECT, NRF91_UICR_APPROTECT_UNPROTECT_VAL);
250+
}
251+
252+
while (target_mem_read32(target, NRF91_NVMC_READY) == 0) {
253+
platform_delay(1);
254+
DEBUG_INFO("Waiting for NVMC to become ready\n");
255+
}
256+
257+
target_mem_write32(target, NRF91_NVMC_CONFIG, NRF91_NVMC_CONFIG_REN);
258+
}
259+
return true;
84260
}
85261

86262
bool nrf91_probe(target_s *target)
87263
{
88264
adiv5_access_port_s *ap = cortex_ap(target);
89265

90-
if (ap->dp->version < 2U)
266+
if (ap->dp->version < 2U || ap->dp->target_partno != NRF91_PARTNO)
91267
return false;
92268

93-
switch (ap->dp->target_partno) {
94-
case 0x90:
269+
uint32_t partno = target_mem_read32(target, 0x00FF0140);
270+
uint32_t hwrevision = target_mem_read32(target, 0x00FF0144);
271+
uint32_t variant = target_mem_read32(target, 0x00FF0148);
272+
273+
DEBUG_INFO("nRF%04" PRIx32 " %4s%4s detected!\n", partno, (const char *)&variant, (const char *)&hwrevision);
274+
275+
switch (ap->dp->target_revision) {
276+
case 0:
277+
case 1:
278+
case 2:
95279
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);
280+
break;
281+
case 3:
282+
target->driver = "Nordic nRF91x1";
99283
break;
100284
default:
285+
target->driver = "Nordic nRF91";
286+
}
287+
288+
target->target_options |= TOPT_INHIBIT_NRST;
289+
target_add_ram(target, 0x20000000, 256U * 1024U);
290+
nrf91_add_flash(target, 0, 4096U * 256U, 4096U);
291+
292+
target->mass_erase = nrf91_mass_erase;
293+
target->exit_flash_mode = nrf91_exit_flash_mode;
294+
295+
return true;
296+
}
297+
298+
static bool nrf91_rescue_do_recover(target_s *target)
299+
{
300+
adiv5_access_port_s *ap = (adiv5_access_port_s *)target->priv;
301+
302+
const bool hardened_approtect = ap->dp->target_revision > 2;
303+
304+
/* on some revisions, this needs to be repeated */
305+
for (size_t i = 0; i < 3; ++i) {
306+
if (!nrf91_ctrl_ap_mass_erase(ap))
307+
continue;
308+
if (!hardened_approtect) {
309+
/* pin reset is needed on older devices */
310+
platform_nrst_set_val(true);
311+
platform_delay(100);
312+
platform_nrst_set_val(false);
313+
314+
/* repetition not needed and debug port inactive at this point */
315+
return false;
316+
}
317+
318+
//check if CSW DEVICEEN is set
319+
struct adiv5_access_port ahb_ap = *ap;
320+
ahb_ap.apsel = 0x0U;
321+
const uint32_t csw = ap->dp->ap_read(&ahb_ap, ADIV5_AP_CSW);
322+
if (csw & ADIV5_AP_CSW_DEVICEEN) {
323+
DEBUG_INFO("nRF91 Rescue succeeded.\n");
324+
break;
325+
}
326+
}
327+
328+
return false;
329+
}
330+
331+
bool nrf91_rescue_probe(adiv5_access_port_s *ap)
332+
{
333+
target_s *target = target_new();
334+
if (!target) {
101335
return false;
102336
}
337+
adiv5_ap_ref(ap);
338+
target->attach = (void *)nrf91_rescue_do_recover;
339+
target->priv = ap;
340+
target->priv_free = (void *)adiv5_ap_unref;
341+
target->driver = "nRF91 Rescue (Attach, then scan again!)";
342+
343+
return true;
344+
}
103345

346+
/* check if nRF91 target is in secure state, return false if device is protected */
347+
bool nrf91_dp_prepare(adiv5_debug_port_s *const dp)
348+
{
349+
adiv5_access_port_s ahb_ap = {
350+
.dp = dp,
351+
.apsel = 0x0U,
352+
};
353+
adiv5_access_port_s ctrl_ap = {
354+
.dp = dp,
355+
.apsel = 0x4U,
356+
};
357+
ahb_ap.idr = adiv5_ap_read(&ahb_ap, ADIV5_AP_IDR);
358+
ahb_ap.csw = adiv5_ap_read(&ahb_ap, ADIV5_AP_CSW);
359+
ctrl_ap.idr = adiv5_ap_read(&ctrl_ap, ADIV5_AP_IDR);
360+
361+
if (ahb_ap.idr != NRF91_AHB_AP_IDR_EXPECTED) {
362+
DEBUG_ERROR(
363+
"nRF91: AHB-AP IDR is 0x%08" PRIx32 ", expected 0x%08" PRIx32 "\n", ahb_ap.idr, NRF91_AHB_AP_IDR_EXPECTED);
364+
}
365+
366+
if (ctrl_ap.idr != NRF91_CTRL_IDR_EXPECTED) {
367+
DEBUG_ERROR(
368+
"nRF91: CTRL-AP IDR is 0x%08" PRIx32 ", expected 0x%08" PRIx32 "\n", ctrl_ap.idr, NRF91_CTRL_IDR_EXPECTED);
369+
}
370+
371+
if (!(ahb_ap.csw & ADIV5_AP_CSW_DEVICEEN)) {
372+
DEBUG_INFO("nRF91 is in secure state, creating rescue target\n");
373+
adiv5_access_port_s *ap = calloc(1, sizeof(*ap));
374+
if (!ap) { /* calloc failed: heap exhaustion */
375+
DEBUG_ERROR("calloc: failed in %s\n", __func__);
376+
return false;
377+
}
378+
memcpy(ap, &ctrl_ap, sizeof(*ap));
379+
nrf91_rescue_probe(ap);
380+
return false;
381+
}
104382
return true;
105383
}

0 commit comments

Comments
 (0)