/*	$NetBSD: copy.S,v 1.25.8.1 2018/02/04 12:40:31 martin Exp $	*/

/*
 * Copyright (c) 1998, 2000, 2004, 2008 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Charles M. Hannum.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * Copyright (c) 1990 The Regents of the University of California.
 * All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * William Jolitz.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)locore.s	7.3 (Berkeley) 5/13/91
 */

#include <machine/asm.h>
__KERNEL_RCSID(0, "$NetBSD: copy.S,v 1.25.8.1 2018/02/04 12:40:31 martin Exp $");

#include "assym.h"

#include <sys/errno.h>

#include <machine/frameasm.h>
#include <machine/cputypes.h>

#define GET_CURPCB(reg)	\
	movl	CPUVAR(CURLWP),reg; \
	movl	L_PCB(reg),reg

/*
 * These are arranged so that the abnormal case is a forwards
 * conditional branch - which will be predicted not-taken by
 * both Intel and AMD processors.
 */
#define DEFERRED_SWITCH_CHECK \
	CHECK_DEFERRED_SWITCH			; \
	jnz	99f				; \
98:

#define DEFERRED_SWITCH_CALL \
99:						; \
	call	_C_LABEL(do_pmap_load)		; \
	jmp	98b

/*
 * The following primitives are to copy regions of memory.
 * Label must be before all copy functions.
 */
	.text
LABEL(x86_copyfunc_start)

/*
 * Handle deferred pmap switch.  We must re-enable preemption without
 * making a function call, so that the program counter is visible to
 * cpu_kpreempt_exit().  It can then know if it needs to restore the
 * pmap on returning, because a preemption occurred within one of the
 * copy functions.
 */
NENTRY(do_pmap_load)
	pushl	%ebp
	movl	%esp,%ebp
	pushl	%ebx
	movl	CPUVAR(CURLWP),%ebx
1:
	incl	L_NOPREEMPT(%ebx)
	call	_C_LABEL(pmap_load)
	decl	L_NOPREEMPT(%ebx)
	jnz	2f
	cmpl	$0,L_DOPREEMPT(%ebx)
	jz	2f
	pushl	$0
	call	_C_LABEL(kpreempt)
	addl	$4,%esp
2:
	cmpl	$0,CPUVAR(WANT_PMAPLOAD)
	jnz	1b
	popl	%ebx
	leave
	ret
END(do_pmap_load)

/*
 * void *return_address(unsigned int level);
 *
 * The return address if level == 0, the return address of the caller
 * `level' levels down the stack if level > 0.
 */
ENTRY(return_address)
	movl	%ebp,%eax	/* frame pointer -> %eax */
	movl	4(%esp),%ecx	/* level -> %ecx */
	movl	CPUVAR(CURLWP),%edx
	movl	L_PCB(%edx),%edx
	movl	$_C_LABEL(return_address_fault),PCB_ONFAULT(%edx)
	cmpl	$0,%ecx
	je	2f
1:
	movl	(%eax),%eax	/* next frame pointer */
	decl	%ecx
	jnz	1b
2:
	movl	0x4(%eax),%eax
	movl	$0,PCB_ONFAULT(%edx)
	ret
END(return_address)

/*
 * int kcopy(const void *from, void *to, size_t len);
 * Copy len bytes from and to kernel memory, and abort on fault.
 */
ENTRY(kcopy)
	pushl	%esi
	pushl	%edi
	movl	12(%esp),%esi
	movl	16(%esp),%edi
	movl	20(%esp),%ecx
.Lkcopy_start:
	movl	%edi,%eax
	subl	%esi,%eax
	cmpl	%ecx,%eax		/* overlapping? */
	movl	%ecx,%edx
	jb	1f
	/* nope, copy forward */
	shrl	$2,%ecx			/* copy by 32-bit words */
	rep
	movsl
	movl	%edx,%ecx
	andl	$3,%ecx			/* any bytes left? */
	jz	0f
	rep
	movsb
0:
	popl	%edi
	popl	%esi
	xorl	%eax,%eax
	ret

	ALIGN_TEXT
1:	addl	%ecx,%edi		/* copy backward */
	addl	%ecx,%esi
	std
	andl	$3,%ecx			/* any fractional bytes? */
	decl	%edi
	decl	%esi
	rep
	movsb
	movl	%edx,%ecx		/* copy remainder by 32-bit words */
	shrl	$2,%ecx
	subl	$3,%esi
	subl	$3,%edi
	rep
	movsl
	cld

