Index: kernel/arch/ia32/Makefile.inc
===================================================================
--- kernel/arch/ia32/Makefile.inc	(revision 8a36bc1e508c3a617d71ab53048d228b31d763c9)
+++ kernel/arch/ia32/Makefile.inc	(revision d6f9fff8ee83683a0df7000a796720690d276858)
@@ -89,4 +89,5 @@
 	arch/$(KARCH)/src/smp/ipi.c \
 	arch/$(KARCH)/src/ia32.c \
+	arch/$(KARCH)/src/vreg.c \
 	arch/$(KARCH)/src/interrupt.c \
 	arch/$(KARCH)/src/pm.c \
Index: kernel/arch/ia32/include/arch/asm.h
===================================================================
--- kernel/arch/ia32/include/arch/asm.h	(revision 8a36bc1e508c3a617d71ab53048d228b31d763c9)
+++ kernel/arch/ia32/include/arch/asm.h	(revision d6f9fff8ee83683a0df7000a796720690d276858)
@@ -439,4 +439,17 @@
 	asm volatile (
 		"ltr %[sel]"
+		:: [sel] "r" (sel)
+	);
+}
+
+/** Load GS from descriptor table.
+ *
+ * @param sel Selector specifying descriptor of the GS segment.
+ *
+ */
+NO_TRACE static inline void gs_load(uint16_t sel)
+{
+	asm volatile (
+		"mov %[sel], %%gs"
 		:: [sel] "r" (sel)
 	);
Index: kernel/arch/ia32/include/arch/context_struct.ag
===================================================================
--- kernel/arch/ia32/include/arch/context_struct.ag	(revision 8a36bc1e508c3a617d71ab53048d228b31d763c9)
+++ kernel/arch/ia32/include/arch/context_struct.ag	(revision d6f9fff8ee83683a0df7000a796720690d276858)
@@ -66,4 +66,9 @@
                 },
                 {
+                        name : tp,
+                        type : uint32_t
+                },
+
+                {
                         name : ipl,
                         type : ipl_t
Index: kernel/arch/ia32/include/arch/pm.h
===================================================================
--- kernel/arch/ia32/include/arch/pm.h	(revision 8a36bc1e508c3a617d71ab53048d228b31d763c9)
+++ kernel/arch/ia32/include/arch/pm.h	(revision d6f9fff8ee83683a0df7000a796720690d276858)
@@ -45,5 +45,5 @@
 #define UDATA_DES  4
 #define TSS_DES    5
-#define TLS_DES    6  /* Pointer to Thread-Local-Storage data */
+#define VREG_DES   6  /* Virtual registers */
 
 #ifdef CONFIG_FB
@@ -169,5 +169,4 @@
 
 extern void tss_initialize(tss_t *t);
-extern void set_tls_desc(uintptr_t tls);
 
 #endif /* __ASM__ */
Index: kernel/arch/ia32/include/arch/proc/thread.h
===================================================================
--- kernel/arch/ia32/include/arch/proc/thread.h	(revision 8a36bc1e508c3a617d71ab53048d228b31d763c9)
+++ kernel/arch/ia32/include/arch/proc/thread.h	(revision d6f9fff8ee83683a0df7000a796720690d276858)
@@ -39,5 +39,4 @@
 
 typedef struct {
-	sysarg_t tls;
 } thread_arch_t;
 
Index: kernel/arch/ia32/include/arch/vreg.h
===================================================================
--- kernel/arch/ia32/include/arch/vreg.h	(revision d6f9fff8ee83683a0df7000a796720690d276858)
+++ kernel/arch/ia32/include/arch/vreg.h	(revision d6f9fff8ee83683a0df7000a796720690d276858)
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2016 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.
+ */
+
+/** @addtogroup ia32
+ * @{
+ */
+/** @file
+ */
+
+#ifndef KERN_ia32_VREG_H_
+#define KERN_ia32_VREG_H_
+
+#define VREG_TP	0
+
+#ifndef __ASM__
+
+#include <typedefs.h>
+
+extern uint32_t *vreg_ptr;
+
+extern void vreg_init(void);
+
+#endif /* __ASM__ */
+
+#endif
+
+/** @}
+ */
Index: kernel/arch/ia32/src/asm.S
===================================================================
--- kernel/arch/ia32/src/asm.S	(revision 8a36bc1e508c3a617d71ab53048d228b31d763c9)
+++ kernel/arch/ia32/src/asm.S	(revision d6f9fff8ee83683a0df7000a796720690d276858)
@@ -183,15 +183,11 @@
 
 	/*
-	 * Save TLS.
-	 */
-	movl %gs, %edx
-	movl %edx, ISTATE_OFFSET_GS(%esp)
-
-	/*
 	 * Switch to kernel selectors.
 	 */
-	movw $(GDT_SELECTOR(KDATA_DES)), %ax
-	movw %ax, %ds
-	movw %ax, %es
+	movl $(GDT_SELECTOR(KDATA_DES)), %eax
+	movl %eax, %ds
+	movl %eax, %es
+	movl $(GDT_SELECTOR(VREG_DES)), %eax
+	movl %eax, %gs
 	
 	/*
@@ -213,10 +209,4 @@
 	
 	/*
-	 * Restore TLS.
-	 */
-	movl ISTATE_OFFSET_GS(%esp), %edx
-	movl %edx, %gs
-	
-	/*
 	 * Prepare return address and userspace stack for SYSEXIT.
 	 */
@@ -252,5 +242,5 @@
 
 	/*
-	 * Save the selector registers.
+	 * Save the segment registers.
 	 */
 	movl %gs, %ecx
@@ -272,4 +262,6 @@
 	movl %eax, %ds
 	movl %eax, %es
+	movl $(GDT_SELECTOR(VREG_DES)), %eax
+	movl %eax, %gs
 		
 	movl $0, ISTATE_OFFSET_EBP_FRAME(%esp)
@@ -284,5 +276,5 @@
 			
 	/*
-	 * Restore the selector registers.
+	 * Restore the segment registers.
 	 */
 	movl ISTATE_OFFSET_GS(%esp), %ecx
@@ -354,5 +346,5 @@
 	
 	/*
-	 * Save the selector registers.
+	 * Save the segment registers.
 	 */
 	movl %gs, %ecx
@@ -374,4 +366,6 @@
 	movl %eax, %ds
 	movl %eax, %es
+	movl $(GDT_SELECTOR(VREG_DES)), %eax
+	movl %eax, %gs
 	
 	/*
Index: kernel/arch/ia32/src/context.S
===================================================================
--- kernel/arch/ia32/src/context.S	(revision 8a36bc1e508c3a617d71ab53048d228b31d763c9)
+++ kernel/arch/ia32/src/context.S	(revision d6f9fff8ee83683a0df7000a796720690d276858)
@@ -29,4 +29,5 @@
 #include <abi/asmtool.h>
 #include <arch/context_struct.h>
+#include <arch/vreg.h>
 
 .text
@@ -48,4 +49,8 @@
 	movl %edi, CONTEXT_OFFSET_EDI(%edx)	# %edi -> ctx->edi
 	movl %ebp, CONTEXT_OFFSET_EBP(%edx)	# %ebp -> ctx->ebp
+
+	mov vreg_ptr, %ecx
+	movl %gs:VREG_TP(%ecx), %ecx
+	movl %ecx, CONTEXT_OFFSET_TP(%edx)
 
 	xorl %eax, %eax		# context_save returns 1
@@ -72,5 +77,9 @@
 
 	movl %edx, 0(%esp)	# put saved pc on stack
+
+	mov vreg_ptr, %ecx
+	movl CONTEXT_OFFSET_TP(%eax), %edx
+	movl %edx, %gs:VREG_TP(%ecx)
+
 	xorl %eax, %eax		# context_restore returns 0
 	ret
-FUNCTION_END(context_restore_arch)
Index: kernel/arch/ia32/src/ia32.c
===================================================================
--- kernel/arch/ia32/src/ia32.c	(revision 8a36bc1e508c3a617d71ab53048d228b31d763c9)
+++ kernel/arch/ia32/src/ia32.c	(revision d6f9fff8ee83683a0df7000a796720690d276858)
@@ -57,4 +57,6 @@
 #include <genarch/multiboot/multiboot.h>
 #include <genarch/multiboot/multiboot2.h>
+#include <arch/pm.h>
+#include <arch/vreg.h>
 
 #ifdef CONFIG_SMP
@@ -96,4 +98,6 @@
 void arch_post_mm_init(void)
 {
+	vreg_init();
+
 	if (config.cpu_active == 1) {
 		/* Initialize IRQ routing */
@@ -122,4 +126,5 @@
 		zone_merge_all();
 	}
