diff --git a/user/test/x86/kvmtest/Makefile b/user/test/x86/kvmtest/Makefile
new file mode 100644
index 0000000..b93935f
--- /dev/null
+++ b/user/test/x86/kvmtest/Makefile
@@ -0,0 +1,33 @@
+CC=gcc
+AS=gcc
+CFLAGS=-m32 -I. -O2 -Wall
+ASFLAGS=-m32 -I.
+OBJS=kernel.o lib.o boot.o memory.o gdt.o idt.o isrs.o tss.o uart.o
+ALLOBJS=$(OBJS) tests/tests.o
+
+PHONY := all
+all: kernel.bin
+	$(MAKE) -C tests
+
+kernel.bin: $(ALLOBJS) kernel.ld
+	ld -T kernel.ld $(ALLOBJS) -o $@
+
+install: kernel.bin
+	cp $< /boot/
+
+tests/tests.o:
+	$(MAKE) -C tests
+
+-include $(OBJS:.o=.d)
+
+# compile and generate dependency info
+%.o: %.c
+	gcc -c $(CFLAGS) $*.c -o $*.o
+	gcc -MM $(CFLAGS) $*.c > $*.d
+
+PHONY += clean
+clean:
+	$(MAKE) -C tests
+	-rm *.o *~ *.d kernel.bin
+
+.PHONY: $(PHONY)
diff --git a/user/test/x86/kvmtest/boot.S b/user/test/x86/kvmtest/boot.S
new file mode 100644
index 0000000..f74015c
--- /dev/null
+++ b/user/test/x86/kvmtest/boot.S
@@ -0,0 +1,357 @@
+/* boot.S - bootstrap the kernel */
+/* Copyright (C) 1999, 2001  Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define ASM     1
+#include <multiboot.h>
+#include <kernel.h>
+
+.text
+
+.globl  start, _start
+start:
+_start:
+jmp     multiboot_entry
+
+/* Align 32 bits boundary. */
+.align  4
+
+/* Multiboot header. */
+multiboot_header:
+/* magic */
+.long   MULTIBOOT_HEADER_MAGIC
+/* flags */
+.long   MULTIBOOT_HEADER_FLAGS
+/* checksum */
+.long   -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
+#ifndef __ELF__
+	/* header_addr */
+	.long   multiboot_header
+	/* load_addr */
+	.long   _start
+	/* load_end_addr */
+	.long   _edata
+	/* bss_end_addr */
+	.long   _end
+	/* entry_addr */
+	.long   multiboot_entry
+#endif /* ! __ELF__ */
+
+	multiboot_entry:
+	/* Initialize the stack pointer. */
+	movl    $(STACK_START), %esp
+
+	/* Reset EFLAGS. */
+	pushl   $0
+	popf
+
+	/* Push the pointer to the Multiboot information structure. */
+	pushl   %ebx
+	/* Push the magic value. */
+	pushl   %eax
+
+	/* Now enter the C main function... */
+	call    cmain
+
+	/* Halt. */
+	pushl   $halt_message
+	pushl   $0
+	call    printk
+
+	loop:   hlt
+	jmp     loop
+
+.globl isr0
+.globl isr1
+.globl isr2
+.globl isr3
+.globl isr4
+.globl isr5
+.globl isr6
+.globl isr7
+.globl isr8
+.globl isr9
+.globl isr10
+.globl isr11
+.globl isr12
+.globl isr13
+.globl isr14
+.globl isr15
+.globl isr16
+.globl isr17
+.globl isr18
+.globl isr19
+.globl isr20
+.globl isr21
+.globl isr22
+.globl isr23
+.globl isr24
+.globl isr25
+.globl isr26
+.globl isr27
+.globl isr28
+.globl isr29
+.globl isr30
+.globl isr31
+
+/* 0: Divide By Zero Exception */
+isr0:
+	cli
+	pushl $0
+	pushl $0
+	jmp isr_common_stub
+
+/*  1: Debug Exception */
+isr1:
+	cli
+	pushl $0
+	pushl $1
+	jmp isr_common_stub
+
+/*  2: Non Maskable Interrupt Exception */
+isr2:
+	cli
+	pushl $0
+	pushl $2
+	jmp isr_common_stub
+
+/*  3: Int 3 Exception */
+isr3:
+	cli
+	pushl $0
+	pushl $3
+	jmp isr_common_stub
+
+/*  4: INTO Exception */
+isr4:
+	cli
+	pushl $0
+	pushl $4
+	jmp isr_common_stub
+
+/*  5: Out of Bounds Exception */
+isr5:
+	cli
+	pushl $0
+	pushl $5
+	jmp isr_common_stub
+
+/*  6: Invalid Opcode Exception */
+isr6:
+	cli
+	pushl $0
+	pushl $6
+	jmp isr_common_stub
+
+/*  7: Coprocessor Not Available Exception */
+isr7:
+	cli
+	pushl $0
+	pushl $7
+	jmp isr_common_stub
+
+/*  8: Double Fault Exception (With Error Code!) */
+isr8:
+	cli
+	pushl $8
+	jmp isr_common_stub
+
+/*  9: Coprocessor Segment Overrun Exception */
+isr9:
+	cli
+	pushl $0
+	pushl $9
+	jmp isr_common_stub
+
+/* 10: Bad TSS Exception (With Error Code!) */
+isr10:
+	cli
+	pushl $10
+	jmp isr_common_stub
+
+/* 11: Segment Not Present Exception (With Error Code!) */
+isr11:
+	cli
+	pushl $11
+	jmp isr_common_stub
+
+/* 12: Stack Fault Exception (With Error Code!) */
+isr12:
+	cli
+	pushl $12
+	jmp isr_common_stub
+
+/* 13: General Protection Fault Exception (With Error Code!) */
+isr13:
+	cli
+	pushl $13
+	jmp isr_common_stub
+
+/* 14: Page Fault Exception (With Error Code!) */
+isr14:
+	cli
+	pushl $14
+	jmp isr_common_stub
+
+/* 15: Reserved Exception */
+isr15:
+	cli
+	pushl $0
+	pushl $15
+	jmp isr_common_stub
+
+/* 16: Floating Point Exception */
+isr16:
+	cli
+	pushl $0
+	pushl $16
+	jmp isr_common_stub
+
+/* 17: Alignment Check Exception */
+isr17:
+	cli
+	pushl $0
+	pushl $17
+	jmp isr_common_stub
+
+/* 18: Machine Check Exception */
+isr18:
+	cli
+	pushl $0
+	pushl $18
+	jmp isr_common_stub
+
+/* 19: Reserved */
+isr19:
+	cli
+	pushl $0
+	pushl $19
+	jmp isr_common_stub
+
+/* 20: Reserved */
+isr20:
+	cli
+	pushl $0
+	pushl $20
+	jmp isr_common_stub
+
+/* 21: Reserved */
+isr21:
+	cli
+	pushl $0
+	pushl $21
+	jmp isr_common_stub
+
+/* 22: Reserved */
+isr22:
+	cli
+	pushl $0
+	pushl $22
+	jmp isr_common_stub
+
+/* 23: Reserved */
+isr23:
+	cli
+	pushl $0
+	pushl $23
+	jmp isr_common_stub
+
+/* 24: Reserved */
+isr24:
+	cli
+	pushl $0
+	pushl $24
+	jmp isr_common_stub
+
+/* 25: Reserved */
+isr25:
+	cli
+	pushl $0
+	pushl $25
+	jmp isr_common_stub
+
+/* 26: Reserved */
+isr26:
+	cli
+	pushl $0
+	pushl $26
+	jmp isr_common_stub
+
+/* 27: Reserved */
+isr27:
+	cli
+	pushl $0
+	pushl $27
+	jmp isr_common_stub
+
+/* 28: Reserved */
+isr28:
+	cli
+	pushl $0
+	pushl $28
+	jmp isr_common_stub
+
+/* 29: Reserved */
+isr29:
+	cli
+	pushl $0
+	pushl $29
+	jmp isr_common_stub
+
+/* 30: Reserved */
+isr30:
+	cli
+	pushl $0
+	pushl $30
+	jmp isr_common_stub
+
+/* 31: Reserved */
+isr31:
+	cli
+	pushl $0
+	pushl $31
+	jmp isr_common_stub
+
+
+/* This is our common ISR stub. It saves the processor state, sets
+ up for kernel mode segments, calls the C-level fault handler,
+ and finally restores the stack frame. */
+isr_common_stub:
+	pusha
+	pushl %ds
+	pushl %es
+	pushl %fs
+	pushl %gs
+	mov $0x10, %ax
+	mov %ax, %ds
+	mov %ax, %es
+	mov %ax, %fs
+	mov %ax, %gs
+	mov %esp, %eax
+	pushl %eax
+	call fault_handler
+	popl %eax
+	popl %gs
+	popl %fs
+	popl %es
+	popl %ds
+	popa
+	add $8, %esp
+	iret
+
+.data
+	halt_message:
+	.asciz  "Halted.\n"
diff --git a/user/test/x86/kvmtest/gdt.c b/user/test/x86/kvmtest/gdt.c
new file mode 100644
index 0000000..38e5735
--- /dev/null
+++ b/user/test/x86/kvmtest/gdt.c
@@ -0,0 +1,84 @@
+#include <kernel.h>
+#include <lib.h>
+
+/* Defines a GDT entry */
+struct gdt_entry
+{
+	uint16_t limit_low;
+	uint16_t base_low;
+	uint8_t base_middle;
+	uint8_t access;
+	uint8_t granularity;
+	uint8_t base_high;
+} __attribute__((packed));
+
+struct gdt_ptr
+{
+	uint16_t limit;
+	uint32_t base;
+} __attribute__((packed));
+
+/* GDT, with 5 entries:
+ * 0x00 - NULL descriptor
+ * 0x08 - Code segment
+ * 0x10 - Data segment
+ * 0x18 - Primery task
+ * 0x20 - Interrupt task */
+static struct gdt_entry gdt[3 + TSS_COUNT];
+static struct gdt_ptr gp;
+
+static void gdt_flush(const struct gdt_ptr *gp_ptr)
+{
+	asm volatile ("lgdt %0\n\t"
+		      "mov $0x10, %%ax\n\t"
+		      "mov %%ax, %%ds\n\t"
+		      "mov %%ax, %%es\n\t"
+		      "mov %%ax, %%fs\n\t"
+		      "mov %%ax, %%gs\n\t"
+		      "mov %%ax, %%ss\n\t"
+		      "jmp $0x08, $.Lflush2\n\t"
+		      ".Lflush2: "::"m"(*gp_ptr));
+}
+
+void gdt_set_gate(int num, uint32_t base, uint32_t limit, uint8_t access, uint8_t gran)
+{
+	/* Setup the descriptor base address */
+	gdt[num].base_low = (base & 0xFFFF);
+	gdt[num].base_middle = (base >> 16) & 0xFF;
+	gdt[num].base_high = (base >> 24) & 0xFF;
+
+	/* Setup the descriptor limits */
+	gdt[num].limit_low = (limit & 0xFFFF);
+	gdt[num].granularity = ((limit >> 16) & 0x0F);
+
+	/* Finally, set up the granularity and access flags */
+	gdt[num].granularity |= (gran & 0xF0);
+	gdt[num].access = access;
+}
+
+void gdt_install(void)
+{
+	/* Setup the GDT pointer and limit */
+	gp.limit = sizeof(gdt) - 1;
+	gp.base = (uint32_t)&gdt;
+
+	memset(gdt, 0, sizeof(gdt));
+
+	/* Our NULL descriptor */
+	gdt_set_gate(0, 0, 0, 0, 0);
+
+	/* The second entry is our Code Segment. The base address
+	 *  is 0, the limit is 4GBytes, it uses 4KByte granularity,
+	 *  uses 32-bit opcodes, and is a Code Segment descriptor.
+	 *  Please check the table above in the tutorial in order
+	 *  to see exactly what each value means */
+	gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xcf);
+
+	/* The third entry is our Data Segment. It's EXACTLY the
+	 *  same as our code segment, but the descriptor type in
+	 *  this entry's access byte says it's a Data Segment */
+	gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xcf);
+
+	/* Flush out the old GDT and install the new changes! */
+	gdt_flush(&gp);
+}
diff --git a/user/test/x86/kvmtest/idt.c b/user/test/x86/kvmtest/idt.c
new file mode 100644
index 0000000..f2d9de7
--- /dev/null
+++ b/user/test/x86/kvmtest/idt.c
@@ -0,0 +1,47 @@
+#include <kernel.h>
+#include <lib.h>
+
+/* Defines an IDT entry */
+struct idt_entry {
+	uint16_t base_lo;
+	uint16_t sel;
+	uint8_t always0;
+	uint8_t flags;
+	uint16_t base_hi;
+} __attribute__((packed));
+
+struct idt_ptr
+{
+	uint16_t limit;
+	uint32_t base;
+} __attribute__((packed));
+
+struct idt_entry idt[256];
+struct idt_ptr idtp;
+
+
+static void idt_load(const struct idt_ptr *idtptr)
+{
+	asm volatile("lidt %0"::"m" (*idtptr));
+}
+
+void idt_set_gate(uint8_t num, uint32_t base, uint16_t sel, uint8_t flags)
+{
+	/* The interrupt routine's base address */
+	idt[num].base_lo = (base & 0xFFFF);
+	idt[num].base_hi = (base >> 16) & 0xFFFF;
+
+	idt[num].sel = sel;
+	idt[num].always0 = 0;
+	idt[num].flags = flags;
+}
+
+void idt_install()
+{
+	idtp.limit = sizeof(idt) - 1;
+	idtp.base = (uint32_t)&idt;
+
+	memset(&idt, 0, sizeof(idt));
+
+	idt_load(&idtp);
+}
diff --git a/user/test/x86/kvmtest/isrs.c b/user/test/x86/kvmtest/isrs.c
new file mode 100644
index 0000000..3dcfe59
--- /dev/null
+++ b/user/test/x86/kvmtest/isrs.c
@@ -0,0 +1,112 @@
+#include <kernel.h>
+#include <lib.h>
+
+/* This defines what the stack looks like after an ISR was running */
+struct regs
+{
+	unsigned int gs, fs, es, ds;      /* pushed the segs last */
+	unsigned int edi, esi, ebp, esp, ebx, edx, ecx, eax;  /* pushed by 'pusha' */
+	unsigned int int_no, err_code;    /* our 'push byte #' and ecodes do this */
+	unsigned int eip, cs, eflags, useresp, ss;   /* pushed by the processor automatically */
+};
+
+extern void isr0();
+extern void isr1();
+extern void isr2();
+extern void isr3();
+extern void isr4();
+extern void isr5();
+extern void isr6();
+extern void isr7();
+extern void isr8();
+extern void isr9();
+extern void isr10();
+extern void isr11();
+extern void isr12();
+extern void isr13();
+extern void isr14();
+extern void isr15();
+extern void isr16();
+extern void isr17();
+extern void isr18();
+extern void isr19();
+extern void isr20();
+extern void isr21();
+extern void isr22();
+extern void isr23();
+extern void isr24();
+extern void isr25();
+extern void isr26();
+extern void isr27();
+extern void isr28();
+extern void isr29();
+extern void isr30();
+extern void isr31();
+
+void (*isrs[32])() = {isr0, isr1, isr2, isr3, isr4, isr5, isr6, isr7, isr8,
+		      isr9, isr10, isr11, isr12, isr13, isr14, isr15, isr16,
+		      isr17, isr18, isr19, isr20, isr21, isr22, isr23, isr24,
+		      isr25, isr26, isr27, isr28, isr29, isr30, isr31};
+
+void isrs_install_one(uint8_t isr)
+{
+	idt_set_gate(isr, (uint32_t)isrs[isr], 0x08, 0x8E);
+}
+
+void isrs_install(void)
+{
+	int i;
+
+	for (i = 0; i < 32; i++)
+		isrs_install_one(i);
+}
+
+char *exception_messages[] =
+{
+	"Division By Zero",
+	"Debug",
+	"Non Maskable Interrupt",
+	"Breakpoint",
+	"Into Detected Overflow",
+	"Out of Bounds",
+	"Invalid Opcode",
+	"No Coprocessor",
+
+	"Double Fault",
+	"Coprocessor Segment Overrun",
+	"Bad TSS",
+	"Segment Not Present",
+	"Stack Fault",
+	"General Protection Fault",
+	"Page Fault",
+	"Unknown Interrupt",
+
+	"Coprocessor Fault",
+	"Alignment Check",
+	"Machine Check",
+	"Reserved",
+	"Reserved",
+	"Reserved",
+	"Reserved",
+	"Reserved",
+
+	"Reserved",
+	"Reserved",
+	"Reserved",
+	"Reserved",
+	"Reserved",
+	"Reserved",
+	"Reserved",
+	"Reserved"
+};
+
+void fault_handler(struct regs *r)
+{
+	if (r->int_no < 32)
+	{
+		printk(0, exception_messages[r->int_no]);
+		printk(0, "Exception. System Halted!\n");
+		for (;;)
+			asm volatile ("hlt");
+	}
+}
diff --git a/user/test/x86/kvmtest/kernel.c b/user/test/x86/kvmtest/kernel.c
new file mode 100644
index 0000000..1920af7
--- /dev/null
+++ b/user/test/x86/kvmtest/kernel.c
@@ -0,0 +1,194 @@
+/* kernel.c - the C part of the kernel */
+/* Copyright (C) 1999  Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <kernel.h>
+#include <multiboot.h>
+#include <lib.h>
+#include <memory.h>
+
+/* Macros. */
+
+/* Check if the bit BIT in FLAGS is set. */
+#define CHECK_FLAG(flags,bit)   ((flags) & (1 << (bit)))
+
+/* Forward declarations. */
+void cmain (unsigned long magic, unsigned long addr);
+
+#if 0
+/* Check if MAGIC is valid and print the Multiboot information structure
+   pointed by ADDR. */
+static void print_boot_info(unsigned long magic, unsigned long addr)
+{
+	multiboot_info_t *mbi;
+
+	/* Am I booted by a Multiboot-compliant boot loader? */
+	if (magic != MULTIBOOT_BOOTLOADER_MAGIC)
+	{
+		printk ("Invalid magic number: 0x%x\n", (unsigned) magic);
+		return;
+	}
+
+	/* Set MBI to the address of the Multiboot information structure. */
+	mbi = (multiboot_info_t *) addr;
+
+	/* Print out the flags. */
+	printk ("flags = 0x%x\n", (unsigned) mbi->flags);
+
+	/* Are mem_* valid? */
+	if (CHECK_FLAG (mbi->flags, 0))
+		printk ("mem_lower = %uKB, mem_upper = %uKB\n",
+				(unsigned) mbi->mem_lower, (unsigned) mbi->mem_upper);
+
+	/* Is boot_device valid? */
+	if (CHECK_FLAG (mbi->flags, 1))
+		printk ("boot_device = 0x%x\n", (unsigned) mbi->boot_device);
+
+	/* Is the command line passed? */
+	if (CHECK_FLAG (mbi->flags, 2))
+		printk ("cmdline = %s\n", (char *) mbi->cmdline);
+
+	/* Are mods_* valid? */
+	if (CHECK_FLAG (mbi->flags, 3))
+	{
+		module_t *mod;
+		int i;
+
+		printk ("mods_count = %d, mods_addr = 0x%x\n",
+				(int) mbi->mods_count, (int) mbi->mods_addr);
+		for (i = 0, mod = (module_t *) mbi->mods_addr;
+				i < mbi->mods_count;
+				i++, mod++)
+			printk (" mod_start = 0x%x, mod_end = 0x%x, string = %s\n",
+					(unsigned) mod->mod_start,
+					(unsigned) mod->mod_end,
+					(char *) mod->string);
+	}
+
+	/* Bits 4 and 5 are mutually exclusive! */
+	if (CHECK_FLAG (mbi->flags, 4) && CHECK_FLAG (mbi->flags, 5))
+	{
+		printk ("Both bits 4 and 5 are set.\n");
+		return;
+	}
+
+	/* Is the symbol table of a.out valid? */
+	if (CHECK_FLAG (mbi->flags, 4))
+	{
+		aout_symbol_table_t *aout_sym = &(mbi->u.aout_sym);
+
+		printk ("aout_symbol_table: tabsize = 0x%0x, "
+				"strsize = 0x%x, addr = 0x%x\n",
+				(unsigned) aout_sym->tabsize,
+				(unsigned) aout_sym->strsize,
+				(unsigned) aout_sym->addr);
+	}
+
+	/* Is the section header table of ELF valid? */
+	if (CHECK_FLAG (mbi->flags, 5))
+	{
+		elf_section_header_table_t *elf_sec = &(mbi->u.elf_sec);
+
+		printk ("elf_sec: num = %u, size = 0x%x,"
+				" addr = 0x%x, shndx = 0x%x\n",
+				(unsigned) elf_sec->num, (unsigned) elf_sec->size,
+				(unsigned) elf_sec->addr, (unsigned) elf_sec->shndx);
+	}
+
+	/* Are mmap_* valid? */
+	if (CHECK_FLAG (mbi->flags, 6))
+	{
+		memory_map_t *mmap;
+
+		printk ("mmap_addr = 0x%x, mmap_length = 0x%x\n",
+				(unsigned) mbi->mmap_addr, (unsigned) mbi->mmap_length);
+		for (mmap = (memory_map_t *) mbi->mmap_addr;
+				(unsigned long) mmap < mbi->mmap_addr + mbi->mmap_length;
+				mmap = (memory_map_t *) ((unsigned long) mmap
+					+ mmap->size + sizeof (mmap->size)))
+			printk (" size = 0x%x, base_addr = 0x%x%x,"
+					" length = 0x%x%x, type = 0x%x\n",
+					(unsigned) mmap->size,
+					(unsigned) mmap->base_addr_high,
+					(unsigned) mmap->base_addr_low,
+					(unsigned) mmap->length_high,
+					(unsigned) mmap->length_low,
+					(unsigned) mmap->type);
+	}
+}
+#endif
+
+uint16_t verbosity;
+
+static void cmd_parse(multiboot_info_t *mbi)
+{
+	char *v;
+
+	if (!CHECK_FLAG (mbi->flags, 2))
+		return;
+
+	v = strstr((char*)mbi->cmdline, "v=");
+
+	if (!v)
+		return;
+
+	verbosity = atoi(v + 2);
+	printk(3, "cmdline = %s\n", (char *) mbi->cmdline);
+}
+
+extern struct test_desc __tests_start[], __tests_end[];
+
+static void run_tests(void)
+{
+	struct test_desc *test;
+
+	for (test = __tests_start; test < __tests_end; test++) {
+		int r;
+		printk(0, "Start test: %s\n", test->name);
+		r = test->fn();
+		if (r < 0) {
+			printk(0, "Critical failure. Exiting.\n");
+			return;
+		} else if (r == TEST_FAIL)
+			printk(0, "Test fails\n");
+		else
+			printk(0, "Test Succeeds\n");
+	}
+}
+
+void cmain(unsigned long magic, unsigned long addr)
+{
+	cmd_parse((multiboot_info_t*)addr);
+
+	gdt_install();
+	tss_install();
+	uart_init();
+
+	kalloc_init(_kernel_end, KALLOC_SIZE);
+
+	/* Clear the screen. */
+	cls ();
+
+	printk(3, "kernel start=0x%x end=0x%x\n", _kernel_start, _kernel_end);
+	printk(3, "kalloc_size=%d\n", KALLOC_SIZE);
+
+	idt_install();
+	isrs_install();
+
+	run_tests();
+/*	print_boot_info(magic, addr); */
+}
+
diff --git a/user/test/x86/kvmtest/kernel.h b/user/test/x86/kvmtest/kernel.h
new file mode 100644
index 0000000..a2df4a2
--- /dev/null
+++ b/user/test/x86/kvmtest/kernel.h
@@ -0,0 +1,68 @@
+#ifndef _KERNEL_H
+#define _KERNEL_H
+#ifndef ASM
+#include <stdint.h>
+
+
+typedef uint32_t size_t;
+#define NULL ((void*)0)
+
+/* 1M for dynamic memory management */
+#define KALLOC_SIZE (1024*1024 - (_kernel_end - _kernel_start))
+#define TSS_COUNT 4
+#define TSS_GDT_OFFSET 3
+
+extern char _kernel_start[], _kernel_end[];
+
+static inline void outb(int addr, int val)
+{
+	asm volatile ("outb %b1, %w0" : : "d" (addr), "a" (val));
+}
+
+static inline uint8_t inb(int addr)
+{
+	uint8_t val;
+	asm volatile ("inb %w1, %b0" : "=a" (val) : "d" (addr));
+	return val;
+}
+
+void gdt_install(void);
+void gdt_set_gate(int num, uint32_t base, uint32_t limit, uint8_t access,
+		  uint8_t gran);
+void idt_install(void);
+void idt_set_gate(uint8_t num, uint32_t base, uint16_t sel, uint8_t flags);
+
+void isrs_install(void);
+void isrs_install_one(uint8_t isr);
+
+void tss_install(void);
+void tss_setup(uint8_t gate, uint8_t desc, void (*fn)(void));
+void tss_info(void);
+
+void uart_init(void);
+void serial_outch(char c);
+char serial_inch(void);
+
+#define TEST_FAIL 0
+#define TEST_SUCCEED 1
+#define TEST_FAIL_EXIT -1
+typedef int (*testfn_t)(void);
+
+struct test_desc {
+	char *name;
+	testfn_t fn;
+};
+
+#define define_test(_fn, _name)						\
+        static struct test_desc __test_##fn __attribute__((__used__))	\
+        __attribute__((__section__(".tests"))) = {.name = _name, .fn = _fn}
+
+extern uint16_t verbosity;
+
+#endif /* ASM */
+
+/* put stack somewhere in low memory */
+#define STACK_START   0x80000
+#define INT_STACK_START   0x70000
+
+#endif
diff --git a/user/test/x86/kvmtest/kernel.ld b/user/test/x86/kvmtest/kernel.ld
new file mode 100644
index 0000000..4be48e3
--- /dev/null
+++ b/user/test/x86/kvmtest/kernel.ld
@@ -0,0 +1,28 @@
+OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
+OUTPUT_ARCH(i386)
+ENTRY(start)
+SECTIONS
+{
+  . = 0x100000;
+  _kernel_start = .;
+  .text : {
+    *(.text)
+  }
+  .rodata : {
+    *(.rodata)
+  }
+  .data  : {
+    *(.data)
+  }
+  .bss  : {
+    *(.bss)
+  }
+  __tests_start = .;
+  .tests : {
+    *(.tests)
+  }
+  __tests_end = .;
+  . = ALIGN(4096);
+  _kernel_end = .;
+  /DISCARD/ : { *(.comment) }
+}
\ No newline at end of file
diff --git a/user/test/x86/kvmtest/lib.c b/user/test/x86/kvmtest/lib.c
new file mode 100644
index 0000000..e9a86ca
--- /dev/null
+++ b/user/test/x86/kvmtest/lib.c
@@ -0,0 +1,296 @@
+#include <kernel.h>
+#include <lib.h>
+
+/* Some screen stuff. */
+/* The number of columns. */
+#define COLUMNS                 80
+/* The number of lines. */
+#define LINES                   24
+/* The attribute of an character. */
+#define ATTRIBUTE               7
+/* The video memory address. */
+#define VIDEO                   0xB8000
+
+/* Variables. */
+/* Save the X position. */
+static int xpos;
+/* Save the Y position. */
+static int ypos;
+/* Point to the video memory. */
+static volatile unsigned char *video;
+
+/* Clear the screen and initialize VIDEO, XPOS and YPOS. */
+void cls (void)
+{
+	int i;
+
+	video = (unsigned char *) VIDEO;
+
+	for (i = 0; i < COLUMNS * LINES * 2; i++)
+		*(video + i) = 0;
+
+	xpos = 0;
+	ypos = 0;
+}
+
+/* Convert the integer D to a string and save the string in BUF. If
+   BASE is equal to 'd', interpret that D is decimal, and if BASE is
+   equal to 'x', interpret that D is hexadecimal. */
+void itoa (char *buf, int base, int d)
+{
+	char *p = buf;
+	char *p1, *p2;
+	unsigned long ud = d;
+	int divisor = 10;
+
+	/* If %d is specified and D is minus, put `-' in the head. */
+	if (base == 'd' && d < 0)
+	{
+		*p++ = '-';
+		buf++;
+		ud = -d;
+	}
+	else if (base == 'x')
+		divisor = 16;
+
+	/* Divide UD by DIVISOR until UD == 0. */
+	do
+	{
+		int remainder = ud % divisor;
+
+		*p++ = (remainder < 10) ? remainder + '0' : remainder + 'a' - 10;
+	}
+	while (ud /= divisor);
+
+	/* Terminate BUF. */
+	*p = 0;
+
+	/* Reverse BUF. */
+	p1 = buf;
+	p2 = p - 1;
+	while (p1 < p2)
+	{
+		char tmp = *p1;
+		*p1 = *p2;
+		*p2 = tmp;
+		p1++;
+		p2--;
+	}
+}
+
+int isdigit(int c)
+{
+	return c >= '0' && c <='9';
+}
+
+int isupper(int c)
+{
+	return c >= 'A' && c <= 'Z';
+}
+
+int islower(int c)
+{
+	return c >= 'a' && c <= 'z';
+}
+
+int atoi_base(const char *buf, int base)
+{
+	int minus,val,digit,base_1;
+	char c;
+
+	base_1 = base - 1;
+
+	if((minus = *buf == '-'))
+		buf++;
+
+	val = 0;
+	while ((c = *buf++)) {
+		if (isdigit(c))
+			digit = c - 48;
+		else if (isupper(c))
+			digit = c - 'A' + 10;
+		else if (islower(c))
+			digit = c - 'a' + 10;
+		else
+			break;
+
+		if (digit < 0 || digit > base_1)
+			break;
+
+		val = base*val + digit;
+	}
+
+	return minus ? -val : val;
+}
+
+/* Put the character C on the screen. */
+static void kputchar (int c)
+{
+#ifdef QEMU
+	outb(0x504, c);
+#endif
+	serial_outch(c);
+
+	if (c == '\n' || c == '\r')
+	{
+newline:
+		xpos = 0;
+		ypos++;
+		if (ypos >= LINES)
+			ypos = 0;
+		return;
+	}
+
+	*(video + (xpos + ypos * COLUMNS) * 2) = c & 0xFF;
+	*(video + (xpos + ypos * COLUMNS) * 2 + 1) = ATTRIBUTE;
+
+	xpos++;
+	if (xpos >= COLUMNS)
+		goto newline;
+}
+
+/* Format a string and print it on the screen, just like the libc
+   function printf. */
+void printk (uint16_t level, const char *format, ...)
+{
+	char **arg = (char **) &format;
+	int c;
+	char buf[20];
+
+	if (level > verbosity)
+		return;
+
+	arg++;
+
+	while ((c = *format++) != 0)
+	{
+		if (c != '%')
+			kputchar (c);
+		else
+		{
+			char *p;
+
+			c = *format++;
+			switch (c)
+			{
+				case 'd':
+				case 'u':
+				case 'x':
+					itoa (buf, c, *((int *) arg++));
+					p = buf;
+					goto string;
+					break;
+
+				case 's':
+					p = *arg++;
+					if (! p)
+						p = "(null)";
+
+string:
+					while (*p)
+						kputchar (*p++);
+					break;
+
+				default:
+					kputchar (*((int *) arg++));
+					break;
+			}
+		}
+	}
+}
+
+void *memset(void *s, int c, size_t n)
+{
+	char *ss = s;
+	int i;
+
+	for (i = 0; i < n; i++)
+		ss[i] = c;
+
+	return s;
+}
+
+char *strstr(const char *phaystack, const char *pneedle)
+{
+	const unsigned char *haystack, *needle;
+	char b, c;
+
+	haystack = (const unsigned char *) phaystack;
+	needle = (const unsigned char *) pneedle;
+
+	b = *needle;
+	if (b != '\0')
+	{
+		haystack--;   /* possible ANSI violation */
+		do
+		{
+			c = *++haystack;
+			if (c == '\0')
+				goto ret0;
+		}
+		while (c != b);
+
+		c = *++needle;
+		if (c == '\0')
+			goto foundneedle;
+		++needle;
+		goto jin;
+
+		for (;;)
+		{
+			char a;
+			const unsigned char *rhaystack, *rneedle;
+
+			do
+			{
+				a = *++haystack;
+				if (a == '\0')
+					goto ret0;
+				if (a == b)
+					break;
+				a = *++haystack;
+				if (a == '\0')
+					goto ret0;
+			shloop:;
+			}
+			while (a != b);
+
+		jin:
+			a = *++haystack;
+			if (a == '\0')
+				goto ret0;
+
+			if (a != c)
+				goto shloop;
+
+			rhaystack = haystack-- + 1;
+			rneedle = needle;
+			a = *rneedle;
+
+			if (*rhaystack == a)
+				do
+				{
+					if (a == '\0')
+						goto foundneedle;
+					++rhaystack;
+					a = *++needle;
+					if (*rhaystack != a)
+						break;
+					if (a == '\0')
+						goto foundneedle;
+					++rhaystack;
+					a = *++needle;
+				}
+				while (*rhaystack == a);
+
+			needle = rneedle; /* took the register-poor approach */
+
+			if (a == '\0')
+				break;
+		}
+	}
+foundneedle:
+	return (char*) haystack;
+ret0:
+	return 0;
+}
diff --git a/user/test/x86/kvmtest/lib.h b/user/test/x86/kvmtest/lib.h
new file mode 100644
index 0000000..b00849c
--- /dev/null
+++ b/user/test/x86/kvmtest/lib.h
@@ -0,0 +1,13 @@
+#ifndef _LIB_H
+#define _LIB_H
+void cls(void);
+void itoa(char *buf, int base, int d);
+void printk(uint16_t level, const char *format, ...);
+void *memset(void *s, int c, size_t n);
+char *strstr(const char *phaystack, const char *pneedle);
+int atoi_base(const char *buf, int base);
+static inline int atoi(const char *buf)
+{
+	return atoi_base(buf, 10);
+}
+#endif
diff --git a/user/test/x86/kvmtest/memory.c b/user/test/x86/kvmtest/memory.c
new file mode 100644
index 0000000..1f0e7f2
--- /dev/null
+++ b/user/test/x86/kvmtest/memory.c
@@ -0,0 +1,100 @@
+#include <kernel.h>
+#include <memory.h>
+
+#define ALIGNMASK 1U
+#define ALIGN(s) (((s) + ALIGNMASK) & ~ALIGNMASK)
+#define POFF ALIGN(sizeof(size_t))
+#define MINSIZ ALIGN(sizeof(struct cell *))
+#define C2P(c) ((char *)(c) + POFF)
+#define P2C(p) ((struct cell *)((char *)(p) - POFF))
+#define ISADJ(c1,c2) ((struct cell *)(C2P(c1) + (c1)->size) == (struct cell *)(c2))
+
+struct cell {
+	size_t size;
+	struct cell *next;
+};
+
+static struct mem_pool {
+	struct cell *tail;
+} *kmem;
+
+void kalloc_init(void *mem, size_t size)
+{
+	kmem->tail = mem;
+	kmem->tail->size = size - POFF;
+	kmem->tail->next = kmem->tail;
+}
+
+void *kalloc(size_t size)
+{
+	struct cell *c1, *c2, *c3;
+
+	size = size < MINSIZ ? MINSIZ : ALIGN(size);
+
+	c1 = kmem->tail;
+	while (c1->next->size < size) {
+		if (c1->next == kmem->tail)
+			return NULL;
+		c1 = c1->next;
+	}
+	c2 = c1->next;
+	if (c2->size > (POFF + size)) { /* split new cell */
+		c3 = (struct cell *)(C2P(c2) + size);
+		c3->size = c2->size - (size + POFF);
+		c3->next = c2->next;
+		c2->size = size;
+		c1->next = c3;
+	} else { /* use the entire cell */
+		c1->next = c2->next;
+		if (c2 == kmem->tail) {
+			kmem->tail = c1;
+		}
+	}
+
+	return C2P(c2);
+}
+
+void kfree(void *ptr)
+{
+	struct cell *c1, *c2, *c3;
+	int j1, j2;
+
+/* splice the cell back into the list */
+	c1 = kmem->tail;
+	c2 = P2C(ptr);
+
+	if (c2 > c1) { /* append to end of list */
+		if (ISADJ(c1,c2)) { /* join with last cell */
+			c1->size += POFF + c2->size;
+			return;
+		}
+		c2->next = c1->next;
+		c1->next = c2;
+		kmem->tail = c2;
+		return;
+	}
+
+	while (c1->next < c2) { /* find insertion point */
+		c1 = c1->next;
+	}
+	c3 = c1->next;
+
+	j1 = ISADJ(c1,c2); /* c1 and c2 need to be joined */
+	j2 = ISADJ(c2,c3); /* c2 and c3 need to be joined */
+
+	if (j1) {
+		if (j2) { /* splice all three cells together */
+			c1->next = c3->next;
+			c1->size += POFF + c3->size;
+		}
+		c1->size += POFF + c2->size;
+	} else {
+		c1->next = c2;
+		if (j2) {
+			c2->next = c3->next;
+			c2->size += POFF + c3->size;
+		} else {
+			c2->next = c3;
+		}
+	}
+}
diff --git a/user/test/x86/kvmtest/memory.h b/user/test/x86/kvmtest/memory.h
new file mode 100644
index 0000000..9a0c984
--- /dev/null
+++ b/user/test/x86/kvmtest/memory.h
@@ -0,0 +1,6 @@
+#ifndef _MEMORY_H
+#define _MEMORY_H
+void kalloc_init(void *mem, size_t size);
+void *kalloc(size_t size);
+void kfree(void *ptr);
+#endif
diff --git a/user/test/x86/kvmtest/multiboot.h b/user/test/x86/kvmtest/multiboot.h
new file mode 100644
index 0000000..85a4605
--- /dev/null
+++ b/user/test/x86/kvmtest/multiboot.h
@@ -0,0 +1,109 @@
+/* multiboot.h - the header for Multiboot */
+/* Copyright (C) 1999, 2001  Free Software Foundation, Inc.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Macros. */
+
+/* The magic number for the Multiboot header. */
+#define MULTIBOOT_HEADER_MAGIC          0x1BADB002
+
+/* The flags for the Multiboot header. */
+#ifdef __ELF__
+# define MULTIBOOT_HEADER_FLAGS         0x00000003
+#else
+# define MULTIBOOT_HEADER_FLAGS         0x00010003
+#endif
+
+/* The magic number passed by a Multiboot-compliant boot loader. */
+#define MULTIBOOT_BOOTLOADER_MAGIC      0x2BADB002
+
+#ifndef ASM
+/* Do not include here in boot.S. */
+
+/* Types. */
+
+/* The Multiboot header. */
+typedef struct multiboot_header
+{
+	unsigned long magic;
+	unsigned long flags;
+	unsigned long checksum;
+	unsigned long header_addr;
+	unsigned long load_addr;
+	unsigned long load_end_addr;
+	unsigned long bss_end_addr;
+	unsigned long entry_addr;
+} multiboot_header_t;
+
+/* The symbol table for a.out. */
+typedef struct aout_symbol_table
+{
+	unsigned long tabsize;
+	unsigned long strsize;
+	unsigned long addr;
+	unsigned long reserved;
+} aout_symbol_table_t;
+
+/* The section header table for ELF. */
+typedef struct elf_section_header_table
+{
+	unsigned long num;
+	unsigned long size;
+	unsigned long addr;
+	unsigned long shndx;
+} elf_section_header_table_t;
+
+/* The Multiboot information. */
+typedef struct multiboot_info
+{
+	unsigned long flags;
+	unsigned long mem_lower;
+	unsigned long mem_upper;
+	unsigned long boot_device;
+	unsigned long cmdline;
+	unsigned long mods_count;
+	unsigned long mods_addr;
+	union
+	{
+		aout_symbol_table_t aout_sym;
+		elf_section_header_table_t elf_sec;
+	} u;
+	unsigned long mmap_length;
+	unsigned long mmap_addr;
+} multiboot_info_t;
+
+/* The module structure. */
+typedef struct module
+{
+	unsigned long mod_start;
+	unsigned long mod_end;
+	unsigned long string;
+	unsigned long reserved;
+} module_t;
+
+/* The memory map. Be careful that the offset 0 is base_addr_low
+   but no size. */
+typedef struct memory_map
+{
+	unsigned long size;
+	unsigned long base_addr_low;
+	unsigned long base_addr_high;
+	unsigned long length_low;
+	unsigned long length_high;
+	unsigned long type;
+} memory_map_t;
+
+#endif /* ! ASM */
diff --git a/user/test/x86/kvmtest/tests/Makefile b/user/test/x86/kvmtest/tests/Makefile
new file mode 100644
index 0000000..751d352
--- /dev/null
+++ b/user/test/x86/kvmtest/tests/Makefile
@@ -0,0 +1,22 @@
+CC=gcc
+AS=gcc
+CFLAGS=-m32 -I. -I.. -O2 -Wall
+ASFLAGS=-m32 -I. -I..
+OBJS=tss_test.o
+
+PHONY := all
+all: tests.o
+
+tests.o: $(OBJS)
+	ld -m elf_i386 -r $(OBJS) -o $@
+
+-include $(OBJS:.o=.d)
+
+# compile and generate dependency info
+%.o: %.c
+	gcc -c $(CFLAGS) $*.c -o $*.o
+	gcc -MM $(CFLAGS) $*.c > $*.d
+
+PHONY += clean
+clean:
+	-rm *.o *~ *.d
diff --git a/user/test/x86/kvmtest/tests/tss_test.c b/user/test/x86/kvmtest/tests/tss_test.c
new file mode 100644
index 0000000..835dc0b
--- /dev/null
+++ b/user/test/x86/kvmtest/tests/tss_test.c
@@ -0,0 +1,74 @@
+#include <kernel.h>
+#include <lib.h>
+
+
+static void nmi_tss(void)
+{
+start:
+	printk(0, "NMI task is running\n");
+	tss_info();
+	asm volatile ("iret");
+	printk(0, "NMI task restart after iret.\n");
+	goto start;
+}
+
+static int test_divider;
+
+static void de_tss(void)
+{
+start:
+	printk(0, "DE task is running\n");
+	tss_info();
+	test_divider = 10;
+	asm volatile ("iret");
+	goto start;
+}
+
+static void of_tss(void)
+{
+start:
+	printk(0, "OF task is running\n");
+	tss_info();
+	asm volatile ("iret");
+	goto start;
+}
+
+static int tss_test(void)
+{
+	int ret = TEST_SUCCEED, res;
+
+	tss_setup(2, 1, nmi_tss);
+	tss_setup(0, 2, de_tss);
+	tss_setup(4, 3, of_tss);
+
+	printk(0, "Triggering nmi\n");
+	asm volatile ("int $2");
+	printk(0, "Return from nmi 1\n");
+	asm volatile ("int $2");
+	printk(0, "Return from nmi 2\n");
+	printk(0, "Try to devide by 0\n");
+	res = 1500 / test_divider;
+	printk(0, "Result is %d\n", res);
+	if (res != 150) {
+		ret = TEST_FAIL;
+		goto restore_isrs;
+	}
+	printk(0, "Call int 0\n");
+	asm volatile ("int $0");
+	printk(0, "Return from int 0\n");
+	printk(0, "Call into\n");
+	asm volatile ("addb $127, %b0\ninto"::"a"(127));
+	printk(0, "Return from into\n");
+	printk(0, "Calling nmi task by lcall\n");
+	asm volatile("lcall $0x20, $0");
+	printk(0, "Return from call\n");
+
+restore_isrs:
+	isrs_install_one(0);
+	isrs_install_one(2);
+	isrs_install_one(4);
+
+	return ret;
+}
+
+define_test(tss_test, "TSS test");
diff --git a/user/test/x86/kvmtest/tss.c b/user/test/x86/kvmtest/tss.c
new file mode 100644
index 0000000..1d73531
--- /dev/null
+++ b/user/test/x86/kvmtest/tss.c
@@ -0,0 +1,108 @@
+#include <kernel.h>
+#include <lib.h>
+
+struct tss_desc {
+	uint16_t   link;
+	uint16_t   link_h;
+
+	uint32_t   esp0;
+	uint16_t   ss0;
+	uint16_t   ss0_h;
+	
+	uint32_t   esp1;
+	uint16_t   ss1;
+	uint16_t   ss1_h;
+	
+	uint32_t   esp2;
+	uint16_t   ss2;
+	uint16_t   ss2_h;
+	
+	uint32_t   cr3;
+	uint32_t   eip;
+	uint32_t   eflags;
+	
+	uint32_t   eax;
+	uint32_t   ecx;
+	uint32_t   edx;
+	uint32_t   ebx;
+	
+	uint32_t   esp;
+	uint32_t   ebp;
+	
+	uint32_t   esi;
+	uint32_t   edi;
+	
+	uint16_t   es;
+	uint16_t   es_h;
+	
+	uint16_t   cs;
+	uint16_t   cs_h;
+	
+	uint16_t   ss;
+	uint16_t   ss_h;
+	
+	uint16_t   ds;
+	uint16_t   ds_h;
+	
+	uint16_t   fs;
+	uint16_t   fs_h;
+	
+	uint16_t   gs;
+	uint16_t   gs_h;
+	
+	uint16_t   ldt;
+	uint16_t   ldt_h;
+	
+	uint16_t   trap;
+	uint16_t   iomap;
+} __attribute__ ((packed));
+
+static struct tss_desc tss[TSS_COUNT];
+
+void tss_install(void)
+{
+	uint16_t desc_size = sizeof(struct tss_desc);
+	int i;
+
+	for (i = 0; i < TSS_COUNT; i++) {
+		tss[i].ss0 = tss[i].ss1 = tss[i].ss2 = 0x10;
+		tss[i].esp0 = tss[i].esp1 = tss[i].esp2 = INT_STACK_START;
+		tss[i].cs = 0x08;
+		tss[i].ds = tss[i].es = tss[i].fs = tss[i].gs = tss[i].ss =0x10;
+		tss[i].esp = INT_STACK_START;
+		tss[i].iomap = (uint16_t)desc_size;
+		gdt_set_gate(TSS_GDT_OFFSET + i, (uint32_t)&tss[i],
+			     desc_size - 1, 0x89, 0x0f);
+	}
+
+	tss[0].esp0 = tss[0].esp1 = tss[0].esp2 = tss[0].esp = STACK_START;
+
+	asm volatile ( "ltr %%ax" : : "a" ( 0x18 ) );
+}
+
+void tss_setup(uint8_t gate, uint8_t i, void (*fn)(void))
+{
+	if (i >= TSS_COUNT) {
+		printk(0, "Try to setup TSS out if bound %d\n", tss);
+		return;
+	}
+	tss[i].eip = (uint32_t)fn;
+	printk(2, "TSS set gate %d to TSS %x\n", gate,
+	       (i + TSS_GDT_OFFSET) << 3);
+	idt_set_gate(gate, 0, (i + TSS_GDT_OFFSET) << 3, 0x85);
+}
+
+void tss_info(void)
+{
+	uint16_t tr, i;
+
+	asm volatile ("str %0":"=r"(tr));
+
+	i = (tr >> 3) - TSS_GDT_OFFSET;
+
+	if (i >= TSS_COUNT)
+		printk(0, "Current TR %x is wrong!\n", tr);
+
+	printk(0, "TR=%x Main TSS back link %x. Current TSS back link %x\n",
+	       tr, tss[0]. link, tss[i].link);
+}
diff --git a/user/test/x86/kvmtest/uart.c b/user/test/x86/kvmtest/uart.c
new file mode 100644
index 0000000..d9661d1
--- /dev/null
+++ b/user/test/x86/kvmtest/uart.c
@@ -0,0 +1,43 @@
+#include <kernel.h>
+
+#define PORT1 0x3F8
+#define PORT2 0x2F8
+#define PORT3 0x3E8
+#define PORT4 0x2E8
+
+#define BAUD38400  0x03
+#define BAUD115200 0x01
+#define BAUD57600  0x02
+#define BAUD19200  0x06
+#define BAUD9600   0x0C
+#define BAUD4800   0x18
+#define BAUD2400   0x30
+
+void uart_init(void)
+{
+	outb(PORT1 + 1 , 0);   /* Turn off interrupts */
+
+	outb(PORT1 + 3 , 0x80);  /* SET DLAB ON */
+	/* Set Baud rate - Divisor Latch Low Byte */
+	outb(PORT1 + 0 , BAUD38400);
+	/* Set Baud rate - Divisor Latch High Byte */
+	outb(PORT1 + 1 , 0x00);
+	outb(PORT1 + 3 , 0x03);  /* 8 Bits, No Parity, 1 Stop Bit */
+	outb(PORT1 + 2 , 0xC7);  /* FIFO Control Register */
+	outb(PORT1 + 4 , 0x0B);  /* Turn on DTR, RTS, and OUT2 */
+}
+
+void serial_outch(char c)
+{
+	outb(PORT1, c);
+}
+
+char serial_inch(void)
+{
+	char c;
+	do {
+		c = inb(PORT1 + 5);
+		if (c & 1)
+			return inb(PORT1);
+	} while(1);
+}