.Lkcopy_end:
	popl	%edi
	popl	%esi
	xorl	%eax,%eax
	ret
END(kcopy)

/*****************************************************************************/

/*
 * The following primitives are used to copy data in and out of the user's
 * address space.
 */

/*
 * int copyout(const void *from, void *to, size_t len);
 * Copy len bytes into the user's address space.
 * see copyout(9)
 */
ENTRY(copyout)
	DEFERRED_SWITCH_CHECK
	pushl	%esi
	pushl	%edi
	movl	12(%esp),%esi	/* from */
	movl	16(%esp),%edi	/* to */
	movl	20(%esp),%eax	/* len */

	movl	%edi,%edx
	addl	%eax,%edx
	jc	_C_LABEL(copy_efault)
	cmpl	$VM_MAXUSER_ADDRESS,%edx
	ja	_C_LABEL(copy_efault)

.Lcopyout_start:
	movl	%eax,%ecx
	shrl	$2,%ecx
	rep
	movsl
	andl	$3,%eax
	jz	.Lcopyout_end
	movl	%eax,%ecx
	rep
	movsb
.Lcopyout_end:

	popl	%edi
	popl	%esi
	xorl	%eax,%eax
	ret
	DEFERRED_SWITCH_CALL
END(copyout)

/*
 * int copyin(const void *from, void *to, size_t len);
 * Copy len bytes from the user's address space.
 * see copyin(9)
 */
ENTRY(copyin)
	DEFERRED_SWITCH_CHECK
	pushl	%esi
	pushl	%edi
	movl	12(%esp),%esi	/* from */
	movl	16(%esp),%edi	/* to */
	movl	20(%esp),%eax	/* len */

	movl	%esi,%edx
	addl	%eax,%edx
	jc	_C_LABEL(copy_efault)
	cmpl	$VM_MAXUSER_ADDRESS,%edx
	ja	_C_LABEL(copy_efault)

.Lcopyin_start:
	movl	%eax,%ecx
	shrl	$2,%ecx
	rep
	movsl
	andl	$3,%eax
	jz	.Lcopyin_end
	movl	%eax,%ecx
	rep
	movsb
.Lcopyin_end:

	popl	%edi
	popl	%esi
	xorl	%eax,%eax
	ret
	DEFERRED_SWITCH_CALL
END(copyin)

NENTRY(copy_efault)
	movl	$EFAULT,%eax
	popl	%edi
	popl	%esi
	ret
END(copy_efault)

/*
 * kcopy_fault is used by kcopy and copy_fault is used by copyin/out.
 *
 * they're distinguished for lazy pmap switching.  see trap().
 */

NENTRY(kcopy_fault)
	popl	%edi
	popl	%esi
	ret
END(kcopy_fault)

NENTRY(copy_fault)
	popl	%edi
	popl	%esi
	ret
END(copy_fault)

NENTRY(return_address_fault)
	movl	$0,PCB_ONFAULT(%edx)
	movl	$0,%eax
	ret
END(return_address_fault)

/*
 * int copyoutstr(const void *from, void *to, size_t maxlen, size_t *lencopied);
 * Copy a NUL-terminated string, at most maxlen characters long, into the
 * user's address space.  Return the number of characters copied (including the
 * NUL) in *lencopied.  If the string is too long, return ENAMETOOLONG; else
 * return 0 or EFAULT.
 * see copyoutstr(9)
 */
ENTRY(copyoutstr)
	DEFERRED_SWITCH_CHECK
	pushl	%esi
	pushl	%edi
	movl	12(%esp),%esi	/* esi = from */
	movl	16(%esp),%edi	/* edi = to */
	movl	20(%esp),%edx	/* edx = maxlen */

	/*
	 * Get min(%edx, VM_MAXUSER_ADDRESS-%edi).
	 */
	movl	$VM_MAXUSER_ADDRESS,%eax
	subl	%edi,%eax
	jc	_C_LABEL(copystr_efault)
	cmpl	%edx,%eax
	jae	1f
	movl	%eax,%edx
	movl	%eax,20(%esp)
1:	incl	%edx

.Lcopyoutstr_start:
1:	decl	%edx
	jz	2f
	lodsb
	stosb
	testb	%al,%al
	jnz	1b
.Lcopyoutstr_end:

	/* Success -- 0 byte reached. */
	decl	%edx
	xorl	%eax,%eax
	jmp	copystr_return

