[patch] missing icache flush on alpha

Andrea Arcangeli (andrea@suse.de)
Fri, 10 Dec 1999 02:23:05 +0100 (CET)


The alpha has the icache not coherent with the dcache. So also in UP, if
we write some code and we jump to it, we are going to execute the
random data that was in memory before the write because the data written
is still dirty in the dcache.

Even more potential is the SMP case: CPU1 can write the irq handler code
to the CPU1-dcache and then can initialize a device after registering an
irq. Then the other CPU0 (all irqs goes to CPU0 in 2.2.x you just know ;)
can go to execute the irq handler before CPU0 had a chance to see the
module code that is only present in the CPU1-dcache.

A module loading is exactly like self modifying code.

So I fixed this issue doing a proper imb() in UP and a sync-IPI imb() in
SMP, before going to execute the module code (the imb() force the two
caches to synchronize).

Patch against 2.2.14pre12 but it will almost sure apply cleanly also on
the top of and all the others 2.2.x kernels:

diff -urN 2.2.14pre12/arch/alpha/kernel/smp.c 2.2.14pre12-imb/arch/alpha/kernel/smp.c
--- 2.2.14pre12/arch/alpha/kernel/smp.c Thu Dec 9 03:20:56 1999
+++ 2.2.14pre12-imb/arch/alpha/kernel/smp.c Thu Dec 9 14:11:58 1999
@@ -839,6 +839,22 @@
}

static void
+ipi_imb(void)
+{
+ imb();
+}
+
+void
+smp_imb(void)
+{
+ /* Must wait other processors to flush their icache before continue. */
+ if (smp_call_function(ipi_imb, NULL, 1, 1))
+ printk(KERN_CRIT "smp_imb: timed out\n");
+
+ imb();
+}
+
+static void
ipi_flush_tlb_all(void *ignored)
{
tbia();
diff -urN 2.2.14pre12/include/asm-alpha/pgtable.h 2.2.14pre12-imb/include/asm-alpha/pgtable.h
--- 2.2.14pre12/include/asm-alpha/pgtable.h Wed Dec 8 17:42:31 1999
+++ 2.2.14pre12-imb/include/asm-alpha/pgtable.h Thu Dec 9 14:11:46 1999
@@ -17,13 +17,27 @@
#include <asm/spinlock.h> /* For the task lock */


-/* Caches aren't brain-dead on the Alpha. */
-#define flush_cache_all() do { } while (0)
+/* The icache is not coherent with the dcache on alpha, thus before
+ running self modified code we must always run an imb().
+ Actually flush_cache_all() is real overkill as it's recalled from
+ vmalloc() before accessing pagetables and on the Alpha we are not required
+ to flush the icache before doing that, but the semantic of flush_cache_all()
+ requires us to flush _all_ the caches and so we must be correct here. It's
+ instead vmalloc that should be changed to use a more finegrined cache
+ flush operation (I suspect that also other archs doesn't need an icache
+ flush while handling pagetables). OTOH vmalloc is not a performance critical
+ path so after all we can live with it for now. */
+#define flush_cache_all() flush_icache_range(0, 0)
#define flush_cache_mm(mm) do { } while (0)
#define flush_cache_range(mm, start, end) do { } while (0)
#define flush_cache_page(vma, vmaddr) do { } while (0)
#define flush_page_to_ram(page) do { } while (0)
-#define flush_icache_range(start, end) do { } while (0)
+#ifndef __SMP__
+#define flush_icache_range(start, end) imb()
+#else
+#define flush_icache_range(start, end) smp_imb()
+extern void smp_imb(void);
+#endif

/*
* Use a few helper functions to hide the ugly broken ASN

The patch can be downloaded also from here:

ftp://ftp.*.kernel.org/pub/linux/kernel/people/andrea/patches/v2.2/2.2.14pre12/alpha-imb-1.gz

The effect of this bug is that after loading a module the kernel could
end executing random data.

Andrea

PS. Please read the comment I written in the patch before complaing me
that I don't need to do icache flushes while reading pagetables. It's
vmalloc that should be fixed according to the other archs. Check arm and
sparc too.

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/