18c2ecf20Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */
28c2ecf20Sopenharmony_ci#ifndef _ASM_X86_CMPXCHG_32_H
38c2ecf20Sopenharmony_ci#define _ASM_X86_CMPXCHG_32_H
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci/*
68c2ecf20Sopenharmony_ci * Note: if you use set64_bit(), __cmpxchg64(), or their variants,
78c2ecf20Sopenharmony_ci *       you need to test for the feature in boot_cpu_data.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci/*
118c2ecf20Sopenharmony_ci * CMPXCHG8B only writes to the target if we had the previous
128c2ecf20Sopenharmony_ci * value in registers, otherwise it acts as a read and gives us the
138c2ecf20Sopenharmony_ci * "new previous" value.  That is why there is a loop.  Preloading
148c2ecf20Sopenharmony_ci * EDX:EAX is a performance optimization: in the common case it means
158c2ecf20Sopenharmony_ci * we need only one locked operation.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * A SIMD/3DNOW!/MMX/FPU 64-bit store here would require at the very
188c2ecf20Sopenharmony_ci * least an FPU save and/or %cr0.ts manipulation.
198c2ecf20Sopenharmony_ci *
208c2ecf20Sopenharmony_ci * cmpxchg8b must be used with the lock prefix here to allow the
218c2ecf20Sopenharmony_ci * instruction to be executed atomically.  We need to have the reader
228c2ecf20Sopenharmony_ci * side to see the coherent 64bit value.
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_cistatic inline void set_64bit(volatile u64 *ptr, u64 value)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	u32 low  = value;
278c2ecf20Sopenharmony_ci	u32 high = value >> 32;
288c2ecf20Sopenharmony_ci	u64 prev = *ptr;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	asm volatile("\n1:\t"
318c2ecf20Sopenharmony_ci		     LOCK_PREFIX "cmpxchg8b %0\n\t"
328c2ecf20Sopenharmony_ci		     "jnz 1b"
338c2ecf20Sopenharmony_ci		     : "=m" (*ptr), "+A" (prev)
348c2ecf20Sopenharmony_ci		     : "b" (low), "c" (high)
358c2ecf20Sopenharmony_ci		     : "memory");
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_CMPXCHG64
398c2ecf20Sopenharmony_ci#define arch_cmpxchg64(ptr, o, n)					\
408c2ecf20Sopenharmony_ci	((__typeof__(*(ptr)))__cmpxchg64((ptr), (unsigned long long)(o), \
418c2ecf20Sopenharmony_ci					 (unsigned long long)(n)))
428c2ecf20Sopenharmony_ci#define arch_cmpxchg64_local(ptr, o, n)					\
438c2ecf20Sopenharmony_ci	((__typeof__(*(ptr)))__cmpxchg64_local((ptr), (unsigned long long)(o), \
448c2ecf20Sopenharmony_ci					       (unsigned long long)(n)))
458c2ecf20Sopenharmony_ci#endif
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic inline u64 __cmpxchg64(volatile u64 *ptr, u64 old, u64 new)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	u64 prev;
508c2ecf20Sopenharmony_ci	asm volatile(LOCK_PREFIX "cmpxchg8b %1"
518c2ecf20Sopenharmony_ci		     : "=A" (prev),
528c2ecf20Sopenharmony_ci		       "+m" (*ptr)
538c2ecf20Sopenharmony_ci		     : "b" ((u32)new),
548c2ecf20Sopenharmony_ci		       "c" ((u32)(new >> 32)),
558c2ecf20Sopenharmony_ci		       "0" (old)
568c2ecf20Sopenharmony_ci		     : "memory");
578c2ecf20Sopenharmony_ci	return prev;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic inline u64 __cmpxchg64_local(volatile u64 *ptr, u64 old, u64 new)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	u64 prev;
638c2ecf20Sopenharmony_ci	asm volatile("cmpxchg8b %1"
648c2ecf20Sopenharmony_ci		     : "=A" (prev),
658c2ecf20Sopenharmony_ci		       "+m" (*ptr)
668c2ecf20Sopenharmony_ci		     : "b" ((u32)new),
678c2ecf20Sopenharmony_ci		       "c" ((u32)(new >> 32)),
688c2ecf20Sopenharmony_ci		       "0" (old)
698c2ecf20Sopenharmony_ci		     : "memory");
708c2ecf20Sopenharmony_ci	return prev;
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci#ifndef CONFIG_X86_CMPXCHG64
748c2ecf20Sopenharmony_ci/*
758c2ecf20Sopenharmony_ci * Building a kernel capable running on 80386 and 80486. It may be necessary
768c2ecf20Sopenharmony_ci * to simulate the cmpxchg8b on the 80386 and 80486 CPU.
778c2ecf20Sopenharmony_ci */
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci#define arch_cmpxchg64(ptr, o, n)				\
808c2ecf20Sopenharmony_ci({								\
818c2ecf20Sopenharmony_ci	__typeof__(*(ptr)) __ret;				\
828c2ecf20Sopenharmony_ci	__typeof__(*(ptr)) __old = (o);				\
838c2ecf20Sopenharmony_ci	__typeof__(*(ptr)) __new = (n);				\
848c2ecf20Sopenharmony_ci	alternative_io(LOCK_PREFIX_HERE				\
858c2ecf20Sopenharmony_ci			"call cmpxchg8b_emu",			\
868c2ecf20Sopenharmony_ci			"lock; cmpxchg8b (%%esi)" ,		\
878c2ecf20Sopenharmony_ci		       X86_FEATURE_CX8,				\
888c2ecf20Sopenharmony_ci		       "=A" (__ret),				\
898c2ecf20Sopenharmony_ci		       "S" ((ptr)), "0" (__old),		\
908c2ecf20Sopenharmony_ci		       "b" ((unsigned int)__new),		\
918c2ecf20Sopenharmony_ci		       "c" ((unsigned int)(__new>>32))		\
928c2ecf20Sopenharmony_ci		       : "memory");				\
938c2ecf20Sopenharmony_ci	__ret; })
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci#define arch_cmpxchg64_local(ptr, o, n)				\
978c2ecf20Sopenharmony_ci({								\
988c2ecf20Sopenharmony_ci	__typeof__(*(ptr)) __ret;				\
998c2ecf20Sopenharmony_ci	__typeof__(*(ptr)) __old = (o);				\
1008c2ecf20Sopenharmony_ci	__typeof__(*(ptr)) __new = (n);				\
1018c2ecf20Sopenharmony_ci	alternative_io("call cmpxchg8b_emu",			\
1028c2ecf20Sopenharmony_ci		       "cmpxchg8b (%%esi)" ,			\
1038c2ecf20Sopenharmony_ci		       X86_FEATURE_CX8,				\
1048c2ecf20Sopenharmony_ci		       "=A" (__ret),				\
1058c2ecf20Sopenharmony_ci		       "S" ((ptr)), "0" (__old),		\
1068c2ecf20Sopenharmony_ci		       "b" ((unsigned int)__new),		\
1078c2ecf20Sopenharmony_ci		       "c" ((unsigned int)(__new>>32))		\
1088c2ecf20Sopenharmony_ci		       : "memory");				\
1098c2ecf20Sopenharmony_ci	__ret; })
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci#endif
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci#define system_has_cmpxchg_double() boot_cpu_has(X86_FEATURE_CX8)
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci#endif /* _ASM_X86_CMPXCHG_32_H */
116