162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0-only */
262306a36Sopenharmony_ci#include <linux/linkage.h>
362306a36Sopenharmony_ci#include <asm/percpu.h>
462306a36Sopenharmony_ci#include <asm/processor-flags.h>
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci.text
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/*
962306a36Sopenharmony_ci * Emulate 'cmpxchg16b %gs:(%rsi)'
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Inputs:
1262306a36Sopenharmony_ci * %rsi : memory location to compare
1362306a36Sopenharmony_ci * %rax : low 64 bits of old value
1462306a36Sopenharmony_ci * %rdx : high 64 bits of old value
1562306a36Sopenharmony_ci * %rbx : low 64 bits of new value
1662306a36Sopenharmony_ci * %rcx : high 64 bits of new value
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * Notably this is not LOCK prefixed and is not safe against NMIs
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_ciSYM_FUNC_START(this_cpu_cmpxchg16b_emu)
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	pushfq
2362306a36Sopenharmony_ci	cli
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	/* if (*ptr == old) */
2662306a36Sopenharmony_ci	cmpq	PER_CPU_VAR(0(%rsi)), %rax
2762306a36Sopenharmony_ci	jne	.Lnot_same
2862306a36Sopenharmony_ci	cmpq	PER_CPU_VAR(8(%rsi)), %rdx
2962306a36Sopenharmony_ci	jne	.Lnot_same
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	/* *ptr = new */
3262306a36Sopenharmony_ci	movq	%rbx, PER_CPU_VAR(0(%rsi))
3362306a36Sopenharmony_ci	movq	%rcx, PER_CPU_VAR(8(%rsi))
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	/* set ZF in EFLAGS to indicate success */
3662306a36Sopenharmony_ci	orl	$X86_EFLAGS_ZF, (%rsp)
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	popfq
3962306a36Sopenharmony_ci	RET
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci.Lnot_same:
4262306a36Sopenharmony_ci	/* *ptr != old */
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	/* old = *ptr */
4562306a36Sopenharmony_ci	movq	PER_CPU_VAR(0(%rsi)), %rax
4662306a36Sopenharmony_ci	movq	PER_CPU_VAR(8(%rsi)), %rdx
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	/* clear ZF in EFLAGS to indicate failure */
4962306a36Sopenharmony_ci	andl	$(~X86_EFLAGS_ZF), (%rsp)
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	popfq
5262306a36Sopenharmony_ci	RET
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ciSYM_FUNC_END(this_cpu_cmpxchg16b_emu)
55