lock_stubs.S revision 1.29
1/*	$NetBSD: lock_stubs.S,v 1.29 2018/07/14 14:29:40 maxv Exp $	*/
2
3/*-
4 * Copyright (c) 2006, 2007, 2008, 2009 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Andrew Doran.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * AMD64 lock stubs.  Calling convention:
34 *
35 * %rdi		arg 1
36 * %rsi		arg 2
37 * %rdx		arg 3
38 * %rax		return value
39 */
40
41#include "opt_multiprocessor.h"
42#include "opt_lockdebug.h"
43
44#include <machine/asm.h>
45#include <machine/frameasm.h>
46
47#include "assym.h"
48
49#define	ENDLABEL(name,a) .align	a; LABEL(name)
50#define	LOCK(num)	\
51	HOTPATCH(HP_NAME_NOLOCK, 1)	; \
52	lock
53#define	RET(num)	\
54	HOTPATCH(HP_NAME_RETFENCE, 3)	; \
55	ret; nop; nop			; \
56	ret
57
58#ifndef LOCKDEBUG
59
60/*
61 * void mutex_enter(kmutex_t *mtx);
62 *
63 * Acquire a mutex and post a load fence.
64 */
65	.align	64
66
67ENTRY(mutex_enter)
68	movq	CPUVAR(CURLWP), %rcx
69	xorq	%rax, %rax
70	LOCK(1)
71	cmpxchgq %rcx, (%rdi)
72	jnz	1f
73	RET(1)
741:
75	jmp	_C_LABEL(mutex_vector_enter)
76END(mutex_enter)
77
78/*
79 * void mutex_exit(kmutex_t *mtx);
80 *
81 * Release a mutex and post a load fence.
82 *
83 * See comments in mutex_vector_enter() about doing this operation unlocked
84 * on multiprocessor systems, and comments in arch/x86/include/lock.h about
85 * memory ordering on Intel x86 systems.
86 */
87ENTRY(mutex_exit)
88	movq	CPUVAR(CURLWP), %rax
89	xorq	%rdx, %rdx
90	cmpxchgq %rdx, (%rdi)
91	jnz	1f
92	ret
931:
94	jmp	_C_LABEL(mutex_vector_exit)
95END(mutex_exit)
96
97/*
98 * void mutex_spin_enter(kmutex_t *mtx);
99 *
100 * Acquire a spin mutex and post a load fence.
101 */
102ENTRY(mutex_spin_enter)
103	movl	$1, %eax
104	movl	CPUVAR(ILEVEL), %esi
105	movzbl	MTX_IPL(%rdi), %ecx		/* new SPL */
106	cmpl	%ecx, %esi			/* higher? */
107	cmovgl	%esi, %ecx
108	movl	%ecx, CPUVAR(ILEVEL)		/* splraiseipl() */
109	subl	%eax, CPUVAR(MTX_COUNT)		/* decl doesnt set CF */
110	cmovncl	CPUVAR(MTX_OLDSPL), %esi
111	movl	%esi, CPUVAR(MTX_OLDSPL)
112	xchgb	%al, MTX_LOCK(%rdi)		/* lock */
113#ifdef MULTIPROCESSOR	/* XXX for xen */
114	testb	%al, %al
115	jnz	1f
116#endif
117	RET(2)
1181:
119	jmp	_C_LABEL(mutex_spin_retry)	/* failed; hard case */
120END(mutex_spin_enter)
121
122/*
123 * void mutex_spin_exit(kmutex_t *mtx);
124 *
125 * Release a spin mutex and post a load fence.
126 */
127ENTRY(mutex_spin_exit)
128#ifdef DIAGNOSTIC
129
130	movl	$0x0001, %eax			/* new + expected value */
131	movq	CPUVAR(SELF), %r8
132	cmpxchgb %ah, MTX_LOCK(%rdi)		/* unlock */
133	jnz	_C_LABEL(mutex_vector_exit)	/* hard case if problems */
134	movl	CPU_INFO_MTX_OLDSPL(%r8), %edi
135	incl	CPU_INFO_MTX_COUNT(%r8)
136	jnz	1f
137	cmpl	CPU_INFO_ILEVEL(%r8), %edi
138	jae	1f
139	movl	CPU_INFO_IUNMASK(%r8,%rdi,4), %esi
140	CLI(ax)
141	testl	CPU_INFO_IPENDING(%r8), %esi
142	jnz	_C_LABEL(Xspllower)
143	movl	%edi, CPU_INFO_ILEVEL(%r8)
144	STI(ax)
1451:	rep					/* double byte ret as branch */
146	ret					/* target: see AMD docs */
147
148#else	/* DIAGNOSTIC */
149
150	movq	CPUVAR(SELF), %rsi
151	movb	$0x00, MTX_LOCK(%rdi)
152	movl	CPU_INFO_MTX_OLDSPL(%rsi), %ecx
153	incl	CPU_INFO_MTX_COUNT(%rsi)
154	movl	CPU_INFO_ILEVEL(%rsi),%edx
155	cmovnzl	%edx,%ecx
156	pushq	%rbx
157	cmpl	%edx,%ecx			/* new level is lower? */
158	jae	2f
1591:
160	movl	CPU_INFO_IPENDING(%rsi),%eax
161	testl	%eax,CPU_INFO_IUNMASK(%rsi,%rcx,4)/* deferred interrupts? */
162	jnz	3f
163	movl	%eax,%ebx
164	cmpxchg8b CPU_INFO_ISTATE(%rsi)		/* swap in new ilevel */
165	jnz	4f
1662:
167	popq	%rbx
168	ret
1693:
170	popq	%rbx
171	movl	%ecx, %edi
172	jmp	_C_LABEL(Xspllower)
1734:
174	jmp	1b
175
176#endif	/* DIAGNOSTIC */
177
178END(mutex_spin_exit)
179
180/*
181 * void	rw_enter(krwlock_t *rwl, krw_t op);
182 *
183 * Acquire one hold on a RW lock.
184 */
185ENTRY(rw_enter)
186	cmpl	$RW_READER, %esi
187	jne	2f
188
189	/*
190	 * Reader: this is the most common case.
191	 */
192	movq	(%rdi), %rax
1930:
194	testb	$(RW_WRITE_LOCKED|RW_WRITE_WANTED), %al
195	jnz	3f
196	leaq	RW_READ_INCR(%rax), %rdx
197	LOCK(2)
198	cmpxchgq %rdx, (%rdi)
199	jnz	1f
200	RET(3)
2011:
202	jmp	0b
203
204	/*
205	 * Writer: if the compare-and-set fails, don't bother retrying.
206	 */
2072:	movq	CPUVAR(CURLWP), %rcx
208	xorq	%rax, %rax
209	orq	$RW_WRITE_LOCKED, %rcx
210	LOCK(3)
211	cmpxchgq %rcx, (%rdi)
212	jnz	3f
213	RET(4)
2143:
215	jmp	_C_LABEL(rw_vector_enter)
216END(rw_enter)
217
218/*
219 * void	rw_exit(krwlock_t *rwl);
220 *
221 * Release one hold on a RW lock.
222 */
223ENTRY(rw_exit)
224	movq	(%rdi), %rax
225	testb	$RW_WRITE_LOCKED, %al
226	jnz	2f
227
228	/*
229	 * Reader
230	 */
2310:	testb	$RW_HAS_WAITERS, %al
232	jnz	3f
233	cmpq	$RW_READ_INCR, %rax
234	jb	3f
235	leaq	-RW_READ_INCR(%rax), %rdx
236	LOCK(4)
237	cmpxchgq %rdx, (%rdi)
238	jnz	1f
239	ret
2401:
241	jmp	0b
242
243	/*
244	 * Writer
245	 */
2462:	leaq	-RW_WRITE_LOCKED(%rax), %rdx
247	subq	CPUVAR(CURLWP), %rdx
248	jnz	3f
249	LOCK(5)
250	cmpxchgq %rdx, (%rdi)
251	jnz	3f
252	ret
253
2543:	jmp	_C_LABEL(rw_vector_exit)
255END(rw_exit)
256
257/*
258 * int	rw_tryenter(krwlock_t *rwl, krw_t op);
259 *
260 * Try to acquire one hold on a RW lock.
261 */
262ENTRY(rw_tryenter)
263	cmpl	$RW_READER, %esi
264	jne	2f
265
266	/*
267	 * Reader: this is the most common case.
268	 */
269	movq	(%rdi), %rax
2700:
271	testb	$(RW_WRITE_LOCKED|RW_WRITE_WANTED), %al
272	jnz	4f
273	leaq	RW_READ_INCR(%rax), %rdx
274	LOCK(8)
275	cmpxchgq %rdx, (%rdi)
276	jnz	1f
277	movl	%edx, %eax			/* nonzero */
278	RET(5)
2791:
280	jmp	0b
281
282	/*
283	 * Writer: if the compare-and-set fails, don't bother retrying.
284	 */
2852:	movq	CPUVAR(CURLWP), %rcx
286	xorq	%rax, %rax
287	orq	$RW_WRITE_LOCKED, %rcx
288	LOCK(9)
289	cmpxchgq %rcx, (%rdi)
290	movl	$0, %eax
291	setz	%al
2923:
293	RET(6)
294	ret
2954:
296	xorl	%eax, %eax
297	jmp	3b
298END(rw_tryenter)
299
300#endif	/* LOCKDEBUG */
301
302/*
303 * Spinlocks.
304 */
305ENTRY(__cpu_simple_lock_init)
306	movb	$0, (%rdi)
307	ret
308END(__cpu_simple_lock_init)
309
310ENTRY(__cpu_simple_lock)
311	movl	$0x0100, %eax
3121:
313	LOCK(6)
314	cmpxchgb %ah, (%rdi)
315	jnz	2f
316	RET(7)
3172:
318	movl	$0x0100, %eax
319	pause
320	nop
321	nop
322	cmpb	$0, (%rdi)
323	je	1b
324	jmp	2b
325END(__cpu_simple_lock)
326
327ENTRY(__cpu_simple_unlock)
328	movb	$0, (%rdi)
329	ret
330END(__cpu_simple_unlock)
331
332ENTRY(__cpu_simple_lock_try)
333	movl	$0x0100, %eax
334	LOCK(7)
335	cmpxchgb %ah, (%rdi)
336	movl	$0, %eax
337	setz	%al
338	RET(8)
339END(__cpu_simple_lock_try)
340
341