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