| 1 | /* SPDX-License-Identifier: GPL-2.0-only */ |
| 2 | #include <linux/linkage.h> |
| 3 | #include <asm/percpu.h> |
| 4 | #include <asm/processor-flags.h> |
| 5 | |
| 6 | .text |
| 7 | |
| 8 | /* |
| 9 | * Emulate 'cmpxchg16b %gs:(%rsi)' |
| 10 | * |
| 11 | * Inputs: |
| 12 | * %rsi : memory location to compare |
| 13 | * %rax : low 64 bits of old value |
| 14 | * %rdx : high 64 bits of old value |
| 15 | * %rbx : low 64 bits of new value |
| 16 | * %rcx : high 64 bits of new value |
| 17 | * |
| 18 | * Notably this is not LOCK prefixed and is not safe against NMIs |
| 19 | */ |
| 20 | SYM_FUNC_START(this_cpu_cmpxchg16b_emu) |
| 21 | |
| 22 | pushfq |
| 23 | cli |
| 24 | |
| 25 | /* if (*ptr == old) */ |
| 26 | cmpq __percpu (%rsi), %rax |
| 27 | jne .Lnot_same |
| 28 | cmpq __percpu 8(%rsi), %rdx |
| 29 | jne .Lnot_same |
| 30 | |
| 31 | /* *ptr = new */ |
| 32 | movq %rbx, __percpu (%rsi) |
| 33 | movq %rcx, __percpu 8(%rsi) |
| 34 | |
| 35 | /* set ZF in EFLAGS to indicate success */ |
| 36 | orl $X86_EFLAGS_ZF, (%rsp) |
| 37 | |
| 38 | popfq |
| 39 | RET |
| 40 | |
| 41 | .Lnot_same: |
| 42 | /* *ptr != old */ |
| 43 | |
| 44 | /* old = *ptr */ |
| 45 | movq __percpu (%rsi), %rax |
| 46 | movq __percpu 8(%rsi), %rdx |
| 47 | |
| 48 | /* clear ZF in EFLAGS to indicate failure */ |
| 49 | andl $(~X86_EFLAGS_ZF), (%rsp) |
| 50 | |
| 51 | popfq |
| 52 | RET |
| 53 | |
| 54 | SYM_FUNC_END(this_cpu_cmpxchg16b_emu) |
| 55 | |