arm64: add fault handling to SWP emulation

Add excpetion table and fixup for SWP/SWPB instruction emulation.
This prevents the kernel from panicing when emulating a SWP/SWPB
instruction that access unmapped memory.

Change-Id: I4a9ca34fa161a0f306cdb663827d9bee39cec733
Signed-off-by: Alex Van Brunt <avanbrunt@nvidia.com>
Reviewed-on: http://git-master/r/370278
This commit is contained in:
Alex Van Brunt
2014-02-20 18:18:53 -08:00
committed by Greg Hackmann
parent d0c5e1444d
commit 70f16b592e

View File

@@ -32,6 +32,71 @@
#include <asm/system_misc.h>
#include <linux/debugfs.h>
/*
* Error-checking SWP macros implemented using ldrex{b}/strex{b}
*/
static int swpb(u8 in, u8 *out, u8 *addr)
{
u8 _out;
int res;
int err = 0;
do {
__asm__ __volatile__(
"0: ldxrb %w1, %4\n"
"1: stxrb %w0, %w3, %4\n"
"2:\n"
" .section .fixup,\"ax\"\n"
" .align 2\n"
"3: mov %w2, %5\n"
" b 2b\n"
" .previous\n"
" .section __ex_table,\"a\"\n"
" .align 3\n"
" .quad 0b, 3b\n"
" .quad 1b, 3b\n"
" .previous"
: "=&r" (res), "=r" (_out), "=r" (err)
: "r" (in), "Q" (addr), "i" (-EFAULT)
: "cc", "memory");
} while (err == 0 && res != 0);
if (err == 0)
*out = _out;
return err;
}
static int swp(u32 in, u32 *out, u32 *addr)
{
u32 _out;
int res;
int err = 0;
do {
__asm__ __volatile__(
"0: ldxr %w1, %4\n"
"1: stxr %w0, %w3, %4\n"
"2:\n"
" .section .fixup,\"ax\"\n"
" .align 2\n"
"3: mov %w2, %5\n"
" b 2b\n"
" .previous\n"
" .section __ex_table,\"a\"\n"
" .align 3\n"
" .quad 0b, 3b\n"
" .quad 1b, 3b\n"
" .previous"
: "=&r" (res), "=r" (_out), "=r" (err)
: "r" (in), "Q" (addr), "i" (-EFAULT)
: "cc", "memory");
} while (err == 0 && res != 0);
if (err == 0)
*out = _out;
return err;
}
/*
* Macros/defines for extracting register numbers from instruction.
*/
@@ -61,6 +126,7 @@ static int swp_handler(struct pt_regs *regs, unsigned int instr)
u32 destreg, data, type;
uintptr_t address;
unsigned int res = 0;
int err;
u32 temp32;
u8 temp8;
@@ -100,9 +166,9 @@ static int swp_handler(struct pt_regs *regs, unsigned int instr)
res = -EFAULT;
}
if (type == TYPE_SWPB) {
do {
temp8 = ldax8((u8 *) address);
} while (stlx8((u8 *) address, (u8) data));
err = swpb((u8) data, &temp8, (u8 *) address);
if (err)
return err;
regs->regs[destreg] = temp8;
regs->pc += 4;
swpb_count++;
@@ -111,9 +177,9 @@ static int swp_handler(struct pt_regs *regs, unsigned int instr)
pr_debug("SWP instruction on unaligned pointer!\n");
return -EFAULT;
} else {
do {
temp32 = ldax32((u32 *) address);
} while (stlx32((u32 *) address, (u32) data));
err = swp((u32) data, &temp32, (u32 *) address);
if (err)
return err;
regs->regs[destreg] = temp32;
regs->pc += 4;
swp_count++;