Index: Makefile
===================================================================
--- Makefile	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ Makefile	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -129,4 +129,5 @@
 	generic/src/proc/the.c \
 	generic/src/syscall/syscall.c \
+	generic/src/syscall/copy.c \
 	generic/src/mm/buddy.c \
 	generic/src/mm/frame.c \
Index: arch/amd64/include/interrupt.h
===================================================================
--- arch/amd64/include/interrupt.h	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ arch/amd64/include/interrupt.h	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -87,4 +87,9 @@
 };
 
+static inline void istate_set_retaddr(istate_t *istate, __address retaddr)
+{
+	istate->rip = retaddr;
+}
+
 extern void (* disable_irqs_function)(__u16 irqmask);
 extern void (* enable_irqs_function)(__u16 irqmask);
Index: arch/amd64/src/asm_utils.S
===================================================================
--- arch/amd64/src/asm_utils.S	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ arch/amd64/src/asm_utils.S	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -61,8 +61,4 @@
 	jmp printf
 
-.global memcpy
-memcpy:
-	jmp _memcpy
-	
 .global cpuid
 .global has_cpuid
@@ -70,5 +66,53 @@
 .global read_efer_flag
 .global set_efer_flag
-	
+.global memcpy
+.global memcpy_from_uspace
+.global memcpy_to_uspace
+.global memcpy_from_uspace_failover_address
+.global memcpy_to_uspace_failover_address
+
+#define MEMCPY_DST	%rdi
+#define MEMCPY_SRC	%rsi
+#define MEMCPY_SIZE	%rdx
+
+/**
+ * Copy memory from/to userspace.
+ *
+ * This is almost conventional memcpy().
+ * The difference is that there is a failover part
+ * to where control is returned from a page fault if
+ * the page fault occurs during copy_from_uspace()
+ * or copy_to_uspace().
+ *
+ * @param MEMCPY_DST	Destination address.
+ * @param MEMCPY_SRC	Source address.
+ * @param MEMCPY_SIZE	Number of bytes to copy.
+ *
+ * @retrun MEMCPY_SRC on success, 0 on failure.
+ */
+memcpy:
+memcpy_from_uspace:
+memcpy_to_uspace:
+	movq MEMCPY_SRC, %rax
+
+	movq MEMCPY_SIZE, %rcx
+	shrq $3, %rcx			/* size / 8 */
+	
+	rep movsq			/* copy as much as possible word by word */
+
+	movq MEMCPY_SIZE, %rcx
+	andq $7, %rcx			/* size % 8 */
+	jz 0f
+	
+	rep movsb			/* copy the rest byte by byte */
+	
+0:
+	ret				/* return MEMCPY_SRC, success */
+
+memcpy_from_uspace_failover_address:
+memcpy_to_uspace_failover_address:
+	xorq %rax, %rax			/* return 0, failure */
+	ret
+
 ## Determine CPUID support
 #
Index: arch/amd64/src/mm/page.c
===================================================================
--- arch/amd64/src/mm/page.c	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ arch/amd64/src/mm/page.c	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -167,5 +167,5 @@
 	
 	page = read_cr2();
