xref: /netbsd-src/sys/arch/m68k/m68k/copy.s (revision 5ae0a955493425be9125d9bd3239f6810ff39e12)
1/*	$NetBSD: copy.s,v 1.50 2023/09/26 14:33:55 tsutsui Exp $	*/
2
3/*-
4 * Copyright (c) 1998, 2019 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Charles M. Hannum and by Jason R. Thorpe.
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/*-
33 * Copyright (c) 1990 The Regents of the University of California.
34 * All rights reserved.
35 *
36 * This code is derived from software contributed to Berkeley by
37 * the Systems Programming Group of the University of Utah Computer
38 * Science Department.
39 *
40 * Redistribution and use in source and binary forms, with or without
41 * modification, are permitted provided that the following conditions
42 * are met:
43 * 1. Redistributions of source code must retain the above copyright
44 *    notice, this list of conditions and the following disclaimer.
45 * 2. Redistributions in binary form must reproduce the above copyright
46 *    notice, this list of conditions and the following disclaimer in the
47 *    documentation and/or other materials provided with the distribution.
48 * 3. Neither the name of the University nor the names of its contributors
49 *    may be used to endorse or promote products derived from this software
50 *    without specific prior written permission.
51 *
52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62 * SUCH DAMAGE.
63 */
64
65/*
66 * This file contains the functions for user-space access:
67 * copyin/copyout, ufetch/ustore, etc.
68 */
69
70#include <sys/errno.h>
71#include <machine/asm.h>
72
73#include "assym.h"
74
75	.file	"copy.s"
76	.text
77
78#ifdef CI_CURPCB
79#define	GETCURPCB(r)	movl	_C_LABEL(cpu_info_store)+CI_CURPCB,r
80#else
81#define	GETCURPCB(r)	movl	_C_LABEL(curpcb),r
82#endif
83
84#ifdef	DIAGNOSTIC
85/*
86 * The following routines all use the "moves" instruction to access
87 * memory with "user" privilege while running in supervisor mode.
88 * The "function code" registers actually determine what type of
89 * access "moves" does, and the kernel arranges to leave them set
90 * for "user data" access when these functions are called.
91 *
92 * The diagnostics:  CHECK_SFC,  CHECK_DFC
93 * will verify that the sfc/dfc register values are correct.
94 */
95.Lbadfc:
96	PANIC("copy.s: bad sfc or dfc")
97	jra	.Lbadfc
98#define	CHECK_SFC	movec %sfc,%d0; subql #FC_USERD,%d0; bne .Lbadfc
99#define	CHECK_DFC	movec %dfc,%d0; subql #FC_USERD,%d0; bne .Lbadfc
100#else	/* DIAGNOSTIC */
101#define	CHECK_SFC
102#define	CHECK_DFC
103#endif	/* DIAGNOSTIC */
104
105/*
106 * copyin(void *from, void *to, size_t len);
107 * Copy len bytes from the user's address space.
108 *
109 * This is probably not the best we can do, but it is still 2-10 times
110 * faster than the C version in the portable gen directory.
111 *
112 * Things that might help:
113 *	- unroll the longword copy loop (might not be good for a 68020)
114 *	- longword align when possible (only on the 68020)
115 */
116ENTRY(copyin)
117	CHECK_SFC
118	movl	12(%sp),%d0		| check count
119	jeq	.Lciret			| == 0, don't do anything
120	movl	%d2,-(%sp)		| save scratch register
121	GETCURPCB(%a0)			| set fault handler
122	movl	#.Lcifault,PCB_ONFAULT(%a0)
123	movl	8(%sp),%a0		| src address
124	movl	12(%sp),%a1		| dest address
125	movl	%a0,%d1
126	btst	#0,%d1			| src address odd?
127	jeq	.Lcieven		| no, skip alignment
128	movsb	(%a0)+,%d2		| yes, copy a byte
129	movb	%d2,(%a1)+
130	subql	#1,%d0			| adjust count
131	jeq	.Lcidone		| count 0, all done
132.Lcieven:
133	movl	%a1,%d1
134	btst	#0,%d1			| dest address odd?
135	jne	.Lcibytes		| yes, must copy bytes
136	movl	%d0,%d1			| OK, both even.  Get count
137	lsrl	#2,%d1			|   and convert to longwords
138	jeq	.Lcibytes		| count 0, skip longword loop
139	subql	#1,%d1			| predecrement for dbf
140.Lcilloop:
141	movsl	(%a0)+,%d2		| copy a longword
142	movl	%d2,(%a1)+
143	dbf	%d1,.Lcilloop		| decrement low word of count
144	subil	#0x10000,%d1		| decrement high word of count
145	jcc	.Lcilloop
146	andl	#3,%d0			| what remains
147	jeq	.Lcidone		| nothing, all done
148.Lcibytes:
149	subql	#1,%d0			| predecrement for dbf
150.Lcibloop:
151	movsb	(%a0)+,%d2		| copy a byte
152	movb	%d2,(%a1)+
153	dbf	%d0,.Lcibloop		| decrement low word of count
154	subil	#0x10000,%d0		| decrement high word of count
155	jcc	.Lcibloop
156	clrl	%d0			| no error
157.Lcidone:
158	GETCURPCB(%a0)			| clear fault handler
159	clrl	PCB_ONFAULT(%a0)
160	movl	(%sp)+,%d2		| restore scratch register
161.Lciret:
162	rts
163.Lcifault:
164	jra	.Lcidone
165
166/*
167 * copyout(void *from, void *to, size_t len);
168 * Copy len bytes into the user's address space.
169 *
170 * This is probably not the best we can do, but it is still 2-10 times
171 * faster than the C version in the portable gen directory.
172 *
173 * Things that might help:
174 *	- unroll the longword copy loop (might not be good for a 68020)
175 *	- longword align when possible (only on the 68020)
176 */
177ENTRY(copyout)
178	CHECK_DFC
179	movl	12(%sp),%d0		| check count
180	jeq	.Lcoret			| == 0, don't do anything
181	movl	%d2,-(%sp)		| save scratch register
182	GETCURPCB(%a0)			| set fault handler
183	movl	#.Lcofault,PCB_ONFAULT(%a0)
184	movl	8(%sp),%a0		| src address
185	movl	12(%sp),%a1		| dest address
186	movl	%a0,%d1
187	btst	#0,%d1			| src address odd?
188	jeq	.Lcoeven		| no, skip alignment
189	movb	(%a0)+,%d2		| yes, copy a byte
190	movsb	%d2,(%a1)+
191	subql	#1,%d0			| adjust count
192	jeq	.Lcodone		| count 0, all done
193.Lcoeven:
194	movl	%a1,%d1
195	btst	#0,%d1			| dest address odd?
196	jne	.Lcobytes		| yes, must copy bytes
197	movl	%d0,%d1			| OK, both even.  Get count
198	lsrl	#2,%d1			|   and convert to longwords
199	jeq	.Lcobytes		| count 0, skip longword loop
200	subql	#1,%d1			| predecrement for dbf
201.Lcolloop:
202	movl	(%a0)+,%d2		| copy a longword
203	movsl	%d2,(%a1)+
204	dbf	%d1,.Lcolloop		| decrement low word of count
205	subil	#0x10000,%d1		| decrement high word of count
206	jcc	.Lcolloop
207	andl	#3,%d0			| what remains
208	jeq	.Lcodone		| nothing, all done
209.Lcobytes:
210	subql	#1,%d0			| predecrement for dbf
211.Lcobloop:
212	movb	(%a0)+,%d2		| copy a byte
213	movsb	%d2,(%a1)+
214	dbf	%d0,.Lcobloop		| decrement low word of count
215	subil	#0x10000,%d0		| decrement high word of count
216	jcc	.Lcobloop
217	clrl	%d0			| no error
218.Lcodone:
219	GETCURPCB(%a0)			| clear fault handler
220	clrl	PCB_ONFAULT(%a0)
221	movl	(%sp)+,%d2		| restore scratch register
222.Lcoret:
223	rts
224.Lcofault:
225	jra	.Lcodone
226
227/*
228 * copyinstr(void *from, void *to, size_t maxlen, size_t *lencopied);
229 * Copy a NUL-terminated string, at most maxlen characters long, from the
230 * user's address space.  Return the number of characters copied (including
231 * the NUL) in *lencopied.  If the string is too long, return ENAMETOOLONG;
232 * else return 0 or EFAULT.
233 */
234ENTRY(copyinstr)
235	CHECK_SFC
236	GETCURPCB(%a0)			| set fault handler
237	movl	#.Lcisfault,PCB_ONFAULT(%a0)
238	movl	4(%sp),%a0		| a0 = fromaddr
239	movl	8(%sp),%a1		| a1 = toaddr
240	clrl	%d0
241	movl	12(%sp),%d1		| count
242	jeq	.Lcistoolong		| nothing to copy
243	subql	#1,%d1			| predecrement for dbeq
244.Lcisloop:
245	movsb	(%a0)+,%d0		| copy a byte
246	movb	%d0,(%a1)+
247	dbeq	%d1,.Lcisloop		| decrement low word of count
248	jeq	.Lcisdone		| copied null, exit
249	subil	#0x10000,%d1		| decrement high word of count
250	jcc	.Lcisloop		| more room, keep going
251.Lcistoolong:
252	moveq	#ENAMETOOLONG,%d0	| ran out of space
253.Lcisdone:
254	tstl	16(%sp)		| length desired?
255	jeq	.Lcisexit
256	subl	4(%sp),%a0		| yes, calculate length copied
257	movl	16(%sp),%a1		| store at return location
258	movl	%a0,(%a1)
259.Lcisexit:
260	GETCURPCB(%a0)			| clear fault handler
261	clrl	PCB_ONFAULT(%a0)
262	rts
263.Lcisfault:
264	jra	.Lcisdone
265
266/*
267 * copyoutstr(void *from, void *to, size_t maxlen, size_t *lencopied);
268 * Copy a NUL-terminated string, at most maxlen characters long, into the
269 * user's address space.  Return the number of characters copied (including
270 * the NUL) in *lencopied.  If the string is too long, return ENAMETOOLONG;
271 * else return 0 or EFAULT.
272 */
273ENTRY(copyoutstr)
274	CHECK_DFC
275	GETCURPCB(%a0)			| set fault handler
276	movl	#.Lcosfault,PCB_ONFAULT(%a0)
277	movl	4(%sp),%a0		| a0 = fromaddr
278	movl	8(%sp),%a1		| a1 = toaddr
279	clrl	%d0
280	movl	12(%sp),%d1		| count
281	jeq	.Lcostoolong		| nothing to copy
282	subql	#1,%d1			| predecrement for dbeq
283.Lcosloop:
284	movb	(%a0)+,%d0		| copy a byte
285	movsb	%d0,(%a1)+
286	dbeq	%d1,.Lcosloop		| decrement low word of count
287	jeq	.Lcosdone		| copied null, exit
288	subil	#0x10000,%d1		| decrement high word of count
289	jcc	.Lcosloop		| more room, keep going
290.Lcostoolong:
291	moveq	#ENAMETOOLONG,%d0	| ran out of space
292.Lcosdone:
293	tstl	16(%sp)		| length desired?
294	jeq	.Lcosexit
295	subl	4(%sp),%a0		| yes, calculate length copied
296	movl	16(%sp),%a1		| store at return location
297	movl	%a0,(%a1)
298.Lcosexit:
299	GETCURPCB(%a0)			| clear fault handler
300	clrl	PCB_ONFAULT(%a0)
301	rts
302.Lcosfault:
303	jra	.Lcosdone
304
305/*
306 * kcopy(const void *src, void *dst, size_t len);
307 *
308 * Copy len bytes from src to dst, aborting if we encounter a fatal
309 * page fault.
310 *
311 * kcopy() _must_ save and restore the old fault handler since it is
312 * called by uiomove(), which may be in the path of servicing a non-fatal
313 * page fault.
314 */
315ENTRY(kcopy)
316	link	%a6,#-4
317	GETCURPCB(%a0)			| set fault handler
318	movl	PCB_ONFAULT(%a0),-4(%a6) | save old handler first
319	movl	#.Lkcfault,PCB_ONFAULT(%a0)
320	movl	16(%a6),-(%sp)		| push len
321	movl	8(%a6),-(%sp)		| push src
322	movl	12(%a6),-(%sp)		| push dst
323	jbsr	_C_LABEL(memcpy)	| copy it
324	addl	#12,%sp			| pop args
325	clrl	%d0			| success!
326.Lkcdone:
327	GETCURPCB(%a0)			| restore fault handler
328	movl	-4(%a6),PCB_ONFAULT(%a0)
329	unlk	%a6
330	rts
331.Lkcfault:
332	addl	#16,%sp			| pop args and return address
333	jra	.Lkcdone
334
335#define	UFETCH_PROLOGUE							\
336	CHECK_SFC						;	\
337	movl	4(%sp),%a0		/* address to read */	;	\
338	GETCURPCB(%a1)			/* a1 = curpcb */	;	\
339	movl	#.Lufetchstore_fault,PCB_ONFAULT(%a1)
340
341/* LINTSTUB: _ufetch_8(const uint8_t *uaddr, uint8_t *valp); */
342ENTRY(_ufetch_8)
343	UFETCH_PROLOGUE
344	movsb	(%a0),%d0		| do read from user space
345	movl	8(%sp),%a0		| destination address
346	movb	%d0,(%a0)
347	jra	.Lufetchstore_success
348
349/* LINTSTUB: _ufetch_16(const uint16_t *uaddr, uint16_t *valp); */
350ENTRY(_ufetch_16)
351	UFETCH_PROLOGUE
352	movsw	(%a0),%d0		| do read from user space
353	movl	8(%sp),%a0		| destination address
354	movw	%d0,(%a0)
355	jra	.Lufetchstore_success
356
357/* LINTSTUB: _ufetch_32(const uint32_t *uaddr, uint32_t *valp); */
358ENTRY(_ufetch_32)
359	UFETCH_PROLOGUE
360	movsl	(%a0),%d0		| do read from user space
361	movl	8(%sp),%a0		| destination address
362	movl	%d0,(%a0)
363	jra	.Lufetchstore_success
364
365#define	USTORE_PROLOGUE							\
366	CHECK_DFC						;	\
367	movl	4(%sp),%a0		/* address to write */	;	\
368	GETCURPCB(%a1)			/* a1 = curpcb */	;	\
369	movl	#.Lufetchstore_fault,PCB_ONFAULT(%a1)
370
371/* LINTSTUB: _ustore_8(uint8_t *uaddr, uint8_t val); */
372ENTRY(_ustore_8)
373	USTORE_PROLOGUE
374	movb	11(%sp),%d0		| value to store
375	movsb	%d0,(%a0)		| do write to user space
376	jra	.Lufetchstore_success
377
378/* LINTSTUB: _ustore_16(uint16_t *uaddr, uint16_t val); */
379ENTRY(_ustore_16)
380	USTORE_PROLOGUE
381	movw	10(%sp),%d0		| value to store
382	movsw	%d0,(%a0)		| do write to user space
383	jra	.Lufetchstore_success
384
385/* LINTSTUB: _ustore_32(uint32_t *uaddr, uint32_t val); */
386ENTRY(_ustore_32)
387	USTORE_PROLOGUE
388	movl	8(%sp),%d0		| value to store
389	movsl	%d0,(%a0)		| do write to user space
390	jra	.Lufetchstore_success
391
392.Lufetchstore_success:
393	clrl	%d0			| return 0
394.Lufetchstore_fault:
395	clrl	PCB_ONFAULT(%a1)	| clear fault handler
396	rts
397