Index: kernel/generic/include/ipc/ipc.h
===================================================================
--- kernel/generic/include/ipc/ipc.h	(revision eeb2bde2cd490c6feb4ee861d340112bfcb495af)
+++ kernel/generic/include/ipc/ipc.h	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
@@ -195,4 +195,10 @@
  */
 #define IPC_M_DATA_READ		7
+
+/** Debug the recipient.
+ * - ARG1 - specifies the debug method (from udebug_method_t)
+ * - other arguments are specific to the debug method
+ */
+#define IPC_M_DEBUG_ALL		8
 
 /* Well-known methods */
@@ -308,4 +314,6 @@
 extern void ipc_backsend_err(phone_t *, call_t *, unative_t);
 extern void ipc_print_task(task_id_t);
+extern void ipc_answerbox_slam_phones(answerbox_t *, bool);
+extern void ipc_cleanup_call_list(link_t *);
 
 extern answerbox_t *ipc_phone_0;
Index: kernel/generic/include/ipc/ipc_kbox.h
===================================================================
--- kernel/generic/include/ipc/ipc_kbox.h	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
+++ kernel/generic/include/ipc/ipc_kbox.h	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2008 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup genericipc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef KERN_IPC_KBOX_H_
+#define KERN_IPC_KBOX_H_
+
+#include <typedefs.h>
+
+extern int ipc_connect_kbox(task_id_t);
+extern void ipc_kbox_cleanup(void);
+
+#endif
+
+/** @}
+ */
Index: kernel/generic/include/ipc/sysipc.h
===================================================================
--- kernel/generic/include/ipc/sysipc.h	(revision eeb2bde2cd490c6feb4ee861d340112bfcb495af)
+++ kernel/generic/include/ipc/sysipc.h	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
@@ -58,4 +58,5 @@
     irq_code_t *ucode);
 unative_t sys_ipc_unregister_irq(inr_t inr, devno_t devno);
+unative_t sys_ipc_connect_kbox(sysarg64_t *task_id);
 
 #endif
Index: kernel/generic/include/proc/task.h
===================================================================
--- kernel/generic/include/proc/task.h	(revision eeb2bde2cd490c6feb4ee861d340112bfcb495af)
+++ kernel/generic/include/proc/task.h	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
@@ -53,4 +53,5 @@
 #include <mm/tlb.h>
 #include <proc/scheduler.h>
+#include <udebug/udebug.h>
 
 struct thread;
@@ -94,4 +95,18 @@
 	 */
 	atomic_t active_calls;
+
+#ifdef CONFIG_UDEBUG
+	/** Debugging stuff */
+	udebug_task_t udebug;
+
+	/** Kernel answerbox */
+	answerbox_t kernel_box;
+	/** Thread used to service kernel answerbox */
+	struct thread *kb_thread;
+	/** Kbox thread creation vs. begin of cleanup mutual exclusion */
+	mutex_t kb_cleanup_lock;
+	/** True if cleanup of kbox has already started */
+	bool kb_finished;
+#endif
 	
 	/** Architecture specific task data. */
Index: kernel/generic/include/proc/thread.h
===================================================================
--- kernel/generic/include/proc/thread.h	(revision eeb2bde2cd490c6feb4ee861d340112bfcb495af)
+++ kernel/generic/include/proc/thread.h	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
@@ -47,4 +47,5 @@
 #include <mm/tlb.h>
 #include <proc/uarg.h>
+#include <udebug/udebug.h>
 
 #define THREAD_STACK_SIZE	STACK_SIZE
@@ -204,4 +205,10 @@
 	/** Thread's kernel stack. */
 	uint8_t *kstack;
+
+#ifdef CONFIG_UDEBUG
+	/** Debugging stuff */
+	udebug_thread_t udebug;
+#endif
+
 } thread_t;
 
Index: kernel/generic/include/syscall/syscall.h
===================================================================
--- kernel/generic/include/syscall/syscall.h	(revision eeb2bde2cd490c6feb4ee861d340112bfcb495af)
+++ kernel/generic/include/syscall/syscall.h	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
@@ -79,4 +79,5 @@
 	
 	SYS_DEBUG_ENABLE_CONSOLE,
+	SYS_IPC_CONNECT_KBOX,
 	SYSCALL_END
 } syscall_t;
