From patchwork Mon Mar 30 13:06:19 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gleb Natapov X-Patchwork-Id: 15130 Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n2UD6aD9031289 for ; Mon, 30 Mar 2009 13:06:37 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752214AbZC3NGc (ORCPT ); Mon, 30 Mar 2009 09:06:32 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1752160AbZC3NGc (ORCPT ); Mon, 30 Mar 2009 09:06:32 -0400 Received: from mx2.redhat.com ([66.187.237.31]:50963 "EHLO mx2.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752069AbZC3NG3 (ORCPT ); Mon, 30 Mar 2009 09:06:29 -0400 Received: from int-mx2.corp.redhat.com (int-mx2.corp.redhat.com [172.16.27.26]) by mx2.redhat.com (8.13.8/8.13.8) with ESMTP id n2UD6OnU012415 for ; Mon, 30 Mar 2009 09:06:24 -0400 Received: from ns3.rdu.redhat.com (ns3.rdu.redhat.com [10.11.255.199]) by int-mx2.corp.redhat.com (8.13.1/8.13.1) with ESMTP id n2UD6L4H001747; Mon, 30 Mar 2009 09:06:22 -0400 Received: from dhcp-1-237.tlv.redhat.com (dhcp-1-237.tlv.redhat.com [10.35.1.237]) by ns3.rdu.redhat.com (8.13.8/8.13.8) with ESMTP id n2UD6KIf016479; Mon, 30 Mar 2009 09:06:20 -0400 Received: by dhcp-1-237.tlv.redhat.com (Postfix, from userid 587) id A8ED518D41C; Mon, 30 Mar 2009 16:06:19 +0300 (IDT) Date: Mon, 30 Mar 2009 16:06:19 +0300 From: Gleb Natapov To: Avi Kivity Cc: kvm@vger.kernel.org Subject: Re: [PATCH 4/4] Fix task switching. Message-ID: <20090330130619.GC7285@redhat.com> References: <20090329141202.30481.91797.stgit@trex.usersys.redhat.com> <20090329141218.30481.92675.stgit@trex.usersys.redhat.com> <49D07729.2020400@redhat.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <49D07729.2020400@redhat.com> X-Scanned-By: MIMEDefang 2.58 on 172.16.27.26 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org 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. -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html 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 +#include + +.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 +#include + +/* 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 +#include + +/* 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 +#include + +/* 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 +#include +#include +#include + +/* 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 + + +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 +#include + +/* 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 +#include + +#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 +#include + + +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 +#include + +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 + +#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); +}