root/arch/x86/lib/atomic64_cx8_32.S
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
 * atomic64_t for 586+
 *
 * Copyright © 2010  Luca Barbieri
 */

#include <linux/linkage.h>
#include <asm/alternative.h>

.macro read64 reg
        movl %ebx, %eax
        movl %ecx, %edx
/* we need LOCK_PREFIX since otherwise cmpxchg8b always does the write */
        LOCK_PREFIX
        cmpxchg8b (\reg)
.endm

.macro read64_nonatomic reg
        movl (\reg), %eax
        movl 4(\reg), %edx
.endm

SYM_FUNC_START(atomic64_read_cx8)
        read64 %ecx
        RET
SYM_FUNC_END(atomic64_read_cx8)

SYM_FUNC_START(atomic64_set_cx8)
1:
/* we don't need LOCK_PREFIX since aligned 64-bit writes
 * are atomic on 586 and newer */
        cmpxchg8b (%esi)
        jne 1b

        RET
SYM_FUNC_END(atomic64_set_cx8)

SYM_FUNC_START(atomic64_xchg_cx8)
1:
        LOCK_PREFIX
        cmpxchg8b (%esi)
        jne 1b

        RET
SYM_FUNC_END(atomic64_xchg_cx8)

.macro addsub_return func ins insc
SYM_FUNC_START(atomic64_\func\()_return_cx8)
        pushl %ebp
        pushl %ebx
        pushl %esi
        pushl %edi

        movl %eax, %esi
        movl %edx, %edi
        movl %ecx, %ebp

        read64_nonatomic %ecx
1:
        movl %eax, %ebx
        movl %edx, %ecx
        \ins\()l %esi, %ebx
        \insc\()l %edi, %ecx
        LOCK_PREFIX
        cmpxchg8b (%ebp)
        jne 1b

10:
        movl %ebx, %eax
        movl %ecx, %edx
        popl %edi
        popl %esi
        popl %ebx
        popl %ebp
        RET
SYM_FUNC_END(atomic64_\func\()_return_cx8)
.endm

addsub_return add add adc
addsub_return sub sub sbb

.macro incdec_return func ins insc
SYM_FUNC_START(atomic64_\func\()_return_cx8)
        pushl %ebx

        read64_nonatomic %esi
1:
        movl %eax, %ebx
        movl %edx, %ecx
        \ins\()l $1, %ebx
        \insc\()l $0, %ecx
        LOCK_PREFIX
        cmpxchg8b (%esi)
        jne 1b

10:
        movl %ebx, %eax
        movl %ecx, %edx
        popl %ebx
        RET
SYM_FUNC_END(atomic64_\func\()_return_cx8)
.endm

incdec_return inc add adc
incdec_return dec sub sbb

SYM_FUNC_START(atomic64_dec_if_positive_cx8)
        pushl %ebx

        read64 %esi
1:
        movl %eax, %ebx
        movl %edx, %ecx
        subl $1, %ebx
        sbb $0, %ecx
        js 2f
        LOCK_PREFIX
        cmpxchg8b (%esi)
        jne 1b

2:
        movl %ebx, %eax
        movl %ecx, %edx
        popl %ebx
        RET
SYM_FUNC_END(atomic64_dec_if_positive_cx8)

SYM_FUNC_START(atomic64_add_unless_cx8)
        pushl %ebp
        pushl %ebx
/* these just push these two parameters on the stack */
        pushl %edi
        pushl %ecx

        movl %eax, %ebp
        movl %edx, %edi

        read64 %esi
1:
        cmpl %eax, 0(%esp)
        je 4f
2:
        movl %eax, %ebx
        movl %edx, %ecx
        addl %ebp, %ebx
        adcl %edi, %ecx
        LOCK_PREFIX
        cmpxchg8b (%esi)
        jne 1b

        movl $1, %eax
3:
        addl $8, %esp
        popl %ebx
        popl %ebp
        RET
4:
        cmpl %edx, 4(%esp)
        jne 2b
        xorl %eax, %eax
        jmp 3b
SYM_FUNC_END(atomic64_add_unless_cx8)

SYM_FUNC_START(atomic64_inc_not_zero_cx8)
        pushl %ebx

        read64 %esi
1:
        movl %eax, %ecx
        orl %edx, %ecx
        jz 3f
        movl %eax, %ebx
        xorl %ecx, %ecx
        addl $1, %ebx
        adcl %edx, %ecx
        LOCK_PREFIX
        cmpxchg8b (%esi)
        jne 1b

        movl $1, %eax
3:
        popl %ebx
        RET
SYM_FUNC_END(atomic64_inc_not_zero_cx8)