Index: HelenOS.config
===================================================================
--- HelenOS.config	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ HelenOS.config	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -73,4 +73,5 @@
 @ "athlon_xp" Athlon XP
 @ "athlon_mp" Athlon MP
+@ "i486" i486
 ! [PLATFORM=ia32] PROCESSOR (choice)
 
Index: abi/include/ipc/event.h
===================================================================
--- abi/include/ipc/event.h	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ abi/include/ipc/event.h	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -36,4 +36,5 @@
 #define ABI_IPC_EVENT_H_
 
+/** Global events */
 typedef enum event_type {
 	/** New data available in kernel log */
Index: abi/include/ipc/methods.h
===================================================================
--- abi/include/ipc/methods.h	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ abi/include/ipc/methods.h	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -96,6 +96,4 @@
  *                       error is sent back to caller. Otherwise 
  *                       the call is accepted and the response is sent back.
- *                     - the hash of the client task is passed to userspace
- *                       (on the receiving side) as ARG4 of the call.
  *                     - the hash of the allocated phone is passed to userspace
  *                       (on the receiving side) as ARG5 of the call.
Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ boot/Makefile.common	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -143,4 +143,5 @@
 	$(USPACE_PATH)/app/blkdump/blkdump \
 	$(USPACE_PATH)/app/bnchmark/bnchmark \
+	$(USPACE_PATH)/app/devctl/devctl \
 	$(USPACE_PATH)/app/dltest/dltest \
 	$(USPACE_PATH)/app/dltest2/dltest2 \
Index: kernel/arch/amd64/include/asm.h
===================================================================
--- kernel/arch/amd64/include/asm.h	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ kernel/arch/amd64/include/asm.h	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -41,4 +41,6 @@
 #include <trace.h>
 
+#define IO_SPACE_BOUNDARY	((void *) (64 * 1024))
+
 /** Return base address of current stack.
  *
@@ -87,13 +89,16 @@
 NO_TRACE static inline uint8_t pio_read_8(ioport8_t *port)
 {
-	uint8_t val;
-	
-	asm volatile (
-		"inb %w[port], %b[val]\n"
-		: [val] "=a" (val)
-		: [port] "d" (port)
-	);
-	
-	return val;
+	if (port < (ioport8_t *) IO_SPACE_BOUNDARY) {
+		uint8_t val;
+		
+		asm volatile (
+			"inb %w[port], %b[val]\n"
+			: [val] "=a" (val)
+			: [port] "d" (port)
+		);
+		
+		return val;
+	} else
+		return (uint8_t) *port;
 }
 
@@ -108,13 +113,16 @@
 NO_TRACE static inline uint16_t pio_read_16(ioport16_t *port)
 {
-	uint16_t val;
-	
-	asm volatile (
-		"inw %w[port], %w[val]\n"
-		: [val] "=a" (val)
-		: [port] "d" (port)
-	);
-	
-	return val;
+	if (port < (ioport16_t *) IO_SPACE_BOUNDARY) {
+		uint16_t val;
+		
+		asm volatile (
+			"inw %w[port], %w[val]\n"
+			: [val] "=a" (val)
+			: [port] "d" (port)
+		);
+		
+		return val;
+	} else
+		return (uint16_t) *port;
 }
 
@@ -129,13 +137,16 @@
 NO_TRACE static inline uint32_t pio_read_32(ioport32_t *port)
 {
-	uint32_t val;
-	
-	asm volatile (
-		"inl %w[port], %[val]\n"
-		: [val] "=a" (val)
-		: [port] "d" (port)
-	);
-	
-	return val;
+	if (port < (ioport32_t *) IO_SPACE_BOUNDARY) {
+		uint32_t val;
+		
+		asm volatile (
+			"inl %w[port], %[val]\n"
+			: [val] "=a" (val)
+			: [port] "d" (port)
+		);
+		
+		return val;
+	} else
+		return (uint32_t) *port;
 }
 
@@ -150,9 +161,11 @@
 NO_TRACE static inline void pio_write_8(ioport8_t *port, uint8_t val)
 {
-	asm volatile (
-		"outb %b[val], %w[port]\n"
-		:: [val] "a" (val),
-		   [port] "d" (port)
-	);
+	if (port < (ioport8_t *) IO_SPACE_BOUNDARY) {
+		asm volatile (
+			"outb %b[val], %w[port]\n"
+			:: [val] "a" (val), [port] "d" (port)
+		);	
+	} else
+		*port = val;
 }
 
@@ -167,9 +180,11 @@
 NO_TRACE static inline void pio_write_16(ioport16_t *port, uint16_t val)
 {
-	asm volatile (
-		"outw %w[val], %w[port]\n"
-		:: [val] "a" (val),
-		   [port] "d" (port)
-	);
+	if (port < (ioport16_t *) IO_SPACE_BOUNDARY) {
+		asm volatile (
+			"outw %w[val], %w[port]\n"
+			:: [val] "a" (val), [port] "d" (port)
+		);
+	} else
+		*port = val;
 }
 
@@ -184,9 +199,11 @@
 NO_TRACE static inline void pio_write_32(ioport32_t *port, uint32_t val)
 {
-	asm volatile (
-		"outl %[val], %w[port]\n"
-		:: [val] "a" (val),
-		   [port] "d" (port)
-	);
+	if (port < (ioport32_t *) IO_SPACE_BOUNDARY) {
+		asm volatile (
+			"outl %[val], %w[port]\n"
+			:: [val] "a" (val), [port] "d" (port)
+		);
+	} else
+		*port = val;
 }
 
Index: kernel/arch/ia32/Makefile.inc
===================================================================
--- kernel/arch/ia32/Makefile.inc	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ kernel/arch/ia32/Makefile.inc	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -43,4 +43,8 @@
 ## Accepted CPUs
 #
+
+ifeq ($(PROCESSOR),i486)
+	CMN2 = -march=i486
+endif
 
 ifeq ($(PROCESSOR),athlon_xp)
Index: kernel/arch/ia32/include/asm.h
===================================================================
--- kernel/arch/ia32/include/asm.h	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ kernel/arch/ia32/include/asm.h	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -101,4 +101,6 @@
 GEN_WRITE_REG(dr7)
 
+#define IO_SPACE_BOUNDARY	((void *) (64 * 1024))
+
 /** Byte to port
  *
@@ -111,9 +113,11 @@
 NO_TRACE static inline void pio_write_8(ioport8_t *port, uint8_t val)
 {
-	asm volatile (
-		"outb %b[val], %w[port]\n"
-		:: [val] "a" (val),
-		   [port] "d" (port)
-	);
+	if (port < (ioport8_t *) IO_SPACE_BOUNDARY) {
+		asm volatile (
+			"outb %b[val], %w[port]\n"
+			:: [val] "a" (val), [port] "d" (port)
+		);	
+	} else
+		*port = val;
 }
 
@@ -128,9 +132,11 @@
 NO_TRACE static inline void pio_write_16(ioport16_t *port, uint16_t val)
 {
-	asm volatile (
-		"outw %w[val], %w[port]\n"
-		:: [val] "a" (val),
-		   [port] "d" (port)
-	);
+	if (port < (ioport16_t *) IO_SPACE_BOUNDARY) {
+		asm volatile (
+			"outw %w[val], %w[port]\n"
+			:: [val] "a" (val), [port] "d" (port)
+		);
+	} else
+		*port = val;
 }
 
@@ -145,9 +151,11 @@
 NO_TRACE static inline void pio_write_32(ioport32_t *port, uint32_t val)
 {
-	asm volatile (
-		"outl %[val], %w[port]\n"
-		:: [val] "a" (val),
-		   [port] "d" (port)
-	);
+	if (port < (ioport32_t *) IO_SPACE_BOUNDARY) {
+		asm volatile (
+			"outl %[val], %w[port]\n"
+			:: [val] "a" (val), [port] "d" (port)
+		);
+	} else
+		*port = val;
 }
 
@@ -162,13 +170,16 @@
 NO_TRACE static inline uint8_t pio_read_8(ioport8_t *port)
 {
-	uint8_t val;
-	
-	asm volatile (
-		"inb %w[port], %b[val]\n"
-		: [val] "=a" (val)
-		: [port] "d" (port)
-	);
-	
-	return val;
+	if (((void *)port) < IO_SPACE_BOUNDARY) {
+		uint8_t val;
+		
+		asm volatile (
+			"inb %w[port], %b[val]\n"
+			: [val] "=a" (val)
+			: [port] "d" (port)
+		);
+		
+		return val;
+	} else
+		return (uint8_t) *port;
 }
 
@@ -183,13 +194,16 @@
 NO_TRACE static inline uint16_t pio_read_16(ioport16_t *port)
 {
-	uint16_t val;
-	
-	asm volatile (
-		"inw %w[port], %w[val]\n"
-		: [val] "=a" (val)
-		: [port] "d" (port)
-	);
-	
-	return val;
+	if (((void *)port) < IO_SPACE_BOUNDARY) {
+		uint16_t val;
+		
+		asm volatile (
+			"inw %w[port], %w[val]\n"
+			: [val] "=a" (val)
+			: [port] "d" (port)
+		);
+		
+		return val;
+	} else
+		return (uint16_t) *port;
 }
 
@@ -204,13 +218,16 @@
 NO_TRACE static inline uint32_t pio_read_32(ioport32_t *port)
 {
-	uint32_t val;
-	
-	asm volatile (
-		"inl %w[port], %[val]\n"
-		: [val] "=a" (val)
-		: [port] "d" (port)
-	);
-	
-	return val;
+	if (((void *)port) < IO_SPACE_BOUNDARY) {
+		uint32_t val;
+		
+		asm volatile (
+			"inl %w[port], %[val]\n"
+			: [val] "=a" (val)
+			: [port] "d" (port)
+		);
+		
+		return val;
+	} else
+		return (uint32_t) *port;
 }
 
@@ -311,4 +328,6 @@
 }
 
+#ifndef PROCESSOR_i486
+
 /** Write to MSR */
 NO_TRACE static inline void write_msr(uint32_t msr, uint64_t value)
@@ -335,4 +354,6 @@
 	return ((uint64_t) dx << 32) | ax;
 }
