| From e4dc601bf99ccd1c95b7e6eef1d3cf3c4b0d4961 Mon Sep 17 00:00:00 2001 |
| From: Geert Uytterhoeven <geert@linux-m68k.org> |
| Date: Sun, 28 Sep 2014 10:50:06 +0200 |
| Subject: m68k: Disable/restore interrupts in hwreg_present()/hwreg_write() |
| |
| From: Geert Uytterhoeven <geert@linux-m68k.org> |
| |
| 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 | 6 ++++++ |
| 1 file changed, 6 insertions(+) |
| |
| --- a/arch/m68k/mm/hwtest.c |
| +++ b/arch/m68k/mm/hwtest.c |
| @@ -28,9 +28,11 @@ |
| 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, un |
| : "=&d" (ret), "=&r" (save_sp), "=&r" (save_vbr) |
| : "a" (regp), "a" (tmp_vectors), "g" (val) |
| ); |
| + local_irq_restore(flags); |
| |
| return( ret ); |
| } |