-	if (!as_page_fault(page)) {
+	if (as_page_fault(page, istate) == AS_PF_FAULT) {
 		print_info_errcode(n, istate);
 		printf("Page fault address: %llX\n", page);
Index: arch/ia32/include/interrupt.h
===================================================================
--- arch/ia32/include/interrupt.h	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ arch/ia32/include/interrupt.h	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -84,4 +84,9 @@
 };
 
+static inline void istate_set_retaddr(istate_t *istate, __address retaddr)
+{
+	istate->eip = retaddr;
+}
+
 extern void (* disable_irqs_function)(__u16 irqmask);
 extern void (* enable_irqs_function)(__u16 irqmask);
Index: arch/ia32/src/asm.S
===================================================================
--- arch/ia32/src/asm.S	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ arch/ia32/src/asm.S	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -38,4 +38,65 @@
 .global enable_l_apic_in_msr
 .global interrupt_handlers
+.global memcpy
+.global memcpy_from_uspace
+.global memcpy_from_uspace_failover_address
+.global memcpy_to_uspace
+.global memcpy_to_uspace_failover_address
+
+
+#define MEMCPY_DST	4
+#define MEMCPY_SRC	8
+#define MEMCPY_SIZE	12
+
+/** Copy memory to/from userspace.
+ *
+ * This is almost conventional memcpy().
+ * The difference is that there is a failover part
+ * to where control is returned from a page fault
+ * if the page fault occurs during copy_from_uspace()
+ * or copy_to_uspace().
+ *
+ * @param MEMCPY_DST(%esp)	Destination address.
+ * @param MEMCPY_SRC(%esp)	Source address.
+ * @param MEMCPY_SIZE(%esp)	Size.
+ *
+ * @return MEMCPY_SRC(%esp) on success and 0 on failure.
+ */
+memcpy:
+memcpy_from_uspace:
+memcpy_to_uspace:
+	movl %edi, %edx				/* save %edi */
+	movl %esi, %eax				/* save %esi */
+	
+	movl MEMCPY_SIZE(%esp), %ecx
+	shrl $2, %ecx				/* size / 4 */
+	
+	movl MEMCPY_DST(%esp), %edi
+	movl MEMCPY_SRC(%esp), %esi
+	
+	rep movsl				/* copy as much as possible word by word */
+
+	movl MEMCPY_SIZE(%esp), %ecx
+	andl $3, %ecx				/* size % 4 */
+	jz 0f
+	
+	rep movsb				/* copy the rest byte by byte */
+
+0:
+	movl %edx, %edi
+	movl %eax, %esi
+	movl MEMCPY_SRC(%esp), %eax		/* MEMCPY_SRC(%esp), success */
+	ret
+	
+/*
+ * We got here from as_page_fault() after the memory operations
+ * above had caused a page fault.
+ */
+memcpy_from_uspace_failover_address:
+memcpy_to_uspace_failover_address:
+	movl %edx, %edi
+	movl %eax, %esi
+	xorl %eax, %eax				/* return 0, failure */
+	ret
 
 ## Turn paging on
Index: arch/ia32/src/boot/boot.S
===================================================================
--- arch/ia32/src/boot/boot.S	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ arch/ia32/src/boot/boot.S	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -35,12 +35,4 @@
 #define START_STACK (BOOT_OFFSET - BOOT_STACK_SIZE)
 
-
-
-
-
-
-
-
-
 .section K_TEXT_START, "ax"
 
@@ -436,9 +428,5 @@
 #endif	
 
-
-
 .section K_DATA_START, "aw", @progbits
-
-
 
 .align 4096
Index: arch/ia32/src/interrupt.c
===================================================================
--- arch/ia32/src/interrupt.c	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ arch/ia32/src/interrupt.c	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -145,5 +145,5 @@
 
 	page = read_cr2();
-	if (!as_page_fault(page)) {
+	if (as_page_fault(page, istate) == AS_PF_FAULT) {
 		PRINT_INFO_ERRCODE(istate);
 		printf("page fault address: %#x\n", page);
Index: arch/ia64/include/interrupt.h
===================================================================
--- arch/ia64/include/interrupt.h	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ arch/ia64/include/interrupt.h	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -107,4 +107,9 @@
 };
 
+static inline void istate_set_retaddr(istate_t *istate, __address retaddr)
+{
+	/* TODO */
+}
+
 extern void *ivt;
 
Index: arch/ia64/src/asm.S
===================================================================
--- arch/ia64/src/asm.S	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ arch/ia64/src/asm.S	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -31,7 +31,23 @@
 .text
 
+/** Copy memory from/to userspace.
+ *
+ * @param in0 Destination address.
+ * @param in1 Source address.
+ * @param in2 Number of byte to copy.
+ */
 .global memcpy
+.global memcpy_from_uspace
+.global memcpy_to_uspace
+.global memcpy_from_uspace_failover_address
+.global memcpy_to_uspace_failover_address
 memcpy:
+memcpy_from_uspace:
+memcpy_to_uspace:
 	br _memcpy
+	
+memcpy_from_uspace_failover_address:
+memcpy_to_uspace_failover_address:
+	br memcpy_from_uspace_failover_address
 
 .global memsetb
Index: arch/ia64/src/mm/tlb.c
===================================================================
--- arch/ia64/src/mm/tlb.c	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ arch/ia64/src/mm/tlb.c	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -448,5 +448,5 @@
 		 */
 		page_table_unlock(AS, true);
-		if (!as_page_fault(va)) {
+		if (as_page_fault(va, istate) == AS_PF_FAULT) {
 			panic("%s: va=%p, rid=%d, iip=%p\n", __FUNCTION__, istate->cr_ifa, rr.map.rid, istate->cr_iip);
 		}
@@ -494,5 +494,5 @@
 		 */
 		page_table_unlock(AS, true);
-		if (!as_page_fault(va)) {
+		if (as_page_fault(va, istate) == AS_PF_FAULT) {
 			panic("%s: va=%p, rid=%d, iip=%p\n", __FUNCTION__, va, rid, istate->cr_iip);
 		}
@@ -609,5 +609,5 @@
 	} else {
 		page_table_unlock(AS, true);
-		if (!as_page_fault(va)) {
+		if (as_page_fault(va, istate) == AS_PF_FAULT) {
 			panic("%s: va=%p, rid=%d\n", __FUNCTION__, va, rr.map.rid);
 		}
Index: arch/mips32/include/exception.h
===================================================================
--- arch/mips32/include/exception.h	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ arch/mips32/include/exception.h	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -94,4 +94,9 @@
 };
 
+static inline void istate_set_retaddr(istate_t *istate, __address retaddr)
+{
+	/* TODO */
+}
+
 extern void exception(istate_t *istate);
 extern void tlb_refill_entry(void);
Index: arch/mips32/src/asm.S
===================================================================
--- arch/mips32/src/asm.S	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ arch/mips32/src/asm.S	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -58,8 +58,22 @@
 	nop
 
+
 .global memcpy
+.global memcpy_from_uspace
+.global memcpy_to_uspace
+.global memcpy_from_uspace_failover_address
+.global memcpy_to_uspace_failover_address
 memcpy:
+memcpy_from_uspace:
+memcpy_to_uspace:
 	j _memcpy
 	nop
+
+memcpy_from_uspace_failover_address:
+memcpy_to_uspace_failover_address:
+	j memcpy_from_uspace_failover_address
+	nop
+
+
 
 .macro fpu_gp_save reg ctx
Index: arch/mips32/src/mm/tlb.c
===================================================================
--- arch/mips32/src/mm/tlb.c	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ arch/mips32/src/mm/tlb.c	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -45,5 +45,5 @@
 static void tlb_modified_fail(istate_t *istate);
 
-static pte_t *find_mapping_and_check(__address badvaddr);
+static pte_t *find_mapping_and_check(__address badvaddr, istate_t *istate, int *pfrc);
 
 static void prepare_entry_lo(entry_lo_t *lo, bool g, bool v, bool d, bool cacheable, __address pfn);
@@ -92,4 +92,5 @@
 	__address badvaddr;
 	pte_t *pte;
+	int pfrc;
 
 	badvaddr = cp0_badvaddr_read();
@@ -101,7 +102,21 @@
 	page_table_lock(AS, true);
 
-	pte = find_mapping_and_check(badvaddr);
-	if (!pte)
-		goto fail;
+	pte = find_mapping_and_check(badvaddr, istate, &pfrc);
+	if (!pte) {
+		switch (pfrc) {
+		case AS_PF_FAULT:
+			goto fail;
+			break;
+		case AS_PF_DEFER:
+			/*
+			 * The page fault came during copy_from_uspace()
+			 * or copy_to_uspace().
+			 */
+			page_table_unlock(AS, true);
+			return;
+		default:
+			panic("unexpected pfrc (%d)\n", pfrc);
+		}
+	}
 
 	/*
@@ -149,4 +164,5 @@
 	entry_hi_t hi;
 	pte_t *pte;
+	int pfrc;
 
 	badvaddr = cp0_badvaddr_read();
@@ -171,7 +187,21 @@
 	}
 
-	pte = find_mapping_and_check(badvaddr);
-	if (!pte)
-		goto fail;
+	pte = find_mapping_and_check(badvaddr, istate, &pfrc);
+	if (!pte) {
+		switch (pfrc) {
+		case AS_PF_FAULT:
+			goto fail;
+			break;
+		case AS_PF_DEFER:
+			/*
+			 * The page fault came during copy_from_uspace()
+			 * or copy_to_uspace().
+			 */
+			page_table_unlock(AS, true);			 
+			return;
+		default:
+			panic("unexpected pfrc (%d)\n", pfrc);
+		}
+	}
 
 	/*
@@ -218,4 +248,5 @@
 	entry_hi_t hi;
 	pte_t *pte;
+	int pfrc;
 
 	badvaddr = cp0_badvaddr_read();
@@ -240,7 +271,21 @@
 	}
 
-	pte = find_mapping_and_check(badvaddr);
-	if (!pte)
-		goto fail;
+	pte = find_mapping_and_check(badvaddr, istate, &pfrc);
+	if (!pte) {
+		switch (pfrc) {
+		case AS_PF_FAULT:
+			goto fail;
+			break;
+		case AS_PF_DEFER:
+			/*
+			 * The page fault came during copy_from_uspace()
+			 * or copy_to_uspace().
+			 */
+			page_table_unlock(AS, true);			 
+			return;
+		default:
+			panic("unexpected pfrc (%d)\n", pfrc);
+		}
+	}
 
 	/*
@@ -322,8 +367,10 @@
  *
  * @param badvaddr Faulting virtual address.
+ * @param istate Pointer to interrupted state.
+ * @param pfrc Pointer to variable where as_page_fault() return code will be stored.
  *
  * @return PTE on success, NULL otherwise.
  */
