Skip to content

Commit 8941178

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

File tree

5 files changed

+294
-10
lines changed

5 files changed

+294
-10
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

+270-10
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 */
@@ -49,6 +106,25 @@ static bool nrf91_flash_erase(target_flash_s *flash, target_addr_t addr, size_t
49106
return nrf91_wait_ready(target, NULL);
50107
}
51108

109+
static bool nrf91_uicr_flash_erase(target_flash_s *flash, target_addr_t addr, size_t len)
110+
{
111+
target_s *target = flash->t;
112+
113+
bool erase_needed = false;
114+
115+
for (size_t offset = 0; offset < len; offset += 4) {
116+
if (target_mem_read32(target, addr + offset) != NRF91_UICR_ERASED_VAL) {
117+
erase_needed = true;
118+
break;
119+
}
120+
}
121+
122+
if (erase_needed) {
123+
gdb_out("Skipping UICR erase, mass erase might be needed\n");
124+
}
125+
return true;
126+
}
127+
52128
static bool nrf91_flash_write(target_flash_s *flash, target_addr_t dest, const void *src, size_t len)
53129
{
54130
target_s *target = flash->t;
@@ -68,6 +144,7 @@ static bool nrf91_flash_write(target_flash_s *flash, target_addr_t dest, const v
68144

69145
static void nrf91_add_flash(target_s *target, uint32_t addr, size_t length, size_t erasesize)
70146
{
147+
/* add main flash */
71148
target_flash_s *flash = calloc(1, sizeof(*flash));
72149
if (!flash) { /* calloc failed: heap exhaustion */
73150
DEBUG_WARN("calloc: failed in %s\n", __func__);
@@ -81,25 +158,208 @@ static void nrf91_add_flash(target_s *target, uint32_t addr, size_t length, size
81158
flash->write = nrf91_flash_write;
82159
flash->erased = 0xff;
83160
target_add_flash(target, flash);
161+
162+
/* add separate UICR flash */
163+
target_flash_s *flash_uicr = calloc(1, sizeof(*flash_uicr));
164+
if (!flash_uicr) { /* calloc failed: heap exhaustion */
165+
DEBUG_WARN("calloc: failed in %s\n", __func__);
166+
return;
167+
}
168+
169+
flash_uicr->start = 0xff8000U;
170+
flash_uicr->length = 0x1000U;
171+
flash_uicr->blocksize = 0x4U;
172+
flash_uicr->erase = nrf91_uicr_flash_erase;
173+
flash_uicr->write = nrf91_flash_write;
174+
flash_uicr->erased = 0xff;
175+
target_add_flash(target, flash_uicr);
176+
}
177+
178+
static bool nrf91_mass_erase(target_s *target)
179+
{
180+
adiv5_access_port_s *ap = cortex_ap(target);
181+
adiv5_access_port_s ctrl_ap = {
182+
.dp = ap->dp,
183+
.apsel = 0x4U,
184+
};
185+
186+
if (!nrf91_ctrl_ap_mass_erase(&ctrl_ap)) {
187+
return false;
188+
}
189+
190+
if (ap->dp->target_revision > 2) {
191+
target_mem_write32(target, NRF91_NVMC_CONFIG, NRF91_NVMC_CONFIG_WEN);
192+
while (target_mem_read32(target, NRF91_NVMC_READY) == 0) {
193+
platform_delay(1);
194+
DEBUG_INFO("Waiting for NVMC to become ready\n");
195+
}
196+
197+
target_mem_write(target, 0, empty_app, empty_app_len);
198+
target_mem_write32(target, NRF91_UICR_APPROTECT, NRF91_UICR_APPROTECT_UNPROTECT_VAL);
199+
target_mem_write32(target, NRF91_UICR_SECUREAPPROTECT, NRF91_UICR_APPROTECT_UNPROTECT_VAL);
200+
201+
while (target_mem_read32(target, NRF91_NVMC_READY) == 0) {
202+
platform_delay(1);
203+
DEBUG_INFO("Waiting for NVMC to become ready\n");
204+
}
205+
206+
target_mem_write32(target, NRF91_NVMC_CONFIG, NRF91_NVMC_CONFIG_REN);
207+
}
208+
209+
return true;
210+
}
211+
212+
static bool nrf91_exit_flash_mode(target_s *const target)
213+
{
214+
adiv5_access_port_s *ap = cortex_ap(target);
215+
/* Persist AP access if uninitialized (only needed for devices with hardenend APPROTECT) */
216+
if (ap->dp->target_revision > 2) {
217+
bool approtect_erased = target_mem_read32(target, NRF91_UICR_APPROTECT) == NRF91_UICR_ERASED_VAL;
218+
bool secureapprotect_erased = target_mem_read32(target, NRF91_UICR_SECUREAPPROTECT) == NRF91_UICR_ERASED_VAL;
219+
220+
target_mem_write32(target, NRF91_NVMC_CONFIG, NRF91_NVMC_CONFIG_WEN);
221+
222+
while (target_mem_read32(target, NRF91_NVMC_READY) == 0) {
223+
platform_delay(1);
224+
DEBUG_INFO("Waiting for NVMC to become ready\n");
225+
}
226+
227+
if (approtect_erased) {
228+
target_mem_write32(target, NRF91_UICR_APPROTECT, NRF91_UICR_APPROTECT_UNPROTECT_VAL);
229+
}
230+
if (secureapprotect_erased) {
231+
target_mem_write32(target, NRF91_UICR_SECUREAPPROTECT, NRF91_UICR_APPROTECT_UNPROTECT_VAL);
232+
}
233+
234+
while (target_mem_read32(target, NRF91_NVMC_READY) == 0) {
235+
platform_delay(1);
236+
DEBUG_INFO("Waiting for NVMC to become ready\n");
237+
}
238+
239+
target_mem_write32(target, NRF91_NVMC_CONFIG, NRF91_NVMC_CONFIG_REN);
240+
}
241+
return true;
84242
}
85243

86244
bool nrf91_probe(target_s *target)
87245
{
88246
adiv5_access_port_s *ap = cortex_ap(target);
89247

90-
if (ap->dp->version < 2U)
248+
if (ap->dp->version < 2U || ap->dp->target_partno != NRF91_PARTNO)
91249
return false;
92250

93-
switch (ap->dp->target_partno) {
94-
case 0x90:
251+
#ifndef ENABLE_DEBUG
252+
uint32_t partno = target_mem_read32(target, 0x00FF0140);
253+
uint32_t hwrevision = target_mem_read32(target, 0x00FF0144);
254+
uint32_t variant = target_mem_read32(target, 0x00FF0148);
255+
DEBUG_INFO("nRF%04" PRIx32 " %4s%4s detected!\n", partno, (const char *)&variant, (const char *)&hwrevision);
256+
#endif
257+
switch (ap->dp->target_revision) {
258+
case 0:
259+
case 1:
260+
case 2:
95261
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);
262+
break;
263+
case 3:
264+
target->driver = "Nordic nRF91x1";
99265
break;
100266
default:
267+
target->driver = "Nordic nRF91";
268+
}
269+
270+
target->target_options |= TOPT_INHIBIT_NRST;
271+
target_add_ram(target, 0x20000000, 256U * 1024U);
272+
nrf91_add_flash(target, 0, 4096U * 256U, 4096U);
273+
274+
target->mass_erase = nrf91_mass_erase;
275+
target->exit_flash_mode = nrf91_exit_flash_mode;
276+
277+
return true;
278+
}
279+
280+
static bool nrf91_rescue_do_recover(target_s *target)
281+
{
282+
adiv5_access_port_s *ap = (adiv5_access_port_s *)target->priv;
283+
284+
const bool hardened_approtect = ap->dp->target_revision > 2;
285+
286+
/* on some revisions, this needs to be repeated */
287+
for (size_t i = 0; i < 3; ++i) {
288+
if (!nrf91_ctrl_ap_mass_erase(ap))
289+
continue;
290+
if (!hardened_approtect) {
291+
/* pin reset is needed on older devices */
292+
platform_nrst_set_val(true);
293+
platform_delay(100);
294+
platform_nrst_set_val(false);
295+
296+
/* repetition not needed and debug port inactive at this point */
297+
return false;
298+
}
299+
300+
//check if CSW DEVICEEN is set
301+
struct adiv5_access_port ahb_ap = *ap;
302+
ahb_ap.apsel = 0x0U;
303+
const uint32_t csw = ap->dp->ap_read(&ahb_ap, ADIV5_AP_CSW);
304+
if (csw & ADIV5_AP_CSW_DEVICEEN) {
305+
DEBUG_INFO("nRF91 Rescue succeeded.\n");
306+
break;
307+
}
308+
}
309+
310+
return false;
311+
}
312+
313+
bool nrf91_rescue_probe(adiv5_access_port_s *ap)
314+
{
315+
target_s *target = target_new();
316+
if (!target) {
101317
return false;
102318
}
319+
adiv5_ap_ref(ap);
320+
target->attach = (void *)nrf91_rescue_do_recover;
321+
target->priv = ap;
322+
target->priv_free = (void *)adiv5_ap_unref;
323+
target->driver = "nRF91 Rescue (Attach, then scan again!)";
103324

104325
return true;
105326
}
327+
328+
/* check if nRF91 target is in secure state, return false if device is protected */
329+
bool nrf91_dp_prepare(adiv5_debug_port_s *const dp)
330+
{
331+
adiv5_access_port_s ahb_ap = {
332+
.dp = dp,
333+
.apsel = 0x0U,
334+
};
335+
adiv5_access_port_s ctrl_ap = {
336+
.dp = dp,
337+
.apsel = 0x4U,
338+
};
339+
ahb_ap.idr = adiv5_ap_read(&ahb_ap, ADIV5_AP_IDR);
340+
ahb_ap.csw = adiv5_ap_read(&ahb_ap, ADIV5_AP_CSW);
341+
ctrl_ap.idr = adiv5_ap_read(&ctrl_ap, ADIV5_AP_IDR);
342+
343+
if (ahb_ap.idr != NRF91_AHB_AP_IDR_EXPECTED) {
344+
DEBUG_ERROR(
345+
"nRF91: AHB-AP IDR is 0x%08" PRIx32 ", expected 0x%08" PRIx32 "\n", ahb_ap.idr, NRF91_AHB_AP_IDR_EXPECTED);
346+
}
347+
348+
if (ctrl_ap.idr != NRF91_CTRL_IDR_EXPECTED) {
349+
DEBUG_ERROR(
350+
"nRF91: CTRL-AP IDR is 0x%08" PRIx32 ", expected 0x%08" PRIx32 "\n", ctrl_ap.idr, NRF91_CTRL_IDR_EXPECTED);
351+
}
352+
353+
if (!(ahb_ap.csw & ADIV5_AP_CSW_DEVICEEN)) {
354+
DEBUG_INFO("nRF91 is in secure state, creating rescue target\n");
355+
adiv5_access_port_s *ap = calloc(1, sizeof(*ap));
356+
if (!ap) { /* calloc failed: heap exhaustion */
357+
DEBUG_ERROR("calloc: failed in %s\n", __func__);
358+
return false;
359+
}
360+
memcpy(ap, &ctrl_ap, sizeof(*ap));
361+
nrf91_rescue_probe(ap);
362+
return false;
363+
}
364+
return true;
365+
}

src/target/target_probe.c

+13
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)