Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e4dc601b authored by Geert Uytterhoeven's avatar Geert Uytterhoeven
Browse files

m68k: Disable/restore interrupts in hwreg_present()/hwreg_write()



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: default avatarFinn Thain <fthain@telegraphics.com.au>
Signed-off-by: default avatarGeert Uytterhoeven <geert@linux-m68k.org>
Cc: stable@vger.kernel.org
parent fd4d453b
Loading
Loading
Loading
Loading
+6 −0
Original line number Original line Diff line number Diff line
@@ -28,9 +28,11 @@
int hwreg_present( volatile void *regp )
int hwreg_present( volatile void *regp )
{
{
    int	ret = 0;
    int	ret = 0;
    unsigned long flags;
    long	save_sp, save_vbr;
    long	save_sp, save_vbr;
    long	tmp_vectors[3];
    long	tmp_vectors[3];


    local_irq_save(flags);
    __asm__ __volatile__
    __asm__ __volatile__
	(	"movec	%/vbr,%2\n\t"
	(	"movec	%/vbr,%2\n\t"
		"movel	#Lberr1,%4@(8)\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)
		: "=&d" (ret), "=&r" (save_sp), "=&r" (save_vbr)
		: "a" (regp), "a" (tmp_vectors)
		: "a" (regp), "a" (tmp_vectors)
                );
                );
    local_irq_restore(flags);


    return( ret );
    return( ret );
}
}
@@ -58,9 +61,11 @@ EXPORT_SYMBOL(hwreg_present);
int hwreg_write( volatile void *regp, unsigned short val )
int hwreg_write( volatile void *regp, unsigned short val )
{
{
	int		ret;
	int		ret;
	unsigned long flags;
	long	save_sp, save_vbr;
	long	save_sp, save_vbr;
	long	tmp_vectors[3];
	long	tmp_vectors[3];


	local_irq_save(flags);
	__asm__ __volatile__
	__asm__ __volatile__
	(	"movec	%/vbr,%2\n\t"
	(	"movec	%/vbr,%2\n\t"
		"movel	#Lberr2,%4@(8)\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)
		: "=&d" (ret), "=&r" (save_sp), "=&r" (save_vbr)
		: "a" (regp), "a" (tmp_vectors), "g" (val)
		: "a" (regp), "a" (tmp_vectors), "g" (val)
	);
	);
	local_irq_restore(flags);


	return( ret );
	return( ret );
}
}