Subject: fault handler problem of user space access fucntions in sh3/Locore.c
To: None <port-sh3@netbsd.org>
From: Izumi Tsutsui <tsutsui@ceres.dti.ne.jp>
List: port-sh3
Date: 03/14/2003 20:54:29
I noticed current user space access functions for sh3
(copyin*(), copyout*() and [fs]uword() etc.) didn't return
correct values if a bad address was encountered.
(They return random values.)

All these functions use "labels as values" extension of gcc
to set fault handler address:
---
{
 :
	curpcb->pcb_onfault = &&Err999;
	memcpy(to, from, len);
	curpcb->pcb_onfault = 0;
	return (0);

 Err999:
	curpcb->pcb_onfault = 0;
	return (EFAULT);
}
---

but it seems all instructions after the Err999 labels are removed
during gcc optimization:
---
 1fc:	08 d8       	mov.l	220 <copyout+0x44>,r8	! 0x0 (== curpcb)
 1fe:	82 62       	mov.l	@r8,r2
 200:	08 d1       	mov.l	224 <copyout+0x48>,r1	! 0x212 (== &&Err999)
 202:	1c 12       	mov.l	r1,@(48,r2)
	curpcb->pcb_onfault = &&Err999;

 204:	08 d1       	mov.l	228 <copyout+0x4c>,r1	! 0x0 (== memcpy)
 206:	0b 41       	jsr	@r1
 208:	33 65       	 mov	r3,r5
	memcpy(to, from, len);

 20a:	82 62       	mov.l	@r8,r2
 20c:	00 e1       	mov	#0,r1
 20e:	1c 12       	mov.l	r1,@(48,r2)
 210:	00 e0       	mov	#0,r0
	curpcb->pcb_onfault = 0;

	(&&Err999 points here)
 212:	e3 6f       	mov	r14,r15
 214:	26 4f       	lds.l	@r15+,pr
 216:	f6 6e       	mov.l	@r15+,r14
 218:	0b 00       	rts	
 21a:	f6 68       	 mov.l	@r15+,r8
	return (0);
