lock_stubs.S revision 1.7
11.7Sad/*	$NetBSD: lock_stubs.S,v 1.7 2007/11/10 20:06:23 ad Exp $	*/
21.2Sad
31.2Sad/*-
41.2Sad * Copyright (c) 2006, 2007 The NetBSD Foundation, Inc.
51.2Sad * All rights reserved.
61.2Sad *
71.2Sad * This code is derived from software contributed to The NetBSD Foundation
81.2Sad * by Andrew Doran.
91.2Sad *
101.2Sad * Redistribution and use in source and binary forms, with or without
111.2Sad * modification, are permitted provided that the following conditions
121.2Sad * are met:
131.2Sad * 1. Redistributions of source code must retain the above copyright
141.2Sad *    notice, this list of conditions and the following disclaimer.
151.2Sad * 2. Redistributions in binary form must reproduce the above copyright
161.2Sad *    notice, this list of conditions and the following disclaimer in the
171.2Sad *    documentation and/or other materials provided with the distribution.
181.2Sad * 3. All advertising materials mentioning features or use of this software
191.2Sad *    must display the following acknowledgement:
201.2Sad *	This product includes software developed by the NetBSD
211.2Sad *	Foundation, Inc. and its contributors.
221.2Sad * 4. Neither the name of The NetBSD Foundation nor the names of its
231.2Sad *    contributors may be used to endorse or promote products derived
241.2Sad *    from this software without specific prior written permission.
251.2Sad *
261.2Sad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
271.2Sad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
281.2Sad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
291.2Sad * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
301.2Sad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
311.2Sad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
321.2Sad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
331.2Sad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
341.2Sad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
351.2Sad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
361.2Sad * POSSIBILITY OF SUCH DAMAGE.
371.2Sad */
381.2Sad
391.2Sad/*
401.2Sad * AMD64 lock stubs.  Calling convention:
411.2Sad *
421.2Sad * %rdi		arg 1
431.2Sad * %rsi		arg 2
441.2Sad * %rdx		arg 3
451.2Sad * %rax		return value
461.2Sad */
471.2Sad
481.2Sad#include "opt_multiprocessor.h"
491.2Sad#include "opt_lockdebug.h"
501.2Sad
511.2Sad#include <machine/asm.h>
521.2Sad
531.2Sad#include "assym.h"
541.2Sad
551.2Sad#if defined(DIAGNOSTIC) || defined(MULTIPROCESSOR) || defined(LOCKDEBUG)
561.2Sad#define	FULL
571.2Sad#endif
581.2Sad
591.7Sad#define	END(name,a)	.align	a; LABEL(name)
601.2Sad
611.7Sad#define	LOCK(name)	LABEL(name) lock
621.2Sad
631.2Sad#ifndef LOCKDEBUG
641.2Sad
651.2Sad/*
661.2Sad * void mutex_enter(kmutex_t *mtx);
671.2Sad *
681.2Sad * Acquire a mutex and post a load fence.
691.2Sad */
701.2Sad	.align	64
711.2Sad
721.2SadNENTRY(mutex_enter)				/* 0x0000, 25 bytes */
731.2Sad	movq	CPUVAR(CURLWP), %rcx
741.2Sad	xorq	%rax, %rax
751.7Sad	LOCK(lockpatch1)
761.2Sad	cmpxchgq %rcx, MTX_OWNER(%rdi)
771.2Sad	jnz,pn	_C_LABEL(mutex_vector_enter)
781.2Sad	ret
791.2Sad
801.2Sad/*
811.2Sad * void mutex_exit(kmutex_t *mtx);
821.2Sad *
831.2Sad * Release a mutex and post a load fence.
841.2Sad *
851.2Sad * See comments in mutex_vector_enter() about doing this operation unlocked
861.2Sad * on multiprocessor systems, and comments in arch/x86/include/lock.h about
871.2Sad * memory ordering on Intel x86 systems.
881.2Sad */
891.2SadNENTRY(mutex_exit)				/* 0x0020, 24 bytes */
901.2Sad	movq	CPUVAR(CURLWP), %rax
911.2Sad	xorq	%rdx, %rdx
921.2Sad	cmpxchgq %rdx, MTX_OWNER(%rdi)
931.2Sad	jnz,pn	_C_LABEL(mutex_vector_exit)
941.2Sad	ret
951.2Sad
961.2Sad/*
971.2Sad * void mutex_spin_enter(kmutex_t *mtx);
981.2Sad *
991.2Sad * Acquire a spin mutex and post a load fence.
1001.2Sad */
1011.4SadNENTRY(mutex_spin_enter)
1021.4Sad	movq	CPUVAR(SELF), %r8
1031.2Sad#if defined(FULL)
1041.2Sad	movl	$0x0100, %eax			/* new + expected value */
1051.2Sad#endif
1061.4Sad	movl	CPU_INFO_ILEVEL(%r8), %esi
1071.4Sad	subl	$1, CPU_INFO_MTX_COUNT(%r8)	/* decl doesnt set CF */
1081.2Sad	movzbl	MTX_IPL(%rdi), %ecx		/* new SPL */
1091.4Sad	cmovncl	CPU_INFO_MTX_OLDSPL(%r8), %esi
1101.2Sad	cmpl	%ecx, %esi			/* higher? */
1111.4Sad	movl	%esi, CPU_INFO_MTX_OLDSPL(%r8)
1121.2Sad	cmovgl	%esi, %ecx
1131.4Sad	movl	%ecx, CPU_INFO_ILEVEL(%r8)	/* splraiseipl() */
1141.2Sad#if defined(FULL)
1151.7Sad	LOCK(lockpatch11)
1161.2Sad	cmpxchgb %ah, MTX_LOCK(%rdi)		/* lock */
1171.2Sad	jnz,pn	_C_LABEL(mutex_spin_retry)	/* failed; hard case */
1181.2Sad#endif
1191.2Sad	ret
1201.2Sad
1211.2Sad/*
1221.2Sad * void mutex_spin_exit(kmutex_t *mtx);
1231.2Sad *
1241.2Sad * Release a spin mutex and post a load fence.
1251.2Sad */
1261.4SadNENTRY(mutex_spin_exit)
1271.2Sad#ifdef DIAGNOSTIC
1281.2Sad
1291.2Sad	movl	$0x0001, %eax			/* new + expected value */
1301.4Sad	movq	CPUVAR(SELF), %r8
1311.2Sad	cmpxchgb %ah, MTX_LOCK(%rdi)		/* unlock */
1321.2Sad	jnz,pn	_C_LABEL(mutex_vector_exit)	/* hard case if problems */
1331.4Sad	movl	CPU_INFO_MTX_OLDSPL(%r8), %edi
1341.4Sad	incl	CPU_INFO_MTX_COUNT(%r8)
1351.2Sad	jnz	1f
1361.4Sad	cmpl	CPU_INFO_ILEVEL(%r8), %edi
1371.2Sad	jae	1f
1381.4Sad	movl	CPU_INFO_IUNMASK(%r8,%rdi,4), %esi
1391.2Sad	cli
1401.4Sad	testl	CPU_INFO_IPENDING(%r8), %esi
1411.2Sad	jnz	_C_LABEL(Xspllower)
1421.4Sad	movl	%edi, CPU_INFO_ILEVEL(%r8)
1431.2Sad	sti
1441.2Sad1:	rep					/* double byte ret as branch */
1451.2Sad	ret					/* target: see AMD docs */
1461.2Sad
1471.2Sad#else	/* DIAGNOSTIC */
1481.2Sad
1491.4Sad	movq	CPUVAR(SELF), %rsi
1501.2Sad#ifdef MULTIPROCESSOR
1511.2Sad	movb	$0x00, MTX_LOCK(%rdi)
1521.2Sad#endif
1531.4Sad	movl	CPU_INFO_MTX_OLDSPL(%rsi), %ecx
1541.4Sad	incl	CPU_INFO_MTX_COUNT(%rsi)
1551.4Sad	movl	CPU_INFO_ILEVEL(%rsi),%edx
1561.2Sad	cmovnzl	%edx,%ecx
1571.2Sad	cmpl	%edx,%ecx			/* new level is lower? */
1581.2Sad	pushq	%rbx
1591.2Sad	jae,pn	2f
1601.2Sad1:
1611.4Sad	movl	CPU_INFO_IPENDING(%rsi),%eax
1621.4Sad	testl	%eax,CPU_INFO_IUNMASK(%rsi,%rcx,4)/* deferred interrupts? */
1631.2Sad	movl	%eax,%ebx
1641.2Sad	jnz,pn	3f
1651.4Sad	cmpxchg8b CPU_INFO_ISTATE(%rsi)		/* swap in new ilevel */
1661.3Syamt	jnz,pn	1b
1671.2Sad2:
1681.2Sad	popq	%rbx
1691.2Sad	ret
1701.2Sad3:
1711.2Sad	popq	%rbx
1721.2Sad	movl	%ecx, %edi
1731.2Sad	jmp	_C_LABEL(Xspllower)
1741.2Sad
1751.2Sad#endif	/* DIAGNOSTIC */
1761.2Sad
1771.2Sad/*
1781.2Sad * void	rw_enter(krwlock_t *rwl, krw_t op);
1791.2Sad *
1801.2Sad * Acquire one hold on a RW lock.
1811.2Sad */
1821.2SadNENTRY(rw_enter)				/* 0x00c0, 62 bytes */
1831.2Sad	cmpl	$RW_READER, %esi
1841.2Sad	jne	2f
1851.2Sad
1861.2Sad	/*
1871.2Sad	 * Reader: this is the most common case.
1881.2Sad	 */
1891.2Sad1:	movq	RW_OWNER(%rdi), %rax
1901.2Sad	testb	$(RW_WRITE_LOCKED|RW_WRITE_WANTED), %al
1911.2Sad	leaq	RW_READ_INCR(%rax), %rdx
1921.2Sad	jnz,pn	_C_LABEL(rw_vector_enter)
1931.7Sad	LOCK(lockpatch2)
1941.2Sad	cmpxchgq %rdx, RW_OWNER(%rdi)
1951.2Sad	jnz,pn	1b
1961.2Sad	ret
1971.2Sad
1981.2Sad	/*
1991.2Sad	 * Writer: if the compare-and-set fails, don't bother retrying.
2001.2Sad	 */
2011.2Sad2:	movq	CPUVAR(CURLWP), %rcx
2021.2Sad	xorq	%rax, %rax
2031.2Sad	orq	$RW_WRITE_LOCKED, %rcx
2041.7Sad	LOCK(lockpatch3)
2051.2Sad	cmpxchgq %rcx, RW_OWNER(%rdi)
2061.2Sad	jnz,pn	_C_LABEL(rw_vector_enter)
2071.2Sad	ret
2081.2Sad
2091.2Sad/*
2101.2Sad * void	rw_exit(krwlock_t *rwl);
2111.2Sad *
2121.2Sad * Release one hold on a RW lock.
2131.2Sad */
2141.2SadNENTRY(rw_exit)					/* 0x0100, 64 bytes */
2151.2Sad	movq	RW_OWNER(%rdi), %rax
2161.2Sad	testb	$RW_WRITE_LOCKED, %al
2171.2Sad	jnz	2f
2181.2Sad
2191.2Sad	/*
2201.2Sad	 * Reader
2211.2Sad	 */
2221.2Sad1:	testb	$RW_HAS_WAITERS, %al
2231.2Sad	jnz,pn	3f
2241.2Sad	cmpq	$RW_READ_INCR, %rax
2251.2Sad	leaq	-RW_READ_INCR(%rax), %rdx
2261.2Sad	jb,pn	3f
2271.7Sad	LOCK(lockpatch4)
2281.2Sad	cmpxchgq %rdx, RW_OWNER(%rdi)
2291.2Sad	jnz,pn	1b
2301.2Sad	ret
2311.2Sad
2321.2Sad	/*
2331.2Sad	 * Writer
2341.2Sad	 */
2351.2Sad2:	leaq	-RW_WRITE_LOCKED(%rax), %rdx
2361.2Sad	subq	CPUVAR(CURLWP), %rdx
2371.2Sad	jnz,pn	3f
2381.7Sad	LOCK(lockpatch5)
2391.2Sad	cmpxchgq %rdx, RW_OWNER(%rdi)
2401.2Sad	jnz	3f
2411.2Sad	ret
2421.2Sad
2431.2Sad3:	jmp	_C_LABEL(rw_vector_exit)
2441.2Sad
2451.2Sad#endif	/* LOCKDEBUG */
2461.2Sad
2471.2Sad/*
2481.2Sad * int _lock_cas(uintptr_t *val, uintptr_t old, uintptr_t new);
2491.2Sad *
2501.2Sad * Perform an atomic compare-and-set operation.
2511.2Sad */
2521.2SadNENTRY(_lock_cas)				/* 0x0140, 19 bytes */
2531.2Sad	movq	%rsi, %rax
2541.7Sad	LOCK(lockpatch6)
2551.2Sad	cmpxchgq %rdx, (%rdi)
2561.2Sad	movq	$0, %rax
2571.2Sad	setz	%al				/* = 1 if success */
2581.2Sad	ret
2591.2Sad
2601.2Sad/*
2611.7Sad * Memory barrier operations, may be patched at runtime.
2621.2Sad */
2631.7Sad	.align	8
2641.7SadENTRY(mb_read)
2651.7Sad	LOCK(lockpatch7)
2661.7Sad	addq	$0, 0(%rsp)
2671.2Sad	ret
2681.2SadEND(mb_read_end, 8)
2691.2Sad
2701.7SadENTRY(mb_write)
2711.7Sad	nop
2721.2Sad	ret
2731.2SadEND(mb_write_end, 8)
2741.2Sad
2751.7SadENTRY(mb_memory)
2761.7Sad	LOCK(lockpatch8)
2771.7Sad	addq	$0, 0(%rsp)
2781.7Sad	ret
2791.7SadEND(mb_memory_end, 8)
2801.7Sad
2811.7SadENTRY(sse2_mb_read)
2821.7Sad	lfence
2831.7Sad	ret
2841.7SadEND(sse2_mb_read_end, 8)
2851.7Sad
2861.7SadENTRY(sse2_mb_memory)
2871.2Sad	mfence
2881.2Sad	ret
2891.7SadEND(sse2_mb_memory_end, 8)
2901.2Sad
2911.2Sad/*
2921.2Sad * Make sure code after the ret is properly encoded with nopness
2931.2Sad * by gas, or could stall newer processors.
2941.2Sad */
2951.2Sad
2961.7SadENTRY(x86_mb_nop)
2971.7Sad	nop
2981.2Sad	ret
2991.2SadEND(x86_mb_nop_end, 8)
3001.7Sad
3011.7Sad/*
3021.7Sad * XXX Don't belong here.
3031.7Sad */
3041.7SadENTRY(atomic_inc_uint)
3051.7Sad	LOCK(lockpatch9)
3061.7Sad	incl	(%rdi)
3071.7Sad	ret
3081.7Sad
3091.7SadENTRY(atomic_dec_uint_nv)
3101.7Sad	movl	$-1, %eax
3111.7Sad	LOCK(lockpatch10)
3121.7Sad	xaddl	%eax, (%rdi)
3131.7Sad	decl	%eax
3141.7Sad	ret
3151.7Sad
3161.7Sad/*
3171.7Sad * Patchpoints to replace with NOP when ncpu == 1.
3181.7Sad */
3191.7Sad#ifndef LOCKDEBUG
3201.7SadLABEL(x86_lockpatch)
3211.7Sad	.quad	lockpatch1, lockpatch2, lockpatch3, lockpatch4
3221.7Sad	.quad	lockpatch5, lockpatch6, lockpatch7, lockpatch8
3231.7Sad	.quad	lockpatch9, lockpatch10
3241.7Sad#ifdef FULL
3251.7Sad	.quad	lockpatch11
3261.7Sad#endif
3271.7Sad	.quad	0
3281.7Sad#endif
329