+
 }
 
@@ -223,7 +228,4 @@
 sysarg_t sys_tls_set(uintptr_t addr)
 {
-	THREAD->arch.tls = addr;
-	set_tls_desc(addr);
-	
 	return EOK;
 }
Index: kernel/arch/ia32/src/pm.c
===================================================================
--- kernel/arch/ia32/src/pm.c	(revision 8a36bc1e508c3a617d71ab53048d228b31d763c9)
+++ kernel/arch/ia32/src/pm.c	(revision d6f9fff8ee83683a0df7000a796720690d276858)
@@ -41,4 +41,6 @@
 #include <panic.h>
 #include <arch/mm/page.h>
+#include <mm/km.h>
+#include <mm/frame.h>
 #include <mm/slab.h>
 #include <memstr.h>
@@ -51,10 +53,10 @@
 
 /*
- * We have no use for segmentation so we set up flat mode. In this
- * mode, we use, for each privilege level, two segments spanning the
+ * We don't have much use for segmentation so we set up flat mode.
+ * In this mode, we use, for each privilege level, two segments spanning the
  * whole memory. One is for code and one is for data.
  *
- * One is for GS register which holds pointer to the TLS thread
- * structure in it's base.
+ * One special segment apart of that is for the GS register which holds
+ * a pointer to the VREG page in its base.
  */
 descriptor_t gdt[GDT_ITEMS] = {
@@ -71,6 +73,6 @@
 	/* TSS descriptor - set up will be completed later */
 	{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
-	/* TLS descriptor */
-	{ 0xffff, 0, 0, AR_PRESENT | AR_DATA | AR_WRITABLE | DPL_USER, 0xf, 0, 0, 1, 1, 0 },
+	/* VREG descriptor - segment used for virtual registers, will be reinitialized later */
+	{ 0xffff, 0 , 0, AR_PRESENT | AR_DATA | AR_WRITABLE | DPL_USER, 0xf, 0, 0, 1, 1, 0 },
 	/* VESA Init descriptor */
 #ifdef CONFIG_FB
@@ -82,5 +84,5 @@
 static idescriptor_t idt[IDT_ITEMS];
 
-static tss_t tss;
+static tss_t tss0;
 
 tss_t *tss_p = NULL;
@@ -95,6 +97,6 @@
 {
 	d->base_0_15 = base & 0xffff;
-	d->base_16_23 = ((base) >> 16) & 0xff;
-	d->base_24_31 = ((base) >> 24) & 0xff;
+	d->base_16_23 = (base >> 16) & 0xff;
+	d->base_24_31 = (base >> 24) & 0xff;
 }
 
@@ -265,7 +267,6 @@
 		 * the heap hasn't been initialized so far.
 		 */
-		tss_p = &tss;
-	}
-	else {
+		tss_p = &tss0;
+	} else {
 		tss_p = (tss_t *) malloc(sizeof(tss_t), FRAME_ATOMIC);
 		if (!tss_p)
@@ -292,16 +293,4 @@
 }
 
-void set_tls_desc(uintptr_t tls)
-{
-	ptr_16_32_t cpugdtr;
-	descriptor_t *gdt_p;
-
-	gdtr_store(&cpugdtr);
-	gdt_p = (descriptor_t *) cpugdtr.base;
-	gdt_setbase(&gdt_p[TLS_DES], tls);
-	/* Reload gdt register to update GS in CPU */
-	gdtr_load(&cpugdtr);
-}
-
 /** @}
  */
Index: kernel/arch/ia32/src/proc/scheduler.c
===================================================================
--- kernel/arch/ia32/src/proc/scheduler.c	(revision 8a36bc1e508c3a617d71ab53048d228b31d763c9)
+++ kernel/arch/ia32/src/proc/scheduler.c	(revision d6f9fff8ee83683a0df7000a796720690d276858)
@@ -70,7 +70,4 @@
 	CPU->arch.tss->esp0 = kstk;
 	CPU->arch.tss->ss0 = GDT_SELECTOR(KDATA_DES);
-	
-	/* Set up TLS in GS register */
-	set_tls_desc(THREAD->arch.tls);
 }
 
