diff options
Diffstat (limited to 'arch/x86/lib/cmpxchg16b_emu.S')
| -rw-r--r-- | arch/x86/lib/cmpxchg16b_emu.S | 65 | 
1 files changed, 65 insertions, 0 deletions
diff --git a/arch/x86/lib/cmpxchg16b_emu.S b/arch/x86/lib/cmpxchg16b_emu.S new file mode 100644 index 00000000000..1e572c507d0 --- /dev/null +++ b/arch/x86/lib/cmpxchg16b_emu.S @@ -0,0 +1,65 @@ +/* + *	This program is free software; you can redistribute it and/or + *	modify it under the terms of the GNU General Public License + *	as published by the Free Software Foundation; version 2 + *	of the License. + * + */ +#include <linux/linkage.h> +#include <asm/alternative-asm.h> +#include <asm/frame.h> +#include <asm/dwarf2.h> + +#ifdef CONFIG_SMP +#define SEG_PREFIX %gs: +#else +#define SEG_PREFIX +#endif + +.text + +/* + * Inputs: + * %rsi : memory location to compare + * %rax : low 64 bits of old value + * %rdx : high 64 bits of old value + * %rbx : low 64 bits of new value + * %rcx : high 64 bits of new value + * %al  : Operation successful + */ +ENTRY(this_cpu_cmpxchg16b_emu) +CFI_STARTPROC + +# +# Emulate 'cmpxchg16b %gs:(%rsi)' except we return the result in %al not +# via the ZF.  Caller will access %al to get result. +# +# Note that this is only useful for a cpuops operation.  Meaning that we +# do *not* have a fully atomic operation but just an operation that is +# *atomic* on a single cpu (as provided by the this_cpu_xx class of +# macros). +# +this_cpu_cmpxchg16b_emu: +	pushf +	cli + +	cmpq SEG_PREFIX(%rsi), %rax +	jne not_same +	cmpq SEG_PREFIX 8(%rsi), %rdx +	jne not_same + +	movq %rbx, SEG_PREFIX(%rsi) +	movq %rcx, SEG_PREFIX 8(%rsi) + +	popf +	mov $1, %al +	ret + + not_same: +	popf +	xor %al,%al +	ret + +CFI_ENDPROC + +ENDPROC(this_cpu_cmpxchg16b_emu)  | 
