mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-12-02 01:19:49 +00:00
ARC: Flush & invalidate D$ with a single command
We don't implement separate flush_dcache_all() intentionally as entire data cache invalidation is dangerous operation even if we flush data cache right before invalidation. There is the real example: We may get stuck in the following code if we store any context (like BLINK register) on stack in invalidate_dcache_all() function. BLINK register is the register where return address is automatically saved when we do function call with instructions like 'bl'. void flush_dcache_all() { __dc_entire_op(OP_FLUSH); // Other code // } void invalidate_dcache_all() { __dc_entire_op(OP_INV); // Other code // } void foo(void) { flush_dcache_all(); invalidate_dcache_all(); } Now let's see what really happens during that code execution: foo() |->> call flush_dcache_all [return address is saved to BLINK register] [push BLINK] (save to stack) ![point 1] |->> call __dc_entire_op(OP_FLUSH) [return address is saved to BLINK register] [flush L1 D$] return [jump to BLINK] <<------ [other flush_dcache_all code] [pop BLINK] (get from stack) return [jump to BLINK] <<------ |->> call invalidate_dcache_all [return address is saved to BLINK register] [push BLINK] (save to stack) ![point 2] |->> call __dc_entire_op(OP_FLUSH) [return address is saved to BLINK register] [invalidate L1 D$] ![point 3] // Oops!!! // We lose return address from invalidate_dcache_all function: // we save it to stack and invalidate L1 D$ after that! return [jump to BLINK] <<------ [other invalidate_dcache_all code] [pop BLINK] (get from stack) // we don't have this data in L1 dcache as we invalidated it in [point 3] // so we get it from next memory level (for example DDR memory) // but in the memory we have value which we save in [point 1], which // is return address from flush_dcache_all function (instead of // address from current invalidate_dcache_all function which we // saved in [point 2] !) return [jump to BLINK] <<------ // As BLINK points to invalidate_dcache_all, we call it again and // loop forever. Fortunately we may do flush and invalidation of D$ with a single one instruction which automatically mitigates a situation described above. And because invalidate_dcache_all() isn't used in common U-Boot code we implement "flush and invalidate dcache all" instead. Signed-off-by: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com> Signed-off-by: Alexey Brodkin <abrodkin@synopsys.com>
This commit is contained in:
parent
5e0c68edad
commit
c27814be33
2 changed files with 85 additions and 5 deletions
|
@ -30,6 +30,7 @@
|
||||||
#ifndef __ASSEMBLY__
|
#ifndef __ASSEMBLY__
|
||||||
|
|
||||||
void cache_init(void);
|
void cache_init(void);
|
||||||
|
void flush_n_invalidate_dcache_all(void);
|
||||||
|
|
||||||
#endif /* __ASSEMBLY__ */
|
#endif /* __ASSEMBLY__ */
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,80 @@
|
||||||
#include <asm/arcregs.h>
|
#include <asm/arcregs.h>
|
||||||
#include <asm/cache.h>
|
#include <asm/cache.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* [ NOTE 1 ]:
|
||||||
|
* Data cache (L1 D$ or SL$) entire invalidate operation or data cache disable
|
||||||
|
* operation may result in unexpected behavior and data loss even if we flush
|
||||||
|
* data cache right before invalidation. That may happens if we store any context
|
||||||
|
* on stack (like we store BLINK register on stack before function call).
|
||||||
|
* BLINK register is the register where return address is automatically saved
|
||||||
|
* when we do function call with instructions like 'bl'.
|
||||||
|
*
|
||||||
|
* There is the real example:
|
||||||
|
* We may hang in the next code as we store any BLINK register on stack in
|
||||||
|
* invalidate_dcache_all() function.
|
||||||
|
*
|
||||||
|
* void flush_dcache_all() {
|
||||||
|
* __dc_entire_op(OP_FLUSH);
|
||||||
|
* // Other code //
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* void invalidate_dcache_all() {
|
||||||
|
* __dc_entire_op(OP_INV);
|
||||||
|
* // Other code //
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* void foo(void) {
|
||||||
|
* flush_dcache_all();
|
||||||
|
* invalidate_dcache_all();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* Now let's see what really happens during that code execution:
|
||||||
|
*
|
||||||
|
* foo()
|
||||||
|
* |->> call flush_dcache_all
|
||||||
|
* [return address is saved to BLINK register]
|
||||||
|
* [push BLINK] (save to stack) ![point 1]
|
||||||
|
* |->> call __dc_entire_op(OP_FLUSH)
|
||||||
|
* [return address is saved to BLINK register]
|
||||||
|
* [flush L1 D$]
|
||||||
|
* return [jump to BLINK]
|
||||||
|
* <<------
|
||||||
|
* [other flush_dcache_all code]
|
||||||
|
* [pop BLINK] (get from stack)
|
||||||
|
* return [jump to BLINK]
|
||||||
|
* <<------
|
||||||
|
* |->> call invalidate_dcache_all
|
||||||
|
* [return address is saved to BLINK register]
|
||||||
|
* [push BLINK] (save to stack) ![point 2]
|
||||||
|
* |->> call __dc_entire_op(OP_FLUSH)
|
||||||
|
* [return address is saved to BLINK register]
|
||||||
|
* [invalidate L1 D$] ![point 3]
|
||||||
|
* // Oops!!!
|
||||||
|
* // We lose return address from invalidate_dcache_all function:
|
||||||
|
* // we save it to stack and invalidate L1 D$ after that!
|
||||||
|
* return [jump to BLINK]
|
||||||
|
* <<------
|
||||||
|
* [other invalidate_dcache_all code]
|
||||||
|
* [pop BLINK] (get from stack)
|
||||||
|
* // we don't have this data in L1 dcache as we invalidated it in [point 3]
|
||||||
|
* // so we get it from next memory level (for example DDR memory)
|
||||||
|
* // but in the memory we have value which we save in [point 1], which
|
||||||
|
* // is return address from flush_dcache_all function (instead of
|
||||||
|
* // address from current invalidate_dcache_all function which we
|
||||||
|
* // saved in [point 2] !)
|
||||||
|
* return [jump to BLINK]
|
||||||
|
* <<------
|
||||||
|
* // As BLINK points to invalidate_dcache_all, we call it again and
|
||||||
|
* // loop forever.
|
||||||
|
*
|
||||||
|
* Fortunately we may fix that by using flush & invalidation of D$ with a single
|
||||||
|
* one instruction (instead of flush and invalidation instructions pair) and
|
||||||
|
* enabling force function inline with '__attribute__((always_inline))' gcc
|
||||||
|
* attribute to avoid any function call (and BLINK store) between cache flush
|
||||||
|
* and disable.
|
||||||
|
*/
|
||||||
|
|
||||||
/* Bit values in IC_CTRL */
|
/* Bit values in IC_CTRL */
|
||||||
#define IC_CTRL_CACHE_DISABLE BIT(0)
|
#define IC_CTRL_CACHE_DISABLE BIT(0)
|
||||||
|
|
||||||
|
@ -256,8 +330,7 @@ void cache_init(void)
|
||||||
/* IOC Aperture size is equal to DDR size */
|
/* IOC Aperture size is equal to DDR size */
|
||||||
long ap_size = CONFIG_SYS_SDRAM_SIZE;
|
long ap_size = CONFIG_SYS_SDRAM_SIZE;
|
||||||
|
|
||||||
flush_dcache_all();
|
flush_n_invalidate_dcache_all();
|
||||||
invalidate_dcache_all();
|
|
||||||
|
|
||||||
if (!is_power_of_2(ap_size) || ap_size < 4096)
|
if (!is_power_of_2(ap_size) || ap_size < 4096)
|
||||||
panic("IOC Aperture size must be power of 2 and bigger 4Kib");
|
panic("IOC Aperture size must be power of 2 and bigger 4Kib");
|
||||||
|
@ -483,13 +556,19 @@ void flush_cache(unsigned long start, unsigned long size)
|
||||||
flush_dcache_range(start, start + size);
|
flush_dcache_range(start, start + size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void invalidate_dcache_all(void)
|
/*
|
||||||
|
* As invalidate_dcache_all() is not used in generic U-Boot code and as we
|
||||||
|
* don't need it in arch/arc code alone (invalidate without flush) we implement
|
||||||
|
* flush_n_invalidate_dcache_all (flush and invalidate in 1 operation) because
|
||||||
|
* it's much safer. See [ NOTE 1 ] for more details.
|
||||||
|
*/
|
||||||
|
void flush_n_invalidate_dcache_all(void)
|
||||||
{
|
{
|
||||||
__dc_entire_op(OP_INV);
|
__dc_entire_op(OP_FLUSH_N_INV);
|
||||||
|
|
||||||
#ifdef CONFIG_ISA_ARCV2
|
#ifdef CONFIG_ISA_ARCV2
|
||||||
if (slc_exists)
|
if (slc_exists)
|
||||||
__slc_entire_op(OP_INV);
|
__slc_entire_op(OP_FLUSH_N_INV);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue