Skip to content

Commit 304cf02

Browse files
ahasztaganangl
authored andcommitted
[nrf fromtree] drivers: nrf_qspi_nor: Fix EXTXIP + Flash write race condition
When parts of code are running from EXTXIP and at the same time QSPI external flash writes/erases are happenning there is a high risk of a race condition where a thread running EXTXIP code starts executing while an external flash operation is in progress; This can lead to execution stalling or bus faults/usage faults. The issue was observed when parts of MCUMGR were relocated to EXTXIP and DFU was performed. To fix the issue added the CONFIG_NORDIC_QSPI_NOR_XIP_FLASH_SCHED_LOCK option which should be used when executing parts of code from EXTXIP. It ensures the blocking variant of nrfx_qspi_ is used and that no preemption happens while an external flash write is in progress (by making qspi_wait_while_writing blocking) Signed-off-by: Artur Hadasz <artur.hadasz@nordicsemi.no> Signed-off-by: Andrzej Puzdrowski <andrzej.puzdrowski@nordicsemi.no> (cherry picked from commit 318948b)
1 parent f7722b5 commit 304cf02

2 files changed

Lines changed: 55 additions & 1 deletion

File tree

drivers/flash/Kconfig.nordic_qspi_nor

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,18 @@ config NORDIC_QSPI_NOR_XIP
5252
QSPI NOR flash chip is executed until the driver has been setup.
5353
This will also disable power management for the QSPI NOR flash chip.
5454

55+
config NORDIC_QSPI_NOR_XIP_FLASH_SCHED_LOCK
56+
bool "Hold scheduler lock during QSPI NOR program/erase when XIP"
57+
depends on NORDIC_QSPI_NOR_XIP
58+
depends on MULTITHREADING
59+
help
60+
When executing code from external QSPI flash (XIP), other threads must
61+
not fetch instructions from the same device while it is programming or
62+
erasing. Taking the scheduler lock for the duration of each flash
63+
operation prevents other threads from running XIP code concurrently.
64+
WIP polling uses busy-wait instead of sleeping so the lock is not
65+
violated. Enable for split XIP + DFU workloads.
66+
5567
config NORDIC_QSPI_NOR_TIMEOUT_MS
5668
int "Timeout for QSPI operations (ms)"
5769
default 500

drivers/flash/nrf_qspi_nor.c

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,11 @@ static void qspi_release(const struct device *dev)
350350

351351
static inline void qspi_wait_for_completion(const struct device *dev, int res)
352352
{
353+
#if defined(CONFIG_NORDIC_QSPI_NOR_XIP_FLASH_SCHED_LOCK)
354+
/* Do nothing, as the QSPI transfers are blocking */
355+
ARG_UNUSED(res);
356+
ARG_UNUSED(dev);
357+
#else
353358
struct qspi_nor_data *dev_data = dev->data;
354359

355360
if (res == 0) {
@@ -366,6 +371,7 @@ static inline void qspi_wait_for_completion(const struct device *dev, int res)
366371
irq_unlock(key);
367372
#endif /* CONFIG_MULTITHREADING */
368373
}
374+
#endif /* CONFIG_NORDIC_QSPI_NOR_XIP_FLASH_SCHED_LOCK */
369375
}
370376

371377
static inline void qspi_complete(struct qspi_nor_data *dev_data)
@@ -377,6 +383,20 @@ static inline void qspi_complete(struct qspi_nor_data *dev_data)
377383
#endif /* CONFIG_MULTITHREADING */
378384
}
379385

386+
#if defined(CONFIG_NORDIC_QSPI_NOR_XIP_FLASH_SCHED_LOCK)
387+
388+
/** In case of XIP execution from external flash, the QSPI transfers must be blocking
389+
* in order to avoid race conditions where at the same time the QSPI external flash
390+
* is being programmed and the QSPI is being used to execute code from the external flash.
391+
* Passing NULL to nrfx_qspi_init() will make the QSPI transfers blocking.
392+
* Note - after the QSPI transfer is complete (the READY event is received) a
393+
* nrfx_qspi_... blocking function will exit, but the external flash write operation
394+
* will be still in progress. Thus, qspi_wait_while_writing must be blocking as well.
395+
* Actually, most of the race conditions would in fact occur by preempting qspi_wait_while_writing
396+
*/
397+
#define qspi_handler NULL
398+
399+
#else
380400
/**
381401
* @brief QSPI handler
382402
*
@@ -392,6 +412,7 @@ static void qspi_handler(nrfx_qspi_evt_t event, void *p_context)
392412
qspi_complete(dev_data);
393413
}
394414
}
415+
#endif /* CONFIG_NORDIC_QSPI_NOR_XIP_FLASH_SCHED_LOCK */
395416

396417
/* QSPI send custom command.
397418
*
@@ -484,9 +505,14 @@ static int qspi_wait_while_writing(const struct device *dev, k_timeout_t poll_pe
484505
do {
485506
#ifdef CONFIG_MULTITHREADING
486507
if (!K_TIMEOUT_EQ(poll_period, K_NO_WAIT)) {
508+
#if defined(CONFIG_NORDIC_QSPI_NOR_XIP_FLASH_SCHED_LOCK)
509+
/* Must not sleep while k_sched_lock held; wait same duration as k_sleep. */
510+
k_busy_wait(k_ticks_to_us_floor32(poll_period.ticks));
511+
#else
487512
k_sleep(poll_period);
488-
}
489513
#endif
514+
}
515+
#endif /* CONFIG_MULTITHREADING */
490516
rc = qspi_rdsr(dev, 1);
491517
} while ((rc >= 0)
492518
&& ((rc & SPI_NOR_WIP_BIT) != 0U));
@@ -974,6 +1000,10 @@ static int qspi_nor_write(const struct device *dev, off_t addr,
9741000

9751001
qspi_acquire(dev);
9761002

1003+
#if defined(CONFIG_NORDIC_QSPI_NOR_XIP_FLASH_SCHED_LOCK)
1004+
k_sched_lock();
1005+
#endif
1006+
9771007
rc = qspi_nor_write_protection_set(dev, false);
9781008
if (rc == 0) {
9791009
if (size < 4U) {
@@ -989,6 +1019,10 @@ static int qspi_nor_write(const struct device *dev, off_t addr,
9891019

9901020
rc2 = qspi_nor_write_protection_set(dev, true);
9911021

1022+
#if defined(CONFIG_NORDIC_QSPI_NOR_XIP_FLASH_SCHED_LOCK)
1023+
k_sched_unlock();
1024+
#endif
1025+
9921026
qspi_release(dev);
9931027

9941028
return rc != 0 ? rc : rc2;
@@ -1020,8 +1054,16 @@ static int qspi_nor_erase(const struct device *dev, off_t addr, size_t size)
10201054

10211055
qspi_acquire(dev);
10221056

1057+
#if defined(CONFIG_NORDIC_QSPI_NOR_XIP_FLASH_SCHED_LOCK)
1058+
k_sched_lock();
1059+
#endif
1060+
10231061
rc = qspi_erase(dev, addr, size);
10241062

1063+
#if defined(CONFIG_NORDIC_QSPI_NOR_XIP_FLASH_SCHED_LOCK)
1064+
k_sched_unlock();
1065+
#endif
1066+
10251067
qspi_release(dev);
10261068

10271069
return rc;

0 commit comments

Comments
 (0)