From 0e1952a4d1af65ae11144ec591876fe6c1ce88ee Mon Sep 17 00:00:00 2001 From: Marcin Bukat Date: Wed, 30 Sep 2026 16:42:20 +0200 Subject: [PATCH] rk27xx: invalidate the cache with the cache controller off commit_discard_idcache() invalidated both cache ways while running from cached SDRAM. When the loop polling for the invalidate to finish starts on a cache line of its own, it is fetched through the ways being invalidated and the CPU takes a data abort, reported at the loop's branch. Whether it crashed thus depended on where the linker put the function. usb_storage calls it on every USB connect. On rk27generic a jpeg change that grew clip_jpeg_fd by 8 bytes moved the loop onto a new line, and the firmware crashed as the USB screen came up, with an empty backtrace. Turn the cache controller off around the invalidate, as crt0.S does at start-up. The cache is write-through, so no data is lost. Tested on a generic rk2705 with the function padded so the poll loop starts a new cache line: without this change it crashes at the first USB connect, with it the device enumerates as a mass storage device. The normally linked build works too. Co-Authored-By: Claude Opus 5.5 Change-Id: I319b811fdef30717999b60132000e70f410001fc --- firmware/target/arm/rk27xx/system-rk27xx.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/firmware/target/arm/rk27xx/system-rk27xx.c b/firmware/target/arm/rk27xx/system-rk27xx.c index cf80e09509..ff0aaf38a9 100644 --- a/firmware/target/arm/rk27xx/system-rk27xx.c +++ b/firmware/target/arm/rk27xx/system-rk27xx.c @@ -208,11 +208,21 @@ static void cache_invalidate_way(int way) void commit_discard_idcache(void) { int old_irq = disable_irq_save(); + unsigned long devid = DEVID; + + /* Invalidate with the cache off, as crt0 does. This code runs from + * cached SDRAM: invalidating the ways while fetching through them fails + * when the poll loop starts on a cache line of its own, depending on + * where the linker happened to put it. The cache is write-through, so + * nothing is lost by turning it off. */ + DEVID = devid & ~(1UL << 31); cache_invalidate_way(0); cache_invalidate_way(1); + DEVID = devid; + restore_irq(old_irq); } void commit_discard_dcache (void) __attribute__((alias("commit_discard_idcache")));