Index: kernel/arch/ia32/src/proc/thread.c
===================================================================
--- kernel/arch/ia32/src/proc/thread.c	(revision 8a36bc1e508c3a617d71ab53048d228b31d763c9)
+++ kernel/arch/ia32/src/proc/thread.c	(revision d6f9fff8ee83683a0df7000a796720690d276858)
@@ -41,5 +41,4 @@
 void thread_create_arch(thread_t *t)
 {
-	t->arch.tls = 0;
 }
 
Index: kernel/arch/ia32/src/userspace.c
===================================================================
--- kernel/arch/ia32/src/userspace.c	(revision 8a36bc1e508c3a617d71ab53048d228b31d763c9)
+++ kernel/arch/ia32/src/userspace.c	(revision d6f9fff8ee83683a0df7000a796720690d276858)
@@ -59,6 +59,6 @@
 		"popfl\n"
 		
-		/* Set up GS register (TLS) */
-		"movl %[tls_des], %%gs\n"
+		/* Set up GS register (virtual register segment) */
+		"movl %[vreg_des], %%gs\n"
 		
 		"pushl %[udata_des]\n"
@@ -81,5 +81,5 @@
 		  [entry] "r" (kernel_uarg->uspace_entry),
 		  [uarg] "r" (kernel_uarg->uspace_uarg),
