From 3986754ee4a1fa8537dea77cfac3e5ce05c44607 Mon Sep 17 00:00:00 2001 From: Emanuele Ghidoli Date: Tue, 30 May 2023 10:27:27 +0200 Subject: common/memsize.c: Fix get_ram_size() when cache is enabled Ensure that every write is flushed to memory and afterward reads are from memory. Since the algorithm rely on the fact that accessing to not existent memory lead to write at addr / 2 without this modification accesses to aliased (not physically present) addresses are cached and wrong size is returned. This was discovered while working on a TI AM625 based board where cache is normally enabled, see commit c02712a74849 ("arm: mach-k3: Enable dcache in SPL"). Upstream-Status: Submitted [https://lore.kernel.org/all/20230530133327.178278-1-francesco@dolcini.it/] Signed-off-by: Emanuele Ghidoli Signed-off-by: Francesco Dolcini --- common/memsize.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/common/memsize.c b/common/memsize.c index e95c68265a..9b46eca105 100644 --- a/common/memsize.c +++ b/common/memsize.c @@ -6,9 +6,19 @@ #include #include +#include +#include +#include DECLARE_GLOBAL_DATA_PTR; +#ifdef CONFIG_SYS_CACHELINE_SIZE +# define MEMSIZE_CACHELINE_SIZE CONFIG_SYS_CACHELINE_SIZE +#else +/* Just use the greatest cache flush alignment requirement I'm aware of */ +# define MEMSIZE_CACHELINE_SIZE 128 +#endif + #ifdef __PPC__ /* * At least on G2 PowerPC cores, sequential accesses to non-existent @@ -19,6 +29,16 @@ DECLARE_GLOBAL_DATA_PTR; # define sync() /* nothing */ #endif +static void dcache_flush_invalidate(volatile long *p) +{ + uintptr_t start, stop; + + start = ALIGN_DOWN((uintptr_t)p, MEMSIZE_CACHELINE_SIZE); + stop = start + MEMSIZE_CACHELINE_SIZE; + flush_dcache_range(start, stop); + invalidate_dcache_range(start, stop); +} + /* * Check memory range for valid RAM. A simple memory test determines * the actually available RAM size between addresses `base' and @@ -33,6 +53,7 @@ long get_ram_size(long *base, long maxsize) long val; long size; int i = 0; + int dcache_en = dcache_status(); for (cnt = (maxsize / sizeof(long)) >> 1; cnt > 0; cnt >>= 1) { addr = base + cnt; /* pointer arith! */ @@ -40,6 +61,8 @@ long get_ram_size(long *base, long maxsize) save[i++] = *addr; sync(); *addr = ~cnt; + if (dcache_en) + dcache_flush_invalidate(addr); } addr = base; @@ -49,6 +72,9 @@ long get_ram_size(long *base, long maxsize) *addr = 0; sync(); + if (dcache_en) + dcache_flush_invalidate(addr); + if ((val = *addr) != 0) { /* Restore the original data before leaving the function. */ sync(); -- cgit v1.2.3