162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci#ifndef ASM_X86_CMPXCHG_H
362306a36Sopenharmony_ci#define ASM_X86_CMPXCHG_H
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <linux/compiler.h>
662306a36Sopenharmony_ci#include <asm/cpufeatures.h>
762306a36Sopenharmony_ci#include <asm/alternative.h> /* Provides LOCK_PREFIX */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci/*
1062306a36Sopenharmony_ci * Non-existent functions to indicate usage errors at link time
1162306a36Sopenharmony_ci * (or compile-time if the compiler implements __compiletime_error().
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ciextern void __xchg_wrong_size(void)
1462306a36Sopenharmony_ci	__compiletime_error("Bad argument size for xchg");
1562306a36Sopenharmony_ciextern void __cmpxchg_wrong_size(void)
1662306a36Sopenharmony_ci	__compiletime_error("Bad argument size for cmpxchg");
1762306a36Sopenharmony_ciextern void __xadd_wrong_size(void)
1862306a36Sopenharmony_ci	__compiletime_error("Bad argument size for xadd");
1962306a36Sopenharmony_ciextern void __add_wrong_size(void)
2062306a36Sopenharmony_ci	__compiletime_error("Bad argument size for add");
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/*
2362306a36Sopenharmony_ci * Constants for operation sizes. On 32-bit, the 64-bit size it set to
2462306a36Sopenharmony_ci * -1 because sizeof will never return -1, thereby making those switch
2562306a36Sopenharmony_ci * case statements guaranteed dead code which the compiler will
2662306a36Sopenharmony_ci * eliminate, and allowing the "missing symbol in the default case" to
2762306a36Sopenharmony_ci * indicate a usage error.
2862306a36Sopenharmony_ci */
2962306a36Sopenharmony_ci#define __X86_CASE_B	1
3062306a36Sopenharmony_ci#define __X86_CASE_W	2
3162306a36Sopenharmony_ci#define __X86_CASE_L	4
3262306a36Sopenharmony_ci#ifdef CONFIG_64BIT
3362306a36Sopenharmony_ci#define __X86_CASE_Q	8
3462306a36Sopenharmony_ci#else
3562306a36Sopenharmony_ci#define	__X86_CASE_Q	-1		/* sizeof will never return -1 */
3662306a36Sopenharmony_ci#endif
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/*
3962306a36Sopenharmony_ci * An exchange-type operation, which takes a value and a pointer, and
4062306a36Sopenharmony_ci * returns the old value.
4162306a36Sopenharmony_ci */
4262306a36Sopenharmony_ci#define __xchg_op(ptr, arg, op, lock)					\
4362306a36Sopenharmony_ci	({								\
4462306a36Sopenharmony_ci	        __typeof__ (*(ptr)) __ret = (arg);			\
4562306a36Sopenharmony_ci		switch (sizeof(*(ptr))) {				\
4662306a36Sopenharmony_ci		case __X86_CASE_B:					\
4762306a36Sopenharmony_ci			asm volatile (lock #op "b %b0, %1\n"		\
4862306a36Sopenharmony_ci				      : "+q" (__ret), "+m" (*(ptr))	\
4962306a36Sopenharmony_ci				      : : "memory", "cc");		\
5062306a36Sopenharmony_ci			break;						\
5162306a36Sopenharmony_ci		case __X86_CASE_W:					\
5262306a36Sopenharmony_ci			asm volatile (lock #op "w %w0, %1\n"		\
5362306a36Sopenharmony_ci				      : "+r" (__ret), "+m" (*(ptr))	\
5462306a36Sopenharmony_ci				      : : "memory", "cc");		\
5562306a36Sopenharmony_ci			break;						\
5662306a36Sopenharmony_ci		case __X86_CASE_L:					\
5762306a36Sopenharmony_ci			asm volatile (lock #op "l %0, %1\n"		\
5862306a36Sopenharmony_ci				      : "+r" (__ret), "+m" (*(ptr))	\
5962306a36Sopenharmony_ci				      : : "memory", "cc");		\
6062306a36Sopenharmony_ci			break;						\
6162306a36Sopenharmony_ci		case __X86_CASE_Q:					\
6262306a36Sopenharmony_ci			asm volatile (lock #op "q %q0, %1\n"		\
6362306a36Sopenharmony_ci				      : "+r" (__ret), "+m" (*(ptr))	\
6462306a36Sopenharmony_ci				      : : "memory", "cc");		\
6562306a36Sopenharmony_ci			break;						\
6662306a36Sopenharmony_ci		default:						\
6762306a36Sopenharmony_ci			__ ## op ## _wrong_size();			\
6862306a36Sopenharmony_ci		}							\
6962306a36Sopenharmony_ci		__ret;							\
7062306a36Sopenharmony_ci	})
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/*
7362306a36Sopenharmony_ci * Note: no "lock" prefix even on SMP: xchg always implies lock anyway.
7462306a36Sopenharmony_ci * Since this is generally used to protect other memory information, we
7562306a36Sopenharmony_ci * use "asm volatile" and "memory" clobbers to prevent gcc from moving
7662306a36Sopenharmony_ci * information around.
7762306a36Sopenharmony_ci */
7862306a36Sopenharmony_ci#define arch_xchg(ptr, v)	__xchg_op((ptr), (v), xchg, "")
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/*
8162306a36Sopenharmony_ci * Atomic compare and exchange.  Compare OLD with MEM, if identical,
8262306a36Sopenharmony_ci * store NEW in MEM.  Return the initial value in MEM.  Success is
8362306a36Sopenharmony_ci * indicated by comparing RETURN with OLD.
8462306a36Sopenharmony_ci */
8562306a36Sopenharmony_ci#define __raw_cmpxchg(ptr, old, new, size, lock)			\
8662306a36Sopenharmony_ci({									\
8762306a36Sopenharmony_ci	__typeof__(*(ptr)) __ret;					\
8862306a36Sopenharmony_ci	__typeof__(*(ptr)) __old = (old);				\
8962306a36Sopenharmony_ci	__typeof__(*(ptr)) __new = (new);				\
9062306a36Sopenharmony_ci	switch (size) {							\
9162306a36Sopenharmony_ci	case __X86_CASE_B:						\
9262306a36Sopenharmony_ci	{								\
9362306a36Sopenharmony_ci		volatile u8 *__ptr = (volatile u8 *)(ptr);		\
9462306a36Sopenharmony_ci		asm volatile(lock "cmpxchgb %2,%1"			\
9562306a36Sopenharmony_ci			     : "=a" (__ret), "+m" (*__ptr)		\
9662306a36Sopenharmony_ci			     : "q" (__new), "0" (__old)			\
9762306a36Sopenharmony_ci			     : "memory");				\
9862306a36Sopenharmony_ci		break;							\
9962306a36Sopenharmony_ci	}								\
10062306a36Sopenharmony_ci	case __X86_CASE_W:						\
10162306a36Sopenharmony_ci	{								\
10262306a36Sopenharmony_ci		volatile u16 *__ptr = (volatile u16 *)(ptr);		\
10362306a36Sopenharmony_ci		asm volatile(lock "cmpxchgw %2,%1"			\
10462306a36Sopenharmony_ci			     : "=a" (__ret), "+m" (*__ptr)		\
10562306a36Sopenharmony_ci			     : "r" (__new), "0" (__old)			\
10662306a36Sopenharmony_ci			     : "memory");				\
10762306a36Sopenharmony_ci		break;							\
10862306a36Sopenharmony_ci	}								\
10962306a36Sopenharmony_ci	case __X86_CASE_L:						\
11062306a36Sopenharmony_ci	{								\
11162306a36Sopenharmony_ci		volatile u32 *__ptr = (volatile u32 *)(ptr);		\
11262306a36Sopenharmony_ci		asm volatile(lock "cmpxchgl %2,%1"			\
11362306a36Sopenharmony_ci			     : "=a" (__ret), "+m" (*__ptr)		\
11462306a36Sopenharmony_ci			     : "r" (__new), "0" (__old)			\
11562306a36Sopenharmony_ci			     : "memory");				\
11662306a36Sopenharmony_ci		break;							\
11762306a36Sopenharmony_ci	}								\
11862306a36Sopenharmony_ci	case __X86_CASE_Q:						\
11962306a36Sopenharmony_ci	{								\
12062306a36Sopenharmony_ci		volatile u64 *__ptr = (volatile u64 *)(ptr);		\
12162306a36Sopenharmony_ci		asm volatile(lock "cmpxchgq %2,%1"			\
12262306a36Sopenharmony_ci			     : "=a" (__ret), "+m" (*__ptr)		\
12362306a36Sopenharmony_ci			     : "r" (__new), "0" (__old)			\
12462306a36Sopenharmony_ci			     : "memory");				\
12562306a36Sopenharmony_ci		break;							\
12662306a36Sopenharmony_ci	}								\
12762306a36Sopenharmony_ci	default:							\
12862306a36Sopenharmony_ci		__cmpxchg_wrong_size();					\
12962306a36Sopenharmony_ci	}								\
13062306a36Sopenharmony_ci	__ret;								\
13162306a36Sopenharmony_ci})
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci#define __cmpxchg(ptr, old, new, size)					\
13462306a36Sopenharmony_ci	__raw_cmpxchg((ptr), (old), (new), (size), LOCK_PREFIX)
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci#define __sync_cmpxchg(ptr, old, new, size)				\
13762306a36Sopenharmony_ci	__raw_cmpxchg((ptr), (old), (new), (size), "lock; ")
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci#define __cmpxchg_local(ptr, old, new, size)				\
14062306a36Sopenharmony_ci	__raw_cmpxchg((ptr), (old), (new), (size), "")
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci#ifdef CONFIG_X86_32
14362306a36Sopenharmony_ci# include <asm/cmpxchg_32.h>
14462306a36Sopenharmony_ci#else
14562306a36Sopenharmony_ci# include <asm/cmpxchg_64.h>
14662306a36Sopenharmony_ci#endif
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci#define arch_cmpxchg(ptr, old, new)					\
14962306a36Sopenharmony_ci	__cmpxchg(ptr, old, new, sizeof(*(ptr)))
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci#define arch_sync_cmpxchg(ptr, old, new)				\
15262306a36Sopenharmony_ci	__sync_cmpxchg(ptr, old, new, sizeof(*(ptr)))
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci#define arch_cmpxchg_local(ptr, old, new)				\
15562306a36Sopenharmony_ci	__cmpxchg_local(ptr, old, new, sizeof(*(ptr)))
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci#define __raw_try_cmpxchg(_ptr, _pold, _new, size, lock)		\
15962306a36Sopenharmony_ci({									\
16062306a36Sopenharmony_ci	bool success;							\
16162306a36Sopenharmony_ci	__typeof__(_ptr) _old = (__typeof__(_ptr))(_pold);		\
16262306a36Sopenharmony_ci	__typeof__(*(_ptr)) __old = *_old;				\
16362306a36Sopenharmony_ci	__typeof__(*(_ptr)) __new = (_new);				\
16462306a36Sopenharmony_ci	switch (size) {							\
16562306a36Sopenharmony_ci	case __X86_CASE_B:						\
16662306a36Sopenharmony_ci	{								\
16762306a36Sopenharmony_ci		volatile u8 *__ptr = (volatile u8 *)(_ptr);		\
16862306a36Sopenharmony_ci		asm volatile(lock "cmpxchgb %[new], %[ptr]"		\
16962306a36Sopenharmony_ci			     CC_SET(z)					\
17062306a36Sopenharmony_ci			     : CC_OUT(z) (success),			\
17162306a36Sopenharmony_ci			       [ptr] "+m" (*__ptr),			\
17262306a36Sopenharmony_ci			       [old] "+a" (__old)			\
17362306a36Sopenharmony_ci			     : [new] "q" (__new)			\
17462306a36Sopenharmony_ci			     : "memory");				\
17562306a36Sopenharmony_ci		break;							\
17662306a36Sopenharmony_ci	}								\
17762306a36Sopenharmony_ci	case __X86_CASE_W:						\
17862306a36Sopenharmony_ci	{								\
17962306a36Sopenharmony_ci		volatile u16 *__ptr = (volatile u16 *)(_ptr);		\
18062306a36Sopenharmony_ci		asm volatile(lock "cmpxchgw %[new], %[ptr]"		\
18162306a36Sopenharmony_ci			     CC_SET(z)					\
18262306a36Sopenharmony_ci			     : CC_OUT(z) (success),			\
18362306a36Sopenharmony_ci			       [ptr] "+m" (*__ptr),			\
18462306a36Sopenharmony_ci			       [old] "+a" (__old)			\
18562306a36Sopenharmony_ci			     : [new] "r" (__new)			\
18662306a36Sopenharmony_ci			     : "memory");				\
18762306a36Sopenharmony_ci		break;							\
18862306a36Sopenharmony_ci	}								\
18962306a36Sopenharmony_ci	case __X86_CASE_L:						\
19062306a36Sopenharmony_ci	{								\
19162306a36Sopenharmony_ci		volatile u32 *__ptr = (volatile u32 *)(_ptr);		\
19262306a36Sopenharmony_ci		asm volatile(lock "cmpxchgl %[new], %[ptr]"		\
19362306a36Sopenharmony_ci			     CC_SET(z)					\
19462306a36Sopenharmony_ci			     : CC_OUT(z) (success),			\
19562306a36Sopenharmony_ci			       [ptr] "+m" (*__ptr),			\
19662306a36Sopenharmony_ci			       [old] "+a" (__old)			\
19762306a36Sopenharmony_ci			     : [new] "r" (__new)			\
19862306a36Sopenharmony_ci			     : "memory");				\
19962306a36Sopenharmony_ci		break;							\
20062306a36Sopenharmony_ci	}								\
20162306a36Sopenharmony_ci	case __X86_CASE_Q:						\
20262306a36Sopenharmony_ci	{								\
20362306a36Sopenharmony_ci		volatile u64 *__ptr = (volatile u64 *)(_ptr);		\
20462306a36Sopenharmony_ci		asm volatile(lock "cmpxchgq %[new], %[ptr]"		\
20562306a36Sopenharmony_ci			     CC_SET(z)					\
20662306a36Sopenharmony_ci			     : CC_OUT(z) (success),			\
20762306a36Sopenharmony_ci			       [ptr] "+m" (*__ptr),			\
20862306a36Sopenharmony_ci			       [old] "+a" (__old)			\
20962306a36Sopenharmony_ci			     : [new] "r" (__new)			\
21062306a36Sopenharmony_ci			     : "memory");				\
21162306a36Sopenharmony_ci		break;							\
21262306a36Sopenharmony_ci	}								\
21362306a36Sopenharmony_ci	default:							\
21462306a36Sopenharmony_ci		__cmpxchg_wrong_size();					\
21562306a36Sopenharmony_ci	}								\
21662306a36Sopenharmony_ci	if (unlikely(!success))						\
21762306a36Sopenharmony_ci		*_old = __old;						\
21862306a36Sopenharmony_ci	likely(success);						\
21962306a36Sopenharmony_ci})
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci#define __try_cmpxchg(ptr, pold, new, size)				\
22262306a36Sopenharmony_ci	__raw_try_cmpxchg((ptr), (pold), (new), (size), LOCK_PREFIX)
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci#define __try_cmpxchg_local(ptr, pold, new, size)			\
22562306a36Sopenharmony_ci	__raw_try_cmpxchg((ptr), (pold), (new), (size), "")
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci#define arch_try_cmpxchg(ptr, pold, new) 				\
22862306a36Sopenharmony_ci	__try_cmpxchg((ptr), (pold), (new), sizeof(*(ptr)))
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci#define arch_try_cmpxchg_local(ptr, pold, new)				\
23162306a36Sopenharmony_ci	__try_cmpxchg_local((ptr), (pold), (new), sizeof(*(ptr)))
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci/*
23462306a36Sopenharmony_ci * xadd() adds "inc" to "*ptr" and atomically returns the previous
23562306a36Sopenharmony_ci * value of "*ptr".
23662306a36Sopenharmony_ci *
23762306a36Sopenharmony_ci * xadd() is locked when multiple CPUs are online
23862306a36Sopenharmony_ci */
23962306a36Sopenharmony_ci#define __xadd(ptr, inc, lock)	__xchg_op((ptr), (inc), xadd, lock)
24062306a36Sopenharmony_ci#define xadd(ptr, inc)		__xadd((ptr), (inc), LOCK_PREFIX)
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci#endif	/* ASM_X86_CMPXCHG_H */
243