blob: fc41968a9e9237552a7e7a6ed8faab082319718c [file] [log] [blame] [edit]
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !race
#include "textflag.h"
// Linux/ARM atomic operations.
// Because there is so much variation in ARM devices,
// the Linux kernel provides an appropriate compare-and-swap
// implementation at address 0xffff0fc0. Caller sets:
// R0 = old value
// R1 = new value
// R2 = addr
// LR = return address
// The function returns with CS true if the swap happened.
// http://lxr.linux.no/linux+v2.6.37.2/arch/arm/kernel/entry-armv.S#L850
// On older kernels (before 2.6.24) the function can incorrectly
// report a conflict, so we have to double-check the compare ourselves
// and retry if necessary.
//
// http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=b49c0f24cf6744a3f4fd09289fe7cade349dead5
//
TEXT cas<>(SB),NOSPLIT,$0
MOVW $0xffff0fc0, R15
TEXT ·CompareAndSwapInt32(SB),NOSPLIT,$0
B ·CompareAndSwapUint32(SB)
// Implement using kernel cas for portability.
TEXT ·CompareAndSwapUint32(SB),NOSPLIT,$0-13
MOVW addr+0(FP), R2
// trigger potential paging fault here,
// because we don't know how to traceback through __kuser_cmpxchg
MOVW (R2), R0
MOVW old+4(FP), R0
casagain:
MOVW new+8(FP), R1
BL cas<>(SB)
BCC cascheck
MOVW $1, R0
casret:
MOVB R0, swapped+12(FP)
RET
cascheck:
// Kernel lies; double-check.
MOVW addr+0(FP), R2
MOVW old+4(FP), R0
MOVW 0(R2), R3
CMP R0, R3
BEQ casagain
MOVW $0, R0
B casret
TEXT ·CompareAndSwapUintptr(SB),NOSPLIT,$0
B ·CompareAndSwapUint32(SB)
TEXT ·AddInt32(SB),NOSPLIT,$0
B ·AddUint32(SB)
// Implement using kernel cas for portability.
TEXT ·AddUint32(SB),NOSPLIT,$0-12
MOVW addr+0(FP), R2
MOVW delta+4(FP), R4
addloop1:
MOVW 0(R2), R0
MOVW R0, R1
ADD R4, R1
BL cas<>(SB)
BCC addloop1
MOVW R1, new+8(FP)
RET
TEXT ·AddUintptr(SB),NOSPLIT,$0
B ·AddUint32(SB)
TEXT ·SwapInt32(SB),NOSPLIT,$0
B ·SwapUint32(SB)
// Implement using kernel cas for portability.
TEXT ·SwapUint32(SB),NOSPLIT,$0-12
MOVW addr+0(FP), R2
MOVW new+4(FP), R1
swaploop1:
MOVW 0(R2), R0
MOVW R0, R4 // cas smashes R0
BL cas<>(SB)
BCC swaploop1
MOVW R4, old+8(FP)
RET
TEXT ·SwapUintptr(SB),NOSPLIT,$0
B ·SwapUint32(SB)
TEXT cas64<>(SB),NOSPLIT,$0
MOVW $0xffff0f60, R15 // R15 = hardware PC. __kuser_cmpxchg64: Linux-3.1 and above
TEXT kernelCAS64<>(SB),NOSPLIT,$0-21
// int (*__kuser_cmpxchg64_t)(const int64_t *oldval, const int64_t *newval, volatile int64_t *ptr);
MOVW addr+0(FP), R2 // ptr
// trigger potential paging fault here,
// because we don't know how to traceback through __kuser_cmpxchg64
MOVW (R2), R0
// make unaligned atomic access panic
AND.S $7, R2, R1
BEQ 2(PC)
MOVW R1, (R1)
MOVW $oldval+4(FP), R0
MOVW $newval+12(FP), R1
BL cas64<>(SB)
MOVW.CS $1, R0 // C is set if the kernel has changed *ptr
MOVW.CC $0, R0
MOVW R0, ret+20(FP)
RET
TEXT ·generalCAS64(SB),NOSPLIT,$0-21
B runtime∕internal∕atomic·Cas64(SB)
GLOBL armCAS64(SB), NOPTR, $4
TEXT setupAndCallCAS64<>(SB),NOSPLIT,$-4-21
MOVW $0xffff0ffc, R0 // __kuser_helper_version
MOVW (R0), R0
// __kuser_cmpxchg64 only present if helper version >= 5
CMP $5, R0
MOVW.CS $kernelCAS64<>(SB), R1
MOVW.CS R1, armCAS64(SB)
MOVW.CS R1, R15 // R15 = hardware PC
MOVB runtime·armArch(SB), R0
// LDREXD, STREXD only present on ARMv6K or higher
CMP $6, R0 // TODO(minux): how to differentiate ARMv6 with ARMv6K?
MOVW.CS $·armCompareAndSwapUint64(SB), R1
MOVW.CS R1, armCAS64(SB)
MOVW.CS R1, R15
// we are out of luck, can only use runtime's emulated 64-bit cas
MOVW $·generalCAS64(SB), R1
MOVW R1, armCAS64(SB)
MOVW R1, R15
TEXT ·CompareAndSwapInt64(SB),NOSPLIT,$0
B ·CompareAndSwapUint64(SB)
TEXT ·CompareAndSwapUint64(SB),NOSPLIT,$-4-21
MOVW armCAS64(SB), R0
CMP $0, R0
MOVW.NE R0, R15 // R15 = hardware PC
B setupAndCallCAS64<>(SB)
TEXT ·AddInt64(SB),NOSPLIT,$0
B ·addUint64(SB)
TEXT ·AddUint64(SB),NOSPLIT,$0
B ·addUint64(SB)
TEXT ·SwapInt64(SB),NOSPLIT,$0
B ·swapUint64(SB)
TEXT ·SwapUint64(SB),NOSPLIT,$0
B ·swapUint64(SB)
TEXT ·LoadInt32(SB),NOSPLIT,$0
B ·LoadUint32(SB)
TEXT ·LoadUint32(SB),NOSPLIT,$0-8
MOVW addr+0(FP), R2
loadloop1:
MOVW 0(R2), R0
MOVW R0, R1
BL cas<>(SB)
BCC loadloop1
MOVW R1, val+4(FP)
RET
TEXT ·LoadInt64(SB),NOSPLIT,$0
B ·loadUint64(SB)
TEXT ·LoadUint64(SB),NOSPLIT,$0
B ·loadUint64(SB)
TEXT ·LoadUintptr(SB),NOSPLIT,$0
B ·LoadUint32(SB)
TEXT ·LoadPointer(SB),NOSPLIT,$0
B ·LoadUint32(SB)
TEXT ·StoreInt32(SB),NOSPLIT,$0
B ·StoreUint32(SB)
TEXT ·StoreUint32(SB),NOSPLIT,$0-8
MOVW addr+0(FP), R2
MOVW val+4(FP), R1
storeloop1:
MOVW 0(R2), R0
BL cas<>(SB)
BCC storeloop1
RET
TEXT ·StoreInt64(SB),NOSPLIT,$0
B ·storeUint64(SB)
TEXT ·StoreUint64(SB),NOSPLIT,$0
B ·storeUint64(SB)
TEXT ·StoreUintptr(SB),NOSPLIT,$0
B ·StoreUint32(SB)