summaryrefslogtreecommitdiff
path: root/arch/arc/kernel/unaligned.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arc/kernel/unaligned.c')
-rw-r--r--arch/arc/kernel/unaligned.c55
1 files changed, 36 insertions, 19 deletions
diff --git a/arch/arc/kernel/unaligned.c b/arch/arc/kernel/unaligned.c
index c0f832f595d3..3b2d8b1bd271 100644
--- a/arch/arc/kernel/unaligned.c
+++ b/arch/arc/kernel/unaligned.c
@@ -1,20 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011-2012 Synopsys (www.synopsys.com)
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* vineetg : May 2011
* -Adapted (from .26 to .35)
* -original contribution by Tim.yao@amlogic.com
- *
*/
#include <linux/types.h>
+#include <linux/perf_event.h>
#include <linux/ptrace.h>
#include <linux/uaccess.h>
#include <asm/disasm.h>
+#include "unaligned.h"
+
+#ifdef CONFIG_CPU_BIG_ENDIAN
+#define BE 1
+#define FIRST_BYTE_16 "swap %1, %1\n swape %1, %1\n"
+#define FIRST_BYTE_32 "swape %1, %1\n"
+#else
+#define BE 0
+#define FIRST_BYTE_16
+#define FIRST_BYTE_32
+#endif
#define __get8_unaligned_check(val, addr, err) \
__asm__( \
@@ -23,7 +31,7 @@
" .section .fixup,\"ax\"\n" \
" .align 4\n" \
"3: mov %0, 1\n" \
- " b 2b\n" \
+ " j 2b\n" \
" .previous\n" \
" .section __ex_table,\"a\"\n" \
" .align 4\n" \
@@ -36,9 +44,9 @@
do { \
unsigned int err = 0, v, a = addr; \
__get8_unaligned_check(v, a, err); \
- val = v ; \
+ val = v << ((BE) ? 8 : 0); \
__get8_unaligned_check(v, a, err); \
- val |= v << 8; \
+ val |= v << ((BE) ? 0 : 8); \
if (err) \
goto fault; \
} while (0)
@@ -47,13 +55,13 @@
do { \
unsigned int err = 0, v, a = addr; \
__get8_unaligned_check(v, a, err); \
- val = v << 0; \
+ val = v << ((BE) ? 24 : 0); \
__get8_unaligned_check(v, a, err); \
- val |= v << 8; \
+ val |= v << ((BE) ? 16 : 8); \
__get8_unaligned_check(v, a, err); \
- val |= v << 16; \
+ val |= v << ((BE) ? 8 : 16); \
__get8_unaligned_check(v, a, err); \
- val |= v << 24; \
+ val |= v << ((BE) ? 0 : 24); \
if (err) \
goto fault; \
} while (0)
@@ -63,6 +71,7 @@
unsigned int err = 0, v = val, a = addr;\
\
__asm__( \
+ FIRST_BYTE_16 \
"1: stb.ab %1, [%2, 1]\n" \
" lsr %1, %1, 8\n" \
"2: stb %1, [%2]\n" \
@@ -70,7 +79,7 @@
" .section .fixup,\"ax\"\n" \
" .align 4\n" \
"4: mov %0, 1\n" \
- " b 3b\n" \
+ " j 3b\n" \
" .previous\n" \
" .section __ex_table,\"a\"\n" \
" .align 4\n" \
@@ -87,8 +96,9 @@
#define put32_unaligned_check(val, addr) \
do { \
unsigned int err = 0, v = val, a = addr;\
- __asm__( \
\
+ __asm__( \
+ FIRST_BYTE_32 \
"1: stb.ab %1, [%2, 1]\n" \
" lsr %1, %1, 8\n" \
"2: stb.ab %1, [%2, 1]\n" \
@@ -100,7 +110,7 @@
" .section .fixup,\"ax\"\n" \
" .align 4\n" \
"6: mov %0, 1\n" \
- " b 5b\n" \
+ " j 5b\n" \
" .previous\n" \
" .section __ex_table,\"a\"\n" \
" .align 4\n" \
@@ -190,7 +200,6 @@ int misaligned_fixup(unsigned long address, struct pt_regs *regs,
struct callee_regs *cregs)
{
struct disasm_state state;
- char buf[TASK_COMM_LEN];
/* handle user mode only and only if enabled by sysadmin */
if (!user_mode(regs) || !unaligned_enabled)
@@ -202,11 +211,11 @@ int misaligned_fixup(unsigned long address, struct pt_regs *regs,
" performance significantly\n. To enable further"
" logging of such instances, please \n"
" echo 0 > /proc/sys/kernel/ignore-unaligned-usertrap\n",
- get_task_comm(buf, current), task_pid_nr(current));
+ current->comm, task_pid_nr(current));
} else {
/* Add rate limiting if it gets down to it */
pr_warn("%s(%d): unaligned access to/from 0x%lx by PC: 0x%lx\n",
- get_task_comm(buf, current), task_pid_nr(current),
+ current->comm, task_pid_nr(current),
address, regs->ret);
}
@@ -228,13 +237,21 @@ int misaligned_fixup(unsigned long address, struct pt_regs *regs,
if (state.fault)
goto fault;
+ /* clear any remnants of delay slot */
if (delay_mode(regs)) {
- regs->ret = regs->bta;
+ regs->ret = regs->bta & ~1U;
regs->status32 &= ~STATUS_DE_MASK;
} else {
regs->ret += state.instr_len;
+
+ /* handle zero-overhead-loop */
+ if ((regs->ret == regs->lp_end) && (regs->lp_count)) {
+ regs->ret = regs->lp_start;
+ regs->lp_count--;
+ }
}
+ perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, regs, address);
return 0;
fault: