xref: /netbsd-src/sys/arch/mips/mips/lock_stubs_ras.S (revision 63aea4bd5b445e491ff0389fe27ec78b3099dba3)
1/*	$NetBSD: lock_stubs_ras.S,v 1.5 2015/06/07 06:07:49 matt Exp $	*/
2
3/*-
4 * Copyright (c) 2007 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#include "opt_cputype.h"
33#include "opt_lockdebug.h"
34#include "opt_multiprocessor.h"
35
36#include <sys/errno.h>
37
38#include <machine/asm.h>
39
40#include "assym.h"
41
42/*
43 * We rely on mips_vector_init to choose to not use these routines if we are
44 * on a system with multiple CPUs.  We can still use this on a simple CPU
45 * with MULTIPROCESSOR since it might be useful to be using preemption.
46 */
47
48/*
49 * Lock stubs for non-MP kernels.  These are implemented using restartable
50 * sequences, since LL/SC are either not available (MIPS1 and a couple of
51 * oddball MIPS3 CPUs) or not desirable (overhead).
52 *
53 * The order of the generated code is particularly important here.  Some
54 * assumptions:
55 *
56 * o All of the critical sections are 20 bytes in size, and the second
57 *   instruction in each critical section is aligned on a 16 byte boundary
58 *   (see top of _restart_lock_ras() for why).  The entry is defined here as
59 *   the point where a restart occurs if we trap within the section.
60 *
61 * o The entire code block is aligned on a 256 byte boundary, and is
62 *   256 bytes in size.  This is to allow us to do an pessimistic check
63 *   after taking a trap with:
64 *
65 *	if ((addr & ~255) == _lock_ras_start)
66 *		addr = _restart_lock_ras(addr);
67 *
68 *   See definition of MIPS_LOCK_RAS_SIZE in asm.h.
69 *
70 * o In order to keep the size of the block down, the routines are run
71 *   into each other.  Use objdump -d to check alignment after making
72 *   changes.
73 */
74#ifdef _LP64
75	.set	mips3
76#else
77	.set	mips1
78#endif
79	.set	noreorder
80	.set	noat
81
82/*
83 * to work around the branch prediction engine misbehavior of
84 * Loongson 2F processors we need to clear the branch target buffer before
85 * a j ra.  This requires extra instructions which don't fit in the RAS blocks,
86 * so do a PC-relative just to a block of code (this is the same size as
87 * a j ra) where we can let the assembler install the workaround.
88 */
89#ifdef MIPS3_LOONGSON2F
90#define J_RA	j loongson_return
91#else
92#define J_RA	j ra
93#endif
94
95
96/*
97 * unsigned long ras_atomic_cas_ulong(volatile unsigned long *val,
98 *     unsigned long old, unsigned long new);
99 */
100	.text
101	.p2align LOG2_MIPS_LOCK_RAS_SIZE
102
103EXPORT(_lock_ras_start)
104STATIC_LEAF(ras_atomic_cas_noupdate)
105	J_RA
106	 move	v0, t0
107END(ras_atomic_cas_noupdate)
108
109	nop
110	.if ((. - _lock_ras_start) & 15) != 12
111	.error	"bas ras offset"
112	.endif
113STATIC_LEAF_NOPROFILE(ras_atomic_cas_ulong)
114	PTR_L	t0, (a0)	/* <- critical section start */
115_atomic_cas_ulong_ras_start:
116	 nop
117	bne	t0, a1, ras_atomic_cas_noupdate
118 	 nop
119	PTR_S	a2, (a0)	/* <- critical section end */
120_atomic_cas_ulong_ras_end:
121	J_RA
122	 move	v0, a1
123END(ras_atomic_cas_ulong)
124
125/*
126 * unsigned int ras_atomic_cas_uint(volatile unsigned int *val,
127 *     unsigned int old, unsigned int new);
128 */
129	nop
130	.if ((. - _lock_ras_start) & 15) != 12
131	.error	"bas ras offset"
132	.endif
133STATIC_LEAF_NOPROFILE(ras_atomic_cas_uint)
134	INT_L	t0, (a0)	/* <- critical section start */
135_atomic_cas_uint_ras_start:
136	 nop
137	bne	t0, a1, ras_atomic_cas_noupdate
138 	 nop
139	INT_S	a2, (a0)	/* <- critical section end */
140_atomic_cas_uint_ras_end:
141	J_RA
142	 move	v0, a1
143END(ras_atomic_cas_uint)
144
145/*
146 * int _ucas_ulong_ras(volatile u_long *val, u_long old, u_long new,
147 *     u_long *retp);
148 */
149	nop
150	.if ((. - _lock_ras_start) & 15) != 12
151	.error	"bas ras offset"
152	.endif
153STATIC_LEAF_NOPROFILE(_ucas_ulong_ras)
154	LONG_L	t0, (a0)	/* <- critical section start */
155_ucas_ulong_ras_start:
156	 nop
157	bne	t0, a1, _ucas_ulong_ras_end
158 	 nop
159	LONG_S	a2, (a0)	/* <- critical section end */
160_ucas_ulong_ras_end:
161	PTR_S	zero, PCB_ONFAULT(v1)
162	J_RA
163	 LONG_S	t0, 0(a3)
164END(_ucas_ulong_ras)
165
166/*
167 * int _ucas_uint_ras(volatile u_int *val, u_int old, u_int new, u_int *retp);
168 */
169	.if ((. - _lock_ras_start) & 15) != 12
170	.error	"bad ras offset"
171	.endif
172STATIC_LEAF_NOPROFILE(_ucas_uint_ras)
173	INT_L	t0, (a0)	/* <- critical section start */
174_ucas_uint_ras_start:
175	 nop
176	bne	t0, a1, _ucas_uint_ras_end
177 	 nop
178	INT_S	a2, (a0)	/* <- critical section end */
179_ucas_uint_ras_end:
180	PTR_S	zero, PCB_ONFAULT(v1)
181	J_RA
182	 INT_S	t0, 0(a3)
183END(_ucas_uint_ras)
184
185#ifndef LOCKDEBUG
186/*
187 * void ras_mutex_enter(kmutex_t *mtx);
188 */
189	.if ((. - _lock_ras_start) & 15) != 12
190	.error	"bad ras offset"
191	.endif
192STATIC_LEAF_NOPROFILE(ras_mutex_enter)
193	PTR_L	t0, (a0)	/* <- critical section start */
194_mutex_enter_ras_start:
195	 nop
196	bnez	t0, ras_mutex_vector_enter
197	 nop
198	PTR_S	MIPS_CURLWP, (a0)/* <- critical section end */
199_mutex_enter_ras_end:
200	J_RA
201	 nop
202END(ras_mutex_enter)
203
204/*
205 * int ras_mutex_exit(kmutex_t *mtx);
206 */
207	nop
208	.if ((. - _lock_ras_start) & 15) != 12
209	.error	"bas ras offset"
210	.endif
211STATIC_LEAF_NOPROFILE(ras_mutex_exit)
212	PTR_L	t0, (a0)	/* <- critical section start */
213_mutex_exit_ras_start:
214	 nop
215	bne	t0, MIPS_CURLWP, ras_mutex_vector_exit
216	 nop
217	PTR_S	zero, (a0)	/* <- critical section end */
218_mutex_exit_ras_exit:
219	J_RA
220	 nop
221END(ras_mutex_exit)
222
223/*
224 * These could moved out to fit in more RAS sequences.
225 */
226STATIC_LEAF_NOPROFILE(ras_mutex_vector_enter)
227	j	_C_LABEL(mutex_vector_enter)
228	 nop
229END(ras_mutex_vector_enter)
230
231STATIC_LEAF_NOPROFILE(ras_mutex_vector_exit)
232	j	_C_LABEL(mutex_vector_exit)
233	 nop
234END(ras_mutex_vector_exit)
235#endif	/* !LOCKDEBUG */
236
237	.p2align LOG2_MIPS_LOCK_RAS_SIZE	/* Get out of the RAS block */
238
239	.set at
240#ifdef MIPS3_LOONGSON2F
241loongson_return:
242	j	ra
243	 nop
244#endif
245
246/*
247 * Patch up the given address.  We arrive here if we might have trapped
248 * within one of the critical sections above.  Do:
249 *
250 *	if ((addr & ~15) == ras)
251 *		return ras - 4;
252 *	... check next ...
253 *	return addr;
254 *
255 * Registers on entry:
256 *
257 *	k1	fault PC
258 *	ra	return address
259 *
260 * On exit:
261 *
262 *	k1	adjusted fault PC
263 *	ra	return address
264 *	t0	clobbered
265 *	t1	clobbered
266 */
267
268#define	RAS_MKMASK(a)	(1 << (((a)-_lock_ras_start) >> 4))
269
270/*
271 * Since each RAS is aligned on a 16 byte boundary, we can use its offset
272 * from _lock_ras_start to construct a bitmask of the valid RAS within.
273 */
274#ifndef LOCKDEBUG
275#define	MUTEX_RAS_MASK	(RAS_MKMASK(_mutex_enter_ras_start) \
276			|RAS_MKMASK(_mutex_exit_ras_start))
277#else
278#define	MUTEX_RAS_MASK	0
279#endif
280
281#define	RAS_MASK	(RAS_MKMASK(_atomic_cas_ulong_ras_start) \
282			|RAS_MKMASK(_atomic_cas_uint_ras_start) \
283			|RAS_MKMASK(_ucas_ulong_ras_start) \
284			|RAS_MKMASK(_ucas_uint_ras_start) \
285			|MUTEX_RAS_MASK)
286
287/*
288 * The caller has already determined that
289 * _lock_ras_start == (k1 & -MIPS_LOCK_RAS_SIZE)
290 */
291LEAF_NOPROFILE(_restart_lock_ras)
292	and	t0, k1, MIPS_LOCK_RAS_SIZE - 1
293				/* look at addr bits in ras region */
294	srl	t0, 4		/* focus on each set of 16 bytes */
295	li	t1, 1		/* need this to make a bitmask */
296	sllv	t1, t1, t0	/* now we have a bitmask of the PC */
297	andi	t1, RAS_MASK	/* was the PC in a RAS? */
298	bnez	t1, 1f		/* yes, adjust PC */
299	 and	t0, k1, 15	/* get offset in RAS */
300
301	j	ra
302	 nop
3031:
304	addu	t0, 4		/* bias offset by one more instruction */
305	j	ra
306	 PTR_SUBU k1, t0	/* and subtract that from the PC */
307END(_restart_lock_ras)
308
309/*
310 * int ras_ucas_uint(volatile u_int *ptr, u_int old, u_int new, u_int *retp);
311 */
312STATIC_LEAF(ras_ucas_uint)
313	PTR_L	v1, L_PCB(MIPS_CURLWP)
314	PTR_LA	v0, _C_LABEL(ras_ucaserr)
315	PTR_S	v0, PCB_ONFAULT(v1)
316	bltz	a0, _C_LABEL(ras_ucaserr)
317	 nop
318	b	_C_LABEL(_ucas_uint_ras)
319	 move	v0, zero			# assume success
320END(ras_ucas_uint)
321
322/*
323 * int ras_ucas_ulong(volatile u_long *ptr, u_long old, u_long new, u_long *retp);
324 */
325STATIC_LEAF(ras_ucas_ulong)
326	PTR_L	v1, L_PCB(MIPS_CURLWP)
327	PTR_LA	v0, _C_LABEL(ras_ucaserr)
328	PTR_S	v0, PCB_ONFAULT(v1)
329	bltz	a0, _C_LABEL(ras_ucaserr)
330	 nop
331	b	_C_LABEL(_ucas_ulong_ras)
332	 move	v0, zero			# assume success
333END(ras_ucas_ulong)
334
335/*
336 *
337 */
338STATIC_LEAF(ras_ucaserr)
339	PTR_S	zero, PCB_ONFAULT(v1)		# reset fault handler
340	j	ra
341	 li	v0, EFAULT			# return EFAULT on error
342END(ras_ucaserr)
343
344#ifndef LOCKDEBUG
345/*
346 * void	mutex_spin_enter(kmutex_t *mtx);
347 */
348STATIC_NESTED(ras_mutex_spin_enter, CALLFRAME_SIZ, ra)
349	move	t0, a0
350	PTR_L	t2, L_CPU(MIPS_CURLWP)
351	INT_L	a0, MTX_IPL(t0)
352#ifdef PARANOIA
353	INT_L	ta1, CPU_INFO_CPL(t2)		# get current cpl
354#endif
355
356	/*
357	 * We need to raise our IPL.
358	 * call splraise (only uses a0-a3, v0-v1, and ra)
359	 */
360	move	t3, ra
361	jal	_C_LABEL(splraise)
362	 nop
363	move	ra, t3
364
365	/*
366	 * If this is the first lock of the mutex, store the previous IPL
367	 * for exit.
368	 */
3691:
370	INT_L	ta2, CPU_INFO_MTX_COUNT(t2)
371	nop
372	INT_ADDU ta3, ta2, -1
373	INT_S	ta3, CPU_INFO_MTX_COUNT(t2)
374
375	bnez	ta2, 2f
376	 nop
377	INT_S	v0, CPU_INFO_MTX_OLDSPL(t2)	/* returned by splraise */
3782:
379#if defined(DIAGNOSTIC)
380	INT_L	t3, MTX_LOCK(t0)
381	li	t1, 1
382	bnez	t3, 3f
383	 nop
384	j	ra
385	 INT_S	t1, MTX_LOCK(t0)
3863:
387	j	_C_LABEL(mutex_spin_retry)
388	 nop
389#else	/* DIAGNOSTIC */
390	j	ra
391	 nop
392#endif	/* DIAGNOSTIC */
393END(ras_mutex_spin_enter)
394
395/*
396 * void	mutex_spin_exit(kmutex_t *mtx);
397 */
398LEAF(ras_mutex_spin_exit)
399	PTR_L	t2, L_CPU(MIPS_CURLWP)
400	nop
401#if defined(DIAGNOSTIC)
402	INT_L	t0, MTX_LOCK(a0)
403	nop
404	beqz	t0, 2f
405	 nop
406	INT_S	zero, MTX_LOCK(a0)
407#endif
408
409	/*
410	 * We need to grab this before the mutex count is incremented
411	 * because if we get an interrupt, it may see the count as zero
412	 * and overwrite the oldspl value with a bogus value.
413	 */
414#ifdef PARANOIA
415	INT_L	a2, MTX_IPL(a0)
416#endif
417	INT_L	a0, CPU_INFO_MTX_OLDSPL(t2)
418
419	/*
420	 * Increment the mutex count
421	 */
422	INT_L	t0, CPU_INFO_MTX_COUNT(t2)
423	nop
424	INT_ADDU t0, t0, 1
425	INT_S	t0, CPU_INFO_MTX_COUNT(t2)
426
427	/*
428	 * If the IPL doesn't change, nothing to do
429	 */
430	INT_L	a1, CPU_INFO_CPL(t2)
431	nop
432
433#ifdef PARANOIA
434	sltu	v0, a1, a2		# v0 = cpl < mtx_ipl
435	sltu	v1, a1, a0		# v1 = cpl < oldspl
436	sll	v0, 1
437	or	v0, v1
43812:	bnez	v0, 12b			# loop forever if either is true
439	 nop
440#endif /* PARANOIA */
441
442	beq	a0, a1, 1f		# if oldspl == cpl
443	 nop				#   no reason to drop ipl
444
445	bltz	t0, 1f			# there are still holders
446	 nop				# so don't drop IPL
447
448	/*
449	 * Mutex count is zero so we need to restore the old IPL
450	 */
451#ifdef PARANOIA
452	sltiu	v0, a0, IPL_HIGH+1
45313:	beqz	v0, 13b			# loop forever if ipl > IPL_HIGH
454	 nop
455#endif
456	j	 _C_LABEL(splx)
457	 nop
4581:
459	j	ra
460	 nop
461#if defined(DIAGNOSTIC)
4622:
463	j	_C_LABEL(mutex_vector_exit)
464	 nop
465#endif
466END(ras_mutex_spin_exit)
467#endif	/* !LOCKDEBUG */
468
469	.data
470EXPORT(mips_locore_atomicvec)
471	PTR_WORD 	ras_atomic_cas_uint
472	PTR_WORD 	ras_atomic_cas_ulong
473	PTR_WORD	ras_ucas_uint
474	PTR_WORD	ras_ucas_ulong
475#ifdef LOCKDEBUG
476	PTR_WORD	mutex_enter
477	PTR_WORD	mutex_exit
478	PTR_WORD	mutex_spin_enter
479	PTR_WORD	mutex_spin_exit
480#else
481	PTR_WORD	ras_mutex_enter
482	PTR_WORD	ras_mutex_exit
483	PTR_WORD	ras_mutex_spin_enter
484	PTR_WORD	ras_mutex_spin_exit
485#endif	/* !LOCKDEBUG */
486