162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
262306a36Sopenharmony_ci#ifndef TOOLS_ASM_X86_CMPXCHG_H
362306a36Sopenharmony_ci#define TOOLS_ASM_X86_CMPXCHG_H
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <linux/compiler.h>
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci/*
862306a36Sopenharmony_ci * Non-existant functions to indicate usage errors at link time
962306a36Sopenharmony_ci * (or compile-time if the compiler implements __compiletime_error().
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ciextern void __cmpxchg_wrong_size(void)
1262306a36Sopenharmony_ci	__compiletime_error("Bad argument size for cmpxchg");
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/*
1562306a36Sopenharmony_ci * Constants for operation sizes. On 32-bit, the 64-bit size it set to
1662306a36Sopenharmony_ci * -1 because sizeof will never return -1, thereby making those switch
1762306a36Sopenharmony_ci * case statements guaranteeed dead code which the compiler will
1862306a36Sopenharmony_ci * eliminate, and allowing the "missing symbol in the default case" to
1962306a36Sopenharmony_ci * indicate a usage error.
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_ci#define __X86_CASE_B	1
2262306a36Sopenharmony_ci#define __X86_CASE_W	2
2362306a36Sopenharmony_ci#define __X86_CASE_L	4
2462306a36Sopenharmony_ci#ifdef __x86_64__
2562306a36Sopenharmony_ci#define __X86_CASE_Q	8
2662306a36Sopenharmony_ci#else
2762306a36Sopenharmony_ci#define	__X86_CASE_Q	-1		/* sizeof will never return -1 */
2862306a36Sopenharmony_ci#endif
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/*
3162306a36Sopenharmony_ci * Atomic compare and exchange.  Compare OLD with MEM, if identical,
3262306a36Sopenharmony_ci * store NEW in MEM.  Return the initial value in MEM.  Success is
3362306a36Sopenharmony_ci * indicated by comparing RETURN with OLD.
3462306a36Sopenharmony_ci */
3562306a36Sopenharmony_ci#define __raw_cmpxchg(ptr, old, new, size, lock)			\
3662306a36Sopenharmony_ci({									\
3762306a36Sopenharmony_ci	__typeof__(*(ptr)) __ret;					\
3862306a36Sopenharmony_ci	__typeof__(*(ptr)) __old = (old);				\
3962306a36Sopenharmony_ci	__typeof__(*(ptr)) __new = (new);				\
4062306a36Sopenharmony_ci	switch (size) {							\
4162306a36Sopenharmony_ci	case __X86_CASE_B:						\
4262306a36Sopenharmony_ci	{								\
4362306a36Sopenharmony_ci		volatile u8 *__ptr = (volatile u8 *)(ptr);		\
4462306a36Sopenharmony_ci		asm volatile(lock "cmpxchgb %2,%1"			\
4562306a36Sopenharmony_ci			     : "=a" (__ret), "+m" (*__ptr)		\
4662306a36Sopenharmony_ci			     : "q" (__new), "0" (__old)			\
4762306a36Sopenharmony_ci			     : "memory");				\
4862306a36Sopenharmony_ci		break;							\
4962306a36Sopenharmony_ci	}								\
5062306a36Sopenharmony_ci	case __X86_CASE_W:						\
5162306a36Sopenharmony_ci	{								\
5262306a36Sopenharmony_ci		volatile u16 *__ptr = (volatile u16 *)(ptr);		\
5362306a36Sopenharmony_ci		asm volatile(lock "cmpxchgw %2,%1"			\
5462306a36Sopenharmony_ci			     : "=a" (__ret), "+m" (*__ptr)		\
5562306a36Sopenharmony_ci			     : "r" (__new), "0" (__old)			\
5662306a36Sopenharmony_ci			     : "memory");				\
5762306a36Sopenharmony_ci		break;							\
5862306a36Sopenharmony_ci	}								\
5962306a36Sopenharmony_ci	case __X86_CASE_L:						\
6062306a36Sopenharmony_ci	{								\
6162306a36Sopenharmony_ci		volatile u32 *__ptr = (volatile u32 *)(ptr);		\
6262306a36Sopenharmony_ci		asm volatile(lock "cmpxchgl %2,%1"			\
6362306a36Sopenharmony_ci			     : "=a" (__ret), "+m" (*__ptr)		\
6462306a36Sopenharmony_ci			     : "r" (__new), "0" (__old)			\
6562306a36Sopenharmony_ci			     : "memory");				\
6662306a36Sopenharmony_ci		break;							\
6762306a36Sopenharmony_ci	}								\
6862306a36Sopenharmony_ci	case __X86_CASE_Q:						\
6962306a36Sopenharmony_ci	{								\
7062306a36Sopenharmony_ci		volatile u64 *__ptr = (volatile u64 *)(ptr);		\
7162306a36Sopenharmony_ci		asm volatile(lock "cmpxchgq %2,%1"			\
7262306a36Sopenharmony_ci			     : "=a" (__ret), "+m" (*__ptr)		\
7362306a36Sopenharmony_ci			     : "r" (__new), "0" (__old)			\
7462306a36Sopenharmony_ci			     : "memory");				\
7562306a36Sopenharmony_ci		break;							\
7662306a36Sopenharmony_ci	}								\
7762306a36Sopenharmony_ci	default:							\
7862306a36Sopenharmony_ci		__cmpxchg_wrong_size();					\
7962306a36Sopenharmony_ci	}								\
8062306a36Sopenharmony_ci	__ret;								\
8162306a36Sopenharmony_ci})
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci#define __cmpxchg(ptr, old, new, size)					\
8462306a36Sopenharmony_ci	__raw_cmpxchg((ptr), (old), (new), (size), LOCK_PREFIX)
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci#define cmpxchg(ptr, old, new)						\
8762306a36Sopenharmony_ci	__cmpxchg(ptr, old, new, sizeof(*(ptr)))
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci#endif	/* TOOLS_ASM_X86_CMPXCHG_H */
91