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