+
+#endif /* PROCESSOR_i486 */
 
 
Index: kernel/arch/ia32/include/atomic.h
===================================================================
--- kernel/arch/ia32/include/atomic.h	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ kernel/arch/ia32/include/atomic.h	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -121,5 +121,7 @@
 	asm volatile (
 		"0:\n"
+#ifndef PROCESSOR_i486
 		"pause\n"        /* Pentium 4's HT love this instruction */
+#endif
 		"mov %[count], %[tmp]\n"
 		"testl %[tmp], %[tmp]\n"
Index: kernel/arch/ia32/include/boot/boot.h
===================================================================
--- kernel/arch/ia32/include/boot/boot.h	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ kernel/arch/ia32/include/boot/boot.h	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -43,4 +43,6 @@
 #define MULTIBOOT_HEADER_FLAGS  0x00010003
 
+#define MULTIBOOT_LOADER_MAGIC  0x2BADB002
+
 #ifndef __ASM__
 
Index: kernel/arch/ia32/include/cycle.h
===================================================================
--- kernel/arch/ia32/include/cycle.h	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ kernel/arch/ia32/include/cycle.h	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -40,4 +40,7 @@
 NO_TRACE static inline uint64_t get_cycle(void)
 {
+#ifdef PROCESSOR_i486
+	return 0;
+#else
 	uint64_t v;
 	
@@ -48,4 +51,5 @@
 	
 	return v;
+#endif
 }
 
Index: kernel/arch/ia32/src/asm.S
===================================================================
--- kernel/arch/ia32/src/asm.S	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ kernel/arch/ia32/src/asm.S	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -405,5 +405,11 @@
 	xorl %eax, %eax
 	cmpl $(GDT_SELECTOR(KTEXT_DES)), ISTATE_OFFSET_CS(%esp)
+#ifdef PROCESSOR_i486
+	jz 0f
+		movl %eax, %ebp
+	0:
+#else
 	cmovnzl %eax, %ebp
+#endif
 
 	movl %ebp, ISTATE_OFFSET_EBP_FRAME(%esp)
Index: kernel/arch/ia32/src/boot/boot.S
===================================================================
--- kernel/arch/ia32/src/boot/boot.S	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ kernel/arch/ia32/src/boot/boot.S	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -97,4 +97,10 @@
 	pm_status $status_prot
 	
+#include "vesa_prot.inc"
+	
+#ifndef PROCESSOR_i486
+	
+	pm_status $status_prot2
+	
 	movl $(INTEL_CPUID_LEVEL), %eax
 	cpuid
@@ -105,16 +111,20 @@
 	cpuid
 	bt $(INTEL_PSE), %edx
-	jc pse_supported
+	jnc pse_unsupported
+		
+		/* Map kernel and turn paging on */
+		pm_status $status_pse
+		call map_kernel_pse
+		jmp stack_init
+	
+#endif /* PROCESSOR_i486 */
 	
 	pse_unsupported:
 		
-		pm_error $err_pse
-	
-	pse_supported:
-	
-#include "vesa_prot.inc"
-	
-	/* Map kernel and turn paging on */
-	call map_kernel
+		/* Map kernel and turn paging on */
+		pm_status $status_non_pse
+		call map_kernel
+	
+	stack_init:
 	
 	/* Create the first stack frame */
@@ -122,5 +132,5 @@
 	movl %esp, %ebp
 	
-	pm2_status $status_prot2
+	pm2_status $status_prot3
 	
 	/* Call arch_pre_main(grub_eax, grub_ebx) */
@@ -140,5 +150,5 @@
 		jmp hlt0
 
-/** Setup mapping for the kernel.
+/** Setup mapping for the kernel (PSE variant)
  *
  * Setup mapping for both the unmapped and mapped sections
@@ -146,6 +156,7 @@
  *
  */
-.global map_kernel
-map_kernel:
+.global map_kernel_pse
+map_kernel_pse:
+	/* Paging features */
 	movl %cr4, %ecx
 	orl $(1 << 4), %ecx      /* PSE on */
@@ -158,5 +169,5 @@
 	xorl %ebx, %ebx
 	
-	floop:
+	floop_pse:
 		movl $((1 << 7) | (1 << 1) | (1 << 0)), %eax
 		orl %ebx, %eax
@@ -169,5 +180,5 @@
 		incl %ecx
 		cmpl $512, %ecx
-		jl floop
+		jl floop_pse
 	
 	movl %esi, %cr3
@@ -177,4 +188,179 @@
 	movl %ebx, %cr0
 	ret
+
+/** Setup mapping for the kernel (non-PSE variant).
+ *
+ * Setup mapping for both the unmapped and mapped sections
+ * of the kernel. For simplicity, we map the entire 4G space.
+ *
+ */
+.global map_kernel
+map_kernel:
+	/* Paging features */
+	movl %cr4, %ecx
+	andl $(~(1 << 5)), %ecx  /* PAE off */
+	movl %ecx, %cr4
+	
+	call calc_kernel_end
+	call find_mem_for_pt
+	
+	mov kernel_end, %esi
+	mov free_area, %ecx
+	
+	cmpl %esi, %ecx
+	jbe use_kernel_end
+		
+		mov %ecx, %esi
+		
+		/* Align address down to 4k */
+		andl $(~4095), %esi
+		
+	use_kernel_end:
+		
+		/* Align address to 4k */
+		addl $4095, %esi
+		andl $(~4095), %esi
+		
+		/* Allocate space for page tables */
+		movl %esi, pt_loc
+		movl $ballocs, %edi
+		andl $0x7fffffff, %edi
+		
+		movl %esi, (%edi)
+		addl $4, %edi
+		movl $(2 * 1024 * 1024), (%edi)
+		
+		/* Fill page tables */
+		xorl %ecx, %ecx
+		xorl %ebx, %ebx
+		
+		floop_pt:
+			movl $((1 << 1) | (1 << 0)), %eax
+			orl %ebx, %eax
+			movl %eax, (%esi, %ecx, 4)
+			addl $(4 * 1024), %ebx
+			
+			incl %ecx
+			cmpl $(512 * 1024), %ecx
+			
+			jl floop_pt
+		
+		/* Fill page directory */
+		movl $(page_directory + 0), %esi
+		movl $(page_directory + 2048), %edi
+		xorl %ecx, %ecx
+		movl pt_loc, %ebx
+		
+		floop:
+			movl $((1 << 1) | (1 << 0)), %eax
+			orl %ebx, %eax
+			
+			/* Mapping 0x00000000 + %ecx * 4M => 0x00000000 + %ecx * 4M */
+			movl %eax, (%esi, %ecx, 4)
+			
+			/* Mapping 0x80000000 + %ecx * 4M => 0x00000000 + %ecx * 4M */
+			movl %eax, (%edi, %ecx, 4)
+			addl $(4 * 1024), %ebx
+			
+			incl %ecx
+			cmpl $512, %ecx
+			
+			jl floop
+		
+		movl %esi, %cr3
+		
+		movl %cr0, %ebx
+		orl $(1 << 31), %ebx  /* paging on */
+		movl %ebx, %cr0
+		
+		ret
+
+/** Calculate unmapped address of the end of the kernel. */
+calc_kernel_end:
+	movl $hardcoded_load_address, %edi
+	andl $0x7fffffff, %edi
+	movl (%edi), %esi
+	andl $0x7fffffff, %esi
+	
+	movl $hardcoded_ktext_size, %edi
+	andl $0x7fffffff, %edi
+	addl (%edi), %esi
+	andl $0x7fffffff, %esi
+	
+	movl $hardcoded_kdata_size, %edi
+	andl $0x7fffffff, %edi
+	addl (%edi), %esi
+	andl $0x7fffffff, %esi
+	movl %esi, kernel_end
+	
+	ret
+
+/** Find free 2M (+4k for alignment) region where to store page tables */
+find_mem_for_pt:
+	/* Check if multiboot info is present */
+	cmpl $MULTIBOOT_LOADER_MAGIC, grub_eax
+	je check_multiboot_map
+		
+		ret
+	
+	check_multiboot_map:
+		
+		/* Copy address of the multiboot info to ebx */
+		movl grub_ebx, %ebx
+		
+		/* Check if memory map flag is present */
+		movl (%ebx), %edx
+		andl $(1 << 6), %edx
+		jnz use_multiboot_map
+			
+			ret
+		
+	use_multiboot_map:
+		
+		/* Copy address of the memory map to edx */
+		movl 48(%ebx), %edx
+		movl %edx, %ecx
+		
+		addl 44(%ebx), %ecx
+		
+		/* Find a free region at least 2M in size */
+		check_memmap_loop:
+			
+			/* Is this a free region? */
+			cmp $1, 20(%edx)
+			jnz next_region
+			
+			/* Check size */
+			cmp $0, 16(%edx)
+			jnz next_region
+			
+			cmpl $(2 * 1024 * 1024 + 4 * 1024), 12(%edx)
+			jbe next_region
+			
+			cmp $0, 8(%edx)
+			jz found_region
+		
+		next_region:
+			
+			cmp %ecx, %edx
+			jbe next_region_do
+			
+				ret
+		
+		next_region_do:
+			
+			addl (%edx), %edx
+			addl $4, %edx
+			jmp check_memmap_loop
+			
+		found_region:
+			
+			/* Use end of the found region */
+			mov 4(%edx), %ecx
+			add 12(%edx), %ecx
+			sub $(2 * 1024 * 1024), %ecx
+			mov %ecx, free_area
+			
+			ret
 
 /** Print string to EGA display (in light red) and halt.
@@ -521,13 +707,20 @@
 grub_eax:
 	.long 0
-
 grub_ebx:
 	.long 0
 
-err_pse:
-	.asciz "Page Size Extension not supported. System halted."
+pt_loc:
+	.long 0
+kernel_end:
+	.long 0
+free_area:
+	.long 0
 
 status_prot:
 	.asciz "[prot] "
+status_pse:
+	.asciz "[pse] "
+status_non_pse:
+	.asciz "[non_pse] "
 status_vesa_copy:
 	.asciz "[vesa_copy] "
@@ -538,4 +731,6 @@
 status_prot2:
 	.asciz "[prot2] "
+status_prot3:
+	.asciz "[prot3] "
 status_main:
 	.asciz "[main] "
Index: kernel/arch/ia32/src/cpu/cpu.c
===================================================================
--- kernel/arch/ia32/src/cpu/cpu.c	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ kernel/arch/ia32/src/cpu/cpu.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -118,9 +118,11 @@
 		);
 	}
-	
+
+#ifndef PROCESSOR_i486
 	if (CPU->arch.fi.bits.sep) {
 		/* Setup fast SYSENTER/SYSEXIT syscalls */
 		syscall_setup_cpu();
 	}
+#endif
 }
 
Index: kernel/arch/ia32/src/proc/scheduler.c
===================================================================
--- kernel/arch/ia32/src/proc/scheduler.c	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ kernel/arch/ia32/src/proc/scheduler.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -60,8 +60,10 @@
 	uintptr_t kstk = (uintptr_t) &THREAD->kstack[STACK_SIZE];
 	
+#ifndef PROCESSOR_i486
 	if (CPU->arch.fi.bits.sep) {
 		/* Set kernel stack for CP3 -> CPL0 switch via SYSENTER */
 		write_msr(IA32_MSR_SYSENTER_ESP, kstk - sizeof(istate_t));
 	}
+#endif
 	
 	/* Set kernel stack for CPL3 -> CPL0 switch via interrupt */
Index: kernel/arch/ia32/src/syscall.c
===================================================================
--- kernel/arch/ia32/src/syscall.c	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ kernel/arch/ia32/src/syscall.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -39,4 +39,6 @@
 #include <arch/pm.h>
 
+#ifndef PROCESSOR_i486
+
 /** Enable & setup support for SYSENTER/SYSEXIT */
 void syscall_setup_cpu(void)
@@ -50,4 +52,6 @@
 }
 
+#endif /* PROCESSOR_i486 */
+
 /** @}
  */
Index: kernel/generic/include/ipc/ipc.h
===================================================================
--- kernel/generic/include/ipc/ipc.h	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ kernel/generic/include/ipc/ipc.h	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -40,4 +40,5 @@
 #include <synch/mutex.h>
 #include <synch/waitq.h>
+#include <typedefs.h>
 
 #define IPC_MAX_PHONES  32
@@ -97,6 +98,9 @@
 typedef struct {
 	sysarg_t args[IPC_CALL_LEN];
-	/** Task which made or forwarded the call with IPC_FF_ROUTE_FROM_ME. */
-	struct task *task;
+	/**
+	 * Task which made or forwarded the call with IPC_FF_ROUTE_FROM_ME,
+	 * or the task which answered the call.
+	 */
+	task_id_t task_id;
 	/** Phone which made or last masqueraded this call. */
 	phone_t *phone;
Index: kernel/generic/src/console/console.c
===================================================================
--- kernel/generic/src/console/console.c	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ kernel/generic/src/console/console.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -248,5 +248,5 @@
 }
 