2:	/* edx is zero -- return EFAULT or ENAMETOOLONG. */
	cmpl	$VM_MAXUSER_ADDRESS,%edi
	jae	_C_LABEL(copystr_efault)
	movl	$ENAMETOOLONG,%eax
	jmp	copystr_return
	DEFERRED_SWITCH_CALL
END(copyoutstr)

/*
 * int copyinstr(const void *from, void *to, size_t maxlen, size_t *lencopied);
 * Copy a NUL-terminated string, at most maxlen characters long, from the
 * user's address space.  Return the number of characters copied (including the
 * NUL) in *lencopied.  If the string is too long, return ENAMETOOLONG; else
 * return 0 or EFAULT.
 * see copyinstr(9)
 */
ENTRY(copyinstr)
	DEFERRED_SWITCH_CHECK
	pushl	%esi
	pushl	%edi
	movl	12(%esp),%esi		/* %esi = from */
	movl	16(%esp),%edi		/* %edi = to */
	movl	20(%esp),%edx		/* %edx = maxlen */

	/*
	 * Get min(%edx, VM_MAXUSER_ADDRESS-%esi).
	 */
	movl	$VM_MAXUSER_ADDRESS,%eax
	subl	%esi,%eax
	jc	_C_LABEL(copystr_efault)
	cmpl	%edx,%eax
	jae	1f
	movl	%eax,%edx
	movl	%eax,20(%esp)
1:	incl	%edx

.Lcopyinstr_start:
1:	decl	%edx
	jz	2f
	lodsb
	stosb
	testb	%al,%al
	jnz	1b
.Lcopyinstr_end:

	/* Success -- 0 byte reached. */
	decl	%edx
	xorl	%eax,%eax
	jmp	copystr_return

2:	/* edx is zero -- return EFAULT or ENAMETOOLONG. */
	cmpl	$VM_MAXUSER_ADDRESS,%esi
	jae	_C_LABEL(copystr_efault)
	movl	$ENAMETOOLONG,%eax
	jmp	copystr_return
	DEFERRED_SWITCH_CALL
END(copyinstr)

NENTRY(copystr_efault)
	movl	$EFAULT,%eax
	jmp	copystr_return
END(copystr_efault)

NENTRY(copystr_fault)
copystr_return:
	/* Set *lencopied and return %eax. */
	movl	20(%esp),%ecx
	subl	%edx,%ecx
	movl	24(%esp),%edx
	testl	%edx,%edx
	jz	8f
	movl	%ecx,(%edx)

8:	popl	%edi
	popl	%esi
	ret
END(copystr_fault)

/*
 * int copystr(const void *from, void *to, size_t maxlen, size_t *lencopied);
 * Copy a NUL-terminated string, at most maxlen characters long.  Return the
 * number of characters copied (including the NUL) in *lencopied.  If the
 * string is too long, return ENAMETOOLONG; else return 0.
 * see copystr(9)
 */
ENTRY(copystr)
	pushl	%esi
	pushl	%edi

	movl	12(%esp),%esi		/* esi = from */
	movl	16(%esp),%edi		/* edi = to */
	movl	20(%esp),%edx		/* edx = maxlen */
	incl	%edx

1:	decl	%edx
	jz	4f
	lodsb
	stosb
	testb	%al,%al
	jnz	1b

	/* Success -- 0 byte reached. */
	decl	%edx
	xorl	%eax,%eax
	jmp	6f

4:	/* edx is zero -- return ENAMETOOLONG. */
	movl	$ENAMETOOLONG,%eax

6:	/* Set *lencopied and return %eax. */
	movl	20(%esp),%ecx
	subl	%edx,%ecx
	movl	24(%esp),%edx
	testl	%edx,%edx
	jz	7f
	movl	%ecx,(%edx)

7:	popl	%edi
	popl	%esi
	ret
END(copystr)

/*
 * long fuword(const void *uaddr);
 * Fetch an int from the user's address space.
 * see fuword(9)
 */
ENTRY(fuword)
	DEFERRED_SWITCH_CHECK
	movl	4(%esp),%edx
	cmpl	$VM_MAXUSER_ADDRESS-4,%edx
	ja	_C_LABEL(fusuaddrfault)
	GET_CURPCB(%ecx)
	movl	$_C_LABEL(fusufault),PCB_ONFAULT(%ecx)

	movl	(%edx),%eax

	movl	$0,PCB_ONFAULT(%ecx)
	ret
	DEFERRED_SWITCH_CALL