---
The register r0 is not preserved during tlb_exception(),
so the function returns random values on the fault.
I don't know whether this is gcc's bug or not,
(gcc-3.x won't help either) but the easiest way to fix is
to rewrite these functions in asm as some other ports do.

I'd like to commit the attached patch, but any
suggestions/comments are appreciate since I'm not
familiar with SH3 asm. (mostly taken from gcc -S output.)
---
Izumi Tsutsui
tsutsui@ceres.dti.ne.jp

Index: sh3/Locore.c
===================================================================
RCS file: /cvsroot/src/sys/arch/sh3/sh3/Locore.c,v
retrieving revision 1.15
diff -u -r1.15 Locore.c
--- sh3/Locore.c	2003/01/18 06:33:43	1.15
+++ sh3/Locore.c	2003/03/14 04:27:46
@@ -224,144 +224,6 @@
 }
 
 /*
- * copyout(caddr_t from, caddr_t to, size_t len);
- * Copy len bytes into the user's address space.
- */
-int
-copyout(const void *kaddr, void *uaddr, size_t len)
-{
-	const void *from = kaddr;
-	char *to = uaddr;
-
-	if (to + len < to || to + len > (char *)VM_MAXUSER_ADDRESS)
-		return (EFAULT);
-
-	curpcb->pcb_onfault = &&Err999;
-	memcpy(to, from, len);
-	curpcb->pcb_onfault = 0;
-	return (0);
-
- Err999:
-	curpcb->pcb_onfault = 0;
-	return (EFAULT);
-}
-
-/*
- * copyin(caddr_t from, caddr_t to, size_t len);
- * Copy len bytes from the user's address space.
- */
-int
-copyin(const void *uaddr, void *kaddr, size_t len)
-{
-	const char *from = uaddr;
-	void *to = kaddr;
-
-	if (from + len < from || from + len > (char *)VM_MAXUSER_ADDRESS)
-		return (EFAULT);
-
-	curpcb->pcb_onfault = &&Err999;
-	memcpy(to, from, len);
-	curpcb->pcb_onfault = 0;
-	return (0);
-
- Err999:
-	curpcb->pcb_onfault = 0;
-	return (EFAULT);
-}
-
-/*
- * copyoutstr(caddr_t from, caddr_t 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.
- */
-int
-copyoutstr(const void *kaddr, void *uaddr, size_t maxlen, size_t *lencopied)
-{
-	const char *from = kaddr;
-	char *to = uaddr;
-	int cnt;
-	int rc = 0;
-	const char *from_top = from;
-
-	curpcb->pcb_onfault = &&Err999;
-
-	if ((cnt = (char *)VM_MAXUSER_ADDRESS - to) > maxlen)
-		cnt = maxlen;
-
-	while (cnt--) {
-		if ((*to++ = *from++) == 0) {
-			rc = 0;
-			goto out;
-		}
-	}
-
-	if (to >= (char *)VM_MAXUSER_ADDRESS)
-		rc = EFAULT;
-	else
-		rc = ENAMETOOLONG;
-
- out:
-	if (lencopied)
-		*lencopied = from - from_top;
-	curpcb->pcb_onfault = 0;
-	return (rc);
-
- Err999:
-	if (lencopied)
-		*lencopied = from - from_top;
-	curpcb->pcb_onfault = 0;
-	return (EFAULT);
-}
-
-/*
- * copyinstr(caddr_t from, caddr_t 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.
- */
-int
-copyinstr(const void *uaddr, void *kaddr, size_t maxlen, size_t *lencopied)
-{
-	const char *from = uaddr;
-	char *to = kaddr;
-	int cnt;
-	int rc = 0;
-	const char *from_top = from;
-
-	curpcb->pcb_onfault = &&Err999;
-
-	if ((cnt = (char *)VM_MAXUSER_ADDRESS - to) > maxlen)
-		cnt = maxlen;
-
-	while (cnt--) {
-		if ((*to++ = *from++) == 0) {
-			rc = 0;
-			goto out;
-		}
-	}
-
-	if (to >= (char *)VM_MAXUSER_ADDRESS)
-		rc = EFAULT;
-	else
-		rc = ENAMETOOLONG;
-
- out:
-	if (lencopied)
-		*lencopied = from - from_top;
-	curpcb->pcb_onfault = 0;
-	return (rc);
-
- Err999:
-	if (lencopied)
-		*lencopied = from - from_top;
-	curpcb->pcb_onfault = 0;
-	return (EFAULT);
-}
-
-/*
  * copystr(caddr_t from, caddr_t 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
@@ -386,227 +248,4 @@
 		*lencopied = i;
 
 	return (ENAMETOOLONG);
-}
-
-/*
- * fuword(caddr_t uaddr);
- * Fetch an int from the user's address space.
- */
-long
-fuword(const void *base)
-{
-	const char *uaddr = base;
-	long rc;
-
-	if (uaddr > (char *)VM_MAXUSER_ADDRESS - sizeof(long))
-		return (-1);
-
-	curpcb->pcb_onfault = &&Err999;
-
-	rc = *(long *)uaddr;
-
-	curpcb->pcb_onfault = 0;
-	return (rc);
-
- Err999:
-	curpcb->pcb_onfault = 0;
-	return (-1);
-}
-
-/*
- * fusword(caddr_t uaddr);
- * Fetch a short from the user's address space.
- */
-int
-fusword(const void *base)
-{
-	const char *uaddr = base;
-	int rc;
-
-	if (uaddr > (char *)VM_MAXUSER_ADDRESS - sizeof(short))
-		return (-1);
-
-	curpcb->pcb_onfault = &&Err999;
-
-	rc = *(unsigned short *)uaddr;
-
-	curpcb->pcb_onfault = 0;
-	return (rc);
-
- Err999:
-	curpcb->pcb_onfault = 0;
-	return (-1);
-}
-
-/*
- * fuswintr(caddr_t uaddr);
- * Fetch a short from the user's address space.  Can be called during an
- * interrupt.
- */
-int
-fuswintr(const void *base)
-{
-	const char *uaddr = base;
-	int rc;
-
-	if (uaddr > (char *)VM_MAXUSER_ADDRESS - sizeof(short))
-		return (-1);
-
-	curpcb->pcb_onfault = &&Err999;
-	curpcb->pcb_faultbail = 1;
-
-	rc = *(unsigned short *)uaddr;
-
-	curpcb->pcb_onfault = 0;
-	curpcb->pcb_faultbail = 0;
-	return (rc);
-
- Err999:
-	curpcb->pcb_onfault = 0;
-	curpcb->pcb_faultbail = 0;
-	return (-1);
-}
-
-/*
- * fubyte(caddr_t uaddr);
- * Fetch a byte from the user's address space.
- */
-int
-fubyte(const void *base)
-{
-	const char *uaddr = base;
-	int rc;
-
-	if (uaddr > (char *)VM_MAXUSER_ADDRESS - sizeof(char))
-		return (-1);
-
-	curpcb->pcb_onfault = &&Err999;
-
-	rc = *(unsigned char *)uaddr;
-
-	curpcb->pcb_onfault = 0;
-	return (rc);
-
- Err999:
-	curpcb->pcb_onfault = 0;
-	return (-1);
-}
-
-/*
- * suword(caddr_t uaddr, int x);
- * Store an int in the user's address space.
- */
-int
-suword(void *base, long x)
-{
-	char *uaddr = base;
-
-	if (uaddr > (char *)VM_MAXUSER_ADDRESS - sizeof(long))
-		return (-1);
-
-	curpcb->pcb_onfault = &&Err999;
-	*(int *)uaddr = x;
-	curpcb->pcb_onfault = 0;
-
-	return (0);
-
- Err999:
-	curpcb->pcb_onfault = 0;
-	return (-1);
-}
-
-/*
- * susword(void *uaddr, short x);
- * Store a short in the user's address space.
- */
-int
-susword(void *base, short x)
-{
-	char *uaddr = base;
-
-	if (uaddr > (char *)VM_MAXUSER_ADDRESS - sizeof(short))
-		return (-1);
-
-	curpcb->pcb_onfault = &&Err999;
-
-	*(short *)uaddr = x;
-
-	curpcb->pcb_onfault = 0;
-	return (0);
-
- Err999:
-	curpcb->pcb_onfault = 0;
-	return (-1);
-}
-
-/*
- * suswintr(caddr_t uaddr, short x);
- * Store a short in the user's address space.  Can be called during an
- * interrupt.
- */
-int
-suswintr(void *base, short x)
-{
-	char *uaddr = base;
-
-	if (uaddr > (char *)VM_MAXUSER_ADDRESS - sizeof(short))
-		return (-1);
-
-	curpcb->pcb_onfault = &&Err999;
-	curpcb->pcb_faultbail = 1;
-
-	*(short *)uaddr = x;
-
-	curpcb->pcb_onfault = 0;
-	curpcb->pcb_faultbail = 0;
-	return (0);
-
- Err999:
-	curpcb->pcb_onfault = 0;
-	curpcb->pcb_faultbail = 0;
-
-	return (-1);
-}
-
-/*
- * subyte(caddr_t uaddr, char x);
- * Store a byte in the user's address space.
- */
-int
-subyte(void *base, int x)
-{
-	char *uaddr = base;
-
-	if (uaddr > (char *)VM_MAXUSER_ADDRESS - sizeof(char))
-		return (-1);
-
-	curpcb->pcb_onfault = &&Err999;
-
-	*(char *)uaddr = x;
-
-	curpcb->pcb_onfault = 0;
-	return (0);
-
- Err999:
-	curpcb->pcb_onfault = 0;
-	return (-1);
-}
-
-int
-kcopy(const void *src, void *dst, size_t len)
-{
-	caddr_t oldfault;
-
-	oldfault = curpcb->pcb_onfault;
-
-	curpcb->pcb_onfault = &&Err999;
-	memcpy(dst, src, len);
-	curpcb->pcb_onfault = oldfault;
-
-	return (0);
-
- Err999:
-	curpcb->pcb_onfault = oldfault;
-
-	return (EFAULT);
 }