-void klog_update(void *e)
+void klog_update(void *event)
 {
 	if (!atomic_get(&klog_inited))
Index: kernel/generic/src/ipc/event.c
===================================================================
--- kernel/generic/src/ipc/event.c	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ kernel/generic/src/ipc/event.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -81,8 +81,8 @@
 }
 
-void event_task_init(task_t *t)
+void event_task_init(task_t *task)
 {
 	for (unsigned int i = EVENT_END; i < EVENT_TASK_END; i++)
-		event_initialize(evno2event(i, t));
+		event_initialize(evno2event(i, task));
 }
 
@@ -130,5 +130,5 @@
 }
 
-void event_task_set_unmask_callback(task_t *t, event_task_type_t evno,
+void event_task_set_unmask_callback(task_t *task, event_task_type_t evno,
     event_callback_t callback)
 {
@@ -136,5 +136,5 @@
 	ASSERT(evno < EVENT_TASK_END);
 		
-	_event_set_unmask_callback(evno2event(evno, t), callback);
+	_event_set_unmask_callback(evno2event(evno, task), callback);
 }
 
@@ -160,4 +160,6 @@
 				IPC_SET_ARG4(call->data, a4);
 				IPC_SET_ARG5(call->data, a5);
+				
+				call->data.task_id = TASK ? TASK->taskid : 0;
 				
 				irq_spinlock_lock(&event->answerbox->irq_lock, true);
@@ -211,5 +213,5 @@
 /** Send per-task kernel notification event
  *
- * @param t    Destination task.
+ * @param task Destination task.
  * @param evno Event type.
  * @param mask Mask further notifications after a successful
@@ -229,11 +231,11 @@
  *
  */
-int event_task_notify(task_t *t, event_task_type_t evno, bool mask, sysarg_t a1,
-    sysarg_t a2, sysarg_t a3, sysarg_t a4, sysarg_t a5)
+int event_task_notify(task_t *task, event_task_type_t evno, bool mask,
+    sysarg_t a1, sysarg_t a2, sysarg_t a3, sysarg_t a4, sysarg_t a5)
 {
 	ASSERT(evno >= (int) EVENT_END);
 	ASSERT(evno < EVENT_TASK_END);
 	
-	return event_enqueue(evno2event(evno, t), mask, a1, a2, a3, a4, a5);
+	return event_enqueue(evno2event(evno, task), mask, a1, a2, a3, a4, a5);
 }
 
Index: kernel/generic/src/ipc/ipc.c
===================================================================
--- kernel/generic/src/ipc/ipc.c	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ kernel/generic/src/ipc/ipc.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -230,4 +230,6 @@
 		}
 	}
+
+	call->data.task_id = TASK->taskid;
 	
 	if (do_lock)
@@ -294,5 +296,5 @@
 		atomic_inc(&phone->active_calls);
 		call->data.phone = phone;
-		call->data.task = TASK;
+		call->data.task_id = TASK->taskid;
 	}
 	
@@ -406,5 +408,5 @@
 			call->caller_phone = call->data.phone;
 		call->data.phone = newphone;
-		call->data.task = TASK;
+		call->data.task_id = TASK->taskid;
 	}
 	
Index: kernel/generic/src/ipc/sysipc.c
===================================================================
--- kernel/generic/src/ipc/sysipc.c	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ kernel/generic/src/ipc/sysipc.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -54,4 +54,5 @@
 #include <mm/as.h>
 #include <print.h>
+#include <macros.h>
 
 /**
@@ -252,6 +253,4 @@
 			/* The connection was accepted */
 			phone_connect(phoneid, &answer->sender->answerbox);
-			/* Set 'task hash' as arg4 of response */
-			IPC_SET_ARG4(answer->data, (sysarg_t) TASK);
 			/* Set 'phone hash' as arg5 of response */
 			IPC_SET_ARG5(answer->data,
@@ -375,6 +374,6 @@
 				    IPC_GET_ARG2(*olddata),
 				    IPC_GET_ARG3(*olddata),
-				    (sysarg_t) olddata->task,
-				    (sysarg_t) TASK);
+				    LOWER32(olddata->task_id),
+				    UPPER32(olddata->task_id));
 				IPC_SET_RETVAL(answer->data, rc);
 			}
Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/Makefile	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -37,4 +37,5 @@
 	app/blkdump \
 	app/bnchmark \
+	app/devctl \
 	app/edit \
 	app/ext2info \
Index: uspace/app/devctl/Makefile
===================================================================
--- uspace/app/devctl/Makefile	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
+++ uspace/app/devctl/Makefile	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -0,0 +1,35 @@
+#
+# Copyright (c) 2011 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.
+#
+
+USPACE_PREFIX = ../..
+BINARY = devctl
+
+SOURCES = \
+	devctl.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/devctl/devctl.c
===================================================================
--- uspace/app/devctl/devctl.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
+++ uspace/app/devctl/devctl.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2011 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 devctl
+ * @{
+ */
+/** @file Control device framework (devman server).
+ */
+
+#include <devman.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/typefmt.h>
+
+#define NAME "devctl"
+
+#define MAX_NAME_LENGTH 1024
+
+static int fun_tree_print(devman_handle_t funh, int lvl)
+{
+	char name[MAX_NAME_LENGTH];
+	devman_handle_t devh;
+	devman_handle_t *cfuns;
+	size_t count, i;
+	int rc;
+	int j;
+
+	for (j = 0; j < lvl; j++)
+		printf("    ");
+
+	rc = devman_fun_get_name(funh, name, MAX_NAME_LENGTH);
+	if (rc != EOK) {
+		str_cpy(name, MAX_NAME_LENGTH, "unknown");
+		return ENOMEM;
+	}
+
+	if (name[0] == '\0')
+		str_cpy(name, MAX_NAME_LENGTH, "/");
+
+	printf("%s (%" PRIun ")\n", name, funh);
+
+	rc = devman_fun_get_child(funh, &devh);
+	if (rc == ENOENT)
+		return EOK;
+
+	if (rc != EOK) {
+		printf(NAME ": Failed getting child device for function "
+		    "%s.\n", "xxx");
+		return rc;
+	}
+
+	rc = devman_dev_get_functions(devh, &cfuns, &count);
+	if (rc != EOK) {
+		printf(NAME ": Failed getting list of functions for "
+		    "device %s.\n", "xxx");
+		return rc;
+	}
+
+	for (i = 0; i < count; i++)
+		fun_tree_print(cfuns[i], lvl + 1);
+
+	free(cfuns);
+	return EOK;
+}
+
+int main(int argc, char *argv[])
+{
+	devman_handle_t root_fun;
+	int rc;
+
+	rc = devman_fun_get_handle("/", &root_fun, 0);
+	if (rc != EOK) {
+		printf(NAME ": Error resolving root function.\n");
+		return 1;
+	}
+
+	rc = fun_tree_print(root_fun, 0);
+	if (rc != EOK)
+		return 1;
+
+	return 0;
+}
+
+/** @}
+ */
Index: uspace/app/lsusb/main.c
===================================================================
--- uspace/app/lsusb/main.c	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/app/lsusb/main.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -81,5 +81,5 @@
 		}
 		char path[MAX_PATH_LENGTH];
-		rc = devman_get_device_path(dev_handle, path, MAX_PATH_LENGTH);
+		rc = devman_fun_get_path(dev_handle, path, MAX_PATH_LENGTH);
 		if (rc != EOK) {
 			continue;
@@ -120,5 +120,5 @@
 		}
 		char path[MAX_PATH_LENGTH];
