Index: kernel/generic/src/main/uinit.c
===================================================================
--- kernel/generic/src/main/uinit.c	(revision bec63799120b9ca58aeb87488126bc2768a57ee9)
+++ kernel/generic/src/main/uinit.c	(revision 357d9ddc7ba6ffcee059b550de69328607b1ce37)
@@ -60,17 +60,10 @@
 #endif
 
-	uspace_arg_t *uarg = arg;
-	uspace_arg_t local_uarg;
-
-	local_uarg.uspace_entry = uarg->uspace_entry;
-	local_uarg.uspace_stack = uarg->uspace_stack;
-	local_uarg.uspace_stack_size = uarg->uspace_stack_size;
-	local_uarg.uspace_uarg = uarg->uspace_uarg;
-	local_uarg.uspace_thread_function = USPACE_NULL;
-	local_uarg.uspace_thread_arg = USPACE_NULL;
-
+	uinit_arg_t *uarg = arg;
+	sysarg_t pc = uarg->pc;
+	sysarg_t sp = uarg->sp;
 	free(uarg);
 
-	userspace(&local_uarg);
+	userspace(pc, sp);
 }
 
Index: kernel/generic/src/proc/program.c
===================================================================
--- kernel/generic/src/proc/program.c	(revision bec63799120b9ca58aeb87488126bc2768a57ee9)
+++ kernel/generic/src/proc/program.c	(revision 357d9ddc7ba6ffcee059b550de69328607b1ce37)
@@ -53,4 +53,5 @@
 #include <syscall/copy.h>
 #include <proc/program.h>
+#include <userspace.h>
 
 /**
@@ -72,6 +73,5 @@
 errno_t program_create(as_t *as, uspace_addr_t entry_addr, char *name, program_t *prg)
 {
-	uspace_arg_t *kernel_uarg = (uspace_arg_t *)
-	    malloc(sizeof(uspace_arg_t));
+	uinit_arg_t *kernel_uarg = malloc(sizeof(uinit_arg_t));
 	if (!kernel_uarg)
 		return ENOMEM;
@@ -104,10 +104,6 @@
 	}
 
-	kernel_uarg->uspace_entry = entry_addr;
-	kernel_uarg->uspace_stack = virt;
-	kernel_uarg->uspace_stack_size = STACK_SIZE_USER;
-	kernel_uarg->uspace_thread_function = USPACE_NULL;
-	kernel_uarg->uspace_thread_arg = USPACE_NULL;
-	kernel_uarg->uspace_uarg = USPACE_NULL;
+	kernel_uarg->pc = entry_addr;
+	kernel_uarg->sp = arch_get_initial_sp(virt, STACK_SIZE_USER);
 
 	/*
Index: kernel/generic/src/proc/thread.c
===================================================================
--- kernel/generic/src/proc/thread.c	(revision bec63799120b9ca58aeb87488126bc2768a57ee9)
+++ kernel/generic/src/proc/thread.c	(revision 357d9ddc7ba6ffcee059b550de69328607b1ce37)
@@ -944,8 +944,12 @@
 
 /** Process syscall to create new thread.
- *
- */
-sys_errno_t sys_thread_create(uspace_ptr_uspace_arg_t uspace_uarg, uspace_ptr_char uspace_name,
-    size_t name_len, uspace_ptr_thread_id_t uspace_thread_id)
+ * The started thread will have initial pc and sp set to the exact values passed
+ * to the syscall. The kernel will not touch any stack data below the stack
+ * pointer, but some architectures may require some space to be available
+ * for use above it. See userspace() in kernel, and <libarch/thread.h> in libc.
+ *
+ */
+sys_errno_t sys_thread_create(sysarg_t pc, sysarg_t sp,
+    uspace_ptr_char uspace_name, size_t name_len)
 {
 	if (name_len > THREAD_NAME_BUFLEN - 1)
@@ -963,60 +967,36 @@
 	 * In case of success, kernel_uarg will be freed in uinit().
 	 */
-	uspace_arg_t *kernel_uarg =
-	    (uspace_arg_t *) malloc(sizeof(uspace_arg_t));
+	uinit_arg_t *kernel_uarg = malloc(sizeof(uinit_arg_t));
 	if (!kernel_uarg)
 		return (sys_errno_t) ENOMEM;
 
-	rc = copy_from_uspace(kernel_uarg, uspace_uarg, sizeof(uspace_arg_t));
-	if (rc != EOK) {
-		free(kernel_uarg);
-		return (sys_errno_t) rc;
-	}
+	kernel_uarg->pc = pc;
+	kernel_uarg->sp = sp;
+
+	// TODO: fix some unnecessary inconsistencies between architectures
 
 	thread_t *thread = thread_create(uinit, kernel_uarg, TASK,
 	    THREAD_FLAG_USPACE | THREAD_FLAG_NOATTACH, namebuf);
-	if (thread) {
-		if (uspace_thread_id) {
-			rc = copy_to_uspace(uspace_thread_id, &thread->tid,
-			    sizeof(thread->tid));
-			if (rc != EOK) {
-				/*
-				 * We have encountered a failure, but the thread
-				 * has already been created. We need to undo its
-				 * creation now.
-				 */
-
-				/*
-				 * The new thread structure is initialized, but
-				 * is still not visible to the system.
-				 * We can safely deallocate it.
-				 */
-				slab_free(thread_cache, thread);
-				free(kernel_uarg);
-
-				return (sys_errno_t) rc;
-			}
-		}
+	if (!thread) {
+		free(kernel_uarg);
+		return (sys_errno_t) ENOMEM;
+	}
 
 #ifdef CONFIG_UDEBUG
-		/*
-		 * Generate udebug THREAD_B event and attach the thread.
-		 * This must be done atomically (with the debug locks held),
-		 * otherwise we would either miss some thread or receive
-		 * THREAD_B events for threads that already existed
-		 * and could be detected with THREAD_READ before.
-		 */
-		udebug_thread_b_event_attach(thread, TASK);
+	/*
+	 * Generate udebug THREAD_B event and attach the thread.
+	 * This must be done atomically (with the debug locks held),
+	 * otherwise we would either miss some thread or receive
+	 * THREAD_B events for threads that already existed
+	 * and could be detected with THREAD_READ before.
+	 */
+	udebug_thread_b_event_attach(thread, TASK);
 #else
-		thread_attach(thread, TASK);
+	thread_attach(thread, TASK);
 #endif
-		thread_start(thread);
-		thread_put(thread);
-
-		return 0;
-	} else
-		free(kernel_uarg);
-
-	return (sys_errno_t) ENOMEM;
+	thread_start(thread);
+	thread_put(thread);
+
+	return (sys_errno_t) EOK;
 }
 
