Skip to content

Commit 7810829

Browse files
author
Rohit Grover
committed
fixes configuration-store/#21: avoid Read-While-Write errors
Refer to https://github.com/ARMmbed/configuration-store/issues/21 for more background information. Read-while-write errors may occur when there is a read/fetch on a bank of internal flash while an erase/program operation to the same bank is active. The possibility of read-while-write errors depends on the application's span of internal-flash. In the trivial (and also the default) case when this boundary is set to lie at the junction between the two banks of internal flash (i.e. BLOCK1_START_ADDR), there is *no* possibility of read-while-write. In the more common case, when the system doesn't reserve all of BLOCK1 for the flash driver, there exists the possibility of read-while-write. To avoid this problem for the common case (above), we enforce the following: - Force synchronous mode of execution in the storage_driver. - Disable interrupts during erase/program operations. This prevents the execution of arbitrary user-code which might access flash. - Ensure all code and data structures used in the storage driver execute out of RAM. Refer to __RAMFUNC which allows for this. We assume that CONFIG_HARDWARE_MTD_START_ADDR is the boundary between application and this driver.
1 parent 14a14a0 commit 7810829

File tree

1 file changed

+174
-4
lines changed

1 file changed

+174
-4
lines changed

hal/targets/hal/TARGET_Freescale/TARGET_KSDK2_MCUS/TARGET_K64F/storage_driver.c

Lines changed: 174 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,102 @@ extern volatile uint32_t *const kFCCOBx;
146146
#define SIZEOF_DOUBLE_PHRASE (16)
147147
#endif /* #ifdef USING_KSDK2 */
148148

