lock_stubs.S revision 1.31
1/*	$NetBSD: lock_stubs.S,v 1.31 2019/02/11 14:59:32 cherry 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#if !defined(XENPV)
140	movl	CPU_INFO_IUNMASK(%r8,%rdi,4), %esi
141	CLI(ax)
142	testl	CPU_INFO_IPENDING(%r8), %esi
143	jnz	_C_LABEL(Xspllower)
144#endif
145#if defined(XEN)
146	movl	CPU_INFO_XUNMASK(%r8,%rdi,4), %esi
147	CLI(ax)
148	testl	CPU_INFO_XPENDING(%r8), %esi
149	jnz	_C_LABEL(Xspllower)
150#endif
151	movl	%edi, CPU_INFO_ILEVEL(%r8)
152	STI(ax)
1531:	rep					/* double byte ret as branch */
154	ret					/* target: see AMD docs */
155
156#else	/* DIAGNOSTIC */
157
158	movq	CPUVAR(SELF), %rsi
159	movb	$0x00, MTX_LOCK(%rdi)
160	movl	CPU_INFO_MTX_OLDSPL(%rsi), %ecx
161	incl	CPU_INFO_MTX_COUNT(%rsi)
162	movl	CPU_INFO_ILEVEL(%rsi),%edx
163	cmovnzl	%edx,%ecx
164	pushq	%rbx
165	cmpl	%edx,%ecx			/* new level is lower? */
166	jae	2f
1671:
168#if !defined(XENPV)
169	movl	CPU_INFO_IPENDING(%rsi),%eax
170	testl	%eax,CPU_INFO_IUNMASK(%rsi,%rcx,4)/* deferred interrupts? */
171	jnz	3f
172	movl	%eax,%ebx
173	cmpxchg8b CPU_INFO_ISTATE(%rsi)		/* swap in new ilevel */
174	jnz	4f
175#endif
176#if defined(XEN)
177	movl	CPU_INFO_XPENDING(%rsi),%eax
178	testl	%eax,CPU_INFO_XUNMASK(%rsi,%rcx,4)/* deferred interrupts? */
179	jnz	3f
180	movl	%edx, %eax
181	cmpxchgl %ecx, CPU_INFO_ILEVEL(%rsi)
182	jnz	4f
183#endif
1842:
185	popq	%rbx
186	ret
1873:
188	popq	%rbx
189	movl	%ecx, %edi
190	jmp	_C_LABEL(Xspllower)
1914:
192	jmp	1b
193
194#endif	/* DIAGNOSTIC */
195
196END(mutex_spin_exit)
197
198/*
199 * void	rw_enter(krwlock_t *rwl, krw_t op);
200 *
201 * Acquire one hold on a RW lock.
202 */
203ENTRY(rw_enter)
204	cmpl	$RW_READER, %esi
205	jne	2f
206
207	/*
208	 * Reader: this is the most common case.
209	 */
210	movq	(%rdi), %rax
2110:
212	testb	$(RW_WRITE_LOCKED|RW_WRITE_WANTED), %al
213	jnz	3f
214	leaq	RW_READ_INCR(%rax), %rdx
215	LOCK(2)
216	cmpxchgq %rdx, (%rdi)
217	jnz	1f
218	RET(3)
2191:
220	jmp	0b
221
222	/*
223	 * Writer: if the compare-and-set fails, don't bother retrying.
224	 */
2252:	movq	CPUVAR(CURLWP), %rcx
226	xorq	%rax, %rax
227	orq	$RW_WRITE_LOCKED, %rcx
228	LOCK(3)
229	cmpxchgq %rcx, (%rdi)
230	jnz	3f
231	RET(4)
2323:
233	jmp	_C_LABEL(rw_vector_enter)
234END(rw_enter)
235
236/*
237 * void	rw_exit(krwlock_t *rwl);
238 *
239 * Release one hold on a RW lock.
240 */
241ENTRY(rw_exit)
242	movq	(%rdi), %rax
243	testb	$RW_WRITE_LOCKED, %al
244	jnz	2f
245
246	/*
247	 * Reader
248	 */
2490:	testb	$RW_HAS_WAITERS, %al
250	jnz	3f
251	cmpq	$RW_READ_INCR, %rax
252	jb	3f
253	leaq	-RW_READ_INCR(%rax), %rdx
254	LOCK(4)
255	cmpxchgq %rdx, (%rdi)
256	jnz	1f
257	ret
2581:
259	jmp	0b
260
261	/*
262	 * Writer
263	 */
2642:	leaq	-RW_WRITE_LOCKED(%rax), %rdx
265	subq	CPUVAR(CURLWP), %rdx
266	jnz	3f
267	LOCK(5)
268	cmpxchgq %rdx, (%rdi)
269	jnz	3f
270	ret
271
2723:	jmp	_C_LABEL(rw_vector_exit)
273END(rw_exit)
274
275/*
276 * int	rw_tryenter(krwlock_t *rwl, krw_t op);
277 *
278 * Try to acquire one hold on a RW lock.
279 */
280ENTRY(rw_tryenter)
281	cmpl	$RW_READER, %esi
282	jne	2f
283
284	/*
285	 * Reader: this is the most common case.
286	 */
287	movq	(%rdi), %rax
2880:
289	testb	$(RW_WRITE_LOCKED|RW_WRITE_WANTED), %al
290	jnz	4f
291	leaq	RW_READ_INCR(%rax), %rdx
292	LOCK(8)
293	cmpxchgq %rdx, (%rdi)
294	jnz	1f
295	movl	%edx, %eax			/* nonzero */
296	RET(5)
2971:
298	jmp	0b
299
300	/*
301	 * Writer: if the compare-and-set fails, don't bother retrying.
302	 */
3032:	movq	CPUVAR(CURLWP), %rcx
304	xorq	%rax, %rax
305	orq	$RW_WRITE_LOCKED, %rcx
306	LOCK(9)
307	cmpxchgq %rcx, (%rdi)
308	movl	$0, %eax
309	setz	%al
3103:
311	RET(6)
312	ret
3134:
314	xorl	%eax, %eax
315	jmp	3b
316END(rw_tryenter)
317
318#endif	/* LOCKDEBUG */
319
320/*
321 * Spinlocks.
322 */
323ENTRY(__cpu_simple_lock_init)
324	movb	$0, (%rdi)
325	ret
326END(__cpu_simple_lock_init)
327
328ENTRY(__cpu_simple_lock)
329	movl	$0x0100, %eax
3301:
331	LOCK(6)
332	cmpxchgb %ah, (%rdi)
333	jnz	2f
334	RET(7)
3352:
336	movl	$0x0100, %eax
337	pause
338	nop
339	nop
340	cmpb	$0, (%rdi)
341	je	1b
342	jmp	2b
343END(__cpu_simple_lock)
344
345ENTRY(__cpu_simple_unlock)
346	movb	$0, (%rdi)
347	ret
348END(__cpu_simple_unlock)
349
350ENTRY(__cpu_simple_lock_try)
351	movl	$0x0100, %eax
352	LOCK(7)
353	cmpxchgb %ah, (%rdi)
354	movl	$0, %eax
355	setz	%al
356	RET(8)
357END(__cpu_simple_lock_try)
358
359