xref: /netbsd-src/sys/arch/mips/mips/lock_stubs_ras.S (revision ca453df649ce9db45b64d73678ba06cbccf9aa11)
1/*	$NetBSD: lock_stubs_ras.S,v 1.3 2011/04/29 22:04:42 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#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 * unsigned long ras_atomic_cas_ulong(volatile unsigned long *val,
85 *     unsigned long old, unsigned long new);
86 */
87	.text
88	.p2align LOG2_MIPS_LOCK_RAS_SIZE
89
90EXPORT(_lock_ras_start)
91STATIC_LEAF(ras_atomic_cas_noupdate)
92	j	ra
93	 move	v0, t0
94END(ras_atomic_cas_noupdate)
95
96	nop
97	.if ((. - _lock_ras_start) & 15) != 12
98	.error	"bas ras offset"
99	.endif
100STATIC_LEAF_NOPROFILE(ras_atomic_cas_ulong)
101	PTR_L	t0, (a0)	/* <- critical section start */
102_atomic_cas_ulong_ras_start:
103	 nop
104	bne	t0, a1, ras_atomic_cas_noupdate
105 	 nop
106	PTR_S	a2, (a0)	/* <- critical section end */
107_atomic_cas_ulong_ras_end:
108	j	ra
109	 move	v0, a1
110END(ras_atomic_cas_ulong)
111
112/*
113 * unsigned int ras_atomic_cas_uint(volatile unsigned int *val,
114 *     unsigned int old, unsigned int new);
115 */
116	nop
117	.if ((. - _lock_ras_start) & 15) != 12
118	.error	"bas ras offset"
119	.endif
120STATIC_LEAF_NOPROFILE(ras_atomic_cas_uint)
121	INT_L	t0, (a0)	/* <- critical section start */
122_atomic_cas_uint_ras_start:
123	 nop
124	bne	t0, a1, ras_atomic_cas_noupdate
125 	 nop
126	INT_S	a2, (a0)	/* <- critical section end */
127_atomic_cas_uint_ras_end:
128	j	ra
129	 move	v0, a1
130END(ras_atomic_cas_uint)
131
132/*
133 * int _ucas_ulong_ras(volatile u_long *val, u_long old, u_long new,
134 *     u_long *retp);
135 */
136	nop
137	.if ((. - _lock_ras_start) & 15) != 12
138	.error	"bas ras offset"
139	.endif
140STATIC_LEAF_NOPROFILE(_ucas_ulong_ras)
141	LONG_L	t0, (a0)	/* <- critical section start */
142_ucas_ulong_ras_start:
143	 nop
144	bne	t0, a1, _ucas_ulong_ras_end
145 	 nop
146	LONG_S	a2, (a0)	/* <- critical section end */
147_ucas_ulong_ras_end:
148	PTR_S	zero, PCB_ONFAULT(v1)
149	j	ra
150	 LONG_S	t0, 0(a3)
151END(_ucas_ulong_ras)
152
153/*
154 * int _ucas_uint_ras(volatile u_int *val, u_int old, u_int new, u_int *retp);
155 */
156	.if ((. - _lock_ras_start) & 15) != 12
157	.error	"bad ras offset"
158	.endif
159STATIC_LEAF_NOPROFILE(_ucas_uint_ras)
160	INT_L	t0, (a0)	/* <- critical section start */
161_ucas_uint_ras_start:
162	 nop
163	bne	t0, a1, _ucas_uint_ras_end
164 	 nop
165	INT_S	a2, (a0)	/* <- critical section end */
166_ucas_uint_ras_end:
167	PTR_S	zero, PCB_ONFAULT(v1)
168	j	ra
169	 INT_S	t0, 0(a3)
170END(_ucas_uint_ras)
171
172#ifndef LOCKDEBUG
173/*
174 * void ras_mutex_enter(kmutex_t *mtx);
175 */
176	.if ((. - _lock_ras_start) & 15) != 12
177	.error	"bad ras offset"
178	.endif
179STATIC_LEAF_NOPROFILE(ras_mutex_enter)
180	PTR_L	t0, (a0)	/* <- critical section start */
181_mutex_enter_ras_start:
182	 nop
183	bnez	t0, ras_mutex_vector_enter
184	 nop
185	PTR_S	MIPS_CURLWP, (a0)/* <- critical section end */
186_mutex_enter_ras_end:
187	j	ra
188	 nop
189END(ras_mutex_enter)
190
191/*
192 * int ras_mutex_exit(kmutex_t *mtx);
193 */
194	nop
195	.if ((. - _lock_ras_start) & 15) != 12
196	.error	"bas ras offset"
197	.endif
198STATIC_LEAF_NOPROFILE(ras_mutex_exit)
199	PTR_L	t0, (a0)	/* <- critical section start */
200_mutex_exit_ras_start:
201	 nop
202	bne	t0, MIPS_CURLWP, ras_mutex_vector_exit
203	 nop
204	PTR_S	zero, (a0)	/* <- critical section end */
205_mutex_exit_ras_exit:
206	j	ra
207	 nop
208END(ras_mutex_exit)
209
210/*
211 * These could moved out to fit in more RAS sequences.
212 */
213STATIC_LEAF_NOPROFILE(ras_mutex_vector_enter)
214	j	_C_LABEL(mutex_vector_enter)
215	 nop
216END(ras_mutex_vector_enter)
217
218STATIC_LEAF_NOPROFILE(ras_mutex_vector_exit)
219	j	_C_LABEL(mutex_vector_exit)
220	 nop
221END(ras_mutex_vector_exit)
222#endif	/* !LOCKDEBUG */
223
224	.p2align LOG2_MIPS_LOCK_RAS_SIZE	/* Get out of the RAS block */
225
226/*
227 * Patch up the given address.  We arrive here if we might have trapped
228 * within one of the critical sections above.  Do:
229 *
230 *	if ((addr & ~15) == ras)
231 *		return ras - 4;
232 *	... check next ...
233 *	return addr;
234 *
235 * Registers on entry:
236 *
237 *	k1	fault PC
238 *	ra	return address
239 *
240 * On exit:
241 *
242 *	k1	adjusted fault PC
243 *	ra	return address
244 *	t0	clobbered
245 *	t1	clobbered
246 */
247
248#define	RAS_MKMASK(a)	(1 << (((a)-_lock_ras_start) >> 4))
249
250/*
251 * Since each RAS is aligned on a 16 byte boundary, we can use its offset
252 * from _lock_ras_start to construct a bitmask of the valid RAS within.
253 */
254#ifndef LOCKDEBUG
255#define	MUTEX_RAS_MASK	(RAS_MKMASK(_mutex_enter_ras_start) \
256			|RAS_MKMASK(_mutex_exit_ras_start))
257#else
258#define	MUTEX_RAS_MASK	0
259#endif
260
261#define	RAS_MASK	(RAS_MKMASK(_atomic_cas_ulong_ras_start) \
262			|RAS_MKMASK(_atomic_cas_uint_ras_start) \
263			|RAS_MKMASK(_ucas_ulong_ras_start) \
264			|RAS_MKMASK(_ucas_uint_ras_start) \
265			|MUTEX_RAS_MASK)
266
267/*
268 * The caller has already determined that
269 * _lock_ras_start == (k1 & -MIPS_LOCK_RAS_SIZE)
270 */
271LEAF_NOPROFILE(_restart_lock_ras)
272	and	t0, k1, MIPS_LOCK_RAS_SIZE - 1
273				/* look at addr bits in ras region */
274	srl	t0, 4		/* focus on each set of 16 bytes */
275	li	t1, 1		/* need this to make a bitmask */
276	sllv	t1, t1, t0	/* now we have a bitmask of the PC */
277	andi	t1, RAS_MASK	/* was the PC in a RAS? */
278	bnez	t1, 1f		/* yes, adjust PC */
279	 and	t0, k1, 15	/* get offset in RAS */
280
281	j	ra
282	 nop
2831:
284	addu	t0, 4		/* bias offset by one more instruction */
285	j	ra
286	 PTR_SUBU k1, t0	/* and subtract that from the PC */
287END(_restart_lock_ras)
288
289/*
290 * int ras_ucas_uint(volatile u_int *ptr, u_int old, u_int new, u_int *retp);
291 */
292STATIC_LEAF(ras_ucas_uint)
293	PTR_L	v1, L_PCB(MIPS_CURLWP)
294	PTR_LA	v0, _C_LABEL(ras_ucaserr)
295	PTR_S	v0, PCB_ONFAULT(v1)
296	bltz	a0, _C_LABEL(ras_ucaserr)
297	 nop
298	b	_C_LABEL(_ucas_uint_ras)
299	 move	v0, zero			# assume success
300END(ras_ucas_uint)
301
302/*
303 * int ras_ucas_ulong(volatile u_long *ptr, u_long old, u_long new, u_long *retp);
304 */
305STATIC_LEAF(ras_ucas_ulong)
306	PTR_L	v1, L_PCB(MIPS_CURLWP)
307	PTR_LA	v0, _C_LABEL(ras_ucaserr)
308	PTR_S	v0, PCB_ONFAULT(v1)
309	bltz	a0, _C_LABEL(ras_ucaserr)
310	 nop
311	b	_C_LABEL(_ucas_ulong_ras)
312	 move	v0, zero			# assume success
313END(ras_ucas_ulong)
314
315/*
316 *
317 */
318STATIC_LEAF(ras_ucaserr)
319	PTR_S	zero, PCB_ONFAULT(v1)		# reset fault handler
320	j	ra
321	 li	v0, EFAULT			# return EFAULT on error
322END(ras_ucaserr)
323
324#ifndef LOCKDEBUG
325/*
326 * void	mutex_spin_enter(kmutex_t *mtx);
327 */
328STATIC_NESTED(ras_mutex_spin_enter, CALLFRAME_SIZ, ra)
329	move	t0, a0
330	PTR_L	t2, L_CPU(MIPS_CURLWP)
331	INT_L	a0, MTX_IPL(t0)
332#ifdef PARANOIA
333	INT_L	ta1, CPU_INFO_CPL(t2)		# get current cpl
334#endif
335
336	/*
337	 * We need to raise our IPL.
338	 * call splraise (only uses a0-a3, v0-v1, and ra)
339	 */
340	move	t3, ra
341	jal	_C_LABEL(splraise)
342	 nop
343	move	ra, t3
344
345	/*
346	 * If this is the first lock of the mutex, store the previous IPL
347	 * for exit.
348	 */
3491:
350	INT_L	ta2, CPU_INFO_MTX_COUNT(t2)
351	nop
352	INT_ADDU ta3, ta2, -1
353	INT_S	ta3, CPU_INFO_MTX_COUNT(t2)
354
355	bnez	ta2, 2f
356	 nop
357	INT_S	v0, CPU_INFO_MTX_OLDSPL(t2)	/* returned by splraise */
3582:
359#if defined(DIAGNOSTIC)
360	INT_L	t3, MTX_LOCK(t0)
361	li	t1, 1
362	bnez	t3, 3f
363	 nop
364	j	ra
365	 INT_S	t1, MTX_LOCK(t0)
3663:
367	j	_C_LABEL(mutex_spin_retry)
368	 nop
369#else	/* DIAGNOSTIC */
370	j	ra
371	 nop
372#endif	/* DIAGNOSTIC */
373END(ras_mutex_spin_enter)
374
375/*
376 * void	mutex_spin_exit(kmutex_t *mtx);
377 */
378LEAF(ras_mutex_spin_exit)
379	PTR_L	t2, L_CPU(MIPS_CURLWP)
380	nop
381#if defined(DIAGNOSTIC)
382	INT_L	t0, MTX_LOCK(a0)
383	nop
384	beqz	t0, 2f
385	 nop
386	INT_S	zero, MTX_LOCK(a0)
387#endif
388
389	/*
390	 * We need to grab this before the mutex count is incremented
391	 * because if we get an interrupt, it may see the count as zero
392	 * and overwrite the oldspl value with a bogus value.
393	 */
394#ifdef PARANOIA
395	INT_L	a2, MTX_IPL(a0)
396#endif
397	INT_L	a0, CPU_INFO_MTX_OLDSPL(t2)
398
399	/*
400	 * Increment the mutex count
401	 */
402	INT_L	t0, CPU_INFO_MTX_COUNT(t2)
403	nop
404	INT_ADDU t0, t0, 1
405	INT_S	t0, CPU_INFO_MTX_COUNT(t2)
406
407	/*
408	 * If the IPL doesn't change, nothing to do
409	 */
410	INT_L	a1, CPU_INFO_CPL(t2)
411	nop
412
413#ifdef PARANOIA
414	sltu	v0, a1, a2		# v0 = cpl < mtx_ipl
415	sltu	v1, a1, a0		# v1 = cpl < oldspl
416	sll	v0, 1
417	or	v0, v1
41812:	bnez	v0, 12b			# loop forever if either is true
419	 nop
420#endif /* PARANOIA */
421
422	beq	a0, a1, 1f		# if oldspl == cpl
423	 nop				#   no reason to drop ipl
424
425	bltz	t0, 1f			# there are still holders
426	 nop				# so don't drop IPL
427
428	/*
429	 * Mutex count is zero so we need to restore the old IPL
430	 */
431#ifdef PARANOIA
432	sltiu	v0, a0, IPL_HIGH+1
43313:	beqz	v0, 13b			# loop forever if ipl > IPL_HIGH
434	 nop
435#endif
436	j	 _C_LABEL(splx)
437	 nop
4381:
439	j	ra
440	 nop
441#if defined(DIAGNOSTIC)
4422:
443	j	_C_LABEL(mutex_vector_exit)
444	 nop
445#endif
446END(ras_mutex_spin_exit)
447#endif	/* !LOCKDEBUG */
448
449	.data
450EXPORT(mips_locore_atomicvec)
451	PTR_WORD 	ras_atomic_cas_uint
452	PTR_WORD 	ras_atomic_cas_ulong
453	PTR_WORD	ras_ucas_uint
454	PTR_WORD	ras_ucas_ulong
455#ifdef LOCKDEBUG
456	PTR_WORD	mutex_enter
457	PTR_WORD	mutex_exit
458	PTR_WORD	mutex_spin_enter
459	PTR_WORD	mutex_spin_exit
460#else
461	PTR_WORD	ras_mutex_enter
462	PTR_WORD	ras_mutex_exit
463	PTR_WORD	ras_mutex_spin_enter
464	PTR_WORD	ras_mutex_spin_exit
465#endif	/* !LOCKDEBUG */
466