m68k: Disable/restore interrupts in hwreg_present()/hwreg_write()
authorGeert Uytterhoeven <geert@linux-m68k.org>
Sun, 28 Sep 2014 08:50:06 +0000 (10:50 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 30 Oct 2014 16:35:11 +0000 (09:35 -0700)
commit e4dc601bf99ccd1c95b7e6eef1d3cf3c4b0d4961 upstream.

hwreg_present() and hwreg_write() temporarily change the VBR register to
another vector table. This table contains a valid bus error handler
only, all other entries point to arbitrary addresses.

If an interrupt comes in while the temporary table is active, the
processor will start executing at such an arbitrary address, and the
kernel will crash.

While most callers run early, before interrupts are enabled, or
explicitly disable interrupts, Finn Thain pointed out that macsonic has
one callsite that doesn't, causing intermittent boot crashes.
There's another unsafe callsite in hilkbd.

Fix this for good by disabling and restoring interrupts inside
hwreg_present() and hwreg_write().

Explicitly disabling interrupts can be removed from the callsites later.

Reported-by: Finn Thain <fthain@telegraphics.com.au>
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/m68k/mm/hwtest.c

index 2c7dde3c6430fc3bbfe1de8d812253ee90173d3c..2a5259fd23ebc532bfc73b6d54ec2d800cd95af0 100644 (file)
 int hwreg_present( volatile void *regp )
 {
     int        ret = 0;
+    unsigned long flags;
     long       save_sp, save_vbr;
     long       tmp_vectors[3];
 
+    local_irq_save(flags);
     __asm__ __volatile__
        (       "movec  %/vbr,%2\n\t"
                "movel  #Lberr1,%4@(8)\n\t"
@@ -46,6 +48,7 @@ int hwreg_present( volatile void *regp )
                : "=&d" (ret), "=&r" (save_sp), "=&r" (save_vbr)
                : "a" (regp), "a" (tmp_vectors)
                 );
+    local_irq_restore(flags);
 
     return( ret );
 }
@@ -58,9 +61,11 @@ EXPORT_SYMBOL(hwreg_present);
 int hwreg_write( volatile void *regp, unsigned short val )
 {
        int             ret;
+       unsigned long flags;
        long    save_sp, save_vbr;
        long    tmp_vectors[3];
 
+       local_irq_save(flags);
        __asm__ __volatile__
        (       "movec  %/vbr,%2\n\t"
                "movel  #Lberr2,%4@(8)\n\t"
@@ -78,6 +83,7 @@ int hwreg_write( volatile void *regp, unsigned short val )
                : "=&d" (ret), "=&r" (save_sp), "=&r" (save_vbr)
                : "a" (regp), "a" (tmp_vectors), "g" (val)
        );
+       local_irq_restore(flags);
 
        return( ret );
 }