1/*	$NetBSD: copy.S,v 1.18 2020/06/30 16:20:01 maxv Exp $	*/
2
3/*
4 * Copyright (c) 1992, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Digital Equipment Corporation and Ralph Campbell.
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 * 3. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * Copyright (C) 1989 Digital Equipment Corporation.
35 * Permission to use, copy, modify, and distribute this software and
36 * its documentation for any purpose and without fee is hereby granted,
37 * provided that the above copyright notice appears in all copies.
38 * Digital Equipment Corporation makes no representations about the
39 * suitability of this software for any purpose.  It is provided "as is"
40 * without express or implied warranty.
41 *
42 * from: Header: /sprite/src/kernel/mach/ds3100.md/RCS/loMem.s,
43 *	v 1.1 89/07/11 17:55:04 nelson Exp  SPRITE (DECWRL)
44 * from: Header: /sprite/src/kernel/mach/ds3100.md/RCS/machAsm.s,
45 *	v 9.2 90/01/29 18:00:39 shirriff Exp  SPRITE (DECWRL)
46 * from: Header: /sprite/src/kernel/vm/ds3100.md/vmPmaxAsm.s,
47 *	v 1.1 89/07/10 14:27:41 nelson Exp  SPRITE (DECWRL)
48 *
49 *	@(#)locore.s	8.5 (Berkeley) 1/4/94
50 */
51
52/*
53 * copy(9) - kernel space to/from user space copy functions.
54 * fetch(9) - fetch data from user-space.
55 * store(9) - store data to user-space.
56 */
57
58#include <sys/errno.h>
59#include <mips/asm.h>
60#include "assym.h"
61
62	.set	noreorder
63
64/*
65 * int copyinstr(void *uaddr, void *kaddr, size_t maxlen, size_t *lencopied)
66 * Copy a NUL-terminated string, at most maxlen characters long, from the
67 * user's address space.  Return the number of characters copied (including
68 * the NUL) in *lencopied.  If the string is too long, return ENAMETOOLONG;
69 * else return 0 or EFAULT.
70 */
71LEAF(copyinstr)
72	PTR_L	v1, L_PCB(MIPS_CURLWP)
73	PTR_LA	v0, _C_LABEL(copystrerr)
74	blt	a0, zero, _C_LABEL(copystrefault)
75	 PTR_S	v0, PCB_ONFAULT(v1)
76	move	t0, a2
77	beq	a2, zero, 4f
78	 nop
791:
80	lbu	v0, 0(a0)
81	PTR_SUBU a2, a2, 1
82	beq	v0, zero, 2f
83	 sb	v0, 0(a1)			# write trailing NUL
84	PTR_ADDU a0, a0, 1
85	bne	a2, zero, 1b
86	 PTR_ADDU a1, a1, 1
874:
88	li	v0, ENAMETOOLONG
892:
90	beq	a3, zero, 3f
91	 PTR_SUBU a2, t0, a2
92	PTR_S	a2, 0(a3)
933:
94	j	ra				# v0 is 0 or ENAMETOOLONG
95	 PTR_S	zero, PCB_ONFAULT(v1)
96END(copyinstr)
97
98/*
99 * int copyoutstr(void *uaddr, void *kaddr, size_t maxlen, size_t *lencopied);
100 * Copy a NIL-terminated string, at most maxlen characters long, into the
101 * user's address space.  Return the number of characters copied (including
102 * the NIL) in *lencopied.  If the string is too long, return ENAMETOOLONG;
103 * else return 0 or EFAULT.
104 */
105LEAF(copyoutstr)
106	PTR_L	v1, L_PCB(MIPS_CURLWP)
107	PTR_LA	v0, _C_LABEL(copystrerr)
108	blt	a1, zero, _C_LABEL(copystrefault)
109	 PTR_S	v0, PCB_ONFAULT(v1)
110	move	t0, a2
111	beq	a2, zero, 4f
112	 nop
1131:
114	lbu	v0, 0(a0)
115	PTR_SUBU a2, a2, 1
116	beq	v0, zero, 2f
117	 sb	v0, 0(a1)
118	PTR_ADDU a0, a0, 1
119	bne	a2, zero, 1b
120	 PTR_ADDU a1, a1, 1
1214:
122	li	v0, ENAMETOOLONG
1232:
124	beq	a3, zero, 3f
125	 PTR_SUBU a2, t0, a2
126	PTR_S	a2, 0(a3)
1273:
128	j	ra				# v0 is 0 or ENAMETOOLONG
129	 PTR_S	zero, PCB_ONFAULT(v1)
130END(copyoutstr)
131
132LEAF(copystrerr)
133	j	ra
134	 PTR_S	zero, PCB_ONFAULT(v1)
135END(copystrerr)
136
137LEAF(copystrefault)
138	b	copystrerr
139	 li	v0, EFAULT
140END(copystrefault)
141
142/*
143 * kcopy(const void *src, void *dst, size_t len);
144 *
145 * Copy len bytes from src to dst, aborting if we encounter a fatal
146 * page fault.
147 *
148 * kcopy() _must_ save and restore the old fault handler since it is
149 * called by uiomove(), which may be in the path of servicing a non-fatal
150 * page fault.
151 */
152NESTED(kcopy, 2*CALLFRAME_SIZ, ra)
153	PTR_SUBU sp, sp, 2*CALLFRAME_SIZ	# set up stack frame
154	/* Frame contains RA (31) and S0 (16). */
155	.mask	0x80010000, -SZREG
156	REG_S	ra, CALLFRAME_SIZ+CALLFRAME_RA(sp)	# save ra
157	REG_S	s0, CALLFRAME_SIZ+CALLFRAME_S0(sp)	# save s0
158	move	v0, a0				# swap a0, a1 for call to memcpy
159	move	a0, a1
160	move	a1, v0
161	PTR_L	v1, L_PCB(MIPS_CURLWP)		# set up fault handler
162	PTR_LA	v0, _C_LABEL(kcopyerr)
163	PTR_L	s0, PCB_ONFAULT(v1)		# save old handler
164	jal	memcpy
165	 PTR_S	v0, PCB_ONFAULT(v1)
166
167	PTR_L	v1, L_PCB(MIPS_CURLWP)		# restore the old handler
168	REG_L	ra, CALLFRAME_SIZ+CALLFRAME_RA(sp)	# restore ra
169	PTR_S	s0, PCB_ONFAULT(v1)
170	REG_L	s0, CALLFRAME_SIZ+CALLFRAME_S0(sp)	# restore s0
171	PTR_ADDU sp, sp, 2*CALLFRAME_SIZ	# kill stack frame
172	j	ra
173	 move	v0, zero			# success!
174END(kcopy)
175
176LEAF(kcopyerr)
177	PTR_L	v1, L_PCB(MIPS_CURLWP)		# restore the old handler
178	REG_L	ra, CALLFRAME_SIZ+CALLFRAME_RA(sp) 	# restore ra
179	PTR_S	s0, PCB_ONFAULT(v1)
180	REG_L	s0, CALLFRAME_SIZ+CALLFRAME_S0(sp) 	# restore s0
181	j	ra
182	 PTR_ADDU sp, sp, 2*CALLFRAME_SIZ	# kill stack frame
183END(kcopyerr)
184
185/*
186 * int copyin(void *uaddr, void *kaddr, size_t len)
187 * Copies len bytes of data from the user-space address uaddr to the
188 * kernel-space address kaddr.  copyin returns 0 on success or EFAULT
189 * if a bad address is encountered.
190 */
191NESTED(copyin, CALLFRAME_SIZ, ra)
192	PTR_SUBU sp, sp, CALLFRAME_SIZ
193	.mask	0x80000000, -4
194	REG_S	ra, CALLFRAME_RA(sp)
195	blt	a0, zero, _C_LABEL(copyefault)
196	 move	v0, a0				# swap a0, a1 for call to memcpy
197	move	a0, a1
198	move	a1, v0
199	PTR_L	v1, L_PCB(MIPS_CURLWP)
200	PTR_LA	v0, _C_LABEL(copyerr)
201	jal	memcpy
202	 PTR_S	v0, PCB_ONFAULT(v1)
203
204	PTR_L	v1, L_PCB(MIPS_CURLWP)
205	REG_L	ra, CALLFRAME_RA(sp)
206	PTR_ADDU sp, sp, CALLFRAME_SIZ
207	PTR_S	zero, PCB_ONFAULT(v1)
208	j	ra
209	 move	v0, zero
210END(copyin)
211
212/*
213 * int copyout(void *kaddr, void *uaddr, size_t len)
214 * Copies len bytes of data from the kernel-space address kaddr to the
215 * user-space address uaddr.  copyout returns 0 on success or EFAULT
216 * if a bad address is encountered.
217 */
218NESTED(copyout, CALLFRAME_SIZ, ra)
219	PTR_SUBU sp, sp, CALLFRAME_SIZ
220	.mask	0x80000000, -4
221	REG_S	ra, CALLFRAME_RA(sp)
222	blt	a1, zero, _C_LABEL(copyefault)
223	 move	v0, a0				# swap a0, a1 for call to memcpy
224	move	a0, a1
225	move	a1, v0
226	PTR_L	v1, L_PCB(MIPS_CURLWP)
227	PTR_LA	v0, _C_LABEL(copyerr)
228	jal	memcpy
229	 PTR_S	v0, PCB_ONFAULT(v1)
230
231	PTR_L	v1, L_PCB(MIPS_CURLWP)
232	REG_L	ra, CALLFRAME_RA(sp)
233	PTR_ADDU sp, sp, CALLFRAME_SIZ
234	PTR_S	zero, PCB_ONFAULT(v1)
235	j	ra
236	 move	v0, zero
237END(copyout)
238
239LEAF(copyerr)
240	PTR_L	v1, L_PCB(MIPS_CURLWP)
241	REG_L	ra, CALLFRAME_RA(sp)
242	PTR_ADDU sp, sp, CALLFRAME_SIZ
243	j	ra
244	 PTR_S	zero, PCB_ONFAULT(v1)
245END(copyerr)
246
247LEAF(copyefault)
248	b	copyerr
249	 li	v0, EFAULT
250END(copyefault)
251
252LEAF(kfetch_32)
253	PTR_L	v1, L_PCB(MIPS_CURLWP)
254	PTR_LA	v0, _C_LABEL(kfetcherr)
255	bgez	a0, _C_LABEL(kfetcherr)
256	 PTR_S	v0, PCB_ONFAULT(v1)
257	INT_L	v0, 0(a0)			# fetch int
258	/*
259	 * Normally a sync instructions would be used but this has to work on
260	 * MIPS1 which doesn't have a sync.
261	 */
262	nop					# load delay for mips1
263	move	t0, v0				# dependent instruction
264	xor	t0, v0				# make t0 zero
265	j	ra
266	 PTR_S	t0, PCB_ONFAULT(v1)
267END(kfetch_32)
268
269/**************************************************************************/
270
271#define	UFETCHSTORE_PROLOGUE						 \
272	PTR_L	v1, L_PCB(MIPS_CURLWP)					;\
273	PTR_LA	v0, _C_LABEL(ufetchstore_fault)				;\
274	blt	a0, zero, _C_LABEL(ufetchstore_efault)			;\
275	 PTR_S	v0, PCB_ONFAULT(v1)
276
277	/* keep to a single insn; it's used in a branch delay slot */
278#define	UFETCHSTORE_EPILOGUE						;\
279	PTR_S	zero, PCB_ONFAULT(v1)
280
281#define	UFETCHTORE_RETURN_SUCCESS					;\
282	j	ra							;\
283	 move	v0, zero
284
285/* LINTSTUB: int _ufetch_8(const uint8_t *uaddr, uint8_t *valp); */
286LEAF(_ufetch_8)
287	UFETCHSTORE_PROLOGUE
288	lbu	v0, 0(a0)		/* v0 = *uaddr */
289	 UFETCHSTORE_EPILOGUE		/* load delay slot (MIPS1) */
290	sb	v0, 0(a1)		/* *valp = v0 */
291	UFETCHTORE_RETURN_SUCCESS
292END(_ufetch_8)
293
294/* LINTSTUB: int _ufetch_16(const uint16_t *uaddr, uint16_t *valp); */
295LEAF(_ufetch_16)
296	UFETCHSTORE_PROLOGUE
297	lhu	v0, 0(a0)		/* v0 = *uaddr */
298	 UFETCHSTORE_EPILOGUE		/* load delay slot (MIPS1) */
299	sh	v0, 0(a1)		/* *valp = v0 */
300	UFETCHTORE_RETURN_SUCCESS
301END(_ufetch_16)
302
303/* LINTSTUB: int _ufetch_32(const uint32_t *uaddr, uint32_t *valp); */
304LEAF(_ufetch_32)
305	UFETCHSTORE_PROLOGUE
306	lw	v0, 0(a0)		/* v0 = *uaddr */
307	 UFETCHSTORE_EPILOGUE		/* load delay slot (MIPS1) */
308	sw	v0, 0(a1)		/* *valp = v0 */
309	UFETCHTORE_RETURN_SUCCESS
310END(_ufetch_32)
311
312#ifdef _LP64
313/* LINTSTUB: int _ufetch_64(const uint64_t *uaddr, uint64_t *valp); */
314LEAF(_ufetch_64)
315	UFETCHSTORE_PROLOGUE
316	ld	v0, 0(a0)		/* v0 = *uaddr */
317	 UFETCHSTORE_EPILOGUE		/* load delay slot (MIPS1, LOL) */
318	sd	v0, 0(a1)		/* *valp = v0 */
319	UFETCHTORE_RETURN_SUCCESS
320END(_ufetch_64)
321#endif /* _LP64 */
322
323/* LINTSTUB: int _ustore_8(uint8_t *uaddr, uint8_t val); */
324LEAF(_ustore_8)
325	UFETCHSTORE_PROLOGUE
326	sb	a1, 0(a0)		/* *uaddr = val */
327	UFETCHSTORE_EPILOGUE
328	UFETCHTORE_RETURN_SUCCESS
329END(_ustore_8)
330
331/* LINTSTUB: int _ustore_16(uint16_t *uaddr, uint16_t val); */
332LEAF(_ustore_16)
333	UFETCHSTORE_PROLOGUE
334	sh	a1, 0(a0)		/* *uaddr = val */
335	UFETCHSTORE_EPILOGUE
336	UFETCHTORE_RETURN_SUCCESS
337END(_ustore_16)
338
339/* LINTSTUB: int _ustore_32(uint32_t *uaddr, uint32_t val); */
340LEAF(_ustore_32)
341	UFETCHSTORE_PROLOGUE
342	sw	a1, 0(a0)		/* *uaddr = val */
343	UFETCHSTORE_EPILOGUE
344	UFETCHTORE_RETURN_SUCCESS
345END(_ustore_32)
346
347#ifdef _LP64
348/* LINTSTUB: int _ustore_64(uint64_t *uaddr, uint64_t val); */
349LEAF(_ustore_64)
350	UFETCHSTORE_PROLOGUE
351	sd	a1, 0(a0)		/* *uaddr = val */
352	UFETCHSTORE_EPILOGUE
353	UFETCHTORE_RETURN_SUCCESS
354END(_ustore_64)
355#endif /* _LP64 */
356
357LEAF(ufetchstore_efault)
358	li	v0, EFAULT
359XLEAF(ufetchstore_fault)
360	j	ra
361	 UFETCHSTORE_EPILOGUE
362END(ufetchstore_efault)
363
364/**************************************************************************/
365
366/*
367 * uint32_t mips_ufetch32(const void *)
368 * Fetches a 32-bit datum from the user-space address and
369 * returns it in v0.
370 *
371 * We have this in addition to the MI fetch(9) API for the convenience
372 * of bds_emul.S and fp.S.
373 */
374LEAF(mips_ufetch32)
375	PTR_L	v1, L_PCB(MIPS_CURLWP)
376	PTR_LA	v0, _C_LABEL(mips_ufetch32_fault)
377	blt	a0, zero, _C_LABEL(mips_ufetch32_fault)
378	 PTR_S	v0, PCB_ONFAULT(v1)
379	INT_L	v0, 0(a0)			# fetch int
380	j	ra
381	 PTR_S	zero, PCB_ONFAULT(v1)
382END(mips_ufetch32)
383
384/*
385 * int mips_ustore32_isync(void *, uint32_t)
386 * Have to flush instruction cache afterwards.
387 */
388LEAF(mips_ustore32_isync)
389	PTR_L	v1, L_PCB(MIPS_CURLWP)
390	PTR_LA	v0, _C_LABEL(mips_ufetch32_fault)
391	blt	a0, zero, _C_LABEL(mips_ufetch32_fault)
392	 PTR_S	v0, PCB_ONFAULT(v1)
393	INT_S	a1, 0(a0)			# store word
394	PTR_S	zero, PCB_ONFAULT(v1)
395	PTR_L	t9, _C_LABEL(mips_cache_ops) + MIPSX_FLUSHICACHE
396	move	v0, zero
397	j	t9				# NOTE: must not clobber v0!
398	 li	a1, 4				# size of word
399END(mips_ustore32_isync)
400
401LEAF(mips_ufetch32_fault)
402	li	v0, -1
403	j	ra
404	 PTR_S	zero, PCB_ONFAULT(v1)
405END(mips_ufetch32_fault)
406
407/**************************************************************************/
408
409/*
410 * int badaddr(void addr, int len)
411 * See if access to addr with a len type instruction causes a machine check.
412 * len is length of access (1=byte, 2=short, 4=long)
413 */
414LEAF(badaddr)
415	PTR_L	v1, L_PCB(MIPS_CURLWP)
416	PTR_LA	v0, _C_LABEL(baderr)
417	bne	a1, 1, 2f
418	 PTR_S	v0, PCB_ONFAULT(v1)
419	b	5f
420	 lbu	v0, (a0)
4212:
422	bne	a1, 2, 4f
423	 nop
424	b	5f
425	 lhu	v0, (a0)
4264:
427	INT_L	v0, (a0)
4285:
429	/*
430	 * Normally a sync instructions would be used but this has to work on
431	 * MIPS1 which doesn't have a sync.
432	 */
433	nop
434	move	t0, v0			# dependent instruction
435	xor	t0, t0			# zero t0
436	PTR_S	t0, PCB_ONFAULT(v1)	# clear onfault
437	j	ra
438	 move	v0, zero		# made it w/o errors
439END(badaddr)
440
441LEAF(kfetcherr)
442	PTR_S	zero, PCB_ONFAULT(v1)
443	j	ra
444	 move	v0, a1
445END(kfetcherr)
446
447LEAF(fswberr)
448XLEAF(baderr)
449	PTR_S	zero, PCB_ONFAULT(v1)
450	j	ra
451	 li	v0, -1
452END(fswberr)
453