-		  [tls_des] "r" (GDT_SELECTOR(TLS_DES))
+		  [vreg_des] "r" (GDT_SELECTOR(VREG_DES))
 		: "eax");
 	
Index: kernel/arch/ia32/src/vreg.c
===================================================================
--- kernel/arch/ia32/src/vreg.c	(revision d6f9fff8ee83683a0df7000a796720690d276858)
+++ kernel/arch/ia32/src/vreg.c	(revision d6f9fff8ee83683a0df7000a796720690d276858)
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2016 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.
+ */
+
+/** @addtogroup ia32
+ * @{
+ */
+/** @file
+ */
+
+#include <arch/vreg.h>
+#include <arch/pm.h>
+#include <config.h>
+#include <typedefs.h>
+#include <arch/asm.h>
+#include <panic.h>
+#include <mm/km.h>
+#include <mm/frame.h>
+
+/*
+ * During initialization, we need to make sure that context_save() and
+ * context_restore() touch some meaningful address when saving/restoring VREGs.
+ * When a processor is initialized, we set its GS base to a private page and
+ * vreg_ptr to zero.
+ */
+static uint32_t vreg_tp_dummy;
+uint32_t *vreg_ptr = &vreg_tp_dummy;
+
+void vreg_init(void)
+{
+	uintptr_t frame;
+	uint32_t *page;
+	descriptor_t *gdt_p = (descriptor_t *) gdtr.base;
+
+	frame = frame_alloc(1, FRAME_ATOMIC | FRAME_HIGHMEM, 0);
+	if (!frame)
+		frame = frame_alloc(1, FRAME_ATOMIC | FRAME_LOWMEM, 0);
+	if (!frame)
+		panic("Cannot allocate VREG frame.");
+
+	page = (uint32_t *) km_map(frame, PAGE_SIZE,
+	    PAGE_READ | PAGE_WRITE | PAGE_USER | PAGE_CACHEABLE);
+
+	gdt_setbase(&gdt_p[VREG_DES], (uintptr_t) page);
+	gdt_setlimit(&gdt_p[VREG_DES], sizeof(uint32_t));
+
+	gs_load(GDT_SELECTOR(VREG_DES));
+
+	vreg_ptr = NULL; 
+}
+
+/** @}
+ */
Index: uspace/lib/c/arch/ia32/Makefile.common
===================================================================
--- uspace/lib/c/arch/ia32/Makefile.common	(revision 8a36bc1e508c3a617d71ab53048d228b31d763c9)
+++ uspace/lib/c/arch/ia32/Makefile.common	(revision d6f9fff8ee83683a0df7000a796720690d276858)
@@ -28,7 +28,7 @@
 
 ifeq ($(PROCESSOR),i486)
-	GCC_CFLAGS += -march=i486 -fno-omit-frame-pointer
+	GCC_CFLAGS += -march=i486 -mno-tls-direct-seg-refs -fno-omit-frame-pointer
 else
-	GCC_CFLAGS += -march=pentium -fno-omit-frame-pointer
+	GCC_CFLAGS += -march=pentium -mno-tls-direct-seg-refs -fno-omit-frame-pointer
 endif
 
Index: uspace/lib/c/arch/ia32/include/libarch/tls.h
===================================================================
--- uspace/lib/c/arch/ia32/include/libarch/tls.h	(revision 8a36bc1e508c3a617d71ab53048d228b31d763c9)
+++ uspace/lib/c/arch/ia32/include/libarch/tls.h	(revision d6f9fff8ee83683a0df7000a796720690d276858)
@@ -47,5 +47,5 @@
 static inline void __tcb_set(tcb_t *tcb)
 {
-	__SYSCALL1(SYS_TLS_SET, (sysarg_t) tcb);
+	asm volatile ("movl %0, %%gs:0" :: "r" (tcb));
 }
 
@@ -54,8 +54,5 @@
 	void *retval;
 	
-	asm (
-		"movl %%gs:0, %0"
-		: "=r" (retval)
-	);
+	asm volatile ("movl %%gs:0, %0" : "=r" (retval));
 	
 	return retval;
Index: uspace/lib/c/arch/ia32/src/fibril.S
===================================================================
--- uspace/lib/c/arch/ia32/src/fibril.S	(revision 8a36bc1e508c3a617d71ab53048d228b31d763c9)
+++ uspace/lib/c/arch/ia32/src/fibril.S	(revision d6f9fff8ee83683a0df7000a796720690d276858)
@@ -77,9 +77,6 @@
 	
 	# set thread local storage
-	pushl %edx
 	movl CONTEXT_OFFSET_TLS(%eax), %edx	# Set arg1 to TLS addr
-	movl $1, %eax				# Syscall SYS_TLS_SET
-	int $0x30
-	popl %edx
+	movl %edx, %gs:0
 	
 	xorl %eax, %eax		# context_restore returns 0