Index: sh3/genassym.cf
===================================================================
RCS file: /cvsroot/src/sys/arch/sh3/sh3/genassym.cf,v
retrieving revision 1.7
diff -u -r1.7 genassym.cf
--- sh3/genassym.cf	2003/01/18 06:33:43	1.7
+++ sh3/genassym.cf	2003/03/14 04:27:47
@@ -37,10 +37,11 @@
 include <sys/proc.h>
 include <sys/signal.h>
 include <sys/mbuf.h>
+include <sys/user.h>
+include <sys/errno.h>
 include <uvm/uvm_extern.h>
-include <sh3/pcb.h>
 include <sh3/locore.h>
-include <sh3/proc.h>
+include <sh3/vmparam.h>
 
 define	TF_SIZE			sizeof(struct trapframe)
 define	TF_EXPEVT		offsetof(struct trapframe, tf_expevt)
@@ -95,10 +96,18 @@
 
 define	SC_EFLAGS		offsetof(struct sigcontext, sc_ssr)
 
+define	PCB_ONFAULT		offsetof(struct pcb, pcb_onfault)
+define	PCB_FAULTBAIL		offsetof(struct pcb, pcb_faultbail)
+
 define	LSONPROC		LSONPROC
 define	LSRUN			LSRUN
 
 define	UVMEXP_INTRS		offsetof(struct uvmexp, intrs)