Index: kernel/generic/include/udebug/udebug.h
===================================================================
--- kernel/generic/include/udebug/udebug.h	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
+++ kernel/generic/include/udebug/udebug.h	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2008 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup generic	
+ * @{
+ */
+/** @file
+ */
+
+#ifndef KERN_UDEBUG_H_
+#define KERN_UDEBUG_H_
+
+#include <ipc/ipc.h>
+
+typedef enum { /* udebug_method_t */
+
+/** Start debugging the recipient.
+ * Causes all threads in the receiving task to stop. When they
+ * are all stoped, an answer with retval 0 is generated.
+ */
+UDEBUG_M_BEGIN = 1,
+
+/** Finish debugging the recipient.
+ * Answers all pending GO and GUARD messages.
+ */
+UDEBUG_M_END,
+
+/** Set which events should be captured.
+ */
+UDEBUG_M_SET_EVMASK,
+
+/** Make sure the debugged task is still there.
+ * This message is answered when the debugged task dies
+ * or the debugging session ends.
+ */
+UDEBUG_M_GUARD,
+
+/** Run a thread until a debugging event occurs.
+ * This message is answered when the thread stops
+ * in a debugging event.
+ *
+ * - ARG2 - id of the thread to run
+ */
+UDEBUG_M_GO,
+
+/** Stop a thread being debugged.
+ * Creates a special STOP event in the thread, causing
+ * it to answer a pending GO message (if any).
+ */
+UDEBUG_M_STOP,
+
+/** Read arguments of a syscall.
+ *
+ * - ARG2 - thread identification
+ * - ARG3 - destination address in the caller's address space
+ *
+ */
+UDEBUG_M_ARGS_READ,
+
+/** Read the list of the debugged tasks's threads.
+ *
+ * - ARG2 - destination address in the caller's address space
+ * - ARG3 - size of receiving buffer in bytes
+ *
+ * The kernel fills the buffer with a series of sysarg_t values
+ * (thread ids). On answer, the kernel will set:
+ *
+ * - ARG2 - number of bytes that were actually copied
+ * - ARG3 - number of bytes of the complete data
+ *
+ */
+UDEBUG_M_THREAD_READ,
+
+/** Read the debugged tasks's memory.
+ *
+ * - ARG2 - destination address in the caller's address space
+ * - ARG3 - source address in the recipient's address space
+ * - ARG4 - size of receiving buffer in bytes
+ *
+ */
+UDEBUG_M_MEM_READ,
+
+} udebug_method_t;
+
+				
+typedef enum {
+	UDEBUG_EVENT_FINISHED = 1,	/**< Debuging session has finished */
+	UDEBUG_EVENT_STOP,		/**< Stopped on DEBUG_STOP request */
+	UDEBUG_EVENT_SYSCALL_B,		/**< Before beginning syscall execution */
+	UDEBUG_EVENT_SYSCALL_E,		/**< After finishing syscall execution */
+	UDEBUG_EVENT_THREAD_B,		/**< The task created a new thread */
+	UDEBUG_EVENT_THREAD_E		/**< A thread exited */
+} udebug_event_t;
+
+#define UDEBUG_EVMASK(event) (1 << ((event) - 1))
+
+typedef enum {
+	UDEBUG_EM_FINISHED	= UDEBUG_EVMASK(UDEBUG_EVENT_FINISHED),
+	UDEBUG_EM_STOP		= UDEBUG_EVMASK(UDEBUG_EVENT_STOP),
+	UDEBUG_EM_SYSCALL_B	= UDEBUG_EVMASK(UDEBUG_EVENT_SYSCALL_B),
+	UDEBUG_EM_SYSCALL_E	= UDEBUG_EVMASK(UDEBUG_EVENT_SYSCALL_E),
+	UDEBUG_EM_THREAD_B	= UDEBUG_EVMASK(UDEBUG_EVENT_THREAD_B),
+	UDEBUG_EM_THREAD_E	= UDEBUG_EVMASK(UDEBUG_EVENT_THREAD_E),
+	UDEBUG_EM_ALL		=
+		UDEBUG_EVMASK(UDEBUG_EVENT_FINISHED) |
+		UDEBUG_EVMASK(UDEBUG_EVENT_STOP) |
+		UDEBUG_EVMASK(UDEBUG_EVENT_SYSCALL_B) |
+		UDEBUG_EVMASK(UDEBUG_EVENT_SYSCALL_E) |
+		UDEBUG_EVMASK(UDEBUG_EVENT_THREAD_B) |
+		UDEBUG_EVMASK(UDEBUG_EVENT_THREAD_E)
+} udebug_evmask_t;
+
+#ifdef KERNEL
+
+#include <synch/mutex.h>
+#include <arch/interrupt.h>
+#include <atomic.h>
+
+typedef enum {
+	/** Task is not being debugged */
+	UDEBUG_TS_INACTIVE,
+	/** BEGIN operation in progress (waiting for threads to stop) */
+	UDEBUG_TS_BEGINNING,
+	/** Debugger fully connected */
+	UDEBUG_TS_ACTIVE,
+	/** Task is shutting down, no more debug activities allowed */
+	UDEBUG_TS_SHUTDOWN
+} udebug_task_state_t;
+
+/** Debugging part of task_t structure.
+ */
+typedef struct {
+	/** Synchronize debug ops on this task / access to this structure */
+	mutex_t lock;
+	char *lock_owner;
+
+	udebug_task_state_t dt_state;
+	call_t *begin_call;
+	int not_stoppable_count;
+	struct task *debugger;
+	udebug_evmask_t evmask;
+} udebug_task_t;
+
+/** Debugging part of thread_t structure.
+ */
+typedef struct {
+	/** 
+	 * Prevent deadlock with udebug_before_thread_runs() in interrupt
+	 * handler, without actually disabling interrupts.
+	 * ==0 means "unlocked", >0 means "locked"
+	 */
+	atomic_t int_lock;
+
+	/** Synchronize debug ops on this thread / access to this structure */
+	mutex_t lock;
+
+	waitq_t go_wq;
+	call_t *go_call;
+	unative_t syscall_args[6];
+
+	/** What type of event are we stopped in or 0 if none */
+	udebug_event_t cur_event;	
+	bool stop;
+	bool stoppable;
+	bool debug_active; /**< In a debugging session */
+} udebug_thread_t;
+
+struct task;
+struct thread;
+
+void udebug_task_init(udebug_task_t *ut);
+void udebug_thread_initialize(udebug_thread_t *ut);
+
+void udebug_syscall_event(unative_t a1, unative_t a2, unative_t a3,
+    unative_t a4, unative_t a5, unative_t a6, unative_t id, unative_t rc,
+    bool end_variant);
+
+void udebug_thread_b_event(struct thread *t);
+void udebug_thread_e_event(void);
+
+void udebug_stoppable_begin(void);
+void udebug_stoppable_end(void);
+
+void udebug_before_thread_runs(void);
+
+int udebug_task_cleanup(struct task *ta);
+
+#endif
+
+#endif
+
+/** @}
+ */
Index: kernel/generic/include/udebug/udebug_ipc.h
===================================================================
--- kernel/generic/include/udebug/udebug_ipc.h	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
+++ kernel/generic/include/udebug/udebug_ipc.h	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2008 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup generic	
+ * @{
+ */
+/** @file
+ */
+
+#ifndef KERN_UDEBUG_IPC_H_
+#define KERN_UDEBUG_IPC_H_
+
+#include <ipc/ipc.h>
+
+int udebug_request_preprocess(call_t *call, phone_t *phone);
+void udebug_call_receive(call_t *call);
+
+
+#endif
+
+/** @}
+ */
Index: kernel/generic/include/udebug/udebug_ops.h
===================================================================
--- kernel/generic/include/udebug/udebug_ops.h	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
+++ kernel/generic/include/udebug/udebug_ops.h	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2008 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup generic	
+ * @{
+ */
+/** @file
+ */
+
+#ifndef KERN_UDEBUG_OPS_H_
+#define KERN_UDEBUG_OPS_H_
+
+#include <ipc/ipc.h>
+
+int udebug_begin(call_t *call);
+int udebug_end(void);
+int udebug_set_evmask(udebug_evmask_t mask);
+
+int udebug_go(thread_t *t, call_t *call);
+int udebug_stop(thread_t *t, call_t *call);
+
+int udebug_thread_read(void **buffer, size_t buf_size, size_t *n);
+int udebug_args_read(thread_t *t, void **buffer);
+
+int udebug_mem_read(unative_t uspace_addr, size_t n, void **buffer);
+
+#endif
+
+/** @}
+ */
Index: kernel/generic/src/ipc/ipc.c
===================================================================
--- kernel/generic/src/ipc/ipc.c	(revision eeb2bde2cd490c6feb4ee861d340112bfcb495af)
+++ kernel/generic/src/ipc/ipc.c	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
@@ -44,4 +44,5 @@
 #include <synch/synch.h>
 #include <ipc/ipc.h>
+#include <ipc/ipc_kbox.h>
 #include <errno.h>
 #include <mm/slab.h>
@@ -52,4 +53,5 @@
 
 #include <print.h>
+#include <console/console.h>
 #include <proc/thread.h>
 #include <arch/interrupt.h>
@@ -428,5 +430,5 @@
  * @param lst		Head of the list to be cleaned up.
  */
-static void ipc_cleanup_call_list(link_t *lst)
+void ipc_cleanup_call_list(link_t *lst)
 {
 	call_t *call;
@@ -443,4 +445,71 @@
 }
 
+/** Disconnects all phones connected to an answerbox.
+ *
+ * @param box		Answerbox to disconnect phones from.
+ * @param notify_box	If true, the answerbox will get a hangup message for
+ *			each disconnected phone.
+ */
+void ipc_answerbox_slam_phones(answerbox_t *box, bool notify_box)
+{
+	phone_t *phone;
+	DEADLOCK_PROBE_INIT(p_phonelck);
+	ipl_t ipl;
+	call_t *call;
+
+	call = notify_box ? ipc_call_alloc(0) : NULL;
+
+	/* Disconnect all phones connected to our answerbox */
+restart_phones:
+	ipl = interrupts_disable();
+	spinlock_lock(&box->lock);
+	while (!list_empty(&box->connected_phones)) {
+		phone = list_get_instance(box->connected_phones.next,
+		    phone_t, link);
+		if (SYNCH_FAILED(mutex_trylock(&phone->lock))) {
+			spinlock_unlock(&box->lock);
+			interrupts_restore(ipl);
+			DEADLOCK_PROBE(p_phonelck, DEADLOCK_THRESHOLD);
+			goto restart_phones;
+		}
+		
+		/* Disconnect phone */
+		ASSERT(phone->state == IPC_PHONE_CONNECTED);
+
+		list_remove(&phone->link);
+		phone->state = IPC_PHONE_SLAMMED;
+
+		if (notify_box) {
+			mutex_unlock(&phone->lock);
+			spinlock_unlock(&box->lock);
+			interrupts_restore(ipl);
+
+			/*
+			 * Send one message to the answerbox for each
+			 * phone. Used to make sure the kbox thread
+			 * wakes up after the last phone has been
+			 * disconnected.
+			 */
+			IPC_SET_METHOD(call->data, IPC_M_PHONE_HUNGUP);
+			call->flags |= IPC_CALL_DISCARD_ANSWER;
+			_ipc_call(phone, box, call);
+
+			/* Allocate another call in advance */
+			call = ipc_call_alloc(0);
+
+			/* Must start again */
+			goto restart_phones;
+		}
+
+		mutex_unlock(&phone->lock);
+	}
+
+	spinlock_unlock(&box->lock);
+	interrupts_restore(ipl);
+
+	/* Free unused call */
+	if (call) ipc_call_free(call);
+}
+
 /** Cleans up all IPC communication of the current task.
  *
@@ -452,6 +521,4 @@
 	int i;
 	call_t *call;
-	phone_t *phone;
-	DEADLOCK_PROBE_INIT(p_phonelck);
 
 	/* Disconnect all our phones ('ipc_phone_hangup') */
@@ -462,25 +529,14 @@
 	ipc_irq_cleanup(&TASK->answerbox);
 
-	/* Disconnect all phones connected to our answerbox */
-restart_phones:
+	/* Disconnect all phones connected to our regular answerbox */
+	ipc_answerbox_slam_phones(&TASK->answerbox, false);
+
+#ifdef CONFIG_UDEBUG
+	/* Clean up kbox thread and communications */
+	ipc_kbox_cleanup();
+#endif
+
+	/* Answer all messages in 'calls' and 'dispatched_calls' queues */
 	spinlock_lock(&TASK->answerbox.lock);
-	while (!list_empty(&TASK->answerbox.connected_phones)) {
-		phone = list_get_instance(TASK->answerbox.connected_phones.next,
-		    phone_t, link);
-		if (SYNCH_FAILED(mutex_trylock(&phone->lock))) {
-			spinlock_unlock(&TASK->answerbox.lock);
-			DEADLOCK_PROBE(p_phonelck, DEADLOCK_THRESHOLD);
-			goto restart_phones;
-		}
-		
-		/* Disconnect phone */
-		ASSERT(phone->state == IPC_PHONE_CONNECTED);
-		phone->state = IPC_PHONE_SLAMMED;
-		list_remove(&phone->link);
-
-		mutex_unlock(&phone->lock);
-	}
-
-	/* Answer all messages in 'calls' and 'dispatched_calls' queues */
 	ipc_cleanup_call_list(&TASK->answerbox.dispatched_calls);
 	ipc_cleanup_call_list(&TASK->answerbox.calls);
Index: kernel/generic/src/ipc/ipc_kbox.c
===================================================================
--- kernel/generic/src/ipc/ipc_kbox.c	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
+++ kernel/generic/src/ipc/ipc_kbox.c	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2008 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup genericipc
+ * @{
+ */
+/** @file
+ */
+
+#include <synch/synch.h>
+#include <synch/spinlock.h>
+#include <synch/mutex.h>
+#include <ipc/ipc.h>
+#include <ipc/ipcrsc.h>
+#include <arch.h>
+#include <errno.h>
+#include <print.h>
+#include <udebug/udebug_ipc.h>
+#include <ipc/ipc_kbox.h>
+
+void ipc_kbox_cleanup(void)
+{
+	bool have_kb_thread;
+
+	/* Only hold kb_cleanup_lock while setting kb_finished - this is enough */
+	mutex_lock(&TASK->kb_cleanup_lock);
+	TASK->kb_finished = true;
+	mutex_unlock(&TASK->kb_cleanup_lock);
+
+	have_kb_thread = (TASK->kb_thread != NULL);
+
+	/* From now on nobody will try to connect phones or attach kbox threads */
+
+	/*
+	 * Disconnect all phones connected to our kbox. Passing true for
+	 * notify_box causes a HANGUP message to be inserted for each
+	 * disconnected phone. This ensures the kbox thread is going to
+	 * wake up and terminate.
+	 */
+	ipc_answerbox_slam_phones(&TASK->kernel_box, have_kb_thread);
+	
+	if (have_kb_thread) {
+		printf("join kb_thread..\n");
+		thread_join(TASK->kb_thread);
+		thread_detach(TASK->kb_thread);
+		printf("join done\n");
+		TASK->kb_thread = NULL;
+	}
+
+	/* Answer all messages in 'calls' and 'dispatched_calls' queues */
+	spinlock_lock(&TASK->kernel_box.lock);
+	ipc_cleanup_call_list(&TASK->kernel_box.dispatched_calls);
+	ipc_cleanup_call_list(&TASK->kernel_box.calls);
+	spinlock_unlock(&TASK->kernel_box.lock);
+}
+
+
+static void kbox_thread_proc(void *arg)
+{
+	call_t *call;
+	int method;
+	bool done;
+	ipl_t ipl;
+
+	(void)arg;
+	printf("kbox_thread_proc()\n");
+	done = false;
+
+	while (!done) {
+		//printf("kbox: wait for call\n");
+		call = ipc_wait_for_call(&TASK->kernel_box, SYNCH_NO_TIMEOUT,
+			SYNCH_FLAGS_NONE);
+
+		if (call != NULL) {
+			method = IPC_GET_METHOD(call->data);
+
+			if (method == IPC_M_DEBUG_ALL) {
+				udebug_call_receive(call);
+			}
+
+			if (method == IPC_M_PHONE_HUNGUP) {
+				printf("kbox: handle hangup message\n");
+
+				/* Was it our debugger, who hung up? */
+				if (call->sender == TASK->udebug.debugger) {
+					/* Terminate debugging session (if any) */
+					printf("kbox: terminate debug session\n");
+					ipl = interrupts_disable();
+					spinlock_lock(&TASK->lock);
+					udebug_task_cleanup(TASK);
+					spinlock_unlock(&TASK->lock);
+					interrupts_restore(ipl);
+				} else {
+					printf("kbox: was not debugger\n");
+				}
+
+				printf("kbox: continue with hangup message\n");
+				IPC_SET_RETVAL(call->data, 0);
+				ipc_answer(&TASK->kernel_box, call);
+
+				ipl = interrupts_disable();
+				spinlock_lock(&TASK->lock);
+				spinlock_lock(&TASK->answerbox.lock);
+				if (list_empty(&TASK->answerbox.connected_phones)) {
+					/* Last phone has been disconnected */
+					TASK->kb_thread = NULL;
+					done = true;
+					printf("phone list is empty\n");
+				}
+				spinlock_unlock(&TASK->answerbox.lock);
+				spinlock_unlock(&TASK->lock);
+				interrupts_restore(ipl);
+			}
+		}
+	}
+
+	printf("kbox: finished\n");
+}
+
+
+/**
+ * Connect phone to a task kernel-box specified by id.
+ *
+ * Note that this is not completely atomic. For optimisation reasons,
+ * The task might start cleaning up kbox after the phone has been connected
+ * and before a kbox thread has been created. This must be taken into account
+ * in the cleanup code.
+ *
+ * @return 		Phone id on success, or negative error code.
+ */
+int ipc_connect_kbox(task_id_t taskid)
+{
+	int newphid;
+	task_t *ta;
+	thread_t *kb_thread;
+	ipl_t ipl;
+
+	ipl = interrupts_disable();
+	spinlock_lock(&tasks_lock);
+
+	ta = task_find_by_id(taskid);
+	if (ta == NULL) {
+		spinlock_unlock(&tasks_lock);
+		interrupts_restore(ipl);
+		return ENOENT;
+	}
+
+	atomic_inc(&ta->refcount);
+
+	spinlock_unlock(&tasks_lock);
+	interrupts_restore(ipl);
+
+	mutex_lock(&ta->kb_cleanup_lock);
+
+	if (atomic_predec(&ta->refcount) == 0) {
+		mutex_unlock(&ta->kb_cleanup_lock);
+		task_destroy(ta);
+		return ENOENT;
+	}
+
+	if (ta->kb_finished != false) {
+		mutex_unlock(&ta->kb_cleanup_lock);
+		return EINVAL;
+	}
+
+	newphid = phone_alloc();
+	if (newphid < 0) {
+		mutex_unlock(&ta->kb_cleanup_lock);
+		return ELIMIT;
+	}
+
+	/* Connect the newly allocated phone to the kbox */
+	ipc_phone_connect(&TASK->phones[newphid], &ta->kernel_box);
+
+	if (ta->kb_thread != NULL) {
+		mutex_unlock(&ta->kb_cleanup_lock);
+		return newphid;
+	}
+
+	/* Create a kbox thread */
+	kb_thread = thread_create(kbox_thread_proc, NULL, ta, 0, "kbox", false);
+	if (!kb_thread) {
+		mutex_unlock(&ta->kb_cleanup_lock);
+		return ENOMEM;
+	}
+
+	ta->kb_thread = kb_thread;
+	thread_ready(kb_thread);
+
+	mutex_unlock(&ta->kb_cleanup_lock);
+
+	return newphid;
+}
+
+/** @}
+ */
Index: kernel/generic/src/ipc/sysipc.c
===================================================================
--- kernel/generic/src/ipc/sysipc.c	(revision eeb2bde2cd490c6feb4ee861d340112bfcb495af)
+++ kernel/generic/src/ipc/sysipc.c	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
@@ -43,4 +43,6 @@
 #include <ipc/irq.h>
 #include <ipc/ipcrsc.h>
+#include <ipc/ipc_kbox.h>
+#include <udebug/udebug_ipc.h>
 #include <arch/interrupt.h>
 #include <print.h>
@@ -296,8 +298,9 @@
  *
  * @param call		Call structure with the request.
+ * @param phone		Phone that the call will be sent through.
  *
  * @return 		Return 0 on success, ELIMIT or EPERM on error.
  */
-static int request_preprocess(call_t *call)
+static int request_preprocess(call_t *call, phone_t *phone)
 {
 	int newphid;
@@ -341,4 +344,8 @@
 		}
 		break;
+#ifdef CONFIG_UDEBUG
+	case IPC_M_DEBUG_ALL:
+		return udebug_request_preprocess(call, phone);
+#endif
 	default:
 		break;
@@ -370,4 +377,5 @@
 	if (call->buffer) {
 		/* This must be an affirmative answer to IPC_M_DATA_READ. */
+		/* or IPC_M_DEBUG_ALL/UDEBUG_M_MEM_READ... */
 		uintptr_t dst = IPC_GET_ARG1(call->data);
 		size_t size = IPC_GET_ARG2(call->data);
@@ -400,5 +408,11 @@
 		}
 		IPC_SET_ARG5(call->data, phoneid);
-	} 
+	}
+	switch (IPC_GET_METHOD(call->data)) {
+	case IPC_M_DEBUG_ALL:
+		return -1;
+	default:
+		break;
+	}
 	return 0;
 }
@@ -442,5 +456,5 @@
 	IPC_SET_ARG5(call.data, 0);
 
-	if (!(res = request_preprocess(&call))) {
+	if (!(res = request_preprocess(&call, phone))) {
 		rc = ipc_call_sync(phone, &call);
 		if (rc != EOK)
@@ -482,5 +496,5 @@
 	GET_CHECK_PHONE(phone, phoneid, return ENOENT);
 
-	if (!(res = request_preprocess(&call))) {
+	if (!(res = request_preprocess(&call, phone))) {
 		rc = ipc_call_sync(phone, &call);
 		if (rc != EOK)
@@ -551,5 +565,5 @@
 	IPC_SET_ARG5(call->data, 0);
 
-	if (!(res = request_preprocess(call)))
+	if (!(res = request_preprocess(call, phone)))
 		ipc_call(phone, call);
 	else
@@ -585,5 +599,5 @@
 		return (unative_t) rc;
 	}
-	if (!(res = request_preprocess(call)))
+	if (!(res = request_preprocess(call, phone)))
 		ipc_call(phone, call);
 	else
@@ -869,4 +883,29 @@
 }
 
+#include <console/console.h>
+
+/**
+ * Syscall connect to a task by id.
+ *
+ * @return 		Phone id on success, or negative error code.
+ */
+unative_t sys_ipc_connect_kbox(sysarg64_t *uspace_taskid_arg)
+{
+#ifdef CONFIG_UDEBUG
+	sysarg64_t taskid_arg;
+	int rc;
+	
+	rc = copy_from_uspace(&taskid_arg, uspace_taskid_arg, sizeof(sysarg64_t));
+	if (rc != 0)
+		return (unative_t) rc;
+
+	printf("sys_ipc_connect_kbox(%lld, %d)\n", taskid_arg.value);
+
+	return ipc_connect_kbox(taskid_arg.value);
+#else
+	return (unative_t) ENOTSUP;
+#endif
+}
+
 /** @}
  */
Index: kernel/generic/src/main/uinit.c
===================================================================
--- kernel/generic/src/main/uinit.c	(revision eeb2bde2cd490c6feb4ee861d340112bfcb495af)
+++ kernel/generic/src/main/uinit.c	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
@@ -47,4 +47,6 @@
 #include <mm/slab.h>
 #include <arch.h>
+#include <udebug/udebug.h>
+
 
 /** Thread used to bring up userspace thread.
@@ -66,4 +68,8 @@
 	 */
 	thread_detach(THREAD);
+
+#ifdef CONFIG_UDEBUG
+	udebug_stoppable_end();
+#endif
 	
 	uarg.uspace_entry = ((uspace_arg_t *) arg)->uspace_entry;
Index: kernel/generic/src/proc/task.c
===================================================================
--- kernel/generic/src/proc/task.c	(revision eeb2bde2cd490c6feb4ee861d340112bfcb495af)
+++ kernel/generic/src/proc/task.c	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
@@ -156,5 +156,16 @@
 	ta->capabilities = 0;
 	ta->cycles = 0;
-	
+
+#ifdef CONFIG_UDEBUG
+	/* Init debugging stuff */
+	udebug_task_init(&ta->udebug);
+
+	/* Init kbox stuff */
+	ipc_answerbox_init(&ta->kernel_box, ta);
+	ta->kb_thread = NULL;
+	mutex_initialize(&ta->kb_cleanup_lock, MUTEX_PASSIVE);
+	ta->kb_finished = false;
+#endif
+
 	ipc_answerbox_init(&ta->answerbox, ta);
 	for (i = 0; i < IPC_MAX_PHONES; i++)
Index: kernel/generic/src/proc/thread.c
===================================================================
--- kernel/generic/src/proc/thread.c	(revision eeb2bde2cd490c6feb4ee861d340112bfcb495af)
+++ kernel/generic/src/proc/thread.c	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
@@ -181,4 +181,8 @@
 	}
 
+#ifdef CONFIG_UDEBUG
+	mutex_initialize(&t->udebug.lock, MUTEX_PASSIVE);
+#endif
+
 	return 0;
 }
@@ -348,4 +352,9 @@
 	t->threads_tree_node.key = (uintptr_t) t;
 
+#ifdef CONFIG_UDEBUG
+	/* Init debugging stuff */
+	udebug_thread_initialize(&t->udebug);
+#endif
+
 	/* might depend on previous initialization */
 	thread_create_arch(t);	
@@ -410,10 +419,15 @@
 
 	/*
-	 * Attach to the current task.
+	 * Attach to the specified task.
 	 */
 	ipl = interrupts_disable();
 	spinlock_lock(&task->lock);
+
 	atomic_inc(&task->refcount);
-	atomic_inc(&task->lifecount);
+
+	/* Must not count kbox thread into lifecount */
+	if (t->flags & THREAD_FLAG_USPACE)
+		atomic_inc(&task->lifecount);
+
 	list_append(&t->th_link, &task->th_head);
 	spinlock_unlock(&task->lock);
@@ -438,12 +452,17 @@
 	ipl_t ipl;
 
-	if (atomic_predec(&TASK->lifecount) == 0) {
-		/*
-		 * We are the last thread in the task that still has not exited.
-		 * With the exception of the moment the task was created, new
-		 * threads can only be created by threads of the same task.
-		 * We are safe to perform cleanup.
-		 */
-		if (THREAD->flags & THREAD_FLAG_USPACE) {
+	if (THREAD->flags & THREAD_FLAG_USPACE) {
+#ifdef CONFIG_UDEBUG
+		/* Generate udebug THREAD_E event */
+		udebug_thread_e_event();
+#endif
+		if (atomic_predec(&TASK->lifecount) == 0) {
+			/*
+			 * We are the last userspace thread in the task that
+			 * still has not exited. With the exception of the
+			 * moment the task was created, new userspace threads
+			 * can only be created by threads of the same task.
+			 * We are safe to perform cleanup.
+			 */
 			ipc_cleanup();
 			futex_cleanup();
@@ -742,4 +761,9 @@
 		thread_ready(t);
 
+#ifdef CONFIG_UDEBUG
+		/* Generate udebug THREAD_B event */
+		udebug_thread_b_event(t);
+#endif
+
 		return 0;
 	} else
Index: kernel/generic/src/syscall/syscall.c
===================================================================
--- kernel/generic/src/syscall/syscall.c	(revision eeb2bde2cd490c6feb4ee861d340112bfcb495af)
+++ kernel/generic/src/syscall/syscall.c	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
@@ -54,4 +54,5 @@
 #include <sysinfo/sysinfo.h>
 #include <console/console.h>
+#include <udebug/udebug.h>
 
 /** Print using kernel facility
@@ -102,7 +103,14 @@
 	unative_t rc;
 
-	if (id < SYSCALL_END)
+#ifdef CONFIG_UDEBUG
+	udebug_syscall_event(a1, a2, a3, a4, a5, a6, id, 0, false);
+#endif
+
+	if (id < SYSCALL_END) {	
+#ifdef CONFIG_UDEBUG
+		udebug_stoppable_begin();
+#endif
 		rc = syscall_table[id](a1, a2, a3, a4, a5, a6);
-	else {
+	} else {
 		printf("Task %" PRIu64": Unknown syscall %#" PRIxn, TASK->taskid, id);
 		task_kill(TASK->taskid);
@@ -112,4 +120,9 @@
 	if (THREAD->interrupted)
 		thread_exit();
+
+#ifdef CONFIG_UDEBUG
+	udebug_syscall_event(a1, a2, a3, a4, a5, a6, id, rc, true);
+	udebug_stoppable_end();
+#endif
 	
 	return rc;
@@ -166,5 +179,7 @@
 	
 	/* Debug calls */
-	(syshandler_t) sys_debug_enable_console
+	(syshandler_t) sys_debug_enable_console,
+
+	(syshandler_t) sys_ipc_connect_kbox
 };
 
Index: kernel/generic/src/udebug/udebug.c
===================================================================
--- kernel/generic/src/udebug/udebug.c	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
+++ kernel/generic/src/udebug/udebug.c	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
@@ -0,0 +1,502 @@
+/*
+ * Copyright (c) 2008 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup generic
+ * @{
+ */
+
+/**
+ * @file
+ * @brief	Udebug.
+ *
+ * Functions in this file are executed directly in each thread, which
+ * may or may not be the subject of debugging. The udebug_stoppable_begin/end()
+ * functions are also executed in the clock interrupt handler. To avoid
+ * deadlock, functions in this file are protected from the interrupt
+ * by locking the recursive lock THREAD->udebug.int_lock (just an atomic
+ * variable). This prevents udebug_stoppable_begin/end() from being
+ * executed in the interrupt handler (they are skipped).
+ *
+ * Functions in udebug_ops.c and udebug_ipc.c execute in different threads,
+ * so they needn't be protected from the (preemptible) interrupt-initiated
+ * code.
+ */
+ 
+#include <synch/waitq.h>
+#include <print.h>
+#include <udebug/udebug.h>
+#include <errno.h>
+#include <arch.h>
+
+static inline void udebug_int_lock(void)
+{
+	atomic_inc(&THREAD->udebug.int_lock);
+}
+
+static inline void udebug_int_unlock(void)
+{
+	atomic_dec(&THREAD->udebug.int_lock);
+}
+
+void udebug_task_init(udebug_task_t *ut)
+{
+	mutex_initialize(&ut->lock, MUTEX_PASSIVE);
+	ut->dt_state = UDEBUG_TS_INACTIVE;
+	ut->begin_call = NULL;
+	ut->not_stoppable_count = 0;
+	ut->evmask = 0;
+}
+
+void udebug_thread_initialize(udebug_thread_t *ut)
+{
+	mutex_initialize(&ut->lock, MUTEX_PASSIVE);
+	waitq_initialize(&ut->go_wq);
+
+	/*
+	 * At the beginning the thread is stoppable, so int_lock be set, too.
+	 */
+	atomic_set(&ut->int_lock, 1);
+
+	ut->go_call = NULL;
+	ut->stop = true;
+	ut->stoppable = true;
+	ut->debug_active = false;
+	ut->cur_event = 0; /* none */
+}
+
+static void udebug_wait_for_go(waitq_t *wq)
+{
+	int rc;
+	ipl_t ipl;
+
+	ipl = waitq_sleep_prepare(wq);
+
+	wq->missed_wakeups = 0;	/* Enforce blocking. */
+	rc = waitq_sleep_timeout_unsafe(wq, SYNCH_NO_TIMEOUT, SYNCH_FLAGS_NONE);
+
+	waitq_sleep_finish(wq, rc, ipl);
+}
+
+/** Do a preliminary check that a debugging session is in progress.
+ * 
+ * This only requires the THREAD->udebug.lock mutex (and not
+ * TASK->udebug.lock mutex). For an undebugged task, this will
+ * never block (while there could be collisions by different threads
+ * on the TASK mutex), thus improving SMP perormance for undebugged tasks.
+ */
+static bool udebug_thread_precheck(void)
+{
+	bool res;
+
+	mutex_lock(&THREAD->udebug.lock);
+	res = THREAD->udebug.debug_active;
+	mutex_unlock(&THREAD->udebug.lock);
+
+	return res;
+}
+
+void udebug_stoppable_begin(void)
+{
+	int nsc;
+	call_t *db_call, *go_call;
+
+	ASSERT(THREAD);
+	ASSERT(TASK);
+
+	udebug_int_lock();
+
+	/* Early check for undebugged tasks */
+	if (!udebug_thread_precheck()) {
+		udebug_int_unlock();
+		return;
+	}
+
+	mutex_lock(&TASK->udebug.lock);
+
+	nsc = --TASK->udebug.not_stoppable_count;
+
+	/* Lock order OK, THREAD->udebug.lock is after TASK->udebug.lock */
+	mutex_lock(&THREAD->udebug.lock);
+	ASSERT(THREAD->udebug.stoppable == false);
+	THREAD->udebug.stoppable = true;
+
+	if (TASK->udebug.dt_state == UDEBUG_TS_BEGINNING && nsc == 0) {
+		/*
+		 * This was the last non-stoppable thread. Reply to
+		 * DEBUG_BEGIN call.
+		 */
+
+		db_call = TASK->udebug.begin_call;
+		ASSERT(db_call);
+
+		TASK->udebug.dt_state = UDEBUG_TS_ACTIVE;
+		TASK->udebug.begin_call = NULL;
+
+		IPC_SET_RETVAL(db_call->data, 0);
+		ipc_answer(&TASK->answerbox, db_call);		
+
+	} else if (TASK->udebug.dt_state == UDEBUG_TS_ACTIVE) {
+		/*
+		 * Active debugging session
+		 */
+
+		if (THREAD->udebug.debug_active && THREAD->udebug.stop) {
+			/*
+			 * Thread was requested to stop - answer go call
+			 */
+
+			/* Make sure nobody takes this call away from us */
+			go_call = THREAD->udebug.go_call;
+			THREAD->udebug.go_call = NULL;
+			ASSERT(go_call);
+
+			IPC_SET_RETVAL(go_call->data, 0);
+			IPC_SET_ARG1(go_call->data, UDEBUG_EVENT_STOP);
+
+			THREAD->udebug.cur_event = UDEBUG_EVENT_STOP;
+
+	    		ipc_answer(&TASK->answerbox, go_call);
+		}
+	}
+
+	mutex_unlock(&THREAD->udebug.lock);
+        mutex_unlock(&TASK->udebug.lock);
+}
+
+void udebug_stoppable_end(void)
+{
+	/* Early check for undebugged tasks */
+	if (!udebug_thread_precheck()) {
+		udebug_int_unlock();
+		return;
+	}
+
+restart:
+	mutex_lock(&TASK->udebug.lock);
+	mutex_lock(&THREAD->udebug.lock);
+
+	if (THREAD->udebug.debug_active &&
+	    THREAD->udebug.stop == true) {
+		TASK->udebug.begin_call = NULL;
+		mutex_unlock(&THREAD->udebug.lock);
+		mutex_unlock(&TASK->udebug.lock);
+
+		udebug_wait_for_go(&THREAD->udebug.go_wq);
+
+		goto restart;
+		/* must try again - have to lose stoppability atomically */
+	} else {
+		++TASK->udebug.not_stoppable_count;
+		ASSERT(THREAD->udebug.stoppable == true);
+		THREAD->udebug.stoppable = false;
+
+		mutex_unlock(&THREAD->udebug.lock);
+		mutex_unlock(&TASK->udebug.lock);
+	}
+
+	udebug_int_unlock();
+}
+
+/** Upon being scheduled to run, check if the current thread should stop.
+ *
+ * This function is called from clock(). Preemption is enabled.
+ * interrupts are disabled, but since this is called after
+ * being scheduled-in, we can enable them, if we're careful enough
+ * not to allow arbitrary recursion or deadlock with the thread context.
+ */
+void udebug_before_thread_runs(void)
+{
+	ipl_t ipl;
+
+	return;
+	ASSERT(!PREEMPTION_DISABLED);
+
+	/* 
+	 * Prevent agains re-entering, such as when preempted inside this
+	 * function.
+	 */
+	if (atomic_get(&THREAD->udebug.int_lock) != 0)
+		return;
+
+	udebug_int_lock();
+
+	ipl = interrupts_enable();
+
+	/* Now we're free to do whatever we need (lock mutexes, sleep, etc.) */
+
+	/* Check if we're supposed to stop */
+	udebug_stoppable_begin();
+	udebug_stoppable_end();
+
+	interrupts_restore(ipl);
+
+	udebug_int_unlock();
+}
+
+void udebug_syscall_event(unative_t a1, unative_t a2, unative_t a3,
+    unative_t a4, unative_t a5, unative_t a6, unative_t id, unative_t rc,
+    bool end_variant)
+{
+	call_t *call;
+	udebug_event_t etype;
+
+	etype = end_variant ? UDEBUG_EVENT_SYSCALL_E : UDEBUG_EVENT_SYSCALL_B;
+
+	udebug_int_lock();
+
+	/* Early check for undebugged tasks */
+	if (!udebug_thread_precheck()) {
+		udebug_int_unlock();
+		return;
+	}
+
+	mutex_lock(&TASK->udebug.lock);
+	mutex_lock(&THREAD->udebug.lock);
+
+	/* Must only generate events when in debugging session and have go */
+	if (THREAD->udebug.debug_active != true ||
+	    THREAD->udebug.stop == true ||
+	    (TASK->udebug.evmask & UDEBUG_EVMASK(etype)) == 0) {
+		mutex_unlock(&THREAD->udebug.lock);
+		mutex_unlock(&TASK->udebug.lock);
+		return;
+	}
+
+	//printf("udebug_syscall_event\n");
+	call = THREAD->udebug.go_call;
+	THREAD->udebug.go_call = NULL;
+
+	IPC_SET_RETVAL(call->data, 0);
+	IPC_SET_ARG1(call->data, etype);
+	IPC_SET_ARG2(call->data, id);
+	IPC_SET_ARG3(call->data, rc);
+	//printf("udebug_syscall_event/ipc_answer\n");
+
+	THREAD->udebug.syscall_args[0] = a1;
+	THREAD->udebug.syscall_args[1] = a2;
+	THREAD->udebug.syscall_args[2] = a3;
+	THREAD->udebug.syscall_args[3] = a4;
+	THREAD->udebug.syscall_args[4] = a5;
+	THREAD->udebug.syscall_args[5] = a6;
+
+	/*
+	 * Make sure udebug.stop is true when going to sleep
+	 * in case we get woken up by DEBUG_END. (At which
+	 * point it must be back to the initial true value).
+	 */
+	THREAD->udebug.stop = true;
+	THREAD->udebug.cur_event = etype;
+
+	ipc_answer(&TASK->answerbox, call);
+
+	mutex_unlock(&THREAD->udebug.lock);
+	mutex_unlock(&TASK->udebug.lock);
+
+	udebug_wait_for_go(&THREAD->udebug.go_wq);
+
+	udebug_int_unlock();
+}
+
+void udebug_thread_b_event(struct thread *t)
+{
+	call_t *call;
+
+	udebug_int_lock();
+
+	mutex_lock(&TASK->udebug.lock);
+	mutex_lock(&THREAD->udebug.lock);
+
+	printf("udebug_thread_b_event\n");
+	printf("- check state\n");
+
+	/* Must only generate events when in debugging session */
+	if (THREAD->udebug.debug_active != true) {
+		printf("- debug_active: %s, udebug.stop: %s\n",
+			THREAD->udebug.debug_active ? "yes(+)" : "no(-)",
+			THREAD->udebug.stop ? "yes(-)" : "no(+)");
+		mutex_unlock(&THREAD->udebug.lock);
+		mutex_unlock(&TASK->udebug.lock);
+		return;
+	}
+
+	printf("- trigger event\n");
+
+	call = THREAD->udebug.go_call;
+	THREAD->udebug.go_call = NULL;
+	IPC_SET_RETVAL(call->data, 0);
+	IPC_SET_ARG1(call->data, UDEBUG_EVENT_THREAD_B);
+	IPC_SET_ARG2(call->data, (unative_t)t);
+
+	/*
+	 * Make sure udebug.stop is true when going to sleep
+	 * in case we get woken up by DEBUG_END. (At which
+	 * point it must be back to the initial true value).
+	 */
+	THREAD->udebug.stop = true;
+	THREAD->udebug.cur_event = UDEBUG_EVENT_THREAD_B;
+
+	ipc_answer(&TASK->answerbox, call);
+
+	mutex_unlock(&THREAD->udebug.lock);
+	mutex_unlock(&TASK->udebug.lock);
+
+	printf("- sleep\n");
+	udebug_wait_for_go(&THREAD->udebug.go_wq);
+
+	udebug_int_unlock();
+}
+
+void udebug_thread_e_event(void)
+{
+	call_t *call;
+
+	udebug_int_lock();
+
+	mutex_lock(&TASK->udebug.lock);
+	mutex_lock(&THREAD->udebug.lock);
+
+//	printf("udebug_thread_e_event\n");
+//	printf("- check state\n");
+
+	/* Must only generate events when in debugging session */
+	if (THREAD->udebug.debug_active != true) {
+/*		printf("- debug_active: %s, udebug.stop: %s\n",
+			THREAD->udebug.debug_active ? "yes(+)" : "no(-)",
+			THREAD->udebug.stop ? "yes(-)" : "no(+)");*/
+		mutex_unlock(&THREAD->udebug.lock);
+		mutex_unlock(&TASK->udebug.lock);
+		return;
+	}
+
+//	printf("- trigger event\n");
+
+	call = THREAD->udebug.go_call;
+	THREAD->udebug.go_call = NULL;
+	IPC_SET_RETVAL(call->data, 0);
+	IPC_SET_ARG1(call->data, UDEBUG_EVENT_THREAD_E);
+
+	/* Prevent any further debug activity in thread */
+	THREAD->udebug.debug_active = false;
+	THREAD->udebug.cur_event = 0;		/* none */
+	THREAD->udebug.stop = true;	/* set to initial value */
+
+	ipc_answer(&TASK->answerbox, call);
+
+	mutex_unlock(&THREAD->udebug.lock);
+	mutex_unlock(&TASK->udebug.lock);
+
+	/* Leave int_lock enabled */
+	/* This event does not sleep - debugging has finished in this thread */
+}
+
+/**
+ * Terminate task debugging session.
+ *
+ * \param ta->udebug.lock must be already locked.
+ * \return Zero on success or negative error code.
+ */
+int udebug_task_cleanup(struct task *ta)
+{
+	thread_t *t;
+	link_t *cur;
+	int flags;
+	ipl_t ipl;
+
+	printf("udebug_task_cleanup()\n");
+	printf("task %llu\n", ta->taskid);
+
+	udebug_int_lock();
+
+	if (ta->udebug.dt_state != UDEBUG_TS_BEGINNING &&
+	    ta->udebug.dt_state != UDEBUG_TS_ACTIVE) {
+		printf("udebug_task_cleanup(): task not being debugged\n");
+		return EINVAL;
+	}
+
+	/* Finish debugging of all userspace threads */
+	for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) {
+		t = list_get_instance(cur, thread_t, th_link);
+
+		mutex_lock(&t->udebug.lock);
+
+		ipl = interrupts_disable();
+		spinlock_lock(&t->lock);
+
+		flags = t->flags;
+
+		spinlock_unlock(&t->lock);
+		interrupts_restore(ipl);
+
+		/* Only process userspace threads */
+		if ((flags & THREAD_FLAG_USPACE) != 0) {
+			/* Prevent any further debug activity in thread */
+			t->udebug.debug_active = false;
+			t->udebug.cur_event = 0;	/* none */
+
+			/* Still has go? */
+			if (t->udebug.stop == false) {
+				/*
+				* Yes, so clear go. As debug_active == false,
+				 * this doesn't affect anything.
+				 */
+				t->udebug.stop = true;	
+
+				/* Answer GO call */
+				printf("answer GO call with EVENT_FINISHED\n");
+				IPC_SET_RETVAL(t->udebug.go_call->data, 0);
+				IPC_SET_ARG1(t->udebug.go_call->data, UDEBUG_EVENT_FINISHED);
+
+				ipc_answer(&ta->answerbox, t->udebug.go_call);
+				t->udebug.go_call = NULL;
+			} else {
+				/*
+				 * Debug_stop is already at initial value.
+				 * Yet this means the thread needs waking up.
+				 */
+
+				/*
+				 * t's lock must not be held when calling
+				 * waitq_wakeup.
+				 */
+				waitq_wakeup(&t->udebug.go_wq, WAKEUP_FIRST);
+			}
+		}
+		mutex_unlock(&t->udebug.lock);
+	}
+
+	ta->udebug.dt_state = UDEBUG_TS_INACTIVE;
+	ta->udebug.debugger = NULL;
+
+	udebug_int_unlock();
+
+	return 0;
+}
+
+
+/** @}
+ */
Index: kernel/generic/src/udebug/udebug_ipc.c
===================================================================
--- kernel/generic/src/udebug/udebug_ipc.c	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
+++ kernel/generic/src/udebug/udebug_ipc.c	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2008 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup generic
+ * @{
+ */
+
+/**
+ * @file
+ * @brief	Udebug IPC message handling.
+ */
+ 
+#include <print.h>
+#include <proc/task.h>
+#include <proc/thread.h>
+#include <arch.h>
+#include <errno.h>
+#include <ipc/ipc.h>
+#include <syscall/copy.h>
+#include <udebug/udebug.h>
+#include <udebug/udebug_ops.h>
+#include <udebug/udebug_ipc.h>
+
+int udebug_request_preprocess(call_t *call, phone_t *phone)
+{
+	switch (IPC_GET_ARG1(call->data)) {
+	/* future UDEBUG_M_REGS_WRITE, UDEBUG_M_MEM_WRITE: */
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static void udebug_receive_begin(call_t *call)
+{
+	int rc;
+
+	rc = udebug_begin(call);
+	if (rc < 0) {
+		IPC_SET_RETVAL(call->data, rc);
+		ipc_answer(&TASK->kernel_box, call);
+		return;
+	}
+
+	if (rc != 0) {
+		IPC_SET_RETVAL(call->data, 0);
+		ipc_answer(&TASK->kernel_box, call);
+	}
+}
+
+static void udebug_receive_end(call_t *call)
+{
+	int rc;
+
+	rc = udebug_end();
+
+	IPC_SET_RETVAL(call->data, rc);
+	ipc_answer(&TASK->kernel_box, call);
+}
+
+static void udebug_receive_set_evmask(call_t *call)
+{
+	int rc;
+	udebug_evmask_t mask;
+
+	mask = IPC_GET_ARG2(call->data);
+	rc = udebug_set_evmask(mask);
+
+	IPC_SET_RETVAL(call->data, rc);
+	ipc_answer(&TASK->kernel_box, call);
+}
+
+
+static void udebug_receive_go(call_t *call)
+{
+	thread_t *t;
+	int rc;
+
+	//printf("debug_go()\n");
+
+	t = (thread_t *)IPC_GET_ARG2(call->data);
+
+	rc = udebug_go(t, call);
+	if (rc < 0) {
+		IPC_SET_RETVAL(call->data, rc);
+		ipc_answer(&TASK->kernel_box, call);
+		return;
+	}
+}
+
+static void udebug_receive_stop(call_t *call)
+{
+	thread_t *t;
+	int rc;
+
+	printf("debug_stop()\n");
+
+	t = (thread_t *)IPC_GET_ARG2(call->data);
+
+	rc = udebug_stop(t, call);
+	IPC_SET_RETVAL(call->data, rc);
+	ipc_answer(&TASK->kernel_box, call);
+}
+
+static void udebug_receive_thread_read(call_t *call)
+{
+	unative_t uspace_addr;
+	unative_t to_copy;
+	unsigned total_bytes;
+	unsigned buf_size;
+	void *buffer;
+	size_t n;
+	int rc;
+
+	uspace_addr = IPC_GET_ARG2(call->data);	/* Destination address */
+	buf_size = IPC_GET_ARG3(call->data);	/* Dest. buffer size */
+
+	/*
+	 * Read thread list. Variable n will be filled with actual number
+	 * of threads times thread-id size.
+	 */
+	rc = udebug_thread_read(&buffer, buf_size, &n);
+	if (rc < 0) {
+		IPC_SET_RETVAL(call->data, rc);
+		ipc_answer(&TASK->kernel_box, call);
+		return;
+	}
+
+	total_bytes = n;
+
+	/* Copy MAX(buf_size, total_bytes) bytes */
+
+	if (buf_size > total_bytes)
+		to_copy = total_bytes;
+	else
+		to_copy = buf_size;
+
+	/*
+	 * Make use of call->buffer to transfer data to caller's userspace
+	 */
+
+	IPC_SET_RETVAL(call->data, 0);
+	/* ARG1=dest, ARG2=size as in IPC_M_DATA_READ so that
+	   same code in process_answer() can be used 
+	   (no way to distinguish method in answer) */
+	IPC_SET_ARG1(call->data, uspace_addr);
+	IPC_SET_ARG2(call->data, to_copy);
+
+	IPC_SET_ARG3(call->data, total_bytes);
+	call->buffer = buffer;
+
+	ipc_answer(&TASK->kernel_box, call);
+}
+
+static void udebug_receive_args_read(call_t *call)
+{
+	thread_t *t;
+	unative_t uspace_addr;
+	int rc;
+	void *buffer;
+
+	t = (thread_t *)IPC_GET_ARG2(call->data);
+
+	rc = udebug_args_read(t, &buffer);
+	if (rc != EOK) {
+		IPC_SET_RETVAL(call->data, rc);
+		ipc_answer(&TASK->kernel_box, call);
+		return;
+	}
+
+	/*
+	 * Make use of call->buffer to transfer data to caller's userspace
+	 */
+
+	uspace_addr = IPC_GET_ARG3(call->data);
+
+	IPC_SET_RETVAL(call->data, 0);
+	/* ARG1=dest, ARG2=size as in IPC_M_DATA_READ so that
+	   same code in process_answer() can be used 
+	   (no way to distinguish method in answer) */
+	IPC_SET_ARG1(call->data, uspace_addr);
+	IPC_SET_ARG2(call->data, 6 * sizeof(unative_t));
+	call->buffer = buffer;
+
+	ipc_answer(&TASK->kernel_box, call);
+}
+
+static void udebug_receive_mem_read(call_t *call)
+{
+	unative_t uspace_dst;
+	unative_t uspace_src;
+	unsigned size;
+	void *buffer;
+	int rc;
+
+	uspace_dst = IPC_GET_ARG2(call->data);
+	uspace_src = IPC_GET_ARG3(call->data);
+	size = IPC_GET_ARG4(call->data);
+
+	rc = udebug_mem_read(uspace_src, size, &buffer);
+	if (rc < 0) {
+		IPC_SET_RETVAL(call->data, rc);
+		ipc_answer(&TASK->kernel_box, call);
+		return;
+	}
+
+	IPC_SET_RETVAL(call->data, 0);
+	/* ARG1=dest, ARG2=size as in IPC_M_DATA_READ so that
+	   same code in process_answer() can be used 
+	   (no way to distinguish method in answer) */
+	IPC_SET_ARG1(call->data, uspace_dst);
+	IPC_SET_ARG2(call->data, size);
+	call->buffer = buffer;
+
+	ipc_answer(&TASK->kernel_box, call);
+}
+
+/**
+ * Handle a debug call received on the kernel answerbox.
+ *
+ * This is called by the kbox servicing thread.
+ */
+void udebug_call_receive(call_t *call)
+{
+	int debug_method;
+
+	debug_method = IPC_GET_ARG1(call->data);
+
+	if (debug_method != UDEBUG_M_BEGIN) {
+		/*
+		 * Verify that the sender is this task's debugger.
+		 * Note that this is the only thread that could change
+		 * TASK->debugger. Therefore no locking is necessary
+		 * and the sender can be safely considered valid until
+		 * control exits this function.
+		 */
+		if (TASK->udebug.debugger != call->sender) {
+			IPC_SET_RETVAL(call->data, EINVAL);
+			ipc_answer(&TASK->kernel_box, call);
+			return;	
+		}
+	}
+
+	switch (debug_method) {
+	case UDEBUG_M_BEGIN:
+		udebug_receive_begin(call);
+		break;
+	case UDEBUG_M_END:
+		udebug_receive_end(call);
+		break;
+	case UDEBUG_M_SET_EVMASK:
+		udebug_receive_set_evmask(call);
+		break;
+	case UDEBUG_M_GO:
+		udebug_receive_go(call);
+		break;
+	case UDEBUG_M_STOP:
+		udebug_receive_stop(call);
+		break;
+	case UDEBUG_M_THREAD_READ:
+		udebug_receive_thread_read(call);
+		break;
+	case UDEBUG_M_ARGS_READ:
+		udebug_receive_args_read(call);
+		break;
+	case UDEBUG_M_MEM_READ:
+		udebug_receive_mem_read(call);
+		break;
+	}
+}
+
+/** @}
+ */
Index: kernel/generic/src/udebug/udebug_ops.c
===================================================================
--- kernel/generic/src/udebug/udebug_ops.c	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
+++ kernel/generic/src/udebug/udebug_ops.c	(revision fb9b0b0531bad96f4f612fbc28c26852439f1adb)
@@ -0,0 +1,444 @@
+/*
+ * Copyright (c) 2008 Jiri Svoboda
+ * 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.
+ */
+
+/** @addtogroup generic
+ * @{
+ */
+
+/**
+ * @file
+ * @brief	Udebug operations.
+ */
+ 
+#include <print.h>
+#include <proc/task.h>
+#include <proc/thread.h>
+#include <arch.h>
+#include <errno.h>
+#include <syscall/copy.h>
+#include <ipc/ipc.h>
+#include <udebug/udebug.h>
+#include <udebug/udebug_ops.h>
+
+/**
+ * Prepare a thread for a debugging operation.
+ *
+ * Simply put, return thread t with t->udebug.lock held,
+ * but only if it verifies all conditions.
+ *
+ * Specifically, verifies that thread t exists, is a userspace thread,
+ * and belongs to the current task (TASK). Verifies, that the thread
+ * has (or hasn't) go according to having_go (typically false).
+ * It also locks t->udebug.lock, making sure that t->udebug.debug_active
+ * is true - that the thread is in a valid debugging session.
+ *
+ * With this verified and the t->udebug.lock mutex held, it is ensured
+ * that the thread cannot leave the debugging session, let alone cease
+ * to exist.
+ *
+ * In this function, holding the TASK->udebug.lock mutex prevents the
+ * thread from leaving the debugging session, while relaxing from
+ * the t->lock spinlock to the t->udebug.lock mutex.
+ *
+ * Returns EOK if all went well, or an error code otherwise.
+ */
+static int _thread_op_begin(thread_t *t, bool having_go)
+{
+	task_id_t taskid;
+	ipl_t ipl;
+
+	taskid = TASK->taskid;
+
+	mutex_lock(&TASK->udebug.lock);
+
+	/* thread_exists() must be called with threads_lock held */
+	ipl = interrupts_disable();
+	spinlock_lock(&threads_lock);
+
+	if (!thread_exists(t)) {
+		spinlock_unlock(&threads_lock);
+		interrupts_restore(ipl);
+		mutex_unlock(&TASK->udebug.lock);
+		return ENOENT;
+	}
+
+	/* t->lock is enough to ensure the thread's existence */
+	spinlock_lock(&t->lock);
+	spinlock_unlock(&threads_lock);
+
+	/* Verify that 't' is a userspace thread */
+	if ((t->flags & THREAD_FLAG_USPACE) == 0) {
+		/* It's not, deny its existence */
+		spinlock_unlock(&t->lock);
+		interrupts_restore(ipl);
+		mutex_unlock(&TASK->udebug.lock);
+		return ENOENT;
+	}
+
+	/* Verify debugging state */
+	if (t->udebug.debug_active != true) {
+		/* Not in debugging session or undesired GO state */
+		spinlock_unlock(&t->lock);
+		interrupts_restore(ipl);
+		mutex_unlock(&TASK->udebug.lock);
+		return ENOENT;
+	}
+
+	/*
+	 * Since the thread has debug_active == true, TASK->udebug.lock
+	 * is enough to ensure its existence and that debug_active remains
+	 * true.
+	 */
+	spinlock_unlock(&t->lock);
+	interrupts_restore(ipl);
+
+	/* Only mutex TASK->udebug.lock left */
+	
+	/* Now verify that the thread belongs to the current task */
+	if (t->task != TASK) {
+		/* No such thread belonging this task*/
+		mutex_unlock(&TASK->udebug.lock);
+		return ENOENT;
+	}
+
+	/*
+	 * Now we need to grab the thread's debug lock for synchronization
+	 * of the threads stoppability/stop state.
+	 */
+	mutex_lock(&t->udebug.lock);
+
+	/* The big task mutex is no longer needed */
+	mutex_unlock(&TASK->udebug.lock);
+
+	if (!t->udebug.stop != having_go) {
+		/* Not in debugging session or undesired GO state */
+		mutex_unlock(&t->udebug.lock);
+		return EINVAL;
+	}
+
+	/* Only t->udebug.lock left */
+
+	return EOK;	/* All went well */
+}
+
+
+static void _thread_op_end(thread_t *t)
+{
+	mutex_unlock(&t->udebug.lock);
+}
+
+/**
+ * \return 0 (ok, but not done yet), 1 (done) or negative error code.
+ */
+int udebug_begin(call_t *call)
+{
+	int reply;
+
+	thread_t *t;
+	link_t *cur;
+
+	printf("udebug_begin()\n");
+
+	mutex_lock(&TASK->udebug.lock);
+	printf("debugging task %llu\n", TASK->taskid);
+
+	if (TASK->udebug.dt_state != UDEBUG_TS_INACTIVE) {
+		mutex_unlock(&TASK->udebug.lock);
+		printf("udebug_begin(): busy error\n");
+
+		return EBUSY;
+	}
+
+	TASK->udebug.dt_state = UDEBUG_TS_BEGINNING;
+	TASK->udebug.begin_call = call;
+	TASK->udebug.debugger = call->sender;
+
+	if (TASK->udebug.not_stoppable_count == 0) {
+		TASK->udebug.dt_state = UDEBUG_TS_ACTIVE;
+		TASK->udebug.begin_call = NULL;
+		reply = 1; /* immediate reply */
+	} else {
+		reply = 0; /* no reply */
+	}
+	
+	/* Set udebug.debug_active on all of the task's userspace threads */
+
+	for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) {
+		t = list_get_instance(cur, thread_t, th_link);
+
+		mutex_lock(&t->udebug.lock);
+		if ((t->flags & THREAD_FLAG_USPACE) != 0)
+			t->udebug.debug_active = true;
+		mutex_unlock(&t->udebug.lock);
+	}
+
+	mutex_unlock(&TASK->udebug.lock);
+
+	printf("udebug_begin() done (%s)\n", 
+	    reply ? "reply" : "stoppability wait");
+
+	return reply;
+}
+
+int udebug_end(void)
+{
+	int rc;
+
+	printf("udebug_end()\n");
+
+	mutex_lock(&TASK->udebug.lock);
+	printf("task %llu\n", TASK->taskid);
+
+	rc = udebug_task_cleanup(TASK);
+
+	mutex_unlock(&TASK->udebug.lock);
+
+	return rc;
+}
+
+int udebug_set_evmask(udebug_evmask_t mask)
+{
+	printf("udebug_set_mask()\n");
+
+	printf("debugging task %llu\n", TASK->taskid);
+
+	mutex_lock(&TASK->udebug.lock);
+
+	if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
+		mutex_unlock(&TASK->udebug.lock);
+		printf("udebug_set_mask(): not active debuging session\n");
+
+		return EINVAL;
+	}
+
+	TASK->udebug.evmask = mask;
+
+	mutex_unlock(&TASK->udebug.lock);
+
+	return 0;
+}
+
+
+int udebug_go(thread_t *t, call_t *call)
+{
+	int rc;
+
+//	printf("udebug_go()\n");
+
+	/* On success, this will lock t->udebug.lock */
+	rc = _thread_op_begin(t, false);
+	if (rc != EOK) {
+		return rc;
+	}
+
+	t->udebug.go_call = call;
+	t->udebug.stop = false;
+	t->udebug.cur_event = 0;	/* none */
+
+	/*
+	 * Neither t's lock nor threads_lock may be held during wakeup
+	 */
+	waitq_wakeup(&t->udebug.go_wq, WAKEUP_FIRST);
+
+	_thread_op_end(t);
+
+	return 0;
+}
+
+int udebug_stop(thread_t *t, call_t *call)
+{
+	int rc;
+
+	printf("udebug_stop()\n");
+	mutex_lock(&TASK->udebug.lock);
+
+	/*
+	 * On success, this will lock t->udebug.lock. Note that this makes sure
+	 * the thread is not stopped.
+	 */
+	rc = _thread_op_begin(t, true);
+	if (rc != EOK) {
+		return rc;
+	}
+
+	/* Take GO away from the thread */
+	t->udebug.stop = true;
+
+	if (!t->udebug.stoppable) {
+		/* Answer will be sent when the thread becomes stoppable */
+		_thread_op_end(t);
+		return 0;
+	}
+
+	/*
+	 * Answer GO call
+	 */
+	printf("udebug_stop - answering go call\n");
+
+	/* Make sure nobody takes this call away from us */
+	call = t->udebug.go_call;
+	t->udebug.go_call = NULL;
+
+	IPC_SET_RETVAL(call->data, 0);
+	IPC_SET_ARG1(call->data, UDEBUG_EVENT_STOP);
+	printf("udebug_stop/ipc_answer\n");
+
+	THREAD->udebug.cur_event = UDEBUG_EVENT_STOP;
+
+	_thread_op_end(t);
+
+	ipc_answer(&TASK->answerbox, call);
+	mutex_unlock(&TASK->udebug.lock);
+
+	printf("udebog_stop/done\n");
+	return 0;
+}
+
+int udebug_thread_read(void **buffer, size_t buf_size, size_t *n)
+{
+	thread_t *t;
+	link_t *cur;
+	unative_t tid;
+	unsigned copied_ids;
+	ipl_t ipl;
+	unative_t *id_buffer;
+	int flags;
+	size_t max_ids;
+
+	printf("udebug_thread_read()\n");
+
+	/* Allocate a buffer to hold thread IDs */
+	id_buffer = malloc(buf_size, 0);
+
+	mutex_lock(&TASK->udebug.lock);
+
+	/* Verify task state */
+	if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
+		mutex_unlock(&TASK->udebug.lock);
+		return EINVAL;
+	}
+
+	ipl = interrupts_disable();
+	spinlock_lock(&TASK->lock);
+	/* Copy down the thread IDs */
+
+	max_ids = buf_size / sizeof(unative_t);
+	copied_ids = 0;
+
+	/* FIXME: make sure the thread isn't past debug shutdown... */
+	for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) {
+		/* Do not write past end of buffer */
+		if (copied_ids >= max_ids) break;
+
+		t = list_get_instance(cur, thread_t, th_link);
+
+		spinlock_lock(&t->lock);
+		flags = t->flags;
+		spinlock_unlock(&t->lock);
+
+		/* Not interested in kernel threads */
+		if ((flags & THREAD_FLAG_USPACE) != 0) {
+			/* Using thread struct pointer as identification hash */
+			tid = (unative_t) t;
+			id_buffer[copied_ids++] = tid;
+		}
+	}
+
+	spinlock_unlock(&TASK->lock);
+	interrupts_restore(ipl);
+
+	mutex_unlock(&TASK->udebug.lock);
+
+	*buffer = id_buffer;
+	*n = copied_ids * sizeof(unative_t);
+
+	return 0;
+}
+
+int udebug_args_read(thread_t *t, void **buffer)
+{
+	int rc;
+	unative_t *arg_buffer;
+
+//	printf("udebug_args_read()\n");
+
+	/* Prepare a buffer to hold the arguments */
+	arg_buffer = malloc(6 * sizeof(unative_t), 0);
+
+	/* On success, this will lock t->udebug.lock */
+	rc = _thread_op_begin(t, false);
+	if (rc != EOK) {
+		return rc;
+	}
+
+	/* Additionally we need to verify that we are inside a syscall */
+	if (t->udebug.cur_event != UDEBUG_EVENT_SYSCALL_B &&
+	    t->udebug.cur_event != UDEBUG_EVENT_SYSCALL_E) {
+		_thread_op_end(t);
+		return EINVAL;
+	}
+
+	/* Copy to a local buffer before releasing the lock */
+	memcpy(arg_buffer, t->udebug.syscall_args, 6 * sizeof(unative_t));
+
+	_thread_op_end(t);
+
+	*buffer = arg_buffer;
+	return 0;
+}
+
+int udebug_mem_read(unative_t uspace_addr, size_t n, void **buffer)
+{
+	void *data_buffer;
+	int rc;
+
+	/* Verify task state */
+	mutex_lock(&TASK->udebug.lock);
+
+	if (TASK->udebug.dt_state != UDEBUG_TS_ACTIVE) {
+		mutex_unlock(&TASK->udebug.lock);
+		return EBUSY;
+	}
+
+	data_buffer = malloc(n, 0);
+
+//	printf("udebug_mem_read: src=%u, size=%u\n", uspace_addr, n);
+
+	/* NOTE: this is not strictly from a syscall... but that shouldn't
+	 * be a problem */
+	rc = copy_from_uspace(data_buffer, (void *)uspace_addr, n);
+	mutex_unlock(&TASK->udebug.lock);
+
+	if (rc != 0) return rc;
+
+	*buffer = data_buffer;
+	return 0;
+}
+
+/** @}
+ */
