|
|
Message-ID: <20161025091807.GX19531@linaro.org>
Date: Tue, 25 Oct 2016 18:18:10 +0900
From: AKASHI Takahiro <takahiro.akashi@...aro.org>
To: Colin Vidal <colin@...dal.org>
Cc: kernel-hardening@...ts.openwall.com,
"Reshetova, Elena" <elena.reshetova@...el.com>,
David Windsor <dave@...gbits.org>,
Kees Cook <keescook@...omium.org>,
Hans Liljestrand <ishkamiel@...il.com>
Subject: Re: [RFC 2/2] arm: implementation for HARDENED_ATOMIC
On Tue, Oct 18, 2016 at 04:59:21PM +0200, Colin Vidal wrote:
> This adds arm-specific code in order to support HARDENED_ATOMIC
> feature. When overflow is detected in atomic_t, atomic64_t or
> atomic_long_t, an exception is raised and call
> hardened_atomic_overflow.
>
> Signed-off-by: Colin Vidal <colin@...dal.org>
> ---
> arch/arm/Kconfig | 1 +
> arch/arm/include/asm/atomic.h | 434 +++++++++++++++++++++++++++++-------------
> arch/arm/mm/fault.c | 15 ++
> 3 files changed, 320 insertions(+), 130 deletions(-)
>
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index b5d529f..fcf4a64 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -36,6 +36,7 @@ config ARM
> select HAVE_ARCH_AUDITSYSCALL if (AEABI && !OABI_COMPAT)
> select HAVE_ARCH_BITREVERSE if (CPU_32v7M || CPU_32v7) && !CPU_32v6
> select HAVE_ARCH_HARDENED_USERCOPY
> + select HAVE_ARCH_HARDENED_ATOMIC
> select HAVE_ARCH_JUMP_LABEL if !XIP_KERNEL && !CPU_ENDIAN_BE32 && MMU
> select HAVE_ARCH_KGDB if !CPU_ENDIAN_BE32 && MMU
> select HAVE_ARCH_MMAP_RND_BITS if MMU
> diff --git a/arch/arm/include/asm/atomic.h b/arch/arm/include/asm/atomic.h
> index 66d0e21..fdaee17 100644
> --- a/arch/arm/include/asm/atomic.h
> +++ b/arch/arm/include/asm/atomic.h
> @@ -17,18 +17,52 @@
> #include <linux/irqflags.h>
> #include <asm/barrier.h>
> #include <asm/cmpxchg.h>
> +#include <linux/bug.h>
>
> #define ATOMIC_INIT(i) { (i) }
>
> #ifdef __KERNEL__
>
> +#ifdef CONFIG_HARDENED_ATOMIC
> +#define HARDENED_ATOMIC_INSN "bkpt 0xf103"
> +#define _ASM_EXTABLE(from, to) \
> + ".pushsection __ex_table,\"a\"\n" \
> + ".align 3\n" \
> + ".long "#from","#to"\n" \
> + ".popsection"
> +#define __OVERFLOW_POST \
> + "bvc 3f\n" \
> + "2: "HARDENED_ATOMIC_INSN"\n" \
> + "3:\n"
> +#define __OVERFLOW_POST_RETURN \
> + "bvc 3f\n" \
> + "mov %0,%1\n" \
> + "2: "HARDENED_ATOMIC_INSN"\n" \
> + "3:\n"
> +#define __OVERFLOW_EXTABLE \
> + "4:\n" \
> + _ASM_EXTABLE(2b, 4b)
> +#else
> +#define __OVERFLOW_POST
> +#define __OVERFLOW_POST_RETURN
> +#define __OVERFLOW_EXTABLE
> +#endif
> +
> /*
> * On ARM, ordinary assignment (str instruction) doesn't clear the local
> * strex/ldrex monitor on some implementations. The reason we can use it for
> * atomic_set() is the clrex or dummy strex done on every exception return.
> */
> #define atomic_read(v) READ_ONCE((v)->counter)
> +static inline int atomic_read_wrap(const atomic_wrap_t *v)
> +{
> + return atomic_read(v);
> +}
> #define atomic_set(v,i) WRITE_ONCE(((v)->counter), (i))
> +static inline void atomic_set_wrap(atomic_wrap_t *v, int i)
> +{
> + atomic_set(v, i);
> +}
>
> #if __LINUX_ARM_ARCH__ >= 6
>
> @@ -38,38 +72,46 @@
> * to ensure that the update happens.
> */
>
> -#define ATOMIC_OP(op, c_op, asm_op) \
> -static inline void atomic_##op(int i, atomic_t *v) \
> +#define __ATOMIC_OP(op, suffix, c_op, asm_op, post_op, extable) \
> +static inline void atomic_##op##suffix(int i, atomic##suffix##_t *v) \
> { \
> unsigned long tmp; \
> int result; \
> \
> prefetchw(&v->counter); \
> - __asm__ __volatile__("@ atomic_" #op "\n" \
> + __asm__ __volatile__("@ atomic_" #op #suffix "\n" \
> "1: ldrex %0, [%3]\n" \
> " " #asm_op " %0, %0, %4\n" \
> + post_op \
> " strex %1, %0, [%3]\n" \
> " teq %1, #0\n" \
> -" bne 1b" \
> +" bne 1b\n" \
> + extable \
> : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter) \
> : "r" (&v->counter), "Ir" (i) \
> : "cc"); \
> } \
>
> -#define ATOMIC_OP_RETURN(op, c_op, asm_op) \
> -static inline int atomic_##op##_return_relaxed(int i, atomic_t *v) \
> +#define ATOMIC_OP(op, c_op, asm_op) \
> + __ATOMIC_OP(op, _wrap, c_op, asm_op, , ) \
> + __ATOMIC_OP(op, , c_op, asm_op##s, __OVERFLOW_POST, __OVERFLOW_EXTABLE)
> +
> +#define __ATOMIC_OP_RETURN(op, suffix, c_op, asm_op, post_op, extable) \
> +static inline int atomic_##op##_return##suffix##_relaxed(int i, atomic##suffix##_t *v) \
> { \
> unsigned long tmp; \
> int result; \
> \
> prefetchw(&v->counter); \
> \
> - __asm__ __volatile__("@ atomic_" #op "_return\n" \
> + __asm__ __volatile__("@ atomic_" #op "_return" #suffix "\n" \
> "1: ldrex %0, [%3]\n" \
> " " #asm_op " %0, %0, %4\n" \
> + post_op \
> " strex %1, %0, [%3]\n" \
> " teq %1, #0\n" \
> -" bne 1b" \
> +" bne 1b\n" \
> + extable \
> : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter) \
> : "r" (&v->counter), "Ir" (i) \
> : "cc"); \
> @@ -77,6 +119,11 @@ static inline int atomic_##op##_return_relaxed(int i, atomic_t *v) \
> return result; \
> }
>
> +#define ATOMIC_OP_RETURN(op, c_op, asm_op) \
> + __ATOMIC_OP_RETURN(op, _wrap, c_op, asm_op, , ) \
> + __ATOMIC_OP_RETURN(op, , c_op, asm_op##s, \
> + __OVERFLOW_POST_RETURN, __OVERFLOW_EXTABLE)
> +
This definition will create atomic_add_return_wrap_relaxed(),
but should the name be atomic_add_return_relaxed_wrap()?
(I don't know we need _wrap version of _relaxed functions.
See Elena's atomic-long.h.)
Thanks,
-Takahiro AKASHI
> #define ATOMIC_FETCH_OP(op, c_op, asm_op) \
> static inline int atomic_fetch_##op##_relaxed(int i, atomic_t *v) \
> { \
> @@ -108,26 +155,34 @@ static inline int atomic_fetch_##op##_relaxed(int i, atomic_t *v) \
> #define atomic_fetch_or_relaxed atomic_fetch_or_relaxed
> #define atomic_fetch_xor_relaxed atomic_fetch_xor_relaxed
>
> -static inline int atomic_cmpxchg_relaxed(atomic_t *ptr, int old, int new)
> -{
> - int oldval;
> - unsigned long res;
> -
> - prefetchw(&ptr->counter);
> -
> - do {
> - __asm__ __volatile__("@ atomic_cmpxchg\n"
> - "ldrex %1, [%3]\n"
> - "mov %0, #0\n"
> - "teq %1, %4\n"
> - "strexeq %0, %5, [%3]\n"
> - : "=&r" (res), "=&r" (oldval), "+Qo" (ptr->counter)
> - : "r" (&ptr->counter), "Ir" (old), "r" (new)
> - : "cc");
> - } while (res);
> -
> - return oldval;
> +#define __ATOMIC_CMPXCHG_RELAXED(suffix) \
> +static inline int atomic_cmpxchg##suffix##_relaxed(atomic##suffix##_t *ptr, \
> + int old, int new) \
> +{ \
> + int oldval; \
> + unsigned long res; \
> + \
> + prefetchw(&ptr->counter); \
> + \
> + do { \
> + __asm__ __volatile__("@ atomic_cmpxchg" #suffix "\n" \
> + "ldrex %1, [%3]\n" \
> + "mov %0, #0\n" \
> + "teq %1, %4\n" \
> + "strexeq %0, %5, [%3]\n" \
> + : "=&r" (res), "=&r" (oldval), "+Qo" (ptr->counter) \
> + : "r" (&ptr->counter), "Ir" (old), "r" (new) \
> + : "cc"); \
> + } while (res); \
> + \
> + return oldval; \
> }
> +
> +__ATOMIC_CMPXCHG_RELAXED()
> +__ATOMIC_CMPXCHG_RELAXED(_wrap)
> +
> +#undef __ATOMIC_CMPXCHG_RELAXED
> +
> #define atomic_cmpxchg_relaxed atomic_cmpxchg_relaxed
>
> static inline int __atomic_add_unless(atomic_t *v, int a, int u)
> @@ -141,12 +196,21 @@ static inline int __atomic_add_unless(atomic_t *v, int a, int u)
> __asm__ __volatile__ ("@ atomic_add_unless\n"
> "1: ldrex %0, [%4]\n"
> " teq %0, %5\n"
> -" beq 2f\n"
> -" add %1, %0, %6\n"
> +" beq 4f\n"
> +" adds %1, %0, %6\n"
> +
> +#ifdef CONFIG_HARDENED_ATOMIC
> +" bvc 3f\n"
> +"2: "HARDENED_ATOMIC_INSN"\n"
> +"3:\n"
> +#endif
> " strex %2, %1, [%4]\n"
> " teq %2, #0\n"
> " bne 1b\n"
> -"2:"
> +"4:"
> +#ifdef CONFIG_HARDENED_ATOMIC
> + _ASM_EXTABLE(2b, 4b)
> +#endif
> : "=&r" (oldval), "=&r" (newval), "=&r" (tmp), "+Qo" (v->counter)
> : "r" (&v->counter), "r" (u), "r" (a)
> : "cc");
> @@ -163,8 +227,8 @@ static inline int __atomic_add_unless(atomic_t *v, int a, int u)
> #error SMP not supported on pre-ARMv6 CPUs
> #endif
>
> -#define ATOMIC_OP(op, c_op, asm_op) \
> -static inline void atomic_##op(int i, atomic_t *v) \
> +#define __ATOMIC_OP(op, suffix, c_op, asm_op) \
> +static inline void atomic_##op##suffix(int i, atomic##suffix##_t *v) \
> { \
> unsigned long flags; \
> \
> @@ -173,8 +237,12 @@ static inline void atomic_##op(int i, atomic_t *v) \
> raw_local_irq_restore(flags); \
> } \
>
> -#define ATOMIC_OP_RETURN(op, c_op, asm_op) \
> -static inline int atomic_##op##_return(int i, atomic_t *v) \
> +#define ATOMIC_OP(op, c_op, asm_op) \
> + __ATOMIC_OP(op, _wrap, c_op, asm_op) \
> + __ATOMIC_OP(op, , c_op, asm_op)
> +
> +#define __ATOMIC_OP_RETURN(op, suffix, c_op, asm_op) \
> +static inline int atomic_##op##_return##suffix(int i, atomic##suffix##_t *v) \
> { \
> unsigned long flags; \
> int val; \
> @@ -187,6 +255,10 @@ static inline int atomic_##op##_return(int i, atomic_t *v) \
> return val; \
> }
>
> +#define ATOMIC_OP_RETURN(op, c_op, asm_op) \
> + __ATOMIC_OP_RETURN(op, wrap, c_op, asm_op) \
> + __ATOMIC_OP_RETURN(op, , c_op, asm_op)
> +
> #define ATOMIC_FETCH_OP(op, c_op, asm_op) \
> static inline int atomic_fetch_##op(int i, atomic_t *v) \
> { \
> @@ -215,6 +287,11 @@ static inline int atomic_cmpxchg(atomic_t *v, int old, int new)
> return ret;
> }
>
> +static inline int atomic_cmpxchg_wrap(atomic_wrap_t *v, int old, int new)
> +{
> + return atomic_cmpxchg((atomic_t *)v, old, new);
> +}
> +
> static inline int __atomic_add_unless(atomic_t *v, int a, int u)
> {
> int c, old;
> @@ -227,6 +304,11 @@ static inline int __atomic_add_unless(atomic_t *v, int a, int u)
>
> #endif /* __LINUX_ARM_ARCH__ */
>
> +static inline int __atomic_add_unless_wrap(atomic_wrap_t *v, int a, int u)
> +{
> + return __atomic_add_unless((atomic_t *)v, a, u);
> +}
> +
> #define ATOMIC_OPS(op, c_op, asm_op) \
> ATOMIC_OP(op, c_op, asm_op) \
> ATOMIC_OP_RETURN(op, c_op, asm_op) \
> @@ -250,18 +332,30 @@ ATOMIC_OPS(xor, ^=, eor)
> #undef ATOMIC_OPS
> #undef ATOMIC_FETCH_OP
> #undef ATOMIC_OP_RETURN
> +#undef __ATOMIC_OP_RETURN
> #undef ATOMIC_OP
> +#undef __ATOMIC_OP
>
> #define atomic_xchg(v, new) (xchg(&((v)->counter), new))
> -
> +#define atomic_xchg_wrap(v, new) atomic_xchg(v, new)
> #define atomic_inc(v) atomic_add(1, v)
> +static inline void atomic_inc_wrap(atomic_wrap_t *v)
> +{
> + atomic_add_wrap(1, v);
> +}
> #define atomic_dec(v) atomic_sub(1, v)
> +static inline void atomic_dec_wrap(atomic_wrap_t *v)
> +{
> + atomic_sub_wrap(1, v);
> +}
>
> #define atomic_inc_and_test(v) (atomic_add_return(1, v) == 0)
> #define atomic_dec_and_test(v) (atomic_sub_return(1, v) == 0)
> #define atomic_inc_return_relaxed(v) (atomic_add_return_relaxed(1, v))
> +#define atomic_inc_return_wrap_relaxed(v) (atomic_add_return_wrap_relaxed(1, v))
> #define atomic_dec_return_relaxed(v) (atomic_sub_return_relaxed(1, v))
> #define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0)
> +#define atomic_sub_and_test_wrap(i, v) (atomic_sub_return_wrap(i, v) == 0)
>
> #define atomic_add_negative(i,v) (atomic_add_return(i, v) < 0)
>
> @@ -270,62 +364,81 @@ typedef struct {
> long long counter;
> } atomic64_t;
>
> +#ifdef CONFIG_HARDENED_ATOMIC
> +typedef struct {
> + long long counter;
> +} atomic64_wrap_t;
> +#else
> +typedef atomic64_t atomic64_wrap_t;
> +#endif
> +
> #define ATOMIC64_INIT(i) { (i) }
>
> -#ifdef CONFIG_ARM_LPAE
> -static inline long long atomic64_read(const atomic64_t *v)
> -{
> - long long result;
> +#define __ATOMIC64_READ(suffix, asm_op) \
> +static inline long long \
> +atomic64_read##suffix(const atomic64##suffix##_t *v) \
> +{ \
> + long long result; \
> + \
> + __asm__ __volatile__("@ atomic64_read" #suffix "\n" \
> +" " #asm_op " %0, %H0, [%1]" \
> + : "=&r" (result) \
> + : "r" (&v->counter), "Qo" (v->counter) \
> + ); \
> + \
> + return result; \
> +}
>
> - __asm__ __volatile__("@ atomic64_read\n"
> -" ldrd %0, %H0, [%1]"
> - : "=&r" (result)
> - : "r" (&v->counter), "Qo" (v->counter)
> - );
> +#ifdef CONFIG_ARM_LPAE
> +__ATOMIC64_READ(, ldrd)
> +__ATOMIC64_READ(wrap, ldrd)
>
> - return result;
> +#define __ATOMIC64_SET(suffix) \
> +static inline void atomic64_set##suffix(atomic64##suffix##_t *v, long long i) \
> +{ \
> + __asm__ __volatile__("@ atomic64_set" #suffix "\n" \
> +" strd %2, %H2, [%1]" \
> + : "=Qo" (v->counter) \
> + : "r" (&v->counter), "r" (i) \
> + ); \
> }
>
> -static inline void atomic64_set(atomic64_t *v, long long i)
> -{
> - __asm__ __volatile__("@ atomic64_set\n"
> -" strd %2, %H2, [%1]"
> - : "=Qo" (v->counter)
> - : "r" (&v->counter), "r" (i)
> - );
> -}
> -#else
> -static inline long long atomic64_read(const atomic64_t *v)
> -{
> - long long result;
> +__ATOMIC64_SET()
> +__ATOMIC64_SET(_wrap)
>
> - __asm__ __volatile__("@ atomic64_read\n"
> -" ldrexd %0, %H0, [%1]"
> - : "=&r" (result)
> - : "r" (&v->counter), "Qo" (v->counter)
> - );
> +#undef __ATOMIC64
>
> - return result;
> +#else
> +__ATOMIC64_READ(, ldrexd)
> +__ATOMIC64_READ(_wrap, ldrexd)
> +
> +#define __ATOMIC64_SET(suffix) \
> +static inline void atomic64_set##suffix(atomic64##suffix##_t *v, long long i) \
> +{ \
> + long long tmp; \
> + \
> + prefetchw(&v->counter); \
> + __asm__ __volatile__("@ atomic64_set" #suffix"\n" \
> +"1: ldrexd %0, %H0, [%2]\n" \
> +" strexd %0, %3, %H3, [%2]\n" \
> +" teq %0, #0\n" \
> +" bne 1b" \
> + : "=&r" (tmp), "=Qo" (v->counter) \
> + : "r" (&v->counter), "r" (i) \
> + : "cc"); \
> }
>
> -static inline void atomic64_set(atomic64_t *v, long long i)
> -{
> - long long tmp;
> +__ATOMIC64_SET()
> +__ATOMIC64_SET(_wrap)
> +
> +#undef __ATOMIC64_SET
>
> - prefetchw(&v->counter);
> - __asm__ __volatile__("@ atomic64_set\n"
> -"1: ldrexd %0, %H0, [%2]\n"
> -" strexd %0, %3, %H3, [%2]\n"
> -" teq %0, #0\n"
> -" bne 1b"
> - : "=&r" (tmp), "=Qo" (v->counter)
> - : "r" (&v->counter), "r" (i)
> - : "cc");
> -}
> #endif
>
> -#define ATOMIC64_OP(op, op1, op2) \
> -static inline void atomic64_##op(long long i, atomic64_t *v) \
> +#undef __ATOMIC64_READ
> +
> +#define __ATOMIC64_OP(op, suffix, op1, op2, post_op, extable) \
> +static inline void atomic64_##op##suffix(long long i, atomic64##suffix##_t *v) \
> { \
> long long result; \
> unsigned long tmp; \
> @@ -335,17 +448,31 @@ static inline void atomic64_##op(long long i, atomic64_t *v) \
> "1: ldrexd %0, %H0, [%3]\n" \
> " " #op1 " %Q0, %Q0, %Q4\n" \
> " " #op2 " %R0, %R0, %R4\n" \
> + post_op \
> " strexd %1, %0, %H0, [%3]\n" \
> " teq %1, #0\n" \
> -" bne 1b" \
> +" bne 1b\n" \
> + extable \
> : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter) \
> : "r" (&v->counter), "r" (i) \
> : "cc"); \
> -} \
> +}
>
> -#define ATOMIC64_OP_RETURN(op, op1, op2) \
> +#define ATOMIC64_OP(op, op1, op2) \
> + __ATOMIC64_OP(op, _wrap, op1, op2, , ) \
> + __ATOMIC64_OP(op, , op1, op2##s, __OVERFLOW_POST, __OVERFLOW_EXTABLE)
> +
> +#undef __OVERFLOW_POST_RETURN
> +#define __OVERFLOW_POST_RETURN \
> + "bvc 3f\n" \
> + "mov %0, %1\n" \
> + "mov %H0, %H1\n" \
> + "2: "HARDENED_ATOMIC_INSN"\n" \
> + "3:\n"
> +
> +#define __ATOMIC64_OP_RETURN(op, suffix, op1, op2, post_op, extable) \
> static inline long long \
> -atomic64_##op##_return_relaxed(long long i, atomic64_t *v) \
> +atomic64_##op##_return##suffix##_relaxed(long long i, atomic64##suffix##_t *v) \
> { \
> long long result; \
> unsigned long tmp; \
> @@ -356,9 +483,11 @@ atomic64_##op##_return_relaxed(long long i, atomic64_t *v) \
> "1: ldrexd %0, %H0, [%3]\n" \
> " " #op1 " %Q0, %Q0, %Q4\n" \
> " " #op2 " %R0, %R0, %R4\n" \
> + post_op \
> " strexd %1, %0, %H0, [%3]\n" \
> " teq %1, #0\n" \
> -" bne 1b" \
> +" bne 1b\n" \
> + extable \
> : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter) \
> : "r" (&v->counter), "r" (i) \
> : "cc"); \
> @@ -366,6 +495,11 @@ atomic64_##op##_return_relaxed(long long i, atomic64_t *v) \
> return result; \
> }
>
> +#define ATOMIC64_OP_RETURN(op, op1, op2) \
> + __ATOMIC64_OP_RETURN(op, _wrap, op1, op2, , ) \
> + __ATOMIC64_OP_RETURN(op, , op1, op2##s, __OVERFLOW_POST_RETURN, \
> + __OVERFLOW_EXTABLE)
> +
> #define ATOMIC64_FETCH_OP(op, op1, op2) \
> static inline long long \
> atomic64_fetch_##op##_relaxed(long long i, atomic64_t *v) \
> @@ -422,70 +556,98 @@ ATOMIC64_OPS(xor, eor, eor)
> #undef ATOMIC64_OPS
> #undef ATOMIC64_FETCH_OP
> #undef ATOMIC64_OP_RETURN
> +#undef __ATOMIC64_OP_RETURN
> #undef ATOMIC64_OP
> -
> -static inline long long
> -atomic64_cmpxchg_relaxed(atomic64_t *ptr, long long old, long long new)
> -{
> - long long oldval;
> - unsigned long res;
> -
> - prefetchw(&ptr->counter);
> -
> - do {
> - __asm__ __volatile__("@ atomic64_cmpxchg\n"
> - "ldrexd %1, %H1, [%3]\n"
> - "mov %0, #0\n"
> - "teq %1, %4\n"
> - "teqeq %H1, %H4\n"
> - "strexdeq %0, %5, %H5, [%3]"
> - : "=&r" (res), "=&r" (oldval), "+Qo" (ptr->counter)
> - : "r" (&ptr->counter), "r" (old), "r" (new)
> - : "cc");
> - } while (res);
> -
> - return oldval;
> +#undef __ATOMIC64_OP
> +#undef __OVERFLOW_EXTABLE
> +#undef __OVERFLOW_POST_RETURN
> +#undef __OVERFLOW_RETURN
> +
> +#define __ATOMIC64_CMPXCHG_RELAXED(suffix) \
> +static inline long long atomic64_cmpxchg##suffix##_relaxed( \
> + atomic64##suffix##_t *ptr, long long old, long long new) \
> +{ \
> + long long oldval; \
> + unsigned long res; \
> + \
> + prefetchw(&ptr->counter); \
> + \
> + do { \
> + __asm__ __volatile__("@ atomic64_cmpxchg" #suffix "\n" \
> + "ldrexd %1, %H1, [%3]\n" \
> + "mov %0, #0\n" \
> + "teq %1, %4\n" \
> + "teqeq %H1, %H4\n" \
> + "strexdeq %0, %5, %H5, [%3]" \
> + : "=&r" (res), "=&r" (oldval), "+Qo" (ptr->counter) \
> + : "r" (&ptr->counter), "r" (old), "r" (new) \
> + : "cc"); \
> + } while (res); \
> + \
> + return oldval; \
> }
> -#define atomic64_cmpxchg_relaxed atomic64_cmpxchg_relaxed
>
> -static inline long long atomic64_xchg_relaxed(atomic64_t *ptr, long long new)
> -{
> - long long result;
> - unsigned long tmp;
> -
> - prefetchw(&ptr->counter);
> +__ATOMIC64_CMPXCHG_RELAXED()
> +__ATOMIC64_CMPXCHG_RELAXED(_wrap)
> +#define atomic64_cmpxchg_relaxed atomic64_cmpxchg_relaxed
>
> - __asm__ __volatile__("@ atomic64_xchg\n"
> -"1: ldrexd %0, %H0, [%3]\n"
> -" strexd %1, %4, %H4, [%3]\n"
> -" teq %1, #0\n"
> -" bne 1b"
> - : "=&r" (result), "=&r" (tmp), "+Qo" (ptr->counter)
> - : "r" (&ptr->counter), "r" (new)
> - : "cc");
> +#undef __ATOMIC64_CMPXCHG_RELAXED
>
> - return result;
> +#define __ATOMIC64_XCHG_RELAXED(suffix) \
> +static inline long long atomic64_xchg##suffix##_relaxed( \
> + atomic64##suffix##_t *ptr, long long new) \
> +{ \
> + long long result; \
> + unsigned long tmp; \
> + \
> + prefetchw(&ptr->counter); \
> + \
> + __asm__ __volatile__("@ atomic64_xchg" #suffix "\n" \
> +"1: ldrexd %0, %H0, [%3]\n" \
> +" strexd %1, %4, %H4, [%3]\n" \
> +" teq %1, #0\n" \
> +" bne 1b" \
> + : "=&r" (result), "=&r" (tmp), "+Qo" (ptr->counter) \
> + : "r" (&ptr->counter), "r" (new) \
> + : "cc"); \
> + \
> + return result; \
> }
> +
> +__ATOMIC64_XCHG_RELAXED()
> +__ATOMIC64_XCHG_RELAXED(_wrap)
> #define atomic64_xchg_relaxed atomic64_xchg_relaxed
>
> +#undef __ATOMIC64_XCHG_RELAXED
> +
> static inline long long atomic64_dec_if_positive(atomic64_t *v)
> {
> long long result;
> - unsigned long tmp;
> + u64 tmp;
>
> smp_mb();
> prefetchw(&v->counter);
>
> __asm__ __volatile__("@ atomic64_dec_if_positive\n"
> -"1: ldrexd %0, %H0, [%3]\n"
> -" subs %Q0, %Q0, #1\n"
> -" sbc %R0, %R0, #0\n"
> +"1: ldrexd %1, %H1, [%3]\n"
> +" subs %Q0, %Q1, #1\n"
> +" sbcs %R0, %R1, #0\n"
> +#ifdef CONFIG_HARDENED_ATOMIC
> +" bvc 3f\n"
> +" mov %Q0, %Q1\n"
> +" mov %R0, %R1\n"
> +"2: "HARDENED_ATOMIC_INSN"\n"
> +"3:\n"
> +#endif
> " teq %R0, #0\n"
> -" bmi 2f\n"
> +" bmi 4f\n"
> " strexd %1, %0, %H0, [%3]\n"
> " teq %1, #0\n"
> " bne 1b\n"
> -"2:"
> +"4:\n"
> +#ifdef CONFIG_HARDENED_ATOMIC
> + _ASM_EXTABLE(2b, 4b)
> +#endif
> : "=&r" (result), "=&r" (tmp), "+Qo" (v->counter)
> : "r" (&v->counter)
> : "cc");
> @@ -509,13 +671,21 @@ static inline int atomic64_add_unless(atomic64_t *v, long long a, long long u)
> " teq %0, %5\n"
> " teqeq %H0, %H5\n"
> " moveq %1, #0\n"
> -" beq 2f\n"
> +" beq 4f\n"
> " adds %Q0, %Q0, %Q6\n"
> -" adc %R0, %R0, %R6\n"
> +" adcs %R0, %R0, %R6\n"
> +#ifdef CONFIG_HARDENED_ATOMIC
> +" bvc 3f\n"
> +"2: "HARDENED_ATOMIC_INSN"\n"
> +"3:\n"
> +#endif
> " strexd %2, %0, %H0, [%4]\n"
> " teq %2, #0\n"
> " bne 1b\n"
> -"2:"
> +"4:\n"
> +#ifdef CONFIG_HARDENED_ATOMIC
> + _ASM_EXTABLE(2b, 4b)
> +#endif
> : "=&r" (val), "+r" (ret), "=&r" (tmp), "+Qo" (v->counter)
> : "r" (&v->counter), "r" (u), "r" (a)
> : "cc");
> @@ -529,6 +699,7 @@ static inline int atomic64_add_unless(atomic64_t *v, long long a, long long u)
> #define atomic64_add_negative(a, v) (atomic64_add_return((a), (v)) < 0)
> #define atomic64_inc(v) atomic64_add(1LL, (v))
> #define atomic64_inc_return_relaxed(v) atomic64_add_return_relaxed(1LL, (v))
> +#define atomic64_inc_return_wrap_relaxed(v) atomic64_add_return_wrap_relaxed(1LL, v)
> #define atomic64_inc_and_test(v) (atomic64_inc_return(v) == 0)
> #define atomic64_sub_and_test(a, v) (atomic64_sub_return((a), (v)) == 0)
> #define atomic64_dec(v) atomic64_sub(1LL, (v))
> @@ -536,6 +707,9 @@ static inline int atomic64_add_unless(atomic64_t *v, long long a, long long u)
> #define atomic64_dec_and_test(v) (atomic64_dec_return((v)) == 0)
> #define atomic64_inc_not_zero(v) atomic64_add_unless((v), 1LL, 0LL)
>
> +#define atomic64_inc_wrap(v) atomic64_add_wrap(1LL, v)
> +#define atomic64_dec_wrap(v) atomic64_sub_wrap(1LL, v)
> +
> #endif /* !CONFIG_GENERIC_ATOMIC64 */
> #endif
> #endif
> diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c
> index 3a2e678..ce8ee00 100644
> --- a/arch/arm/mm/fault.c
> +++ b/arch/arm/mm/fault.c
> @@ -580,6 +580,21 @@ do_PrefetchAbort(unsigned long addr, unsigned int ifsr, struct pt_regs *regs)
> const struct fsr_info *inf = ifsr_info + fsr_fs(ifsr);
> struct siginfo info;
>
> +#ifdef CONFIG_HARDENED_ATOMIC
> + if (fsr_fs(ifsr) == FAULT_CODE_DEBUG) {
> + unsigned long pc = instruction_pointer(regs);
> + unsigned int bkpt;
> +
> + if (!probe_kernel_address((const unsigned int *)pc, bkpt) &&
> + cpu_to_le32(bkpt) == 0xe12f1073) {
> + current->thread.error_code = ifsr;
> + current->thread.trap_no = 0;
> + hardened_atomic_overflow(regs);
> + fixup_exception(regs);
> + return;
> + }
> + }
> +#endif
> if (!inf->fn(addr, ifsr | FSR_LNX_PF, regs))
> return;
>
> --
> 2.7.4
>
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.