+
+define	VM_MAXUSER_ADDRESS	VM_MAXUSER_ADDRESS
+
+define	EFAULT			EFAULT
+define	ENAMETOOLONG		ENAMETOOLONG
 
 #
 # in_cksum.S
Index: sh3/locore_subr.S
===================================================================
RCS file: /cvsroot/src/sys/arch/sh3/sh3/locore_subr.S,v
retrieving revision 1.13
diff -u -r1.13 locore_subr.S
--- sh3/locore_subr.S	2003/01/18 06:33:43	1.13
+++ sh3/locore_subr.S	2003/03/14 04:27:48
@@ -426,6 +426,596 @@
 	rts
 	 nop
 
+/*
+ * copyout(caddr_t kaddr, caddr_t uaddr, size_t len);
+ * Copy len bytes into the user's address space.
+ */
+ENTRY(copyout)
+	mov.l	r14,	@-r15
+	sts.l	pr,	@-r15
+	mov	r15,	r14
+
+	mov	#EFAULT, r0		/* assume there was a problem */
+	mov	r4,	r3
+	mov	r5,	r2
+	mov	r5,	r4
+	add	r6,	r2
+	cmp/hs	r5,	r2		/* bomb if uaddr+len wraps */
+	bf	2f
+	mov.l	_Lcopyout.VM_MAXUSER_ADDRESS, r1
+	cmp/hi	r1,	r2		/* bomb if uaddr isn't in user space */
+	bt	2f
+
+	mov.l	_Lcopyout.curpcb, r1	/* set fault hander */
+	mov.l	@r1,	r2
+	mov.l	_Lcopyout.onfault, r1
+	mov.l	r1,	@(PCB_ONFAULT,r2)
+	mov.l	_Lcopyout.memcpy, r1
+	jsr	@r1			/* memcpy(uaddr, kaddr, len) */
+	 mov	r3,	r5
+
+	mov	#0,	r0
+1:
+	mov.l	_Lcopyout.curpcb, r1	/* clear fault handler */
+	mov.l	@r1,	r2
+	mov	#0,	r1
+	mov.l	r1,	@(PCB_ONFAULT,r2)
+2:
+	mov	r14,	r15
+	lds.l	@r15+,	pr
+	rts
+	 mov.l	@r15+,	r14
+
+3:
+	bra	1b
+	 mov	#EFAULT, r0
+
+	.align 2
+_Lcopyout.onfault:
+	.long	3b
+_Lcopyout.VM_MAXUSER_ADDRESS:
+	.long	VM_MAXUSER_ADDRESS
+_Lcopyout.curpcb:
+	.long	_C_LABEL(curpcb)
+_Lcopyout.memcpy:
+	.long	_C_LABEL(memcpy)
+
+/*
+ * copyin(caddr_t uaddr, caddr_t kaddr, size_t len);
+ * Copy len bytes from the user's address space.
+ */
+ENTRY(copyin)
+	mov.l	r14,	@-r15
+	sts.l	pr,	@-r15
+	mov	r15,	r14
+
+	mov	#EFAULT, r0		/* assume there was a problem */
+	mov	r4,	r3
+	mov	r5,	r4
+	mov	r3,	r2
+	add	r6,	r2
+	cmp/hs	r3,	r2		/* bomb if uaddr+len wraps */
+	bf	2f
+	mov.l	_Lcopyin.VM_MAXUSER_ADDRESS, r1
+	cmp/hi	r1,	r2		/* bomb if uaddr isn't in user space */
+	bt	2f
+
+	mov.l	_Lcopyin.curpcb, r1	/* set fault hander */
+	mov.l	@r1,	r2
+	mov.l	_Lcopyin.onfault, r1
+	mov.l	r1,	@(PCB_ONFAULT,r2)
+	mov.l	_Lcopyin.memcpy, r1
+	jsr	@r1			/* memcpy(kaddr, uaddr, len) */
+	 mov	r3,	r5
+
+	mov	#0,	r0
+1:
+	mov.l	_Lcopyin.curpcb, r1	/* clear fault hander */
+	mov.l	@r1,	r2
+	mov	#0,	r1
+	mov.l	r1,	@(PCB_ONFAULT,r2)
+2:
+	mov	r14,	r15
+	lds.l	@r15+,	pr
+	rts
+	 mov.l	@r15+,	r14
+
+3:
+	bra	1b
+	 mov	#EFAULT, r0
+
+	.align 2
+_Lcopyin.onfault:
+	.long	3b
+_Lcopyin.VM_MAXUSER_ADDRESS:
+	.long	VM_MAXUSER_ADDRESS
+_Lcopyin.curpcb:
+	.long	_C_LABEL(curpcb)
+_Lcopyin.memcpy:
+	.long	_C_LABEL(memcpy)
+
+/*
+ * copyoutstr(caddr_t kaddr, caddr_t uaddr, 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.
+ */
+ENTRY(copyoutstr)
+	mov.l	r8,	@-r15
+
+	mov	#EFAULT, r3		/* assume there was a problem */
+	mov	r4,	r8
+	mov.l	_Lcopyoutstr.curpcb, r1	/* set fault handler */
+	mov.l	@r1,	r2
+	mov.l	_Lcopyoutstr.onfault, r1
+	mov.l	r1,	@(PCB_ONFAULT,r2)
+	mov.l	_Lcopyoutstr.VM_MAXUSER_ADDRESS, r1
+	mov	r1,	r0
+	sub	r5,	r0
+	cmp/hi	r6,	r0		/* don't beyond user space */
+	bf	2f
+	bra	2f
+	 mov	r6,	r0
+
+	.align 2
+1:
+	mov.b	@r4+,	r1		/* copy str */
+	mov.b	r1,	@r5
+	extu.b	r1,	r1
+	add	#1,	r5
+	tst	r1,	r1
+	bf	2f
+	bra	3f
+	 mov	#0,	r3
+	.align 2
+2:
+	add	#-1,	r0
+	cmp/eq	#-1,	r0
+	bf	1b
+	mov.l	_Lcopyoutstr.VM_MAXUSER_ADDRESS, r1
+	cmp/hs	r1,	r5
+	bt	3f
+	mov	#ENAMETOOLONG, r3
+
+3:
+	tst	r7,	r7		/* set lencopied if needed */
+	bt	4f
+	mov	r4,	r1
+	sub	r8,	r1
+	mov.l	r1,	@r7
+4:
+	mov.l	_Lcopyoutstr.curpcb, r1	/* clear fault handler */
+	mov.l	@r1,	r2
+	mov	#0,	r1
+	mov.l	r1,	@(PCB_ONFAULT,r2)
+
+	mov	r3,	r0
+	rts
+	 mov.l	@r15+,	r8
+
+5:
+	bra	4b
+	 mov	#EFAULT, r0
+
+	.align 2
+_Lcopyoutstr.onfault:
+	.long	5b
+_Lcopyoutstr.VM_MAXUSER_ADDRESS:
+	.long	VM_MAXUSER_ADDRESS
+_Lcopyoutstr.curpcb:
+	.long	_C_LABEL(curpcb)
+
+/*
+ * copyinstr(caddr_t from, caddr_t 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.
+ */
+ENTRY(copyinstr)
+	mov.l	r8,	@-r15
+	mov	#EFAULT, r3		/* assume there was a problem */
+	mov	r4,	r8
+	mov.l	_Lcopyinstr.curpcb, r1	/* set fault handler */
+	mov.l	@r1,	r2
+	mov.l	_Lcopyinstr.onfault, r1
+	mov.l	r1,	@(PCB_ONFAULT,r2)
+
+	mov.l	_Lcopyinstr.VM_MAXUSER_ADDRESS, r1
+	mov	r1,	r0
+	sub	r5,	r0
+	cmp/hi	r6,	r0		/* don't beyond user space */
+	bf	2f
+	bra	2f
+	 mov	r6,	r0
+
+	.align 2
+1:
+	mov.b	@r4+,	r1		/* copy str */
+	mov.b	r1,	@r5
+	extu.b	r1,	r1
+	add	#1,	r5
+	tst	r1,	r1
+	bf	2f
+	bra	3f
+	 mov	#0,	r3
+	.align 2
+2:
+	add	#-1,	r0
+	cmp/eq	#-1,	r0
+	bf	1b
+	mov.l	_Lcopyinstr.VM_MAXUSER_ADDRESS, r1
+	cmp/hs	r1,	r5
+	bt	3f
+	mov	#ENAMETOOLONG, r3
+
+3:
+	tst	r7,	r7		/* set lencopied if needed */
+	bt	4f
+	mov	r4,	r1
+	sub	r8,	r1
+	mov.l	r1,	@r7
+4:
+	mov.l	_Lcopyinstr.curpcb, r1	/* clear fault handler */
+	mov.l	@r1,	r2
+	mov	#0,	r1
+	mov.l	r1,	@(PCB_ONFAULT,r2)
+
+	mov	r3,	r0
+	rts
+	 mov.l	@r15+,	r8
+
+5:
+	bra	4b
+	 mov	#EFAULT, r0
+
+	.align 2
+_Lcopyinstr.onfault:
+	.long	5b
+_Lcopyinstr.VM_MAXUSER_ADDRESS:
+	.long	VM_MAXUSER_ADDRESS
+_Lcopyinstr.curpcb:
+	.long	_C_LABEL(curpcb)
+
+/*
+ * fuword(caddr_t uaddr);
+ * Fetch an int from the user's address space.
+ */
+ENTRY(fuword)
+	mov.l	_Lfuword.VM_MAXUSER_ADDRESS,	r1
+	cmp/hi	r1,	r4		/* bomb if uaddr isn't in user space */
+	bt/s	2f
+	 mov	#-1,	r0
+	mov.l	_Lfuword.curpcb, r1	/* set fault handler */
+	mov.l	@r1,	r2
+	mov.l	_Lfuword.onfault, r1
+	mov.l	r1,	@(PCB_ONFAULT,r2)
+	mov.l	@r4,	r0		/* fetch the value */
+1:
+	mov.l	_Lfuword.curpcb, r1	/* clear fault handler */
+	mov.l	@r1,	r2
+	mov	#0,	r1
+	mov.l	r1,	@(PCB_ONFAULT,r2)
+2:
+	rts
+	 nop
+
+3:
+	bra	1b
+	 mov	#-1,	r0
+	
+	.align 2
+_Lfuword.onfault:
+	.long	3b
+_Lfuword.VM_MAXUSER_ADDRESS:
+	.long	VM_MAXUSER_ADDRESS - 4 /*sizeof(long)*/
+_Lfuword.curpcb:
+	.long	_C_LABEL(curpcb)
+
+/*
+ * fusword(caddr_t uaddr);
+ * Fetch a short from the user's address space.
+ */
+ENTRY(fusword)
+	mov.l	_Lfusword.VM_MAXUSER_ADDRESS, r1
+	cmp/hi	r1,	r4		/* bomb if uaddr isn't user space */
+	bt/s	2f
+	 mov	#-1,	r0
+	mov.l	_Lfusword.curpcb, r1	/* set fault handler */
+	mov.l	@r1,	r2
+	mov.l	_Lfusword.onfault, r1
+	mov.l	r1,	@(PCB_ONFAULT,r2)
+	mov.w	@r4,	r1		/* fetch the value */
+	exts.w	r1,	r0
+1:
+	mov.l	_Lfusword.curpcb, r1	/* clear fault handler */
+	mov.l	@r1,	r2
+	mov	#0,	r1
+	mov.l	r1,	@(PCB_ONFAULT,r2)
+2:
+	rts
+	 nop
+
+3:
+	bra	1b
+	 mov	#-1,	r0
+
+	.align 2
+_Lfusword.onfault:
+	.long	3b
+_Lfusword.VM_MAXUSER_ADDRESS:
+	.long	VM_MAXUSER_ADDRESS - 2 /*sizeof(short)*/
+_Lfusword.curpcb:
+	.long	_C_LABEL(curpcb)
+
+/*
+ * fuswintr(caddr_t uaddr);
+ * Fetch a short from the user's address space.  Can be called during an
+ * interrupt.
+ */
+ENTRY(fuswintr)
+	mov.l	_Lfuswintr.VM_MAXUSER_ADDRESS, r1
+	cmp/hi	r1,	r4		/* bomb if uaddr isn't user space */
+	bt/s	2f
+	 mov	#-1,	r0
+	mov.l	_Lfuswintr.curpcb, r1	/* set fault handler */
+	mov.l	@r1,	r2
+	mov.l	_Lfuswintr.onfault, r1
+	mov.l	r1,	@(PCB_ONFAULT,r2)
+	mov	#1,	r1		/* set faultbail */
+	mov.l	r1,	@(PCB_FAULTBAIL,r2)
+	mov.w	@r4,	r1		/* fetch the value */
+	exts.w	r1,	r0
+1:
+	mov.l	_Lfuswintr.curpcb, r1	/* clear fault handler and faultbail */
+	mov.l	@r1,	r2
+	mov	#0,	r1
+	mov.l	r1,	@(PCB_ONFAULT,r2)
+	mov.l	r1,	@(PCB_FAULTBAIL,r2)
+2:
+	rts
+	 nop
+
+3:
+	bra	1b
+	 mov	#-1,	r0
+
+	.align 2
+_Lfuswintr.onfault:
+	.long	3b
+_Lfuswintr.VM_MAXUSER_ADDRESS:
+	.long	VM_MAXUSER_ADDRESS - 2 /*sizeof(short)*/
+_Lfuswintr.curpcb:
+	.long	_C_LABEL(curpcb)
+
+/*
+ * fubyte(caddr_t uaddr);
+ * Fetch a byte from the user's address space.
+ */
+ENTRY(fubyte)
+	mov.l	_Lfubyte.VM_MAXUSER_ADDRESS, r1
+	cmp/hi	r1,	r4		/* bomb if uaddr isn't in user space */
+	bt/s	2f
+	 mov	#-1,	r0
+	mov.l	_Lfubyte.curpcb, r1	/* set fault handler */
+	mov.l	@r1,	r2
+	mov.l	_Lfubyte.onfault, r1
+	mov.l	r1,	@(PCB_ONFAULT,r2)
+	mov.b	@r4,	r1		/* fetch the value */
+	exts.b	r1,	r0
+1:
+	mov.l	_Lfubyte.curpcb, r1		/* clear fault handler */
+	mov.l	@r1,	r2
+	mov	#0,	r1
+	mov.l	r1,	@(PCB_ONFAULT,r2)
+2:
+	rts
+	 nop
+
+3:
+	bra	1b
+	 mov	#-1,	r0
+
+	.align 2
+_Lfubyte.onfault:
+	.long	3b
+_Lfubyte.VM_MAXUSER_ADDRESS:
+	.long	VM_MAXUSER_ADDRESS - 1 /*sizeof(char)*/
+_Lfubyte.curpcb:
+	.long	_C_LABEL(curpcb)
+
+/*
+ * suword(caddr_t uaddr, int x);
+ * Store an int in the user's address space.
+ */
+ENTRY(suword)
+	mov.l	_Lsuword.VM_MAXUSER_ADDRESS, r1
+	cmp/hi	r1,	r4		/* bomb if uaddr isn't in user space */
+	bt/s	2f
+	 mov	#-1,	r0
+	mov.l	_Lsuword.curpcb, r1	/* set fault handler */
+	mov.l	@r1,	r2
+	mov.l	_Lsuword.onfault, r1
+	mov.l	r1,	@(PCB_ONFAULT,r2)
+	mov.l	r5,	@r4		/* store the value */
+	mov	#0,	r0
+1:
+	mov.l	_Lsuword.curpcb, r1	/* clear fault handler */
+	mov.l	@r1,	r2
+	mov	#0,	r1
+	mov.l	r1,	@(PCB_ONFAULT,r2)
+2:
+	rts
+	 nop
+
+3:
+	bra	1b
+	 mov	#-1,	r0
+
+	.align 2
+_Lsuword.onfault:
+	.long	3b
+_Lsuword.VM_MAXUSER_ADDRESS:
+	.long	VM_MAXUSER_ADDRESS - 4 /*sizeof(long)*/
+_Lsuword.curpcb:
+	.long	_C_LABEL(curpcb)
+
+/*
+ * susword(void *uaddr, short x);
+ * Store a short in the user's address space.
+ */
+ENTRY(susword)
+	mov.l	_Lsusword.VM_MAXUSER_ADDRESS, r1
+	cmp/hi	r1,	r4		/* bomb if uaddr isn't in user space */
+	bt/s	2f
+	 mov	#-1,	r0
+	mov.l	_Lsusword.curpcb, r1	/* set fault handler */
+	mov.l	@r1,	r2
+	mov.l	_Lsusword.onfault, r1
+	mov.l	r1,	@(PCB_ONFAULT,r2)
+	mov.w	r5,	@r4		/* store the value */
+	mov	#0,	r0
+1:
+	mov.l	_Lsusword.curpcb, r1	/* clear fault handler */
+	mov.l	@r1,	r2
+	mov	#0,	r1
+	mov.l	r1,	@(PCB_ONFAULT,r2)
+2:
+	rts
+	 nop
+
+3:
+	bra	1b
+	 mov	#-1,	r0
+
+	.align 2
+_Lsusword.onfault:
+	.long	3b
+_Lsusword.VM_MAXUSER_ADDRESS:
+	.long	VM_MAXUSER_ADDRESS - 2 /*sizeof(short)*/
+_Lsusword.curpcb:
+	.long	_C_LABEL(curpcb)
+
+/*
+ * suswintr(caddr_t uaddr, short x);
+ * Store a short in the user's address space.  Can be called during an
+ * interrupt.
+ */
+ENTRY(suswintr)
+	mov.l	_Lsuswintr.VM_MAXUSER_ADDRESS, r1
+	cmp/hi	r1,	r4		/* bomb if uaddr isn't in user space */
+	bt/s	2f
+	 mov	#-1,	r0
+	mov.l	_Lsuswintr.curpcb, r1	/* set fault handler */
+	mov.l	@r1,	r2
+	mov.l	_Lsuswintr.onfault, r1
+	mov.l	r1,	@(PCB_ONFAULT,r2)
+	mov	#1,	r1		/* set faultbail */
+	mov.l	r1,	@(PCB_FAULTBAIL,r2)
+	mov.w	r5,	@r4		/* store the value */
+	mov	#0,	r0
+1:
+	mov.l	_Lsuswintr.curpcb, r1	/* clear fault handler and faultbail */
+	mov.l	@r1,	r2
+	mov	#0,	r1
+	mov.l	r1,	@(PCB_ONFAULT,r2)
+	mov.l	r1,	@(PCB_FAULTBAIL,r2)
+2:
+	rts
+	 nop
+
+3:
+	bra	1b
+	 mov	#-1,	r0
+
+	.align 2
+_Lsuswintr.onfault:
+	.long	3b
+_Lsuswintr.VM_MAXUSER_ADDRESS:
+	.long	VM_MAXUSER_ADDRESS - 2 /*sizeof(short)*/
+_Lsuswintr.curpcb:
+	.long	_C_LABEL(curpcb)
+
+/*
+ * subyte(caddr_t uaddr, char x);
+ * Store a byte in the user's address space.
+ */
+ENTRY(subyte)
+	mov.l	_Lsubyte.VM_MAXUSER_ADDRESS, r1
+	cmp/hi	r1,	r4		/* bomb if uaddr isn't in user space */
+	bt/s	2f
+	 mov	#-1,	r0
+	mov.l	_Lsubyte.curpcb, r1	/* set fault handler */
+	mov.l	@r1,	r2
+	mov.l	_Lsubyte.onfault, r1
+	mov.l	r1,	@(PCB_ONFAULT,r2)
+	mov.b	r5,	@r4		/* store the value */
+	mov	#0,	r0
+1:
+	mov.l	_Lsubyte.curpcb, r1	/* clear fault handler */
+	mov.l	@r1,	r2
+	mov	#0,	r1
+	mov.l	r1,	@(PCB_ONFAULT,r2)
+2:
+	rts	
+	 nop
+
+3:
+	bra	1b
+	 mov	#-1,	r0
+
+	.align 2
+_Lsubyte.onfault:
+	.long	3b
+_Lsubyte.VM_MAXUSER_ADDRESS:
+	.long	VM_MAXUSER_ADDRESS - 1 /*sizeof(char)*/
+_Lsubyte.curpcb:
+	.long	_C_LABEL(curpcb)
+
+/*
+ * kcopy(const void *src, void *dst, size_t len)
+ */
+ENTRY(kcopy)
+	mov.l	r8,	@-r15
+	mov.l	r14,	@-r15
+	sts.l	pr,	@-r15
+	mov	r15,	r14
+
+	mov	r4,	r3
+	mov.l	_Lkcopy.curpcb, r1
+	mov.l	@r1,	r2
+	mov.l	@(PCB_ONFAULT,r2) ,r8	/* save old fault handler */
+	mov.l	_Lkcopy.onfault, r1
+	mov.l	r1,	@(PCB_ONFAULT,r2) /* set fault handler */
+	mov.l	_Lkcopy.memcpy, r1
+	mov	r5,	r4
+	jsr	@r1			/* memcpy(dst, src, len) */
+	 mov	r3,	r5
+	mov	#0,	r0
+1:
+	mov.l	_Lkcopy.curpcb, r1	/* restore fault handler */
+	mov.l	@r1,	r2
+	mov.l	r8,	@(PCB_ONFAULT,r2)
+
+	mov	r14,	r15
+	lds.l	@r15+,	pr
+	mov.l	@r15+,	r14
+	rts
+	 mov.l	@r15+,	r8
+
+2:
+	bra	1b
+	 mov	#EFAULT, r0
+
+	.align 2
+_Lkcopy.onfault:
+	.long	2b
+_Lkcopy.curpcb:
+	.long	_C_LABEL(curpcb)
+_Lkcopy.memcpy:
+	.long	_C_LABEL(memcpy)
+
 #if defined(DDB) || defined(KGDB)
 /*
  * int setjmp(label_t *):