-		rc = devman_get_device_path(hc_handle, path, MAX_PATH_LENGTH);
+		rc = devman_fun_get_path(hc_handle, path, MAX_PATH_LENGTH);
 		if (rc != EOK) {
 			printf(NAME ": Error resolving path of HC with SID %"
Index: uspace/app/mkbd/main.c
===================================================================
--- uspace/app/mkbd/main.c	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/app/mkbd/main.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -240,5 +240,5 @@
 	
 	char path[MAX_PATH_LENGTH];
-	rc = devman_get_device_path(dev_handle, path, MAX_PATH_LENGTH);
+	rc = devman_fun_get_path(dev_handle, path, MAX_PATH_LENGTH);
 	if (rc != EOK) {
 		return ENOMEM;
Index: uspace/app/tester/hw/serial/serial1.c
===================================================================
--- uspace/app/tester/hw/serial/serial1.c	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/app/tester/hw/serial/serial1.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -72,5 +72,5 @@
 	
 	devman_handle_t handle;
-	int res = devman_device_get_handle("/hw/pci0/00:01.0/com1/a", &handle,
+	int res = devman_fun_get_handle("/hw/pci0/00:01.0/com1/a", &handle,
 	    IPC_FLAG_BLOCKING);
 	if (res != EOK)
Index: uspace/lib/c/arch/ia32/Makefile.common
===================================================================
--- uspace/lib/c/arch/ia32/Makefile.common	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/lib/c/arch/ia32/Makefile.common	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -28,5 +28,10 @@
 
 CLANG_ARCH = i386
-GCC_CFLAGS += -march=pentium -fno-omit-frame-pointer
+
+ifeq ($(PROCESSOR),i486)
+	GCC_CFLAGS += -march=i486 -fno-omit-frame-pointer
+else
+	GCC_CFLAGS += -march=pentium -fno-omit-frame-pointer
+endif
 
 ENDIANESS = LE
Index: uspace/lib/c/arch/ia32/Makefile.inc
===================================================================
--- uspace/lib/c/arch/ia32/Makefile.inc	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/lib/c/arch/ia32/Makefile.inc	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -28,5 +28,5 @@
 
 ARCH_SOURCES = \
-	arch/$(UARCH)/src/entry.s \
+	arch/$(UARCH)/src/entry.S \
 	arch/$(UARCH)/src/entryjmp.s \
 	arch/$(UARCH)/src/thread_entry.s \
Index: uspace/lib/c/arch/ia32/include/ddi.h
===================================================================
--- uspace/lib/c/arch/ia32/include/ddi.h	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/lib/c/arch/ia32/include/ddi.h	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -41,63 +41,81 @@
 static inline uint8_t pio_read_8(ioport8_t *port)
 {
-	uint8_t val;
-	
-	asm volatile (
-		"inb %w[port], %b[val]\n"
-		: [val] "=a" (val)
-		: [port] "d" (port)
-	);
-	
-	return val;
+	if (port < (ioport8_t *) IO_SPACE_BOUNDARY) {
+		uint8_t val;
+		
+		asm volatile (
+			"inb %w[port], %b[val]\n"
+			: [val] "=a" (val)
+			: [port] "d" (port)
+		);
+		
+		return val;
+	} else
+		return (uint8_t) *port;
 }
 
 static inline uint16_t pio_read_16(ioport16_t *port)
 {
-	uint16_t val;
-	
-	asm volatile (
-		"inw %w[port], %w[val]\n"
-		: [val] "=a" (val)
-		: [port] "d" (port)
-	);
-	
-	return val;
+	if (port < (ioport16_t *) IO_SPACE_BOUNDARY) {
+		uint16_t val;
+		
+		asm volatile (
+			"inw %w[port], %w[val]\n"
+			: [val] "=a" (val)
+			: [port] "d" (port)
+		);
+		
+		return val;
+	} else
+		return (uint16_t) *port;
 }
 
 static inline uint32_t pio_read_32(ioport32_t *port)
 {
-	uint32_t val;
-	
-	asm volatile (
-		"inl %w[port], %[val]\n"
-		: [val] "=a" (val)
-		: [port] "d" (port)
-	);
-	
-	return val;
+	if (port < (ioport32_t *) IO_SPACE_BOUNDARY) {
+		uint32_t val;
+		
+		asm volatile (
+			"inl %w[port], %[val]\n"
+			: [val] "=a" (val)
+			: [port] "d" (port)
+		);
+		
+		return val;
+	} else
+		return (uint32_t) *port;
 }
 
 static inline void pio_write_8(ioport8_t *port, uint8_t val)
 {
-	asm volatile (
-		"outb %b[val], %w[port]\n"
-		:: [val] "a" (val), [port] "d" (port)
-	);
+	if (port < (ioport8_t *) IO_SPACE_BOUNDARY) {
+		asm volatile (
+			"outb %b[val], %w[port]\n"
+			:: [val] "a" (val), [port] "d" (port)
+		);	
+	} else
+		*port = val;
 }
 
 static inline void pio_write_16(ioport16_t *port, uint16_t val)
 {
-	asm volatile (
-		"outw %w[val], %w[port]\n"
-		:: [val] "a" (val), [port] "d" (port)
-	);
+	if (port < (ioport16_t *) IO_SPACE_BOUNDARY) {
+		asm volatile (
+			"outw %w[val], %w[port]\n"
+			:: [val] "a" (val), [port] "d" (port)
+		);
+	} else
+		*port = val;
 }
 
 static inline void pio_write_32(ioport32_t *port, uint32_t val)
 {
-	asm volatile (
-		"outl %[val], %w[port]\n"
-		:: [val] "a" (val), [port] "d" (port)
-	);
+	if (port < (ioport32_t *) IO_SPACE_BOUNDARY) {
+		asm volatile (
+			"outl %[val], %w[port]\n"
+			:: [val] "a" (val), [port] "d" (port)
+		);
+	} else
+		*port = val;
 }
 
Index: uspace/lib/c/arch/ia32/src/entry.S
===================================================================
--- uspace/lib/c/arch/ia32/src/entry.S	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
+++ uspace/lib/c/arch/ia32/src/entry.S	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -0,0 +1,68 @@
+#
+# Copyright (c) 2005 Martin Decky
+# 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.
+#
+
+INTEL_CPUID_STANDARD = 1
+INTEL_SEP = 11
+
+.section .init, "ax"
+
+.org 0
+
+.globl __entry
+
+## User-space task entry point
+#
+# %edi contains the PCB pointer
+#
+__entry:
+	mov %ss, %ax
+	mov %ax, %ds
+	mov %ax, %es
+	mov %ax, %fs
+	# Do not set %gs, it contains descriptor that can see TLS
+	
+#ifndef PROCESSOR_i486	
+	# Detect the mechanism used for making syscalls
+	movl $(INTEL_CPUID_STANDARD), %eax
+	cpuid
+	bt $(INTEL_SEP), %edx
+	jnc 0f
+		leal __syscall_fast_func, %eax
+		movl $__syscall_fast, (%eax)
+	0:
+#endif
+	
+	#
+	# Create the first stack frame.
+	#
+	pushl $0
+	movl %esp, %ebp
+	
+	# Pass the PCB pointer to __main as the first argument
+	pushl %edi
+	call __main
Index: pace/lib/c/arch/ia32/src/entry.s
===================================================================
--- uspace/lib/c/arch/ia32/src/entry.s	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ 	(revision )
@@ -1,65 +1,0 @@
-#
-# Copyright (c) 2005 Martin Decky
-# 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.
-#
-
-INTEL_CPUID_STANDARD = 1
-INTEL_SEP = 11
-
-.section .init, "ax"
-
-.org 0
-
-.globl __entry
-
-## User-space task entry point
-#
-# %edi contains the PCB pointer
-#
-__entry:
-	mov %ss, %ax
-	mov %ax, %ds
-	mov %ax, %es
-	mov %ax, %fs
-	# Do not set %gs, it contains descriptor that can see TLS
-	
-	# Detect the mechanism used for making syscalls
-	movl $(INTEL_CPUID_STANDARD), %eax
-	cpuid
-	bt $(INTEL_SEP), %edx
-	jnc 0f
-	leal __syscall_fast_func, %eax
-	movl $__syscall_fast, (%eax)
-0:
-	#
-	# Create the first stack frame.
-	#
-	pushl $0
-	movl %esp, %ebp
-	
-	# Pass the PCB pointer to __main as the first argument
-	pushl %edi
-	call __main
Index: uspace/lib/c/generic/async.c
===================================================================
--- uspace/lib/c/generic/async.c	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/lib/c/generic/async.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -98,4 +98,5 @@
 #include <ipc/ipc.h>
 #include <async.h>
+#include "private/async.h"
 #undef LIBC_ASYNC_C_
 
@@ -112,5 +113,5 @@
 #include <mem.h>
 #include <stdlib.h>
-#include "private/async.h"
+#include <macros.h>
 
 #define CLIENT_HASH_TABLE_BUCKETS  32
@@ -138,5 +139,5 @@
 	link_t link;
 	
-	sysarg_t in_task_hash;
+	task_id_t in_task_id;
 	atomic_t refcnt;
 	void *data;
@@ -150,6 +151,6 @@
 	link_t link;
 	
-	/** Incoming client task hash. */
-	sysarg_t in_task_hash;
+	/** Incoming client task ID. */
+	task_id_t in_task_id;
 	
 	/** Incoming phone hash. */
@@ -283,8 +284,10 @@
 {
 	assert(key);
+	assert(keys == 2);
 	assert(item);
 	
 	client_t *client = hash_table_get_instance(item, client_t, link);
-	return (key[0] == client->in_task_hash);
+	return (key[0] == LOWER32(client->in_task_id) &&
+	    (key[1] == UPPER32(client->in_task_id)));
 }
 
@@ -574,11 +577,14 @@
 }
 
-static client_t *async_client_get(sysarg_t client_hash, bool create)
-{
-	unsigned long key = client_hash;
+static client_t *async_client_get(task_id_t client_id, bool create)
+{
+	unsigned long key[2] = {
+		LOWER32(client_id),
+		UPPER32(client_id),
+	};
 	client_t *client = NULL;
 
 	futex_down(&async_futex);
-	link_t *lnk = hash_table_find(&client_hash_table, &key);
+	link_t *lnk = hash_table_find(&client_hash_table, key);
 	if (lnk) {
 		client = hash_table_get_instance(lnk, client_t, link);
@@ -587,9 +593,9 @@
 		client = malloc(sizeof(client_t));
 		if (client) {
-			client->in_task_hash = client_hash;
+			client->in_task_id = client_id;
 			client->data = async_client_data_create();
 		
 			atomic_set(&client->refcnt, 1);
-			hash_table_insert(&client_hash_table, &key, &client->link);
+			hash_table_insert(&client_hash_table, key, &client->link);
 		}
 	}
@@ -602,10 +608,13 @@
 {
 	bool destroy;
-	unsigned long key = client->in_task_hash;
+	unsigned long key[2] = {
+		LOWER32(client->in_task_id),
+		UPPER32(client->in_task_id)
+	};
 	
 	futex_down(&async_futex);
 	
 	if (atomic_predec(&client->refcnt) == 0) {
-		hash_table_remove(&client_hash_table, &key, 1);
+		hash_table_remove(&client_hash_table, key, 2);
 		destroy = true;
 	} else
@@ -628,7 +637,7 @@
 }
 
-void *async_get_client_data_by_hash(sysarg_t client_hash)
-{
-	client_t *client = async_client_get(client_hash, false);
+void *async_get_client_data_by_id(task_id_t client_id)
+{
+	client_t *client = async_client_get(client_id, false);
 	if (!client)
 		return NULL;
@@ -641,7 +650,7 @@
 }
 
-void async_put_client_data_by_hash(sysarg_t client_hash)
-{
-	client_t *client = async_client_get(client_hash, false);
+void async_put_client_data_by_id(task_id_t client_id)
+{
+	client_t *client = async_client_get(client_id, false);
 
 	assert(client);
@@ -680,5 +689,5 @@
 	 */
 
-	client_t *client = async_client_get(fibril_connection->in_task_hash, true);
+	client_t *client = async_client_get(fibril_connection->in_task_id, true);
 	if (!client) {
 		ipc_answer_0(fibril_connection->callid, ENOMEM);
@@ -737,5 +746,5 @@
  * particular fibrils.
  *
- * @param in_task_hash  Identification of the incoming connection.
+ * @param in_task_id    Identification of the incoming connection.
  * @param in_phone_hash Identification of the incoming connection.
  * @param callid        Hash of the opening IPC_M_CONNECT_ME_TO call.
@@ -751,5 +760,5 @@
  *
  */
-fid_t async_new_connection(sysarg_t in_task_hash, sysarg_t in_phone_hash,
+fid_t async_new_connection(task_id_t in_task_id, sysarg_t in_phone_hash,
     ipc_callid_t callid, ipc_call_t *call,
     async_client_conn_t cfibril, void *carg)
@@ -763,5 +772,5 @@
 	}
 	
-	conn->in_task_hash = in_task_hash;
+	conn->in_task_id = in_task_id;
 	conn->in_phone_hash = in_phone_hash;
 	list_initialize(&conn->msg_queue);
@@ -822,5 +831,5 @@
 	case IPC_M_CONNECT_ME_TO:
 		/* Open new connection with fibril, etc. */
-		async_new_connection(call->in_task_hash, IPC_GET_ARG5(*call),
+		async_new_connection(call->in_task_id, IPC_GET_ARG5(*call),
 		    callid, call, client_connection, NULL);
 		return;
@@ -970,5 +979,5 @@
 {
 	if (!hash_table_create(&client_hash_table, CLIENT_HASH_TABLE_BUCKETS,
-	    1, &client_hash_table_ops))
+	    2, &client_hash_table_ops))
 		abort();
 	
@@ -986,4 +995,7 @@
 	session_ns->arg2 = 0;
 	session_ns->arg3 = 0;
+	
+	fibril_mutex_initialize(&session_ns->remote_state_mtx);
+	session_ns->remote_state_data = NULL;
 	
 	list_initialize(&session_ns->exch_list);
@@ -1463,13 +1475,19 @@
 		return ENOENT;
 	
-	sysarg_t task_hash;
 	sysarg_t phone_hash;
-	int rc = async_req_3_5(exch, IPC_M_CONNECT_TO_ME, arg1, arg2, arg3,
-	    NULL, NULL, NULL, &task_hash, &phone_hash);
+	sysarg_t rc;
+
+	aid_t req;
+	ipc_call_t answer;
+	req = async_send_3(exch, IPC_M_CONNECT_TO_ME, arg1, arg2, arg3,
+	    &answer);
+	async_wait_for(req, &rc);
 	if (rc != EOK)
-		return rc;
-	
+		return (int) rc;
+
+	phone_hash = IPC_GET_ARG5(answer);
+
 	if (client_receiver != NULL)
-		async_new_connection(task_hash, phone_hash, 0, NULL,
+		async_new_connection(answer.in_task_id, phone_hash, 0, NULL,
 		    client_receiver, carg);
 	
@@ -1546,4 +1564,7 @@
 	sess->arg3 = 0;
 	
+	fibril_mutex_initialize(&sess->remote_state_mtx);
+	sess->remote_state_data = NULL;
+	
 	list_initialize(&sess->exch_list);
 	fibril_mutex_initialize(&sess->mutex);
@@ -1627,4 +1648,7 @@
 	sess->arg3 = arg3;
 	
+	fibril_mutex_initialize(&sess->remote_state_mtx);
+	sess->remote_state_data = NULL;
+	
 	list_initialize(&sess->exch_list);
 	fibril_mutex_initialize(&sess->mutex);
@@ -1632,4 +1656,22 @@
 	
 	return sess;
+}
+
+/** Set arguments for new connections.
+ *
+ * FIXME This is an ugly hack to work around the problem that parallel
+ * exchanges are implemented using parallel connections. When we create
+ * a callback session, the framework does not know arguments for the new
+ * connections.
+ *
+ * The proper solution seems to be to implement parallel exchanges using
+ * tagging.
+ */
+void async_sess_args_set(async_sess_t *sess, sysarg_t arg1, sysarg_t arg2,
+    sysarg_t arg3)
+{
+	sess->arg1 = arg1;
+	sess->arg2 = arg2;
+	sess->arg3 = arg3;
 }
 
@@ -1677,4 +1719,7 @@
 	sess->arg3 = arg3;
 	
+	fibril_mutex_initialize(&sess->remote_state_mtx);
+	sess->remote_state_data = NULL;
+	
 	list_initialize(&sess->exch_list);
 	fibril_mutex_initialize(&sess->mutex);
@@ -1707,4 +1752,7 @@
 	sess->arg2 = 0;
 	sess->arg3 = 0;
+	
+	fibril_mutex_initialize(&sess->remote_state_mtx);
+	sess->remote_state_data = NULL;
 	
 	list_initialize(&sess->exch_list);
@@ -2371,4 +2419,7 @@
 	sess->arg3 = 0;
 	
+	fibril_mutex_initialize(&sess->remote_state_mtx);
+	sess->remote_state_data = NULL;
+	
 	list_initialize(&sess->exch_list);
 	fibril_mutex_initialize(&sess->mutex);
@@ -2417,4 +2468,7 @@
 	sess->arg3 = 0;
 	
+	fibril_mutex_initialize(&sess->remote_state_mtx);
+	sess->remote_state_data = NULL;
+	
 	list_initialize(&sess->exch_list);
 	fibril_mutex_initialize(&sess->mutex);
@@ -2458,4 +2512,7 @@
 	sess->arg2 = 0;
 	sess->arg3 = 0;
+	
+	fibril_mutex_initialize(&sess->remote_state_mtx);
+	sess->remote_state_data = NULL;
 	
 	list_initialize(&sess->exch_list);
@@ -2499,4 +2556,74 @@
 }
 
+/** Lock and get session remote state
+ *
+ * Lock and get the local replica of the remote state
+ * in stateful sessions. The call should be paired
+ * with async_remote_state_release*().
+ *
+ * @param[in] sess Stateful session.
+ *
+ * @return Local replica of the remote state.
+ *
+ */
+void *async_remote_state_acquire(async_sess_t *sess)
+{
+	fibril_mutex_lock(&sess->remote_state_mtx);
+	return sess->remote_state_data;
+}
+
+/** Update the session remote state
+ *
+ * Update the local replica of the remote state
+ * in stateful sessions. The remote state must
+ * be already locked.
+ *
+ * @param[in] sess  Stateful session.
+ * @param[in] state New local replica of the remote state.
+ *
+ */
+void async_remote_state_update(async_sess_t *sess, void *state)
+{
+	assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
+	sess->remote_state_data = state;
+}
+
+/** Release the session remote state
+ *
+ * Unlock the local replica of the remote state
+ * in stateful sessions.
+ *
+ * @param[in] sess Stateful session.
+ *
+ */
+void async_remote_state_release(async_sess_t *sess)
+{
+	assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
+	
+	fibril_mutex_unlock(&sess->remote_state_mtx);
+}
+
+/** Release the session remote state and end an exchange
+ *
+ * Unlock the local replica of the remote state
+ * in stateful sessions. This is convenience function
+ * which gets the session pointer from the exchange
+ * and also ends the exchange.
+ *
+ * @param[in] exch Stateful session's exchange.
+ *
+ */
+void async_remote_state_release_exchange(async_exch_t *exch)
+{
+	if (exch == NULL)
+		return;
+	
+	async_sess_t *sess = exch->sess;
+	assert(fibril_mutex_is_locked(&sess->remote_state_mtx));
+	
+	async_exchange_end(exch);
+	fibril_mutex_unlock(&sess->remote_state_mtx);
+}
+
 /** @}
  */
Index: uspace/lib/c/generic/async_obsolete.c
===================================================================
--- uspace/lib/c/generic/async_obsolete.c	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/lib/c/generic/async_obsolete.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -38,4 +38,5 @@
 #include <async.h>
 #include <async_obsolete.h>
+#include "private/async.h"
 #undef LIBC_ASYNC_C_
 #undef LIBC_ASYNC_OBSOLETE_C_
@@ -44,5 +45,4 @@
 #include <malloc.h>
 #include <errno.h>
-#include "private/async.h"
 
 /** Send message and return id of the sent message.
Index: uspace/lib/c/generic/devman.c
===================================================================
--- uspace/lib/c/generic/devman.c	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/lib/c/generic/devman.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -1,5 +1,5 @@
 /*
  * Copyright (c) 2007 Josef Cejka
- * Copyright (c) 2009 Jiri Svoboda
+ * Copyright (c) 2011 Jiri Svoboda
  * Copyright (c) 2010 Lenka Trochtova
  * All rights reserved.
@@ -89,5 +89,5 @@
 			if (devman_driver_block_sess == NULL)
 				devman_driver_block_sess =
-				    service_connect_blocking(EXCHANGE_SERIALIZE,
+				    service_connect_blocking(EXCHANGE_PARALLEL,
 				    SERVICE_DEVMAN, DEVMAN_DRIVER, 0);
 		}
@@ -138,5 +138,5 @@
 		if (devman_driver_sess == NULL)
 			devman_driver_sess =
-			    service_connect(EXCHANGE_SERIALIZE, SERVICE_DEVMAN,
+			    service_connect(EXCHANGE_PARALLEL, SERVICE_DEVMAN,
 			    DEVMAN_DRIVER, 0);
 		
@@ -195,5 +195,5 @@
 	
 	exch = devman_exchange_begin(DEVMAN_DRIVER);
-	async_connect_to_me(exch, 0, 0, 0, NULL, NULL);
+	async_connect_to_me(exch, 0, 0, 0, conn, NULL);
 	devman_exchange_end(exch);
 	
@@ -342,5 +342,5 @@
 }
 
-int devman_device_get_handle(const char *pathname, devman_handle_t *handle,
+int devman_fun_get_handle(const char *pathname, devman_handle_t *handle,
     unsigned int flags)
 {
@@ -383,5 +383,132 @@
 }
 
-int devman_get_device_path(devman_handle_t handle, char *path, size_t path_size)
+static int devman_get_str_internal(sysarg_t method, sysarg_t arg1, char *buf,
+    size_t buf_size)
+{
+	async_exch_t *exch;
+	ipc_call_t dreply;
+	size_t act_size;
+	sysarg_t dretval;
+	
+	exch = devman_exchange_begin_blocking(LOC_PORT_CONSUMER);
+	
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, method, arg1, &answer);
+	aid_t dreq = async_data_read(exch, buf, buf_size - 1, &dreply);
+	async_wait_for(dreq, &dretval);
+	
+	devman_exchange_end(exch);
+	
+	if (dretval != EOK) {
+		async_wait_for(req, NULL);
+		return dretval;
+	}
+	
+	sysarg_t retval;
+	async_wait_for(req, &retval);
+	
+	if (retval != EOK)
+		return retval;
+	
+	act_size = IPC_GET_ARG2(dreply);
+	assert(act_size <= buf_size - 1);
+	buf[act_size] = '\0';
+	
+	return EOK;
+}
+
+int devman_fun_get_path(devman_handle_t handle, char *buf, size_t buf_size)
+{
+	return devman_get_str_internal(DEVMAN_FUN_GET_PATH, handle, buf,
+	    buf_size);
+}
+
+int devman_fun_get_name(devman_handle_t handle, char *buf, size_t buf_size)
+{
+	return devman_get_str_internal(DEVMAN_FUN_GET_NAME, handle, buf,
+	    buf_size);
+}
+
+static int devman_get_handles_once(sysarg_t method, sysarg_t arg1,
+    devman_handle_t *handle_buf, size_t buf_size, size_t *act_size)
+{
+	async_exch_t *exch = devman_exchange_begin_blocking(DEVMAN_CLIENT);
+
+	ipc_call_t answer;
+	aid_t req = async_send_1(exch, method, arg1, &answer);
+	int rc = async_data_read_start(exch, handle_buf, buf_size);
+	
+	devman_exchange_end(exch);
+	
+	if (rc != EOK) {
+		async_wait_for(req, NULL);
+		return rc;
+	}
+	
+	sysarg_t retval;
+	async_wait_for(req, &retval);
+	
+	if (retval != EOK) {
+		return retval;
+	}
+	
+	*act_size = IPC_GET_ARG1(answer);
+	return EOK;
+}
+
+/** Get list of handles.
+ *
+ * Returns an allocated array of handles.
+ *
+ * @param method	IPC method
+ * @param arg1		IPC argument 1
+ * @param data		Place to store pointer to array of handles
+ * @param count		Place to store number of handles
+ * @return 		EOK on success or negative error code
+ */
+static int devman_get_handles_internal(sysarg_t method, sysarg_t arg1,
+    devman_handle_t **data, size_t *count)
+{
+	devman_handle_t *handles;
+	size_t act_size;
+	size_t alloc_size;
+	int rc;
+
+	*data = NULL;
+	act_size = 0;	/* silence warning */
+
+	rc = devman_get_handles_once(method, arg1, NULL, 0,
+	    &act_size);
+	if (rc != EOK)
+		return rc;
+
+	alloc_size = act_size;
+	handles = malloc(alloc_size);
+	if (handles == NULL)
+		return ENOMEM;
+
+	while (true) {
+		rc = devman_get_handles_once(method, arg1, handles, alloc_size,
+		    &act_size);
+		if (rc != EOK)
+			return rc;
+
+		if (act_size <= alloc_size)
+			break;
+
+		alloc_size *= 2;
+		free(handles);
+
+		handles = malloc(alloc_size);
+		if (handles == NULL)
+			return ENOMEM;
+	}
+
+	*count = act_size / sizeof(devman_handle_t);
+	*data = handles;
+	return EOK;
+}
+
+int devman_fun_get_child(devman_handle_t funh, devman_handle_t *devh)
 {
 	async_exch_t *exch = devman_exchange_begin(DEVMAN_CLIENT);
@@ -389,45 +516,16 @@
 		return ENOMEM;
 	
-	ipc_call_t answer;
-	aid_t req = async_send_1(exch, DEVMAN_DEVICE_GET_DEVICE_PATH,
-	    handle, &answer);
-	
-	ipc_call_t data_request_call;
-	aid_t data_request = async_data_read(exch, path, path_size,
-	    &data_request_call);
-	
-	devman_exchange_end(exch);
-	
-	if (data_request == 0) {
-		async_wait_for(req, NULL);
-		return ENOMEM;
-	}
-	
-	sysarg_t data_request_rc;
-	async_wait_for(data_request, &data_request_rc);
-	
-	sysarg_t opening_request_rc;
-	async_wait_for(req, &opening_request_rc);
-	
-	if (data_request_rc != EOK) {
-		/* Prefer the return code of the opening request. */
-		if (opening_request_rc != EOK)
-			return (int) opening_request_rc;
-		else
-			return (int) data_request_rc;
-	}
-	
-	if (opening_request_rc != EOK)
-		return (int) opening_request_rc;
-	
-	/* To be on the safe-side. */
-	path[path_size - 1] = 0;
-	size_t transferred_size = IPC_GET_ARG2(data_request_call);
-	if (transferred_size >= path_size)
-		return ELIMIT;
-	
-	/* Terminate the string (trailing 0 not send over IPC). */
-	path[transferred_size] = 0;
-	return EOK;
+	sysarg_t retval = async_req_1_1(exch, DEVMAN_FUN_GET_CHILD,
+	    funh, devh);
+	
+	devman_exchange_end(exch);
+	return (int) retval;
+}
+
+int devman_dev_get_functions(devman_handle_t devh, devman_handle_t **funcs,
+    size_t *count)
+{
+	return devman_get_handles_internal(DEVMAN_DEV_GET_FUNCTIONS,
+	    devh, funcs, count);
 }
 
Index: uspace/lib/c/generic/io/printf_core.c
===================================================================
--- uspace/lib/c/generic/io/printf_core.c	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/lib/c/generic/io/printf_core.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -206,5 +206,5 @@
 	}
 	
-	return (int) (counter + 1);
+	return (int) (counter);
 }
 
@@ -244,5 +244,5 @@
 	}
 	
-	return (int) (counter + 1);
+	return (int) (counter);
 }
 
Index: uspace/lib/c/generic/ipc.c
===================================================================
--- uspace/lib/c/generic/ipc.c	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/lib/c/generic/ipc.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -47,4 +47,5 @@
 #include <futex.h>
 #include <fibril.h>
+#include <macros.h>
 
 /**
@@ -611,5 +612,5 @@
 /** Request callback connection.
  *
- * The @a taskhash and @a phonehash identifiers returned
+ * The @a task_id and @a phonehash identifiers returned
  * by the kernel can be used for connection tracking.
  *
@@ -618,5 +619,5 @@
  * @param arg2      User defined argument.
  * @param arg3      User defined argument.
- * @param taskhash  Opaque identifier of the client task.
+ * @param task_id   Identifier of the client task.
  * @param phonehash Opaque identifier of the phone that will
  *                  be used for incoming calls.
@@ -626,8 +627,14 @@
  */
 int ipc_connect_to_me(int phoneid, sysarg_t arg1, sysarg_t arg2, sysarg_t arg3,
-    sysarg_t *taskhash, sysarg_t *phonehash)
-{
-	return ipc_call_sync_3_5(phoneid, IPC_M_CONNECT_TO_ME, arg1, arg2,
-	    arg3, NULL, NULL, NULL, taskhash, phonehash);
+    task_id_t *task_id, sysarg_t *phonehash)
+{
+	ipc_call_t data;
+	int rc = __SYSCALL6(SYS_IPC_CALL_SYNC_FAST, phoneid,
+	    IPC_M_CONNECT_TO_ME, arg1, arg2, arg3, (sysarg_t) &data);
+	if (rc == EOK) {
+		*task_id = data.in_task_id;
+		*phonehash = IPC_GET_ARG5(data);
+	}	
+	return rc;
 }
 
Index: uspace/lib/c/generic/loader.c
===================================================================
--- uspace/lib/c/generic/loader.c	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/lib/c/generic/loader.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -263,6 +263,5 @@
 	
 	int i;
-	for (i = 0; files[i]; i++)
-		;
+	for (i = 0; files[i]; i++);
 
 	ipc_call_t answer;
Index: uspace/lib/c/generic/loc.c
===================================================================
--- uspace/lib/c/generic/loc.c	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/lib/c/generic/loc.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -315,6 +315,6 @@
 /** Register new service.
  *
- * @param fqsn	Fully qualified service name
- * @param sid	Output: ID of new service
+ * @param fqsn Fully qualified service name
+ * @param sid  Output: ID of new service
  *
  */
Index: uspace/lib/c/generic/ns.c
===================================================================
--- uspace/lib/c/generic/ns.c	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/lib/c/generic/ns.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -56,4 +56,11 @@
 	async_exchange_end(exch);
 	
+	/*
+	 * FIXME Ugly hack to work around limitation of implementing
+	 * parallel exchanges using multiple connections. Shift out
+	 * first argument for non-initial connections.
+	 */
+	async_sess_args_set(sess, arg2, arg3, 0);
+	
 	return sess;
 }
@@ -66,4 +73,11 @@
 	    async_connect_me_to_blocking(mgmt, exch, service, arg2, arg3);
 	async_exchange_end(exch);
+	
+	/*
+	 * FIXME Ugly hack to work around limitation of implementing
+	 * parallel exchanges using multiple connections. Shift out
+	 * first argument for non-initial connections.
+	 */
+	async_sess_args_set(sess, arg2, arg3, 0);
 	
 	return sess;
Index: uspace/lib/c/generic/private/async.h
===================================================================
--- uspace/lib/c/generic/private/async.h	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/lib/c/generic/private/async.h	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -36,9 +36,58 @@
 #define LIBC_PRIVATE_ASYNC_H_
 
-#include <ipc/common.h>
+#include <async.h>
 #include <adt/list.h>
 #include <fibril.h>
+#include <fibril_synch.h>
 #include <sys/time.h>
 #include <bool.h>
+
+/** Session data */
+struct _async_sess {
+	/** List of inactive exchanges */
+	list_t exch_list;
+	
+	/** Exchange management style */
+	exch_mgmt_t mgmt;
+	
+	/** Session identification */
+	int phone;
+	
+	/** First clone connection argument */
+	sysarg_t arg1;
+	
+	/** Second clone connection argument */
+	sysarg_t arg2;
+	
+	/** Third clone connection argument */
+	sysarg_t arg3;
+	
+	/** Exchange mutex */
+	fibril_mutex_t mutex;
+	
+	/** Number of opened exchanges */
+	atomic_t refcnt;
+	
+	/** Mutex for stateful connections */
+	fibril_mutex_t remote_state_mtx;
+	
+	/** Data for stateful connections */
+	void *remote_state_data;
+};
+
+/** Exchange data */
+struct _async_exch {
+	/** Link into list of inactive exchanges */
+	link_t sess_link;
+	
+	/** Link into global list of inactive exchanges */
+	link_t global_link;
+	
+	/** Session pointer */
+	async_sess_t *sess;
+	
+	/** Exchange identification */
+	int phone;
+};
 
 /** Structures of this type are used to track the timeout events. */
Index: uspace/lib/c/include/async.h
===================================================================
--- uspace/lib/c/include/async.h	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/lib/c/include/async.h	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -42,5 +42,4 @@
 #include <ipc/common.h>
 #include <fibril.h>
-#include <fibril_synch.h>
 #include <sys/time.h>
 #include <atomic.h>
@@ -96,45 +95,10 @@
 } exch_mgmt_t;
 
-/** Session data */
-typedef struct {
-	/** List of inactive exchanges */
-	list_t exch_list;
-	
-	/** Exchange management style */
-	exch_mgmt_t mgmt;
-	
-	/** Session identification */
-	int phone;
-	
-	/** First clone connection argument */
-	sysarg_t arg1;
-	
-	/** Second clone connection argument */
-	sysarg_t arg2;
-	
-	/** Third clone connection argument */
-	sysarg_t arg3;
-	
-	/** Exchange mutex */
-	fibril_mutex_t mutex;
-	
-	/** Number of opened exchanges */
-	atomic_t refcnt;
-} async_sess_t;
-
-/** Exchange data */
-typedef struct {
-	/** Link into list of inactive exchanges */
-	link_t sess_link;
-	
-	/** Link into global list of inactive exchanges */
-	link_t global_link;
-	
-	/** Session pointer */
-	async_sess_t *sess;
-	
-	/** Exchange identification */
-	int phone;
-} async_exch_t;
+/** Forward declarations */
+struct _async_exch;
+struct _async_sess;
+
+typedef struct _async_sess async_sess_t;
+typedef struct _async_exch async_exch_t;
 
 extern atomic_t threads_in_ipc_wait;
@@ -176,5 +140,5 @@
 extern int async_wait_timeout(aid_t, sysarg_t *, suseconds_t);
 
-extern fid_t async_new_connection(sysarg_t, sysarg_t, ipc_callid_t,
+extern fid_t async_new_connection(task_id_t, sysarg_t, ipc_callid_t,
     ipc_call_t *, async_client_conn_t, void *);
 
@@ -186,6 +150,6 @@
 extern void async_set_client_data_destructor(async_client_data_dtor_t);
 extern void *async_get_client_data(void);
-extern void *async_get_client_data_by_hash(sysarg_t);
-extern void async_put_client_data_by_hash(sysarg_t);
+extern void *async_get_client_data_by_id(task_id_t);
+extern void async_put_client_data_by_id(task_id_t);
 
 extern void async_set_client_connection(async_client_conn_t);
@@ -373,4 +337,10 @@
 
 /*
+ * FIXME These functions just work around problems with parallel exchange
+ * management. Proper solution needs to be implemented.
+ */
+void async_sess_args_set(async_sess_t *sess, sysarg_t, sysarg_t, sysarg_t);
+
+/*
  * User-friendly wrappers for async_share_in_start().
  */
@@ -485,4 +455,9 @@
 extern int async_state_change_finalize(ipc_callid_t, async_exch_t *);
 
+extern void *async_remote_state_acquire(async_sess_t *);
+extern void async_remote_state_update(async_sess_t *, void *);
+extern void async_remote_state_release(async_sess_t *);
+extern void async_remote_state_release_exchange(async_exch_t *);
+
 #endif
 
Index: uspace/lib/c/include/devman.h
===================================================================
--- uspace/lib/c/include/devman.h	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/lib/c/include/devman.h	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -56,7 +56,11 @@
     unsigned int);
 
-extern int devman_device_get_handle(const char *, devman_handle_t *,
+extern int devman_fun_get_handle(const char *, devman_handle_t *,
     unsigned int);
-extern int devman_get_device_path(devman_handle_t, char *, size_t);
+extern int devman_fun_get_child(devman_handle_t, devman_handle_t *);
+extern int devman_dev_get_functions(devman_handle_t, devman_handle_t **,
+    size_t *);
+extern int devman_fun_get_name(devman_handle_t, char *, size_t);
+extern int devman_fun_get_path(devman_handle_t, char *, size_t);
 
 extern int devman_add_device_to_category(devman_handle_t, const char *);
Index: uspace/lib/c/include/ipc/common.h
===================================================================
--- uspace/lib/c/include/ipc/common.h	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/lib/c/include/ipc/common.h	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -39,4 +39,5 @@
 #include <abi/ipc/ipc.h>
 #include <atomic.h>
+#include <task.h>
 
 #define IPC_FLAG_BLOCKING  0x01
@@ -44,5 +45,5 @@
 typedef struct {
 	sysarg_t args[IPC_CALL_LEN];
-	sysarg_t in_task_hash;
+	task_id_t in_task_id;
 	sysarg_t in_phone_hash;
 } ipc_call_t;
Index: uspace/lib/c/include/ipc/devman.h
===================================================================
--- uspace/lib/c/include/ipc/devman.h	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/lib/c/include/ipc/devman.h	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -149,5 +149,8 @@
 typedef enum {
 	DEVMAN_DEVICE_GET_HANDLE = IPC_FIRST_USER_METHOD,
-	DEVMAN_DEVICE_GET_DEVICE_PATH,
+	DEVMAN_DEV_GET_FUNCTIONS,
+	DEVMAN_FUN_GET_CHILD,
+	DEVMAN_FUN_GET_NAME,
+	DEVMAN_FUN_GET_PATH,
 	DEVMAN_FUN_SID_TO_HANDLE
 } client_to_devman_t;
Index: uspace/lib/c/include/ipc/ipc.h
===================================================================
--- uspace/lib/c/include/ipc/ipc.h	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/lib/c/include/ipc/ipc.h	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -254,5 +254,5 @@
     sysarg_t, sysarg_t, void *, ipc_async_callback_t, bool);
 
-extern int ipc_connect_to_me(int, sysarg_t, sysarg_t, sysarg_t, sysarg_t *,
+extern int ipc_connect_to_me(int, sysarg_t, sysarg_t, sysarg_t, task_id_t *,
     sysarg_t *);
 extern int ipc_connect_me(int);
Index: uspace/lib/drv/generic/driver.c
===================================================================
--- uspace/lib/drv/generic/driver.c	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/lib/drv/generic/driver.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -428,6 +428,16 @@
 static void driver_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
 {
+	sysarg_t conn_type;
+
+	if (iid == 0) {
+		/* Callback connection from devman */
+		/* XXX Use separate handler for this type of connection */
+		conn_type = DRIVER_DEVMAN;
+	} else {
+		conn_type = IPC_GET_ARG1(*icall);
+	}
+
 	/* Select interface */
-	switch ((sysarg_t) (IPC_GET_ARG1(*icall))) {
+	switch (conn_type) {
 	case DRIVER_DEVMAN:
 		/* Handle request from device manager */
Index: uspace/lib/usb/src/resolve.c
===================================================================
--- uspace/lib/usb/src/resolve.c	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/lib/usb/src/resolve.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -152,5 +152,5 @@
 		if (str_length(func_start) > 0) {
 			char tmp_path[MAX_DEVICE_PATH ];
-			rc = devman_get_device_path(dev_handle,
+			rc = devman_fun_get_path(dev_handle,
 			    tmp_path, MAX_DEVICE_PATH);
 			if (rc != EOK) {
@@ -173,5 +173,5 @@
 
 	/* First try to get the device handle. */
-	rc = devman_device_get_handle(path, &dev_handle, 0);
+	rc = devman_fun_get_handle(path, &dev_handle, 0);
 	if (rc != EOK) {
 		free(path);
@@ -184,5 +184,5 @@
 		/* Get device handle first. */
 		devman_handle_t tmp_handle;
-		rc = devman_device_get_handle(path, &tmp_handle, 0);
+		rc = devman_fun_get_handle(path, &tmp_handle, 0);
 		if (rc != EOK) {
 			free(path);
Index: uspace/lib/usbvirt/src/device.c
===================================================================
--- uspace/lib/usbvirt/src/device.c	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/lib/usbvirt/src/device.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -87,5 +87,5 @@
 	
 	devman_handle_t handle;
-	int rc = devman_device_get_handle(vhc_path, &handle, 0);
+	int rc = devman_fun_get_handle(vhc_path, &handle, 0);
 	if (rc != EOK)
 		return rc;
Index: uspace/srv/devman/devman.c
===================================================================
--- uspace/srv/devman/devman.c	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/srv/devman/devman.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -548,14 +548,4 @@
 
 	fibril_mutex_lock(&driver->driver_mutex);
-	
-	async_exch_t *exch = async_exchange_begin(driver->sess);
-	async_sess_t *sess = async_connect_me_to(EXCHANGE_SERIALIZE, exch,
-	    DRIVER_DEVMAN, 0, 0);
-	async_exchange_end(exch);
-
-	if (!sess) {
-		fibril_mutex_unlock(&driver->driver_mutex);
-		return;
-	}
 
 	/*
@@ -583,5 +573,5 @@
 		fibril_mutex_unlock(&driver->driver_mutex);
 
-		add_device(sess, driver, dev, tree);
+		add_device(driver, dev, tree);
 
 		/*
@@ -603,6 +593,4 @@
 		link = driver->devices.head.next;
 	}
-
-	async_hangup(sess);
 
 	/*
@@ -718,6 +706,5 @@
  * @param node		The device's node in the device tree.
  */
-void add_device(async_sess_t *sess, driver_t *drv, dev_node_t *dev,
-    dev_tree_t *tree)
+void add_device(driver_t *drv, dev_node_t *dev, dev_tree_t *tree)
 {
 	/*
@@ -736,5 +723,5 @@
 	}
 	
-	async_exch_t *exch = async_exchange_begin(sess);
+	async_exch_t *exch = async_exchange_begin(drv->sess);
 	
 	ipc_call_t answer;
@@ -806,16 +793,7 @@
 	fibril_mutex_unlock(&drv->driver_mutex);
 
-	if (is_running) {
-		/* Notify the driver about the new device. */
-		async_exch_t *exch = async_exchange_begin(drv->sess);
-		async_sess_t *sess = async_connect_me_to(EXCHANGE_SERIALIZE, exch,
-		    DRIVER_DEVMAN, 0, 0);
-		async_exchange_end(exch);
-		
-		if (sess) {
-			add_device(sess, drv, dev, tree);
-			async_hangup(sess);
-		}
-	}
+	/* Notify the driver about the new device. */
+	if (is_running)
+		add_device(drv, dev, tree);
 	
 	return true;
@@ -919,4 +897,35 @@
 	return dev;
 }
+
+/** Get list of device functions. */
+int dev_get_functions(dev_tree_t *tree, dev_node_t *dev,
+    devman_handle_t *hdl_buf, size_t buf_size, size_t *act_size)
+{
+	size_t act_cnt;
+	size_t buf_cnt;
+
+	assert(fibril_rwlock_is_locked(&tree->rwlock));
+
+	buf_cnt = buf_size / sizeof(devman_handle_t);
+
+	act_cnt = list_count(&dev->functions);
+	*act_size = act_cnt * sizeof(devman_handle_t);
+
+	if (buf_size % sizeof(devman_handle_t) != 0)
+		return EINVAL;
+
+	size_t pos = 0;
+	list_foreach(dev->functions, item) {
+		fun_node_t *fun =
+		    list_get_instance(item, fun_node_t, dev_functions);
+
+		if (pos < buf_cnt)
+			hdl_buf[pos] = fun->handle;
+		pos++;
+	}
+
+	return EOK;
+}
+
 
 /* Function nodes */
@@ -1145,5 +1154,5 @@
 	char *rel_path = path;
 	char *next_path_elem = NULL;
-	bool cont = true;
+	bool cont = (rel_path[1] != '\0');
 	
 	while (cont && fun != NULL) {
Index: uspace/srv/devman/devman.h
===================================================================
--- uspace/srv/devman/devman.h	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/srv/devman/devman.h	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -63,4 +63,9 @@
 typedef struct fun_node fun_node_t;
 
+typedef struct {
+	fibril_mutex_t mutex;
+	struct driver *driver;
+} client_t;
+
 typedef enum {
 	/** Driver has not been started. */
@@ -235,5 +240,5 @@
 extern void add_driver(driver_list_t *, driver_t *);
 extern void attach_driver(dev_node_t *, driver_t *);
-extern void add_device(async_sess_t *, driver_t *, dev_node_t *, dev_tree_t *);
+extern void add_device(driver_t *, dev_node_t *, dev_tree_t *);
 extern bool start_driver(driver_t *);
 
@@ -253,4 +258,6 @@
 extern dev_node_t *find_dev_node(dev_tree_t *tree, devman_handle_t handle);
 extern dev_node_t *find_dev_function(dev_node_t *, const char *);
+extern int dev_get_functions(dev_tree_t *tree, dev_node_t *, devman_handle_t *,
+    size_t, size_t *);
 
 extern fun_node_t *create_fun_node(void);
Index: uspace/srv/devman/main.c
===================================================================
--- uspace/srv/devman/main.c	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/srv/devman/main.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -65,25 +65,18 @@
 static dev_tree_t device_tree;
 
+static int init_running_drv(void *drv);
+
 /** Register running driver. */
-static driver_t *devman_driver_register(void)
-{
-	ipc_call_t icall;
-	ipc_callid_t iid;
+static driver_t *devman_driver_register(ipc_callid_t callid, ipc_call_t *call)
+{
 	driver_t *driver = NULL;
+	char *drv_name = NULL;
 
 	log_msg(LVL_DEBUG, "devman_driver_register");
-	
-	iid = async_get_call(&icall);
-	if (IPC_GET_IMETHOD(icall) != DEVMAN_DRIVER_REGISTER) {
-		async_answer_0(iid, EREFUSED);
-		return NULL;
-	}
-	
-	char *drv_name = NULL;
 	
 	/* Get driver name. */
 	int rc = async_data_write_accept((void **) &drv_name, true, 0, 0, 0, 0);
 	if (rc != EOK) {
-		async_answer_0(iid, rc);
+		async_answer_0(callid, rc);
 		return NULL;
 	}
@@ -98,5 +91,5 @@
 		free(drv_name);
 		drv_name = NULL;
-		async_answer_0(iid, ENOENT);
+		async_answer_0(callid, ENOENT);
 		return NULL;
 	}
@@ -112,5 +105,5 @@
 		    driver->name);
 		fibril_mutex_unlock(&driver->driver_mutex);
-		async_answer_0(iid, EEXISTS);
+		async_answer_0(callid, EEXISTS);
 		return NULL;
 	}
@@ -134,12 +127,12 @@
 	log_msg(LVL_DEBUG, "Creating connection to the `%s' driver.",
 	    driver->name);
-	driver->sess = async_callback_receive(EXCHANGE_SERIALIZE);
+	driver->sess = async_callback_receive(EXCHANGE_PARALLEL);
 	if (!driver->sess) {
 		fibril_mutex_unlock(&driver->driver_mutex);
-		async_answer_0(iid, ENOTSUP);
+		async_answer_0(callid, ENOTSUP);
 		return NULL;
 	}
-	
-	fibril_mutex_unlock(&driver->driver_mutex);
+	/* FIXME: Work around problem with callback sessions */
+	async_sess_args_set(driver->sess, DRIVER_DEVMAN, 0, 0);
 	
 	log_msg(LVL_NOTE,
@@ -147,6 +140,22 @@
 	    driver->name);
 	
-	async_answer_0(iid, EOK);
-	
+	/*
+	 * Initialize the driver as running (e.g. pass assigned devices to it)
+	 * in a separate fibril; the separate fibril is used to enable the
+	 * driver to use devman service during the driver's initialization.
+	 */
+	fid_t fid = fibril_create(init_running_drv, driver);
+	if (fid == 0) {
+		log_msg(LVL_ERROR, "Failed to create initialization fibril " \
+		    "for driver `%s'.", driver->name);
+		fibril_mutex_unlock(&driver->driver_mutex);
+		async_answer_0(callid, ENOMEM);
+		return NULL;
+	}
+	
+	fibril_add_ready(fid);
+	fibril_mutex_unlock(&driver->driver_mutex);
+	
+	async_answer_0(callid, EOK);
 	return driver;
 }
@@ -429,23 +438,15 @@
 static void devman_connection_driver(ipc_callid_t iid, ipc_call_t *icall)
 {
+	client_t *client;
+	driver_t *driver;
+	
 	/* Accept the connection. */
 	async_answer_0(iid, EOK);
 	
-	driver_t *driver = devman_driver_register();
-	if (driver == NULL)
-		return;
-	
-	/*
-	 * Initialize the driver as running (e.g. pass assigned devices to it)
-	 * in a separate fibril; the separate fibril is used to enable the
-	 * driver to use devman service during the driver's initialization.
-	 */
-	fid_t fid = fibril_create(init_running_drv, driver);
-	if (fid == 0) {
-		log_msg(LVL_ERROR, "Failed to create initialization fibril " \
-		    "for driver `%s'.", driver->name);
-		return;
-	}
-	fibril_add_ready(fid);
+	client = async_get_client_data();
+	if (client == NULL) {
+		log_msg(LVL_ERROR, "Failed to allocate client data.");
+		return;
+	}
 	
 	while (true) {
@@ -456,5 +457,26 @@
 			break;
 		
+		if (IPC_GET_IMETHOD(call) != DEVMAN_DRIVER_REGISTER) {
+			fibril_mutex_lock(&client->mutex);
+			driver = client->driver;
+			fibril_mutex_unlock(&client->mutex);
+			if (driver == NULL) {
+				/* First call must be to DEVMAN_DRIVER_REGISTER */
+				async_answer_0(callid, ENOTSUP);
+				continue;
+			}
+		}
+		
 		switch (IPC_GET_IMETHOD(call)) {
+		case DEVMAN_DRIVER_REGISTER:
+			fibril_mutex_lock(&client->mutex);
+			if (client->driver != NULL) {
+				fibril_mutex_unlock(&client->mutex);
+				async_answer_0(callid, EINVAL);
+				continue;
+			}
+			client->driver = devman_driver_register(callid, &call);
+			fibril_mutex_unlock(&client->mutex);
+			break;
 		case DEVMAN_ADD_FUNCTION:
 			devman_add_function(callid, &call);
@@ -497,7 +519,6 @@
 }
 
-/** Find device path by its handle. */
-static void devman_get_device_path_by_handle(ipc_callid_t iid,
-    ipc_call_t *icall)
+/** Get device name. */
+static void devman_fun_get_name(ipc_callid_t iid, ipc_call_t *icall)
 {
 	devman_handle_t handle = IPC_GET_ARG1(*icall);
@@ -523,4 +544,41 @@
 	}
 
+	size_t sent_length = str_size(fun->name);
+	if (sent_length > data_len) {
+		sent_length = data_len;
+	}
+
+	async_data_read_finalize(data_callid, fun->name, sent_length);
+	async_answer_0(iid, EOK);
+
+	free(buffer);
+}
+
+
+/** Get device path. */
+static void devman_fun_get_path(ipc_callid_t iid, ipc_call_t *icall)
+{
+	devman_handle_t handle = IPC_GET_ARG1(*icall);
+
+	fun_node_t *fun = find_fun_node(&device_tree, handle);
+	if (fun == NULL) {
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+
+	ipc_callid_t data_callid;
+	size_t data_len;
+	if (!async_data_read_receive(&data_callid, &data_len)) {
+		async_answer_0(iid, EINVAL);
+		return;
+	}
+
+	void *buffer = malloc(data_len);
+	if (buffer == NULL) {
+		async_answer_0(data_callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+
 	size_t sent_length = str_size(fun->pathname);
 	if (sent_length > data_len) {
@@ -532,4 +590,78 @@
 
 	free(buffer);
+}
+
+static void devman_dev_get_functions(ipc_callid_t iid, ipc_call_t *icall)
+{
+	ipc_callid_t callid;
+	size_t size;
+	size_t act_size;
+	int rc;
+	
+	if (!async_data_read_receive(&callid, &size)) {
+		async_answer_0(callid, EREFUSED);
+		async_answer_0(iid, EREFUSED);
+		return;
+	}
+	
+	fibril_rwlock_read_lock(&device_tree.rwlock);
+	
+	dev_node_t *dev = find_dev_node_no_lock(&device_tree,
+	    IPC_GET_ARG1(*icall));
+	if (dev == NULL) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(callid, ENOENT);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	devman_handle_t *hdl_buf = (devman_handle_t *) malloc(size);
+	if (hdl_buf == NULL) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(callid, ENOMEM);
+		async_answer_0(iid, ENOMEM);
+		return;
+	}
+	
+	rc = dev_get_functions(&device_tree, dev, hdl_buf, size, &act_size);
+	if (rc != EOK) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(callid, rc);
+		async_answer_0(iid, rc);
+		return;
+	}
+	
+	fibril_rwlock_read_unlock(&device_tree.rwlock);
+	
+	sysarg_t retval = async_data_read_finalize(callid, hdl_buf, size);
+	free(hdl_buf);
+	
+	async_answer_1(iid, retval, act_size);
+}
+
+
+/** Get handle for child device of a function. */
+static void devman_fun_get_child(ipc_callid_t iid, ipc_call_t *icall)
+{
+	fun_node_t *fun;
+	
+	fibril_rwlock_read_lock(&device_tree.rwlock);
+	
+	fun = find_fun_node(&device_tree, IPC_GET_ARG1(*icall));
+	if (fun == NULL) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	if (fun->child == NULL) {
+		fibril_rwlock_read_unlock(&device_tree.rwlock);
+		async_answer_0(iid, ENOENT);
+		return;
+	}
+	
+	async_answer_1(iid, EOK, fun->child->handle);
+	
+	fibril_rwlock_read_unlock(&device_tree.rwlock);
 }
 
@@ -566,6 +698,15 @@
 			devman_function_get_handle(callid, &call);
 			break;
-		case DEVMAN_DEVICE_GET_DEVICE_PATH:
-			devman_get_device_path_by_handle(callid, &call);
+		case DEVMAN_DEV_GET_FUNCTIONS:
+			devman_dev_get_functions(callid, &call);
+			break;
+		case DEVMAN_FUN_GET_CHILD:
+			devman_fun_get_child(callid, &call);
+			break;
+		case DEVMAN_FUN_GET_NAME:
+			devman_fun_get_name(callid, &call);
+			break;
+		case DEVMAN_FUN_GET_PATH:
+			devman_fun_get_path(callid, &call);
 			break;
 		case DEVMAN_FUN_SID_TO_HANDLE:
@@ -695,5 +836,5 @@
 static void devman_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
 {
-	/* Select interface. */
+	/* Select port. */
 	switch ((sysarg_t) (IPC_GET_ARG1(*icall))) {
 	case DEVMAN_DRIVER:
@@ -721,4 +862,21 @@
 }
 
+static void *devman_client_data_create(void)
+{
+	client_t *client;
+	
+	client = calloc(1, sizeof(client_t));
+	if (client == NULL)
+		return NULL;
+	
+	fibril_mutex_initialize(&client->mutex);
+	return client;
+}
+
+static void devman_client_data_destroy(void *data)
+{
+	free(data);
+}
+
 /** Initialize device manager internal structures. */
 static bool devman_init(void)
@@ -767,5 +925,7 @@
 	}
 	
-	/* Set a handler of incomming connections. */
+	/* Set handlers for incoming connections. */
+	async_set_client_data_constructor(devman_client_data_create);
+	async_set_client_data_destructor(devman_client_data_destroy);
 	async_set_client_connection(devman_connection);
 
Index: uspace/srv/vfs/vfs.c
===================================================================
--- uspace/srv/vfs/vfs.c	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/srv/vfs/vfs.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -36,4 +36,5 @@
  */
 
+#include <vfs/vfs.h>
 #include <ipc/services.h>
 #include <abi/ipc/event.h>
@@ -47,8 +48,12 @@
 #include <as.h>
 #include <atomic.h>
-#include <vfs/vfs.h>
+#include <macros.h>
 #include "vfs.h"
 
 #define NAME  "vfs"
+
+enum {
+	VFS_TASK_STATE_CHANGE
+};
 
 static void vfs_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
@@ -134,8 +139,4 @@
 }
 
-enum {
-	VFS_TASK_STATE_CHANGE
-};
-
 static void notification_received(ipc_callid_t callid, ipc_call_t *call)
 {
@@ -143,6 +144,8 @@
 	case VFS_TASK_STATE_CHANGE:
 		if (IPC_GET_ARG1(*call) == VFS_PASS_HANDLE)
-			vfs_pass_handle(IPC_GET_ARG4(*call),
-			    IPC_GET_ARG5(*call), (int) IPC_GET_ARG2(*call));
+			vfs_pass_handle(
+			    (task_id_t) MERGE_LOUP32(IPC_GET_ARG4(*call),
+			    IPC_GET_ARG5(*call)), call->in_task_id,
+			    (int) IPC_GET_ARG2(*call));
 		break;
 	default:
Index: uspace/srv/vfs/vfs.h
===================================================================
--- uspace/srv/vfs/vfs.h	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/srv/vfs/vfs.h	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -41,4 +41,5 @@
 #include <bool.h>
 #include <ipc/vfs.h>
+#include <task.h>
 
 #ifndef dprintf
@@ -188,5 +189,5 @@
 extern void vfs_client_data_destroy(void *);
 
-extern void vfs_pass_handle(sysarg_t, sysarg_t, int);
+extern void vfs_pass_handle(task_id_t, task_id_t, int);
 extern int vfs_wait_handle_internal(void);
 
Index: uspace/srv/vfs/vfs_file.c
===================================================================
--- uspace/srv/vfs/vfs_file.c	(revision 1877128055f1206aa0e032aef85f30c1cd756d28)
+++ uspace/srv/vfs/vfs_file.c	(revision a6480d5ff230b964a3e3c59d2599383e2065c5f3)
@@ -44,4 +44,5 @@
 #include <fibril_synch.h>
 #include <adt/list.h>
+#include <task.h>
 #include "vfs.h"
 
@@ -346,5 +347,5 @@
 }
 
-void vfs_pass_handle(sysarg_t donor_hash, sysarg_t acceptor_hash, int donor_fd)
+void vfs_pass_handle(task_id_t donor_id, task_id_t acceptor_id, int donor_fd)
 {
 	vfs_client_data_t *donor_data = NULL;
@@ -355,5 +356,5 @@
 	int acceptor_fd;
 
-	acceptor_data = async_get_client_data_by_hash(acceptor_hash);
+	acceptor_data = async_get_client_data_by_id(acceptor_id);
 	if (!acceptor_data)
 		return;
@@ -365,5 +366,5 @@
 	bh->handle = -1;
 
-	donor_data = async_get_client_data_by_hash(donor_hash);
+	donor_data = async_get_client_data_by_id(donor_id);
 	if (!donor_data)
 		goto out;
@@ -402,7 +403,7 @@
 
 	if (donor_data)
-		async_put_client_data_by_hash(donor_hash);
+		async_put_client_data_by_id(donor_id);
 	if (acceptor_data)
-		async_put_client_data_by_hash(acceptor_hash);
+		async_put_client_data_by_id(acceptor_id);
 	if (donor_file)
 		_vfs_file_put(donor_data, donor_file);
