1/* SPDX-License-Identifier: GPL-2.0 */
2#ifndef TOOLS_ASM_X86_CMPXCHG_H
3#define TOOLS_ASM_X86_CMPXCHG_H
4
5#include <linux/compiler.h>
6
7/*
8 * Non-existant functions to indicate usage errors at link time
9 * (or compile-time if the compiler implements __compiletime_error().
10 */
11extern void __cmpxchg_wrong_size(void)
12	__compiletime_error("Bad argument size for cmpxchg");
13
14/*
15 * Constants for operation sizes. On 32-bit, the 64-bit size it set to
16 * -1 because sizeof will never return -1, thereby making those switch
17 * case statements guaranteeed dead code which the compiler will
18 * eliminate, and allowing the "missing symbol in the default case" to
19 * indicate a usage error.
20 */
21#define __X86_CASE_B	1
22#define __X86_CASE_W	2
23#define __X86_CASE_L	4
24#ifdef __x86_64__
25#define __X86_CASE_Q	8
26#else
27#define	__X86_CASE_Q	-1		/* sizeof will never return -1 */
28#endif
29
30/*
31 * Atomic compare and exchange.  Compare OLD with MEM, if identical,
32 * store NEW in MEM.  Return the initial value in MEM.  Success is
33 * indicated by comparing RETURN with OLD.
34 */
35#define __raw_cmpxchg(ptr, old, new, size, lock)			\
36({									\
37	__typeof__(*(ptr)) __ret;					\
38	__typeof__(*(ptr)) __old = (old);				\
39	__typeof__(*(ptr)) __new = (new);				\
40	switch (size) {							\
41	case __X86_CASE_B:						\
42	{								\
43		volatile u8 *__ptr = (volatile u8 *)(ptr);		\
44		asm volatile(lock "cmpxchgb %2,%1"			\
45			     : "=a" (__ret), "+m" (*__ptr)		\
46			     : "q" (__new), "0" (__old)			\
47			     : "memory");				\
48		break;							\
49	}								\
50	case __X86_CASE_W:						\
51	{								\
52		volatile u16 *__ptr = (volatile u16 *)(ptr);		\
53		asm volatile(lock "cmpxchgw %2,%1"			\
54			     : "=a" (__ret), "+m" (*__ptr)		\
55			     : "r" (__new), "0" (__old)			\
56			     : "memory");				\
57		break;							\
58	}								\
59	case __X86_CASE_L:						\
60	{								\
61		volatile u32 *__ptr = (volatile u32 *)(ptr);		\
62		asm volatile(lock "cmpxchgl %2,%1"			\
63			     : "=a" (__ret), "+m" (*__ptr)		\
64			     : "r" (__new), "0" (__old)			\
65			     : "memory");				\
66		break;							\
67	}								\
68	case __X86_CASE_Q:						\
69	{								\
70		volatile u64 *__ptr = (volatile u64 *)(ptr);		\
71		asm volatile(lock "cmpxchgq %2,%1"			\
72			     : "=a" (__ret), "+m" (*__ptr)		\
73			     : "r" (__new), "0" (__old)			\
74			     : "memory");				\
75		break;							\
76	}								\
77	default:							\
78		__cmpxchg_wrong_size();					\
79	}								\
80	__ret;								\
81})
82
83#define __cmpxchg(ptr, old, new, size)					\
84	__raw_cmpxchg((ptr), (old), (new), (size), LOCK_PREFIX)
85
86#define cmpxchg(ptr, old, new)						\
87	__cmpxchg(ptr, old, new, sizeof(*(ptr)))
88
89
90#endif	/* TOOLS_ASM_X86_CMPXCHG_H */
91