END(fuword)

/*
 * int fuswintr(const void *uaddr);
 * Fetch a short from the user's address space.  Can be called during an
 * interrupt.
 * see fuswintr(9)
 */
ENTRY(fuswintr)
	cmpl	$TLBSTATE_VALID,CPUVAR(TLBSTATE)
	jnz	_C_LABEL(fusuaddrfault)
	movl	4(%esp),%edx
	cmpl	$VM_MAXUSER_ADDRESS-2,%edx
	ja	_C_LABEL(fusuaddrfault)
	movl	CPUVAR(CURLWP),%ecx
	movl	L_PCB(%ecx),%ecx
	movl	$_C_LABEL(fusubail),PCB_ONFAULT(%ecx)

	movzwl	(%edx),%eax

	movl	$0,PCB_ONFAULT(%ecx)
	ret
END(fuswintr)

/*
 * int fubyte(const void *uaddr);
 * Fetch a byte from the user's address space.
 * see fubyte(9)
 */
ENTRY(fubyte)
	DEFERRED_SWITCH_CHECK
	movl	4(%esp),%edx
	cmpl	$VM_MAXUSER_ADDRESS-1,%edx
	ja	_C_LABEL(fusuaddrfault)
	GET_CURPCB(%ecx)
	movl	$_C_LABEL(fusufault),PCB_ONFAULT(%ecx)

	movzbl	(%edx),%eax

	movl	$0,PCB_ONFAULT(%ecx)
	ret
	DEFERRED_SWITCH_CALL
END(fubyte)

/*
 * Handle faults from [fs]u*().  Clean up and return -1.
 */
NENTRY(fusufault)
	movl	$0,PCB_ONFAULT(%ecx)
	movl	$-1,%eax
	ret
END(fusufault)

/*
 * Handle faults from [fs]u*().  Clean up and return -1.  This differs from
 * fusufault() in that trap() will recognize it and return immediately rather
 * than trying to page fault.
 */
NENTRY(fusubail)
	movl	$0,PCB_ONFAULT(%ecx)
	movl	$-1,%eax
	ret
END(fusubail)

/*
 * Handle earlier faults from [fs]u*(), due to our of range addresses.
 */
NENTRY(fusuaddrfault)
	movl	$-1,%eax
	ret
END(fusuaddrfault)

/*
 * int suswintr(void *uaddr, short x);
 * Store a short in the user's address space.  Can be called during an
 * interrupt.
 * see suswintr(9)
 */
ENTRY(suswintr)
	cmpl	$TLBSTATE_VALID,CPUVAR(TLBSTATE)
	jnz	_C_LABEL(fusuaddrfault)
	movl	4(%esp),%edx
	cmpl	$VM_MAXUSER_ADDRESS-2,%edx
	ja	_C_LABEL(fusuaddrfault)
	movl	CPUVAR(CURLWP),%ecx
	movl	L_PCB(%ecx),%ecx
	movl	$_C_LABEL(fusubail),PCB_ONFAULT(%ecx)
	movl	8(%esp),%eax

	movw	%ax,(%edx)

	xorl	%eax,%eax
	movl	%eax,PCB_ONFAULT(%ecx)
	ret
END(suswintr)

/*
 * int subyte(void *uaddr, char x);
 * Store a byte in the user's address space.
 * see subyte(9)
 */
ENTRY(subyte)
	DEFERRED_SWITCH_CHECK
	movl	4(%esp),%edx
	cmpl	$VM_MAXUSER_ADDRESS-1,%edx
	ja	_C_LABEL(fusuaddrfault)
	GET_CURPCB(%ecx)
	movl	$_C_LABEL(fusufault),PCB_ONFAULT(%ecx)
	movb	8(%esp),%al

	movb	%al,(%edx)

	xorl	%eax,%eax
	movl	%eax,PCB_ONFAULT(%ecx)
	ret
	DEFERRED_SWITCH_CALL
END(subyte)

/*
 * Compare-and-swap the 32-bit integer in the user-space.
 *
 * int	ucas_32(volatile int32_t *uptr, int32_t old, int32_t new, int32_t *ret);
 */
ENTRY(ucas_32)
	DEFERRED_SWITCH_CHECK
	movl	4(%esp),%edx
	movl	8(%esp),%eax
	movl	12(%esp),%ecx
	/* Fail if kernel-space */
	cmpl	$VM_MAXUSER_ADDRESS-4,%edx
	ja	_C_LABEL(ucas_efault)