-pte_t *find_mapping_and_check(__address badvaddr)
+pte_t *find_mapping_and_check(__address badvaddr, istate_t *istate, int *pfrc)
 {
 	entry_hi_t hi;
@@ -351,4 +398,6 @@
 		return pte;
 	} else {
+		int rc;
+		
 		/*
 		 * Mapping not found in page tables.
@@ -356,5 +405,6 @@
 		 */
 		page_table_unlock(AS, true);
-		if (as_page_fault(badvaddr)) {
+		switch (rc = as_page_fault(badvaddr, istate)) {
+		case AS_PF_OK:
 			/*
 			 * The higher-level page fault handler succeeded,
@@ -365,8 +415,18 @@
 			ASSERT(pte && pte->p);
 			return pte;
-		} else {
+			break;
+		case AS_PF_DEFER:
+			page_table_lock(AS, true);
+			*pfrc = AS_PF_DEFER;
+			return NULL;
+			break;
+		case AS_PF_FAULT:
 			page_table_lock(AS, true);
 			printf("Page fault.\n");
+			*pfrc = AS_PF_FAULT;
 			return NULL;
+			break;
+		default:
+			panic("unexpected rc (%d)\n", rc);
 		}
 		
Index: arch/ppc32/include/exception.h
===================================================================
--- arch/ppc32/include/exception.h	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ arch/ppc32/include/exception.h	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -77,3 +77,8 @@
 };
 
+static inline void istate_set_retaddr(istate_t *istate, __address retaddr)
+{
+	/* TODO */
+}
+
 #endif
Index: arch/ppc32/include/interrupt.h
===================================================================
--- arch/ppc32/include/interrupt.h	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ arch/ppc32/include/interrupt.h	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -30,4 +30,6 @@
 #define __ppc32_INTERRUPT_H__
 
+#include <arch/exception.h>
+
 #define IRQ_COUNT	1
 #define IVT_ITEMS   15
Index: arch/ppc32/src/asm.S
===================================================================
--- arch/ppc32/src/asm.S	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ arch/ppc32/src/asm.S	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -36,4 +36,8 @@
 .global memsetb
 .global memcpy
+.global memcpy_from_uspace
+.global memcpy_to_uspace
+.global memcpy_from_uspace_failover_address
+.global memcpy_to_uspace_failover_address
 
 userspace_asm:
@@ -234,4 +238,7 @@
 
 memcpy:
+memcpy_from_uspace:
+memcpy_to_uspace:
+
 	srwi. r7, r5, 3
 	addi r6, r3, -4
@@ -294,2 +301,6 @@
 	mtctr r7
 	b 1b
+
+memcpy_from_uspace_failover_address:
+memcpy_to_uspace_failover_address:
+	b memcpy_from_uspace_failover_address
Index: arch/ppc32/src/mm/tlb.c
===================================================================
--- arch/ppc32/src/mm/tlb.c	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ arch/ppc32/src/mm/tlb.c	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -69,8 +69,10 @@
  *
  * @param badvaddr Faulting virtual address.
+ * @param istate Pointer to interrupted state.
+ * @param pfrc Pointer to variable where as_page_fault() return code will be stored.
  * @return         PTE on success, NULL otherwise.
  *
  */
-static pte_t *find_mapping_and_check(__address badvaddr)
+static pte_t *find_mapping_and_check(__address badvaddr, istate_t *istate, int *pfcr)
 {
 	/*
@@ -85,4 +87,6 @@
 		return pte;
 	} else {
+		int rc;
+	
 		/*
 		 * Mapping not found in page tables.
@@ -90,5 +94,6 @@
 		 */
 		page_table_unlock(AS, true);
-		if (as_page_fault(badvaddr)) {
+		switch (rc = as_page_fault(badvaddr, istate)) {
+		case AS_PF_OK:
 			/*
 			 * The higher-level page fault handler succeeded,
@@ -99,10 +104,20 @@
 			ASSERT((pte) && (pte->p));
 			return pte;
-		} else {
+			break;
+		case AS_PF_DEFER:
+			page_table_lock(AS, true);
+			*pfcr = rc;
+			return NULL;
+			break;
+		case AS_PF_FAULT:
 			page_table_lock(AS, true);
 			printf("Page fault.\n");
+			*pfcr = rc;
 			return NULL;
-		}
-		
+			break;
+		default:
+			panic("unexpected rc (%d)\n", rc);
+			break;
+		}	
 	}
 }
@@ -140,4 +155,5 @@
 	__u32 hash;
 	__u32 i;
+	int pfcr;
 	
 	if (data) {
@@ -155,7 +171,22 @@
 	page_table_lock(AS, true);
 	
-	pte = find_mapping_and_check(badvaddr);
-	if (!pte)
-		goto fail;
+	pte = find_mapping_and_check(badvaddr, istate, &pfcr);
+	if (!pte) {
+		switch (pfcr) {
+		case AS_PF_FAULT:
+			goto fail;
+			break;
+		case AS_PF_DEFER:
+			/*
+		 	 * The page fault came during copy_from_uspace()
+			 * or copy_to_uspace().
+			 */
+			page_table_unlock(AS, true);
+			return;
+		default:
+			panic("Unexpected pfrc (%d)\n", pfcr);
+			break;
+		}
+	}
 
 	/* Record access to PTE */
Index: arch/sparc64/include/interrupt.h
===================================================================
--- arch/sparc64/include/interrupt.h	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ arch/sparc64/include/interrupt.h	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -31,4 +31,5 @@
 
 #include <typedefs.h>
+#include <arch/types.h>
 
 #define IRQ_COUNT	1	/* TODO */
@@ -44,4 +45,12 @@
 #define trap_virtual_eoi()
 
+struct istate {
+};
+
+static inline void istate_set_retaddr(istate_t *istate, __address retaddr)
+{
+	/* TODO */
+}
+
 extern void interrupt_register(int n, const char *name, iroutine f);
 
Index: arch/sparc64/src/asm.S
===================================================================
--- arch/sparc64/src/asm.S	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ arch/sparc64/src/asm.S	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -30,8 +30,20 @@
 
 .global memcpy
+.global memcpy_from_uspace
+.global memcpy_to_uspace
+.global memcpy_from_uspace_failover_address
+.global memcpy_to_uspace_failover_address
 .global memsetb
 
 memcpy:
+memcpy_from_uspace:
+memcpy_to_uspace:
+
 	b _memcpy
+	nop
+
+memcpy_from_uspace_failover_address:
+memcpy_to_uspace_failover_address:
+	b memcpy_from_uspace_failover_address
 	nop
 
Index: generic/include/interrupt.h
===================================================================
--- generic/include/interrupt.h	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ generic/include/interrupt.h	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -32,4 +32,5 @@
 #include <arch/interrupt.h>
 #include <typedefs.h>
+#include <arch/types.h>
 
 #ifndef IVT_ITEMS
Index: generic/include/mm/as.h
===================================================================
--- generic/include/mm/as.h	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ generic/include/mm/as.h	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -64,4 +64,8 @@
 #define AS_AREA_ATTR_PARTIAL	1	/* Not fully initialized area. */
 
+#define AS_PF_FAULT		0	/**< The page fault was not resolved by asp_page_fault(). */
+#define AS_PF_OK		1	/**< The page fault was resolved by as_page_fault(). */
+#define AS_PF_DEFER		2	/**< The page fault was caused by memcpy_from_uspace(). */
+
 /** Address space area structure.
  *
@@ -122,5 +126,5 @@
 int as_area_send(task_id_t dst_id, __address base);
 extern void as_set_mapping(as_t *as, __address page, __address frame);
-extern int as_page_fault(__address page);
+extern int as_page_fault(__address page, istate_t *istate);
 extern void as_switch(as_t *old, as_t *new);
 extern void as_free(as_t *as);
Index: generic/include/mm/page.h
===================================================================
--- generic/include/mm/page.h	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ generic/include/mm/page.h	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -61,15 +61,4 @@
 #define PAGE_GLOBAL		(1<<PAGE_GLOBAL_SHIFT)
 
-/* TODO - check that userspace is OK, platform specific functions etc */
-static inline void copy_to_uspace(void *dst, void *src, count_t cnt)
-{
-	memcpy(dst, src, cnt);
-} 
-
-static inline void copy_from_uspace(void *dst, void *src, count_t cnt)
-{
-	memcpy(dst, src, cnt);
-}
-
 /** Operations to manipulate page mappings. */
 struct page_mapping_operations {
Index: generic/include/proc/thread.h
===================================================================
--- generic/include/proc/thread.h	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ generic/include/proc/thread.h	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -92,4 +92,10 @@
 	volatile int timeout_pending;		/**< Flag signalling sleep timeout in progress. */
 
+	/** True if this thread is executing copy_from_uspace(). False otherwise. */
+	bool in_copy_from_uspace;
+	/** True if this thread is executing copy_to_uspace(). False otherwise. */
+	bool in_copy_to_uspace;
+
+
 	fpu_context_t *saved_fpu_context;
 	int fpu_context_exists;
Index: generic/include/syscall/copy.h
===================================================================
--- generic/include/syscall/copy.h	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
+++ generic/include/syscall/copy.h	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2006 Jakub Jermar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - 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.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+#ifndef __COPY_H__
+#define __COPY_H__
+
+#include <typedefs.h>
+
+/** Label within memcpy_from_uspace() that contains return -1. */
+extern char memcpy_from_uspace_failover_address;
+
+/** Label within memcpy_to_uspace() that contains return -1. */
+extern char memcpy_to_uspace_failover_address;
+
+extern int copy_from_uspace(void *dst, void *uspace_src, size_t size);
+extern int copy_to_uspace(void *dst_uspace, void *src, size_t size);
+
+/*
+ * This interface must be implemented by each architecture.
+ */
+extern int memcpy_from_uspace(void *dst, void *uspace_src, size_t size);
+extern int memcpy_to_uspace(void *uspace_dst, void *src, size_t size);
+
+#endif
Index: generic/src/ddi/ddi.c
===================================================================
--- generic/src/ddi/ddi.c	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ generic/src/ddi/ddi.c	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -41,7 +41,7 @@
 #include <security/cap.h>
 #include <mm/frame.h>
-#include <mm/page.h>
 #include <mm/as.h>
 #include <synch/spinlock.h>
+#include <syscall/copy.h>
 #include <arch.h>
 #include <align.h>
@@ -184,6 +184,10 @@
 {
 	ddi_memarg_t arg;
-	
-	copy_from_uspace(&arg, uspace_mem_arg, sizeof(ddi_memarg_t));
+	int rc;
+	
+	rc = copy_from_uspace(&arg, uspace_mem_arg, sizeof(ddi_memarg_t));
+	if (rc != 0)
+		return (__native) rc;
+		
 	return (__native) ddi_physmem_map((task_id_t) arg.task_id, ALIGN_DOWN((__address) arg.phys_base, FRAME_SIZE),
 					  ALIGN_DOWN((__address) arg.virt_base, PAGE_SIZE), (count_t) arg.pages,
@@ -200,6 +204,10 @@
 {
 	ddi_ioarg_t arg;
-	
-	copy_from_uspace(&arg, uspace_io_arg, sizeof(ddi_ioarg_t));
+	int rc;
+	
+	rc = copy_from_uspace(&arg, uspace_io_arg, sizeof(ddi_ioarg_t));
+	if (rc != 0)
+		return (__native) rc;
+		
 	return (__native) ddi_iospace_enable((task_id_t) arg.task_id, (__address) arg.ioaddr, (size_t) arg.size);
 }
Index: generic/src/ipc/irq.c
===================================================================
--- generic/src/ipc/irq.c	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ generic/src/ipc/irq.c	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -48,4 +48,5 @@
 #include <ipc/irq.h>
 #include <atomic.h>
+#include <syscall/copy.h>
 
 typedef struct {
@@ -121,7 +122,12 @@
 	irq_code_t *code;
 	irq_cmd_t *ucmds;
+	int rc;
 
 	code = malloc(sizeof(*code), 0);
-	copy_from_uspace(code, ucode, sizeof(*code));
+	rc = copy_from_uspace(code, ucode, sizeof(*code));
+	if (rc != 0) {
+		free(code);
+		return NULL;
+	}
 	
 	if (code->cmdcount > IRQ_MAX_PROG_SIZE) {
@@ -131,5 +137,10 @@
 	ucmds = code->cmds;
 	code->cmds = malloc(sizeof(code->cmds[0]) * (code->cmdcount), 0);
-	copy_from_uspace(code->cmds, ucmds, sizeof(code->cmds[0]) * (code->cmdcount));
+	rc = copy_from_uspace(code->cmds, ucmds, sizeof(code->cmds[0]) * (code->cmdcount));
+	if (rc != 0) {
+		free(code->cmds);
+		free(code);
+		return NULL;
+	}
 
 	return code;
Index: generic/src/ipc/sysipc.c
===================================================================
--- generic/src/ipc/sysipc.c	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ generic/src/ipc/sysipc.c	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -29,7 +29,6 @@
 #include <arch.h>
 #include <proc/task.h>
-
+#include <proc/thread.h>
 #include <errno.h>
-#include <mm/page.h>
 #include <memstr.h>
 #include <debug.h>
@@ -39,8 +38,6 @@
 #include <ipc/ipcrsc.h>
 #include <arch/interrupt.h>
-
 #include <print.h>
-#include <arch.h>
-#include <proc/thread.h>
+#include <syscall/copy.h>
 
 #define GET_CHECK_PHONE(phone,phoneid,err) { \
@@ -229,7 +226,10 @@
 	phone_t *phone;
 	int res;
+	int rc;
 
 	ipc_call_static_init(&call);
-	copy_from_uspace(&call.data.args, &question->args, sizeof(call.data.args));
+	rc = copy_from_uspace(&call.data.args, &question->args, sizeof(call.data.args));
+	if (rc != 0)
+		return (__native) rc;
 
 	GET_CHECK_PHONE(phone, phoneid, return ENOENT);
@@ -241,5 +241,7 @@
 		IPC_SET_RETVAL(call.data, res);
 
-	STRUCT_TO_USPACE(&reply->args, &call.data.args);
+	rc = STRUCT_TO_USPACE(&reply->args, &call.data.args);
+	if (rc != 0)
+		return rc;
 
 	return 0;
@@ -298,4 +300,5 @@
 	phone_t *phone;
 	int res;
+	int rc;
 
 	if (check_call_limit())
@@ -305,5 +308,7 @@
 
 	call = ipc_call_alloc(0);
-	copy_from_uspace(&call->data.args, &data->args, sizeof(call->data.args));
+	rc = copy_from_uspace(&call->data.args, &data->args, sizeof(call->data.args));
+	if (rc != 0)
+		return (__native) rc;
 	if (!(res=request_preprocess(call)))
 		ipc_call(phone, call);
@@ -394,4 +399,5 @@
 	ipc_data_t saved_data;
 	int saveddata = 0;
+	int rc;
 
 	call = get_call(callid);
@@ -403,6 +409,8 @@
 		saveddata = 1;
 	}
-	copy_from_uspace(&call->data.args, &data->args, 
+	rc = copy_from_uspace(&call->data.args, &data->args, 
 			 sizeof(call->data.args));
+	if (rc != 0)
+		return rc;
 
 	answer_preprocess(call, saveddata ? &saved_data : NULL);
Index: generic/src/mm/as.c
===================================================================
--- generic/src/mm/as.c	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ generic/src/mm/as.c	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -58,4 +58,5 @@
 #include <adt/btree.h>
 #include <proc/task.h>
+#include <proc/thread.h>
 #include <arch/asm.h>
 #include <panic.h>
@@ -69,4 +70,6 @@
 #include <arch/types.h>
 #include <typedefs.h>
+#include <syscall/copy.h>
+#include <arch/interrupt.h>
 
 as_operations_t *as_operations = NULL;
@@ -478,8 +481,9 @@
  *
  * @param page Faulting page.
- *
- * @return 0 on page fault, 1 on success.
- */
-int as_page_fault(__address page)
+ * @param istate Pointer to interrupted state.
+ *
+ * @return 0 on page fault, 1 on success or 2 if the fault was caused by copy_to_uspace() or copy_from_uspace().
+ */
+int as_page_fault(__address page, istate_t *istate)
 {
 	pte_t *pte;
@@ -497,5 +501,5 @@
 		 */
 		spinlock_unlock(&AS->lock);
-		return 0;
+		goto page_fault;
 	}
 
@@ -507,5 +511,5 @@
 		spinlock_unlock(&area->lock);
 		spinlock_unlock(&AS->lock);
-		return 0;		
+		goto page_fault;		
 	}
 
@@ -555,5 +559,21 @@
 	spinlock_unlock(&area->lock);
 	spinlock_unlock(&AS->lock);
-	return 1;
+	return AS_PF_OK;
+
+page_fault:
+	if (!THREAD)
+		return AS_PF_FAULT;
+	
+	if (THREAD->in_copy_from_uspace) {
+		THREAD->in_copy_from_uspace = false;
+		istate_set_retaddr(istate, (__address) &memcpy_from_uspace_failover_address);
+	} else if (THREAD->in_copy_to_uspace) {
+		THREAD->in_copy_to_uspace = false;
+		istate_set_retaddr(istate, (__address) &memcpy_to_uspace_failover_address);
+	} else {
+		return AS_PF_FAULT;
+	}
+
+	return AS_PF_DEFER;
 }
 
@@ -885,6 +905,9 @@
 {
 	as_area_acptsnd_arg_t arg;
-	
-	copy_from_uspace(&arg, uspace_accept_arg, sizeof(as_area_acptsnd_arg_t));
+	int rc;
+	
+	rc = copy_from_uspace(&arg, uspace_accept_arg, sizeof(as_area_acptsnd_arg_t));
+	if (rc != 0)
+		return rc;
 	
 	if (!arg.size)
@@ -907,6 +930,9 @@
 {
 	as_area_acptsnd_arg_t arg;
-	
-	copy_from_uspace(&arg, uspace_send_arg, sizeof(as_area_acptsnd_arg_t));
+	int rc;
+	
+	rc = copy_from_uspace(&arg, uspace_send_arg, sizeof(as_area_acptsnd_arg_t));
+	if (rc != 0)
+		return rc;
 
 	if (!arg.size)
Index: generic/src/mm/slab.c
===================================================================
--- generic/src/mm/slab.c	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ generic/src/mm/slab.c	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -176,5 +176,5 @@
 		slab = data + fsize - sizeof(*slab);
 	}
-		
+	
 	/* Fill in slab structures */
 	for (i=0; i < (1 << cache->order); i++)
@@ -278,6 +278,6 @@
 		/* Allow recursion and reclaiming
 		 * - this should work, as the slab control structures
-		 *   are small and do not need to allocte with anything
-		 *   other ten frame_alloc when they are allocating,
+		 *   are small and do not need to allocate with anything
+		 *   other than frame_alloc when they are allocating,
 		 *   that's why we should get recursion at most 1-level deep
 		 */
@@ -880,5 +880,5 @@
 
 	ASSERT(_slab_initialized);
-	ASSERT( size && size <= (1 << SLAB_MAX_MALLOC_W));
+	ASSERT(size && size <= (1 << SLAB_MAX_MALLOC_W));
 	
 	if (size < (1 << SLAB_MIN_MALLOC_W))
@@ -890,5 +890,4 @@
 }
 
-
 void free(void *obj)
 {
Index: generic/src/printf/vsnprintf.c
===================================================================
--- generic/src/printf/vsnprintf.c	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ generic/src/printf/vsnprintf.c	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -40,7 +40,7 @@
 
 /** Write string to given buffer.
- * Write at most data->size characters including trailing zero. According to C99 has snprintf to return number
+ * Write at most data->size characters including trailing zero. According to C99, snprintf() has to return number
  * of characters that would have been written if enough space had been available. Hence the return value is not
- * number of really printed characters but size of input string. Number of really used characters 
+ * number of really printed characters but size of the input string. Number of really used characters 
  * is stored in data->len.
  * @param str source string to print
@@ -91,4 +91,2 @@
 	return printf_core(fmt, &ps, ap);
 }
-
-
Index: generic/src/proc/task.c
===================================================================
--- generic/src/proc/task.c	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ generic/src/proc/task.c	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -49,5 +49,5 @@
 #include <print.h>
 #include <elf.h>
-
+#include <syscall/copy.h>
 
 #ifndef LOADED_PROG_STACK_PAGES_NO
@@ -171,5 +171,5 @@
  * @param uspace_task_id Userspace address of 8-byte buffer where to store current task ID.
  *
- * @return Always returns 0.
+ * @return 0 on success or an error code from @ref errno.h.
  */
 __native sys_task_get_id(task_id_t *uspace_task_id)
@@ -179,7 +179,5 @@
 	 * remains constant for the lifespan of the task.
 	 */
-	copy_to_uspace(uspace_task_id, &TASK->taskid, sizeof(TASK->taskid));
-
-	return 0;
+	return (__native) copy_to_uspace(uspace_task_id, &TASK->taskid, sizeof(TASK->taskid));
 }
 
Index: generic/src/proc/thread.c
===================================================================
--- generic/src/proc/thread.c	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ generic/src/proc/thread.c	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -61,4 +61,6 @@
 #include <debug.h>
 #include <main/uinit.h>
+#include <syscall/copy.h>
+#include <errno.h>
 
 char *thread_states[] = {"Invalid", "Running", "Sleeping", "Ready", "Entering", "Exiting"}; /**< Thread states */
@@ -304,4 +306,7 @@
 	t->sleep_queue = NULL;
 	t->timeout_pending = 0;
+
+	t->in_copy_from_uspace = false;
+	t->in_copy_to_uspace = false;
 	
 	t->rwlock_holder_type = RWLOCK_NONE;
@@ -463,9 +468,16 @@
 	uspace_arg_t *kernel_uarg;
 	__u32 tid;
-
-	copy_from_uspace(namebuf, uspace_name, THREAD_NAME_BUFLEN);
+	int rc;
+
+	rc = copy_from_uspace(namebuf, uspace_name, THREAD_NAME_BUFLEN);
+	if (rc != 0)
+		return (__native) rc;
 
 	kernel_uarg = (uspace_arg_t *) malloc(sizeof(uspace_arg_t), 0);	
-	copy_from_uspace(kernel_uarg, uspace_uarg, sizeof(uspace_arg_t));
+	rc = copy_from_uspace(kernel_uarg, uspace_uarg, sizeof(uspace_arg_t));
+	if (rc != 0) {
+		free(kernel_uarg);
+		return (__native) rc;
+	}
 
 	if ((t = thread_create(uinit, kernel_uarg, TASK, 0, namebuf))) {
@@ -477,5 +489,5 @@
 	}
 
-	return (__native) -1;
+	return (__native) ENOMEM;
 }
 
Index: generic/src/smp/ipi.c
===================================================================
--- generic/src/smp/ipi.c	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ generic/src/smp/ipi.c	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -44,8 +44,8 @@
  * @param ipi Message to broadcast.
  *
- * @bugs The decision whether to actually send the IPI must be based
- *	 on a different criterion. The current version has
- *	 problems when some of the detected CPUs are marked
- *	 disabled in machine configuration.
+ * @bug The decision whether to actually send the IPI must be based
+ * 	on a different criterion. The current version has
+ *	problems when some of the detected CPUs are marked
+ *	disabled in machine configuration.
  */
 void ipi_broadcast(int ipi)
Index: generic/src/synch/waitq.c
===================================================================
--- generic/src/synch/waitq.c	(revision 22cf454db08f8dc472db143a6567d89fb30cc29d)
+++ generic/src/synch/waitq.c	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -31,5 +31,5 @@
  * @brief	Wait queue.
  *
- * Wait queue is the basic synchronization primitive upon all
+ * Wait queue is the basic synchronization primitive upon which all
  * other synchronization primitives build.
  *
Index: generic/src/syscall/copy.c
===================================================================
--- generic/src/syscall/copy.c	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
+++ generic/src/syscall/copy.c	(revision e3c762cdcb8d0aafcf71fd9b78017dd48ac328e2)
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2006 Jakub Jermar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - 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.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
+ */
+
+/**
+ * @file	copy.c
+ * @brief	Copying between kernel and userspace.
+ *
+ * This file contains sanitized functions for copying data
+ * between kernel and userspace.
+ */
+
+#include <syscall/copy.h>
+#include <proc/thread.h>
+#include <mm/as.h>
+#include <macros.h>
+#include <arch.h>
+#include <errno.h>
+#include <typedefs.h>
+
+/** Copy data from userspace to kernel.
+ *
+ * Provisions are made to return value even after page fault.
+ *
+ * This function can be called only from syscall.
+ *
+ * @param dst Destination kernel address.
+ * @param uspace_src Source userspace address.
+ * @param size Size of the data to be copied.
+ *
+ * @return 0 on success or error code from @ref errno.h.
+ */
+int copy_from_uspace(void *dst, void *uspace_src, size_t size)
+{
+	ipl_t ipl;
+	int rc;
+	
+	ASSERT(THREAD);
+	ASSERT(!THREAD->in_copy_from_uspace);
+	
+	if (!KERNEL_ADDRESS_SPACE_SHADOWED) {
+		if (overlaps((__address) uspace_src, size,
+			KERNEL_ADDRESS_SPACE_START, KERNEL_ADDRESS_SPACE_END-KERNEL_ADDRESS_SPACE_START)) {
+			/*
+			 * The userspace source block conflicts with kernel address space.
+			 */
+			return EPERM;
+		}
+	}
+	
+	ipl = interrupts_disable();
+	THREAD->in_copy_from_uspace = true;
+	
+	rc = memcpy_from_uspace(dst, uspace_src, size);
+
+	THREAD->in_copy_from_uspace = false;
+
+	interrupts_restore(ipl);
+	return !rc ? EPERM : 0;
+}
+
+/** Copy data from kernel to userspace.
+ *
+ * Provisions are made to return value even after page fault.
+ *
+ * This function can be called only from syscall.
+ *
+ * @param uspace_dst Destination userspace address.
+ * @param uspace_src Source kernel address.
+ * @param size Size of the data to be copied.
+ *
+ * @return 0 on success or error code from @ref errno.h.
+ */
+int copy_to_uspace(void *uspace_dst, void *src, size_t size)
+{
+	ipl_t ipl;
+	int rc;
+	
+	ASSERT(THREAD);
+	ASSERT(!THREAD->in_copy_from_uspace);
+	
+	if (!KERNEL_ADDRESS_SPACE_SHADOWED) {
+		if (overlaps((__address) uspace_dst, size,
+			KERNEL_ADDRESS_SPACE_START, KERNEL_ADDRESS_SPACE_END-KERNEL_ADDRESS_SPACE_START)) {
+			/*
+			 * The userspace destination block conflicts with kernel address space.
+			 */
+			return EPERM;
+		}
+	}
+	
+	ipl = interrupts_disable();
+	THREAD->in_copy_from_uspace = true;
+	
+	rc = memcpy_to_uspace(uspace_dst, src, size);
+
+	THREAD->in_copy_from_uspace = false;
+
+	interrupts_restore(ipl);
+	return !rc ? EPERM : 0;
+}
