All of lore.kernel.org
 help / color / mirror / Atom feed
From: Gleb Natapov <gleb@redhat.com>
To: Avi Kivity <avi@redhat.com>
Cc: kvm@vger.kernel.org
Subject: Re: [PATCH 4/4] Fix task switching.
Date: Mon, 30 Mar 2009 16:06:19 +0300	[thread overview]
Message-ID: <20090330130619.GC7285@redhat.com> (raw)
In-Reply-To: <49D07729.2020400@redhat.com>

On Mon, Mar 30, 2009 at 10:39:21AM +0300, Avi Kivity wrote:
> Gleb Natapov wrote:
>> The patch fixes two problems with task switching.
>> 1. Back link is written to a wrong TSS.
>> 2. Instruction emulation is not needed if the reason for task switch
>>    is a task gate in IDT and access to it is caused by an external even.
>>
>> 2 is currently solved only for VMX since there is not reliable way to
>> skip an instruction in SVM. We should emulate it instead.
>>
>>   
>
> Looks good, but please split into (at least) two patches.  Also please  
> provide a test case so we don't regress again.
>
This what I am using for testing. After running make you should get
kernel.bin that can be booted from grub. Runs on real HW too. I am
planing to add more test.

Signed-off-by: Gleb Natapov <gleb@redhat.com>
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);
+}
--
			Gleb.

  reply	other threads:[~2009-03-30 13:06 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-03-29 14:12 [PATCH 1/4] Fix handling of a fault during NMI unblocked due to IRET Gleb Natapov
2009-03-29 14:12 ` [PATCH 2/4] Rewrite twisted maze of if() statements with more straightforward switch() Gleb Natapov
2009-03-30  7:35   ` Avi Kivity
2009-03-30 16:03     ` Jan Kiszka
2009-03-29 14:12 ` [PATCH 3/4] Do not zero idt_vectoring_info in vmx_complete_interrupts() Gleb Natapov
2009-03-29 14:12 ` [PATCH 4/4] Fix task switching Gleb Natapov
2009-03-30  7:39   ` Avi Kivity
2009-03-30 13:06     ` Gleb Natapov [this message]
2009-03-30 16:04   ` Jan Kiszka
2009-03-30 16:21     ` Gleb Natapov
2009-03-30 16:35       ` Jan Kiszka
2009-03-30 16:39         ` Gleb Natapov
2009-03-30 16:46         ` Gleb Natapov
2009-03-30 23:54           ` Julian Stecklina
2009-03-31  9:03     ` Kohl, Bernhard (NSN - DE/Munich)
2009-03-31 15:21       ` Kohl, Bernhard (NSN - DE/Munich)
2009-03-31 15:22         ` Gleb Natapov
2009-04-01  7:21           ` Jan Kiszka

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20090330130619.GC7285@redhat.com \
    --to=gleb@redhat.com \
    --cc=avi@redhat.com \
    --cc=kvm@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.