149+
/* While the K64F flash controller is capable of launching operations asynchronously and
150+
* allowing program execution to continue while an erase/program is active, it
151+
* doesn't allow simultaneous read accesses while and erase/program is active on
152+
* the same block of flash.
153+
*
154+
* Read/fetch accesses can originate arbitrarily as a result of program
155+
* execution. This means that code which operates on flash should not reside in
156+
* flash; or at least it should not reside in the same bank of flash as it is
157+
* operating upon. The only way to ensure that application code and flash driver
158+
* are residing on separate banks of flash is to reserve bank-0 (or BLOCK0) for
159+
* the application and bank-1 (BLOCK1) for the driver--this also happens to be
160+
* the default setting.
161+
*
162+
* But it is quite likely that this default will be over-ridden by the use of
163+
* config options depending upon the actual application. If we don't have a
164+
* clean separation between the application and the space managed by this
165+
* driver, then we need to enforce the following:
166+
*
167+
* - Force synchronous mode of execution in the storage_driver.
168+
* - Disable interrupts during erase/program operations.
169+
* - Ensure all code and data structures used in the storage driver execute
170+
* out of RAM. Refer to __RAMFUNC (below) which allows for this.
171+
*
172+
* It is difficult to determine the application's span of internal-flash at
173+
* compile time. Therefore we assume that CONFIG_HARDWARE_MTD_START_ADDR is the
174+
* boundary between application and this driver. When this boundary is set to
175+
* lie at BLOCK1_START_ADDR, there is no possibility of read-while-write run-
176+
* time errors.
177+
*
178+
* In the following, caps.asynchronous_ops is defined to be 1 if and only if
179+
* asynchronous operation mode is requested and there doesn't exist the
180+
* possibility of concurrent reads.
181+
*/
182+
183+
#if (defined(CONFIG_HARDWARE_MTD_START_ADDR) && (CONFIG_HARDWARE_MTD_START_ADDR != BLOCK1_START_ADDR))
184+
#define EXISTS_POSSIBILITY_OF_CONCURRENT_READ 1
185+
#else
186+
#define EXISTS_POSSIBILITY_OF_CONCURRENT_READ 0
187+
#endif
188+
189+
/* Define '__RAMFUNC' as an attribute to mark a function as residing in RAM. */
190+
#if EXISTS_POSSIBILITY_OF_CONCURRENT_READ
191+
#if defined(__GNUC__) || defined (__CC_ARM) || defined(__clang__) // GCC, armcc and llvm/clang
192+
#ifndef __RAMFUNC
193+
/* define __RAMFUNC to put a declaration in the .data section--i.e. the
194+
* initialized data section. This will be copied into RAM automatically by the
195+
* startup sequence. */
196+
#define __RAMFUNC __attribute__ ((section (".data#"))) /* The '#' following ".data" needs a bit of
197+
* explanation. Without it, we are liable to get the following warning 'Warning: ignoring
198+
* changed section attributes for .data'. This is because __attribute__((section(".data")))
199+
* generates the following assembly:
200+
*
201+
* .section .data,"ax",%progbits
202+
*
203+
* But .data doesn't need the 'x' (execute) attribute bit. To remove the warning, we specify
204+
* the attribute with a '#' at the tail, which emits:
205+
*
206+
* .section .data#,"ax",%progbits
207+
*
208+
* Note that '#' (in the above) acts like a comment-start, and masks the additional
209+
* attributes which don't apply to '.data'.
210+
*/
211+
#endif /* #ifndef __RAMFUNC */
212+
#elif defined ( __ICCARM__ )
213+
#ifndef __RAMFUNC
214+
#define __RAMFUNC __ramfunc
215+
#endif
216+
#else // unknown compiler
217+
#error "This compiler is not yet supported. If you can contribute support for defining a function to be RAM resident, please provide a definition for __RAMFUNC"
218+
#endif
219+
220+
/**
221+
* @brief RAM resident memcpy.
222+
*
223+
* If we require all functions involved in the erase and programming of flash to
224+
* be memory-resident (see the explanations around __RAMFUNC above), then we
225+
* need to provide our own version of memcpy().
226+
*
227+
* @Note 'n' should be a multiple of sizeof(uint32_t).
228+
*/
229+
__RAMFUNC
230+
static void MEMCPY(void *_dest, const void *_src, size_t n)
231+
{
232+
uint32_t *dest = _dest;
233+
const uint32_t *src = _src;
234+
while (n) {
235+
*dest++ = *src++;
236+
n -= sizeof(uint32_t);
237+
}
238+
}
239+
240+
#else /* #if EXISTS_POSSIBILITY_OF_CONCURRENT_READ */
241+
#define __RAMFUNC /* empty */
242+
#define MEMCPY memcpy
243+
#endif /* #if EXISTS_POSSIBILITY_OF_CONCURRENT_READ */
244+
149245

150246
/*
151247
* forward declarations
@@ -205,7 +301,8 @@ static const ARM_DRIVER_VERSION version = {
205301
};
206302

207303

208-
#if (!defined(CONFIG_HARDWARE_MTD_ASYNC_OPS) || CONFIG_HARDWARE_MTD_ASYNC_OPS)
304+
#if ((!defined(CONFIG_HARDWARE_MTD_ASYNC_OPS) || CONFIG_HARDWARE_MTD_ASYNC_OPS) && \
305+
!EXISTS_POSSIBILITY_OF_CONCURRENT_READ)
209306
#define ASYNC_OPS 1
210307
#else
211308
#define ASYNC_OPS 0
@@ -278,6 +375,7 @@ enum FlashCommandOps {
278375
* Read out the CCIF (Command Complete Interrupt Flag) to ensure all previous
279376
* operations have completed.
280377
*/
378+
__RAMFUNC
281379
static inline bool controllerCurrentlyBusy(void)
282380
{
283381
#ifdef USING_KSDK2
@@ -287,6 +385,7 @@ static inline bool controllerCurrentlyBusy(void)
287385
#endif
288386
}
289387

388+
__RAMFUNC
290389
static inline bool failedWithAccessError(void)
291390
{
292391
#ifdef USING_KSDK2
@@ -300,6 +399,7 @@ static inline bool failedWithAccessError(void)
300399
#endif /* ifdef USING_KSDK2 */
301400
}
302401

