Index: .bzrignore
===================================================================
--- .bzrignore	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ .bzrignore	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -1,33 +1,54 @@
+*.ag.probe.[cs]
 *.disasm
+*.la
+*.lo
 *.map
 *.prev
-*.ag.probe.[cs]
-*.lo
-*.la
 *.so.*
-Makefile.depend
 Makefile.ag.depend
 Makefile.common
 Makefile.config
-cscope.out
+Makefile.depend
 autotool/
+boot/distroot/
+boot/initrd.img
 common.h
 config.h
+cscope.out
 hdisk.img
 image.iso
-boot/distroot/
-boot/initrd.img
+kernel/arch/amd64/_link.ld
+kernel/arch/amd64/include/arch/boot/memmap_struct.h
+kernel/arch/amd64/include/arch/common.h
+kernel/arch/amd64/include/arch/context_struct.h
+kernel/arch/amd64/include/arch/istate_struct.h
+kernel/arch/amd64/include/arch/kseg_struct.h
+kernel/arch/arm32/_link.ld
+kernel/arch/arm32/include/arch/context_struct.h
+kernel/arch/arm32/include/arch/istate_struct.h
+kernel/arch/ia32/_link.ld
+kernel/arch/ia32/include/arch/boot/memmap_struct.h
+kernel/arch/ia32/include/arch/context_struct.h
+kernel/arch/ia32/include/arch/istate_struct.h
+kernel/arch/ia64/include/arch/context_struct.h
+kernel/arch/ia64/include/arch/istate_struct.h
+kernel/arch/mips32/include/arch/context_struct.h
+kernel/arch/mips32/include/arch/fpu_context_struct.h
+kernel/arch/mips32/include/arch/istate_struct.h
+kernel/arch/ppc32/include/arch/context_struct.h
+kernel/arch/ppc32/include/arch/fpu_context_struct.h
+kernel/arch/ppc32/include/arch/istate_struct.h
+kernel/arch/sparc64/include/arch/context_struct.h
+kernel/arch/sparc64/include/arch/istate_struct.h
+kernel/genarch/include/genarch/multiboot/multiboot_info_struct.h
+kernel/genarch/include/genarch/multiboot/multiboot_memmap_struct.h
+kernel/generic/src/debug/real_map.bin
 kernel/kernel.bin
 kernel/kernel.dump
 kernel/kernel.raw
-kernel/arch/amd64/_link.ld
-kernel/arch/amd64/include/arch/common.h
-kernel/arch/amd64/include/arch/kseg_struct.h
-kernel/arch/amd64/include/arch/boot/memmap_struct.h
-kernel/generic/src/debug/real_map.bin
 uspace/app/barber/barber
 uspace/app/barber/images.c
+uspace/app/barber/images.h
 uspace/app/barber/images.s
-uspace/app/barber/images.h
 uspace/app/barber/images.zip
 uspace/app/barber/images_desc.c
@@ -37,15 +58,18 @@
 uspace/app/blkdump/blkdump
 uspace/app/bnchmark/bnchmark
-uspace/drv/char/pl050/pl050
+uspace/app/corecfg/corecfg
 uspace/app/date/date
 uspace/app/devctl/devctl
+uspace/app/df/df
 uspace/app/dltest/dltest
 uspace/app/dltests/dltests
 uspace/app/dnscfg/dnscfg
 uspace/app/dnsres/dnsres
+uspace/app/download/download
 uspace/app/edit/edit
 uspace/app/fdisk/fdisk
 uspace/app/fontviewer/fontviewer
 uspace/app/getterm/getterm
+uspace/app/gunzip/gunzip
 uspace/app/inet/inet
 uspace/app/init/init
@@ -62,12 +86,13 @@
 uspace/app/modplay/modplay
 uspace/app/netecho/netecho
+uspace/app/netspeed/netspeed
 uspace/app/nettest1/nettest1
 uspace/app/nettest2/nettest2
 uspace/app/nettest3/nettest3
 uspace/app/nic/nic
-uspace/app/netecho/netecho
 uspace/app/nterm/nterm
 uspace/app/ping/ping
 uspace/app/ping6/ping6
+uspace/app/pkg/pkg
 uspace/app/rcubench/rcubench
 uspace/app/rcutest/rcutest
@@ -85,7 +110,9 @@
 uspace/app/top/top
 uspace/app/trace/trace
+uspace/app/untar/untar
 uspace/app/usbinfo/usbinfo
 uspace/app/vcalc/vcalc
 uspace/app/vdemo/vdemo
+uspace/app/viewer/viewer
 uspace/app/vlaunch/images.c
 uspace/app/vlaunch/images.h
@@ -99,8 +126,4 @@
 uspace/app/websrv/websrv
 uspace/app/wifi_supplicant/wifi_supplicant
-uspace/dist/demo.txt
-uspace/dist/demo.wav
-uspace/dist/demo.xm
-uspace/dist/logo.tga
 uspace/dist/app/barber
 uspace/dist/app/bdsh
@@ -108,14 +131,18 @@
 uspace/dist/app/blkdump
 uspace/dist/app/bnchmark
+uspace/dist/app/corecfg
 uspace/dist/app/date
 uspace/dist/app/devctl
+uspace/dist/app/df
 uspace/dist/app/dltest
 uspace/dist/app/dltests
 uspace/dist/app/dnscfg
 uspace/dist/app/dnsres
+uspace/dist/app/download
 uspace/dist/app/edit
 uspace/dist/app/fdisk
 uspace/dist/app/fontviewer
 uspace/dist/app/getterm
+uspace/dist/app/gunzip
 uspace/dist/app/inet
 uspace/dist/app/kill
@@ -131,12 +158,13 @@
 uspace/dist/app/modplay
 uspace/dist/app/netecho
+uspace/dist/app/netspeed
 uspace/dist/app/nettest1
 uspace/dist/app/nettest2
 uspace/dist/app/nettest3
 uspace/dist/app/nic
-uspace/dist/app/netecho
 uspace/dist/app/nterm
 uspace/dist/app/ping
 uspace/dist/app/ping6
+uspace/dist/app/pkg
 uspace/dist/app/rcubench
 uspace/dist/app/rcutest
@@ -154,7 +182,9 @@
 uspace/dist/app/top
 uspace/dist/app/trace
+uspace/dist/app/untar
 uspace/dist/app/usbinfo
 uspace/dist/app/vcalc
 uspace/dist/app/vdemo
+uspace/dist/app/viewer
 uspace/dist/app/vlaunch
 uspace/dist/app/vterm
@@ -163,7 +193,11 @@
 uspace/dist/app/websrv
 uspace/dist/app/wifi_supplicant
+uspace/dist/demo.txt
+uspace/dist/demo.wav
+uspace/dist/demo.xm
 uspace/dist/drv/ahci/
 uspace/dist/drv/ar9271/
 uspace/dist/drv/ata_bd/
+uspace/dist/drv/atkbd/
 uspace/dist/drv/cmos-rtc/
 uspace/dist/drv/e1k/
@@ -181,5 +215,4 @@
 uspace/dist/drv/root/
 uspace/dist/drv/rootpc/
-uspace/dist/drv/virt/
 uspace/dist/drv/rtl8139/
 uspace/dist/drv/rtl8169/
@@ -195,6 +228,9 @@
 uspace/dist/drv/usbmid/
 uspace/dist/drv/vhc/
+uspace/dist/drv/virt/
 uspace/dist/drv/xtkbd/
-uspace/dist/drv/atkbd/
+uspace/dist/inc/_link.ld
+uspace/dist/inc/c/
+uspace/dist/logo.tga
 uspace/dist/srv/apic
 uspace/dist/srv/cdfs
@@ -203,4 +239,5 @@
 uspace/dist/srv/console
 uspace/dist/srv/devman
+uspace/dist/srv/dhcp
 uspace/dist/srv/dnsrsrv
 uspace/dist/srv/ethip
@@ -214,9 +251,12 @@
 uspace/dist/srv/input
 uspace/dist/srv/isdv4_tablet
+uspace/dist/srv/klog
 uspace/dist/srv/locfs
 uspace/dist/srv/loopip
 uspace/dist/srv/mfs
+uspace/dist/srv/nconfsrv
 uspace/dist/srv/output
 uspace/dist/srv/remcons
+uspace/dist/srv/rfb
 uspace/dist/srv/sata_bd
 uspace/dist/srv/slip
@@ -245,15 +285,12 @@
 uspace/drv/bus/usb/usbmid/usbmid
 uspace/drv/bus/usb/vhc/vhc
+uspace/drv/char/atkbd/atkbd
 uspace/drv/char/i8042/i8042
 uspace/drv/char/ns8250/ns8250
+uspace/drv/char/pl050/pl050
 uspace/drv/char/ps2mouse/ps2mouse
 uspace/drv/char/xtkbd/xtkbd
-uspace/drv/char/atkbd/atkbd
+uspace/drv/fb/amdm37x_dispc/amdm37x_dispc
 uspace/drv/fb/kfb/kfb
-uspace/drv/root/root/root
-uspace/drv/root/virt/virt
-uspace/drv/platform/icp/icp
-uspace/drv/platform/pc/pc
-uspace/drv/platform/rootpc/rootpc
 uspace/drv/nic/ar9271/ar9271
 uspace/drv/nic/e1k/e1k
@@ -261,4 +298,13 @@
 uspace/drv/nic/rtl8139/rtl8139
 uspace/drv/nic/rtl8169/rtl8169
+uspace/drv/platform/amdm37x/amdm37x
+uspace/drv/platform/icp/icp
+uspace/drv/platform/mac/mac
+uspace/drv/platform/malta/malta
+uspace/drv/platform/msim/msim
+uspace/drv/platform/pc/pc
+uspace/drv/platform/rootpc/rootpc
+uspace/drv/root/root/root
+uspace/drv/root/virt/virt
 uspace/drv/test/test1/test1
 uspace/drv/test/test2/test2
@@ -270,11 +316,33 @@
 uspace/lib/c/arch/amd64/_link.ld
 uspace/lib/c/arch/amd64/include/libarch/common.h
+uspace/lib/c/arch/amd64/include/libarch/fibril_context.h
+uspace/lib/c/arch/amd64/include/libarch/istate_struct.h
+uspace/lib/c/arch/arm32/include/libarch/fibril_context.h
+uspace/lib/c/arch/arm32/include/libarch/istate_struct.h
+uspace/lib/c/arch/ia32/_link-dlexe.ld
+uspace/lib/c/arch/ia32/_link-loader.ld
+uspace/lib/c/arch/ia32/_link-shlib.ld
+uspace/lib/c/arch/ia32/_link.ld
+uspace/lib/c/arch/ia32/include/libarch/fibril_context.h
+uspace/lib/c/arch/ia32/include/libarch/istate_struct.h
+uspace/lib/c/arch/ia64/include/libarch/fibril_context.h
+uspace/lib/c/arch/ia64/include/libarch/istate_struct.h
+uspace/lib/c/arch/mips32/include/libarch/fibril_context.h
+uspace/lib/c/arch/mips32/include/libarch/istate_struct.h
+uspace/lib/c/arch/mips32eb/include/libarch/fibril_context.h
+uspace/lib/c/arch/mips32eb/include/libarch/istate_struct.h
+uspace/lib/c/arch/ppc32/include/libarch/fibril_context.h
+uspace/lib/c/arch/ppc32/include/libarch/istate_struct.h
+uspace/lib/c/arch/sparc64/include/libarch/fibril_context.h
+uspace/lib/c/arch/sparc64/include/libarch/istate_struct.h
 uspace/lib/c/test-libc
 uspace/lib/label/test-liblabel
 uspace/lib/pcut/test-libpcut-*
 uspace/lib/posix/collisions.list
+uspace/lib/posix/include/libc
+uspace/lib/posix/redefs-hide-libc-symbols.list
 uspace/lib/posix/redefs-hide-libc.xargs
+uspace/lib/posix/redefs-show-posix-symbols.list
 uspace/lib/posix/redefs-show-posix.xargs
-uspace/lib/posix/include/libc
 uspace/lib/posix/test-libposix
 uspace/lib/uri/test-liburi
@@ -302,83 +370,28 @@
 uspace/srv/hid/output/output
 uspace/srv/hid/remcons/remcons
+uspace/srv/hid/rfb/rfb
 uspace/srv/hid/s3c24xx_ts/s3c24xx_ts
+uspace/srv/hw/bus/cuda_adb/cuda_adb
 uspace/srv/hw/char/s3c24xx_uart/s3c24ser
 uspace/srv/hw/irc/apic/apic
 uspace/srv/hw/irc/i8259/i8259
+uspace/srv/hw/irc/icp-ic/icp-ic
+uspace/srv/hw/irc/obio/obio
+uspace/srv/klog/klog
 uspace/srv/loader/loader
 uspace/srv/locsrv/locsrv
 uspace/srv/logger/logger
+uspace/srv/net/dhcp/dhcp
 uspace/srv/net/dnsrsrv/dnsrsrv
 uspace/srv/net/ethip/ethip
 uspace/srv/net/inetsrv/inetsrv
 uspace/srv/net/loopip/loopip
+uspace/srv/net/nconfsrv/nconfsrv
 uspace/srv/net/slip/slip
 uspace/srv/net/tcp/tcp
+uspace/srv/net/tcp/test-tcp
 uspace/srv/net/udp/udp
 uspace/srv/ns/ns
 uspace/srv/taskmon/taskmon
+uspace/srv/vfs/vfs
 uspace/srv/volsrv/volsrv
-uspace/srv/vfs/vfs
-kernel/arch/ia32/_link.ld
-uspace/lib/c/arch/ia32/_link-dlexe.ld
-uspace/lib/c/arch/ia32/_link-loader.ld
-uspace/lib/c/arch/ia32/_link-shlib.ld
-uspace/lib/c/arch/ia32/_link.ld
-kernel/arch/arm32/_link.ld
-uspace/app/corecfg/corecfg
-uspace/dist/app/corecfg
-uspace/dist/srv/rfb
-uspace/srv/hid/rfb/rfb
-uspace/dist/srv/dhcp
-uspace/srv/net/dhcp/dhcp
-uspace/dist/srv/nconfsrv
-uspace/srv/net/nconfsrv/nconfsrv
-uspace/app/netspeed/netspeed
-uspace/dist/app/netspeed
-uspace/app/df/df
-uspace/dist/app/df
-uspace/app/download/download
-uspace/dist/app/download
-uspace/app/viewer/viewer
-uspace/dist/app/viewer
-uspace/app/untar/untar
-uspace/dist/app/untar
-uspace/lib/posix/redefs-hide-libc-symbols.list
-uspace/lib/posix/redefs-show-posix-symbols.list
-uspace/srv/klog/klog
-uspace/dist/srv/klog
-kernel/arch/sparc64/include/arch/context_struct.h
-kernel/arch/sparc64/include/arch/istate_struct.h
-kernel/arch/amd64/include/arch/context_struct.h
-kernel/arch/amd64/include/arch/istate_struct.h
-kernel/arch/ia32/include/arch/context_struct.h
-kernel/arch/ia32/include/arch/istate_struct.h
-kernel/arch/ia32/include/arch/boot/memmap_struct.h
-kernel/arch/ppc32/include/arch/context_struct.h
-kernel/arch/ppc32/include/arch/fpu_context_struct.h
-kernel/arch/ppc32/include/arch/istate_struct.h
-kernel/arch/arm32/include/arch/context_struct.h
-kernel/arch/arm32/include/arch/istate_struct.h
-kernel/arch/mips32/include/arch/context_struct.h
-kernel/arch/mips32/include/arch/fpu_context_struct.h
-kernel/arch/mips32/include/arch/istate_struct.h
-kernel/arch/ia64/include/arch/context_struct.h
-kernel/arch/ia64/include/arch/istate_struct.h
-kernel/genarch/include/genarch/multiboot/multiboot_info_struct.h
-kernel/genarch/include/genarch/multiboot/multiboot_memmap_struct.h
-uspace/lib/c/arch/sparc64/include/libarch/fibril_context.h
-uspace/lib/c/arch/sparc64/include/libarch/istate_struct.h
-uspace/lib/c/arch/amd64/include/libarch/fibril_context.h
-uspace/lib/c/arch/amd64/include/libarch/istate_struct.h
-uspace/lib/c/arch/ia32/include/libarch/fibril_context.h
-uspace/lib/c/arch/ia32/include/libarch/istate_struct.h
-uspace/lib/c/arch/ppc32/include/libarch/fibril_context.h
-uspace/lib/c/arch/ppc32/include/libarch/istate_struct.h
-uspace/lib/c/arch/arm32/include/libarch/fibril_context.h
-uspace/lib/c/arch/arm32/include/libarch/istate_struct.h
-uspace/lib/c/arch/mips32eb/include/libarch/fibril_context.h
-uspace/lib/c/arch/mips32eb/include/libarch/istate_struct.h
-uspace/lib/c/arch/mips32/include/libarch/fibril_context.h
-uspace/lib/c/arch/mips32/include/libarch/istate_struct.h
-uspace/lib/c/arch/ia64/include/libarch/fibril_context.h
-uspace/lib/c/arch/ia64/include/libarch/istate_struct.h
Index: HelenOS.config
===================================================================
--- HelenOS.config	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ HelenOS.config	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -347,5 +347,5 @@
 
 % IOMAP dummy support
-! [PLATFORM=abs32le|PLATFORM=arm32|PLATFORM=mips32|PLATFORM=ppc32|PLATFORM=sparc64] CONFIG_IOMAP_DUMMY (y)
+! [PLATFORM=abs32le|PLATFORM=arm32|PLATFORM=mips32|PLATFORM=ppc32|PLATFORM=riscv64|PLATFORM=sparc64] CONFIG_IOMAP_DUMMY (y)
 
 % ACPI support
Index: boot/Makefile.common
===================================================================
--- boot/Makefile.common	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ boot/Makefile.common	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -182,4 +182,5 @@
 	$(USPACE_PATH)/app/edit/edit \
 	$(USPACE_PATH)/app/fdisk/fdisk \
+	$(USPACE_PATH)/app/gunzip/gunzip \
 	$(USPACE_PATH)/app/inet/inet \
 	$(USPACE_PATH)/app/kill/kill \
@@ -207,4 +208,5 @@
 	$(USPACE_PATH)/app/nterm/nterm \
 	$(USPACE_PATH)/app/ping/ping \
+	$(USPACE_PATH)/app/pkg/pkg \
 	$(USPACE_PATH)/app/stats/stats \
 	$(USPACE_PATH)/app/sysinfo/sysinfo \
@@ -235,5 +237,6 @@
 	$(USPACE_PATH)/lib/posix/test-libposix \
 	$(USPACE_PATH)/lib/uri/test-liburi \
-	$(USPACE_PATH)/app/bdsh/test-bdsh
+	$(USPACE_PATH)/app/bdsh/test-bdsh \
+	$(USPACE_PATH)/srv/net/tcp/test-tcp
 
 RD_DATA_ESSENTIAL = \
Index: boot/arch/riscv64/Makefile.inc
===================================================================
--- boot/arch/riscv64/Makefile.inc	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ boot/arch/riscv64/Makefile.inc	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -34,4 +34,5 @@
 BITS = 64
 ENDIANESS = LE
+EXTRA_CFLAGS = -mcmodel=medany
 
 SOURCES = \
Index: boot/arch/riscv64/_link.ld.in
===================================================================
--- boot/arch/riscv64/_link.ld.in	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ boot/arch/riscv64/_link.ld.in	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -1,9 +1,27 @@
+#include <arch/arch.h>
+
 ENTRY(start)
 
 SECTIONS {
+	. = PHYSMEM_START;
+	
 	.text : {
 		*(BOOTSTRAP);
 		*(.text);
 	}
+	
+	. = ALIGN(0x1000);
+	.htif : {
+		htif_page = .;
+		*(.htif)
+	}
+	. = ALIGN(0x1000);
+	
+	. = ALIGN(0x1000);
+	.pt : {
+		pt_page = .;
+		*(.pt)
+	}
+	. = ALIGN(0x1000);
 	
 	.data : {
Index: boot/arch/riscv64/include/arch.h
===================================================================
--- boot/arch/riscv64/include/arch.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ boot/arch/riscv64/include/arch.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -35,4 +35,8 @@
 #define BOOT_STACK_SIZE  PAGE_SIZE
 
+#define PHYSMEM_START  0x40000000
+#define PHYSMEM_SIZE   1073741824
+#define BOOT_OFFSET    0x48000000
+
 #define DEFAULT_MTVEC      0x00000100
 #define TRAP_VECTOR_RESET  0x0100
Index: boot/arch/riscv64/include/asm.h
===================================================================
--- boot/arch/riscv64/include/asm.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ boot/arch/riscv64/include/asm.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -32,5 +32,8 @@
 #include <stddef.h>
 
-extern void jump_to_kernel(void *, uintptr_t)
+extern char htif_page[];
+extern char pt_page[];
+
+extern void jump_to_kernel(uintptr_t)
     __attribute__((noreturn));
 
Index: boot/arch/riscv64/include/mm.h
===================================================================
--- boot/arch/riscv64/include/mm.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ boot/arch/riscv64/include/mm.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+/** @file
+ */
+
+#ifndef BOOT_riscv64_MM_H_
+#define BOOT_riscv64_MM_H_
+
+#ifndef __ASM__
+	#define KA2PA(x)  (((uintptr_t) (x)) - UINT64_C(0xffff800000000000))
+	#define PA2KA(x)  (((uintptr_t) (x)) + UINT64_C(0xffff800000000000))
+#else
+	#define KA2PA(x)  ((x) - 0xffff800000000000)
+	#define PA2KA(x)  ((x) + 0xffff800000000000)
+#endif
+
+#define PTL_DIRTY       (1 << 7)
+#define PTL_ACCESSED    (1 << 6)
+#define PTL_GLOBAL      (1 << 5)
+#define PTL_USER        (1 << 4)
+#define PTL_EXECUTABLE  (1 << 3)
+#define PTL_WRITABLE    (1 << 2)
+#define PTL_READABLE    (1 << 1)
+#define PTL_VALID       1
+
+#endif
+
+/** @}
+ */
Index: boot/arch/riscv64/include/types.h
===================================================================
--- boot/arch/riscv64/include/types.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ boot/arch/riscv64/include/types.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -42,4 +42,9 @@
 
 typedef struct {
+	volatile uint64_t *tohost;
+	volatile uint64_t *fromhost;
+} ucbinfo_t;
+
+typedef struct {
 	void *start;
 	size_t size;
@@ -67,4 +72,8 @@
 
 typedef struct {
+	ucbinfo_t ucbinfo;
+	uintptr_t physmem_start;
+	uintptr_t htif_frame;
+	uintptr_t pt_frame;
 	memmap_t memmap;
 	taskmap_t taskmap;
Index: boot/arch/riscv64/include/ucb.h
===================================================================
--- boot/arch/riscv64/include/ucb.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ boot/arch/riscv64/include/ucb.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -38,6 +38,6 @@
 #include <stddef.h>
 
-#define CSR_MTOHOST    0x780
-#define CSR_MFROMHOST  0x781
+extern volatile uint64_t tohost;
+extern volatile uint64_t fromhost;
 
 #define HTIF_DEVICE_CONSOLE  1
Index: boot/arch/riscv64/src/asm.S
===================================================================
--- boot/arch/riscv64/src/asm.S	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ boot/arch/riscv64/src/asm.S	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -29,4 +29,23 @@
 #include <abi/asmtool.h>
 #include <arch/arch.h>
+#include <arch/mm.h>
+
+#define MCOUNTEREN_CY_MASK  0x00000001
+#define MCOUNTEREN_TM_MASK  0x00000002
+#define MCOUNTEREN_IR_MASK  0x00000004
+
+#define MSTATUS_MPP_MASK        0x00001800
+#define MSTATUS_MPP_USER        0x00000000
+#define MSTATUS_MPP_SUPERVISOR  0x00000800
+#define MSTATUS_MPP_MACHINE     0x00001800
+
+#define MSTATUS_SUM_MASK        0x00040000
+
+#define SATP_PFN_MASK  0x00000fffffffffff
+
+#define SATP_MODE_MASK  0xf000000000000000
+#define SATP_MODE_BARE  0x0000000000000000
+#define SATP_MODE_SV39  0x8000000000000000
+#define SATP_MODE_SV48  0x9000000000000000
 
 .section BOOTSTRAP
@@ -76,5 +95,38 @@
 
 FUNCTION_BEGIN(jump_to_kernel)
-	j halt
+	/* Enable performance counters access in supervisor mode */
+	csrsi mcounteren, MCOUNTEREN_CY_MASK | MCOUNTEREN_TM_MASK | MCOUNTEREN_IR_MASK
+	
+	/* Setup SV48 paging for supervisor mode */
+	la t0, ptl_0
+	srli t0, t0, 12
+	
+	li t1, SATP_PFN_MASK
+	and t0, t0, t1
+	
+	li t1, SATP_MODE_SV48
+	or t0, t0, t1
+	
+	csrw sptbr, t0
+	
+	/* Jump to supervisor mode */
+	csrr t0, mstatus
+	
+	li t1, ~MSTATUS_MPP_MASK
+	and t0, t0, t1
+	
+	/*
+	 * TODO: Enable running with Supervisor User Mode
+	 * access disabled.
+	 */
+	li t1, MSTATUS_MPP_SUPERVISOR | MSTATUS_SUM_MASK
+	or t0, t0, t1
+	
+	csrw mstatus, t0
+	
+	li ra, PA2KA(BOOT_OFFSET)
+	csrw mepc, ra
+	
+	mret
 FUNCTION_END(jump_to_kernel)
 
@@ -88,2 +140,11 @@
 SYMBOL(boot_stack)
 	.space BOOT_STACK_SIZE
+
+.section .pt, "aw", @progbits
+
+.align PAGE_WIDTH
+SYMBOL(ptl_0)
+	.fill 256, 8, 0
+	/* Identity mapping for [0; 512G) */
+	.quad 0 + (PTL_DIRTY | PTL_ACCESSED | PTL_EXECUTABLE | PTL_WRITABLE | PTL_READABLE | PTL_VALID)
+	.fill 255, 8, 0
Index: boot/arch/riscv64/src/main.c
===================================================================
--- boot/arch/riscv64/src/main.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ boot/arch/riscv64/src/main.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -30,4 +30,6 @@
 #include <arch/arch.h>
 #include <arch/asm.h>
+#include <arch/ucb.h>
+#include <arch/mm.h>
 #include <version.h>
 #include <stddef.h>
@@ -41,7 +43,4 @@
 #include "../../components.h"
 
-#define KA2PA(x)  (((uintptr_t) (x)) - UINT64_C(0xffff800000000000))
-#define PA2KA(x)  (((uintptr_t) (x)) + UINT64_C(0xffff800000000000))
-
 static bootinfo_t bootinfo;
 
@@ -50,12 +49,24 @@
 	version_print();
 	
+	bootinfo.htif_frame = ((uintptr_t) &htif_page) >> PAGE_WIDTH;
+	bootinfo.pt_frame = ((uintptr_t) &pt_page) >> PAGE_WIDTH;
+	
+	bootinfo.ucbinfo.tohost =
+	    (volatile uint64_t *) PA2KA((uintptr_t) &tohost);
+	bootinfo.ucbinfo.fromhost =
+	    (volatile uint64_t *) PA2KA((uintptr_t) &fromhost);
+	
 	// FIXME TODO: read from device tree
-	bootinfo.memmap.total = 1024 * 1024 * 1024;
-	bootinfo.memmap.cnt = 0;
+	bootinfo.physmem_start = PHYSMEM_START;
+	bootinfo.memmap.total = PHYSMEM_SIZE;
+	bootinfo.memmap.cnt = 1;
+	bootinfo.memmap.zones[0].start = (void *) PHYSMEM_START;
+	bootinfo.memmap.zones[0].size = PHYSMEM_SIZE;
 	
-	printf("\nMemory statistics (total %lu MB)\n\n", bootinfo.memmap.total >> 20);
+	printf("\nMemory statistics (total %lu MB, starting at %p)\n\n",
+	    bootinfo.memmap.total >> 20, (void *) bootinfo.physmem_start);
 	printf(" %p: boot info structure\n", &bootinfo);
 	
-	uintptr_t top = 0;
+	uintptr_t top = BOOT_OFFSET;
 	
 	for (size_t i = 0; i < COMPONENTS; i++) {
@@ -66,9 +77,11 @@
 		uintptr_t tail = (uintptr_t) components[i].addr +
 		    components[i].size;
-		if (tail > top)
-			top = tail;
+		if (tail > top) {
+			printf("\n%s: Image too large to fit (%p >= %p), halting.\n",
+			    components[i].name, (void *) tail, (void *) top);
+			halt();
+		}
 	}
 	
-	top = ALIGN_UP(top, PAGE_SIZE);
 	printf(" %p: inflate area\n", (void *) top);
 	
@@ -92,5 +105,5 @@
 			bootinfo.taskmap.cnt++;
 		} else
-			kernel_entry = (void *) top;
+			kernel_entry = (void *) PA2KA(top);
 		
 		dest[i] = (void *) top;
@@ -101,5 +114,5 @@
 	printf(" %p: kernel entry point\n", kernel_entry);
 	
-	if (top >= bootinfo.memmap.total) {
+	if (top >= bootinfo.physmem_start + bootinfo.memmap.total) {
 		printf("Not enough physical memory available.\n");
 		printf("The boot image is too large. Halting.\n");
@@ -125,4 +138,4 @@
 	
 	printf("Booting the kernel...\n");
-	jump_to_kernel(kernel_entry, PA2KA(&bootinfo));
+	jump_to_kernel(PA2KA(&bootinfo));
 }
Index: boot/arch/riscv64/src/ucb.c
===================================================================
--- boot/arch/riscv64/src/ucb.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ boot/arch/riscv64/src/ucb.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -31,4 +31,16 @@
 #include <macros.h>
 
+volatile uint64_t tohost __attribute__((section(".htif")));
+volatile uint64_t fromhost __attribute__((section(".htif")));
+
+static void poll_fromhost()
+{
+	uint64_t val = fromhost;
+	if (!val)
+		return;
+	
+	fromhost = 0;
+}
+
 void htif_cmd(uint8_t device, uint8_t cmd, uint64_t payload)
 {
@@ -36,12 +48,8 @@
 	    (((uint64_t) cmd) << 48) |
 	    (payload & UINT64_C(0xffffffffffff));
-	uint64_t retval;
 	
-	do {
-		asm volatile (
-			"csrrw %[retval], " STRING(CSR_MTOHOST) ", %[val]\n"
-			: [retval] "=r" (retval)
-			: [val] "r" (val)
-		);
-	} while (retval != 0);
+	while (tohost)
+		poll_fromhost();
+	
+	tohost = val;
 }
Index: contrib/qemu/build-from-scratch.sh
===================================================================
--- contrib/qemu/build-from-scratch.sh	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ contrib/qemu/build-from-scratch.sh	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -29,5 +29,5 @@
 #
 
-VERSION=2.9.0
+VERSION=2.10.0
 BASENAME=qemu-${VERSION}
 BASENAME_MASTER=qemu-master
@@ -36,5 +36,5 @@
 URL=http://wiki.qemu-project.org/download/${TARBALL}
 REPO=git://git.qemu.org/qemu.git
-MD5="02781eb15b364aedef79da7a5113f5b7"
+MD5="ca73441de73a9b52c6c49c97190d2185"
 
 OPENSPARC_TARBALL="OpenSPARCT1_Arch.1.5.tar.bz2"
Index: defaults/amd64/Makefile.config
===================================================================
--- defaults/amd64/Makefile.config	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ defaults/amd64/Makefile.config	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -65,4 +65,7 @@
 CONFIG_BFB_BPP = 16
 
+# Include development files (headers, libraries)
+CONFIG_DEVEL_FILES = y
+
 # OHCI root hub power switch, ganged is enough
 OHCI_POWER_SWITCH = ganged
Index: defaults/ia32/Makefile.config
===================================================================
--- defaults/ia32/Makefile.config	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ defaults/ia32/Makefile.config	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -77,4 +77,7 @@
 CONFIG_USE_SHARED_LIBS = y
 
+# Include development files (headers, libraries)
+CONFIG_DEVEL_FILES = y
+
 # OHCI root hub power switch, ganged is enough
 OHCI_POWER_SWITCH = ganged
Index: kernel/arch/abs32le/src/mm/frame.c
===================================================================
--- kernel/arch/abs32le/src/mm/frame.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ kernel/arch/abs32le/src/mm/frame.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -49,5 +49,4 @@
 }
 
-
 void frame_low_arch_init(void)
 {
Index: kernel/arch/ia32/src/mm/frame.c
===================================================================
--- kernel/arch/ia32/src/mm/frame.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ kernel/arch/ia32/src/mm/frame.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -147,5 +147,4 @@
 }
 
-
 void frame_low_arch_init(void)
 {
Index: kernel/arch/riscv64/Makefile.inc
===================================================================
--- kernel/arch/riscv64/Makefile.inc	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ kernel/arch/riscv64/Makefile.inc	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -31,10 +31,15 @@
 BFD = binary
 
+GCC_CFLAGS += -mcmodel=medany
+
 BITS = 64
 ENDIANESS = LE
 
 ARCH_SOURCES = \
+	arch/$(KARCH)/src/boot/boot.S \
 	arch/$(KARCH)/src/asm.S \
+	arch/$(KARCH)/src/context.S \
 	arch/$(KARCH)/src/debug/stacktrace.c \
+	arch/$(KARCH)/src/drivers/ucb.c \
 	arch/$(KARCH)/src/proc/scheduler.c \
 	arch/$(KARCH)/src/proc/task.c \
Index: kernel/arch/riscv64/_link.ld.in
===================================================================
--- kernel/arch/riscv64/_link.ld.in	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ kernel/arch/riscv64/_link.ld.in	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -14,17 +14,7 @@
 
 SECTIONS {
-	.unmapped BOOT_OFFSET: AT (0) {
-		unmapped_ktext_start = .;
+	.image (PA2KA(BOOT_OFFSET)) : AT (0) {
+		ktext_start = .;
 		*(K_TEXT_START);
-		unmapped_ktext_end = .;
-		
-		unmapped_kdata_start = .;
-		*(K_DATA_START);
-		*(K_INI_PTLS);
-		unmapped_kdata_end = .;
-	}
-	
-	.mapped (BOOT_OFFSET + SIZEOF(.unmapped)) : AT (SIZEOF(.unmapped)) {
-		ktext_start = .;
 		*(.text);
 		ktext_end = .;
@@ -36,11 +26,7 @@
 		QUAD(PA2KA(BOOT_OFFSET));
 		hardcoded_ktext_size = .;
-		QUAD(ktext_end - ktext_start + (unmapped_ktext_end - unmapped_ktext_start));
+		QUAD(ktext_end - ktext_start);
 		hardcoded_kdata_size = .;
-		QUAD(kdata_end - kdata_start + (unmapped_kdata_end - unmapped_kdata_start));
-		hardcoded_unmapped_ktext_size = .;
-		QUAD(unmapped_ktext_end - unmapped_ktext_start);
-		hardcoded_unmapped_kdata_size = .;
-		QUAD(unmapped_kdata_end - unmapped_kdata_start);
+		QUAD(kdata_end - kdata_start);
 		*(.sdata);
 		*(.reginfo);
Index: kernel/arch/riscv64/include/arch/arch.h
===================================================================
--- kernel/arch/riscv64/include/arch/arch.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ kernel/arch/riscv64/include/arch/arch.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -38,4 +38,6 @@
 #include <arch/boot/boot.h>
 
+extern void riscv64_pre_main(bootinfo_t *);
+
 #endif
 
Index: kernel/arch/riscv64/include/arch/asm.h
===================================================================
--- kernel/arch/riscv64/include/arch/asm.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ kernel/arch/riscv64/include/arch/asm.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -36,4 +36,5 @@
 #define KERN_riscv64_ASM_H_
 
+#include <arch/cpu.h>
 #include <typedefs.h>
 #include <config.h>
@@ -43,35 +44,62 @@
 NO_TRACE static inline ipl_t interrupts_enable(void)
 {
-	// FIXME
-	return 0;
+	ipl_t ipl;
+	
+	asm volatile (
+		"csrrsi %[ipl], sstatus, " STRING(SSTATUS_SIE_MASK) "\n"
+		: [ipl] "=r" (ipl)
+	);
+	
+	return ipl;
 }
 
 NO_TRACE static inline ipl_t interrupts_disable(void)
 {
-	// FIXME
-	return 0;
+	ipl_t ipl;
+	
+	asm volatile (
+		"csrrci %[ipl], sstatus, " STRING(SSTATUS_SIE_MASK) "\n"
+		: [ipl] "=r" (ipl)
+	);
+	
+	return ipl;
 }
 
 NO_TRACE static inline void interrupts_restore(ipl_t ipl)
 {
-	// FIXME
+	if ((ipl & SSTATUS_SIE_MASK) == SSTATUS_SIE_MASK)
+		interrupts_enable();
+	else
+		interrupts_disable();
 }
 
 NO_TRACE static inline ipl_t interrupts_read(void)
 {
-	// FIXME
-	return 0;
+	ipl_t ipl;
+	
+	asm volatile (
+		"csrr %[ipl], sstatus\n"
+		: [ipl] "=r" (ipl)
+	);
+	
+	return ipl;
 }
 
 NO_TRACE static inline bool interrupts_disabled(void)
 {
-	// FIXME
-	return 0;
+	return ((interrupts_read() & SSTATUS_SIE_MASK) == 0);
 }
 
 NO_TRACE static inline uintptr_t get_stack_base(void)
 {
-	// FIXME
-	return 0;
+	uintptr_t base;
+	
+	asm volatile (
+		"and %[base], sp, %[mask]\n"
+		: [base] "=r" (base)
+		: [mask] "r" (~(STACK_SIZE - 1))
+	);
+	
+	return base;
 }
 
Index: kernel/arch/riscv64/include/arch/atomic.h
===================================================================
--- kernel/arch/riscv64/include/arch/atomic.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ kernel/arch/riscv64/include/arch/atomic.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -40,34 +40,70 @@
 NO_TRACE static inline void atomic_inc(atomic_t *val)
 {
-	// FIXME
+	asm volatile (
+		"amoadd.d zero, %[inc], %[addr]\n"
+		: [addr] "+A" (val->count)
+		: [inc] "r" (1)
+	);
 }
 
 NO_TRACE static inline void atomic_dec(atomic_t *val)
 {
-	// FIXME
+	asm volatile (
+		"amoadd.d zero, %[inc], %[addr]\n"
+		: [addr] "+A" (val->count)
+		: [inc] "r" (-1)
+	);
 }
 
 NO_TRACE static inline atomic_count_t atomic_postinc(atomic_t *val)
 {
-	atomic_inc(val);
-	return val->count - 1;
+	atomic_count_t orig;
+	
+	asm volatile (
+		"amoadd.d %[orig], %[inc], %[addr]\n"
+		: [orig] "=r" (orig), [addr] "+A" (val->count)
+		: [inc] "r" (1)
+	);
+	
+	return orig;
 }
 
 NO_TRACE static inline atomic_count_t atomic_postdec(atomic_t *val)
 {
-	atomic_dec(val);
-	return val->count + 1;
+	atomic_count_t orig;
+	
+	asm volatile (
+		"amoadd.d %[orig], %[inc], %[addr]\n"
+		: [orig] "=r" (orig), [addr] "+A" (val->count)
+		: [inc] "r" (-1)
+	);
+	
+	return orig;
 }
 
 NO_TRACE static inline atomic_count_t atomic_preinc(atomic_t *val)
 {
-	atomic_inc(val);
-	return val->count;
+	atomic_count_t orig;
+	
+	asm volatile (
+		"amoadd.d %[orig], %[inc], %[addr]\n"
+		: [orig] "=r" (orig), [addr] "+A" (val->count)
+		: [inc] "r" (1)
+	);
+	
+	return orig - 1;
 }
 
 NO_TRACE static inline atomic_count_t atomic_predec(atomic_t *val)
 {
-	atomic_dec(val);
-	return val->count;
+	atomic_count_t orig;
+	
+	asm volatile (
+		"amoadd.d %[orig], %[inc], %[addr]\n"
+		: [orig] "=r" (orig), [addr] "+A" (val->count)
+		: [inc] "r" (-1)
+	);
+	
+	return orig + 1;
 }
 
Index: kernel/arch/riscv64/include/arch/boot/boot.h
===================================================================
--- kernel/arch/riscv64/include/arch/boot/boot.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ kernel/arch/riscv64/include/arch/boot/boot.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -36,9 +36,12 @@
 #define KERN_riscv64_BOOT_H_
 
-#define BOOT_OFFSET  0x200000
+#define BOOT_OFFSET  0x48000000
 
 #define TASKMAP_MAX_RECORDS        32
 #define MEMMAP_MAX_RECORDS         32
 #define BOOTINFO_TASK_NAME_BUFLEN  32
+
+/* Temporary stack size for boot process */
+#define TEMP_STACK_SIZE  0x1000
 
 #ifndef __ASM__
@@ -47,4 +50,20 @@
 #include <stdint.h>
 #include <config.h>
+
+typedef struct {
+	volatile uint64_t *tohost;
+	volatile uint64_t *fromhost;
+} ucbinfo_t;
+
+typedef struct {
+	void *start;
+	size_t size;
+} memzone_t;
+
+typedef struct {
+	uint64_t total;
+	size_t cnt;
+	memzone_t zones[MEMMAP_MAX_RECORDS];
+} memmap_t;
 
 typedef struct {
@@ -60,21 +79,11 @@
 
 typedef struct {
-	void *start;
-	size_t size;
-} memzone_t;
-
-typedef struct {
-	uint64_t total;
-	size_t cnt;
-	memzone_t zones[MEMMAP_MAX_RECORDS];
-} memmap_t;
-
-typedef struct {
+	ucbinfo_t ucbinfo;
+	uintptr_t physmem_start;
+	uintptr_t htif_frame;
+	uintptr_t pt_frame;
 	memmap_t memmap;
 	taskmap_t taskmap;
-	ballocs_t ballocs;
 } bootinfo_t;
-
-extern memmap_t memmap;
 
 #endif
Index: kernel/arch/riscv64/include/arch/context.h
===================================================================
--- kernel/arch/riscv64/include/arch/context.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ kernel/arch/riscv64/include/arch/context.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -40,6 +40,4 @@
 #define SP_DELTA  16
 
-// FIXME
-
 #define context_set(context, pc, stack, size) \
 	context_set_generic(context, pc, stack, size)
Index: kernel/arch/riscv64/include/arch/context_struct.ag
===================================================================
--- kernel/arch/riscv64/include/arch/context_struct.ag	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ kernel/arch/riscv64/include/arch/context_struct.ag	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -38,6 +38,6 @@
         members : [
                 #
-                # There is a room for optimization (we can store just those
-                # registers that must be preserved during ABI function call).
+                # Only save registers that must be preserved across function
+                # calls.
                 #
 
@@ -52,126 +52,57 @@
 
                 {
-                        name : zero,
+                        name : gp,
                         type : uint64_t
                 },
                 {
-                        name : ra,
-                        type : uint64_t
-                },
-
-                {
-                        name : x3,
+                        name : tp,
                         type : uint64_t
                 },
                 {
-                        name : x4,
+                        name : s0,
                         type : uint64_t
                 },
                 {
-                        name : x5,
+                        name : s1,
                         type : uint64_t
                 },
                 {
-                        name : x6,
+                        name : s2,
                         type : uint64_t
                 },
                 {
-                        name : x7,
+                        name : s3,
                         type : uint64_t
                 },
                 {
-                        name : x8,
+                        name : s4,
                         type : uint64_t
                 },
                 {
-                        name : x9,
+                        name : s5,
                         type : uint64_t
                 },
                 {
-                        name : x10,
+                        name : s6,
                         type : uint64_t
                 },
                 {
-                        name : x11,
+                        name : s7,
                         type : uint64_t
                 },
                 {
-                        name : x12,
+                        name : s8,
                         type : uint64_t
                 },
                 {
-                        name : x13,
+                        name : s9,
                         type : uint64_t
                 },
                 {
-                        name : x14,
+                        name : s10,
                         type : uint64_t
                 },
                 {
-                        name : x15,
-                        type : uint64_t
-                },
-                {
-                        name : x16,
-                        type : uint64_t
-                },
-                {
-                        name : x17,
-                        type : uint64_t
-                },
-                {
-                        name : x18,
-                        type : uint64_t
-                },
-                {
-                        name : x19,
-                        type : uint64_t
-                },
-                {
-                        name : x20,
-                        type : uint64_t
-                },
-                {
-                        name : x21,
-                        type : uint64_t
-                },
-                {
-                        name : x22,
-                        type : uint64_t
-                },
-                {
-                        name : x23,
-                        type : uint64_t
-                },
-                {
-                        name : x24,
-                        type : uint64_t
-                },
-                {
-                        name : x25,
-                        type : uint64_t
-                },
-                {
-                        name : x26,
-                        type : uint64_t
-                },
-                {
-                        name : x27,
-                        type : uint64_t
-                },
-                {
-                        name : x28,
-                        type : uint64_t
-                },
-                {
-                        name : x29,
-                        type : uint64_t
-                },
-                {
-                        name : x30,
-                        type : uint64_t
-                },
-                {
-                        name : x31,
+                        name : s11,
                         type : uint64_t
                 },
Index: kernel/arch/riscv64/include/arch/cpu.h
===================================================================
--- kernel/arch/riscv64/include/arch/cpu.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ kernel/arch/riscv64/include/arch/cpu.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -36,4 +36,13 @@
 #define KERN_riscv64_CPU_H_
 
+#define SSTATUS_SIE_MASK  0x00000002
+
+#define SATP_PFN_MASK  UINT64_C(0x00000fffffffffff)
+
+#define SATP_MODE_MASK  UINT64_C(0xf000000000000000)
+#define SATP_MODE_BARE  UINT64_C(0x0000000000000000)
+#define SATP_MODE_SV39  UINT64_C(0x8000000000000000)
+#define SATP_MODE_SV48  UINT64_C(0x9000000000000000)
+
 #ifndef __ASM__
 
Index: kernel/arch/riscv64/include/arch/cycle.h
===================================================================
--- kernel/arch/riscv64/include/arch/cycle.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ kernel/arch/riscv64/include/arch/cycle.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -40,5 +40,12 @@
 NO_TRACE static inline uint64_t get_cycle(void)
 {
-	return 0;
+	uint64_t cycle;
+	
+	asm volatile (
+		"rdcycle %[cycle]\n"
+		: [cycle] "=r" (cycle)
+	);
+	
+	return cycle;
 }
 
Index: kernel/arch/riscv64/include/arch/drivers/ucb.h
===================================================================
--- kernel/arch/riscv64/include/arch/drivers/ucb.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/arch/riscv64/include/arch/drivers/ucb.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+/** @addtogroup riscv64
+ * @{
+ */
+/** @file
+ */
+
+#ifndef KERN_riscv64_DRIVERS_UCB_H_
+#define KERN_riscv64_DRIVERS_UCB_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <console/chardev.h>
+
+extern void htif_init(volatile uint64_t *, volatile uint64_t *);
+extern outdev_t *htifout_init(void);
+extern void htif_putchar(outdev_t *, const wchar_t);
+
+#endif
+
+/** @}
+ */
Index: kernel/arch/riscv64/include/arch/mm/asid.h
===================================================================
--- kernel/arch/riscv64/include/arch/mm/asid.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ kernel/arch/riscv64/include/arch/mm/asid.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -42,4 +42,7 @@
 typedef uint32_t asid_t;
 
+#define asid_get()  (ASID_START + 1)
+#define asid_put(asid)
+
 #endif
 
Index: kernel/arch/riscv64/include/arch/mm/frame.h
===================================================================
--- kernel/arch/riscv64/include/arch/mm/frame.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ kernel/arch/riscv64/include/arch/mm/frame.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -43,4 +43,11 @@
 #ifndef __ASM__
 
+#include <arch/boot/boot.h>
+
+extern uintptr_t physmem_start;
+extern uintptr_t htif_frame;
+extern uintptr_t pt_frame;
+extern memmap_t memmap;
+
 extern void frame_low_arch_init(void);
 extern void frame_high_arch_init(void);
Index: kernel/arch/riscv64/include/arch/mm/km.h
===================================================================
--- kernel/arch/riscv64/include/arch/mm/km.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ kernel/arch/riscv64/include/arch/mm/km.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -39,4 +39,10 @@
 #include <stddef.h>
 
+#define KM_RISCV64_IDENTITY_START      UINT64_C(0xffff800000000000)
+#define KM_RISCV64_IDENTITY_SIZE       UINT64_C(0x0000400000000000)
+
+#define KM_RISCV64_NON_IDENTITY_START  UINT64_C(0xffffc00000000000)
+#define KM_RISCV64_NON_IDENTITY_SIZE   UINT64_C(0x0000400000000000)
+
 extern void km_identity_arch_init(void);
 extern void km_non_identity_arch_init(void);
Index: kernel/arch/riscv64/include/arch/mm/page.h
===================================================================
--- kernel/arch/riscv64/include/arch/mm/page.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ kernel/arch/riscv64/include/arch/mm/page.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -50,48 +50,4 @@
 
 /*
- * Page table entry types.
- *
- * - PTE_TYPE_PTR:         pointer to next level PTE
- * - PTE_TYPE_PTR_GLOBAL:  pointer to next level PTE (global mapping)
- *
- * - PTE_TYPE_SRURX:       kernel read, user read execute
- * - PTE_TYPE_SRWURWX:     kernel read write, user read write execute
- * - PTE_TYPE_SRUR:        kernel read, user read
- * - PTE_TYPE_SRWURW:      kernel read write, user read write
- * - PTE_TYPE_SRXURX:      kernel read execute, user read execute
- * - PTE_TYPE_SRWXURWX:    kernel read write execute, user read write execute
- *
- * - PTE_TYPE_SR:          kernel read
- * - PTE_TYPE_SRW:         kernel read write
- * - PTE_TYPE_SRX:         kernel read execute
- * - PTE_TYPE_SRWX:        kernel read write execute
- *
- * - PTE_TYPE_SR_GLOBAL:   kernel read (global mapping)
- * - PTE_TYPE_SRW_GLOBAL:  kernel read write (global mapping)
- * - PTE_TYPE_SRX_GLOBAL:  kernel read execute (global mapping)
- * - PTE_TYPE_SRWX_GLOBAL: kernel read write execute (global mapping)
- */
-
-#define PTE_TYPE_PTR          0
-#define PTE_TYPE_PTR_GLOBAL   1
-
-#define PTE_TYPE_SRURX        2
-#define PTE_TYPE_SRWURWX      3
-#define PTE_TYPE_SRUR         4
-#define PTE_TYPE_SRWURW       5
-#define PTE_TYPE_SRXURX       6
-#define PTE_TYPE_SRWXURWX     7
-
-#define PTE_TYPE_SR           8
-#define PTE_TYPE_SRW          9
-#define PTE_TYPE_SRX          10
-#define PTE_TYPE_SRWX         11
-
-#define PTE_TYPE_SR_GLOBAL    12
-#define PTE_TYPE_SRW_GLOBAL   13
-#define PTE_TYPE_SRX_GLOBAL   14
-#define PTE_TYPE_SRWX_GLOBAL  15
-
-/*
  * Implementation of 4-level page table interface.
  *
@@ -125,4 +81,7 @@
 #define PTL3_INDEX_ARCH(vaddr)  (((vaddr) >> 12) & 0x1ff)
 
+/* Flags mask for non-leaf page table entries */
+#define NON_LEAF_MASK  (~(PAGE_READ | PAGE_WRITE | PAGE_EXEC))
+
 /* Get PTE address accessors for each level. */
 #define GET_PTL1_ADDRESS_ARCH(ptl0, i) \
@@ -139,5 +98,6 @@
 
 /* Set PTE address accessors for each level. */
-#define SET_PTL0_ADDRESS_ARCH(ptl0)
+#define SET_PTL0_ADDRESS_ARCH(ptl0) \
+	(write_satp((uintptr_t) (ptl0)))
 
 #define SET_PTL1_ADDRESS_ARCH(ptl0, i, a) \
@@ -167,15 +127,15 @@
 
 /* Set PTE flags accessors for each level. */
-#define SET_PTL1_FLAGS_ARCH(ptl0, i, x) \
-	set_pt_flags((pte_t *) (ptl0), (size_t) (i), (x))
-
-#define SET_PTL2_FLAGS_ARCH(ptl1, i, x) \
-	set_pt_flags((pte_t *) (ptl1), (size_t) (i), (x))
-
-#define SET_PTL3_FLAGS_ARCH(ptl2, i, x) \
-	set_pt_flags((pte_t *) (ptl2), (size_t) (i), (x))
-
-#define SET_FRAME_FLAGS_ARCH(ptl3, i, x) \
-	set_pt_flags((pte_t *) (ptl3), (size_t) (i), (x))
+#define SET_PTL1_FLAGS_ARCH(ptl0, i, flags) \
+	set_pt_flags((pte_t *) (ptl0), (size_t) (i), ((flags) & NON_LEAF_MASK))
+
+#define SET_PTL2_FLAGS_ARCH(ptl1, i, flags) \
+	set_pt_flags((pte_t *) (ptl1), (size_t) (i), ((flags) & NON_LEAF_MASK))
+
+#define SET_PTL3_FLAGS_ARCH(ptl2, i, flags) \
+	set_pt_flags((pte_t *) (ptl2), (size_t) (i), ((flags) & NON_LEAF_MASK))
+
+#define SET_FRAME_FLAGS_ARCH(ptl3, i, flags) \
+	set_pt_flags((pte_t *) (ptl3), (size_t) (i), (flags))
 
 /* Set PTE present accessors for each level. */
@@ -196,15 +156,6 @@
 #define PTE_PRESENT_ARCH(pte)     ((pte)->valid != 0)
 #define PTE_GET_FRAME_ARCH(pte)   ((uintptr_t) (pte)->pfn << 12)
-
-#define PTE_WRITABLE_ARCH(pte) \
-	(((pte)->type == PTE_TYPE_SRWURWX) || \
-	((pte)->type == PTE_TYPE_SRWURW) || \
-	((pte)->type == PTE_TYPE_SRWXURWX))
-
-#define PTE_EXECUTABLE_ARCH(pte) \
-	(((pte)->type == PTE_TYPE_SRURX) || \
-	((pte)->type == PTE_TYPE_SRWURWX) || \
-	((pte)->type == PTE_TYPE_SRXURX) || \
-	((pte)->type == PTE_TYPE_SRWXURWX))
+#define PTE_WRITABLE_ARCH(pte)    ((pte)->writable != 0)
+#define PTE_EXECUTABLE_ARCH(pte)  ((pte)->executable != 0)
 
 #ifndef __ASM__
@@ -217,8 +168,12 @@
 typedef struct {
 	unsigned long valid : 1;       /**< Valid bit. */
-	unsigned long type : 4;        /**< Entry type. */
-	unsigned long referenced : 1;  /**< Refenced bit. */
+	unsigned long readable : 1;    /**< Readable bit. */
+	unsigned long writable : 1;    /**< Writable bit. */
+	unsigned long executable : 1;  /**< Executable bit. */
+	unsigned long user : 1;        /**< User mode accessible bit. */
+	unsigned long global : 1;      /**< Global mapping bit. */
+	unsigned long accessed : 1;    /**< Accessed bit. */
 	unsigned long dirty : 1;       /**< Dirty bit. */
-	unsigned long reserved : 3;    /**< Reserved bits. */
+	unsigned long reserved : 2;    /**< Reserved bits. */
 	unsigned long pfn : 54;        /**< Physical frame number. */
 } pte_t;
@@ -229,9 +184,9 @@
 	
 	return (((!entry->valid) << PAGE_PRESENT_SHIFT) |
-	    ((entry->type < PTE_TYPE_SR) << PAGE_USER_SHIFT) |
-	    PAGE_READ |
-	    (PTE_WRITABLE_ARCH(entry) << PAGE_WRITE_SHIFT) |
-	    (PTE_EXECUTABLE_ARCH(entry) << PAGE_EXEC_SHIFT) |
-	    ((entry->type >= PTE_TYPE_SR_GLOBAL) << PAGE_GLOBAL_SHIFT));
+	    (entry->user << PAGE_USER_SHIFT) |
+	    (entry->readable << PAGE_READ_SHIFT) |
+	    (entry->writable << PAGE_WRITE_SHIFT) |
+	    (entry->executable << PAGE_EXEC_SHIFT) |
+	    (entry->global << PAGE_GLOBAL_SHIFT));
 }
 
@@ -241,16 +196,11 @@
 	
 	entry->valid = !(flags & PAGE_NOT_PRESENT);
-	
-	if ((flags & PAGE_WRITE) != 0) {
-		if ((flags & PAGE_EXEC) != 0)
-			entry->type = PTE_TYPE_SRWXURWX;
-		else
-			entry->type = PTE_TYPE_SRWURW;
-	} else {
-		if ((flags & PAGE_EXEC) != 0)
-			entry->type = PTE_TYPE_SRXURX;
-		else
-			entry->type = PTE_TYPE_SRUR;
-	}
+	entry->readable = (flags & PAGE_READ) != 0;
+	entry->writable = (flags & PAGE_WRITE) != 0;
+	entry->executable = (flags & PAGE_EXEC) != 0;
+	entry->user = (flags & PAGE_USER) != 0;
+	entry->global = (flags & PAGE_GLOBAL) != 0;
+	entry->accessed = 1;
+	entry->dirty = 1;
 }
 
@@ -264,4 +214,5 @@
 extern void page_arch_init(void);
 extern void page_fault(unsigned int, istate_t *);
+extern void write_satp(uintptr_t);
 
 #endif /* __ASM__ */
Index: kernel/arch/riscv64/src/asm.S
===================================================================
--- kernel/arch/riscv64/src/asm.S	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ kernel/arch/riscv64/src/asm.S	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -31,5 +31,13 @@
 .text
 
-SYMBOL(kernel_image_start)
-	// FIXME
-	nop
+FUNCTION_BEGIN(asm_delay_loop)
+	ret
+FUNCTION_END(asm_delay_loop)
+
+FUNCTION_BEGIN(early_putchar)
+	ret
+FUNCTION_END(early_putchar)
+
+FUNCTION_BEGIN(cpu_halt)
+	j cpu_halt
+FUNCTION_END(cpu_halt)
Index: kernel/arch/riscv64/src/boot/boot.S
===================================================================
--- kernel/arch/riscv64/src/boot/boot.S	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/arch/riscv64/src/boot/boot.S	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#include <abi/asmtool.h>
+#include <arch/boot/boot.h>
+#include <arch/cpu.h>
+
+.section K_TEXT_START, "ax"
+
+SYMBOL(kernel_image_start)
+	/* Setup temporary stack */
+	la sp, temp_stack
+	
+	/* Create the first stack frame */
+	addi sp, sp, -16
+	
+	/* Call riscv64_pre_main() */
+	jal riscv64_pre_main
+	
+	/* Call main_bsp() */
+	jal main_bsp
+	
+	/* Not reached */
+	csrci sstatus, SSTATUS_SIE_MASK
+	hlt0:
+		wfi
+		j hlt0
+
+.data
+
+.align 16
+.space TEMP_STACK_SIZE
+temp_stack:
Index: kernel/arch/riscv64/src/context.S
===================================================================
--- kernel/arch/riscv64/src/context.S	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/arch/riscv64/src/context.S	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,80 @@
+#
+# Copyright (c) 2003-2004 Jakub Jermar
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+#   notice, this list of conditions and the following disclaimer.
+# - Redistributions in binary form must reproduce the above copyright
+#   notice, this list of conditions and the following disclaimer in the
+#   documentation and/or other materials provided with the distribution.
+# - The name of the author may not be used to endorse or promote products
+#   derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+#include <abi/asmtool.h>
+#include <arch/context_struct.h>
+
+.text
+
+FUNCTION_BEGIN(context_save_arch)
+	sd gp, CONTEXT_OFFSET_GP(a0)
+	sd tp, CONTEXT_OFFSET_TP(a0)
+	sd s0, CONTEXT_OFFSET_S0(a0)
+	sd s1, CONTEXT_OFFSET_S1(a0)
+	sd s2, CONTEXT_OFFSET_S2(a0)
+	sd s3, CONTEXT_OFFSET_S3(a0)
+	sd s4, CONTEXT_OFFSET_S4(a0)
+	sd s5, CONTEXT_OFFSET_S5(a0)
+	sd s6, CONTEXT_OFFSET_S6(a0)
+	sd s7, CONTEXT_OFFSET_S7(a0)
+	sd s8, CONTEXT_OFFSET_S8(a0)
+	sd s9, CONTEXT_OFFSET_S9(a0)
+	sd s10, CONTEXT_OFFSET_S10(a0)
+	sd s11, CONTEXT_OFFSET_S11(a0)
+	
+	sd ra, CONTEXT_OFFSET_PC(a0)
+	sd sp, CONTEXT_OFFSET_SP(a0)
+	
+	# context_save returns 1
+	li a0, 1
+	ret
+FUNCTION_END(context_save_arch)
+
+FUNCTION_BEGIN(context_restore_arch)
+	ld gp, CONTEXT_OFFSET_GP(a0)
+	ld tp, CONTEXT_OFFSET_TP(a0)
+	ld s0, CONTEXT_OFFSET_S0(a0)
+	ld s1, CONTEXT_OFFSET_S1(a0)
+	ld s2, CONTEXT_OFFSET_S2(a0)
+	ld s3, CONTEXT_OFFSET_S3(a0)
+	ld s4, CONTEXT_OFFSET_S4(a0)
+	ld s5, CONTEXT_OFFSET_S5(a0)
+	ld s6, CONTEXT_OFFSET_S6(a0)
+	ld s7, CONTEXT_OFFSET_S7(a0)
+	ld s8, CONTEXT_OFFSET_S8(a0)
+	ld s9, CONTEXT_OFFSET_S9(a0)
+	ld s10, CONTEXT_OFFSET_S10(a0)
+	ld s11, CONTEXT_OFFSET_S11(a0)
+	
+	ld ra, CONTEXT_OFFSET_PC(a0)
+	ld sp, CONTEXT_OFFSET_SP(a0)
+	
+	# context_restore returns 0
+	li a0, 0
+	ret
+FUNCTION_END(context_restore_arch)
Index: kernel/arch/riscv64/src/drivers/ucb.c
===================================================================
--- kernel/arch/riscv64/src/drivers/ucb.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ kernel/arch/riscv64/src/drivers/ucb.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#include <arch/drivers/ucb.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <console/chardev.h>
+#include <str.h>
+#include <mm/slab.h>
+
+#define HTIF_DEVICE_CONSOLE  1
+
+#define HTIF_CONSOLE_PUTC  1
+
+static volatile uint64_t *tohost;
+static volatile uint64_t *fromhost;
+
+static outdev_operations_t htifdev_ops = {
+	.write = htif_putchar,
+	.redraw = NULL,
+	.scroll_up = NULL,
+	.scroll_down = NULL
+};
+
+static void poll_fromhost()
+{
+	uint64_t val = *fromhost;
+	if (!val)
+		return;
+	
+	*fromhost = 0;
+}
+
+void htif_init(volatile uint64_t *tohost_addr, volatile uint64_t *fromhost_addr)
+{
+	tohost = tohost_addr;
+	fromhost = fromhost_addr;
+}
+
+outdev_t *htifout_init(void)
+{
+	outdev_t *htifdev = malloc(sizeof(outdev_t), FRAME_ATOMIC);
+	if (!htifdev)
+		return NULL;
+	
+	outdev_initialize("htifdev", htifdev, &htifdev_ops);
+	return htifdev;
+}
+
+static void htif_cmd(uint8_t device, uint8_t cmd, uint64_t payload)
+{
+	uint64_t val = (((uint64_t) device) << 56) |
+	    (((uint64_t) cmd) << 48) |
+	    (payload & UINT64_C(0xffffffffffff));
+	
+	while (*tohost)
+		poll_fromhost();
+	
+	*tohost = val;
+}
+
+void htif_putchar(outdev_t *dev, const wchar_t ch)
+{
+	if (ascii_check(ch))
+		htif_cmd(HTIF_DEVICE_CONSOLE, HTIF_CONSOLE_PUTC, ch);
+	else
+		htif_cmd(HTIF_DEVICE_CONSOLE, HTIF_CONSOLE_PUTC, U_SPECIAL);
+}
Index: kernel/arch/riscv64/src/mm/as.c
===================================================================
--- kernel/arch/riscv64/src/mm/as.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ kernel/arch/riscv64/src/mm/as.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -40,4 +40,15 @@
 }
 
+/** Install address space.
+ *
+ * Install ASID.
+ *
+ * @param as Address space structure.
+ */
+void as_install_arch(as_t *as)
+{
+	// FIXME
+}
+
 /** @}
  */
Index: kernel/arch/riscv64/src/mm/frame.c
===================================================================
--- kernel/arch/riscv64/src/mm/frame.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ kernel/arch/riscv64/src/mm/frame.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -32,5 +32,7 @@
 
 #include <mm/frame.h>
+#include <arch/boot/boot.h>
 #include <arch/mm/frame.h>
+#include <arch/drivers/ucb.h>
 #include <mm/as.h>
 #include <config.h>
@@ -39,9 +41,10 @@
 #include <align.h>
 #include <macros.h>
-
 #include <print.h>
 
-size_t hardcoded_unmapped_ktext_size = 0;
-size_t hardcoded_unmapped_kdata_size = 0;
+uintptr_t physmem_start;
+uintptr_t htif_frame;
+uintptr_t pt_frame;
+memmap_t memmap;
 
 void physmem_print(void)
@@ -49,11 +52,51 @@
 }
 
+static void frame_common_arch_init(bool low)
+{
+	pfn_t minconf =
+	    max3(ADDR2PFN(physmem_start), htif_frame + 1, pt_frame + 1);
+	
+	for (size_t i = 0; i < memmap.cnt; i++) {
+		/* To be safe, make the available zone possibly smaller */
+		uintptr_t base = ALIGN_UP((uintptr_t) memmap.zones[i].start,
+		    FRAME_SIZE);
+		size_t size = ALIGN_DOWN(memmap.zones[i].size -
+		    (base - ((uintptr_t) memmap.zones[i].start)), FRAME_SIZE);
+		
+		if (!frame_adjust_zone_bounds(low, &base, &size))
+			return;
+		
+		pfn_t pfn = ADDR2PFN(base);
+		size_t count = SIZE2FRAMES(size);
+		pfn_t conf;
+		
+		if (low) {
+			if ((minconf < pfn) || (minconf >= pfn + count))
+				conf = pfn;
+			else
+				conf = minconf;
+			
+			zone_create(pfn, count, conf,
+			    ZONE_AVAILABLE | ZONE_LOWMEM);
+		} else {
+			conf = zone_external_conf_alloc(count);
+			if (conf != 0)
+				zone_create(pfn, count, conf,
+				    ZONE_AVAILABLE | ZONE_HIGHMEM);
+		}
+	}
+}
 
 void frame_low_arch_init(void)
 {
+	frame_common_arch_init(true);
+	
+	frame_mark_unavailable(htif_frame, 1);
+	frame_mark_unavailable(pt_frame, 1);
 }
 
 void frame_high_arch_init(void)
 {
+	frame_common_arch_init(false);
 }
 
Index: kernel/arch/riscv64/src/mm/km.c
===================================================================
--- kernel/arch/riscv64/src/mm/km.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ kernel/arch/riscv64/src/mm/km.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -32,18 +32,25 @@
 
 #include <arch/mm/km.h>
+#include <mm/km.h>
 #include <stdbool.h>
 #include <typedefs.h>
+#include <macros.h>
 
 void km_identity_arch_init(void)
 {
+	config.identity_base = KM_RISCV64_IDENTITY_START;
+	config.identity_size = KM_RISCV64_IDENTITY_SIZE;
 }
 
 void km_non_identity_arch_init(void)
 {
+	km_non_identity_span_add(KM_RISCV64_NON_IDENTITY_START,
+	    KM_RISCV64_NON_IDENTITY_SIZE);
 }
 
 bool km_is_non_identity_arch(uintptr_t addr)
 {
-	return false;
+	return iswithin(KM_RISCV64_NON_IDENTITY_START,
+	    KM_RISCV64_NON_IDENTITY_SIZE, addr, 1);
 }
 
Index: kernel/arch/riscv64/src/mm/page.c
===================================================================
--- kernel/arch/riscv64/src/mm/page.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ kernel/arch/riscv64/src/mm/page.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -36,4 +36,5 @@
 #include <genarch/mm/page_pt.h>
 #include <arch/mm/frame.h>
+#include <arch/cpu.h>
 #include <mm/frame.h>
 #include <mm/page.h>
@@ -50,6 +51,27 @@
 void page_arch_init(void)
 {
-	if (config.cpu_active == 1)
+	if (config.cpu_active == 1) {
 		page_mapping_operations = &pt_mapping_operations;
+		
+		page_table_lock(AS_KERNEL, true);
+		
+		/*
+		 * PA2KA(identity) mapping for all low-memory frames.
+		 */
+		for (uintptr_t cur = 0;
+		    cur < min(config.identity_size, config.physmem_end);
+		    cur += FRAME_SIZE)
+			page_mapping_insert(AS_KERNEL, PA2KA(cur), cur,
+			    PAGE_GLOBAL | PAGE_CACHEABLE | PAGE_EXEC | PAGE_WRITE | PAGE_READ);
+		
+		page_table_unlock(AS_KERNEL, true);
+		
+		// FIXME: register page fault extension handler
+		
+		write_satp((uintptr_t) AS_KERNEL->genarch.page_table);
+		
+		/* The boot page table is no longer needed. */
+		// FIXME: frame_mark_available(pt_frame, 1);
+	}
 }
 
@@ -58,4 +80,15 @@
 }
 
+void write_satp(uintptr_t ptl0)
+{
+	uint64_t satp = ((ptl0 >> FRAME_WIDTH) & SATP_PFN_MASK) |
+	    SATP_MODE_SV48;
+	
+	asm volatile (
+		"csrw sptbr, %[satp]\n"
+		:: [satp] "r" (satp)
+	);
+}
+
 /** @}
  */
Index: kernel/arch/riscv64/src/riscv64.c
===================================================================
--- kernel/arch/riscv64/src/riscv64.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ kernel/arch/riscv64/src/riscv64.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -35,6 +35,8 @@
 #include <arch.h>
 #include <stddef.h>
+#include <arch/arch.h>
 #include <arch/interrupt.h>
 #include <arch/asm.h>
+#include <arch/drivers/ucb.h>
 
 #include <func.h>
@@ -49,12 +51,51 @@
 #include <console/console.h>
 #include <mem.h>
+#include <str.h>
 
 char memcpy_from_uspace_failover_address;
 char memcpy_to_uspace_failover_address;
 
+static void riscv64_post_mm_init(void);
+
 arch_ops_t riscv64_ops = {
+	.post_mm_init = riscv64_post_mm_init
 };
 
 arch_ops_t *arch_ops = &riscv64_ops;
+
+void riscv64_pre_main(bootinfo_t *bootinfo)
+{
+	physmem_start = bootinfo->physmem_start;
+	htif_frame = bootinfo->htif_frame;
+	pt_frame = bootinfo->pt_frame;
+	
+	htif_init(bootinfo->ucbinfo.tohost, bootinfo->ucbinfo.fromhost);
+	
+	/* Copy tasks map. */
+	init.cnt = min3(bootinfo->taskmap.cnt, TASKMAP_MAX_RECORDS,
+	    CONFIG_INIT_TASKS);
+	
+	for (size_t i = 0; i < init.cnt; i++) {
+		init.tasks[i].paddr = KA2PA(bootinfo->taskmap.tasks[i].addr);
+		init.tasks[i].size = bootinfo->taskmap.tasks[i].size;
+		str_cpy(init.tasks[i].name, CONFIG_TASK_NAME_BUFLEN,
+		    bootinfo->taskmap.tasks[i].name);
+	}
+	
+	/* Copy physical memory map. */
+	memmap.total = bootinfo->memmap.total;
+	memmap.cnt = min(bootinfo->memmap.cnt, MEMMAP_MAX_RECORDS);
+	for (size_t i = 0; i < memmap.cnt; i++) {
+		memmap.zones[i].start = bootinfo->memmap.zones[i].start;
+		memmap.zones[i].size = bootinfo->memmap.zones[i].size;
+	}
+}
+
+void riscv64_post_mm_init(void)
+{
+	outdev_t *htifout = htifout_init();
+	if (htifout)
+		stdout_wire(htifout);
+}
 
 void calibrate_delay_loop(void)
@@ -90,14 +131,4 @@
 }
 
-int context_save_arch(context_t *ctx)
-{
-	return 1;
-}
-
-void context_restore_arch(context_t *ctx)
-{
-	while (true);
-}
-
 void fpu_init(void)
 {
@@ -122,8 +153,4 @@
 }
 
-void early_putchar(wchar_t ch)
-{
-}
-
 /** @}
  */
Index: kernel/generic/src/ipc/sysipc.c
===================================================================
--- kernel/generic/src/ipc/sysipc.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ kernel/generic/src/ipc/sysipc.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -766,5 +766,5 @@
 		}
 		
-		STRUCT_TO_USPACE(&calldata->args, &call->data.args);
+		STRUCT_TO_USPACE(calldata, &call->data);
 		ipc_call_free(call);
 		
Index: kernel/test/synch/rcu1.c
===================================================================
--- kernel/test/synch/rcu1.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ kernel/test/synch/rcu1.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -389,4 +389,9 @@
 static void seq_func(void *arg)
 {
+	/*
+	 * Temporarily workaround GCC 7.1.0 internal
+	 * compiler error when compiling for riscv64.
+	 */
+#ifndef KARCH_riscv64
 	seq_work_t *work = (seq_work_t*)arg;
 	
@@ -435,5 +440,8 @@
 		}
 		
-	} 
+	}
+#else
+	(void) seq_cb;
+#endif
 }
 
Index: tools/ew.py
===================================================================
--- tools/ew.py	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ tools/ew.py	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -212,5 +212,5 @@
 
 def spike_run(platform, machine, processor):
-	run_in_console('spike image.boot', 'HelenOS/risvc64 on Spike')
+	run_in_console('spike -m1073741824:1073741824 image.boot', 'HelenOS/risvc64 on Spike')
 
 emulators = {
Index: uspace/Makefile
===================================================================
--- uspace/Makefile	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/Makefile	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -48,4 +48,5 @@
 	app/fontviewer \
 	app/getterm \
+	app/gunzip \
 	app/init \
 	app/inet \
@@ -80,4 +81,5 @@
 	app/nic \
 	app/ping \
+	app/pkg \
 	app/sysinfo \
 	app/sysinst \
@@ -132,5 +134,10 @@
 	srv/hid/output \
 	srv/hid/remcons \
+	srv/hw/bus/cuda_adb \
 	srv/hw/char/s3c24xx_uart \
+	srv/hw/irc/apic \
+	srv/hw/irc/i8259 \
+	srv/hw/irc/icp-ic \
+	srv/hw/irc/obio \
 	srv/hid/rfb \
 	drv/audio/hdaudio \
@@ -141,13 +148,6 @@
 	drv/block/ata_bd \
 	drv/block/ddisk \
-	drv/char/i8042 \
-	drv/char/pl050 \
-	drv/char/ps2mouse \
-	drv/char/atkbd \
-	drv/char/xtkbd \
-	drv/test/test1 \
-	drv/test/test2 \
-	drv/test/test3 \
-	drv/fb/kfb \
+	drv/bus/isa \
+	drv/bus/pci/pciintel \
 	drv/bus/usb/ehci \
 	drv/bus/usb/ohci \
@@ -159,4 +159,15 @@
 	drv/bus/usb/usbmid \
 	drv/bus/usb/vhc \
+	drv/char/i8042 \
+	drv/char/ns8250 \
+	drv/char/pl050 \
+	drv/char/ps2mouse \
+	drv/char/atkbd \
+	drv/char/xtkbd \
+	drv/test/test1 \
+	drv/test/test2 \
+	drv/test/test3 \
+	drv/fb/amdm37x_dispc \
+	drv/fb/kfb \
 	drv/nic/ne2k \
 	drv/nic/e1k \
@@ -164,56 +175,16 @@
 	drv/nic/rtl8169 \
 	drv/nic/ar9271 \
-	drv/platform/icp
-
-## Platform-specific hardware support
-#
-
-ifeq ($(UARCH), $(filter $(UARCH),amd64 ia32 ia64))
-	DIRS += \
-		drv/platform/pc \
-		drv/bus/pci/pciintel \
-		drv/bus/isa \
-		drv/char/ns8250 \
-		drv/time/cmos-rtc \
-		srv/hw/irc/apic \
-		srv/hw/irc/i8259
-endif
-
-ifeq ($(UARCH), $(filter $(UARCH),mips32 mips32eb))
-ifeq ($(MACHINE),msim)
-	DIRS += \
-		drv/platform/msim
-else
-	DIRS += \
-		drv/platform/malta \
-		drv/bus/pci/pciintel \
-		drv/bus/isa
-endif
-endif
-
-ifeq ($(UARCH),ppc32)
-	DIRS += \
-		drv/platform/mac \
-		drv/bus/pci/pciintel \
-		srv/hw/bus/cuda_adb
-endif
-
+	drv/platform/amdm37x \
+	drv/platform/icp \
+	drv/platform/mac \
+	drv/platform/malta \
+	drv/platform/msim \
+	drv/platform/pc \
+	drv/time/cmos-rtc
+
+## sun4u driver won't compile on 32-bit targets
 ifeq ($(UARCH),sparc64)
 	DIRS += \
-		srv/hw/irc/obio
-ifeq ($(MACHINE),generic)
-	DIRS += \
-		drv/platform/sun4u \
-		drv/bus/pci/pciintel \
-		drv/bus/isa \
-		drv/char/ns8250
-endif
-endif
-
-ifeq ($(UARCH),arm32)
-	DIRS += \
-		drv/platform/amdm37x \
-		drv/fb/amdm37x_dispc \
-		srv/hw/irc/icp-ic
+		drv/platform/sun4u
 endif
 
Index: uspace/Makefile.common
===================================================================
--- uspace/Makefile.common	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/Makefile.common	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -212,7 +212,8 @@
 ifneq ($(TEST_SOURCES),)
 	TEST_OUTPUT = $(TEST_BINARY)
+	TEST_DISASM = $(TEST_BINARY).disasm
 	TEST_CFLAGS = -I$(LIB_PREFIX)/pcut/include -D__helenos__
 	TEST_OUTPUT_LIBS = $(LIB_PREFIX)/pcut/libpcut.a
-	EXTRA_CLEAN += $(TEST_OUTPUT) $(TEST_OUTPUT).map
+	EXTRA_CLEAN += $(TEST_OUTPUT) $(TEST_OUTPUT).map $(TEST_DISASM)
 ifneq ($(LIBRARY),)
 	TEST_OUTPUT_LIBS += $(OUTPUT)
@@ -226,5 +227,5 @@
 	-[ -f $(DEPEND) ] && cp -a $(DEPEND) $(DEPEND_PREV)
 
-all-test: $(TEST_OUTPUT)
+all-test: $(TEST_OUTPUT) $(TEST_DISASM)
 
 clean:
@@ -320,5 +321,5 @@
 
 ifneq ($(BINARY),)
-%.disasm: $(BINARY)
+$(BINARY).disasm: $(BINARY)
 ifeq ($(CONFIG_LINE_DEBUG),y)
 	$(OBJDUMP) -d -S $< > $@
@@ -335,5 +336,5 @@
 
 ifneq ($(SLIBRARY),)
-%.disasm: $(LOUTPUT)
+$(LOUTPUT).disasm: $(LOUTPUT)
 ifeq ($(CONFIG_LINE_DEBUG),y)
 	$(OBJDUMP) -d -S $< > $@
@@ -368,4 +369,10 @@
 	$(STRIP) $(TEST_OUTPUT)
 endif
+$(TEST_BINARY).disasm: $(TEST_BINARY)
+ifeq ($(CONFIG_LINE_DEBUG),y)
+	$(OBJDUMP) -d -S $< > $@
+else
+	$(OBJDUMP) -d $< > $@
+endif
 endif
 
Index: uspace/app/devctl/devctl.c
===================================================================
--- uspace/app/devctl/devctl.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/app/devctl/devctl.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -35,4 +35,5 @@
 #include <devman.h>
 #include <errno.h>
+#include <io/table.h>
 #include <stdbool.h>
 #include <stdio.h>
@@ -205,4 +206,5 @@
 	size_t ndevs;
 	size_t i;
+	table_t *table = NULL;
 	int rc;
 
@@ -210,4 +212,13 @@
 	if (rc != EOK)
 		return rc;
+
+	rc = table_create(&table);
+	if (rc != EOK) {
+		assert(rc == ENOMEM);
+		goto out;
+	}
+
+	table_header_row(table);
+	table_printf(table, "Driver\t" "Devs\t" "State\n");
 
 	for (i = 0; i < ndrvs; i++) {
@@ -226,11 +237,17 @@
 		sstate = drv_state_str(state);
 
-		printf("%-11s %3zu %s\n", sstate, ndevs, drv_name);
+		table_printf(table, "%s\t" "%zu\t" "%s\n", drv_name, ndevs, sstate);
 skip:
 		free(devs);
 	}
+
+	rc = table_print_out(table, stdout);
+	if (rc != EOK)
+		printf("Error printing driver table.\n");
+out:
 	free(drvs);
-
-	return EOK;
+	table_destroy(table);
+
+	return rc;
 }
 
Index: uspace/app/df/df.c
===================================================================
--- uspace/app/df/df.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/app/df/df.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -28,5 +28,5 @@
 
 /** @addtogroup df
- * @brief Df utility.
+ * @brief Print amounts of free and used disk space.
  * @{
  */
@@ -35,4 +35,6 @@
  */
 
+#include <cap.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -46,19 +48,14 @@
 #define NAME  "df"
 
-#define HEADER_TABLE 	"Filesystem     %4u-blocks           Used      Available Used%% Mounted on"
-#define HEADER_TABLE_HR "Filesystem           Size           Used      Available Used%% Mounted on"
+#define HEADER_TABLE     "Filesystem           Size           Used      Available Used%% Mounted on"
+#define HEADER_TABLE_BLK "Filesystem  Blk. Size     Total        Used   Available Used%% Mounted on"
 
 #define PERCENTAGE(x, tot) (tot ? (100ULL * (x) / (tot)) : 0)
-#define FSBK_TO_BK(x, fsbk, bk) \
-	(((fsbk) != 0 && (fsbk) < (bk)) ? \
-		(unsigned long long) ((x) / ((bk) / (fsbk))) : \
-		(unsigned long long) ((x) * ((fsbk) / (bk))))
-
-static unsigned int unit_size;
-static unsigned int human_readable;
-
-static void size_to_human_readable(char *buf, uint64_t bytes);
+
+static bool display_blocks;
+
+static int size_to_human_readable(uint64_t, size_t, char **);
 static void print_header(void);
-static void print_statfs(struct statfs *, char *, char *);
+static int print_statfs(struct statfs *, char *, char *);
 static void print_usage(void);
 
@@ -67,23 +64,17 @@
 	int optres, errflg = 0;
 	struct statfs st;
-
-	unit_size = 512;
-	human_readable = 0;
-
-	/******************************************/
-	/*   Parse command line options...        */
-	/******************************************/
-	while ((optres = getopt(argc, argv, ":uhb:")) != -1) {
+	int rc;
+
+	display_blocks = false;
+
+	/* Parse command-line options */
+	while ((optres = getopt(argc, argv, ":ubh")) != -1) {
 		switch(optres) {
-		case 'u':
+		case 'h':
 			print_usage();
 			return 0;
 
-		case 'h':
-			human_readable = 1;
-			break;
-
 		case 'b':
-			str_uint32_t(optarg, NULL, 0, 0, &unit_size);
+			display_blocks = true;
 			break;
  
@@ -123,5 +114,7 @@
 	list_foreach(mtab_list, link, mtab_ent_t, mtab_ent) {
 		if (vfs_statfs_path(mtab_ent->mp, &st) == 0) {
-			print_statfs(&st, mtab_ent->fs_name, mtab_ent->mp);
+			rc = print_statfs(&st, mtab_ent->fs_name, mtab_ent->mp);
+			if (rc != EOK)
+				return 1;
 		} else {
 			fprintf(stderr, "Cannot get information for '%s' (%d).\n",
@@ -134,48 +127,53 @@
 }
 
-static void size_to_human_readable(char *buf, uint64_t bytes)
-{
-	const char *units = "BkMGTPEZY";
-	int i = 0;
-
-	while (bytes >= 1024) {
-		bytes /= 1024;
-		i++;
-	}
-
-	snprintf(buf, 6, "%4llu%c", (unsigned long long) bytes, units[i]);
+static int size_to_human_readable(uint64_t nblocks, size_t block_size, char **rptr)
+{
+	cap_spec_t cap;
+
+	cap_from_blocks(nblocks, block_size, &cap);
+	cap_simplify(&cap);
+	return cap_format(&cap, rptr);
 }
 
 static void print_header(void)
 {
-	if (human_readable)
-		printf(HEADER_TABLE_HR);
+	if (!display_blocks)
+		printf(HEADER_TABLE);
 	else
-		printf(HEADER_TABLE, unit_size);
+		printf(HEADER_TABLE_BLK);
 
 	putchar('\n');
 }
 
-static void print_statfs(struct statfs *st, char *name, char *mountpoint)
+static int print_statfs(struct statfs *st, char *name, char *mountpoint)
 {
 	uint64_t const used_blocks = st->f_blocks - st->f_bfree;
 	unsigned const perc_used = PERCENTAGE(used_blocks, st->f_blocks);
+	char *str;
+	int rc;
 
 	printf("%10s", name);
 
-	if (human_readable) {
-		char tmp[1024];
-
+	if (!display_blocks) {
 		/* Print size */
-		size_to_human_readable(tmp, st->f_blocks *  st->f_bsize);
-		printf(" %14s", tmp);
+		rc = size_to_human_readable(st->f_blocks, st->f_bsize, &str);
+		if (rc != EOK)
+			goto error;
+		printf(" %14s", str);
+		free(str);
 
 		/* Number of used blocks */
-		size_to_human_readable(tmp, used_blocks  *  st->f_bsize);
-		printf(" %14s", tmp);
+		rc = size_to_human_readable(used_blocks, st->f_bsize, &str);
+		if (rc != EOK)
+			goto error;
+		printf(" %14s", str);
+		free(str);
 
 		/* Number of available blocks */
-		size_to_human_readable(tmp, st->f_bfree *  st->f_bsize);
-		printf(" %14s", tmp);
+		rc = size_to_human_readable(st->f_bfree, st->f_bsize, &str);
+		if (rc != EOK)
+			goto error;
+		printf(" %14s", str);
+		free(str);
 
 		/* Percentage of used blocks */
@@ -185,22 +183,22 @@
 		printf(" %s\n", mountpoint);
 	} else {
-		/* Blocks / Used blocks / Available blocks / Used% / Mounted on */
-		printf(" %15llu %14llu %14llu %4u%% %s\n", 
-		    FSBK_TO_BK(st->f_blocks, st->f_bsize, unit_size),
-		    FSBK_TO_BK(used_blocks, st->f_bsize, unit_size),
-		    FSBK_TO_BK(st->f_bfree, st->f_bsize, unit_size),
-		    perc_used,
-		    mountpoint);
-	}
-
+		/* Block size / Blocks / Used blocks / Available blocks / Used% / Mounted on */
+		printf(" %10" PRIu32 " %9" PRIu64 " %11" PRIu64 " %11" PRIu64 " %4u%% %s\n", 
+		    st->f_bsize, st->f_blocks, used_blocks, st->f_bfree,
+		    perc_used, mountpoint);
+	}
+
+	return EOK;
+error:
+	printf("\nError: Out of memory.\n");
+	return ENOMEM;
 }
 
 static void print_usage(void)
 {
-	printf("syntax: %s [-u] [-h] [-b <size>] \n", NAME);
-	printf("  u : Show usage.\n");
-	printf("  h : \"Human-readable\" output.\n");
-	printf("  b : Scale block sizes by selected size.\n");
-	printf("\n");
+	printf("Syntax: %s [<options>] \n", NAME);
+	printf("Options:\n");
+	printf("  -h Print help\n");
+	printf("  -b Print exact block sizes and numbers\n");
 }
 
Index: uspace/app/download/main.c
===================================================================
--- uspace/app/download/main.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/app/download/main.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -57,6 +57,6 @@
 static void syntax_print(void)
 {
-	fprintf(stderr, "Usage: download <url>\n");
-	fprintf(stderr, "  This will write the data to stdout, so you may want\n");
+	fprintf(stderr, "Usage: download [-o <outfile>] <url>\n");
+	fprintf(stderr, "  Without -o, data will be written to stdout, so you may want\n");
 	fprintf(stderr, "  to redirect the output, e.g.\n");
 	fprintf(stderr, "\n");
@@ -66,18 +66,55 @@
 int main(int argc, char *argv[])
 {
-	if (argc != 2) {
+	int i;
+	char *ofname = NULL;
+	FILE *ofile = NULL;
+	size_t buf_size = 4096;
+	void *buf = NULL;
+	uri_t *uri = NULL;
+	http_t *http = NULL;
+	int rc;
+
+	if (argc < 2) {
 		syntax_print();
-		return 2;
-	}
-	
-	uri_t *uri = uri_parse(argv[1]);
+		rc = EINVAL;
+		goto error;
+	}
+	
+	i = 1;
+	
+	if (str_cmp(argv[i], "-o") == 0) {
+		++i;
+		if (argc < i + 1) {
+			syntax_print();
+			rc = EINVAL;
+			goto error;
+		}
+		
+		ofname = argv[i++];
+		ofile = fopen(ofname, "wb");
+		if (ofile == NULL) {
+			fprintf(stderr, "Error creating '%s'.\n", ofname);
+			rc = EINVAL;
+			goto error;
+		}
+	}
+	
+	if (argc != i + 1) {
+		syntax_print();
+		rc = EINVAL;
+		goto error;
+	}
+	
+	uri = uri_parse(argv[i]);
 	if (uri == NULL) {
 		fprintf(stderr, "Failed parsing URI\n");
-		return 2;
+		rc = EINVAL;
+		goto error;
 	}
 	
 	if (!uri_validate(uri)) {
 		fprintf(stderr, "The URI is invalid\n");
-		return 2;
+		rc = EINVAL;
+		goto error;
 	}
 	
@@ -86,18 +123,21 @@
 	if (str_cmp(uri->scheme, "http") != 0) {
 		fprintf(stderr, "Only http scheme is supported at the moment\n");
-		return 2;
+		rc = EINVAL;
+		goto error;
 	}
 	
 	if (uri->host == NULL) {
 		fprintf(stderr, "host not set\n");
-		return 2;
+		rc = EINVAL;
+		goto error;
 	}
 	
 	uint16_t port = 80;
 	if (uri->port != NULL) {
-		int rc = str_uint16_t(uri->port, NULL, 10, true, &port);
+		rc = str_uint16_t(uri->port, NULL, 10, true, &port);
 		if (rc != EOK) {
 			fprintf(stderr, "Invalid port number: %s\n", uri->port);
-			return 2;
+			rc = EINVAL;
+			goto error;
 		}
 	}
@@ -111,14 +151,13 @@
 		if (server_path == NULL) {
 			fprintf(stderr, "Failed allocating path\n");
-			uri_destroy(uri);
-			return 3;
-		}
-	}
-	else {
-		int rc = asprintf(&server_path, "%s?%s", path, uri->query);
+			rc = ENOMEM;
+			goto error;
+		}
+	} else {
+		rc = asprintf(&server_path, "%s?%s", path, uri->query);
 		if (rc < 0) {
 			fprintf(stderr, "Failed allocating path\n");
-			uri_destroy(uri);
-			return 3;
+			rc = ENOMEM;
+			goto error;
 		}
 	}
@@ -128,13 +167,12 @@
 	if (req == NULL) {
 		fprintf(stderr, "Failed creating request\n");
-		uri_destroy(uri);
-		return 3;
-	}
-	
-	int rc = http_headers_append(&req->headers, "Host", uri->host);
+		rc = ENOMEM;
+		goto error;
+	}
+	
+	rc = http_headers_append(&req->headers, "Host", uri->host);
 	if (rc != EOK) {
 		fprintf(stderr, "Failed setting Host header: %s\n", str_error(rc));
-		uri_destroy(uri);
-		return rc;
+		goto error;
 	}
 	
@@ -142,13 +180,12 @@
 	if (rc != EOK) {
 		fprintf(stderr, "Failed creating User-Agent header: %s\n", str_error(rc));
-		uri_destroy(uri);
-		return rc;
-	}
-	
-	http_t *http = http_create(uri->host, port);
+		goto error;
+	}
+	
+	http = http_create(uri->host, port);
 	if (http == NULL) {
-		uri_destroy(uri);
 		fprintf(stderr, "Failed creating HTTP object\n");
-		return 3;
+		rc = ENOMEM;
+		goto error;
 	}
 	
@@ -156,6 +193,6 @@
 	if (rc != EOK) {
 		fprintf(stderr, "Failed connecting: %s\n", str_error(rc));
-		uri_destroy(uri);
-		return rc;
+		rc = EIO;
+		goto error;
 	}
 	
@@ -163,6 +200,6 @@
 	if (rc != EOK) {
 		fprintf(stderr, "Failed sending request: %s\n", str_error(rc));
-		uri_destroy(uri);
-		return rc;
+		rc = EIO;
+		goto error;
 	}
 	
@@ -172,6 +209,6 @@
 	if (rc != EOK) {
 		fprintf(stderr, "Failed receiving response: %s\n", str_error(rc));
-		uri_destroy(uri);
-		return rc;
+		rc = EIO;
+		goto error;
 	}
 	
@@ -179,17 +216,15 @@
 		fprintf(stderr, "Server returned status %d %s\n", response->status,
 		    response->message);
-	}
-	else {
-		size_t buf_size = 4096;
-		void *buf = malloc(buf_size);
+	} else {
+		buf = malloc(buf_size);
 		if (buf == NULL) {
 			fprintf(stderr, "Failed allocating buffer\n)");
-			uri_destroy(uri);
-			return ENOMEM;
+			rc = ENOMEM;
+			goto error;
 		}
 		
 		int body_size;
 		while ((body_size = recv_buffer(&http->recv_buffer, buf, buf_size)) > 0) {
-			fwrite(buf, 1, body_size, stdout);
+			fwrite(buf, 1, body_size, ofile != NULL ? ofile : stdout);
 		}
 		
@@ -199,6 +234,22 @@
 	}
 	
+	free(buf);
+	http_destroy(http);
 	uri_destroy(uri);
+	if (ofile != NULL && fclose(ofile) != 0) {
+		printf("Error writing '%s'.\n", ofname);
+		return EIO;
+	}
+
 	return EOK;
+error:
+	free(buf);
+	if (http != NULL)
+		http_destroy(http);
+	if (uri != NULL)
+		uri_destroy(uri);
+	if (ofile != NULL)
+		fclose(ofile);
+	return rc;
 }
 
Index: uspace/app/fdisk/fdisk.c
===================================================================
--- uspace/app/fdisk/fdisk.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/app/fdisk/fdisk.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -35,4 +35,5 @@
  */
 
+#include <cap.h>
 #include <errno.h>
 #include <nchoice.h>
@@ -151,5 +152,5 @@
 	nchoice_t *choice = NULL;
 	char *svcname = NULL;
-	fdisk_cap_t cap;
+	cap_spec_t cap;
 	fdisk_dev_info_t *sdev;
 	char *scap = NULL;
@@ -200,7 +201,7 @@
 		}
 
-		fdisk_cap_simplify(&cap);
-
-		rc = fdisk_cap_format(&cap, &scap);
+		cap_simplify(&cap);
+
+		rc = cap_format(&cap, &scap);
 		if (rc != EOK) {
 			assert(rc == ENOMEM);
@@ -445,6 +446,6 @@
 	int rc;
 	fdisk_part_spec_t pspec;
-	fdisk_cap_t cap;
-	fdisk_cap_t mcap;
+	cap_spec_t cap;
+	cap_spec_t mcap;
 	vol_label_supp_t vlsupp;
 	vol_fstype_t fstype = 0;
@@ -466,7 +467,7 @@
 	}
 
-	fdisk_cap_simplify(&mcap);
-
-	rc = fdisk_cap_format(&mcap, &smcap);
+	cap_simplify(&mcap);
+
+	rc = cap_format(&mcap, &smcap);
 	if (rc != EOK) {
 		rc = ENOMEM;
@@ -490,5 +491,5 @@
 			goto error;
 
-		rc = fdisk_cap_parse(scap, &cap);
+		rc = cap_parse(scap, &cap);
 		if (rc == EOK)
 			break;
@@ -586,7 +587,7 @@
 		}
 
-		fdisk_cap_simplify(&pinfo.capacity);
-
-		rc = fdisk_cap_format(&pinfo.capacity, &scap);
+		cap_simplify(&pinfo.capacity);
+
+		rc = cap_format(&pinfo.capacity, &scap);
 		if (rc != EOK) {
 			printf("Out of memory.\n");
@@ -703,6 +704,6 @@
 	fdisk_part_t *part;
 	fdisk_part_info_t pinfo;
-	fdisk_cap_t cap;
-	fdisk_cap_t mcap;
+	cap_spec_t cap;
+	cap_spec_t mcap;
 	fdisk_dev_flags_t dflags;
 	char *sltype = NULL;
@@ -738,7 +739,7 @@
 	}
 
-	fdisk_cap_simplify(&cap);
-
-	rc = fdisk_cap_format(&cap, &sdcap);
+	cap_simplify(&cap);
+
+	rc = cap_format(&cap, &sdcap);
 	if (rc != EOK) {
 		printf("Out of memory.\n");
@@ -792,7 +793,7 @@
 		}
 
-		fdisk_cap_simplify(&pinfo.capacity);
-
-		rc = fdisk_cap_format(&pinfo.capacity, &scap);
+		cap_simplify(&pinfo.capacity);
+
+		rc = cap_format(&pinfo.capacity, &scap);
 		if (rc != EOK) {
 			printf("Out of memory.\n");
@@ -849,7 +850,7 @@
 		}
 
-		fdisk_cap_simplify(&mcap);
-
-		rc = fdisk_cap_format(&mcap, &smcap);
+		cap_simplify(&mcap);
+
+		rc = cap_format(&mcap, &smcap);
 		if (rc != EOK) {
 			rc = ENOMEM;
@@ -871,7 +872,7 @@
 		}
 
-		fdisk_cap_simplify(&mcap);
-
-		rc = fdisk_cap_format(&mcap, &smcap);
+		cap_simplify(&mcap);
+
+		rc = cap_format(&mcap, &smcap);
 		if (rc != EOK) {
 			rc = ENOMEM;
@@ -896,7 +897,7 @@
 		}
 
-		fdisk_cap_simplify(&mcap);
-
-		rc = fdisk_cap_format(&mcap, &smcap);
+		cap_simplify(&mcap);
+
+		rc = cap_format(&mcap, &smcap);
 		if (rc != EOK) {
 			rc = ENOMEM;
@@ -914,7 +915,7 @@
 		}
 
-		fdisk_cap_simplify(&mcap);
-
-		rc = fdisk_cap_format(&mcap, &smcap);
+		cap_simplify(&mcap);
+
+		rc = cap_format(&mcap, &smcap);
 		if (rc != EOK) {
 			rc = ENOMEM;
Index: uspace/app/gunzip/Makefile
===================================================================
--- uspace/app/gunzip/Makefile	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/app/gunzip/Makefile	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,38 @@
+#
+# Copyright (c) 2017 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 = gunzip
+
+EXTRA_CFLAGS += -I$(LIBCOMPRESS_PREFIX)
+LIBS = $(LIBCOMPRESS_PREFIX)/libcompress.a
+
+SOURCES = \
+	gunzip.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/gunzip/gunzip.c
===================================================================
--- uspace/app/gunzip/gunzip.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/app/gunzip/gunzip.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2017 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 gunzip
+ * @{
+ */
+/** @file
+ */
+
+#include <errno.h>
+#include <gzip.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char *argv[])
+{
+	int rc;
+	void *data, *ddata;
+	size_t size, dsize;
+	size_t nread, nwr;
+	long len;
+	FILE *f, *wf;
+
+	if (argc != 3) {
+		printf("syntax: gunzip <src.gz> <dest>\n");
+		return 1;
+	}
+
+	f = fopen(argv[1], "rb");
+	if (f == NULL) {
+		printf("Error opening '%s'\n", argv[1]);
+		return 1;
+	}
+
+	rc = fseek(f, 0, SEEK_END);
+	if (rc != 0) {
+		printf("Error determining size of '%s'\n", argv[1]);
+		fclose(f);
+		return 1;
+	}
+
+	len = ftell(f);
+	if (len < 0) {
+		printf("Error determining size of '%s'\n", argv[1]);
+		fclose(f);
+		return 1;
+	}
+
+	rc = fseek(f, 0, SEEK_SET);
+	if (rc != 0) {
+		printf ("Error rewinding '%s'\n", argv[1]);
+		fclose(f);
+		return 1;
+	}
+
+	data = malloc(len);
+	if (data == NULL) {
+		printf("Error allocating %ld bytes.\n", len);
+		fclose(f);
+		return 1;
+	}
+
+	nread = fread(data, 1, len, f);
+	if (nread != (size_t)len) {
+		printf("Error reading '%s'\n", argv[1]);
+		fclose(f);
+		return 1;
+	}
+
+	fclose(f);
+
+	size = (size_t) len;
+
+	rc = gzip_expand(data, size, &ddata, &dsize);
+	if (rc != EOK) {
+		printf("Error decompressing data.\n");
+		return 1;
+	}
+
+	wf = fopen(argv[2], "wb");
+	if (wf == NULL) {
+		printf("Error creating file '%s'\n", argv[2]);
+		return 1;
+	}
+
+	nwr = fwrite(ddata, 1, dsize, wf);
+	if (nwr != dsize) {
+		printf("Error writing '%s'\n", argv[2]);
+		fclose(wf);
+		return 1;
+	}
+
+	if (fclose(wf) != 0) {
+		printf("Error writing '%s'\n", argv[2]);
+		return 1;
+	}
+
+	return 0;
+}
+
+/** @}
+ */
Index: uspace/app/inet/inet.c
===================================================================
--- uspace/app/inet/inet.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/app/inet/inet.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -38,4 +38,5 @@
 #include <inet/addr.h>
 #include <inet/inetcfg.h>
+#include <io/table.h>
 #include <loc.h>
 #include <stdio.h>
@@ -48,9 +49,13 @@
 static void print_syntax(void)
 {
-	printf("syntax:\n");
-	printf("\t" NAME " create <addr>/<width> <link-name> <addr-name>\n");
-	printf("\t" NAME " delete <link-name> <addr-name>\n");
-	printf("\t" NAME " add-sr <dest-addr>/<width> <router-addr> <route-name>\n");
-	printf("\t" NAME " del-sr <route-name>\n");
+	printf("%s: Internet configuration utility.\n", NAME);
+	printf("Syntax:\n");
+	printf("  %s list-addr\n", NAME);
+	printf("  %s create-addr <addr>/<width> <link-name> <addr-name>\n", NAME);
+	printf("  %s delete-addr <link-name> <addr-name>\n", NAME);
+	printf("  %s list-sr\n", NAME);
+	printf("  %s create-sr <dest-addr>/<width> <router-addr> <route-name>\n", NAME);
+	printf("  %s delete-sr <route-name>\n", NAME);
+	printf("  %s list-link\n", NAME);
 }
 
@@ -242,9 +247,13 @@
 	inet_addr_info_t ainfo;
 	inet_link_info_t linfo;
+	table_t *table = NULL;
 
 	size_t count;
 	size_t i;
 	int rc;
-	char *astr;
+	char *astr = NULL;
+
+	ainfo.name = NULL;
+	linfo.name = NULL;
 
 	rc = inetcfg_get_addr_list(&addr_list, &count);
@@ -254,10 +263,13 @@
 	}
 
-	printf("Configured addresses:\n");
-	if (count > 0)
-		printf("    [Addr/Width] [Link-Name] [Addr-Name] [Def-MTU]\n");
-	ainfo.name = NULL;
-	linfo.name = NULL;
-	astr = NULL;
+	rc = table_create(&table);
+	if (rc != EOK) {
+		printf("Memory allocation failed.\n");
+		goto out;
+	}
+
+	table_header_row(table);
+	table_printf(table, "Addr/Width\t" "Link-Name\t" "Addr-Name\t"
+	    "Def-MTU\n");
 
 	for (i = 0; i < count; i++) {
@@ -285,6 +297,6 @@
 		}
 
-		printf("    %s %s %s %zu\n", astr, linfo.name,
-		    ainfo.name, linfo.def_mtu);
+		table_printf(table, "%s\t" "%s\t" "%s\t" "%zu\n", astr,
+		    linfo.name, ainfo.name, linfo.def_mtu);
 
 		free(ainfo.name);
@@ -297,7 +309,15 @@
 	}
 
-	if (count == 0)
-		printf("    None\n");
+	if (count != 0) {
+		rc = table_print_out(table, stdout);
+		if (rc != EOK) {
+			printf("Error printing table.\n");
+			goto out;
+		}
+	}
+
+	rc = EOK;
 out:
+	table_destroy(table);
 	if (ainfo.name != NULL)
 		free(ainfo.name);
@@ -309,11 +329,12 @@
 	free(addr_list);
 
-	return EOK;
+	return rc;
 }
 
 static int link_list(void)
 {
-	sysarg_t *link_list;
+	sysarg_t *link_list = NULL;
 	inet_link_info_t linfo;
+	table_t *table = NULL;
 
 	size_t count;
@@ -327,7 +348,12 @@
 	}
 
-	printf("IP links:\n");
-	if (count > 0)
-		printf("    [Link-layer Address] [Link-Name] [Def-MTU]\n");
+	rc = table_create(&table);
+	if (rc != EOK) {
+		printf("Memory allocation failed.\n");
+		goto out;
+	}
+
+	table_header_row(table);
+	table_printf(table, "Link-layer Address\t" "Link-Name\t" "Def-MTU\n");
 
 	for (i = 0; i < count; i++) {
@@ -339,5 +365,6 @@
 		}
 
-		printf("    %02x:%02x:%02x:%02x:%02x:%02x %s %zu\n",
+		table_printf(table, "%02x:%02x:%02x:%02x:%02x:%02x\t"
+		    "%s\t" "%zu\n",
 		    linfo.mac_addr[0], linfo.mac_addr[1],
 		    linfo.mac_addr[2], linfo.mac_addr[3],
@@ -350,22 +377,33 @@
 	}
 
-	if (count == 0)
-		printf("    None\n");
-
+	if (count != 0) {
+		rc = table_print_out(table, stdout);
+		if (rc != EOK) {
+			printf("Error printing table.\n");
+			goto out;
+		}
+	}
+
+	rc = EOK;
+out:
+	table_destroy(table);
 	free(link_list);
 
-	return EOK;
+	return rc;
 }
 
 static int sroute_list(void)
 {
-	sysarg_t *sroute_list;
+	sysarg_t *sroute_list = NULL;
 	inet_sroute_info_t srinfo;
+	table_t *table = NULL;
 
 	size_t count;
 	size_t i;
 	int rc;
-	char *dest_str;
-	char *router_str;
+	char *dest_str = NULL;
+	char *router_str = NULL;
+
+	srinfo.name = NULL;
 
 	rc = inetcfg_get_sroute_list(&sroute_list, &count);
@@ -375,11 +413,12 @@
 	}
 
-	printf("Static routes:\n");
-	if (count > 0)
-		printf("    [Dest/Width] [Router-Addr] [Route-Name]\n");
-
-	srinfo.name = NULL;
-	dest_str = NULL;
-	router_str = NULL;
+	rc = table_create(&table);
+	if (rc != EOK) {
+		printf("Memory allocation failed.\n");
+		goto out;
+	}
+
+	table_header_row(table);
+	table_printf(table, "Dest/Width\t" "Router-Addr\t" "Route-Name\n");
 
 	for (i = 0; i < count; i++) {
@@ -406,5 +445,6 @@
 		}
 
-		printf("    %s %s %s\n", dest_str, router_str, srinfo.name);
+		table_printf(table, "%s\t" "%s\t" "%s\n", dest_str, router_str,
+		    srinfo.name);
 
 		free(srinfo.name);
@@ -417,7 +457,15 @@
 	}
 
-	if (count == 0)
-		printf("    None\n");
+	if (count != 0) {
+		rc = table_print_out(table, stdout);
+		if (rc != EOK) {
+			printf("Error printing table.\n");
+			goto out;
+		}
+	}
+
+	rc = EOK;
 out:
+	table_destroy(table);
 	if (srinfo.name != NULL)
 		free(srinfo.name);
@@ -429,5 +477,5 @@
 	free(sroute_list);
 
-	return EOK;
+	return rc;
 }
 
@@ -443,31 +491,35 @@
 	}
 
-	if (argc < 2) {
+	if (argc < 2 || str_cmp(argv[1], "-h") == 0) {
+		print_syntax();
+		return 0;
+	}
+
+	if (str_cmp(argv[1], "list-addr") == 0) {
 		rc = addr_list();
 		if (rc != EOK)
 			return 1;
+	} else if (str_cmp(argv[1], "create-addr") == 0) {
+		rc = addr_create_static(argc - 2, argv + 2);
+		if (rc != EOK)
+			return 1;
+	} else if (str_cmp(argv[1], "delete-addr") == 0) {
+		rc = addr_delete(argc - 2, argv + 2);
+		if (rc != EOK)
+			return 1;
+	} else if (str_cmp(argv[1], "list-sr") == 0) {
 		rc = sroute_list();
 		if (rc != EOK)
 			return 1;
+	} else if (str_cmp(argv[1], "create-sr") == 0) {
+		rc = sroute_create(argc - 2, argv + 2);
+		if (rc != EOK)
+			return 1;
+	} else if (str_cmp(argv[1], "delete-sr") == 0) {
+		rc = sroute_delete(argc - 2, argv + 2);
+		if (rc != EOK)
+			return 1;
+	} else if (str_cmp(argv[1], "list-link") == 0) {
 		rc = link_list();
-		if (rc != EOK)
-			return 1;
-		return 0;
-	}
-
-	if (str_cmp(argv[1], "create") == 0) {
-		rc = addr_create_static(argc - 2, argv + 2);
-		if (rc != EOK)
-			return 1;
-	} else if (str_cmp(argv[1], "delete") == 0) {
-		rc = addr_delete(argc - 2, argv + 2);
-		if (rc != EOK)
-			return 1;
-	} else if (str_cmp(argv[1], "add-sr") == 0) {
-		rc = sroute_create(argc - 2, argv + 2);
-		if (rc != EOK)
-			return 1;
-	} else if (str_cmp(argv[1], "del-sr") == 0) {
-		rc = sroute_delete(argc - 2, argv + 2);
 		if (rc != EOK)
 			return 1;
Index: uspace/app/pkg/Makefile
===================================================================
--- uspace/app/pkg/Makefile	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/app/pkg/Makefile	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,35 @@
+#
+# Copyright (c) 2017 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 = pkg
+
+SOURCES = \
+	pkg.c
+
+include $(USPACE_PREFIX)/Makefile.common
Index: uspace/app/pkg/pkg.c
===================================================================
--- uspace/app/pkg/pkg.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/app/pkg/pkg.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2017 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 pkg
+ * @{
+ */
+/** @file Package installer
+ *
+ * Utility to simplify installation of coastline packages
+ */
+
+#include <errno.h>
+#include <macros.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <str_error.h>
+#include <task.h>
+
+#define NAME "pkg"
+
+static void print_syntax(void)
+{
+	fprintf(stderr, "syntax: " NAME " install <package-name>\n");
+}
+
+static int cmd_runl(const char *path, ...)
+{
+	va_list ap;
+	const char *arg;
+	int cnt = 0;
+
+	va_start(ap, path);
+	do {
+		arg = va_arg(ap, const char *);
+		cnt++;
+	} while (arg != NULL);
+	va_end(ap);
+
+	va_start(ap, path);
+	task_id_t id;
+	task_wait_t wait;
+	int rc = task_spawn(&id, &wait, path, cnt, ap);
+	va_end(ap);
+
+	if (rc != EOK) {
+		printf("Error spawning %s (%s)\n", path, str_error(rc));
+		return rc;
+	}
+
+	if (!id) {
+		printf("Error spawning %s (invalid task ID)\n", path);
+		return EINVAL;
+	}
+
+	task_exit_t texit;
+	int retval;
+	rc = task_wait(&wait, &texit, &retval);
+	if (rc != EOK) {
+		printf("Error waiting for %s (%s)\n", path, str_error(rc));
+		return rc;
+	}
+
+	if (texit != TASK_EXIT_NORMAL) {
+		printf("Command %s unexpectedly terminated\n", path);
+		return EINVAL;
+	}
+
+	if (retval != 0) {
+		printf("Command %s returned non-zero exit code %d)\n",
+		    path, retval);
+	}
+
+	return retval;
+}
+
+
+static int pkg_install(int argc, char *argv[])
+{
+	char *pkg_name;
+	char *src_uri;
+	char *fname;
+	char *fnunpack;
+	int rc;
+
+	if (argc != 3) {
+		print_syntax();
+		return EINVAL;
+	}
+
+	pkg_name = argv[2];
+
+	rc = asprintf(&src_uri, "http://ci.helenos.org/latest/" STRING(UARCH)
+	    "/%s-for-helenos-" STRING(UARCH) ".tar.gz",
+	    pkg_name);
+	if (rc < 0) {
+		printf("Out of memory.\n");
+		return ENOMEM;
+	}
+
+	rc = asprintf(&fname, "/tmp/%s-for-helenos-" STRING(UARCH)
+	    ".tar.gz", pkg_name);
+	if (rc < 0) {
+		printf("Out of memory.\n");
+		return ENOMEM;
+	}
+
+	rc = asprintf(&fnunpack, "/tmp/%s-for-helenos-" STRING(UARCH) ".tar",
+	    pkg_name);
+	if (rc < 0) {
+		printf("Out of memory.\n");
+		return ENOMEM;
+	}
+	/*XXX error cleanup */
+
+	printf("Downloading '%s'.\n", src_uri);
+
+	rc = cmd_runl("/app/download", "/app/download", "-o", fname,
+	    src_uri, NULL);
+	if (rc != EOK) {
+		printf("Error downloading package archive.\n");
+		return rc;
+	}
+
+	printf("Extracting package\n");
+
+	rc = cmd_runl("/app/gunzip", "/app/gunzip", fname, fnunpack, NULL);
+	if (rc != EOK) {
+		printf("Error uncompressing package archive.\n");
+		return rc;
+	}
+
+	if (remove(fname) != 0) {
+		printf("Error deleting package archive.\n");
+		return rc;
+	}
+
+
+	rc = cmd_runl("/app/untar", "/app/untar", fnunpack, NULL);
+	if (rc != EOK) {
+		printf("Error extracting package archive.\n");
+		return rc;
+	}
+
+	if (remove(fnunpack) != 0) {
+		printf("Error deleting package archive.\n");
+		return rc;
+	}
+
+	printf("Package '%s' installed.\n", pkg_name);
+
+	return EOK;
+}
+
+int main(int argc, char *argv[])
+{
+	char *cmd;
+	int rc;
+
+	if (argc < 2) {
+		fprintf(stderr, "Arguments missing.\n");
+		print_syntax();
+		return 1;
+	}
+
+	cmd = argv[1];
+
+	if (str_cmp(cmd, "install") == 0) {
+		rc = pkg_install(argc, argv);
+	} else {
+		fprintf(stderr, "Unknown command.\n");
+		print_syntax();
+		return 1;
+	}
+
+	if (rc != EOK)
+		return 1;
+
+	return 0;
+}
+
+/** @}
+ */
Index: uspace/app/sysinst/sysinst.c
===================================================================
--- uspace/app/sysinst/sysinst.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/app/sysinst/sysinst.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -38,4 +38,5 @@
 #include <block.h>
 #include <byteorder.h>
+#include <cap.h>
 #include <errno.h>
 #include <fdisk.h>
@@ -89,5 +90,5 @@
 	fdisk_part_t *part;
 	fdisk_part_spec_t pspec;
-	fdisk_cap_t cap;
+	cap_spec_t cap;
 	service_id_t sid;
 	int rc;
Index: uspace/app/tetris/scores.c
===================================================================
--- uspace/app/tetris/scores.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/app/tetris/scores.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -65,5 +65,4 @@
 #include <vfs/vfs.h>
 #include <stdlib.h>
-#include <err.h>
 #include <time.h>
 #include "screen.h"
Index: uspace/app/tetris/screen.c
===================================================================
--- uspace/app/tetris/screen.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/app/tetris/screen.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -54,5 +54,4 @@
  */
 
-#include <err.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -198,5 +197,6 @@
 		scr_end();
 	
-	errx(1, "aborting: %s", why);
+	fprintf(stderr, "aborting: %s", why);
+	abort();
 }
 
Index: uspace/app/tetris/tetris.c
===================================================================
--- uspace/app/tetris/tetris.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/app/tetris/tetris.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -56,5 +56,4 @@
 
 #include <sys/time.h>
-#include <err.h>
 #include <errno.h>
 #include <stdio.h>
@@ -289,6 +288,8 @@
 	for (i = 0; i <= 5; i++) {
 		for (j = i + 1; j <= 5; j++) {
-			if (keys[i] == keys[j])
-				errx(1, "%s", "duplicate command keys specified.");
+			if (keys[i] == keys[j]) {
+				fprintf(stderr, "duplicate command keys specified.");
+				abort();
+			}
 		}
 		
Index: uspace/app/websrv/websrv.c
===================================================================
--- uspace/app/websrv/websrv.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/app/websrv/websrv.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -75,12 +75,14 @@
 static uint16_t port = DEFAULT_PORT;
 
-static char rbuf[BUFFER_SIZE];
-static size_t rbuf_out;
-static size_t rbuf_in;
-
-static char lbuf[BUFFER_SIZE + 1];
-static size_t lbuf_used;
-
-static char fbuf[BUFFER_SIZE];
+typedef struct {
+	tcp_conn_t *conn;
+
+	char rbuf[BUFFER_SIZE];
+	size_t rbuf_out;
+	size_t rbuf_in;
+
+	char lbuf[BUFFER_SIZE + 1];
+	size_t lbuf_used;
+} recv_t;
 
 static bool verbose = false;
@@ -131,15 +133,38 @@
     "</html>\r\n";
 
+
+static int recv_create(tcp_conn_t *conn, recv_t **rrecv)
+{
+	recv_t *recv;
+	
+	recv = calloc(1, sizeof(recv_t));
+	if (recv == NULL)
+		return ENOMEM;
+	
+	recv->conn = conn;
+	recv->rbuf_out = 0;
+	recv->rbuf_in = 0;
+	recv->lbuf_used = 0;
+	
+	*rrecv = recv;
+	return EOK;
+}
+
+static void recv_destroy(recv_t *recv)
+{
+	free(recv);
+}
+
 /** Receive one character (with buffering) */
-static int recv_char(tcp_conn_t *conn, char *c)
+static int recv_char(recv_t *recv, char *c)
 {
 	size_t nrecv;
 	int rc;
 	
-	if (rbuf_out == rbuf_in) {
-		rbuf_out = 0;
-		rbuf_in = 0;
-		
-		rc = tcp_conn_recv_wait(conn, rbuf, BUFFER_SIZE, &nrecv);
+	if (recv->rbuf_out == recv->rbuf_in) {
+		recv->rbuf_out = 0;
+		recv->rbuf_in = 0;
+		
+		rc = tcp_conn_recv_wait(recv->conn, recv->rbuf, BUFFER_SIZE, &nrecv);
 		if (rc != EOK) {
 			fprintf(stderr, "tcp_conn_recv() failed (%d)\n", rc);
@@ -147,20 +172,20 @@
 		}
 		
-		rbuf_in = nrecv;
-	}
-	
-	*c = rbuf[rbuf_out++];
+		recv->rbuf_in = nrecv;
+	}
+	
+	*c = recv->rbuf[recv->rbuf_out++];
 	return EOK;
 }
 
 /** Receive one line with length limit */
-static int recv_line(tcp_conn_t *conn)
-{
-	char *bp = lbuf;
+static int recv_line(recv_t *recv, char **rbuf)
+{
+	char *bp = recv->lbuf;
 	char c = '\0';
 	
-	while (bp < lbuf + BUFFER_SIZE) {
+	while (bp < recv->lbuf + BUFFER_SIZE) {
 		char prev = c;
-		int rc = recv_char(conn, &c);
+		int rc = recv_char(recv, &c);
 		
 		if (rc != EOK)
@@ -172,10 +197,11 @@
 	}
 	
-	lbuf_used = bp - lbuf;
+	recv->lbuf_used = bp - recv->lbuf;
 	*bp = '\0';
 	
-	if (bp == lbuf + BUFFER_SIZE)
+	if (bp == recv->lbuf + BUFFER_SIZE)
 		return ELIMIT;
 	
+	*rbuf = recv->lbuf;
 	return EOK;
 }
@@ -218,24 +244,36 @@
 static int uri_get(const char *uri, tcp_conn_t *conn)
 {
+	char *fbuf = NULL;
+	char *fname = NULL;
+	int rc;
+	int fd = -1;
+	
+	fbuf = calloc(BUFFER_SIZE, 1);
+	if (fbuf == NULL) {
+		rc = ENOMEM;
+		goto out;
+	}
+	
 	if (str_cmp(uri, "/") == 0)
 		uri = "/index.html";
 	
-	char *fname;
-	int rc = asprintf(&fname, "%s%s", WEB_ROOT, uri);
-	if (rc < 0)
-		return ENOMEM;
-	
-	int fd = vfs_lookup_open(fname, WALK_REGULAR, MODE_READ);
+	rc = asprintf(&fname, "%s%s", WEB_ROOT, uri);
+	if (rc < 0) {
+		rc = ENOMEM;
+		goto out;
+	}
+	
+	fd = vfs_lookup_open(fname, WALK_REGULAR, MODE_READ);
 	if (fd < 0) {
 		rc = send_response(conn, msg_not_found);
-		free(fname);
-		return rc;
+		goto out;
 	}
 	
 	free(fname);
+	fname = NULL;
 	
 	rc = send_response(conn, msg_ok);
 	if (rc != EOK)
-		return rc;
+		goto out;
 	
 	aoff64_t pos = 0;
@@ -246,6 +284,6 @@
 		
 		if (nr < 0) {
-			vfs_put(fd);
-			return EIO;
+			rc = EIO;
+			goto out;
 		}
 		
@@ -253,17 +291,22 @@
 		if (rc != EOK) {
 			fprintf(stderr, "tcp_conn_send() failed\n");
-			vfs_put(fd);
-			return rc;
+			goto out;
 		}
 	}
 	
-	vfs_put(fd);
-	
-	return EOK;
-}
-
-static int req_process(tcp_conn_t *conn)
-{
-	int rc = recv_line(conn);
+	rc = EOK;
+out:
+	if (fd >= 0)
+		vfs_put(fd);
+	free(fname);
+	free(fbuf);
+	return rc;
+}
+
+static int req_process(tcp_conn_t *conn, recv_t *recv)
+{
+	char *reqline = NULL;
+
+	int rc = recv_line(recv, &reqline);
 	if (rc != EOK) {
 		fprintf(stderr, "recv_line() failed\n");
@@ -272,15 +315,15 @@
 	
 	if (verbose)
-		fprintf(stderr, "Request: %s", lbuf);
-	
-	if (str_lcmp(lbuf, "GET ", 4) != 0) {
+		fprintf(stderr, "Request: %s", reqline);
+	
+	if (str_lcmp(reqline, "GET ", 4) != 0) {
 		rc = send_response(conn, msg_not_implemented);
 		return rc;
 	}
 	
-	char *uri = lbuf + 4;
+	char *uri = reqline + 4;
 	char *end_uri = str_chr(uri, ' ');
 	if (end_uri == NULL) {
-		end_uri = lbuf + lbuf_used - 2;
+		end_uri = reqline + str_size(reqline) - 2;
 		assert(*end_uri == '\r');
 	}
@@ -300,5 +343,5 @@
 static void usage(void)
 {
-	printf("Skeletal server\n"
+	printf("Simple web server\n"
 	    "\n"
 	    "Usage: " NAME " [options]\n"
@@ -363,23 +406,36 @@
 {
 	int rc;
+	recv_t *recv = NULL;
 	
 	if (verbose)
 		fprintf(stderr, "New connection, waiting for request\n");
 	
-	rbuf_out = 0;
-	rbuf_in = 0;
-	
-	rc = req_process(conn);
+	rc = recv_create(conn, &recv);
+	if (rc != EOK) {
+		fprintf(stderr, "Out of memory.\n");
+		goto error;
+	}
+	
+	rc = req_process(conn, recv);
 	if (rc != EOK) {
 		fprintf(stderr, "Error processing request (%s)\n",
 		    str_error(rc));
-		return;
-	}
-
+		goto error;
+	}
+	
 	rc = tcp_conn_send_fin(conn);
 	if (rc != EOK) {
 		fprintf(stderr, "Error sending FIN.\n");
-		return;
-	}
+		goto error;
+	}
+
+	recv_destroy(recv);
+	return;
+error:
+	rc = tcp_conn_reset(conn);
+	if (rc != EOK)
+		fprintf(stderr, "Error resetting connection.\n");
+	
+	recv_destroy(recv);
 }
 
Index: uspace/lib/c/Makefile
===================================================================
--- uspace/lib/c/Makefile	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/lib/c/Makefile	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -64,4 +64,5 @@
 	generic/bd_srv.c \
 	generic/perm.c \
+	generic/cap.c \
 	generic/clipboard.c \
 	generic/config.c \
@@ -125,4 +126,5 @@
 	generic/io/con_srv.c \
 	generic/io/console.c \
+	generic/io/table.c \
 	generic/io/visualizer.c \
 	generic/io/window.c \
@@ -180,5 +182,7 @@
 
 TEST_SOURCES = \
+	test/fibril/timer.c \
 	test/main.c \
+	test/io/table.c \
 	test/odict.c \
 	test/qsort.c \
Index: uspace/lib/c/generic/adt/hash_table.c
===================================================================
--- uspace/lib/c/generic/adt/hash_table.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/lib/c/generic/adt/hash_table.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -190,5 +190,4 @@
  *
  * @param h    Hash table.
- * @param key  Array of all keys necessary to compute hash index.
  * @param item Item to be inserted into the hash table.
  */
@@ -210,5 +209,4 @@
  *
  * @param h    Hash table.
- * @param key  Array of all keys necessary to compute hash index.
  * @param item Item to be inserted into the hash table.
  * 
Index: uspace/lib/c/generic/async.c
===================================================================
--- uspace/lib/c/generic/async.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/lib/c/generic/async.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -106,4 +106,5 @@
 #include <fibril.h>
 #include <adt/hash_table.h>
+#include <adt/hash.h>
 #include <adt/list.h>
 #include <assert.h>
@@ -587,7 +588,17 @@
 };
 
-/** Compute hash into the connection hash table based on the source phone hash.
- *
- * @param key Pointer to source phone hash.
+typedef struct {
+	task_id_t task_id;
+	sysarg_t phone_hash;
+} conn_key_t;
+
+/** Compute hash into the connection hash table
+ *
+ * The hash is based on the source task ID and the source phone hash. The task
+ * ID is included in the hash because a phone hash alone might not be unique
+ * while we still track connections for killed tasks due to kernel's recycling
+ * of phone structures.
+ *
+ * @param key Pointer to the connection key structure.
  *
  * @return Index into the connection hash table.
@@ -596,6 +607,11 @@
 static size_t conn_key_hash(void *key)
 {
-	sysarg_t in_phone_hash = *(sysarg_t *) key;
-	return in_phone_hash;
+	conn_key_t *ck = (conn_key_t *) key;
+
+	size_t hash = 0;
+	hash = hash_combine(hash, LOWER32(ck->task_id));
+	hash = hash_combine(hash, UPPER32(ck->task_id));
+	hash = hash_combine(hash, ck->phone_hash);
+	return hash;
 }
 
@@ -603,12 +619,16 @@
 {
 	connection_t *conn = hash_table_get_inst(item, connection_t, link);
-	return conn_key_hash(&conn->in_phone_hash);
+	return conn_key_hash(&(conn_key_t){
+		.task_id = conn->in_task_id,
+		.phone_hash = conn->in_phone_hash
+	});
 }
 
 static bool conn_key_equal(void *key, const ht_link_t *item)
 {
-	sysarg_t in_phone_hash = *(sysarg_t *) key;
+	conn_key_t *ck = (conn_key_t *) key;
 	connection_t *conn = hash_table_get_inst(item, connection_t, link);
-	return (in_phone_hash == conn->in_phone_hash);
+	return ((ck->task_id == conn->in_task_id) &&
+	    (ck->phone_hash == conn->in_phone_hash));
 }
 
@@ -716,5 +736,8 @@
 	 */
 	futex_down(&async_futex);
-	hash_table_remove(&conn_hash_table, &fibril_connection->in_phone_hash);
+	hash_table_remove(&conn_hash_table, &(conn_key_t){
+		.task_id = fibril_connection->in_task_id,
+		.phone_hash = fibril_connection->in_phone_hash
+	});
 	futex_up(&async_futex);
 	
@@ -951,5 +974,8 @@
 	futex_down(&async_futex);
 	
-	ht_link_t *link = hash_table_find(&conn_hash_table, &call->in_phone_hash);
+	ht_link_t *link = hash_table_find(&conn_hash_table, &(conn_key_t){
+		.task_id = call->in_task_id,
+		.phone_hash = call->in_phone_hash
+	});
 	if (!link) {
 		futex_up(&async_futex);
Index: uspace/lib/c/generic/cap.c
===================================================================
--- uspace/lib/c/generic/cap.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/lib/c/generic/cap.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 2015 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 libc
+ * @{
+ */
+/**
+ * @file Storage capacity specification.
+ */
+
+#include <cap.h>
+#include <errno.h>
+#include <imath.h>
+#include <stdio.h>
+#include <str.h>
+
+/** Simplified capacity parameters */
+enum {
+	/** Simplified capacity maximum integer digits */
+	scap_max_idig = 3,
+	/** Simplified capacity maximum significant digits */
+	scap_max_sdig = 4
+};
+
+static const char *cu_str[] = {
+	[cu_byte] = "B",
+	[cu_kbyte] = "kB",
+	[cu_mbyte] = "MB",
+	[cu_gbyte] = "GB",
+	[cu_tbyte] = "TB",
+	[cu_pbyte] = "PB",
+	[cu_ebyte] = "EB",
+	[cu_zbyte] = "ZB",
+	[cu_ybyte] = "YB"
+};
+
+void cap_from_blocks(uint64_t nblocks, size_t block_size, cap_spec_t *cap)
+{
+	uint64_t tsize;
+
+	tsize = nblocks * block_size;
+	cap->m = tsize;
+	cap->dp = 0;
+	cap->cunit = cu_byte;
+}
+
+/** Convert capacity to blocks.
+ *
+ * If the value of bytes is not integer, it is properly rounded. If the number
+ * of bytes is not divisible by the number of blocks, it is rounded
+ * up to an integer number of blocks.
+ *
+ * A capacity value entails precision, i.e. it corresponds to a range
+ * of values. @a cvsel selects the value to return. @c cv_nom gives
+ * the nominal (middle) value, @c cv_min gives the minimum value
+ * and @c cv_max gives the maximum value.
+ */
+int cap_to_blocks(cap_spec_t *cap, cap_vsel_t cvsel, size_t block_size,
+    uint64_t *rblocks)
+{
+	int exp;
+	uint64_t bytes;
+	uint64_t f;
+	uint64_t adj;
+	uint64_t blocks;
+	uint64_t rem;
+	int rc;
+
+	exp = cap->cunit * 3 - cap->dp;
+	if (exp < 0) {
+		rc = ipow10_u64(-exp, &f);
+		if (rc != EOK)
+			return ERANGE;
+		bytes = (cap->m + (f / 2)) / f;
+		if (bytes * f - (f / 2) != cap->m)
+			return ERANGE;
+	} else {
+		rc = ipow10_u64(exp, &f);
+		if (rc != EOK)
+			return ERANGE;
+
+		adj = 0;
+		switch (cvsel) {
+		case cv_nom:
+			adj = 0;
+			break;
+		case cv_min:
+			adj = -(f / 2);
+			break;
+		case cv_max:
+			adj = f / 2 - 1;
+			break;
+		}
+
+		bytes = cap->m * f + adj;
+		if ((bytes - adj) / f != cap->m)
+			return ERANGE;
+	}
+
+	rem = bytes % block_size;
+	if ((bytes + rem) < bytes)
+		return ERANGE;
+
+	blocks = (bytes + rem) / block_size;
+
+	*rblocks = blocks;
+	return EOK;
+}
+
+/** Simplify and round capacity to a human-friendly form.
+ *
+ * Change unit and round the number so that we have at most three integer
+ * digits and at most two fractional digits, e.g abc.xy <unit>.
+ */
+void cap_simplify(cap_spec_t *cap)
+{
+	uint64_t div;
+	uint64_t maxv;
+	unsigned sdig;
+	unsigned rdig;
+	int rc;
+
+	/* Change units so that we have at most @c scap_max_idig integer digits */
+	rc = ipow10_u64(scap_max_idig, &maxv);
+	assert(rc == EOK);
+
+	rc = ipow10_u64(cap->dp, &div);
+	assert(rc == EOK);
+
+	while (cap->m / div >= maxv) {
+		++cap->cunit;
+		cap->dp += 3;
+		div = div * 1000;
+	}
+
+	/* Round the number so that we have at most @c scap_max_sdig significant digits */
+	sdig = 1 + ilog10_u64(cap->m); /* number of significant digits */
+	if (sdig > scap_max_sdig) {
+		/* Number of digits to remove */
+		rdig = sdig - scap_max_sdig;
+		if (rdig > cap->dp)
+			rdig = cap->dp;
+
+		rc = ipow10_u64(rdig, &div);
+		assert(rc == EOK);
+
+		cap->m = (cap->m + (div / 2)) / div;
+		cap->dp -= rdig;
+	}
+}
+
+int cap_format(cap_spec_t *cap, char **rstr)
+{
+	int rc;
+	const char *sunit;
+	uint64_t ipart;
+	uint64_t fpart;
+	uint64_t div;
+
+	sunit = NULL;
+
+	if (cap->cunit < 0 || cap->cunit >= CU_LIMIT)
+		assert(false);
+
+	rc = ipow10_u64(cap->dp, &div);
+	if (rc != EOK)
+		return rc;
+
+	ipart = cap->m / div;
+	fpart = cap->m % div;
+
+	sunit = cu_str[cap->cunit];
+	if (cap->dp > 0) {
+		rc = asprintf(rstr, "%" PRIu64 ".%0*" PRIu64 " %s", ipart,
+		    (int)cap->dp, fpart, sunit);
+	} else {
+		rc = asprintf(rstr, "%" PRIu64 " %s", ipart, sunit);
+	}
+	if (rc < 0)
+		return ENOMEM;
+
+	return EOK;
+}
+
+static int cap_digit_val(char c, int *val)
+{
+	switch (c) {
+	case '0': *val = 0; break;
+	case '1': *val = 1; break;
+	case '2': *val = 2; break;
+	case '3': *val = 3; break;
+	case '4': *val = 4; break;
+	case '5': *val = 5; break;
+	case '6': *val = 6; break;
+	case '7': *val = 7; break;
+	case '8': *val = 8; break;
+	case '9': *val = 9; break;
+	default:
+		return EINVAL;
+	}
+
+	return EOK;
+}
+
+int cap_parse(const char *str, cap_spec_t *cap)
+{
+	const char *eptr;
+	const char *p;
+	int d;
+	int dp;
+	unsigned long m;
+	int i;
+
+	m = 0;
+
+	eptr = str;
+	while (cap_digit_val(*eptr, &d) == EOK) {
+		m = m * 10 + d;
+		++eptr;
+	}
+
+	if (*eptr == '.') {
+		++eptr;
+		dp = 0;
+		while (cap_digit_val(*eptr, &d) == EOK) {
+			m = m * 10 + d;
+			++dp;
+			++eptr;
+		}
+	} else {
+		dp = 0;
+	}
+
+	while (*eptr == ' ')
+		++eptr;
+
+	if (*eptr == '\0') {
+		cap->cunit = cu_byte;
+	} else {
+		for (i = 0; i < CU_LIMIT; i++) {
+			if (str_lcasecmp(eptr, cu_str[i],
+			    str_length(cu_str[i])) == 0) {
+				p = eptr + str_size(cu_str[i]);
+				while (*p == ' ')
+					++p;
+				if (*p == '\0')
+					goto found;
+			}
+		}
+
+		return EINVAL;
+found:
+		cap->cunit = i;
+	}
+
+	cap->m = m;
+	cap->dp = dp;
+	return EOK;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/clipboard.c
===================================================================
--- uspace/lib/c/generic/clipboard.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/lib/c/generic/clipboard.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -38,13 +38,13 @@
  */
 
+#include <async.h>
 #include <clipboard.h>
-#include <ns.h>
+#include <errno.h>
+#include <fibril_synch.h>
+#include <ipc/clipboard.h>
 #include <ipc/services.h>
-#include <ipc/clipboard.h>
-#include <fibril_synch.h>
-#include <async.h>
+#include <loc.h>
+#include <malloc.h>
 #include <str.h>
-#include <errno.h>
-#include <malloc.h>
 
 static FIBRIL_MUTEX_INITIALIZE(clip_mutex);
@@ -58,9 +58,18 @@
 static async_exch_t *clip_exchange_begin(void)
 {
+	service_id_t sid;
+	int rc;
+	
 	fibril_mutex_lock(&clip_mutex);
 	
-	while (clip_sess == NULL)
-		clip_sess = service_connect_blocking(SERVICE_CLIPBOARD,
-		    INTERFACE_CLIPBOARD, 0);
+	while (clip_sess == NULL) {
+		rc = loc_service_get_id(SERVICE_NAME_CLIPBOARD, &sid,
+		    IPC_FLAG_BLOCKING);
+		if (rc != EOK)
+			continue;
+		
+		clip_sess = loc_service_connect(sid, INTERFACE_CLIPBOARD,
+		    IPC_FLAG_BLOCKING);
+	}
 	
 	fibril_mutex_unlock(&clip_mutex);
Index: uspace/lib/c/generic/getopt.c
===================================================================
--- uspace/lib/c/generic/getopt.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/lib/c/generic/getopt.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -34,7 +34,7 @@
 #include <assert.h>
 #include <stdarg.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
-#include <err.h>
 #include <errno.h>
 #include <getopt.h>
Index: uspace/lib/c/generic/inet/udp.c
===================================================================
--- uspace/lib/c/generic/inet/udp.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/lib/c/generic/inet/udp.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -227,4 +227,20 @@
 }
 
+/** Set UDP association sending messages with no local address
+ *
+ * @param assoc Association
+ * @param flags Flags
+ */
+int udp_assoc_set_nolocal(udp_assoc_t *assoc)
+{
+	async_exch_t *exch;
+
+	exch = async_exchange_begin(assoc->udp->sess);
+	sysarg_t rc = async_req_1_0(exch, UDP_ASSOC_SET_NOLOCAL, assoc->id);
+	async_exchange_end(exch);
+
+	return rc;
+}
+
 /** Send message via UDP association.
  *
Index: uspace/lib/c/generic/io/table.c
===================================================================
--- uspace/lib/c/generic/io/table.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/lib/c/generic/io/table.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,565 @@
+/*
+ * Copyright (c) 2017 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 libc
+ * @{
+ */
+/**
+ * @file
+ * @brief Tabulate text
+ */
+
+#include <errno.h>
+#include <io/table.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static table_column_t *table_column_first(table_t *);
+static table_column_t *table_column_next(table_column_t *);
+
+/** Add a new row at the end of the table.
+ *
+ * @param table Table
+ * @param rrow Place to store pointer to new row or @c NULL
+ *
+ * @return EOK on success, ENOMEM if out of memory
+ */
+static int table_add_row(table_t *table, table_row_t **rrow)
+{
+	table_row_t *row;
+
+	row = calloc(1, sizeof(table_row_t));
+	if (row == NULL)
+		return ENOMEM;
+
+	row->table = table;
+	list_initialize(&row->cells);
+	list_append(&row->ltable, &table->rows);
+
+	if (rrow != NULL)
+		*rrow = row;
+	return EOK;
+}
+
+/** Add a new cell at the end of the row.
+ *
+ * @param row Row
+ * @param rcell Place to store pointer to new cell or @c NULL
+ * @return EOK on success, ENOMEM if out of memory
+ */
+static int table_row_add_cell(table_row_t *row, table_cell_t **rcell)
+{
+	table_cell_t *cell;
+
+	cell = calloc(1, sizeof(table_cell_t));
+	if (cell == NULL)
+		return ENOMEM;
+
+	cell->text = NULL;
+
+	cell->row = row;
+	list_append(&cell->lrow, &row->cells);
+
+	if (rcell != NULL)
+		*rcell = cell;
+	return EOK;
+}
+
+/** Add a new column at the right side of the table.
+ *
+ * @param table Table
+ * @param rcolumn Place to store pointer to new column or @c NULL
+ *
+ * @return EOK on success, ENOMEM if out of memory
+ */
+static int table_add_column(table_t *table, table_column_t **rcolumn)
+{
+	table_column_t *column;
+
+	column = calloc(1, sizeof(table_column_t));
+	if (column == NULL)
+		return ENOMEM;
+
+	column->table = table;
+	column->width = 0;
+	list_append(&column->ltable, &table->columns);
+
+	if (rcolumn != NULL)
+		*rcolumn = column;
+	return EOK;
+}
+
+/** Start writing next table cell.
+ *
+ * @param table Table
+ * @return EOK on success, ENOMEM if out of memory
+ */
+static int table_write_next_cell(table_t *table)
+{
+	int rc;
+
+	rc = table_row_add_cell(table->wrow, &table->wcell);
+	if (rc != EOK) {
+		assert(rc == ENOMEM);
+		return rc;
+	}
+
+	if (list_count(&table->wrow->cells) == 1) {
+		/* First column */
+		table->wcolumn = table_column_first(table);
+	} else {
+		/* Next column */
+		table->wcolumn = table_column_next(table->wcolumn);
+	}
+
+	if (table->wcolumn == NULL) {
+		rc = table_add_column(table, &table->wcolumn);
+		if (rc != EOK) {
+			assert(rc == ENOMEM);
+			return rc;
+		}
+	}
+
+	return EOK;
+}
+
+/** Start writing next table row.
+ *
+ * @param table Table
+ * @return EOK on success, ENOMEM if out of memory
+ */
+static int table_write_next_row(table_t *table)
+{
+	int rc;
+
+	rc = table_add_row(table, &table->wrow);
+	if (rc != EOK) {
+		assert(rc == ENOMEM);
+		return rc;
+	}
+
+	table->wcell = NULL;
+	return EOK;
+}
+
+/** Get first table row.
+ *
+ * @param table Table
+ * @return First table row or @c NULL if none
+ */
+static table_row_t *table_row_first(table_t *table)
+{
+	link_t *link;
+
+	link = list_first(&table->rows);
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, table_row_t, ltable);
+}
+
+/** Get next table row.
+ *
+ * @param cur Current row
+ * @return Next table row or @c NULL if none
+ */
+static table_row_t *table_row_next(table_row_t *cur)
+{
+	link_t *link;
+
+	link = list_next(&cur->ltable, &cur->table->rows);
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, table_row_t, ltable);
+}
+
+/** Get first cell in table row.
+ *
+ * @param row Table row
+ * @return First cell in row or @c NULL if none
+ */
+static table_cell_t *table_row_cell_first(table_row_t *row)
+{
+	link_t *link;
+
+	link = list_first(&row->cells);
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, table_cell_t, lrow);
+}
+
+/** Get next cell in table row.
+ *
+ * @param cur Current cell
+ * @return Next cell in table row or @c NULL if none
+ */
+static table_cell_t *table_row_cell_next(table_cell_t *cur)
+{
+	link_t *link;
+
+	link = list_next(&cur->lrow, &cur->row->cells);
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, table_cell_t, lrow);
+}
+
+/** Get first table column.
+ *
+ * @param table Table
+ * @return First table column or @c NULL if none
+ */
+static table_column_t *table_column_first(table_t *table)
+{
+	link_t *link;
+
+	link = list_first(&table->columns);
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, table_column_t, ltable);
+}
+
+/** Get next table column.
+ *
+ * @param cur Current column
+ * @return Next table column or @c NULL if none
+ */
+static table_column_t *table_column_next(table_column_t *cur)
+{
+	link_t *link;
+
+	link = list_next(&cur->ltable, &cur->table->columns);
+	if (link == NULL)
+		return NULL;
+
+	return list_get_instance(link, table_column_t, ltable);
+}
+
+/** Append to cell text.
+ *
+ * @param cell Cell
+ * @param str Text to append
+ * @param len Max number of bytes to read from str
+ * @return EOK on success, ENOMEM if out of memory
+ */
+static int table_cell_extend(table_cell_t *cell, const char *str, size_t len)
+{
+	char *cstr;
+	int rc;
+
+	if (cell->text == NULL) {
+		cell->text = str_ndup(str, len);
+		if (cell->text == NULL)
+			return ENOMEM;
+	} else {
+		rc = asprintf(&cstr, "%s%.*s", cell->text, (int)len, str);
+		if (rc < 0)
+			return ENOMEM;
+
+		free(cell->text);
+		cell->text = cstr;
+	}
+
+	return EOK;
+}
+
+/** Create table.
+ *
+ * @para, rtable Place to store pointer to new table.
+ * @return EOK on success, ENOMEM if out of memory
+ */
+int table_create(table_t **rtable)
+{
+	table_t *table;
+	int rc;
+
+	table = calloc(1, sizeof(table_t));
+	if (table == NULL)
+		return ENOMEM;
+
+	table->error = EOK;
+	list_initialize(&table->rows);
+	list_initialize(&table->columns);
+
+	rc = table_add_row(table, &table->wrow);
+	if (rc != EOK)
+		goto error;
+
+	rc = table_row_add_cell(table->wrow, &table->wcell);
+	if (rc != EOK)
+		goto error;
+
+	rc = table_add_column(table, &table->wcolumn);
+	if (rc != EOK)
+		goto error;
+
+	*rtable = table;
+	return EOK;
+error:
+	table_destroy(table);
+	return rc;
+}
+
+/** Destroy table.
+ *
+ * @param table Table
+ */
+void table_destroy(table_t *table)
+{
+	table_row_t *row;
+	table_cell_t *cell;
+	table_column_t *column;
+
+	if (table == NULL)
+		return;
+
+	row = table_row_first(table);
+	while (row != NULL) {
+		cell = table_row_cell_first(row);
+		while (cell != NULL) {
+			list_remove(&cell->lrow);
+			free(cell->text);
+			free(cell);
+			cell = table_row_cell_first(row);
+		}
+
+		list_remove(&row->ltable);
+		free(row);
+
+		row = table_row_first(table);
+	}
+
+	column = table_column_first(table);
+	while (column != NULL) {
+		list_remove(&column->ltable);
+		free(column);
+
+		column = table_column_first(table);
+	}
+
+	free(table);
+}
+
+/** Print out table contents to a file stream.
+ *
+ * @param table Table
+ * @param f File where to write the output
+ *
+ * @return EOK on success, EIO on I/O error
+ */
+int table_print_out(table_t *table, FILE *f)
+{
+	table_row_t *row;
+	table_cell_t *cell;
+	table_column_t *column;
+	bool firstc;
+	bool firstr;
+	size_t spacing;
+	size_t i;
+	int rc;
+
+	if (table->error != EOK)
+		return table->error;
+
+	row = table_row_first(table);
+	firstr = true;
+	while (row != NULL) {
+		cell = table_row_cell_first(row);
+		if (cell == NULL)
+			break;
+
+		column = table_column_first(table);
+		firstc = true;
+
+		while (cell != NULL && cell->text != NULL) {
+			spacing = firstc ? table->metrics.margin_left : 1;
+			for (i = 0; i < spacing; i++) {
+				rc = fprintf(f, " ");
+				if (rc < 0)
+					return EIO;
+			}
+
+			rc = fprintf(f, "%*s", -(int)column->width, cell->text);
+			if (rc < 0)
+				return EIO;
+
+			cell = table_row_cell_next(cell);
+			column = table_column_next(column);
+			firstc = false;
+		}
+		rc = fprintf(f, "\n");
+		if (rc < 0)
+			return EIO;
+
+		if (firstr && table->header_row) {
+			/* Display header separator */
+			column = table_column_first(table);
+			firstc = true;
+			while (column != NULL) {
+				spacing = firstc ? table->metrics.margin_left : 1;
+				for (i = 0; i < spacing; i++) {
+					rc = fprintf(f, " ");
+					if (rc < 0)
+						return EIO;
+				}
+
+				for (i = 0; i < column->width; i++) {
+					rc = fprintf(f, "=");
+					if (rc < 0)
+						return EIO;
+				}
+
+				column = table_column_next(column);
+				firstc = false;
+			}
+
+			rc = fprintf(f, "\n");
+			if (rc < 0)
+				return EIO;
+		}
+
+		row = table_row_next(row);
+		firstr = false;
+	}
+	return EOK;
+}
+
+/** Start a header row.
+ *
+ * @param table Table
+ */
+void table_header_row(table_t *table)
+{
+	assert(list_count(&table->rows) == 1);
+	assert(!table->header_row);
+	table->header_row = true;
+}
+
+/** Insert text into table cell(s).
+ *
+ * Appends text to the current cell. A tab character starts a new cell.
+ * A newline character starts a new row.
+ *
+ * @param table Table
+ * @param fmt Format string
+ * @return EOK on success, ENOMEM if out of memory
+ */
+int table_printf(table_t *table, const char *fmt, ...)
+{
+	va_list args;
+	int rc;
+	char *str;
+	char *sp, *ep;
+	size_t width;
+
+	if (table->error != EOK)
+		return table->error;
+
+	va_start(args, fmt);
+	rc = vasprintf(&str, fmt, args);
+	va_end(args);
+
+	if (rc < 0) {
+		table->error = ENOMEM;
+		return table->error;
+	}
+
+	sp = str;
+	while (*sp != '\0' && table->error == EOK) {
+		ep = sp;
+		while (*ep != '\0' && *ep != '\t' && *ep != '\n')
+			++ep;
+
+		if (table->wcell == NULL) {
+			rc = table_write_next_cell(table);
+			if (rc != EOK) {
+				assert(rc == ENOMEM);
+				goto out;
+			}
+		}
+
+		rc = table_cell_extend(table->wcell, sp, ep - sp);
+		if (rc != EOK) {
+			assert(rc == ENOMEM);
+			table->error = ENOMEM;
+			goto out;
+		}
+
+		/* Update column width */
+		width = str_width(table->wcell->text);
+		if (width > table->wcolumn->width)
+			table->wcolumn->width = width;
+
+		if (*ep == '\t')
+			rc = table_write_next_cell(table);
+		else if (*ep == '\n')
+			rc = table_write_next_row(table);
+		else
+			break;
+
+		if (rc != EOK) {
+			assert(rc == ENOMEM);
+			table->error = ENOMEM;
+			goto out;
+		}
+
+		sp = ep + 1;
+	}
+
+	rc = table->error;
+out:
+	free(str);
+	return rc;
+}
+
+/** Return table error code.
+ *
+ * @param table Table
+ * @return EOK if no error indicated, non-zero error code otherwise
+ */
+int table_get_error(table_t *table)
+{
+	return table->error;
+}
+
+/** Set left table margin.
+ *
+ * @param table Table
+ * @param mleft Left margin
+ */
+void table_set_margin_left(table_t *table, size_t mleft)
+{
+	table->metrics.margin_left = mleft;
+}
+
+/** @}
+ */
Index: uspace/lib/c/generic/ns.c
===================================================================
--- uspace/lib/c/generic/ns.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/lib/c/generic/ns.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -40,25 +40,49 @@
 #include "private/ns.h"
 
+/*
+ * XXX ns does not know about session_ns, so we create an extra session for
+ * actual communicaton
+ */
+static async_sess_t *sess_ns = NULL;
+
 int service_register(service_t service)
 {
-	async_exch_t *exch = async_exchange_begin(session_ns);
+	sysarg_t retval;
+	ipc_call_t answer;
+	
+	async_sess_t *sess = ns_session_get();
+	if (sess == NULL)
+		return EIO;
+	
+	async_exch_t *exch = async_exchange_begin(sess);
+	aid_t req = async_send_1(exch, NS_REGISTER, service, &answer);
 	int rc = async_connect_to_me(exch, 0, service, 0);
+	
 	async_exchange_end(exch);
 	
+	if (rc != EOK) {
+		async_forget(req);
+		return rc;
+	}
+	
+	async_wait_for(req, &retval);
 	return rc;
 }
 
-
 async_sess_t *service_connect(service_t service, iface_t iface, sysarg_t arg3)
 {
-	async_exch_t *exch = async_exchange_begin(session_ns);
-	if (!exch)
+	async_sess_t *sess = ns_session_get();
+	if (sess == NULL)
 		return NULL;
 	
-	async_sess_t *sess =
+	async_exch_t *exch = async_exchange_begin(sess);
+	if (exch == NULL)
+		return NULL;
+	
+	async_sess_t *csess =
 	    async_connect_me_to_iface(exch, iface, service, arg3);
 	async_exchange_end(exch);
 	
-	if (!sess)
+	if (csess == NULL)
 		return NULL;
 	
@@ -68,7 +92,7 @@
 	 * first argument for non-initial connections.
 	 */
-	async_sess_args_set(sess, iface, arg3, 0);
+	async_sess_args_set(csess, iface, arg3, 0);
 	
-	return sess;
+	return csess;
 }
 
@@ -76,10 +100,14 @@
     sysarg_t arg3)
 {
-	async_exch_t *exch = async_exchange_begin(session_ns);
-	async_sess_t *sess =
+	async_sess_t *sess = ns_session_get();
+	if (sess == NULL)
+		return NULL;
+	
+	async_exch_t *exch = async_exchange_begin(sess);
+	async_sess_t *csess =
 	    async_connect_me_to_blocking_iface(exch, iface, service, arg3);
 	async_exchange_end(exch);
 	
-	if (!sess)
+	if (csess == NULL)
 		return NULL;
 	
@@ -89,7 +117,7 @@
 	 * first argument for non-initial connections.
 	 */
-	async_sess_args_set(sess, iface, arg3, 0);
+	async_sess_args_set(csess, iface, arg3, 0);
 	
-	return sess;
+	return csess;
 }
 
@@ -97,5 +125,9 @@
 int ns_ping(void)
 {
-	async_exch_t *exch = async_exchange_begin(session_ns);
+	async_sess_t *sess = ns_session_get();
+	if (sess == NULL)
+		return EIO;
+	
+	async_exch_t *exch = async_exchange_begin(sess);
 	int rc = async_req_0_0(exch, NS_PING);
 	async_exchange_end(exch);
@@ -106,5 +138,10 @@
 int ns_intro(task_id_t id)
 {
-	async_exch_t *exch = async_exchange_begin(session_ns);
+	async_exch_t *exch;
+	async_sess_t *sess = ns_session_get();
+	if (sess == NULL)
+		return EIO;
+	
+	exch = async_exchange_begin(sess);
 	int rc = async_req_2_0(exch, NS_ID_INTRO, LOWER32(id), UPPER32(id));
 	async_exchange_end(exch);
@@ -113,4 +150,19 @@
 }
 
+async_sess_t *ns_session_get(void)
+{
+	async_exch_t *exch;
+	
+	if (sess_ns == NULL) {
+		exch = async_exchange_begin(session_ns);
+		sess_ns = async_connect_me_to_iface(exch, 0, 0, 0);
+		async_exchange_end(exch);
+		if (sess_ns == NULL)
+			return NULL;
+	}
+	
+	return sess_ns;
+}
+
 /** @}
  */
Index: uspace/lib/c/generic/task.c
===================================================================
--- uspace/lib/c/generic/task.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/lib/c/generic/task.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -44,4 +44,5 @@
 #include <async.h>
 #include <errno.h>
+#include <ns.h>
 #include <malloc.h>
 #include <libc.h>
@@ -324,5 +325,9 @@
 int task_setup_wait(task_id_t id, task_wait_t *wait)
 {
-	async_exch_t *exch = async_exchange_begin(session_ns);
+	async_sess_t *sess_ns = ns_session_get();
+	if (sess_ns == NULL)
+		return EIO;
+
+	async_exch_t *exch = async_exchange_begin(sess_ns);
 	wait->aid = async_send_2(exch, NS_TASK_WAIT, LOWER32(id), UPPER32(id),
 	    &wait->result);
@@ -401,5 +406,9 @@
 int task_retval(int val)
 {
-	async_exch_t *exch = async_exchange_begin(session_ns);
+	async_sess_t *sess_ns = ns_session_get();
+	if (sess_ns == NULL)
+		return EIO;
+
+	async_exch_t *exch = async_exchange_begin(sess_ns);
 	int rc = (int) async_req_1_0(exch, NS_RETVAL, val);
 	async_exchange_end(exch);
Index: uspace/lib/c/include/cap.h
===================================================================
--- uspace/lib/c/include/cap.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/lib/c/include/cap.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2015 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 libc
+ * @{
+ */
+/**
+ * @file Storage capacity specification.
+ */
+
+#ifndef LIBC_CAP_H_
+#define LIBC_CAP_H_
+
+#include <adt/list.h>
+#include <loc.h>
+#include <stdint.h>
+#include <types/label.h>
+#include <types/vol.h>
+#include <vbd.h>
+
+/** Capacity unit */
+typedef enum {
+	cu_byte = 0,
+	cu_kbyte,
+	cu_mbyte,
+	cu_gbyte,
+	cu_tbyte,
+	cu_pbyte,
+	cu_ebyte,
+	cu_zbyte,
+	cu_ybyte
+} cap_unit_t;
+
+/** Which of values within the precision of the capacity */
+typedef enum {
+	/** The nominal (middling) value */
+	cv_nom,
+	/** The minimum value */
+	cv_min,
+	/** The maximum value */
+	cv_max
+} cap_vsel_t;
+
+#define CU_LIMIT (cu_ybyte + 1)
+
+/** Storage capacity.
+ *
+ * Storage capacity represents both value and precision.
+ * It is a decimal floating point value combined with a decimal
+ * capacity unit. There is an integer mantisa @c m which in combination
+ * with the number of decimal positions @c dp gives a decimal floating-point
+ * number. E.g. for m = 1025 and dp = 2 the number is 10.25. If the unit
+ * cunit = cu_kbyte, the capacity is 10.25 kByte, i.e. 10 250 bytes.
+ *
+ * Note that 1.000 kByte is equivalent to 1000 Byte, but 1 kByte is less
+ * precise.
+ */
+typedef struct {
+	/** Mantisa */
+	uint64_t m;
+	/** Decimal positions */
+	unsigned dp;
+	/** Capacity unit */
+	cap_unit_t cunit;
+} cap_spec_t;
+
+extern int cap_format(cap_spec_t *, char **);
+extern int cap_parse(const char *, cap_spec_t *);
+extern void cap_simplify(cap_spec_t *);
+extern void cap_from_blocks(uint64_t, size_t, cap_spec_t *);
+extern int cap_to_blocks(cap_spec_t *, cap_vsel_t, size_t, uint64_t *);
+
+#endif
+
+/** @}
+ */
Index: pace/lib/c/include/err.h
===================================================================
--- uspace/lib/c/include/err.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ 	(revision )
@@ -1,49 +1,0 @@
-/*
- * Copyright (c) 2006 Ondrej Palkovsky
- * 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 libc
- * @{
- */
-/** @file
- */
-
-#ifndef LIBC_ERR_H_
-#define LIBC_ERR_H_
-
-#include <stdio.h>
-
-#define errx(status, fmt, ...) \
-	do { \
-		printf((fmt), ##__VA_ARGS__); \
-		exit(status); \
-	} while (0)
-
-#endif
-
-/** @}
- */
Index: uspace/lib/c/include/inet/udp.h
===================================================================
--- uspace/lib/c/include/inet/udp.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/lib/c/include/inet/udp.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -95,4 +95,5 @@
 extern int udp_assoc_create(udp_t *, inet_ep2_t *, udp_cb_t *, void *,
     udp_assoc_t **);
+extern int udp_assoc_set_nolocal(udp_assoc_t *);
 extern void udp_assoc_destroy(udp_assoc_t *);
 extern int udp_assoc_send_msg(udp_assoc_t *, inet_ep_t *, void *, size_t);
Index: uspace/lib/c/include/io/table.h
===================================================================
--- uspace/lib/c/include/io/table.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/lib/c/include/io/table.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2017 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 libc
+ * @{
+ */
+/** @file
+ */
+
+#ifndef LIBC_IO_TABLE_H_
+#define LIBC_IO_TABLE_H_
+
+#include <adt/list.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+
+/** Table metrics */
+typedef struct {
+	/** Space to the left of the table */
+	size_t margin_left;
+} table_metrics_t;
+
+/** Table cell */
+typedef struct {
+	/** Containing row */
+	struct table_row *row;
+	/** Link to table_row_t.cells */
+	link_t lrow;
+	/** Cell text */
+	char *text;
+} table_cell_t;
+
+/** Table row */
+typedef struct table_row {
+	/** Containing table */
+	struct table *table;
+	/** Link to table_t.rows */
+	link_t ltable;
+	/** Cells of this row */
+	list_t cells;
+} table_row_t;
+
+/** Table column */
+typedef struct {
+	/** Containing table */
+	struct table *table;
+	/** Link to table_t.columns */
+	link_t ltable;
+	/** Column width */
+	size_t width;
+} table_column_t;
+
+/** Table */
+typedef struct table {
+	/** @c true if the first row is a header row */
+	bool header_row;
+	/** Encountered error while writing to table */
+	int error;
+	/** Table rows */
+	list_t rows; /* of table_row_t */
+	/** Table columns */
+	list_t columns; /* of table_column_t */
+	/** Current row */
+	table_row_t *wrow;
+	/** Current cell */
+	table_cell_t *wcell;
+	/** Current column */
+	table_column_t *wcolumn;
+	/** Table metrics */
+	table_metrics_t metrics;
+} table_t;
+
+extern int table_create(table_t **);
+extern void table_destroy(table_t *);
+extern int table_print_out(table_t *, FILE *);
+extern void table_header_row(table_t *);
+extern int table_printf(table_t *, const char *, ...);
+extern int table_get_error(table_t *);
+extern void table_set_margin_left(table_t *, size_t);
+
+#endif
+
+/** @}
+ */
Index: uspace/lib/c/include/ipc/ns.h
===================================================================
--- uspace/lib/c/include/ipc/ns.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/lib/c/include/ipc/ns.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -40,4 +40,5 @@
 typedef enum {
 	NS_PING = IPC_FIRST_USER_METHOD,
+	NS_REGISTER,
 	NS_TASK_WAIT,
 	NS_ID_INTRO,
Index: uspace/lib/c/include/ipc/services.h
===================================================================
--- uspace/lib/c/include/ipc/services.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/lib/c/include/ipc/services.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -48,7 +48,7 @@
 	SERVICE_DEVMAN     = FOURCC('d', 'e', 'v', 'n'),
 	SERVICE_IRC        = FOURCC('i', 'r', 'c', ' '),
-	SERVICE_CLIPBOARD  = FOURCC('c', 'l', 'i', 'p'),
 } service_t;
 
+#define SERVICE_NAME_CLIPBOARD "clipboard"
 #define SERVICE_NAME_CORECFG  "corecfg"
 #define SERVICE_NAME_DHCP     "net/dhcp"
Index: uspace/lib/c/include/ipc/udp.h
===================================================================
--- uspace/lib/c/include/ipc/udp.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/lib/c/include/ipc/udp.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -42,4 +42,5 @@
 	UDP_ASSOC_CREATE,
 	UDP_ASSOC_DESTROY,
+	UDP_ASSOC_SET_NOLOCAL,
 	UDP_ASSOC_SEND_MSG,
 	UDP_RMSG_INFO,
Index: uspace/lib/c/include/ns.h
===================================================================
--- uspace/lib/c/include/ns.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/lib/c/include/ns.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -46,4 +46,5 @@
 extern int ns_ping(void);
 extern int ns_intro(task_id_t);
+extern async_sess_t *ns_session_get(void);
 
 #endif
Index: uspace/lib/c/test/fibril/timer.c
===================================================================
--- uspace/lib/c/test/fibril/timer.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/lib/c/test/fibril/timer.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#include <fibril_synch.h>
+#include <pcut/pcut.h>
+
+PCUT_INIT
+
+PCUT_TEST_SUITE(fibril_timer);
+
+static void test_timeout_fn(void *arg)
+{
+	int *i;
+
+	i = (int *)arg;
+	++*i;
+}
+
+PCUT_TEST(create_destroy)
+{
+	fibril_timer_t *t;
+
+	t = fibril_timer_create(NULL);
+	PCUT_ASSERT_NOT_NULL(t);
+	fibril_timer_destroy(t);
+}
+
+PCUT_TEST(create_destroy_user_lock)
+{
+	fibril_mutex_t lock;
+	fibril_timer_t *t;
+
+	fibril_mutex_initialize(&lock);
+	t = fibril_timer_create(&lock);
+	PCUT_ASSERT_NOT_NULL(t);
+	fibril_timer_destroy(t);
+}
+
+PCUT_TEST(set_clear_locked)
+{
+	fibril_mutex_t lock;
+	fibril_timer_t *t;
+	fibril_timer_state_t fts;
+	int cnt;
+
+	fibril_mutex_initialize(&lock);
+	t = fibril_timer_create(&lock);
+	PCUT_ASSERT_NOT_NULL(t);
+
+	fibril_mutex_lock(&lock);
+	cnt = 0;
+
+	fibril_timer_set_locked(t, 100 * 1000 * 1000, test_timeout_fn, &cnt);
+	fibril_usleep(1000);
+	fts = fibril_timer_clear_locked(t);
+	PCUT_ASSERT_INT_EQUALS(fts_active, fts);
+
+	PCUT_ASSERT_INT_EQUALS(0, cnt);
+	fibril_mutex_unlock(&lock);
+
+	fibril_timer_destroy(t);
+}
+
+PCUT_TEST(set_clear_not_locked)
+{
+	fibril_mutex_t lock;
+	fibril_timer_t *t;
+	fibril_timer_state_t fts;
+	int cnt;
+
+	fibril_mutex_initialize(&lock);
+	t = fibril_timer_create(&lock);
+	PCUT_ASSERT_NOT_NULL(t);
+
+	cnt = 0;
+	fibril_timer_set(t, 100 * 1000 * 1000, test_timeout_fn, &cnt);
+	fibril_usleep(1000);
+	fts = fibril_timer_clear(t);
+	PCUT_ASSERT_INT_EQUALS(fts_active, fts);
+
+	PCUT_ASSERT_INT_EQUALS(0, cnt);
+
+	fibril_timer_destroy(t);
+}
+
+PCUT_TEST(fire)
+{
+	fibril_mutex_t lock;
+	fibril_timer_t *t;
+	fibril_timer_state_t fts;
+	int cnt;
+
+	fibril_mutex_initialize(&lock);
+	t = fibril_timer_create(&lock);
+	PCUT_ASSERT_NOT_NULL(t);
+
+	fibril_mutex_lock(&lock);
+	cnt = 0;
+
+	fibril_timer_set_locked(t, 100, test_timeout_fn, &cnt);
+	fibril_mutex_unlock(&lock);
+
+	fibril_usleep(1000);
+
+	fibril_mutex_lock(&lock);
+	fts = fibril_timer_clear_locked(t);
+	PCUT_ASSERT_INT_EQUALS(fts_fired, fts);
+
+	PCUT_ASSERT_INT_EQUALS(1, cnt);
+	fibril_mutex_unlock(&lock);
+
+	fibril_timer_destroy(t);
+}
+
+PCUT_EXPORT(fibril_timer);
Index: uspace/lib/c/test/io/table.c
===================================================================
--- uspace/lib/c/test/io/table.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/lib/c/test/io/table.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#include <io/table.h>
+#include <pcut/pcut.h>
+
+PCUT_INIT
+
+PCUT_TEST_SUITE(table);
+
+PCUT_TEST(smoke) {
+	table_t *table;
+	int rc;
+
+	rc = table_create(&table);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	table_header_row(table);
+	rc = table_printf(table, "A\t" "B\t" "C\t" "D\n");
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	rc = table_printf(table, "1\t" "2\t" "3\t" "4\n");
+	rc = table_printf(table, "i\t" "ii\t" "iii\t" "iv\n");
+	table_destroy(table);
+}
+
+PCUT_EXPORT(table);
Index: uspace/lib/c/test/main.c
===================================================================
--- uspace/lib/c/test/main.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/lib/c/test/main.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -32,8 +32,10 @@
 PCUT_INIT
 
+PCUT_IMPORT(fibril_timer);
 PCUT_IMPORT(odict);
 PCUT_IMPORT(qsort);
 PCUT_IMPORT(sprintf);
 PCUT_IMPORT(str);
+PCUT_IMPORT(table);
 
 PCUT_MAIN()
Index: uspace/lib/drv/generic/driver.c
===================================================================
--- uspace/lib/drv/generic/driver.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/lib/drv/generic/driver.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -943,11 +943,15 @@
 	int rc = async_create_port(INTERFACE_DDF_DRIVER, driver_connection_driver,
 	    NULL, &port);
-	if (rc != EOK)
+	if (rc != EOK) {
+		printf("Error: Failed to create driver port.\n");
 		return rc;
+	}
 	
 	rc = async_create_port(INTERFACE_DDF_DEVMAN, driver_connection_devman,
 	    NULL, &port);
-	if (rc != EOK)
+	if (rc != EOK) {
+		printf("Error: Failed to create devman port.\n");
 		return rc;
+	}
 	
 	async_set_fallback_port_handler(driver_connection_client, NULL);
@@ -964,6 +968,8 @@
 	/* Return success from the task since server has started. */
 	rc = task_retval(0);
-	if (rc != EOK)
+	if (rc != EOK) {
+		printf("Error: Failed returning task value.\n");
 		return rc;
+	}
 	
 	async_manager();
Index: uspace/lib/fdisk/Makefile
===================================================================
--- uspace/lib/fdisk/Makefile	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/lib/fdisk/Makefile	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -33,5 +33,4 @@
 
 SOURCES = \
-	src/cap.c \
 	src/fdisk.c
 
Index: uspace/lib/fdisk/include/fdisk.h
===================================================================
--- uspace/lib/fdisk/include/fdisk.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/lib/fdisk/include/fdisk.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -49,5 +49,5 @@
 extern int fdisk_dev_info_get_svcname(fdisk_dev_info_t *, char **);
 extern void fdisk_dev_info_get_svcid(fdisk_dev_info_t *, service_id_t *);
-extern int fdisk_dev_info_capacity(fdisk_dev_info_t *, fdisk_cap_t *);
+extern int fdisk_dev_info_capacity(fdisk_dev_info_t *, cap_spec_t *);
 
 extern int fdisk_dev_open(fdisk_t *, service_id_t, fdisk_dev_t **);
@@ -56,5 +56,5 @@
 extern void fdisk_dev_get_flags(fdisk_dev_t *, fdisk_dev_flags_t *);
 extern int fdisk_dev_get_svcname(fdisk_dev_t *, char **);
-extern int fdisk_dev_capacity(fdisk_dev_t *, fdisk_cap_t *);
+extern int fdisk_dev_capacity(fdisk_dev_t *, cap_spec_t *);
 
 extern int fdisk_label_get_info(fdisk_dev_t *, fdisk_label_info_t *);
@@ -65,16 +65,10 @@
 extern fdisk_part_t *fdisk_part_next(fdisk_part_t *);
 extern int fdisk_part_get_info(fdisk_part_t *, fdisk_part_info_t *);
-extern int fdisk_part_get_max_avail(fdisk_dev_t *, fdisk_spc_t, fdisk_cap_t *);
-extern int fdisk_part_get_tot_avail(fdisk_dev_t *, fdisk_spc_t, fdisk_cap_t *);
+extern int fdisk_part_get_max_avail(fdisk_dev_t *, fdisk_spc_t, cap_spec_t *);
+extern int fdisk_part_get_tot_avail(fdisk_dev_t *, fdisk_spc_t, cap_spec_t *);
 extern int fdisk_part_create(fdisk_dev_t *, fdisk_part_spec_t *,
     fdisk_part_t **);
 extern int fdisk_part_destroy(fdisk_part_t *);
 extern void fdisk_pspec_init(fdisk_part_spec_t *);
-
-extern int fdisk_cap_format(fdisk_cap_t *, char **);
-extern int fdisk_cap_parse(const char *, fdisk_cap_t *);
-extern void fdisk_cap_simplify(fdisk_cap_t *);
-extern void fdisk_cap_from_blocks(uint64_t, size_t, fdisk_cap_t *);
-extern int fdisk_cap_to_blocks(fdisk_cap_t *, fdisk_cvsel_t, size_t, uint64_t *);
 
 extern int fdisk_ltype_format(label_type_t, char **);
Index: uspace/lib/fdisk/include/types/fdisk.h
===================================================================
--- uspace/lib/fdisk/include/types/fdisk.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/lib/fdisk/include/types/fdisk.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -38,4 +38,5 @@
 
 #include <adt/list.h>
+#include <cap.h>
 #include <loc.h>
 #include <stdint.h>
@@ -43,27 +44,4 @@
 #include <types/vol.h>
 #include <vbd.h>
-
-/** Capacity unit */
-typedef enum {
-	cu_byte = 0,
-	cu_kbyte,
-	cu_mbyte,
-	cu_gbyte,
-	cu_tbyte,
-	cu_pbyte,
-	cu_ebyte,
-	cu_zbyte,
-	cu_ybyte
-} fdisk_cunit_t;
-
-/** Which of values within the precision of the capacity */
-typedef enum {
-	/** The nominal (middling) value */
-	fcv_nom,
-	/** The minimum value */
-	fcv_min,
-	/** The maximum value */
-	fcv_max
-} fdisk_cvsel_t;
 
 typedef enum {
@@ -85,25 +63,4 @@
 
 #define CU_LIMIT (cu_ybyte + 1)
-
-/** Partition capacity.
- *
- * Partition capacity represents both value and precision.
- * It is a decimal floating point value combined with a decimal
- * capacity unit. There is an integer mantisa @c m which in combination
- * with the number of decimal positions @c dp gives a decimal floating-point
- * number. E.g. for m = 1025 and dp = 2 the number is 10.25. If the unit
- * cunit = cu_kbyte, the capacity is 10.25 kByte, i.e. 10 250 bytes.
- *
- * Note that 1.000 kByte is equivalent to 1000 Byte, but 1 kByte is less
- * precise.
- */
-typedef struct {
-	/** Mantisa */
-	uint64_t m;
-	/** Decimal positions */
-	unsigned dp;
-	/** Capacity unit */
-	fdisk_cunit_t cunit;
-} fdisk_cap_t;
 
 /** List of devices available for managing by fdisk */
@@ -169,5 +126,5 @@
 	link_t llog_ba;
 	/** Capacity */
-	fdisk_cap_t capacity;
+	cap_spec_t capacity;
 	/** Partition kind */
 	label_pkind_t pkind;
@@ -193,5 +150,5 @@
 typedef struct {
 	/** Desired capacity */
-	fdisk_cap_t capacity;
+	cap_spec_t capacity;
 	/** Partition kind */
 	label_pkind_t pkind;
@@ -205,5 +162,5 @@
 typedef struct {
 	/** Capacity */
-	fdisk_cap_t capacity;
+	cap_spec_t capacity;
 	/** Partition kind */
 	label_pkind_t pkind;
Index: pace/lib/fdisk/src/cap.c
===================================================================
--- uspace/lib/fdisk/src/cap.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ 	(revision )
@@ -1,288 +1,0 @@
-/*
- * Copyright (c) 2015 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 libfdisk
- * @{
- */
-/**
- * @file Disk management library.
- */
-
-#include <errno.h>
-#include <fdisk.h>
-#include <imath.h>
-#include <stdio.h>
-#include <str.h>
-
-/** Simplified capacity parameters */
-enum {
-	/** Simplified capacity maximum integer digits */
-	scap_max_idig = 3,
-	/** Simplified capacity maximum significant digits */
-	scap_max_sdig = 4
-};
-
-static const char *cu_str[] = {
-	[cu_byte] = "B",
-	[cu_kbyte] = "kB",
-	[cu_mbyte] = "MB",
-	[cu_gbyte] = "GB",
-	[cu_tbyte] = "TB",
-	[cu_pbyte] = "PB",
-	[cu_ebyte] = "EB",
-	[cu_zbyte] = "ZB",
-	[cu_ybyte] = "YB"
-};
-
-void fdisk_cap_from_blocks(uint64_t nblocks, size_t block_size,
-    fdisk_cap_t *cap)
-{
-	uint64_t tsize;
-
-	tsize = nblocks * block_size;
-	cap->m = tsize;
-	cap->dp = 0;
-	cap->cunit = cu_byte;
-}
-
-/** Convert capacity to blocks.
- *
- * If the value of bytes is not integer, it is properly rounded. If the number
- * of bytes is not divisible by the number of blocks, it is rounded
- * up to an integer number of blocks.
- *
- * A capacity value entails precision, i.e. it corresponds to a range
- * of values. @a cvsel selects the value to return. @c fcv_nom gives
- * the nominal (middle) value, @c fcv_min gives the minimum value
- * and @c fcv_max gives the maximum value.
- */
-int fdisk_cap_to_blocks(fdisk_cap_t *cap, fdisk_cvsel_t cvsel,
-    size_t block_size, uint64_t *rblocks)
-{
-	int exp;
-	uint64_t bytes;
-	uint64_t f;
-	uint64_t adj;
-	uint64_t blocks;
-	uint64_t rem;
-	int rc;
-
-	exp = cap->cunit * 3 - cap->dp;
-	if (exp < 0) {
-		rc = ipow10_u64(-exp, &f);
-		if (rc != EOK)
-			return ERANGE;
-		bytes = (cap->m + (f / 2)) / f;
-		if (bytes * f - (f / 2) != cap->m)
-			return ERANGE;
-	} else {
-		rc = ipow10_u64(exp, &f);
-		if (rc != EOK)
-			return ERANGE;
-
-		adj = 0;
-		switch (cvsel) {
-		case fcv_nom:
-			adj = 0;
-			break;
-		case fcv_min:
-			adj = -(f / 2);
-			break;
-		case fcv_max:
-			adj = f / 2 - 1;
-			break;
-		}
-
-		bytes = cap->m * f + adj;
-		if ((bytes - adj) / f != cap->m)
-			return ERANGE;
-	}
-
-	rem = bytes % block_size;
-	if ((bytes + rem) < bytes)
-		return ERANGE;
-
-	blocks = (bytes + rem) / block_size;
-
-	*rblocks = blocks;
-	return EOK;
-}
-
-/** Simplify and round capacity to a human-friendly form.
- *
- * Change unit and round the number so that we have at most three integer
- * digits and at most two fractional digits, e.g abc.xy <unit>.
- */
-void fdisk_cap_simplify(fdisk_cap_t *cap)
-{
-	uint64_t div;
-	uint64_t maxv;
-	unsigned sdig;
-	unsigned rdig;
-	int rc;
-
-	/* Change units so that we have at most @c scap_max_idig integer digits */
-	rc = ipow10_u64(scap_max_idig, &maxv);
-	assert(rc == EOK);
-
-	rc = ipow10_u64(cap->dp, &div);
-	assert(rc == EOK);
-
-	while (cap->m / div >= maxv) {
-		++cap->cunit;
-		cap->dp += 3;
-		div = div * 1000;
-	}
-
-	/* Round the number so that we have at most @c scap_max_sdig significant digits */
-	sdig = 1 + ilog10_u64(cap->m); /* number of significant digits */
-	if (sdig > scap_max_sdig) {
-		/* Number of digits to remove */
-		rdig = sdig - scap_max_sdig;
-		if (rdig > cap->dp)
-			rdig = cap->dp;
-
-		rc = ipow10_u64(rdig, &div);
-		assert(rc == EOK);
-
-		cap->m = (cap->m + (div / 2)) / div;
-		cap->dp -= rdig;
-	}
-}
-
-int fdisk_cap_format(fdisk_cap_t *cap, char **rstr)
-{
-	int rc;
-	const char *sunit;
-	uint64_t ipart;
-	uint64_t fpart;
-	uint64_t div;
-
-	sunit = NULL;
-
-	if (cap->cunit < 0 || cap->cunit >= CU_LIMIT)
-		assert(false);
-
-	rc = ipow10_u64(cap->dp, &div);
-	if (rc != EOK)
-		return rc;
-
-	ipart = cap->m / div;
-	fpart = cap->m % div;
-
-	sunit = cu_str[cap->cunit];
-	if (cap->dp > 0) {
-		rc = asprintf(rstr, "%" PRIu64 ".%0*" PRIu64 " %s", ipart,
-		    (int)cap->dp, fpart, sunit);
-	} else {
-		rc = asprintf(rstr, "%" PRIu64 " %s", ipart, sunit);
-	}
-	if (rc < 0)
-		return ENOMEM;
-
-	return EOK;
-}
-
-static int fdisk_digit_val(char c, int *val)
-{
-	switch (c) {
-	case '0': *val = 0; break;
-	case '1': *val = 1; break;
-	case '2': *val = 2; break;
-	case '3': *val = 3; break;
-	case '4': *val = 4; break;
-	case '5': *val = 5; break;
-	case '6': *val = 6; break;
-	case '7': *val = 7; break;
-	case '8': *val = 8; break;
-	case '9': *val = 9; break;
-	default:
-		return EINVAL;
-	}
-
-	return EOK;
-}
-
-int fdisk_cap_parse(const char *str, fdisk_cap_t *cap)
-{
-	const char *eptr;
-	const char *p;
-	int d;
-	int dp;
-	unsigned long m;
-	int i;
-
-	m = 0;
-
-	eptr = str;
-	while (fdisk_digit_val(*eptr, &d) == EOK) {
-		m = m * 10 + d;
-		++eptr;
-	}
-
-	if (*eptr == '.') {
-		++eptr;
-		dp = 0;
-		while (fdisk_digit_val(*eptr, &d) == EOK) {
-			m = m * 10 + d;
-			++dp;
-			++eptr;
-		}
-	} else {
-		dp = 0;
-	}
-
-	while (*eptr == ' ')
-		++eptr;
-
-	if (*eptr == '\0') {
-		cap->cunit = cu_byte;
-	} else {
-		for (i = 0; i < CU_LIMIT; i++) {
-			if (str_lcasecmp(eptr, cu_str[i],
-			    str_length(cu_str[i])) == 0) {
-				p = eptr + str_size(cu_str[i]);
-				while (*p == ' ')
-					++p;
-				if (*p == '\0')
-					goto found;
-			}
-		}
-
-		return EINVAL;
-found:
-		cap->cunit = i;
-	}
-
-	cap->m = m;
-	cap->dp = dp;
-	return EOK;
-}
-
-/** @}
- */
Index: uspace/lib/fdisk/src/fdisk.c
===================================================================
--- uspace/lib/fdisk/src/fdisk.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/lib/fdisk/src/fdisk.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -35,4 +35,5 @@
 
 #include <adt/list.h>
+#include <cap.h>
 #include <errno.h>
 #include <fdisk.h>
@@ -218,5 +219,5 @@
 }
 
-int fdisk_dev_info_capacity(fdisk_dev_info_t *info, fdisk_cap_t *cap)
+int fdisk_dev_info_capacity(fdisk_dev_info_t *info, cap_spec_t *cap)
 {
 	vbd_disk_info_t vinfo;
@@ -227,5 +228,5 @@
 		return EIO;
 
-	fdisk_cap_from_blocks(vinfo.nblocks, vinfo.block_size, cap);
+	cap_from_blocks(vinfo.nblocks, vinfo.block_size, cap);
 	return EOK;
 }
@@ -294,5 +295,5 @@
 		dev->ext_part = part;
 
-	fdisk_cap_from_blocks(part->nblocks, dev->dinfo.block_size,
+	cap_from_blocks(part->nblocks, dev->dinfo.block_size,
 	    &part->capacity);
 	part->part_id = partid;
@@ -535,7 +536,7 @@
 }
 
-int fdisk_dev_capacity(fdisk_dev_t *dev, fdisk_cap_t *cap)
-{
-	fdisk_cap_from_blocks(dev->dinfo.nblocks, dev->dinfo.block_size, cap);
+int fdisk_dev_capacity(fdisk_dev_t *dev, cap_spec_t *cap)
+{
+	cap_from_blocks(dev->dinfo.nblocks, dev->dinfo.block_size, cap);
 	return EOK;
 }
@@ -677,5 +678,5 @@
 
 /** Get size of largest free block. */
-int fdisk_part_get_max_avail(fdisk_dev_t *dev, fdisk_spc_t spc, fdisk_cap_t *cap)
+int fdisk_part_get_max_avail(fdisk_dev_t *dev, fdisk_spc_t spc, cap_spec_t *cap)
 {
 	int rc;
@@ -696,5 +697,5 @@
 	}
 
-	fdisk_cap_from_blocks(nb, dev->dinfo.block_size, cap);
+	cap_from_blocks(nb, dev->dinfo.block_size, cap);
 	return EOK;
 }
@@ -702,5 +703,5 @@
 /** Get total free space capacity. */
 int fdisk_part_get_tot_avail(fdisk_dev_t *dev, fdisk_spc_t spc,
-    fdisk_cap_t *cap)
+    cap_spec_t *cap)
 {
 	fdisk_free_range_t fr;
@@ -724,5 +725,5 @@
 	} while (fdisk_free_range_next(&fr));
 
-	fdisk_cap_from_blocks(totb, dev->dinfo.block_size, cap);
+	cap_from_blocks(totb, dev->dinfo.block_size, cap);
 	return EOK;
 }
@@ -995,15 +996,15 @@
 	int rc;
 
-	rc = fdisk_cap_to_blocks(&pspec->capacity, fcv_nom, dev->dinfo.block_size,
+	rc = cap_to_blocks(&pspec->capacity, cv_nom, dev->dinfo.block_size,
 	    &nom_blocks);
 	if (rc != EOK)
 		return rc;
 
-	rc = fdisk_cap_to_blocks(&pspec->capacity, fcv_min, dev->dinfo.block_size,
+	rc = cap_to_blocks(&pspec->capacity, cv_min, dev->dinfo.block_size,
 	    &min_blocks);
 	if (rc != EOK)
 		return rc;
 
-	rc = fdisk_cap_to_blocks(&pspec->capacity, fcv_max, dev->dinfo.block_size,
+	rc = cap_to_blocks(&pspec->capacity, cv_max, dev->dinfo.block_size,
 	    &max_blocks);
 	if (rc != EOK)
Index: uspace/lib/http/src/http.c
===================================================================
--- uspace/lib/http/src/http.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/lib/http/src/http.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -121,5 +121,8 @@
 	
 	tcp_conn_destroy(http->conn);
+	http->conn = NULL;
 	tcp_destroy(http->tcp);
+	http->tcp = NULL;
+	
 	return EOK;
 }
@@ -127,4 +130,5 @@
 void http_destroy(http_t *http)
 {
+	(void) http_close(http);
 	recv_buffer_fini(&http->recv_buffer);
 	free(http);
Index: uspace/lib/label/src/gpt.c
===================================================================
--- uspace/lib/label/src/gpt.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/lib/label/src/gpt.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -99,4 +99,5 @@
 	uint8_t *etable[2];
 	size_t bsize;
+	aoff64_t nblocks;
 	uint32_t num_entries;
 	uint32_t esize;
@@ -123,4 +124,10 @@
 	}
 
+	rc = bd->ops->get_nblocks(bd->arg, &nblocks);
+	if (rc != EOK) {
+		rc = EIO;
+		goto error;
+	}
+
 	if (bsize < 512 || (bsize % 512) != 0) {
 		rc = EINVAL;
@@ -147,4 +154,9 @@
 
 	h1ba = uint64_t_le2host(gpt_hdr[0]->alternate_lba);
+
+	if (h1ba >= nblocks) {
+		rc = EINVAL;
+		goto error;
+	}
 
 	rc = bd->ops->read(bd->arg, h1ba, 1, gpt_hdr[1]);
Index: uspace/lib/math/arch/amd64/src/cos.S
===================================================================
--- uspace/lib/math/arch/amd64/src/cos.S	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/lib/math/arch/amd64/src/cos.S	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -68,5 +68,5 @@
 		jnz reduce
 	
-	fstpl %st(1)
+	fstp %st(1)
 	fcos
 	
Index: uspace/lib/math/arch/amd64/src/sin.S
===================================================================
--- uspace/lib/math/arch/amd64/src/sin.S	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/lib/math/arch/amd64/src/sin.S	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -68,5 +68,5 @@
 		jnz reduce
 	
-	fstpl %st(1)
+	fstp %st(1)
 	fsin
 	
Index: uspace/lib/nettl/src/amap.c
===================================================================
--- uspace/lib/nettl/src/amap.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/lib/nettl/src/amap.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -97,4 +97,8 @@
 {
 	log_msg(LOG_DEFAULT, LVL_DEBUG2, "amap_destroy()");
+
+	assert(list_empty(&map->repla));
+	assert(list_empty(&map->laddr));
+	assert(list_empty(&map->llink));
 	free(map);
 }
Index: uspace/lib/nettl/src/portrng.c
===================================================================
--- uspace/lib/nettl/src/portrng.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/lib/nettl/src/portrng.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -182,15 +182,19 @@
 void portrng_free_port(portrng_t *pr, uint16_t pnum)
 {
+	log_msg(LOG_DEFAULT, LVL_DEBUG2, "portrng_free_port(%u)", pnum);
 	log_msg(LOG_DEFAULT, LVL_DEBUG2, "portrng_free_port() - begin");
 	list_foreach(pr->used, lprng, portrng_port_t, port) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG2, "portrng_free_port - check port %u", port->pn);
 		if (port->pn == pnum) {
+			log_msg(LOG_DEFAULT, LVL_DEBUG2, "portrng_free_port - OK");
 			list_remove(&port->lprng);
 			free(port);
+			log_msg(LOG_DEFAULT, LVL_DEBUG2, "portrng_free_port() - end");
 			return;
 		}
 	}
 
+	log_msg(LOG_DEFAULT, LVL_DEBUG2, "portrng_free_port - FAIL");
 	assert(false);
-	log_msg(LOG_DEFAULT, LVL_DEBUG2, "portrng_free_port() - end");
 }
 
@@ -209,3 +213,2 @@
  * @}
  */
-
Index: uspace/srv/clipboard/clipboard.c
===================================================================
--- uspace/srv/clipboard/clipboard.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/clipboard/clipboard.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -27,13 +27,14 @@
  */
 
+#include <async.h>
+#include <errno.h>
+#include <fibril_synch.h>
+#include <ipc/services.h>
+#include <ipc/clipboard.h>
+#include <loc.h>
+#include <malloc.h>
 #include <stdio.h>
 #include <stdbool.h>
-#include <async.h>
-#include <ns.h>
-#include <ipc/services.h>
-#include <ipc/clipboard.h>
-#include <malloc.h>
-#include <fibril_synch.h>
-#include <errno.h>
+#include <task.h>
 
 #define NAME  "clipboard"
@@ -43,4 +44,5 @@
 static clipboard_tag_t clip_tag = CLIPBOARD_TAG_NONE;
 static FIBRIL_MUTEX_INITIALIZE(clip_mtx);
+static service_id_t svc_id;
 
 static void clip_put_data(ipc_callid_t rid, ipc_call_t *request)
@@ -179,10 +181,20 @@
 int main(int argc, char *argv[])
 {
+	int rc;
+	
 	printf("%s: HelenOS clipboard service\n", NAME);
-	
 	async_set_fallback_port_handler(clip_connection, NULL);
-	int rc = service_register(SERVICE_CLIPBOARD);
-	if (rc != EOK)
+	
+	rc = loc_server_register(NAME);
+	if (rc != EOK) {
+		printf("%s: Failed registering server. (%d)\n", NAME, rc);
 		return rc;
+	}
+	
+	rc = loc_service_register(SERVICE_NAME_CLIPBOARD, &svc_id);
+	if (rc != EOK) {
+		printf("%s: Failed registering service. (%d)\n", NAME, rc);
+		return rc;
+	}
 	
 	printf("%s: Accepting connections\n", NAME);
Index: uspace/srv/hw/bus/cuda_adb/cuda_adb.c
===================================================================
--- uspace/srv/hw/bus/cuda_adb/cuda_adb.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/hw/bus/cuda_adb/cuda_adb.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -465,5 +465,5 @@
 
 	if (size != 3) {
-		printf("unrecognized packet, size=%d\n", size);
+		printf("unrecognized packet, size=%zu\n", size);
 		for (i = 0; i < size; ++i) {
 			printf(" 0x%02x", data[i]);
@@ -474,5 +474,5 @@
 
 	if (reg_no != 0) {
-		printf("unrecognized packet, size=%d\n", size);
+		printf("unrecognized packet, size=%zu\n", size);
 		for (i = 0; i < size; ++i) {
 			printf(" 0x%02x", data[i]);
Index: uspace/srv/hw/irc/icp-ic/icp-ic.c
===================================================================
--- uspace/srv/hw/irc/icp-ic/icp-ic.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/hw/irc/icp-ic/icp-ic.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -65,5 +65,5 @@
 		return EINVAL;
 
-	log_msg(LOG_DEFAULT, LVL_NOTE, "Enable IRQ %d", irq);
+	log_msg(LOG_DEFAULT, LVL_NOTE, "Enable IRQ %zu", irq);
 
 	pio_write_32(&icpic_regs->irq_enableset, BIT_V(uint32_t, irq));
Index: uspace/srv/hw/irc/obio/obio.c
===================================================================
--- uspace/srv/hw/irc/obio/obio.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/hw/irc/obio/obio.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -48,4 +48,5 @@
 #include <ddi.h>
 #include <align.h>
+#include <inttypes.h>
 #include <stdbool.h>
 #include <errno.h>
@@ -127,5 +128,5 @@
 	}
 	
-	printf("%s: OBIO registers with base at %lx\n", NAME, base_phys);
+	printf("%s: OBIO registers with base at 0x%" PRIun "\n", NAME, base_phys);
 	
 	async_set_fallback_port_handler(obio_connection, NULL);
Index: uspace/srv/net/dhcp/transport.c
===================================================================
--- uspace/srv/net/dhcp/transport.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/dhcp/transport.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -153,4 +153,10 @@
 	}
 
+	rc = udp_assoc_set_nolocal(assoc);
+	if (rc != EOK) {
+		rc = EIO;
+		goto error;
+	}
+
 	dt->udp = udp;
 	dt->assoc = assoc;
Index: uspace/srv/net/dnsrsrv/dns_msg.c
===================================================================
--- uspace/srv/net/dnsrsrv/dns_msg.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/dnsrsrv/dns_msg.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -77,5 +77,5 @@
 		return ENOMEM;
 
-	str_cpy((*dstr) + s1, nsize - s1, suff);
+	str_cpy(nstr + s1, nsize - s1, suff);
 
 	*dstr = nstr;
@@ -195,12 +195,4 @@
 			break;
 
-		if (!first) {
-			rc = dns_dstr_ext(&name, ".");
-			if (rc != EOK) {
-				rc = ENOMEM;
-				goto error;
-			}
-		}
-
 		if ((lsize & 0xc0) == 0xc0) {
 			/* Pointer */
@@ -233,5 +225,6 @@
 			 * the message continues
 			 */
-			*eoff = eptr;
+			if (*eoff == 0)
+				*eoff = eptr;
 
 			bp = pdu->data + ptr;
@@ -243,4 +236,12 @@
 			rc = EINVAL;
 			goto error;
+		}
+
+		if (!first) {
+			rc = dns_dstr_ext(&name, ".");
+			if (rc != EOK) {
+				rc = ENOMEM;
+				goto error;
+			}
 		}
 
@@ -413,5 +414,5 @@
 	rc = dns_name_decode(pdu, boff, &rr->name, &name_eoff);
 	if (rc != EOK) {
-		log_msg(LOG_DEFAULT, LVL_DEBUG, "Error decoding name");
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "dns_rr_decode: error decoding name");
 		free(rr);
 		return ENOMEM;
@@ -421,4 +422,5 @@
 		free(rr->name);
 		free(rr);
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "dns_rr_decode: error name_off");
 		return EINVAL;
 	}
@@ -430,4 +432,5 @@
 		free(rr->name);
 		free(rr);
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "dns_rr_decode: error bsz");
 		return EINVAL;
 	}
@@ -445,4 +448,7 @@
 	bsz -= sizeof(uint32_t);
 
+	log_msg(LOG_DEFAULT, LVL_DEBUG2, "dns_rr_decode: rtype=0x%x, rclass=0x%x, ttl=0x%x",
+	    rr->rtype, rr->rclass, rr->ttl );
+
 	rdlength = dns_uint16_t_decode(bp, bsz);
 	bp += sizeof(uint16_t);
@@ -452,12 +458,15 @@
 		free(rr->name);
 		free(rr);
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "dns_rr_decode: Error rdlength %zu > bsz %zu", rdlength, bsz);
 		return EINVAL;
 	}
 
 	rr->rdata_size = rdlength;
-	rr->rdata = calloc(1, sizeof(rdlength));
+	log_msg(LOG_DEFAULT, LVL_DEBUG2, "dns_rr_decode: rdlength=%zu", rdlength);
+	rr->rdata = calloc(1, rdlength);
 	if (rr->rdata == NULL) {
 		free(rr->name);
 		free(rr);
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "dns_rr_decode: Error memory");
 		return ENOMEM;
 	}
@@ -470,4 +479,5 @@
 	*eoff = bp - pdu->data;
 	*retrr = rr;
+	log_msg(LOG_DEFAULT, LVL_DEBUG2, "dns_rr_decode: done");
 	return EOK;
 }
@@ -586,5 +596,5 @@
 	memcpy(msg->pdu.data, data, size);
 	msg->pdu.size = size;
-	log_msg(LOG_DEFAULT, LVL_NOTE, "dns_message_decode: pdu->data = %p, "
+	log_msg(LOG_DEFAULT, LVL_DEBUG2, "dns_message_decode: pdu->data = %p, "
 	    "pdu->size=%zu", msg->pdu.data, msg->pdu.size);
 
@@ -605,4 +615,6 @@
 
 	qd_count = uint16_t_be2host(hdr->qd_count);
+	log_msg(LOG_DEFAULT, LVL_DEBUG2, "qd_count=%zu", qd_count);
+
 
 	for (i = 0; i < qd_count; i++) {
@@ -618,4 +630,5 @@
 
 	an_count = uint16_t_be2host(hdr->an_count);
+	log_msg(LOG_DEFAULT, LVL_DEBUG2, "an_count=%zu", an_count);
 
 	for (i = 0; i < an_count; i++) {
Index: uspace/srv/net/dnsrsrv/query.c
===================================================================
--- uspace/srv/net/dnsrsrv/query.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/dnsrsrv/query.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -90,4 +90,5 @@
 	list_append(&question->msg, &msg->question);
 	
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "dns_name_query: send DNS request");
 	dns_message_t *amsg;
 	int rc = dns_request(msg, &amsg);
Index: uspace/srv/net/dnsrsrv/transport.c
===================================================================
--- uspace/srv/net/dnsrsrv/transport.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/dnsrsrv/transport.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -183,4 +183,5 @@
 	void *req_data;
 	size_t req_size;
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "dns_request: Encode dns message");
 	int rc = dns_message_encode(req, &req_data, &req_size);
 	if (rc != EOK)
@@ -194,8 +195,11 @@
 
 	while (ntry < REQ_RETRY_MAX) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "dns_request: Send DNS message");
 		rc = udp_assoc_send_msg(transport_assoc, &ep, req_data,
 		    req_size);
-		if (rc != EOK)
+		if (rc != EOK) {
+			log_msg(LOG_DEFAULT, LVL_DEBUG, "Error %d sending message", rc);
 			goto error;
+		}
 
 		treq = treq_create(req);
Index: uspace/srv/net/inetsrv/inet_link.c
===================================================================
--- uspace/srv/net/inetsrv/inet_link.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/inetsrv/inet_link.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -111,5 +111,5 @@
 	}
 
-	log_msg(LOG_DEFAULT, LVL_NOTE, "inet_iplink_recv: link_id=%zu", packet.link_id);
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_iplink_recv: link_id=%zu", packet.link_id);
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "call inet_recv_packet()");
 	rc = inet_recv_packet(&packet);
Index: uspace/srv/net/inetsrv/inetsrv.c
===================================================================
--- uspace/srv/net/inetsrv/inetsrv.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/inetsrv/inetsrv.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -450,5 +450,5 @@
 	ipc_call_t answer;
 
-	log_msg(LOG_DEFAULT, LVL_NOTE, "inet_ev_recv: iplink=%zu",
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "inet_ev_recv: iplink=%zu",
 	    dgram->iplink);
 
Index: uspace/srv/net/tcp/Makefile
===================================================================
--- uspace/srv/net/tcp/Makefile	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/tcp/Makefile	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -37,6 +37,7 @@
 BINARY = tcp
 
-SOURCES = \
+SOURCES_COMMON = \
 	conn.c \
+	inet.c \
 	iqueue.c \
 	ncsim.c \
@@ -44,10 +45,25 @@
 	rqueue.c \
 	segment.c \
-	service.c \
 	seq_no.c \
-	tcp.c \
 	test.c \
 	tqueue.c \
 	ucall.c
 
+SOURCES = \
+	$(SOURCES_COMMON) \
+	service.c \
+	tcp.c
+
+TEST_SOURCES = \
+	$(SOURCES_COMMON) \
+	test/conn.c \
+	test/iqueue.c \
+	test/main.c \
+	test/pdu.c \
+	test/rqueue.c \
+	test/segment.c \
+	test/seq_no.c \
+	test/tqueue.c \
+	test/ucall.c
+
 include $(USPACE_PREFIX)/Makefile.common
Index: uspace/srv/net/tcp/conn.c
===================================================================
--- uspace/srv/net/tcp/conn.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/tcp/conn.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -44,5 +44,8 @@
 #include <stdlib.h>
 #include "conn.h"
+#include "inet.h"
 #include "iqueue.h"
+#include "pdu.h"
+#include "rqueue.h"
 #include "segment.h"
 #include "seq_no.h"
@@ -57,12 +60,26 @@
 #define TIME_WAIT_TIMEOUT	(2*MAX_SEGMENT_LIFETIME)
 
+/** List of all allocated connections */
 static LIST_INITIALIZE(conn_list);
 /** Taken after tcp_conn_t lock */
 static FIBRIL_MUTEX_INITIALIZE(conn_list_lock);
+/** Connection association map */
 static amap_t *amap;
-
-static void tcp_conn_seg_process(tcp_conn_t *conn, tcp_segment_t *seg);
-static void tcp_conn_tw_timer_set(tcp_conn_t *conn);
-static void tcp_conn_tw_timer_clear(tcp_conn_t *conn);
+/** Taken after tcp_conn_t lock */
+static FIBRIL_MUTEX_INITIALIZE(amap_lock);
+
+/** Internal loopback configuration */
+tcp_lb_t tcp_conn_lb = tcp_lb_none;
+
+static void tcp_conn_seg_process(tcp_conn_t *, tcp_segment_t *);
+static void tcp_conn_tw_timer_set(tcp_conn_t *);
+static void tcp_conn_tw_timer_clear(tcp_conn_t *);
+static void tcp_transmit_segment(inet_ep2_t *, tcp_segment_t *);
+static void tcp_conn_trim_seg_to_wnd(tcp_conn_t *, tcp_segment_t *);
+static void tcp_reply_rst(inet_ep2_t *, tcp_segment_t *);
+
+static tcp_tqueue_cb_t tcp_conn_tqueue_cb = {
+	.transmit_seg = tcp_transmit_segment
+};
 
 /** Initialize connections. */
@@ -78,4 +95,13 @@
 
 	return EOK;
+}
+
+/** Finalize connections. */
+void tcp_conns_fini(void)
+{
+	assert(list_empty(&conn_list));
+
+	amap_destroy(amap);
+	amap = NULL;
 }
 
@@ -130,6 +156,8 @@
 
 	/* Initialize retransmission queue */
-	if (tcp_tqueue_init(&conn->retransmit, conn) != EOK)
+	if (tcp_tqueue_init(&conn->retransmit, conn, &tcp_conn_tqueue_cb)
+	    != EOK) {
 		goto error;
+	}
 
 	tqueue_inited = true;
@@ -147,4 +175,8 @@
 	if (epp != NULL)
 		conn->ident = *epp;
+
+	fibril_mutex_lock(&conn_list_lock);
+	list_append(&conn->link, &conn_list);
+	fibril_mutex_unlock(&conn_list_lock);
 
 	return conn;
@@ -181,5 +213,11 @@
 {
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_conn_free(%p)", conn->name, conn);
+
+	assert(conn->mapped == false);
 	tcp_tqueue_fini(&conn->retransmit);
+
+	fibril_mutex_lock(&conn_list_lock);
+	list_remove(&conn->link);
+	fibril_mutex_unlock(&conn_list_lock);
 
 	if (conn->rcv_buf != NULL)
@@ -271,5 +309,5 @@
 
 	tcp_conn_addref(conn);
-	fibril_mutex_lock(&conn_list_lock);
+	fibril_mutex_lock(&amap_lock);
 
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_add: conn=%p", conn);
@@ -278,11 +316,11 @@
 	if (rc != EOK) {
 		tcp_conn_delref(conn);
-		fibril_mutex_unlock(&conn_list_lock);
+		fibril_mutex_unlock(&amap_lock);
 		return rc;
 	}
 
 	conn->ident = aepp;
-	list_append(&conn->link, &conn_list);
-	fibril_mutex_unlock(&conn_list_lock);
+	conn->mapped = true;
+	fibril_mutex_unlock(&amap_lock);
 
 	return EOK;
@@ -293,10 +331,13 @@
  * Remove connection from the connection map.
  */
-void tcp_conn_remove(tcp_conn_t *conn)
-{
-	fibril_mutex_lock(&conn_list_lock);
+static void tcp_conn_remove(tcp_conn_t *conn)
+{
+	if (!conn->mapped)
+		return;
+
+	fibril_mutex_lock(&amap_lock);
 	amap_remove(amap, &conn->ident);
-	list_remove(&conn->link);
-	fibril_mutex_unlock(&conn_list_lock);
+	conn->mapped = false;
+	fibril_mutex_unlock(&amap_lock);
 	tcp_conn_delref(conn);
 }
@@ -335,4 +376,6 @@
 void tcp_conn_sync(tcp_conn_t *conn)
 {
+	assert(fibril_mutex_is_locked(&conn->lock));
+
 	/* XXX select ISS */
 	conn->iss = 1;
@@ -388,10 +431,10 @@
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_find_ref(%p)", epp);
 
-	fibril_mutex_lock(&conn_list_lock);
+	fibril_mutex_lock(&amap_lock);
 
 	rc = amap_find_match(amap, epp, &arg);
 	if (rc != EOK) {
 		assert(rc == ENOENT);
-		fibril_mutex_unlock(&conn_list_lock);
+		fibril_mutex_unlock(&amap_lock);
 		return NULL;
 	}
@@ -400,5 +443,5 @@
 	tcp_conn_addref(conn);
 
-	fibril_mutex_unlock(&conn_list_lock);
+	fibril_mutex_unlock(&amap_lock);
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_find_ref: got conn=%p",
 	    conn);
@@ -412,5 +455,11 @@
 void tcp_conn_reset(tcp_conn_t *conn)
 {
+	assert(fibril_mutex_is_locked(&conn->lock));
+
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_conn_reset()", conn->name);
+
+	if (conn->cstate == st_closed)
+		return;
+
 	conn->reset = true;
 	tcp_conn_state_set(conn, st_closed);
@@ -865,5 +914,10 @@
 		return cp_done;
 
-	/* TODO */
+	if (conn->fin_is_acked) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: FIN acked -> Time-Wait",
+		    conn->name);
+		tcp_conn_state_set(conn, st_time_wait);
+	}
+
 	return cp_continue;
 }
@@ -1062,9 +1116,9 @@
 		log_msg(LOG_DEFAULT, LVL_DEBUG, " - FIN found in segment.");
 
+		conn->rcv_nxt++;
+		conn->rcv_wnd--;
+
 		/* Send ACK */
 		tcp_tqueue_ctrl_seg(conn, CTL_ACK);
-
-		conn->rcv_nxt++;
-		conn->rcv_wnd--;
 
 		/* Change connection state */
@@ -1211,5 +1265,5 @@
 
 		/* Need to remove and re-insert connection with new identity */
-		fibril_mutex_lock(&conn_list_lock);
+		fibril_mutex_lock(&amap_lock);
 
 		if (inet_addr_is_any(&conn->ident.remote.addr))
@@ -1227,5 +1281,5 @@
 			assert(rc == ENOMEM);
 			log_msg(LOG_DEFAULT, LVL_ERROR, "Out of memory.");
-			fibril_mutex_unlock(&conn_list_lock);
+			fibril_mutex_unlock(&amap_lock);
 			tcp_conn_unlock(conn);
 			return;
@@ -1233,5 +1287,5 @@
 
 		amap_remove(amap, &oldepp);
-		fibril_mutex_unlock(&conn_list_lock);
+		fibril_mutex_unlock(&amap_lock);
 
 		conn->name = (char *) "a";
@@ -1322,5 +1376,5 @@
  * @param seg		Segment
  */
-void tcp_conn_trim_seg_to_wnd(tcp_conn_t *conn, tcp_segment_t *seg)
+static void tcp_conn_trim_seg_to_wnd(tcp_conn_t *conn, tcp_segment_t *seg)
 {
 	uint32_t left, right;
@@ -1346,4 +1400,61 @@
 }
 
+/** Transmit segment over network.
+ *
+ * @param epp Endpoint pair with source and destination information
+ * @param seg Segment (ownership retained by caller)
+ */
+static void tcp_transmit_segment(inet_ep2_t *epp, tcp_segment_t *seg)
+{
+	tcp_pdu_t *pdu;
+	tcp_segment_t *dseg;
+	inet_ep2_t rident;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG,
+	    "tcp_transmit_segment(l:(%u),f:(%u), %p)",
+	    epp->local.port, epp->remote.port, seg);
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "SEG.SEQ=%" PRIu32 ", SEG.WND=%" PRIu32,
+	    seg->seq, seg->wnd);
+
+	tcp_segment_dump(seg);
+
+	if (tcp_conn_lb == tcp_lb_segment) {
+		/* Loop back segment */
+//		tcp_ncsim_bounce_seg(sp, seg);
+
+		/* Reverse the identification */
+		tcp_ep2_flipped(epp, &rident);
+
+		/* Insert segment back into rqueue */
+		dseg = tcp_segment_dup(seg);
+		tcp_rqueue_insert_seg(&rident, dseg);
+		return;
+	}
+
+	if (tcp_pdu_encode(epp, seg, &pdu) != EOK) {
+		log_msg(LOG_DEFAULT, LVL_WARN, "Not enough memory. Segment dropped.");
+		return;
+	}
+
+	if (tcp_conn_lb == tcp_lb_pdu) {
+		/* Loop back PDU */
+		if (tcp_pdu_decode(pdu, &rident, &dseg) != EOK) {
+			log_msg(LOG_DEFAULT, LVL_WARN, "Not enough memory. Segment dropped.");
+			tcp_pdu_delete(pdu);
+			return;
+		}
+
+		tcp_pdu_delete(pdu);
+
+		/* Insert decoded segment into rqueue */
+		tcp_rqueue_insert_seg(&rident, dseg);
+		return;
+	}
+
+	tcp_transmit_pdu(pdu);
+	tcp_pdu_delete(pdu);
+}
+
 /** Compute flipped endpoint pair for response.
  *
@@ -1364,5 +1475,5 @@
  * @param seg		Incoming segment
  */
-void tcp_reply_rst(inet_ep2_t *epp, tcp_segment_t *seg)
+static void tcp_reply_rst(inet_ep2_t *epp, tcp_segment_t *seg)
 {
 	tcp_segment_t *rseg;
Index: uspace/srv/net/tcp/conn.h
===================================================================
--- uspace/srv/net/tcp/conn.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/tcp/conn.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -41,12 +41,11 @@
 
 extern int tcp_conns_init(void);
+extern void tcp_conns_fini(void);
 extern tcp_conn_t *tcp_conn_new(inet_ep2_t *);
 extern void tcp_conn_delete(tcp_conn_t *);
 extern int tcp_conn_add(tcp_conn_t *);
-extern void tcp_conn_remove(tcp_conn_t *);
 extern void tcp_conn_reset(tcp_conn_t *conn);
 extern void tcp_conn_sync(tcp_conn_t *);
 extern void tcp_conn_fin_sent(tcp_conn_t *);
-extern void tcp_conn_ack_of_fin_rcvd(tcp_conn_t *);
 extern tcp_conn_t *tcp_conn_find_ref(inet_ep2_t *);
 extern void tcp_conn_addref(tcp_conn_t *);
@@ -57,8 +56,8 @@
 extern void tcp_conn_segment_arrived(tcp_conn_t *, inet_ep2_t *,
     tcp_segment_t *);
-extern void tcp_conn_trim_seg_to_wnd(tcp_conn_t *, tcp_segment_t *);
 extern void tcp_unexpected_segment(inet_ep2_t *, tcp_segment_t *);
 extern void tcp_ep2_flipped(inet_ep2_t *, inet_ep2_t *);
-extern void tcp_reply_rst(inet_ep2_t *, tcp_segment_t *);
+
+extern tcp_lb_t tcp_conn_lb;
 
 #endif
Index: uspace/srv/net/tcp/inet.c
===================================================================
--- uspace/srv/net/tcp/inet.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/srv/net/tcp/inet.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2015 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 tcp
+ * @{
+ */
+
+/**
+ * @file TCP inet interfacing
+ */
+
+#include <bitops.h>
+#include <byteorder.h>
+#include <errno.h>
+#include <inet/inet.h>
+#include <mem.h>
+#include <io/log.h>
+#include <stdlib.h>
+
+#include "inet.h"
+#include "pdu.h"
+#include "rqueue.h"
+#include "std.h"
+
+#define NAME       "tcp"
+
+static int tcp_inet_ev_recv(inet_dgram_t *dgram);
+static void tcp_received_pdu(tcp_pdu_t *pdu);
+
+static inet_ev_ops_t tcp_inet_ev_ops = {
+	.recv = tcp_inet_ev_recv
+};
+
+/** Received datagram callback */
+static int tcp_inet_ev_recv(inet_dgram_t *dgram)
+{
+	uint8_t *pdu_raw;
+	size_t pdu_raw_size;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_inet_ev_recv()");
+
+	pdu_raw = dgram->data;
+	pdu_raw_size = dgram->size;
+
+	/* Split into header and payload. */
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_inet_ev_recv() - split header/payload");
+
+	tcp_pdu_t *pdu;
+	size_t hdr_size;
+	tcp_header_t *hdr;
+	uint32_t data_offset;
+
+	if (pdu_raw_size < sizeof(tcp_header_t)) {
+		log_msg(LOG_DEFAULT, LVL_WARN, "pdu_raw_size = %zu < sizeof(tcp_header_t) = %zu",
+		    pdu_raw_size, sizeof(tcp_header_t));
+		return EINVAL;
+	}
+
+	hdr = (tcp_header_t *)pdu_raw;
+	data_offset = BIT_RANGE_EXTRACT(uint32_t, DF_DATA_OFFSET_h, DF_DATA_OFFSET_l,
+	    uint16_t_be2host(hdr->doff_flags));
+
+	hdr_size = sizeof(uint32_t) * data_offset;
+
+	if (pdu_raw_size < hdr_size) {
+		log_msg(LOG_DEFAULT, LVL_WARN, "pdu_raw_size = %zu < hdr_size = %zu",
+		    pdu_raw_size, hdr_size);
+		return EINVAL;
+	}
+
+	if (hdr_size < sizeof(tcp_header_t)) {
+		log_msg(LOG_DEFAULT, LVL_WARN, "hdr_size = %zu < sizeof(tcp_header_t) = %zu",
+		    hdr_size, sizeof(tcp_header_t));		return EINVAL;
+	}
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "pdu_raw_size=%zu, hdr_size=%zu",
+	    pdu_raw_size, hdr_size);
+	pdu = tcp_pdu_create(pdu_raw, hdr_size, pdu_raw + hdr_size,
+	    pdu_raw_size - hdr_size);
+	if (pdu == NULL) {
+		log_msg(LOG_DEFAULT, LVL_WARN, "Failed creating PDU. Dropped.");
+		return ENOMEM;
+	}
+
+	pdu->src = dgram->src;
+	pdu->dest = dgram->dest;
+
+	tcp_received_pdu(pdu);
+	tcp_pdu_delete(pdu);
+
+	return EOK;
+}
+
+/** Transmit PDU over network layer. */
+void tcp_transmit_pdu(tcp_pdu_t *pdu)
+{
+	int rc;
+	uint8_t *pdu_raw;
+	size_t pdu_raw_size;
+	inet_dgram_t dgram;
+
+	pdu_raw_size = pdu->header_size + pdu->text_size;
+	pdu_raw = malloc(pdu_raw_size);
+	if (pdu_raw == NULL) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to transmit PDU. Out of memory.");
+		return;
+	}
+
+	memcpy(pdu_raw, pdu->header, pdu->header_size);
+	memcpy(pdu_raw + pdu->header_size, pdu->text,
+	    pdu->text_size);
+
+	dgram.iplink = 0;
+	dgram.src = pdu->src;
+	dgram.dest = pdu->dest;
+	dgram.tos = 0;
+	dgram.data = pdu_raw;
+	dgram.size = pdu_raw_size;
+
+	rc = inet_send(&dgram, INET_TTL_MAX, 0);
+	if (rc != EOK)
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to transmit PDU.");
+
+	free(pdu_raw);
+}
+
+/** Process received PDU. */
+static void tcp_received_pdu(tcp_pdu_t *pdu)
+{
+	tcp_segment_t *dseg;
+	inet_ep2_t rident;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_received_pdu()");
+
+	if (tcp_pdu_decode(pdu, &rident, &dseg) != EOK) {
+		log_msg(LOG_DEFAULT, LVL_WARN, "Not enough memory. PDU dropped.");
+		return;
+	}
+
+	/* Insert decoded segment into rqueue */
+	tcp_rqueue_insert_seg(&rident, dseg);
+}
+
+/** Initialize TCP inet interface. */
+int tcp_inet_init(void)
+{
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_inet_init()");
+
+	rc = inet_init(IP_PROTO_TCP, &tcp_inet_ev_ops);
+	if (rc != EOK) {
+		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed connecting to internet service.");
+		return ENOENT;
+	}
+
+	return EOK;
+}
+
+/**
+ * @}
+ */
Index: uspace/srv/net/tcp/inet.h
===================================================================
--- uspace/srv/net/tcp/inet.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/srv/net/tcp/inet.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015 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 tcp
+ * @{
+ */
+/** @file TCP inet interfacing
+ */
+
+#ifndef TCP_INET_H
+#define TCP_INET_H
+
+#include "tcp_type.h"
+
+extern int tcp_inet_init(void);
+extern void tcp_transmit_pdu(tcp_pdu_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/tcp/iqueue.c
===================================================================
--- uspace/srv/net/tcp/iqueue.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/tcp/iqueue.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -84,6 +84,8 @@
 		    tcp_iqueue_entry_t, link);
 
-		if (seq_no_seg_cmp(iqueue->conn, iqe->seg, qe->seg) >= 0)
+		if (seq_no_seg_cmp(iqueue->conn, iqe->seg, qe->seg) < 0)
 			break;
+
+		link = list_next(link, &iqueue->list);
 	}
 
@@ -92,4 +94,36 @@
 	else
 		list_append(&iqe->link, &iqueue->list);
+}
+
+/** Remove segment from incoming queue.
+ *
+ * @param iqueue	Incoming queue
+ * @param seg		Segment
+ */
+void tcp_iqueue_remove_seg(tcp_iqueue_t *iqueue, tcp_segment_t *seg)
+{
+	tcp_iqueue_entry_t *qe;
+	link_t *link;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "tcp_iqueue_remove_seg()");
+
+	link = list_first(&iqueue->list);
+	while (link != NULL) {
+			log_msg(LOG_DEFAULT, LVL_NOTE, "tcp_iqueue_remove_seg() - next");
+		qe = list_get_instance(link,
+		    tcp_iqueue_entry_t, link);
+
+		if (qe->seg == seg) {
+			log_msg(LOG_DEFAULT, LVL_NOTE, "tcp_iqueue_remove_seg() - found, DONE");
+			list_remove(&qe->link);
+			free(qe);
+			return;
+		}
+
+		link = list_next(link, &iqueue->list);
+	}
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "tcp_iqueue_remove_seg() - not found");
+	assert(false);
 }
 
Index: uspace/srv/net/tcp/iqueue.h
===================================================================
--- uspace/srv/net/tcp/iqueue.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/tcp/iqueue.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -40,4 +40,5 @@
 extern void tcp_iqueue_init(tcp_iqueue_t *, tcp_conn_t *);
 extern void tcp_iqueue_insert_seg(tcp_iqueue_t *, tcp_segment_t *);
+extern void tcp_iqueue_remove_seg(tcp_iqueue_t *, tcp_segment_t *);
 extern int tcp_iqueue_get_ready_seg(tcp_iqueue_t *, tcp_segment_t **);
 
Index: uspace/srv/net/tcp/ncsim.c
===================================================================
--- uspace/srv/net/tcp/ncsim.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/tcp/ncsim.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -73,8 +73,10 @@
 	tcp_squeue_entry_t *sqe;
 	tcp_squeue_entry_t *old_qe;
+	inet_ep2_t rident;
 	link_t *link;
 
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_ncsim_bounce_seg()");
-	tcp_rqueue_bounce_seg(epp, seg);
+	tcp_ep2_flipped(epp, &rident);
+	tcp_rqueue_insert_seg(&rident, seg);
 	return;
 
@@ -125,4 +127,5 @@
 	link_t *link;
 	tcp_squeue_entry_t *sqe;
+	inet_ep2_t rident;
 	int rc;
 
@@ -148,5 +151,6 @@
 
 		log_msg(LOG_DEFAULT, LVL_DEBUG, "NCSim - End Sleep");
-		tcp_rqueue_bounce_seg(&sqe->epp, sqe->seg);
+		tcp_ep2_flipped(&sqe->epp, &rident);
+		tcp_rqueue_insert_seg(&rident, sqe->seg);
 		free(sqe);
 	}
Index: uspace/srv/net/tcp/rqueue.c
===================================================================
--- uspace/srv/net/tcp/rqueue.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/tcp/rqueue.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -38,8 +38,9 @@
 #include <errno.h>
 #include <io/log.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <fibril.h>
+#include <fibril_synch.h>
 #include "conn.h"
-#include "pdu.h"
 #include "rqueue.h"
 #include "segment.h"
@@ -47,59 +48,32 @@
 #include "ucall.h"
 
-/** Transcode bounced segments.
- *
- * If defined, segments bounced via the internal debugging loopback will
- * be encoded to a PDU and the decoded. Otherwise they will be bounced back
- * directly without passing the encoder-decoder.
- */
-#define BOUNCE_TRANSCODE
-
 static prodcons_t rqueue;
+static bool fibril_active;
+static fibril_mutex_t lock;
+static fibril_condvar_t cv;
+static tcp_rqueue_cb_t *rqueue_cb;
 
 /** Initialize segment receive queue. */
-void tcp_rqueue_init(void)
+void tcp_rqueue_init(tcp_rqueue_cb_t *rcb)
 {
 	prodcons_initialize(&rqueue);
+	fibril_mutex_initialize(&lock);
+	fibril_condvar_initialize(&cv);
+	fibril_active = false;
+	rqueue_cb = rcb;
 }
 
-/** Bounce segment directy into receive queue without constructing the PDU.
- *
- * This is for testing purposes only.
- *
- * @param sp	Endpoint pair, oriented for transmission
- * @param seg	Segment
- */
-void tcp_rqueue_bounce_seg(inet_ep2_t *epp, tcp_segment_t *seg)
+/** Finalize segment receive queue. */
+void tcp_rqueue_fini(void)
 {
-	inet_ep2_t rident;
+	inet_ep2_t epp;
 
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_rqueue_bounce_seg()");
+	inet_ep2_init(&epp);
+	tcp_rqueue_insert_seg(&epp, NULL);
 
-#ifdef BOUNCE_TRANSCODE
-	tcp_pdu_t *pdu;
-	tcp_segment_t *dseg;
-
-	if (tcp_pdu_encode(epp, seg, &pdu) != EOK) {
-		log_msg(LOG_DEFAULT, LVL_WARN, "Not enough memory. Segment dropped.");
-		return;
-	}
-
-	if (tcp_pdu_decode(pdu, &rident, &dseg) != EOK) {
-		log_msg(LOG_DEFAULT, LVL_WARN, "Not enough memory. Segment dropped.");
-		return;
-	}
-
-	tcp_pdu_delete(pdu);
-
-	/** Insert decoded segment into rqueue */
-	tcp_rqueue_insert_seg(&rident, dseg);
-	tcp_segment_delete(seg);
-#else
-	/* Reverse the identification */
-	tcp_ep2_flipped(epp, &rident);
-
-	/* Insert segment back into rqueue */
-	tcp_rqueue_insert_seg(&rident, seg);
-#endif
+	fibril_mutex_lock(&lock);
+	while (fibril_active)
+		fibril_condvar_wait(&cv, &lock);
+	fibril_mutex_unlock(&lock);
 }
 
@@ -107,12 +81,14 @@
  *
  * @param epp	Endpoint pair, oriented for reception
- * @param seg	Segment
+ * @param seg	Segment (ownership transferred to rqueue)
  */
 void tcp_rqueue_insert_seg(inet_ep2_t *epp, tcp_segment_t *seg)
 {
 	tcp_rqueue_entry_t *rqe;
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_rqueue_insert_seg()");
 
-	tcp_segment_dump(seg);
+	log_msg(LOG_DEFAULT, LVL_DEBUG2, "tcp_rqueue_insert_seg()");
+
+	if (seg != NULL)
+		tcp_segment_dump(seg);
 
 	rqe = calloc(1, sizeof(tcp_rqueue_entry_t));
@@ -140,9 +116,21 @@
 		rqe = list_get_instance(link, tcp_rqueue_entry_t, link);
 
-		tcp_as_segment_arrived(&rqe->epp, rqe->seg);
+		if (rqe->seg == NULL) {
+			free(rqe);
+			break;
+		}
+
+		rqueue_cb->seg_received(&rqe->epp, rqe->seg);
 		free(rqe);
 	}
 
-	/* Not reached */
+	log_msg(LOG_DEFAULT, LVL_DEBUG2, "tcp_rqueue_fibril() exiting");
+
+	/* Finished */
+	fibril_mutex_lock(&lock);
+	fibril_active = false;
+	fibril_mutex_unlock(&lock);
+	fibril_condvar_broadcast(&cv);
+
 	return 0;
 }
@@ -162,4 +150,5 @@
 
 	fibril_add_ready(fid);
+	fibril_active = true;
 }
 
Index: uspace/srv/net/tcp/rqueue.h
===================================================================
--- uspace/srv/net/tcp/rqueue.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/tcp/rqueue.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -39,9 +39,8 @@
 #include "tcp_type.h"
 
-extern void tcp_rqueue_init(void);
-extern void tcp_rqueue_bounce_seg(inet_ep2_t *, tcp_segment_t *);
+extern void tcp_rqueue_init(tcp_rqueue_cb_t *);
+extern void tcp_rqueue_fibril_start(void);
+extern void tcp_rqueue_fini(void);
 extern void tcp_rqueue_insert_seg(inet_ep2_t *, tcp_segment_t *);
-extern void tcp_rqueue_handler(void *);
-extern void tcp_rqueue_fibril_start(void);
 
 
Index: uspace/srv/net/tcp/segment.c
===================================================================
--- uspace/srv/net/tcp/segment.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/tcp/segment.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -43,5 +43,5 @@
 
 /** Alocate new segment structure. */
-tcp_segment_t *tcp_segment_new(void)
+static tcp_segment_t *tcp_segment_new(void)
 {
 	return calloc(1, sizeof(tcp_segment_t));
Index: uspace/srv/net/tcp/segment.h
===================================================================
--- uspace/srv/net/tcp/segment.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/tcp/segment.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -40,5 +40,4 @@
 #include "tcp_type.h"
 
-extern tcp_segment_t *tcp_segment_new(void);
 extern void tcp_segment_delete(tcp_segment_t *);
 extern tcp_segment_t *tcp_segment_dup(tcp_segment_t *);
Index: uspace/srv/net/tcp/seq_no.c
===================================================================
--- uspace/srv/net/tcp/seq_no.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/tcp/seq_no.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -134,5 +134,12 @@
 }
 
-/** Determine whether segment is fully acked */
+/** Determine whether segment is fully acked.
+ *
+ * @param conn Connection
+ * @param seg  Segment
+ * @param ack  Last received ACK (i.e. SND.UNA)
+ *
+ * @return @c true if segment is fully acked, @c false otherwise
+ */
 bool seq_no_segment_acked(tcp_conn_t *conn, tcp_segment_t *seg, uint32_t ack)
 {
@@ -141,5 +148,9 @@
 }
 
-/** Determine whether initial SYN is acked */
+/** Determine whether initial SYN is acked.
+ *
+ * @param conn Connection
+ * @return @c true if initial SYN is acked, @c false otherwise
+ */
 bool seq_no_syn_acked(tcp_conn_t *conn)
 {
@@ -147,14 +158,30 @@
 }
 
-/** Determine whether segment overlaps the receive window */
+/** Determine whether segment overlaps the receive window.
+ *
+ * @param conn Connection
+ * @param seg  Segment
+ * @return @c true if segment overlaps the receive window, @c false otherwise
+ */
 bool seq_no_segment_acceptable(tcp_conn_t *conn, tcp_segment_t *seg)
 {
 	bool b_in, e_in;
-
+	bool wb_in, we_in;
+
+	/* Beginning of segment is inside window */
 	b_in = seq_no_le_lt(conn->rcv_nxt, seg->seq, conn->rcv_nxt
 	    + conn->rcv_wnd);
 
+	/* End of segment is inside window */
 	e_in = seq_no_le_lt(conn->rcv_nxt, seg->seq + seg->len - 1,
 	    conn->rcv_nxt + conn->rcv_wnd);
+
+	/* Beginning of window is inside segment */
+	wb_in = seq_no_le_lt(seg->seq, conn->rcv_nxt,
+	    seg->seq + seg->len);
+
+	/* End of window is inside segment */
+	we_in = seq_no_le_lt(seg->seq, conn->rcv_nxt + conn->rcv_wnd - 1,
+	    seg->seq + seg->len);
 
 	if (seg->len == 0 && conn->rcv_wnd == 0) {
@@ -165,9 +192,13 @@
 		return false;
 	} else {
-		return b_in || e_in;
-	}
-}
-
-/** Determine size that control bits occupy in sequence space. */
+		return b_in || e_in || wb_in || we_in;
+	}
+}
+
+/** Determine size that control bits occupy in sequence space.
+ *
+ * @param ctrl Control bits combination
+ * @return Number of sequence space units occupied
+ */
 uint32_t seq_no_control_len(tcp_control_t ctrl)
 {
@@ -183,5 +214,11 @@
 }
 
-/** Calculate the amount of trim needed to fit segment in receive window. */
+/** Calculate the amount of trim needed to fit segment in receive window.
+ *
+ * @param conn  Connection
+ * @param seg   Segment
+ * @param left  Place to store number of units to trim at the beginning
+ * @param right Place to store number of units to trim at the end
+ */
 void seq_no_seg_trim_calc(tcp_conn_t *conn, tcp_segment_t *seg,
     uint32_t *left, uint32_t *right)
Index: uspace/srv/net/tcp/service.c
===================================================================
--- uspace/srv/net/tcp/service.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/tcp/service.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -705,4 +705,7 @@
 			log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_impl() - EAGAIN");
 			return EAGAIN;
+		case TCP_ECLOSING:
+			*nrecv = 0;
+			return EOK;
 		default:
 			log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_conn_recv_impl() - trc=%d", rc);
Index: uspace/srv/net/tcp/tcp.c
===================================================================
--- uspace/srv/net/tcp/tcp.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/tcp/tcp.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -36,141 +36,22 @@
 
 #include <async.h>
-#include <bitops.h>
-#include <byteorder.h>
 #include <errno.h>
-#include <inet/inet.h>
 #include <io/log.h>
 #include <stdio.h>
-#include <stdlib.h>
 #include <task.h>
 
 #include "conn.h"
+#include "inet.h"
 #include "ncsim.h"
-#include "pdu.h"
 #include "rqueue.h"
 #include "service.h"
-#include "std.h"
-#include "tcp.h"
 #include "test.h"
+#include "ucall.h"
 
 #define NAME       "tcp"
 
-static int tcp_inet_ev_recv(inet_dgram_t *dgram);
-static void tcp_received_pdu(tcp_pdu_t *pdu);
-
-static inet_ev_ops_t tcp_inet_ev_ops = {
-	.recv = tcp_inet_ev_recv
+static tcp_rqueue_cb_t tcp_rqueue_cb = {
+	.seg_received = tcp_as_segment_arrived
 };
-
-/** Received datagram callback */
-static int tcp_inet_ev_recv(inet_dgram_t *dgram)
-{
-	uint8_t *pdu_raw;
-	size_t pdu_raw_size;
-
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_inet_ev_recv()");
-
-	pdu_raw = dgram->data;
-	pdu_raw_size = dgram->size;
-
-	/* Split into header and payload. */
-
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_inet_ev_recv() - split header/payload");
-
-	tcp_pdu_t *pdu;
-	size_t hdr_size;
-	tcp_header_t *hdr;
-	uint32_t data_offset;
-
-	if (pdu_raw_size < sizeof(tcp_header_t)) {
-		log_msg(LOG_DEFAULT, LVL_WARN, "pdu_raw_size = %zu < sizeof(tcp_header_t) = %zu",
-		    pdu_raw_size, sizeof(tcp_header_t));
-		return EINVAL;
-	}
-
-	hdr = (tcp_header_t *)pdu_raw;
-	data_offset = BIT_RANGE_EXTRACT(uint32_t, DF_DATA_OFFSET_h, DF_DATA_OFFSET_l,
-	    uint16_t_be2host(hdr->doff_flags));
-
-	hdr_size = sizeof(uint32_t) * data_offset;
-
-	if (pdu_raw_size < hdr_size) {
-		log_msg(LOG_DEFAULT, LVL_WARN, "pdu_raw_size = %zu < hdr_size = %zu",
-		    pdu_raw_size, hdr_size);
-		return EINVAL;
-	}
-
-	if (hdr_size < sizeof(tcp_header_t)) {
-		log_msg(LOG_DEFAULT, LVL_WARN, "hdr_size = %zu < sizeof(tcp_header_t) = %zu",
-		    hdr_size, sizeof(tcp_header_t));		return EINVAL;
-	}
-
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "pdu_raw_size=%zu, hdr_size=%zu",
-	    pdu_raw_size, hdr_size);
-	pdu = tcp_pdu_create(pdu_raw, hdr_size, pdu_raw + hdr_size,
-	    pdu_raw_size - hdr_size);
-	if (pdu == NULL) {
-		log_msg(LOG_DEFAULT, LVL_WARN, "Failed creating PDU. Dropped.");
-		return ENOMEM;
-	}
-
-	pdu->src = dgram->src;
-	pdu->dest = dgram->dest;
-
-	tcp_received_pdu(pdu);
-	tcp_pdu_delete(pdu);
-
-	return EOK;
-}
-
-/** Transmit PDU over network layer. */
-void tcp_transmit_pdu(tcp_pdu_t *pdu)
-{
-	int rc;
-	uint8_t *pdu_raw;
-	size_t pdu_raw_size;
-	inet_dgram_t dgram;
-
-	pdu_raw_size = pdu->header_size + pdu->text_size;
-	pdu_raw = malloc(pdu_raw_size);
-	if (pdu_raw == NULL) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to transmit PDU. Out of memory.");
-		return;
-	}
-
-	memcpy(pdu_raw, pdu->header, pdu->header_size);
-	memcpy(pdu_raw + pdu->header_size, pdu->text,
-	    pdu->text_size);
-
-	dgram.iplink = 0;
-	dgram.src = pdu->src;
-	dgram.dest = pdu->dest;
-	dgram.tos = 0;
-	dgram.data = pdu_raw;
-	dgram.size = pdu_raw_size;
-
-	rc = inet_send(&dgram, INET_TTL_MAX, 0);
-	if (rc != EOK)
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed to transmit PDU.");
-
-	free(pdu_raw);
-}
-
-/** Process received PDU. */
-static void tcp_received_pdu(tcp_pdu_t *pdu)
-{
-	tcp_segment_t *dseg;
-	inet_ep2_t rident;
-
-	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_received_pdu()");
-
-	if (tcp_pdu_decode(pdu, &rident, &dseg) != EOK) {
-		log_msg(LOG_DEFAULT, LVL_WARN, "Not enough memory. PDU dropped.");
-		return;
-	}
-
-	/* Insert decoded segment into rqueue */
-	tcp_rqueue_insert_seg(&rident, dseg);
-}
 
 static int tcp_init(void)
@@ -187,5 +68,5 @@
 	}
 
-	tcp_rqueue_init();
+	tcp_rqueue_init(&tcp_rqueue_cb);
 	tcp_rqueue_fibril_start();
 
@@ -195,9 +76,7 @@
 	if (0) tcp_test();
 
-	rc = inet_init(IP_PROTO_TCP, &tcp_inet_ev_ops);
-	if (rc != EOK) {
-		log_msg(LOG_DEFAULT, LVL_ERROR, "Failed connecting to internet service.");
+	rc = tcp_inet_init();
+	if (rc != EOK)
 		return ENOENT;
-	}
 
 	rc = tcp_service_init();
Index: pace/srv/net/tcp/tcp.h
===================================================================
--- uspace/srv/net/tcp/tcp.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ 	(revision )
@@ -1,46 +1,0 @@
-/*
- * 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 tcp
- * @{
- */
-/** @file TCP (Transmission Control Protocol) network module
- */
-
-#ifndef TCP_H
-#define TCP_H
-
-#include <async.h>
-#include "tcp_type.h"
-
-extern void tcp_transmit_pdu(tcp_pdu_t *);
-
-#endif
-
-/** @}
- */
Index: uspace/srv/net/tcp/tcp_type.h
===================================================================
--- uspace/srv/net/tcp/tcp_type.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/tcp/tcp_type.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -126,13 +126,4 @@
 } tcp_iqueue_t;
 
-/** Retransmission queue */
-typedef struct {
-	struct tcp_conn *conn;
-	list_t list;
-
-	/** Retransmission timer */
-	fibril_timer_t *timer;
-} tcp_tqueue_t;
-
 /** Active or passive connection */
 typedef enum {
@@ -156,4 +147,83 @@
 	void (*recv_data)(tcp_conn_t *, void *);
 } tcp_cb_t;
+
+/** Data returned by Status user call */
+typedef struct {
+	/** Connection state */
+	tcp_cstate_t cstate;
+} tcp_conn_status_t;
+
+typedef struct {
+	/** SYN, FIN */
+	tcp_control_t ctrl;
+
+	/** Segment sequence number */
+	uint32_t seq;
+	/** Segment acknowledgement number */
+	uint32_t ack;
+	/** Segment length in sequence space */
+	uint32_t len;
+	/** Segment window */
+	uint32_t wnd;
+	/** Segment urgent pointer */
+	uint32_t up;
+
+	/** Segment data, may be moved when trimming segment */
+	void *data;
+	/** Segment data, original pointer used to free data */
+	void *dfptr;
+} tcp_segment_t;
+
+/** Receive queue entry */
+typedef struct {
+	link_t link;
+	inet_ep2_t epp;
+	tcp_segment_t *seg;
+} tcp_rqueue_entry_t;
+
+/** Receive queue callbacks */
+typedef struct {
+	/** Segment received */
+	void (*seg_received)(inet_ep2_t *, tcp_segment_t *);
+} tcp_rqueue_cb_t;
+
+/** NCSim queue entry */
+typedef struct {
+	link_t link;
+	suseconds_t delay;
+	inet_ep2_t epp;
+	tcp_segment_t *seg;
+} tcp_squeue_entry_t;
+
+/** Incoming queue entry */
+typedef struct {
+	link_t link;
+	tcp_segment_t *seg;
+} tcp_iqueue_entry_t;
+
+/** Retransmission queue entry */
+typedef struct {
+	link_t link;
+	tcp_conn_t *conn;
+	tcp_segment_t *seg;
+} tcp_tqueue_entry_t;
+
+/** Retransmission queue callbacks */
+typedef struct {
+	/** Segment received */
+	void (*transmit_seg)(inet_ep2_t *, tcp_segment_t *);
+} tcp_tqueue_cb_t;
+
+/** Retransmission queue */
+typedef struct {
+	struct tcp_conn *conn;
+	list_t list;
+
+	/** Retransmission timer */
+	fibril_timer_t *timer;
+
+	/** Callbacks */
+	tcp_tqueue_cb_t *cb;
+} tcp_tqueue_t;
 
 /** Connection */
@@ -169,4 +239,6 @@
 	/** Connection identification (local and remote endpoint) */
 	inet_ep2_t ident;
+	/** Connection is in association map */
+	bool mapped;
 
 	/** Active or passive connection */
@@ -246,59 +318,4 @@
 };
 
-/** Data returned by Status user call */
-typedef struct {
-	/** Connection state */
-	tcp_cstate_t cstate;
-} tcp_conn_status_t;
-
-typedef struct {
-	/** SYN, FIN */
-	tcp_control_t ctrl;
-
-	/** Segment sequence number */
-	uint32_t seq;
-	/** Segment acknowledgement number */
-	uint32_t ack;
-	/** Segment length in sequence space */
-	uint32_t len;
-	/** Segment window */
-	uint32_t wnd;
-	/** Segment urgent pointer */
-	uint32_t up;
-
-	/** Segment data, may be moved when trimming segment */
-	void *data;
-	/** Segment data, original pointer used to free data */
-	void *dfptr;
-} tcp_segment_t;
-
-/** Receive queue entry */
-typedef struct {
-	link_t link;
-	inet_ep2_t epp;
-	tcp_segment_t *seg;
-} tcp_rqueue_entry_t;
-
-/** NCSim queue entry */
-typedef struct {
-	link_t link;
-	suseconds_t delay;
-	inet_ep2_t epp;
-	tcp_segment_t *seg;
-} tcp_squeue_entry_t;
-
-/** Incoming queue entry */
-typedef struct {
-	link_t link;
-	tcp_segment_t *seg;
-} tcp_iqueue_entry_t;
-
-/** Retransmission queue entry */
-typedef struct {
-	link_t link;
-	tcp_conn_t *conn;
-	tcp_segment_t *seg;
-} tcp_tqueue_entry_t;
-
 /** Continuation of processing.
  *
@@ -354,5 +371,5 @@
 /** TCP client */
 typedef struct tcp_client {
-	/** Client callbac session */
+	/** Client callback session */
 	async_sess_t *sess;
 	/** Client's connections */
@@ -362,4 +379,14 @@
 } tcp_client_t;
 
+/** Internal loopback type */
+typedef enum {
+	/** No loopback */
+	tcp_lb_none,
+	/** Segment loopback */
+	tcp_lb_segment,
+	/** PDU loopback */
+	tcp_lb_pdu
+} tcp_lb_t;
+
 #endif
 
Index: uspace/srv/net/tcp/test/conn.c
===================================================================
--- uspace/srv/net/tcp/test/conn.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/srv/net/tcp/test/conn.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#include <errno.h>
+#include <inet/endpoint.h>
+#include <io/log.h>
+#include <pcut/pcut.h>
+
+#include "../conn.h"
+#include "../rqueue.h"
+#include "../ucall.h"
+
+PCUT_INIT
+
+PCUT_TEST_SUITE(conn);
+
+static tcp_rqueue_cb_t test_rqueue_cb = {
+	.seg_received = tcp_as_segment_arrived
+};
+
+PCUT_TEST_BEFORE
+{
+	int rc;
+
+	/* We will be calling functions that perform logging */
+	rc = log_init("test-tcp");
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = tcp_conns_init();
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	tcp_rqueue_init(&test_rqueue_cb);
+	tcp_rqueue_fibril_start();
+
+	/* Enable internal loopback */
+	tcp_conn_lb = tcp_lb_segment;
+}
+
+PCUT_TEST_AFTER
+{
+	tcp_rqueue_fini();
+	tcp_conns_fini();
+}
+
+/** Test creating and deleting connection */
+PCUT_TEST(new_delete)
+{
+	tcp_conn_t *conn;
+	inet_ep2_t epp;
+
+	inet_ep2_init(&epp);
+	conn = tcp_conn_new(&epp);
+	PCUT_ASSERT_NOT_NULL(conn);
+
+	tcp_conn_lock(conn);
+	tcp_conn_reset(conn);
+	tcp_conn_unlock(conn);
+	tcp_conn_delete(conn);
+}
+
+/** Test adding, finding and deleting a connection */
+PCUT_TEST(add_find_delete)
+{
+	tcp_conn_t *conn, *cfound;
+	inet_ep2_t epp;
+	int rc;
+
+	inet_ep2_init(&epp);
+
+	conn = tcp_conn_new(&epp);
+	PCUT_ASSERT_NOT_NULL(conn);
+
+	rc = tcp_conn_add(conn);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	/* Find the connection */
+	cfound = tcp_conn_find_ref(&conn->ident);
+	PCUT_ASSERT_EQUALS(conn, cfound);
+	tcp_conn_delref(cfound);
+
+	/* We should have been assigned a port address, should not match */
+	cfound = tcp_conn_find_ref(&epp);
+	PCUT_ASSERT_EQUALS(NULL, cfound);
+
+	tcp_conn_lock(conn);
+	tcp_conn_reset(conn);
+	tcp_conn_unlock(conn);
+	tcp_conn_delete(conn);
+}
+
+/** Test trying to connect to endpoint that sends RST back */
+PCUT_TEST(connect_rst)
+{
+	tcp_conn_t *conn;
+	inet_ep2_t epp;
+	int rc;
+
+	inet_ep2_init(&epp);
+	inet_addr(&epp.local.addr, 127, 0, 0, 1);
+	inet_addr(&epp.remote.addr, 127, 0, 0, 1);
+	epp.remote.port = inet_port_user_lo;
+
+	conn = tcp_conn_new(&epp);
+	PCUT_ASSERT_NOT_NULL(conn);
+
+	rc = tcp_conn_add(conn);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_INT_EQUALS(st_listen, conn->cstate);
+
+	tcp_conn_lock(conn);
+	tcp_conn_sync(conn);
+	PCUT_ASSERT_INT_EQUALS(st_syn_sent, conn->cstate);
+
+	while (conn->cstate == st_syn_sent)
+                fibril_condvar_wait(&conn->cstate_cv, &conn->lock);
+
+	PCUT_ASSERT_INT_EQUALS(st_closed, conn->cstate);
+
+	tcp_conn_unlock(conn);
+	tcp_conn_delete(conn);
+}
+
+/** Test establishing a connection */
+PCUT_TEST(conn_establish)
+{
+	tcp_conn_t *cconn, *sconn;
+	inet_ep2_t cepp, sepp;
+	int rc;
+
+	/* Client EPP */
+	inet_ep2_init(&cepp);
+	inet_addr(&cepp.local.addr, 127, 0, 0, 1);
+	inet_addr(&cepp.remote.addr, 127, 0, 0, 1);
+	cepp.remote.port = inet_port_user_lo;
+
+	/* Server EPP */
+	inet_ep2_init(&sepp);
+	inet_addr(&sepp.local.addr, 127, 0, 0, 1);
+	sepp.local.port = inet_port_user_lo;
+
+	/* Client side of the connection */
+	cconn = tcp_conn_new(&cepp);
+	PCUT_ASSERT_NOT_NULL(cconn);
+
+	rc = tcp_conn_add(cconn);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_INT_EQUALS(st_listen, cconn->cstate);
+	PCUT_ASSERT_FALSE(tcp_conn_got_syn(cconn));
+
+	/* Server side of the connection */
+	sconn = tcp_conn_new(&sepp);
+	PCUT_ASSERT_NOT_NULL(sconn);
+
+	rc = tcp_conn_add(sconn);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	PCUT_ASSERT_INT_EQUALS(st_listen, sconn->cstate);
+	PCUT_ASSERT_FALSE(tcp_conn_got_syn(sconn));
+
+	/* Start establishing the connection */
+
+	tcp_conn_lock(cconn);
+	tcp_conn_sync(cconn);
+	PCUT_ASSERT_INT_EQUALS(st_syn_sent, cconn->cstate);
+	PCUT_ASSERT_FALSE(tcp_conn_got_syn(cconn));
+
+	/* Wait for client-side state to transition */
+	while (cconn->cstate == st_syn_sent)
+                fibril_condvar_wait(&cconn->cstate_cv, &cconn->lock);
+
+	PCUT_ASSERT_INT_EQUALS(st_established, cconn->cstate);
+	PCUT_ASSERT_TRUE(tcp_conn_got_syn(cconn));
+	tcp_conn_unlock(cconn);
+
+	/* Wait for server-side state to transition */
+	tcp_conn_lock(sconn);
+	while (sconn->cstate == st_listen || sconn->cstate == st_syn_received)
+                fibril_condvar_wait(&sconn->cstate_cv, &sconn->lock);
+
+	PCUT_ASSERT_INT_EQUALS(st_established, sconn->cstate);
+	PCUT_ASSERT_TRUE(tcp_conn_got_syn(sconn));
+
+	/* Verify counters */
+	PCUT_ASSERT_EQUALS(cconn->iss + 1, cconn->snd_nxt);
+	PCUT_ASSERT_EQUALS(cconn->iss + 1, cconn->snd_una);
+	PCUT_ASSERT_EQUALS(sconn->iss + 1, sconn->snd_nxt);
+	PCUT_ASSERT_EQUALS(sconn->iss + 1, sconn->snd_una);
+
+	tcp_conn_unlock(sconn);
+
+	tcp_conn_lock(cconn);
+	tcp_conn_reset(cconn);
+	tcp_conn_unlock(cconn);
+	tcp_conn_delete(cconn);
+
+	tcp_conn_lock(sconn);
+	tcp_conn_reset(sconn);
+	tcp_conn_unlock(sconn);
+	tcp_conn_delete(sconn);
+}
+
+PCUT_TEST(ep2_flipped)
+{
+	inet_ep2_t a, fa;
+
+	inet_addr(&a.local.addr, 1, 2, 3, 4);
+	a.local.port = 1234;
+	inet_addr(&a.remote.addr, 5, 6, 7, 8);
+	a.remote.port = 5678;
+
+	tcp_ep2_flipped(&a, &fa);
+
+	PCUT_ASSERT_INT_EQUALS(a.local.port, fa.remote.port);
+	PCUT_ASSERT_INT_EQUALS(a.remote.port, fa.local.port);
+
+	PCUT_ASSERT_TRUE(inet_addr_compare(&a.local.addr, &fa.remote.addr));
+	PCUT_ASSERT_TRUE(inet_addr_compare(&a.remote.addr, &fa.local.addr));
+}
+
+PCUT_EXPORT(conn);
Index: uspace/srv/net/tcp/test/iqueue.c
===================================================================
--- uspace/srv/net/tcp/test/iqueue.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/srv/net/tcp/test/iqueue.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#include <inet/endpoint.h>
+#include <pcut/pcut.h>
+
+#include "../conn.h"
+#include "../iqueue.h"
+#include "../segment.h"
+
+PCUT_INIT
+
+PCUT_TEST_SUITE(iqueue);
+
+/** Test empty queue */
+PCUT_TEST(empty_queue)
+{
+	tcp_conn_t *conn;
+	tcp_iqueue_t iqueue;
+	inet_ep2_t epp;
+	tcp_segment_t *rseg;
+	int rc;
+
+	inet_ep2_init(&epp);
+	conn = tcp_conn_new(&epp);
+	PCUT_ASSERT_NOT_NULL(conn);
+
+	conn->rcv_nxt = 10;
+	conn->rcv_wnd = 20;
+
+	tcp_iqueue_init(&iqueue, conn);
+	rc = tcp_iqueue_get_ready_seg(&iqueue, &rseg);
+	PCUT_ASSERT_ERRNO_VAL(ENOENT, rc);
+
+	tcp_conn_delete(conn);
+}
+
+/** Test iqeue with one segment */
+PCUT_TEST(one_segment)
+{
+	tcp_conn_t *conn;
+	tcp_iqueue_t iqueue;
+	inet_ep2_t epp;
+	tcp_segment_t *rseg;
+	tcp_segment_t *seg;
+	void *data;
+	size_t dsize;
+	int rc;
+
+	inet_ep2_init(&epp);
+	conn = tcp_conn_new(&epp);
+	PCUT_ASSERT_NOT_NULL(conn);
+
+	conn->rcv_nxt = 10;
+	conn->rcv_wnd = 20;
+
+	dsize = 15;
+	data = calloc(dsize, 1);
+	PCUT_ASSERT_NOT_NULL(data);
+
+	seg = tcp_segment_make_data(0, data, dsize);
+	PCUT_ASSERT_NOT_NULL(seg);
+
+	tcp_iqueue_init(&iqueue, conn);
+	rc = tcp_iqueue_get_ready_seg(&iqueue, &rseg);
+	PCUT_ASSERT_ERRNO_VAL(ENOENT, rc);
+
+	seg->seq = 10;
+	tcp_iqueue_insert_seg(&iqueue, seg);
+	rc = tcp_iqueue_get_ready_seg(&iqueue, &rseg);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	seg->seq = 15;
+	tcp_iqueue_insert_seg(&iqueue, seg);
+	rc = tcp_iqueue_get_ready_seg(&iqueue, &rseg);
+	PCUT_ASSERT_ERRNO_VAL(ENOENT, rc);
+	tcp_iqueue_remove_seg(&iqueue, seg);
+
+	tcp_segment_delete(seg);
+	free(data);
+	tcp_conn_delete(conn);
+}
+
+/** Test iqeue with two segments */
+PCUT_TEST(two_segments)
+{
+	tcp_conn_t *conn;
+	tcp_iqueue_t iqueue;
+	inet_ep2_t epp;
+	tcp_segment_t *rseg;
+	tcp_segment_t *seg1, *seg2;
+	void *data;
+	size_t dsize;
+	int rc;
+
+	inet_ep2_init(&epp);
+	conn = tcp_conn_new(&epp);
+	PCUT_ASSERT_NOT_NULL(conn);
+
+	conn->rcv_nxt = 10;
+	conn->rcv_wnd = 20;
+
+	dsize = 15;
+	data = calloc(dsize, 1);
+	PCUT_ASSERT_NOT_NULL(data);
+
+	seg1 = tcp_segment_make_data(0, data, dsize);
+	PCUT_ASSERT_NOT_NULL(seg1);
+	seg2 = tcp_segment_make_data(0, data, dsize);
+	PCUT_ASSERT_NOT_NULL(seg2);
+
+	tcp_iqueue_init(&iqueue, conn);
+	rc = tcp_iqueue_get_ready_seg(&iqueue, &rseg);
+	PCUT_ASSERT_ERRNO_VAL(ENOENT, rc);
+
+	/* Test reception in ascending order */
+	seg1->seq = 5;
+	tcp_iqueue_insert_seg(&iqueue, seg1);
+	seg2->seq = 10;
+	tcp_iqueue_insert_seg(&iqueue, seg2);
+
+	rc = tcp_iqueue_get_ready_seg(&iqueue, &rseg);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_TRUE(rseg == seg1);
+
+	rc = tcp_iqueue_get_ready_seg(&iqueue, &rseg);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_TRUE(rseg == seg2);
+
+	/* Test reception in descending order */
+	seg1->seq = 10;
+	tcp_iqueue_insert_seg(&iqueue, seg1);
+	seg2->seq = 5;
+	tcp_iqueue_insert_seg(&iqueue, seg2);
+
+	rc = tcp_iqueue_get_ready_seg(&iqueue, &rseg);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_EQUALS(seg2, rseg);
+
+	rc = tcp_iqueue_get_ready_seg(&iqueue, &rseg);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	PCUT_ASSERT_EQUALS(seg1, rseg);
+
+	rc = tcp_iqueue_get_ready_seg(&iqueue, &rseg);
+	PCUT_ASSERT_ERRNO_VAL(ENOENT, rc);
+
+	tcp_segment_delete(seg1);
+	tcp_segment_delete(seg2);
+	free(data);
+	tcp_conn_delete(conn);
+}
+
+
+PCUT_EXPORT(iqueue);
Index: uspace/srv/net/tcp/test/main.c
===================================================================
--- uspace/srv/net/tcp/test/main.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/srv/net/tcp/test/main.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2017 Jiri Svobda
+ * 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.
+ */
+
+#include <mem.h>
+#include <pcut/pcut.h>
+
+#include "main.h"
+#include "../segment.h"
+#include "../tcp_type.h"
+
+/** Verify that two segments have the same content */
+void test_seg_same(tcp_segment_t *a, tcp_segment_t *b)
+{
+	PCUT_ASSERT_INT_EQUALS(a->ctrl, b->ctrl);
+	PCUT_ASSERT_INT_EQUALS(a->seq, b->seq);
+	PCUT_ASSERT_INT_EQUALS(a->ack, b->ack);
+	PCUT_ASSERT_INT_EQUALS(a->len, b->len);
+	PCUT_ASSERT_INT_EQUALS(a->wnd, b->wnd);
+	PCUT_ASSERT_INT_EQUALS(a->up, b->up);
+	PCUT_ASSERT_INT_EQUALS(tcp_segment_text_size(a),
+	    tcp_segment_text_size(b));
+	if (tcp_segment_text_size(a) != 0)
+		PCUT_ASSERT_NOT_NULL(a->data);
+	if (tcp_segment_text_size(b) != 0)
+		PCUT_ASSERT_NOT_NULL(b->data);
+	if (tcp_segment_text_size(a) != 0) {
+		PCUT_ASSERT_INT_EQUALS(0, memcmp(a->data, b->data,
+		    tcp_segment_text_size(a)));
+	}
+}
+
+PCUT_INIT
+
+PCUT_IMPORT(conn);
+PCUT_IMPORT(iqueue);
+PCUT_IMPORT(pdu);
+PCUT_IMPORT(rqueue);
+PCUT_IMPORT(segment);
+PCUT_IMPORT(seq_no);
+PCUT_IMPORT(tqueue);
+PCUT_IMPORT(ucall);
+
+PCUT_MAIN()
Index: uspace/srv/net/tcp/test/main.h
===================================================================
--- uspace/srv/net/tcp/test/main.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/srv/net/tcp/test/main.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2017 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 tcp
+ * @{
+ */
+/** @file
+ */
+
+#ifndef TEST_MAIN_H
+#define TEST_MAIN_H
+
+#include "../tcp_type.h"
+
+extern void test_seg_same(tcp_segment_t *, tcp_segment_t *);
+
+#endif
+
+/** @}
+ */
Index: uspace/srv/net/tcp/test/pdu.c
===================================================================
--- uspace/srv/net/tcp/test/pdu.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/srv/net/tcp/test/pdu.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#include <errno.h>
+#include <inet/endpoint.h>
+#include <mem.h>
+#include <pcut/pcut.h>
+#include <stdlib.h>
+
+#include "main.h"
+#include "../pdu.h"
+#include "../segment.h"
+
+PCUT_INIT
+
+PCUT_TEST_SUITE(pdu);
+
+/** Test encode/decode round trip for control PDU */
+PCUT_TEST(encdec_syn)
+{
+	tcp_segment_t *seg, *dseg;
+	tcp_pdu_t *pdu;
+	inet_ep2_t epp, depp;
+	int rc;
+
+	inet_ep2_init(&epp);
+	inet_addr(&epp.local.addr, 1, 2, 3, 4);
+	inet_addr(&epp.remote.addr, 5, 6, 7, 8);
+
+	seg = tcp_segment_make_ctrl(CTL_SYN);
+	PCUT_ASSERT_NOT_NULL(seg);
+
+	seg->seq = 20;
+	seg->ack = 19;
+	seg->wnd = 18;
+	seg->up = 17;
+
+	rc = tcp_pdu_encode(&epp, seg, &pdu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	rc = tcp_pdu_decode(pdu, &depp, &dseg);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	test_seg_same(seg, dseg);
+	tcp_segment_delete(seg);
+}
+
+/** Test encode/decode round trip for data PDU */
+PCUT_TEST(encdec_data)
+{
+	tcp_segment_t *seg, *dseg;
+	tcp_pdu_t *pdu;
+	inet_ep2_t epp, depp;
+	uint8_t *data;
+	size_t i, dsize;
+	int rc;
+
+	inet_ep2_init(&epp);
+	inet_addr(&epp.local.addr, 1, 2, 3, 4);
+	inet_addr(&epp.remote.addr, 5, 6, 7, 8);
+
+	dsize = 15;
+	data = malloc(dsize);
+	PCUT_ASSERT_NOT_NULL(data);
+
+	for (i = 0; i < dsize; i++)
+		data[i] = (uint8_t) i;
+
+	seg = tcp_segment_make_data(CTL_SYN, data, dsize);
+	PCUT_ASSERT_NOT_NULL(seg);
+
+	seg->seq = 20;
+	seg->ack = 19;
+	seg->wnd = 18;
+	seg->up = 17;
+
+	rc = tcp_pdu_encode(&epp, seg, &pdu);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+	rc = tcp_pdu_decode(pdu, &depp, &dseg);
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	test_seg_same(seg, dseg);
+	tcp_segment_delete(seg);
+	free(data);
+}
+
+PCUT_EXPORT(pdu);
Index: uspace/srv/net/tcp/test/rqueue.c
===================================================================
--- uspace/srv/net/tcp/test/rqueue.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/srv/net/tcp/test/rqueue.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#include <adt/prodcons.h>
+#include <inet/endpoint.h>
+#include <io/log.h>
+#include <pcut/pcut.h>
+
+#include "../rqueue.h"
+#include "../segment.h"
+
+PCUT_INIT
+
+PCUT_TEST_SUITE(rqueue);
+
+enum {
+	test_seg_max = 10
+};
+
+static void test_seg_received(inet_ep2_t *, tcp_segment_t *);
+
+static tcp_rqueue_cb_t rcb = {
+	.seg_received = test_seg_received
+};
+
+static int seg_cnt;
+static tcp_segment_t *recv_seg[test_seg_max];
+
+static void test_seg_received(inet_ep2_t *epp, tcp_segment_t *seg)
+{
+	recv_seg[seg_cnt++] = seg;
+}
+
+PCUT_TEST_BEFORE
+{
+	int rc;
+
+	/* We will be calling functions that perform logging */
+	rc = log_init("test-tcp");
+	PCUT_ASSERT_INT_EQUALS(EOK, rc);
+}
+
+/** Test empty queue */
+PCUT_TEST(init_fini)
+{
+	tcp_rqueue_init(&rcb);
+	tcp_rqueue_fibril_start();
+	tcp_rqueue_fini();
+}
+
+/** Test one segment */
+PCUT_TEST(one_segment)
+{
+	tcp_segment_t *seg;
+	inet_ep2_t epp;
+
+	tcp_rqueue_init(&rcb);
+	seg_cnt = 0;
+
+	seg = tcp_segment_make_ctrl(CTL_SYN);
+	PCUT_ASSERT_NOT_NULL(seg);
+	inet_ep2_init(&epp);
+
+	tcp_rqueue_insert_seg(&epp, seg);
+	tcp_rqueue_fibril_start();
+	tcp_rqueue_fini();
+
+	PCUT_ASSERT_INT_EQUALS(1, seg_cnt);
+	PCUT_ASSERT_EQUALS(seg, recv_seg[0]);
+
+	tcp_segment_delete(seg);
+}
+
+/** Test multiple segments */
+PCUT_TEST(multiple_segments)
+{
+	tcp_segment_t *seg[test_seg_max];
+	inet_ep2_t epp;
+	int i;
+
+	tcp_rqueue_init(&rcb);
+	seg_cnt = 0;
+
+    	inet_ep2_init(&epp);
+
+	tcp_rqueue_fibril_start();
+
+	for (i = 0; i < test_seg_max; i++) {
+		seg[i] = tcp_segment_make_ctrl(CTL_ACK);
+		PCUT_ASSERT_NOT_NULL(seg[i]);
+		tcp_rqueue_insert_seg(&epp, seg[i]);
+	}
+
+	tcp_rqueue_fini();
+
+	PCUT_ASSERT_INT_EQUALS(test_seg_max, seg_cnt);
+	for (i = 0; i < test_seg_max; i++) {
+		PCUT_ASSERT_EQUALS(seg[i], recv_seg[i]);
+		tcp_segment_delete(seg[i]);
+	}
+
+}
+
+PCUT_EXPORT(rqueue);
Index: uspace/srv/net/tcp/test/segment.c
===================================================================
--- uspace/srv/net/tcp/test/segment.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/srv/net/tcp/test/segment.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#include <pcut/pcut.h>
+
+#include "main.h"
+#include "../segment.h"
+
+PCUT_INIT
+
+PCUT_TEST_SUITE(segment);
+
+/** Test create/destroy control segment */
+PCUT_TEST(ctrl_seg_mkdel)
+{
+	tcp_segment_t *seg;
+
+	seg = tcp_segment_make_ctrl(CTL_SYN);
+	PCUT_ASSERT_NOT_NULL(seg);
+	tcp_segment_delete(seg);
+}
+
+/** Test create/destroy data segment */
+PCUT_TEST(data_seg_mkdel)
+{
+	tcp_segment_t *seg;
+	uint8_t *data;
+	size_t i, dsize;
+
+	dsize = 15;
+	data = malloc(dsize);
+	PCUT_ASSERT_NOT_NULL(data);
+
+	for (i = 0; i < dsize; i++)
+		data[i] = (uint8_t) i;
+
+	seg = tcp_segment_make_data(CTL_SYN, data, dsize);
+	PCUT_ASSERT_NOT_NULL(seg);
+
+	tcp_segment_delete(seg);
+	free(data);
+}
+
+/** Test create/duplicate/destroy control segment */
+PCUT_TEST(ctrl_seg_dup)
+{
+	tcp_segment_t *seg, *dup;
+
+	seg = tcp_segment_make_ctrl(CTL_SYN);
+	PCUT_ASSERT_NOT_NULL(seg);
+
+	seg->seq = 20;
+	seg->ack = 19;
+	seg->wnd = 18;
+	seg->up = 17;
+
+	dup = tcp_segment_dup(seg);
+	test_seg_same(seg, dup);
+
+	tcp_segment_delete(seg);
+	tcp_segment_delete(dup);
+}
+
+/** Test create/duplicate/destroy data segment */
+PCUT_TEST(data_seg_dup)
+{
+	tcp_segment_t *seg, *dup;
+	uint8_t *data;
+	size_t i, dsize;
+
+	dsize = 15;
+	data = malloc(dsize);
+	PCUT_ASSERT_NOT_NULL(data);
+
+	for (i = 0; i < dsize; i++)
+		data[i] = (uint8_t) i;
+
+	seg = tcp_segment_make_data(CTL_SYN, data, dsize);
+	PCUT_ASSERT_NOT_NULL(seg);
+
+	seg->seq = 20;
+	seg->ack = 19;
+	seg->wnd = 18;
+	seg->up = 17;
+
+	dup = tcp_segment_dup(seg);
+	test_seg_same(seg, dup);
+
+	tcp_segment_delete(seg);
+	tcp_segment_delete(dup);
+	free(data);
+}
+
+/** Test reset segment for segment with ACK not set */
+PCUT_TEST(noack_seg_rst)
+{
+	tcp_segment_t *seg, *rst;
+
+	seg = tcp_segment_make_ctrl(CTL_SYN);
+	PCUT_ASSERT_NOT_NULL(seg);
+
+	seg->seq = 20;
+	seg->ack = 19;
+	seg->wnd = 18;
+	seg->up = 17;
+
+	rst = tcp_segment_make_rst(seg);
+	PCUT_ASSERT_NOT_NULL(seg);
+
+	tcp_segment_delete(seg);
+	tcp_segment_delete(rst);
+}
+
+/** Test reset segment for segment with ACK set */
+PCUT_TEST(ack_seg_rst)
+{
+	tcp_segment_t *seg, *rst;
+
+	seg = tcp_segment_make_ctrl(CTL_SYN | CTL_ACK);
+	PCUT_ASSERT_NOT_NULL(seg);
+
+	seg->seq = 20;
+	seg->ack = 19;
+	seg->wnd = 18;
+	seg->up = 17;
+
+	rst = tcp_segment_make_rst(seg);
+	PCUT_ASSERT_NOT_NULL(seg);
+
+	tcp_segment_delete(seg);
+	tcp_segment_delete(rst);
+}
+
+/** Test copying out data segment text */
+PCUT_TEST(data_seg_text)
+{
+	tcp_segment_t *seg;
+	uint8_t *data;
+	uint8_t *cdata;
+	size_t i, dsize;
+
+	dsize = 15;
+	data = malloc(dsize);
+	PCUT_ASSERT_NOT_NULL(data);
+	cdata = malloc(dsize);
+	PCUT_ASSERT_NOT_NULL(cdata);
+
+	for (i = 0; i < dsize; i++)
+		data[i] = (uint8_t) i;
+
+	seg = tcp_segment_make_data(CTL_SYN, data, dsize);
+	PCUT_ASSERT_NOT_NULL(seg);
+
+	PCUT_ASSERT_INT_EQUALS(dsize, tcp_segment_text_size(seg));
+	tcp_segment_text_copy(seg, cdata, dsize);
+
+	for (i = 0; i < dsize; i++)
+		PCUT_ASSERT_INT_EQUALS(data[i], cdata[i]);
+
+	tcp_segment_delete(seg);
+	free(data);
+	free(cdata);
+}
+
+/** Test trimming data segment text */
+PCUT_TEST(data_seg_trim)
+{
+	tcp_segment_t *seg;
+	uint8_t *data;
+	uint8_t *cdata;
+	size_t i, dsize;
+
+	dsize = 15;
+	data = malloc(dsize);
+	PCUT_ASSERT_NOT_NULL(data);
+	cdata = malloc(dsize);
+	PCUT_ASSERT_NOT_NULL(cdata);
+
+	for (i = 0; i < dsize; i++)
+		data[i] = (uint8_t) i;
+
+	seg = tcp_segment_make_data(CTL_SYN | CTL_FIN, data, dsize);
+	PCUT_ASSERT_NOT_NULL(seg);
+
+	PCUT_ASSERT_INT_EQUALS(dsize, tcp_segment_text_size(seg));
+	tcp_segment_text_copy(seg, cdata, dsize);
+
+	for (i = 0; i < dsize; i++)
+		PCUT_ASSERT_INT_EQUALS(data[i], cdata[i]);
+
+	PCUT_ASSERT_INT_EQUALS(CTL_SYN | CTL_FIN, seg->ctrl);
+
+	tcp_segment_trim(seg, 1, 0);
+	PCUT_ASSERT_INT_EQUALS(CTL_FIN, seg->ctrl);
+	PCUT_ASSERT_INT_EQUALS(dsize, tcp_segment_text_size(seg));
+
+	tcp_segment_trim(seg, 0, 1);
+	PCUT_ASSERT_INT_EQUALS(0, seg->ctrl);
+	PCUT_ASSERT_INT_EQUALS(dsize, tcp_segment_text_size(seg));
+
+	tcp_segment_trim(seg, 1, 0);
+	PCUT_ASSERT_INT_EQUALS(0, seg->ctrl);
+	PCUT_ASSERT_INT_EQUALS(dsize - 1, tcp_segment_text_size(seg));
+
+	tcp_segment_text_copy(seg, cdata, dsize - 1);
+	for (i = 0; i < dsize - 1; i++)
+		PCUT_ASSERT_INT_EQUALS(data[i + 1], cdata[i]);
+
+	tcp_segment_trim(seg, 0, 1);
+	PCUT_ASSERT_INT_EQUALS(0, seg->ctrl);
+	PCUT_ASSERT_INT_EQUALS(dsize - 2, tcp_segment_text_size(seg));
+
+	tcp_segment_text_copy(seg, cdata, dsize - 2);
+	for (i = 0; i < dsize - 2; i++)
+		PCUT_ASSERT_INT_EQUALS(data[i + 1], cdata[i]);
+
+	tcp_segment_delete(seg);
+	free(data);
+	free(cdata);
+}
+
+PCUT_EXPORT(segment);
Index: uspace/srv/net/tcp/test/seq_no.c
===================================================================
--- uspace/srv/net/tcp/test/seq_no.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/srv/net/tcp/test/seq_no.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,549 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#include <errno.h>
+#include <inet/endpoint.h>
+#include <pcut/pcut.h>
+
+#include "../conn.h"
+#include "../segment.h"
+#include "../seq_no.h"
+
+PCUT_INIT
+
+PCUT_TEST_SUITE(seq_no);
+
+/** Test seq_no_ack_acceptable() */
+PCUT_TEST(ack_acceptable)
+{
+	tcp_conn_t *conn;
+	inet_ep2_t epp;
+
+	inet_ep2_init(&epp);
+	conn = tcp_conn_new(&epp);
+	PCUT_ASSERT_NOT_NULL(conn);
+
+	/* ACK is acceptable iff SND.UNA < SEG.ACK <= SND.NXT */
+
+	conn->snd_una = 10;
+	conn->snd_nxt = 30;
+
+	PCUT_ASSERT_FALSE(seq_no_ack_acceptable(conn, 9));
+	PCUT_ASSERT_FALSE(seq_no_ack_acceptable(conn, 10));
+	PCUT_ASSERT_TRUE(seq_no_ack_acceptable(conn, 11));
+	PCUT_ASSERT_TRUE(seq_no_ack_acceptable(conn, 29));
+	PCUT_ASSERT_TRUE(seq_no_ack_acceptable(conn, 30));
+	PCUT_ASSERT_FALSE(seq_no_ack_acceptable(conn, 31));
+
+	/* We also test whether seq_no_lt_le() wraps around properly */
+
+	conn->snd_una = 30;
+	conn->snd_nxt = 10;
+
+	PCUT_ASSERT_FALSE(seq_no_ack_acceptable(conn, 29));
+	PCUT_ASSERT_FALSE(seq_no_ack_acceptable(conn, 30));
+	PCUT_ASSERT_TRUE(seq_no_ack_acceptable(conn, 31));
+	PCUT_ASSERT_TRUE(seq_no_ack_acceptable(conn, 9));
+	PCUT_ASSERT_TRUE(seq_no_ack_acceptable(conn, 10));
+	PCUT_ASSERT_FALSE(seq_no_ack_acceptable(conn, 11));
+
+	tcp_conn_delete(conn);
+}
+
+/** Test seq_no_ack_duplicate() */
+PCUT_TEST(ack_duplicate)
+{
+	tcp_conn_t *conn;
+	inet_ep2_t epp;
+
+	inet_ep2_init(&epp);
+	conn = tcp_conn_new(&epp);
+	PCUT_ASSERT_NOT_NULL(conn);
+
+	/* ACK is duplicate iff SEG.ACK <= SND.UNA */
+
+	conn->snd_una = 10;
+
+	PCUT_ASSERT_TRUE(seq_no_ack_duplicate(conn, 9));
+	PCUT_ASSERT_TRUE(seq_no_ack_duplicate(conn, 10));
+	PCUT_ASSERT_FALSE(seq_no_ack_duplicate(conn, 11));
+
+	conn->snd_una = (uint32_t) -10;
+
+	PCUT_ASSERT_TRUE(seq_no_ack_duplicate(conn, (uint32_t) -11));
+	PCUT_ASSERT_TRUE(seq_no_ack_duplicate(conn, (uint32_t) -10));
+	PCUT_ASSERT_FALSE(seq_no_ack_duplicate(conn, (uint32_t) -9));
+
+	tcp_conn_delete(conn);
+}
+
+/** Test seq_no_in_rcv_wnd() */
+PCUT_TEST(in_rcv_wnd)
+{
+	tcp_conn_t *conn;
+	inet_ep2_t epp;
+
+	inet_ep2_init(&epp);
+	conn = tcp_conn_new(&epp);
+	PCUT_ASSERT_NOT_NULL(conn);
+
+	/* In receive window iff RCV.WND <= SEG.SEQ <= SND.UNA */
+
+	conn->rcv_nxt = 10;
+	conn->rcv_wnd = 20;
+
+	PCUT_ASSERT_FALSE(seq_no_in_rcv_wnd(conn, 9));
+	PCUT_ASSERT_TRUE(seq_no_in_rcv_wnd(conn, 10));
+	PCUT_ASSERT_TRUE(seq_no_in_rcv_wnd(conn, 11));
+	PCUT_ASSERT_TRUE(seq_no_in_rcv_wnd(conn, 29));
+	PCUT_ASSERT_FALSE(seq_no_in_rcv_wnd(conn, 30));
+	PCUT_ASSERT_FALSE(seq_no_in_rcv_wnd(conn, 31));
+
+	/* We also test whether seq_no_le_lt() wraps around properly */
+
+	conn->rcv_nxt = 20;
+	conn->rcv_wnd = (uint32_t) -10;
+
+	PCUT_ASSERT_FALSE(seq_no_in_rcv_wnd(conn,19));
+	PCUT_ASSERT_TRUE(seq_no_in_rcv_wnd(conn, 20));
+	PCUT_ASSERT_TRUE(seq_no_in_rcv_wnd(conn, 21));
+	PCUT_ASSERT_TRUE(seq_no_in_rcv_wnd(conn, 9));
+	PCUT_ASSERT_FALSE(seq_no_in_rcv_wnd(conn, 10));
+	PCUT_ASSERT_FALSE(seq_no_in_rcv_wnd(conn, 11));
+
+	tcp_conn_delete(conn);
+}
+
+/** Test seq_no_new_wnd_update() */
+PCUT_TEST(new_wnd_update)
+{
+	tcp_conn_t *conn;
+	inet_ep2_t epp;
+	tcp_segment_t *seg;
+
+	inet_ep2_init(&epp);
+	conn = tcp_conn_new(&epp);
+	PCUT_ASSERT_NOT_NULL(conn);
+
+	/*
+	 * Segment must be acceptable. Segment has new window update iff
+	 * either SND.WL1 < SEG.SEQ or
+	 * (SND.WL1 = SEG.SEQ and SND.WL2 <= SEG.ACK)
+	 */
+
+	conn->rcv_nxt = 10;
+	conn->rcv_wnd = 20;
+	conn->snd_una = 30;
+	conn->snd_wnd = 40;
+	conn->snd_wl1 = 15;
+	conn->snd_wl2 = 60;
+
+	seg = tcp_segment_make_ctrl(CTL_ACK);
+	PCUT_ASSERT_NOT_NULL(seg);
+
+	seg->seq = 14;
+	seg->ack = 80;
+	PCUT_ASSERT_FALSE(seq_no_new_wnd_update(conn, seg));
+
+	seg->seq = 15;
+	seg->ack = 59;
+	PCUT_ASSERT_FALSE(seq_no_new_wnd_update(conn, seg));
+
+	seg->seq = 15;
+	seg->ack = 60;
+	PCUT_ASSERT_TRUE(seq_no_new_wnd_update(conn, seg));
+
+	seg->seq = 16;
+	seg->ack = 50;
+	PCUT_ASSERT_TRUE(seq_no_new_wnd_update(conn, seg));
+
+	tcp_segment_delete(seg);
+	tcp_conn_delete(conn);
+}
+
+/** Test seq_no_segment_acked() */
+PCUT_TEST(segment_acked)
+{
+	tcp_conn_t *conn;
+	inet_ep2_t epp;
+	tcp_segment_t *seg;
+	uint8_t *data;
+	size_t dsize;
+
+	inet_ep2_init(&epp);
+	conn = tcp_conn_new(&epp);
+	PCUT_ASSERT_NOT_NULL(conn);
+
+	dsize = 15;
+	data = calloc(dsize, 1);
+	PCUT_ASSERT_NOT_NULL(data);
+
+	seg = tcp_segment_make_data(0, data, dsize);
+	PCUT_ASSERT_NOT_NULL(seg);
+
+	/* Segment is acked iff SEG.SEQ + SEG.LEN <= SND.UNA */
+
+	seg->seq = 10;
+	PCUT_ASSERT_INT_EQUALS(dsize, seg->len);
+
+	PCUT_ASSERT_FALSE(seq_no_segment_acked(conn, seg, 24));
+	PCUT_ASSERT_TRUE(seq_no_segment_acked(conn, seg, 25));
+
+	tcp_segment_delete(seg);
+	tcp_conn_delete(conn);
+	free(data);
+}
+
+/** Test seq_no_syn_acked() */
+PCUT_TEST(syn_acked)
+{
+	tcp_conn_t *conn;
+	inet_ep2_t epp;
+
+	inet_ep2_init(&epp);
+	conn = tcp_conn_new(&epp);
+	PCUT_ASSERT_NOT_NULL(conn);
+
+	conn->iss = 1;
+	conn->snd_una = 1;
+	conn->snd_nxt = 2;
+
+	PCUT_ASSERT_FALSE(seq_no_syn_acked(conn));
+
+	conn->snd_una = 2;
+	PCUT_ASSERT_TRUE(seq_no_syn_acked(conn));
+
+	tcp_conn_delete(conn);
+}
+
+/** Test seq_no_segment_ready() */
+PCUT_TEST(segment_ready)
+{
+	tcp_conn_t *conn;
+	inet_ep2_t epp;
+	tcp_segment_t *seg;
+	uint8_t *data;
+	size_t dsize;
+
+	inet_ep2_init(&epp);
+	conn = tcp_conn_new(&epp);
+	PCUT_ASSERT_NOT_NULL(conn);
+
+	dsize = 15;
+	data = calloc(dsize, 1);
+	PCUT_ASSERT_NOT_NULL(data);
+
+	seg = tcp_segment_make_data(0, data, dsize);
+	PCUT_ASSERT_NOT_NULL(seg);
+
+	/* Segment must be acceptable. Ready iff intersects RCV.NXT */
+
+	conn->rcv_nxt = 30;
+	conn->rcv_wnd = 20;
+
+	PCUT_ASSERT_INT_EQUALS(dsize, seg->len);
+
+	seg->seq = 16;
+	PCUT_ASSERT_TRUE(seq_no_segment_ready(conn, seg));
+
+	seg->seq = 17;
+	PCUT_ASSERT_TRUE(seq_no_segment_ready(conn, seg));
+
+	seg->seq = 29;
+	PCUT_ASSERT_TRUE(seq_no_segment_ready(conn, seg));
+
+	seg->seq = 30;
+	PCUT_ASSERT_TRUE(seq_no_segment_ready(conn, seg));
+
+	seg->seq = 31;
+	PCUT_ASSERT_FALSE(seq_no_segment_ready(conn, seg));
+
+	tcp_segment_delete(seg);
+	tcp_conn_delete(conn);
+	free(data);
+}
+
+/** Test seq_no_segment_acceptable() */
+PCUT_TEST(segment_acceptable)
+{
+	tcp_conn_t *conn;
+	inet_ep2_t epp;
+	tcp_segment_t *seg;
+	uint8_t *data;
+	size_t dsize;
+
+	inet_ep2_init(&epp);
+	conn = tcp_conn_new(&epp);
+	PCUT_ASSERT_NOT_NULL(conn);
+
+	dsize = 15;
+	data = calloc(dsize, 1);
+	PCUT_ASSERT_NOT_NULL(data);
+
+	seg = tcp_segment_make_data(0, data, dsize);
+	PCUT_ASSERT_NOT_NULL(seg);
+
+	/* Segment acceptable iff overlaps receive window */
+
+	/* Segment shorter than receive window */
+	conn->rcv_nxt = 30;
+	conn->rcv_wnd = 20;
+
+	PCUT_ASSERT_INT_EQUALS(dsize, seg->len);
+
+	seg->seq = 10;
+	PCUT_ASSERT_FALSE(seq_no_segment_acceptable(conn, seg));
+
+	seg->seq = 15;
+	PCUT_ASSERT_FALSE(seq_no_segment_acceptable(conn, seg));
+
+	seg->seq = 16;
+	PCUT_ASSERT_TRUE(seq_no_segment_acceptable(conn, seg));
+
+	seg->seq = 49;
+	PCUT_ASSERT_TRUE(seq_no_segment_acceptable(conn, seg));
+
+	seg->seq = 50;
+	PCUT_ASSERT_FALSE(seq_no_segment_acceptable(conn, seg));
+
+	/* Segment longer than receive window */
+	conn->rcv_nxt = 30;
+	conn->rcv_wnd = 10;
+
+	PCUT_ASSERT_INT_EQUALS(dsize, seg->len);
+
+	seg->seq = 10;
+	PCUT_ASSERT_FALSE(seq_no_segment_acceptable(conn, seg));
+
+	seg->seq = 15;
+	PCUT_ASSERT_FALSE(seq_no_segment_acceptable(conn, seg));
+
+	seg->seq = 16;
+	PCUT_ASSERT_TRUE(seq_no_segment_acceptable(conn, seg));
+
+	seg->seq = 39;
+	PCUT_ASSERT_TRUE(seq_no_segment_acceptable(conn, seg));
+
+	seg->seq = 40;
+	PCUT_ASSERT_FALSE(seq_no_segment_acceptable(conn, seg));
+
+	tcp_segment_delete(seg);
+	tcp_conn_delete(conn);
+	free(data);
+}
+
+/** Test seq_no_seg_trim_calc() */
+PCUT_TEST(seg_trim_calc)
+{
+	tcp_conn_t *conn;
+	inet_ep2_t epp;
+	tcp_segment_t *seg;
+	uint8_t *data;
+	size_t dsize;
+	uint32_t ltrim, rtrim;
+
+	inet_ep2_init(&epp);
+	conn = tcp_conn_new(&epp);
+	PCUT_ASSERT_NOT_NULL(conn);
+
+	dsize = 15;
+	data = calloc(dsize, 1);
+	PCUT_ASSERT_NOT_NULL(data);
+
+	seg = tcp_segment_make_data(0, data, dsize);
+	PCUT_ASSERT_NOT_NULL(seg);
+
+	/* Segment must be acceptable, amount of trim needed */
+
+	/* Segment shorter than receive window */
+	conn->rcv_nxt = 30;
+	conn->rcv_wnd = 20;
+
+	PCUT_ASSERT_INT_EQUALS(dsize, seg->len);
+
+	seg->seq = 16;
+	seq_no_seg_trim_calc(conn, seg, &ltrim, &rtrim);
+	PCUT_ASSERT_INT_EQUALS(14, ltrim);
+	PCUT_ASSERT_INT_EQUALS(0, rtrim);
+
+	seg->seq = 17;
+	seq_no_seg_trim_calc(conn, seg, &ltrim, &rtrim);
+	PCUT_ASSERT_INT_EQUALS(13, ltrim);
+	PCUT_ASSERT_INT_EQUALS(0, rtrim);
+
+	seg->seq = 29;
+	seq_no_seg_trim_calc(conn, seg, &ltrim, &rtrim);
+	PCUT_ASSERT_INT_EQUALS(1, ltrim);
+	PCUT_ASSERT_INT_EQUALS(0, rtrim);
+
+	seg->seq = 30;
+	seq_no_seg_trim_calc(conn, seg, &ltrim, &rtrim);
+	PCUT_ASSERT_INT_EQUALS(0, ltrim);
+	PCUT_ASSERT_INT_EQUALS(0, rtrim);
+
+	seg->seq = 31;
+	seq_no_seg_trim_calc(conn, seg, &ltrim, &rtrim);
+	PCUT_ASSERT_INT_EQUALS(0, ltrim);
+	PCUT_ASSERT_INT_EQUALS(0, rtrim);
+
+	seg->seq = 35;
+	seq_no_seg_trim_calc(conn, seg, &ltrim, &rtrim);
+	PCUT_ASSERT_INT_EQUALS(0, ltrim);
+	PCUT_ASSERT_INT_EQUALS(0, rtrim);
+
+	seg->seq = 36;
+	seq_no_seg_trim_calc(conn, seg, &ltrim, &rtrim);
+	PCUT_ASSERT_INT_EQUALS(0, ltrim);
+	PCUT_ASSERT_INT_EQUALS(1, rtrim);
+
+	seg->seq = 37;
+	seq_no_seg_trim_calc(conn, seg, &ltrim, &rtrim);
+	PCUT_ASSERT_INT_EQUALS(0, ltrim);
+	PCUT_ASSERT_INT_EQUALS(2, rtrim);
+
+	seg->seq = 48;
+	seq_no_seg_trim_calc(conn, seg, &ltrim, &rtrim);
+	PCUT_ASSERT_INT_EQUALS(0, ltrim);
+	PCUT_ASSERT_INT_EQUALS(13, rtrim);
+
+	seg->seq = 49;
+	seq_no_seg_trim_calc(conn, seg, &ltrim, &rtrim);
+	PCUT_ASSERT_INT_EQUALS(0, ltrim);
+	PCUT_ASSERT_INT_EQUALS(14, rtrim);
+
+	/* Segment longer than receive window */
+	conn->rcv_nxt = 30;
+	conn->rcv_wnd = 10;
+
+	PCUT_ASSERT_INT_EQUALS(dsize, seg->len);
+
+	seg->seq = 16;
+	seq_no_seg_trim_calc(conn, seg, &ltrim, &rtrim);
+	PCUT_ASSERT_INT_EQUALS(14, ltrim);
+	PCUT_ASSERT_INT_EQUALS(0, rtrim);
+
+	seg->seq = 17;
+	seq_no_seg_trim_calc(conn, seg, &ltrim, &rtrim);
+	PCUT_ASSERT_INT_EQUALS(13, ltrim);
+	PCUT_ASSERT_INT_EQUALS(0, rtrim);
+
+	seg->seq = 24;
+	seq_no_seg_trim_calc(conn, seg, &ltrim, &rtrim);
+	PCUT_ASSERT_INT_EQUALS(6, ltrim);
+	PCUT_ASSERT_INT_EQUALS(0, rtrim);
+
+	seg->seq = 25;
+	seq_no_seg_trim_calc(conn, seg, &ltrim, &rtrim);
+	PCUT_ASSERT_INT_EQUALS(5, ltrim);
+	PCUT_ASSERT_INT_EQUALS(0, rtrim);
+
+	seg->seq = 26;
+	seq_no_seg_trim_calc(conn, seg, &ltrim, &rtrim);
+	PCUT_ASSERT_INT_EQUALS(4, ltrim);
+	PCUT_ASSERT_INT_EQUALS(1, rtrim);
+
+	tcp_segment_delete(seg);
+	tcp_conn_delete(conn);
+	free(data);
+}
+
+/** Test seq_no_seg_cmp() */
+PCUT_TEST(seg_cmp)
+{
+	tcp_conn_t *conn;
+	inet_ep2_t epp;
+	tcp_segment_t *seg1, *seg2;
+	uint8_t *data;
+	size_t dsize;
+
+	inet_ep2_init(&epp);
+	conn = tcp_conn_new(&epp);
+	PCUT_ASSERT_NOT_NULL(conn);
+
+	dsize = 15;
+	data = calloc(dsize, 1);
+	PCUT_ASSERT_NOT_NULL(data);
+
+	seg1 = tcp_segment_make_data(0, data, dsize);
+	PCUT_ASSERT_NOT_NULL(seg1);
+	seg2 = tcp_segment_make_data(0, data, dsize);
+	PCUT_ASSERT_NOT_NULL(seg2);
+
+	/* Both segments must be acceptable */
+
+	conn->rcv_nxt = 10;
+	conn->rcv_wnd = 20;
+
+	PCUT_ASSERT_INT_EQUALS(dsize, seg1->len);
+	PCUT_ASSERT_INT_EQUALS(dsize, seg2->len);
+
+	seg1->seq = 5;
+	seg2->seq = 6;
+	PCUT_ASSERT_INT_EQUALS(-1, seq_no_seg_cmp(conn, seg1, seg2));
+
+	seg1->seq = 6;
+	seg2->seq = 6;
+	PCUT_ASSERT_INT_EQUALS(0, seq_no_seg_cmp(conn, seg1, seg2));
+
+	seg1->seq = 6;
+	seg2->seq = 5;
+	PCUT_ASSERT_INT_EQUALS(1, seq_no_seg_cmp(conn, seg1, seg2));
+
+	tcp_segment_delete(seg1);
+	tcp_segment_delete(seg2);
+	tcp_conn_delete(conn);
+	free(data);
+}
+
+/** Test seq_no_control_len() */
+PCUT_TEST(control_len)
+{
+	PCUT_ASSERT_INT_EQUALS(0, seq_no_control_len(0));
+	PCUT_ASSERT_INT_EQUALS(0, seq_no_control_len(CTL_ACK));
+	PCUT_ASSERT_INT_EQUALS(0, seq_no_control_len(CTL_RST));
+	PCUT_ASSERT_INT_EQUALS(0, seq_no_control_len(CTL_ACK | CTL_RST));
+	PCUT_ASSERT_INT_EQUALS(1, seq_no_control_len(CTL_SYN));
+	PCUT_ASSERT_INT_EQUALS(1, seq_no_control_len(CTL_FIN));
+	PCUT_ASSERT_INT_EQUALS(1, seq_no_control_len(CTL_SYN | CTL_ACK));
+	PCUT_ASSERT_INT_EQUALS(1, seq_no_control_len(CTL_FIN | CTL_ACK));
+	PCUT_ASSERT_INT_EQUALS(1, seq_no_control_len(CTL_SYN | CTL_RST));
+	PCUT_ASSERT_INT_EQUALS(1, seq_no_control_len(CTL_FIN | CTL_RST));
+	PCUT_ASSERT_INT_EQUALS(1, seq_no_control_len(CTL_SYN | CTL_ACK |
+	    CTL_RST));
+	PCUT_ASSERT_INT_EQUALS(1, seq_no_control_len(CTL_FIN | CTL_ACK |
+	    CTL_RST));
+	PCUT_ASSERT_INT_EQUALS(2, seq_no_control_len(CTL_SYN | CTL_FIN));
+	PCUT_ASSERT_INT_EQUALS(2, seq_no_control_len(CTL_SYN | CTL_FIN |
+	    CTL_ACK));
+	PCUT_ASSERT_INT_EQUALS(2, seq_no_control_len(CTL_SYN | CTL_FIN |
+	    CTL_RST));
+	PCUT_ASSERT_INT_EQUALS(2, seq_no_control_len(CTL_SYN | CTL_FIN |
+	    CTL_ACK | CTL_RST));
+}
+
+PCUT_EXPORT(seq_no);
Index: uspace/srv/net/tcp/test/tqueue.c
===================================================================
--- uspace/srv/net/tcp/test/tqueue.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/srv/net/tcp/test/tqueue.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+//#include <inet/endpoint.h>
+#include <io/log.h>
+#include <pcut/pcut.h>
+
+#include "../conn.h"
+#include "../tqueue.h"
+
+PCUT_INIT
+
+PCUT_TEST_SUITE(tqueue);
+
+enum {
+	test_seg_max = 10
+};
+
+static int seg_cnt;
+static tcp_segment_t *trans_seg[test_seg_max];
+
+static void tqueue_test_transmit_seg(inet_ep2_t *, tcp_segment_t *);
+
+static tcp_tqueue_cb_t tqueue_test_cb = {
+	.transmit_seg = tqueue_test_transmit_seg
+};
+
+PCUT_TEST_BEFORE
+{
+	int rc;
+
+	/* We will be calling functions that perform logging */
+	rc = log_init("test-tcp");
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = tcp_conns_init();
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+}
+
+PCUT_TEST_AFTER
+{
+	tcp_conns_fini();
+}
+
+
+/** Test  */
+PCUT_TEST(init_fini)
+{
+	tcp_conn_t *conn;
+	inet_ep2_t epp;
+
+	/* XXX tqueue can only be created via tcp_conn_new */
+	inet_ep2_init(&epp);
+	conn = tcp_conn_new(&epp);
+	PCUT_ASSERT_NOT_NULL(conn);
+
+	/* Redirect segment transmission */
+	conn->retransmit.cb = &tqueue_test_cb;
+	seg_cnt = 0;
+
+	tcp_conn_lock(conn);
+	tcp_conn_reset(conn);
+	tcp_conn_unlock(conn);
+	tcp_conn_delete(conn);
+	PCUT_ASSERT_EQUALS(0, seg_cnt);
+}
+
+/** Test sending control segment and tearing down a non-empty queue */
+PCUT_TEST(ctrl_seg_teardown)
+{
+	tcp_conn_t *conn;
+	inet_ep2_t epp;
+
+	/* XXX tqueue can only be created via tcp_conn_new */
+	inet_ep2_init(&epp);
+	conn = tcp_conn_new(&epp);
+	PCUT_ASSERT_NOT_NULL(conn);
+
+	conn->snd_nxt = 10;
+
+	/* Redirect segment transmission */
+	conn->retransmit.cb = &tqueue_test_cb;
+	seg_cnt = 0;
+
+	tcp_conn_lock(conn);
+	tcp_tqueue_ctrl_seg(conn, CTL_SYN);
+	tcp_conn_reset(conn);
+	tcp_conn_unlock(conn);
+	PCUT_ASSERT_EQUALS(11, conn->snd_nxt);
+
+	tcp_conn_delete(conn);
+	PCUT_ASSERT_EQUALS(1, seg_cnt);
+	PCUT_ASSERT_EQUALS(CTL_SYN, trans_seg[0]->ctrl);
+	PCUT_ASSERT_EQUALS(10, trans_seg[0]->seq);
+}
+
+/** Test sending data and FIN */
+PCUT_TEST(new_data_fin)
+{
+	tcp_conn_t *conn;
+	inet_ep2_t epp;
+	int i;
+
+	/* XXX tqueue can only be created via tcp_conn_new */
+	inet_ep2_init(&epp);
+	conn = tcp_conn_new(&epp);
+	PCUT_ASSERT_NOT_NULL(conn);
+
+	conn->cstate = st_established;
+	conn->snd_una = 10;
+	conn->snd_nxt = 10;
+	conn->snd_wnd = 1024;
+	conn->snd_buf_used = 20;
+	conn->snd_buf_fin = true;
+	for (i = 0; i < 20; i++)
+		conn->snd_buf[i] = i;
+
+	/* Redirect segment transmission */
+	conn->retransmit.cb = &tqueue_test_cb;
+	seg_cnt = 0;
+
+	tcp_conn_lock(conn);
+	tcp_tqueue_new_data(conn);
+	tcp_conn_reset(conn);
+	tcp_conn_unlock(conn);
+	PCUT_ASSERT_EQUALS(31, conn->snd_nxt);
+	PCUT_ASSERT_EQUALS(0, conn->snd_buf_used);
+	PCUT_ASSERT_FALSE(conn->snd_buf_fin);
+
+	tcp_conn_delete(conn);
+	PCUT_ASSERT_EQUALS(1, seg_cnt);
+	PCUT_ASSERT_EQUALS(CTL_FIN | CTL_ACK, trans_seg[0]->ctrl);
+	PCUT_ASSERT_EQUALS(10, trans_seg[0]->seq);
+}
+
+/** Test sending data when send window is smaller */
+PCUT_TEST(new_data_small_win)
+{
+	tcp_conn_t *conn;
+	inet_ep2_t epp;
+	int i;
+
+	/* XXX tqueue can only be created via tcp_conn_new */
+	inet_ep2_init(&epp);
+	conn = tcp_conn_new(&epp);
+	PCUT_ASSERT_NOT_NULL(conn);
+
+	conn->cstate = st_established;
+	conn->snd_una = 10;
+	conn->snd_nxt = 10;
+	conn->snd_wnd = 5;
+	conn->snd_buf_used = 30;
+	conn->snd_buf_fin = false;
+	for (i = 0; i < 30; i++)
+		conn->snd_buf[i] = i;
+
+	/* Redirect segment transmission */
+	conn->retransmit.cb = &tqueue_test_cb;
+	seg_cnt = 0;
+
+	tcp_conn_lock(conn);
+	tcp_tqueue_new_data(conn);
+	tcp_conn_reset(conn);
+	tcp_conn_unlock(conn);
+
+	PCUT_ASSERT_EQUALS(15, conn->snd_nxt);
+	PCUT_ASSERT_EQUALS(25, conn->snd_buf_used);
+	PCUT_ASSERT_FALSE(conn->snd_buf_fin);
+	for (i = 0; i < 25; i++)
+		PCUT_ASSERT_INT_EQUALS(5 + i, conn->snd_buf[i]);
+
+	tcp_conn_delete(conn);
+	PCUT_ASSERT_EQUALS(1, seg_cnt);
+	PCUT_ASSERT_EQUALS(CTL_ACK, trans_seg[0]->ctrl);
+	PCUT_ASSERT_EQUALS(10, trans_seg[0]->seq);
+}
+
+/** Test flushing tqueue due to receiving an ACK */
+PCUT_TEST(ack_received)
+{
+	tcp_conn_t *conn;
+	inet_ep2_t epp;
+	int i;
+
+	/* XXX tqueue can only be created via tcp_conn_new */
+	inet_ep2_init(&epp);
+	conn = tcp_conn_new(&epp);
+	PCUT_ASSERT_NOT_NULL(conn);
+
+	conn->cstate = st_established;
+	conn->snd_una = 10;
+	conn->snd_nxt = 10;
+	conn->snd_wnd = 1024;
+
+	/* Redirect segment transmission */
+	conn->retransmit.cb = &tqueue_test_cb;
+	seg_cnt = 0;
+
+	tcp_conn_lock(conn);
+
+	/* Queue first data segment */
+	conn->snd_buf_used = 10;
+	conn->snd_buf_fin = false;
+	for (i = 0; i < 10; i++)
+		conn->snd_buf[i] = i;
+	tcp_tqueue_new_data(conn);
+
+	PCUT_ASSERT_EQUALS(20, conn->snd_nxt);
+
+	/* Queue second data segment */
+	conn->snd_buf_used = 20;
+	conn->snd_buf_fin = false;
+	for (i = 0; i < 20; i++)
+		conn->snd_buf[i] = i;
+	tcp_tqueue_new_data(conn);
+
+	PCUT_ASSERT_EQUALS(40, conn->snd_nxt);
+
+	PCUT_ASSERT_INT_EQUALS(2, list_count(&conn->retransmit.list));
+
+	/* One of the two segments is acked */
+	conn->snd_una = 20;
+	tcp_tqueue_ack_received(conn);
+
+	PCUT_ASSERT_INT_EQUALS(1, list_count(&conn->retransmit.list));
+
+	tcp_conn_reset(conn);
+	tcp_conn_unlock(conn);
+	tcp_conn_delete(conn);
+}
+
+static void tqueue_test_transmit_seg(inet_ep2_t *epp, tcp_segment_t *seg)
+{
+	trans_seg[seg_cnt++] = seg;
+}
+
+PCUT_EXPORT(tqueue);
Index: uspace/srv/net/tcp/test/ucall.c
===================================================================
--- uspace/srv/net/tcp/test/ucall.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
+++ uspace/srv/net/tcp/test/ucall.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -0,0 +1,316 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+#include <errno.h>
+#include <inet/endpoint.h>
+#include <io/log.h>
+#include <pcut/pcut.h>
+
+#include "../conn.h"
+#include "../rqueue.h"
+#include "../ucall.h"
+
+PCUT_INIT
+
+PCUT_TEST_SUITE(ucall);
+
+static void test_cstate_change(tcp_conn_t *, void *, tcp_cstate_t);
+static void test_conns_establish(tcp_conn_t **, tcp_conn_t **);
+static void test_conns_tear_down(tcp_conn_t *, tcp_conn_t *);
+
+static tcp_rqueue_cb_t test_rqueue_cb = {
+	.seg_received = tcp_as_segment_arrived
+};
+
+static tcp_cb_t test_conn_cb = {
+	.cstate_change = test_cstate_change
+};
+
+static tcp_conn_status_t cconn_status;
+static tcp_conn_status_t sconn_status;
+
+static FIBRIL_MUTEX_INITIALIZE(cst_lock);
+static FIBRIL_CONDVAR_INITIALIZE(cst_cv);
+
+PCUT_TEST_BEFORE
+{
+	int rc;
+
+	/* We will be calling functions that perform logging */
+	rc = log_init("test-tcp");
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	rc = tcp_conns_init();
+	PCUT_ASSERT_ERRNO_VAL(EOK, rc);
+
+	tcp_rqueue_init(&test_rqueue_cb);
+	tcp_rqueue_fibril_start();
+
+	/* Enable internal loopback */
+	tcp_conn_lb = tcp_lb_segment;
+}
+
+PCUT_TEST_AFTER
+{
+	tcp_rqueue_fini();
+	tcp_conns_fini();
+}
+
+/** Test creating a listening passive connection and then deleting it. */
+PCUT_TEST(listen_delete)
+{
+	tcp_conn_t *conn;
+	inet_ep2_t epp;
+	tcp_error_t trc;
+	tcp_conn_status_t cstatus;
+
+	inet_ep2_init(&epp);
+	inet_addr(&epp.local.addr, 127, 0, 0, 1);
+	epp.local.port = inet_port_user_lo;
+
+	conn = NULL;
+	trc = tcp_uc_open(&epp, ap_passive, tcp_open_nonblock, &conn);
+	PCUT_ASSERT_INT_EQUALS(TCP_EOK, trc);
+	PCUT_ASSERT_NOT_NULL(conn);
+
+	tcp_uc_status(conn, &cstatus);
+	PCUT_ASSERT_INT_EQUALS(st_listen, cstatus.cstate);
+
+	trc = tcp_uc_close(conn);
+	PCUT_ASSERT_INT_EQUALS(TCP_EOK, trc);
+	tcp_uc_delete(conn);
+}
+
+/** Test trying to connect to endpoint that sends RST back */
+PCUT_TEST(connect_rst)
+{
+	tcp_conn_t *conn;
+	inet_ep2_t epp;
+	tcp_error_t trc;
+
+	inet_ep2_init(&epp);
+	inet_addr(&epp.local.addr, 127, 0, 0, 1);
+	inet_addr(&epp.remote.addr, 127, 0, 0, 1);
+	epp.remote.port = inet_port_user_lo;
+
+	conn = NULL;
+	trc = tcp_uc_open(&epp, ap_active, 0, &conn);
+	PCUT_ASSERT_INT_EQUALS(TCP_ERESET, trc);
+	PCUT_ASSERT_NULL(conn);
+}
+
+/** Test establishing a connection */
+PCUT_TEST(conn_establish)
+{
+	tcp_conn_t *cconn, *sconn;
+
+	test_conns_establish(&cconn, &sconn);
+	test_conns_tear_down(cconn, sconn);
+}
+
+/** Test establishing and then closing down a connection first on one side,
+ * then on_the other. */
+PCUT_TEST(conn_est_close_seq)
+{
+	tcp_conn_t *cconn, *sconn;
+	tcp_error_t trc;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "conn_est_close_seq: establish");
+	/* Establish */
+	test_conns_establish(&cconn, &sconn);
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "conn_est_close_seq: close cconn");
+	/* Close client side */
+	trc = tcp_uc_close(cconn);
+	PCUT_ASSERT_INT_EQUALS(TCP_EOK, trc);
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "conn_est_close_seq: wait cconn = fin-wait-2");
+	/* Wait for cconn to go to Fin-Wait-2 */
+	fibril_mutex_lock(&cst_lock);
+	tcp_uc_status(cconn, &cconn_status);
+	while (cconn_status.cstate != st_fin_wait_2)
+		fibril_condvar_wait(&cst_cv, &cst_lock);
+	fibril_mutex_unlock(&cst_lock);
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "conn_est_close_seq: wait sconn = close-wait");
+	/* Wait for sconn to go to Close-Wait */
+	fibril_mutex_lock(&cst_lock);
+	tcp_uc_status(sconn, &sconn_status);
+	while (sconn_status.cstate != st_close_wait)
+		fibril_condvar_wait(&cst_cv, &cst_lock);
+	fibril_mutex_unlock(&cst_lock);
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "conn_est_close_seq: close sconn");
+	/* Close server side */
+	trc = tcp_uc_close(sconn);
+	PCUT_ASSERT_INT_EQUALS(TCP_EOK, trc);
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "conn_est_close_seq: wait cconn = time-wait");
+	/* Wait for cconn to go to Time-Wait */
+	fibril_mutex_lock(&cst_lock);
+	tcp_uc_status(cconn, &cconn_status);
+	while (cconn_status.cstate != st_time_wait)
+		fibril_condvar_wait(&cst_cv, &cst_lock);
+	fibril_mutex_unlock(&cst_lock);
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "conn_est_close_seq: wait sconn = closed");
+	/* Wait for sconn to go to Closed */
+	fibril_mutex_lock(&cst_lock);
+	tcp_uc_status(sconn, &sconn_status);
+	while (sconn_status.cstate != st_closed) {
+		log_msg(LOG_DEFAULT, LVL_NOTE, "conn_est_close_seq: sconn.status == %d", sconn_status.cstate);
+		fibril_condvar_wait(&cst_cv, &cst_lock);
+	}
+	fibril_mutex_unlock(&cst_lock);
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "conn_est_close_seq: tear down");
+	/* Tear down */
+	test_conns_tear_down(cconn, sconn);
+}
+
+/** Test establishing and then simultaneously closing down a connection. */
+PCUT_TEST(conn_est_close_simult)
+{
+	tcp_conn_t *cconn, *sconn;
+	tcp_error_t trc;
+
+	/* Establish */
+	test_conns_establish(&cconn, &sconn);
+
+	/* Close both sides simultaneously */
+	trc = tcp_uc_close(cconn);
+	PCUT_ASSERT_INT_EQUALS(TCP_EOK, trc);
+	trc = tcp_uc_close(sconn);
+	PCUT_ASSERT_INT_EQUALS(TCP_EOK, trc);
+
+	/* Wait for cconn to go to Time-Wait */
+	fibril_mutex_lock(&cst_lock);
+	tcp_uc_status(cconn, &cconn_status);
+	while (cconn_status.cstate != st_time_wait)
+		fibril_condvar_wait(&cst_cv, &cst_lock);
+	fibril_mutex_unlock(&cst_lock);
+
+	/*
+	 * Wait for sconn to go to Closed or Time-Wait. The connection
+	 * goes to Closed if we managed to call tcp_uc_close() before
+	 * sconn received FIN. Otherwise it goes to Time-Wait.
+	 *
+	 * XXX We may want to add delay to the loopback here to be
+	 * absolutely sure that we go to Closing -> Time-Wait.
+	 */
+	fibril_mutex_lock(&cst_lock);
+	tcp_uc_status(sconn, &sconn_status);
+	while (sconn_status.cstate != st_time_wait &&
+	    sconn_status.cstate != st_closed)
+		fibril_condvar_wait(&cst_cv, &cst_lock);
+	fibril_mutex_unlock(&cst_lock);
+
+	/* Tear down */
+	test_conns_tear_down(cconn, sconn);
+}
+
+static void test_cstate_change(tcp_conn_t *conn, void *arg,
+    tcp_cstate_t old_state)
+{
+	tcp_conn_status_t *status = (tcp_conn_status_t *)arg;
+
+	fibril_mutex_lock(&cst_lock);
+	tcp_uc_status(conn, status);
+	fibril_mutex_unlock(&cst_lock);
+	fibril_condvar_broadcast(&cst_cv);
+}
+
+/** Establish client-server connection */
+static void test_conns_establish(tcp_conn_t **rcconn, tcp_conn_t **rsconn)
+{
+	tcp_conn_t *cconn, *sconn;
+	inet_ep2_t cepp, sepp;
+	tcp_conn_status_t cstatus;
+	tcp_error_t trc;
+
+	/* Client EPP */
+	inet_ep2_init(&cepp);
+	inet_addr(&cepp.local.addr, 127, 0, 0, 1);
+	inet_addr(&cepp.remote.addr, 127, 0, 0, 1);
+	cepp.remote.port = inet_port_user_lo;
+
+	/* Server EPP */
+	inet_ep2_init(&sepp);
+	inet_addr(&sepp.local.addr, 127, 0, 0, 1);
+	sepp.local.port = inet_port_user_lo;
+
+	/* Server side of the connection */
+	sconn = NULL;
+	trc = tcp_uc_open(&sepp, ap_passive, tcp_open_nonblock, &sconn);
+	PCUT_ASSERT_INT_EQUALS(TCP_EOK, trc);
+	PCUT_ASSERT_NOT_NULL(sconn);
+
+	tcp_uc_set_cb(sconn, &test_conn_cb, &sconn_status);
+	PCUT_ASSERT_EQUALS(&sconn_status, tcp_uc_get_userptr(sconn));
+
+	tcp_uc_status(sconn, &cstatus);
+	PCUT_ASSERT_INT_EQUALS(st_listen, cstatus.cstate);
+
+	/* Client side of the connection */
+
+	cconn = NULL;
+	trc = tcp_uc_open(&cepp, ap_active, 0, &cconn);
+	PCUT_ASSERT_INT_EQUALS(TCP_EOK, trc);
+	PCUT_ASSERT_NOT_NULL(cconn);
+
+	tcp_uc_set_cb(cconn, &test_conn_cb, &cconn_status);
+	PCUT_ASSERT_EQUALS(&cconn_status, tcp_uc_get_userptr(cconn));
+
+	/* The client side of the connection should be established by now */
+
+	tcp_uc_status(cconn, &cstatus);
+	PCUT_ASSERT_INT_EQUALS(st_established, cstatus.cstate);
+
+	/* Need to wait for server side */
+
+	fibril_mutex_lock(&cst_lock);
+	tcp_uc_status(sconn, &sconn_status);
+	while (sconn_status.cstate != st_established)
+		fibril_condvar_wait(&cst_cv, &cst_lock);
+	fibril_mutex_unlock(&cst_lock);
+
+	*rcconn = cconn;
+	*rsconn = sconn;
+}
+
+/* Tear down client-server connection. */
+static void test_conns_tear_down(tcp_conn_t *cconn, tcp_conn_t *sconn)
+{
+	tcp_uc_abort(cconn);
+	tcp_uc_delete(cconn);
+
+	tcp_uc_abort(sconn);
+	tcp_uc_delete(sconn);
+}
+
+PCUT_EXPORT(ucall);
Index: uspace/srv/net/tcp/tqueue.c
===================================================================
--- uspace/srv/net/tcp/tqueue.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/tcp/tqueue.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -43,24 +43,30 @@
 #include <mem.h>
 #include <stdlib.h>
+
 #include "conn.h"
+#include "inet.h"
 #include "ncsim.h"
-#include "pdu.h"
 #include "rqueue.h"
 #include "segment.h"
 #include "seq_no.h"
 #include "tqueue.h"
-#include "tcp.h"
 #include "tcp_type.h"
 
 #define RETRANSMIT_TIMEOUT	(2*1000*1000)
 
-static void retransmit_timeout_func(void *arg);
-static void tcp_tqueue_timer_set(tcp_conn_t *conn);
-static void tcp_tqueue_timer_clear(tcp_conn_t *conn);
-
-int tcp_tqueue_init(tcp_tqueue_t *tqueue, tcp_conn_t *conn)
+static void retransmit_timeout_func(void *);
+static void tcp_tqueue_timer_set(tcp_conn_t *);
+static void tcp_tqueue_timer_clear(tcp_conn_t *);
+static void tcp_tqueue_seg(tcp_conn_t *, tcp_segment_t *);
+static void tcp_conn_transmit_segment(tcp_conn_t *, tcp_segment_t *);
+static void tcp_prepare_transmit_segment(tcp_conn_t *, tcp_segment_t *);
+static void tcp_tqueue_send_immed(tcp_conn_t *, tcp_segment_t *);
+
+int tcp_tqueue_init(tcp_tqueue_t *tqueue, tcp_conn_t *conn,
+    tcp_tqueue_cb_t *cb)
 {
 	tqueue->conn = conn;
 	tqueue->timer = fibril_timer_create(&conn->lock);
+	tqueue->cb = cb;
 	if (tqueue->timer == NULL)
 		return ENOMEM;
@@ -78,8 +84,20 @@
 void tcp_tqueue_fini(tcp_tqueue_t *tqueue)
 {
+	link_t *link;
+	tcp_tqueue_entry_t *tqe;
+
 	if (tqueue->timer != NULL) {
 		fibril_timer_destroy(tqueue->timer);
 		tqueue->timer = NULL;
 	}
+
+	while (!list_empty(&tqueue->list)) {
+		link = list_first(&tqueue->list);
+		tqe = list_get_instance(link, tcp_tqueue_entry_t, link);
+		list_remove(link);
+
+		tcp_segment_delete(tqe->seg);
+		free(tqe);
+	}
 }
 
@@ -87,4 +105,6 @@
 {
 	tcp_segment_t *seg;
+
+	assert(fibril_mutex_is_locked(&conn->lock));
 
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_tqueue_ctrl_seg(%p, %u)", conn, ctrl);
@@ -95,8 +115,10 @@
 }
 
-void tcp_tqueue_seg(tcp_conn_t *conn, tcp_segment_t *seg)
+static void tcp_tqueue_seg(tcp_conn_t *conn, tcp_segment_t *seg)
 {
 	tcp_segment_t *rt_seg;
 	tcp_tqueue_entry_t *tqe;
+
+	assert(fibril_mutex_is_locked(&conn->lock));
 
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_tqueue_seg(%p, %p)", conn->name, conn,
@@ -135,5 +157,5 @@
 }
 
-void tcp_prepare_transmit_segment(tcp_conn_t *conn, tcp_segment_t *seg)
+static void tcp_prepare_transmit_segment(tcp_conn_t *conn, tcp_segment_t *seg)
 {
 	/*
@@ -267,5 +289,5 @@
 }
 
-void tcp_conn_transmit_segment(tcp_conn_t *conn, tcp_segment_t *seg)
+static void tcp_conn_transmit_segment(tcp_conn_t *conn, tcp_segment_t *seg)
 {
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "%s: tcp_conn_transmit_segment(%p, %p)",
@@ -279,12 +301,12 @@
 		seg->ack = 0;
 
-	tcp_transmit_segment(&conn->ident, seg);
-}
-
-void tcp_transmit_segment(inet_ep2_t *epp, tcp_segment_t *seg)
+	tcp_tqueue_send_immed(conn, seg);
+}
+
+void tcp_tqueue_send_immed(tcp_conn_t *conn, tcp_segment_t *seg)
 {
 	log_msg(LOG_DEFAULT, LVL_DEBUG,
-	    "tcp_transmit_segment(l:(%u),f:(%u), %p)",
-	    epp->local.port, epp->remote.port, seg);
+	    "tcp_tqueue_send_immed(l:(%u),f:(%u), %p)",
+	    conn->ident.local.port, conn->ident.remote.port, seg);
 
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "SEG.SEQ=%" PRIu32 ", SEG.WND=%" PRIu32,
@@ -292,20 +314,6 @@
 
 	tcp_segment_dump(seg);
-/*
-	tcp_pdu_prepare(conn, seg, &data, &len);
-	tcp_pdu_transmit(data, len);
-*/
-//	tcp_rqueue_bounce_seg(sp, seg);
-//	tcp_ncsim_bounce_seg(sp, seg);
-
-	tcp_pdu_t *pdu;
-
-	if (tcp_pdu_encode(epp, seg, &pdu) != EOK) {
-		log_msg(LOG_DEFAULT, LVL_WARN, "Not enough memory. Segment dropped.");
-		return;
-	}
-
-	tcp_transmit_pdu(pdu);
-	tcp_pdu_delete(pdu);
+
+	conn->retransmit.cb->transmit_seg(&conn->ident, seg);
 }
 
@@ -362,4 +370,6 @@
 static void tcp_tqueue_timer_set(tcp_conn_t *conn)
 {
+	assert(fibril_mutex_is_locked(&conn->lock));
+
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "### %s: tcp_tqueue_timer_set() begin", conn->name);
 
@@ -377,4 +387,6 @@
 static void tcp_tqueue_timer_clear(tcp_conn_t *conn)
 {
+	assert(fibril_mutex_is_locked(&conn->lock));
+
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "### %s: tcp_tqueue_timer_clear() begin", conn->name);
 
Index: uspace/srv/net/tcp/tqueue.h
===================================================================
--- uspace/srv/net/tcp/tqueue.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/tcp/tqueue.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -40,16 +40,11 @@
 #include "tcp_type.h"
 
-extern int tcp_tqueue_init(tcp_tqueue_t *, tcp_conn_t *);
+extern int tcp_tqueue_init(tcp_tqueue_t *, tcp_conn_t *,
+    tcp_tqueue_cb_t *);
 extern void tcp_tqueue_clear(tcp_tqueue_t *);
 extern void tcp_tqueue_fini(tcp_tqueue_t *);
 extern void tcp_tqueue_ctrl_seg(tcp_conn_t *, tcp_control_t);
-extern void tcp_tqueue_seg(tcp_conn_t *, tcp_segment_t *);
 extern void tcp_tqueue_new_data(tcp_conn_t *);
 extern void tcp_tqueue_ack_received(tcp_conn_t *);
-extern void tcp_prepare_transmit_segment(tcp_conn_t *, tcp_segment_t *);
-extern void tcp_conn_transmit_segment(tcp_conn_t *, tcp_segment_t *);
-extern void tcp_transmit_segment(inet_ep2_t *, tcp_segment_t *);
-extern void tcp_header_setup(tcp_conn_t *, tcp_segment_t *, tcp_header_t *);
-extern void tcp_phdr_setup(tcp_conn_t *, tcp_segment_t *, tcp_phdr_t *);
 
 #endif
Index: uspace/srv/net/tcp/ucall.c
===================================================================
--- uspace/srv/net/tcp/ucall.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/tcp/ucall.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -108,4 +108,5 @@
 		assert(nconn->cstate == st_closed);
 		tcp_conn_unlock(nconn);
+		tcp_conn_delete(nconn);
 		return TCP_ERESET;
 	}
@@ -280,4 +281,8 @@
 {
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "tcp_uc_abort()");
+
+	tcp_conn_lock(conn);
+	tcp_conn_reset(conn);
+	tcp_conn_unlock(conn);
 }
 
Index: uspace/srv/net/udp/assoc.c
===================================================================
--- uspace/srv/net/udp/assoc.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/udp/assoc.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -40,4 +40,5 @@
 #include <fibril_synch.h>
 #include <inet/endpoint.h>
+#include <inet/inet.h>
 #include <io/log.h>
 #include <nettl/amap.h>
@@ -261,7 +262,20 @@
 		return EINVAL;
 
+	/* This association has no local address set. Need to determine one. */
+	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_send - check no local addr");
+	if (inet_addr_is_any(&epp.local.addr) && !assoc->nolocal) {
+		log_msg(LOG_DEFAULT, LVL_DEBUG, "Determine local address.");
+		rc = inet_get_srcaddr(&epp.remote.addr, 0, &epp.local.addr);
+		if (rc != EOK) {
+			log_msg(LOG_DEFAULT, LVL_DEBUG, "Cannot determine "
+			    "local address.");
+			return EINVAL;
+		}
+	}
+
 	log_msg(LOG_DEFAULT, LVL_DEBUG, "udp_assoc_send - check version");
 
-	if (epp.remote.addr.version != epp.local.addr.version)
+	if (!inet_addr_is_any(&epp.local.addr) &&
+	    epp.remote.addr.version != epp.local.addr.version)
 		return EINVAL;
 
Index: uspace/srv/net/udp/service.c
===================================================================
--- uspace/srv/net/udp/service.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/udp/service.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -270,4 +270,28 @@
 }
 
+/** Set association sending messages with no local address.
+ *
+ * Handle client request to set nolocal flag (with parameters unmarshalled).
+ *
+ * @param client   UDP client
+ * @param assoc_id Association ID
+ * @return EOK on success, ENOENT if no such association is found
+ */
+static int udp_assoc_set_nolocal_impl(udp_client_t *client, sysarg_t assoc_id)
+{
+	udp_cassoc_t *cassoc;
+	int rc;
+
+	rc = udp_cassoc_get(client, assoc_id, &cassoc);
+	if (rc != EOK) {
+		assert(rc == ENOENT);
+		return ENOENT;
+	}
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "Setting nolocal to true");
+	cassoc->assoc->nolocal = true;
+	return EOK;
+}
+
 /** Send message via association.
  *
@@ -391,4 +415,25 @@
 	assoc_id = IPC_GET_ARG1(*icall);
 	rc = udp_assoc_destroy_impl(client, assoc_id);
+	async_answer_0(iid, rc);
+}
+
+/** Set association with no local address.
+ *
+ * Handle client request to set no local address flag.
+ *
+ * @param client   UDP client
+ * @param iid      Async request ID
+ * @param icall    Async request data
+ */
+static void udp_assoc_set_nolocal_srv(udp_client_t *client, ipc_callid_t iid,
+    ipc_call_t *icall)
+{
+	sysarg_t assoc_id;
+	int rc;
+
+	log_msg(LOG_DEFAULT, LVL_NOTE, "udp_assoc_set_nolocal_srv()");
+
+	assoc_id = IPC_GET_ARG1(*icall);
+	rc = udp_assoc_set_nolocal_impl(client, assoc_id);
 	async_answer_0(iid, rc);
 }
@@ -662,4 +707,7 @@
 			udp_assoc_destroy_srv(&client, callid, &call);
 			break;
+		case UDP_ASSOC_SET_NOLOCAL:
+			udp_assoc_set_nolocal_srv(&client, callid, &call);
+			break;
 		case UDP_ASSOC_SEND_MSG:
 			udp_assoc_send_msg_srv(&client, callid, &call);
Index: uspace/srv/net/udp/udp_inet.c
===================================================================
--- uspace/srv/net/udp/udp_inet.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/udp/udp_inet.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -35,11 +35,7 @@
  */
 
-#include <bitops.h>
-#include <byteorder.h>
 #include <errno.h>
 #include <inet/inet.h>
 #include <io/log.h>
-#include <stdio.h>
-#include <task.h>
 
 #include "assoc.h"
Index: uspace/srv/net/udp/udp_type.h
===================================================================
--- uspace/srv/net/udp/udp_type.h	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/net/udp/udp_type.h	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -41,4 +41,5 @@
 #include <inet/endpoint.h>
 #include <ipc/loc.h>
+#include <stdbool.h>
 #include <stddef.h>
 #include <inet/addr.h>
@@ -119,4 +120,6 @@
 	/** Receive queue CV. Broadcast when new datagram is inserted */
 	fibril_condvar_t rcv_queue_cv;
+	/** Allow sending messages with no local address */
+	bool nolocal;
 
 	udp_assoc_cb_t *cb;
Index: uspace/srv/ns/clonable.c
===================================================================
--- uspace/srv/ns/clonable.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/ns/clonable.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -31,5 +31,5 @@
  */
 
-#include <ipc/ipc.h>
+#include <async.h>
 #include <ipc/services.h>
 #include <adt/list.h>
@@ -81,5 +81,5 @@
 		/* There was no pending connection request. */
 		printf("%s: Unexpected clonable server.\n", NAME);
-		ipc_answer_0(callid, EBUSY);
+		async_answer_0(callid, EBUSY);
 		return;
 	}
@@ -91,11 +91,17 @@
 	assert(csr->service == SERVICE_LOADER);
 	
-	ipc_answer_0(callid, EOK);
+	async_answer_0(callid, EOK);
 	
-	ipc_forward_fast(csr->callid, phone, csr->iface, csr->arg3, 0,
+	async_sess_t *sess = async_callback_receive(EXCHANGE_SERIALIZE);
+	if (sess == NULL)
+		async_answer_0(callid, EIO);
+	
+	async_exch_t *exch = async_exchange_begin(sess);
+	async_forward_fast(csr->callid, exch, csr->iface, csr->arg3, 0,
 	    IPC_FF_NONE);
+	async_exchange_end(exch);
 	
 	free(csr);
-	ipc_hangup(phone);
+	async_hangup(sess);
 }
 
@@ -117,5 +123,5 @@
 	cs_req_t *csr = malloc(sizeof(cs_req_t));
 	if (csr == NULL) {
-		ipc_answer_0(callid, ENOMEM);
+		async_answer_0(callid, ENOMEM);
 		return;
 	}
@@ -126,5 +132,5 @@
 	if (rc < 0) {
 		free(csr);
-		ipc_answer_0(callid, rc);
+		async_answer_0(callid, rc);
 		return;
 	}
Index: uspace/srv/ns/ns.c
===================================================================
--- uspace/srv/ns/ns.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/ns/ns.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -36,5 +36,6 @@
  */
 
-#include <ipc/ipc.h>
+#include <abi/ipc/methods.h>
+#include <async.h>
 #include <ipc/ns.h>
 #include <ipc/services.h>
@@ -47,4 +48,82 @@
 #include "clonable.h"
 #include "task.h"
+
+static void ns_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
+{
+	ipc_call_t call;
+	ipc_callid_t callid;
+	iface_t iface;
+	service_t service;
+
+	iface = IPC_GET_ARG1(*icall);
+	service = IPC_GET_ARG2(*icall);
+	if (service != 0) {
+		/*
+		 * Client requests to be connected to a service.
+		 */
+		if (service_clonable(service)) {
+			connect_to_clonable(service, iface, icall, iid);
+		} else {
+			connect_to_service(service, iface, icall, iid);
+		}
+		return;
+	}
+	
+	async_answer_0(iid, EOK);
+
+	while (true) {
+		process_pending_conn();
+		
+		callid = async_get_call(&call);
+		if (!IPC_GET_IMETHOD(call))
+			break;
+		
+		task_id_t id;
+		sysarg_t retval;
+		
+		service_t service;
+		sysarg_t phone;
+		
+		switch (IPC_GET_IMETHOD(call)) {
+		case NS_REGISTER:
+			service = IPC_GET_ARG1(call);
+			phone = IPC_GET_ARG5(call);
+			
+			/*
+			 * Server requests service registration.
+			 */
+			if (service_clonable(service)) {
+				register_clonable(service, phone, &call, callid);
+				continue;
+			} else {
+				retval = register_service(service, phone, &call);
+			}
+			
+			break;
+		case NS_PING:
+			retval = EOK;
+			break;
+		case NS_TASK_WAIT:
+			id = (task_id_t)
+			    MERGE_LOUP32(IPC_GET_ARG1(call), IPC_GET_ARG2(call));
+			wait_for_task(id, &call, callid);
+			continue;
+		case NS_ID_INTRO:
+			retval = ns_task_id_intro(&call);
+			break;
+		case NS_RETVAL:
+			retval = ns_task_retval(&call);
+			break;
+		default:
+			printf("ns: method not supported\n");
+			retval = ENOTSUP;
+			break;
+		}
+		
+		async_answer_0(callid, retval);
+	}
+
+	(void) ns_task_disconnect(&call);
+}
 
 int main(int argc, char **argv)
@@ -64,74 +143,8 @@
 		return rc;
 	
+	async_set_fallback_port_handler(ns_connection, NULL);
+	
 	printf("%s: Accepting connections\n", NAME);
-	
-	while (true) {
-		process_pending_conn();
-		
-		ipc_call_t call;
-		ipc_callid_t callid = ipc_wait_for_call(&call);
-		
-		task_id_t id;
-		sysarg_t retval;
-		
-		iface_t iface;
-		service_t service;
-		sysarg_t phone;
-		
-		switch (IPC_GET_IMETHOD(call)) {
-		case IPC_M_PHONE_HUNGUP:
-			retval = ns_task_disconnect(&call);
-			break;
-		case IPC_M_CONNECT_TO_ME:
-			service = IPC_GET_ARG2(call);
-			phone = IPC_GET_ARG5(call);
-			
-			/*
-			 * Server requests service registration.
-			 */
-			if (service_clonable(service)) {
-				register_clonable(service, phone, &call, callid);
-				continue;
-			} else
-				retval = register_service(service, phone, &call);
-			
-			break;
-		case IPC_M_CONNECT_ME_TO:
-			iface = IPC_GET_ARG1(call);
-			service = IPC_GET_ARG2(call);
-			
-			/*
-			 * Client requests to be connected to a service.
-			 */
-			if (service_clonable(service)) {
-				connect_to_clonable(service, iface, &call, callid);
-				continue;
-			} else {
-				connect_to_service(service, iface, &call, callid);
-				continue;
-			}
-			break;
-		case NS_PING:
-			retval = EOK;
-			break;
-		case NS_TASK_WAIT:
-			id = (task_id_t)
-			    MERGE_LOUP32(IPC_GET_ARG1(call), IPC_GET_ARG2(call));
-			wait_for_task(id, &call, callid);
-			continue;
-		case NS_ID_INTRO:
-			retval = ns_task_id_intro(&call);
-			break;
-		case NS_RETVAL:
-			retval = ns_task_retval(&call);
-			break;
-		default:
-			retval = ENOENT;
-			break;
-		}
-		
-		if (!(callid & IPC_CALLID_NOTIFICATION))
-			ipc_answer_0(callid, retval);
-	}
+	async_manager();
 	
 	/* Not reached */
Index: uspace/srv/ns/service.c
===================================================================
--- uspace/srv/ns/service.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/ns/service.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -31,7 +31,7 @@
  */
 
-#include <ipc/ipc.h>
 #include <adt/hash_table.h>
 #include <assert.h>
+#include <async.h>
 #include <errno.h>
 #include <stdio.h>
@@ -47,9 +47,6 @@
 	service_t service;
 	
-	/** Phone registered with the interface */
-	sysarg_t phone;
-	
-	/** Incoming phone hash */
-	sysarg_t in_phone_hash;
+	/** Session to the service */
+	async_sess_t *sess;
 } hashed_service_t;
 
@@ -121,6 +118,8 @@
 		
 		hashed_service_t *hashed_service = hash_table_get_inst(link, hashed_service_t, link);
-		(void) ipc_forward_fast(pending->callid, hashed_service->phone,
-		    pending->iface, pending->arg3, 0, IPC_FF_NONE);
+		async_exch_t *exch = async_exchange_begin(hashed_service->sess);
+		async_forward_fast(pending->callid, exch, pending->iface,
+		    pending->arg3, 0, IPC_FF_NONE);
+		async_exchange_end(exch);
 		
 		list_remove(&pending->link);
@@ -151,9 +150,9 @@
 	
 	hashed_service->service = service;
-	hashed_service->phone = phone;
-	hashed_service->in_phone_hash = call->in_phone_hash;
+	hashed_service->sess = async_callback_receive(EXCHANGE_SERIALIZE);
+	if (hashed_service->sess == NULL)
+		return EIO;
 	
 	hash_table_insert(&service_hash_table, &hashed_service->link);
-	
 	return EOK;
 }
@@ -202,11 +201,11 @@
 	
 	hashed_service_t *hashed_service = hash_table_get_inst(link, hashed_service_t, link);
-	(void) ipc_forward_fast(callid, hashed_service->phone, iface, arg3,
-	    0, IPC_FF_NONE);
+	async_exch_t *exch = async_exchange_begin(hashed_service->sess);
+	async_forward_fast(callid, exch, iface, arg3, 0, IPC_FF_NONE);
+	async_exchange_end(exch);
 	return;
 	
 out:
-	if (!(callid & IPC_CALLID_NOTIFICATION))
-		ipc_answer_0(callid, retval);
+	async_answer_0(callid, retval);
 }
 
Index: uspace/srv/ns/task.c
===================================================================
--- uspace/srv/ns/task.c	(revision 6636fb199c2da579a541a22014f1a1e5321c2a68)
+++ uspace/srv/ns/task.c	(revision 91b604993517a2bdc14cfe1ede6902150ab29533)
@@ -32,6 +32,6 @@
  */
 
-#include <ipc/ipc.h>
 #include <adt/hash_table.h>
+#include <async.h>
 #include <stdbool.h>
 #include <errno.h>
@@ -182,10 +182,7 @@
 			continue;
 		
-		if (!(pr->callid & IPC_CALLID_NOTIFICATION)) {
-			texit = ht->have_rval ? TASK_EXIT_NORMAL :
-			    TASK_EXIT_UNEXPECTED;
-			ipc_answer_2(pr->callid, EOK, texit,
-			    ht->retval);
-		}
+		texit = ht->have_rval ? TASK_EXIT_NORMAL :
+		    TASK_EXIT_UNEXPECTED;
+		async_answer_2(pr->callid, EOK, texit, ht->retval);
 		
 		list_remove(&pr->link);
@@ -203,5 +200,5 @@
 	if (ht == NULL) {
 		/* No such task exists. */
-		ipc_answer_0(callid, ENOENT);
+		async_answer_0(callid, ENOENT);
 		return;
 	}
@@ -210,5 +207,5 @@
 		task_exit_t texit = ht->have_rval ? TASK_EXIT_NORMAL :
 		    TASK_EXIT_UNEXPECTED;
-		ipc_answer_2(callid, EOK, texit, ht->retval);
+		async_answer_2(callid, EOK, texit, ht->retval);
 		return;
 	}
@@ -218,6 +215,5 @@
 	    (pending_wait_t *) malloc(sizeof(pending_wait_t));
 	if (!pr) {
-		if (!(callid & IPC_CALLID_NOTIFICATION))
-			ipc_answer_0(callid, ENOMEM);
+		async_answer_0(callid, ENOMEM);
 		return;
 	}
@@ -282,4 +278,24 @@
 int ns_task_retval(ipc_call_t *call)
 {
+	task_id_t id = call->in_task_id;
+	
+	ht_link_t *link = hash_table_find(&task_hash_table, &id);
+	hashed_task_t *ht = (link != NULL) ?
+	    hash_table_get_inst(link, hashed_task_t, link) : NULL;
+	
+	if ((ht == NULL) || (ht->finished))
+		return EINVAL;
+	
+	ht->finished = true;
+	ht->have_rval = true;
+	ht->retval = IPC_GET_ARG1(*call);
+	
+	process_pending_wait();
+	
+	return EOK;
+}
+
+int ns_task_disconnect(ipc_call_t *call)
+{
 	task_id_t id;
 	int rc = get_id_by_phone(call->in_phone_hash, &id);
@@ -287,27 +303,4 @@
 		return rc;
 	
-	ht_link_t *link = hash_table_find(&task_hash_table, &id);
-	hashed_task_t *ht = (link != NULL) ?
-	    hash_table_get_inst(link, hashed_task_t, link) : NULL;
-	
-	if ((ht == NULL) || (ht->finished))
-		return EINVAL;
-	
-	ht->finished = true;
-	ht->have_rval = true;
-	ht->retval = IPC_GET_ARG1(*call);
-	
-	process_pending_wait();
-	
-	return EOK;
-}
-
-int ns_task_disconnect(ipc_call_t *call)
-{
-	task_id_t id;
-	int rc = get_id_by_phone(call->in_phone_hash, &id);
-	if (rc != EOK)
-		return rc;
-	
 	/* Delete from phone-to-id map. */
 	hash_table_remove(&phone_to_id, &call->in_phone_hash);