.Lucas32_start:
	/* Perform the CAS */
	lock
	cmpxchgl %ecx,(%edx)
.Lucas32_end:

	/*
	 * Note: %eax is "old" value.
	 * Set the return values.
	 */
	movl	16(%esp),%edx
	movl	%eax,(%edx)
	xorl	%eax,%eax
	ret
	DEFERRED_SWITCH_CALL
END(ucas_32)

NENTRY(ucas_efault)
	movl	$EFAULT,%eax
	ret
END(ucas_efault)

NENTRY(ucas_fault)
	ret
END(ucas_fault)

/*
 * int	ucas_int(volatile int *uptr, int old, int new, int *ret);
 * int	ucas_ptr(volatile void **uptr, void *old, void *new, void **ret);
 */
STRONG_ALIAS(ucas_ptr, ucas_32)
STRONG_ALIAS(ucas_int, ucas_32)

/*
 * copyin() optimised for bringing in syscall arguments.
 */
ENTRY(x86_copyargs)
	DEFERRED_SWITCH_CHECK
	pushl	%esi
	movl	8(%esp),%esi
	movl	12(%esp),%edx
	movl	16(%esp),%ecx

	/*
	 * In this function, we may copy more than the size given in the third
	 * argument. In order to make sure the real end of the destination
	 * buffer is not past the end of the user's address space, we don't
	 * check the third argument but rather the largest possible size, which
	 * is:
	 * 	(2 + SYS_MAXSYSARGS) * 4 = 10 * 4
	 */
	movl	%esi,%eax
	addl	$(10 * 4),%eax
	jc	_C_LABEL(x86_copyargs_efault)
	cmpl	$VM_MAXUSER_ADDRESS,%eax
	ja	_C_LABEL(x86_copyargs_efault)

.Lx86_copyargs_start:
	/* There are a maximum of 8 args + 2 for syscall indirect */
	cmp	$16,%ecx
	movl	(%esi),%eax
	movl	4(%esi),%ecx
	movl	%eax,(%edx)
	movl	%ecx,4(%edx)
	movl	8(%esi),%eax
	movl	12(%esi),%ecx
	movl	%eax,8(%edx)
	movl	%ecx,12(%edx)

	ja	2f		/* Optimise since most sycalls have <= 4 args */
	jmp	.Lx86_copyargs_end
2:

	movl	16(%esi),%eax
	movl	20(%esi),%ecx
	movl	%eax,16(%edx)
	movl	%ecx,20(%edx)
	movl	24(%esi),%eax
	movl	28(%esi),%ecx
	movl	%eax,24(%edx)
	movl	%ecx,28(%edx)
	movl	32(%esi),%eax
	movl	36(%esi),%ecx
	movl	%eax,32(%edx)
	movl	%ecx,36(%edx)
.Lx86_copyargs_end:

	popl	%esi
	xorl	%eax,%eax
	ret
	DEFERRED_SWITCH_CALL
END(x86_copyargs)

NENTRY(x86_copyargs_efault)
	movl	$EFAULT,%eax
	popl	%esi
	ret
END(x86_copyargs_efault)

NENTRY(x86_copyargs_fault)
	popl	%esi
	ret
END(x86_copyargs_fault)

/*
 * Label must be after all copy functions.
 */
LABEL(x86_copyfunc_end)

/*
 * Fault table of copy functions for trap().
 */
	.section ".rodata"
	.globl _C_LABEL(onfault_table)

_C_LABEL(onfault_table):
	.long .Lcopyin_start
	.long .Lcopyin_end
	.long _C_LABEL(copy_fault)

	.long .Lcopyout_start
	.long .Lcopyout_end
	.long _C_LABEL(copy_fault)

	.long .Lkcopy_start
	.long .Lkcopy_end
	.long _C_LABEL(kcopy_fault)

	.long .Lcopyoutstr_start
	.long .Lcopyoutstr_end
	.long _C_LABEL(copystr_fault)

	.long .Lcopyinstr_start
	.long .Lcopyinstr_end
	.long _C_LABEL(copystr_fault)

	.long .Lucas32_start
	.long .Lucas32_end
	.long _C_LABEL(ucas_fault)

	.long .Lx86_copyargs_start
	.long .Lx86_copyargs_end
	.long _C_LABEL(x86_copyargs_fault)

	.long 0	/* terminate */

	.text