402+
__RAMFUNC
303403
static inline bool failedWithProtectionError()
304404
{
305405
#ifdef USING_KSDK2
@@ -313,6 +413,7 @@ static inline bool failedWithProtectionError()
313413
#endif /* ifdef USING_KSDK2 */
314414
}
315415

416+
__RAMFUNC
316417
static inline bool failedWithRunTimeError()
317418
{
318419
#ifdef USING_KSDK2
@@ -326,6 +427,7 @@ static inline bool failedWithRunTimeError()
326427
#endif /* ifdef USING_KSDK2 */
327428
}
328429

430+
__RAMFUNC
329431
static inline void clearAccessError(void)
330432
{
331433
#ifdef USING_KSDK2
@@ -335,6 +437,7 @@ static inline void clearAccessError(void)
335437
#endif
336438
}
337439

440+
__RAMFUNC
338441
static inline void clearProtectionError(void)
339442
{
340443
#ifdef USING_KSDK2
@@ -359,6 +462,7 @@ static inline void clearProtectionError(void)
359462
* be cleared to launch a command. The FPVIOL bit is cleared by writing a 1 to
360463
* it.
361464
*/
465+
__RAMFUNC
362466
static inline void clearErrorStatusBits()
363467
{
364468
if (failedWithAccessError()) {
@@ -369,6 +473,7 @@ static inline void clearErrorStatusBits()
369473
}
370474
}
371475

476+
__RAMFUNC
372477
static inline void enableCommandCompletionInterrupt(void)
373478
{
374479
#ifdef USING_KSDK2
@@ -378,6 +483,7 @@ static inline void enableCommandCompletionInterrupt(void)
378483
#endif
379484
}
380485

486+
__RAMFUNC
381487
static inline void disbleCommandCompletionInterrupt(void)
382488
{
383489
#ifdef USING_KSDK2
@@ -387,6 +493,7 @@ static inline void disbleCommandCompletionInterrupt(void)
387493
#endif
388494
}
389495

496+
__RAMFUNC
390497
static inline bool commandCompletionInterruptEnabled(void)
391498
{
392499
#ifdef USING_KSDK2
@@ -401,6 +508,7 @@ static inline bool commandCompletionInterruptEnabled(void)
401508
* command by clearing the FSTAT[CCIF] bit by writing a '1' to it. The CCIF flag
402509
* remains zero until the FTFE command completes.
403510
*/
511+
__RAMFUNC
404512
static inline void launchCommand(void)
405513
{
406514
#ifdef USING_KSDK2
@@ -411,6 +519,7 @@ static inline void launchCommand(void)
411519
}
412520

413521
#ifndef USING_KSDK2
522+
__RAMFUNC
414523
static inline void setupAddressInCCOB123(uint64_t addr)
415524
{
416525
BW_FTFE_FCCOB1_CCOBn((uintptr_t)FTFE, (addr >> 16) & 0xFFUL); /* bits [23:16] of the address. */
@@ -419,6 +528,7 @@ static inline void setupAddressInCCOB123(uint64_t addr)
419528
}
420529
#endif /* ifndef USING_KSDK2 */
421530

531+
__RAMFUNC
422532
static inline void setupEraseSector(uint64_t addr)
423533
{
424534
#ifdef USING_KSDK2
@@ -429,6 +539,7 @@ static inline void setupEraseSector(uint64_t addr)
429539
#endif
430540
}
431541

542+
__RAMFUNC
432543
static inline void setupEraseBlock(uint64_t addr)
433544
{
434545
#ifdef USING_KSDK2
@@ -439,6 +550,7 @@ static inline void setupEraseBlock(uint64_t addr)
439550
#endif
440551
}
441552

553+
__RAMFUNC
442554
static inline void setup8ByteWrite(uint64_t addr, const void *data)
443555
{
444556
/* Program FCCOB to load the required command parameters. */
@@ -463,17 +575,18 @@ static inline void setup8ByteWrite(uint64_t addr, const void *data)
463575
#endif /* ifdef USING_KSDK2 */
464576
}
465577

578+
__RAMFUNC
466579
static inline void setupProgramSection(uint64_t addr, const void *data, size_t cnt)
467580
{
468581
#ifdef USING_KSDK2
469582
static const uintptr_t FlexRAMBase = FSL_FEATURE_FLASH_FLEX_RAM_START_ADDRESS;
470-
memcpy((void *)FlexRAMBase, (const uint8_t *)data, cnt);
583+
MEMCPY((void *)FlexRAMBase, (const uint8_t *)data, cnt);
471584

472585
kFCCOBx[0] = BYTES_JOIN_TO_WORD_1_3(PGMSEC, addr);
473586
kFCCOBx[1] = BYTES_JOIN_TO_WORD_2_2(cnt >> 4, 0xFFFFU);
474587
#else /* ifdef USING_KSDK2 */
475588
static const uintptr_t FlexRAMBase = 0x14000000;
476-
memcpy((void *)FlexRAMBase, (const uint8_t *)data, cnt);
589+
MEMCPY((void *)FlexRAMBase, (const uint8_t *)data, cnt);
477590

478591
BW_FTFE_FCCOB0_CCOBn((uintptr_t)FTFE, PGMSEC);
479592
setupAddressInCCOB123(addr);
@@ -489,6 +602,7 @@ static inline void setupProgramSection(uint64_t addr, const void *data, size_t c
489602
* that 'addr' is aligned to a double-phrase boundary (see \ref
490603
* SIZEOF_DOUBLE_PHRASE)--if not, then only a single phrase (8-bytes) write is possible.
491604
*/
605+
__RAMFUNC
492606
static inline size_t sizeofLargestProgramSection(uint64_t addr, size_t size)
493607
{
494608
/* ensure 'size' is aligned to a double-phrase boundary */
@@ -514,6 +628,7 @@ static inline size_t sizeofLargestProgramSection(uint64_t addr, size_t size)
514628
* Advance the state machine for program-data. This function is called only if
515629
* amountLeftToOperate is non-zero.
516630
*/
631+
__RAMFUNC
517632
static inline void setupNextProgramData(struct mtd_k64f_data *context)
518633
{
519634
if ((context->amountLeftToOperate == PROGRAM_PHRASE_SIZEOF_INLINE_DATA) ||
@@ -537,6 +652,7 @@ static inline void setupNextProgramData(struct mtd_k64f_data *context)
537652
* Advance the state machine for erase. This function is called only if
538653
* amountLeftToOperate is non-zero.
539654
*/
655+
__RAMFUNC
540656
static inline void setupNextErase(struct mtd_k64f_data *context)
541657
{
542658
setupEraseSector(context->currentOperatingStorageAddress); /* Program FCCOB to load the required command parameters. */
@@ -545,10 +661,19 @@ static inline void setupNextErase(struct mtd_k64f_data *context)
545661
context->currentOperatingStorageAddress += ERASE_UNIT;
546662
}
547663

664+
__RAMFUNC
548665
static int32_t executeCommand(struct mtd_k64f_data *context)
549666
{
667+
#if EXISTS_POSSIBILITY_OF_CONCURRENT_READ
668+
__disable_irq();
669+
#endif
550670
launchCommand();
551671

672+
/* !Note!: After launching the command, we should be very careful not to execute any
673+
* code which might access the flash concurrent to the ongoing operation.
674+
* Any code that needs to run in parallel should be executed from RAM (see __RAMFUNC).
675+
* Interrupts should be disabled except when executing asynchronously. */
676+
552677
/* At this point, The FTFE reads the command code and performs a series of
553678
* parameter checks and protection checks, if applicable, which are unique
554679
* to each command. */
@@ -569,7 +694,7 @@ static int32_t executeCommand(struct mtd_k64f_data *context)
569694

570695
return ARM_DRIVER_OK; /* signal asynchronous completion. An interrupt will signal completion later. */
571696
#else /* #if ASYNC_OPS */
572-
/* Synchronous operation. */
697+
/* Synchronous operation. This is the common case. */
573698

574699
while (1) {
575700
/* Spin waiting for the command execution to complete. */
@@ -578,19 +703,43 @@ static int32_t executeCommand(struct mtd_k64f_data *context)
578703
/* Execution may result in failure. Check for errors */
579704
if (failedWithAccessError() || failedWithProtectionError()) {
580705
clearErrorStatusBits();
706+
#if EXISTS_POSSIBILITY_OF_CONCURRENT_READ
707+
__enable_irq();
708+
#endif
581709
return ARM_DRIVER_ERROR_PARAMETER;
582710
}
583711
if (failedWithRunTimeError()) {
712+
#if EXISTS_POSSIBILITY_OF_CONCURRENT_READ
713+
__enable_irq();
714+
#endif
584715
return ARM_DRIVER_ERROR; /* unspecified runtime error. */
585716
}
586717

587718
/* signal synchronous completion. */
588719
switch (context->currentCommand) {
589720
case ARM_STORAGE_OPERATION_PROGRAM_DATA:
590721
if (context->amountLeftToOperate == 0) {
722+
#if EXISTS_POSSIBILITY_OF_CONCURRENT_READ
723+
__enable_irq();
724+
#endif
591725
return context->sizeofCurrentOperation;
592726
}
593727

728+
/* Allow pending interrupts to be handled before launching the successive
729+
* operation. This avoids having to block interrupts for long periods.
730+
*
731+
* ProgramData operates in steps of 1KB; initial experiments
732+
* indicate that the time needed to complete a 1KB write is
733+
* around 6.5ms. As a programData progresses, interrupts would
734+
* need to be held blocked for periods of such duration
735+
* if EXISTS_POSSIBILITY_OF_CONCURRENT_READ.
736+
*/
737+
#if EXISTS_POSSIBILITY_OF_CONCURRENT_READ
738+
__enable_irq();
739+
/* service any pending interrupts at this moment */
740+
__disable_irq();
741+
#endif
742+
594743
/* start the successive program operation */
595744
setupNextProgramData(context);
596745
launchCommand();
@@ -599,15 +748,36 @@ static int32_t executeCommand(struct mtd_k64f_data *context)
599748

600749
case ARM_STORAGE_OPERATION_ERASE:
601750
if (context->amountLeftToOperate == 0) {
751+
#if EXISTS_POSSIBILITY_OF_CONCURRENT_READ
752+
__enable_irq();
753+
#endif
602754
return context->sizeofCurrentOperation;
603755
}
604756

757+
/* Allow pending interrupts to be handled before launching the successive
758+
* operation. This avoids having to block interrupts for long periods.
759+
*
760+
* Erase operates in steps of 4KB (ERASE_UNIT); initial
761+
* experiments indicate that the time needed to complete a 4KB
762+
* erase is around 4ms. As an erase progresses, interrupts
763+
* would need to be held blocked for periods of such duration
764+
* if EXISTS_POSSIBILITY_OF_CONCURRENT_READ.
765+
*/
766+
#if EXISTS_POSSIBILITY_OF_CONCURRENT_READ
767+
__enable_irq();
768+
/* service any pending interrupts at this moment */
769+
__disable_irq();
770+
#endif
771+
605772
setupNextErase(context); /* start the successive erase operation */
606773
launchCommand();
607774
/* continue on to the next iteration of the parent loop */
608775
break;
609776

610777
default:
778+
#if EXISTS_POSSIBILITY_OF_CONCURRENT_READ
779+
__enable_irq();
780+
#endif
611781
return 1;
612782
}
613783
}

0 commit comments

Comments
 (0)