From: Jan Kiszka <[email protected]> This implements a basic task switch test for 32-bit targets. It specifically stresses the case that a fault with attached error code triggers the switch via a task gate.
Signed-off-by: Jan Kiszka <[email protected]> Signed-off-by: Marcelo Tosatti <[email protected]> diff --git a/kvm/user/config-i386.mak b/kvm/user/config-i386.mak index 09175d5..a9becfc 100644 --- a/kvm/user/config-i386.mak +++ b/kvm/user/config-i386.mak @@ -5,6 +5,8 @@ ldarch = elf32-i386 CFLAGS += -D__i386__ CFLAGS += -I $(KERNELDIR)/include -tests= +tests = $(TEST_DIR)/taskswitch.flat include config-x86-common.mak + +$(TEST_DIR)/taskswitch.flat: $(cstart.o) $(TEST_DIR)/taskswitch.o diff --git a/kvm/user/test/x86/taskswitch.c b/kvm/user/test/x86/taskswitch.c new file mode 100644 index 0000000..8ed8a93 --- /dev/null +++ b/kvm/user/test/x86/taskswitch.c @@ -0,0 +1,164 @@ +/* + * Copyright 2010 Siemens AG + * Author: Jan Kiszka + * + * Released under GPLv2. + */ + +#include "libcflat.h" + +#define FIRST_SPARE_SEL 0x18 + +struct exception_frame { + unsigned long error_code; + unsigned long ip; + unsigned long cs; + unsigned long flags; +}; + +struct tss32 { + unsigned short prev; + unsigned short res1; + unsigned long esp0; + unsigned short ss0; + unsigned short res2; + unsigned long esp1; + unsigned short ss1; + unsigned short res3; + unsigned long esp2; + unsigned short ss2; + unsigned short res4; + unsigned long cr3; + unsigned long eip; + unsigned long eflags; + unsigned long eax, ecx, edx, ebx, esp, ebp, esi, edi; + unsigned short es; + unsigned short res5; + unsigned short cs; + unsigned short res6; + unsigned short ss; + unsigned short res7; + unsigned short ds; + unsigned short res8; + unsigned short fs; + unsigned short res9; + unsigned short gs; + unsigned short res10; + unsigned short ldt; + unsigned short res11; + unsigned short t:1; + unsigned short res12:15; + unsigned short iomap_base; +}; + +static char main_stack[4096]; +static char fault_stack[4096]; +static struct tss32 main_tss; +static struct tss32 fault_tss; + +static unsigned long long gdt[] __attribute__((aligned(16))) = { + 0, + 0x00cf9b000000ffffull, + 0x00cf93000000ffffull, + 0, 0, /* TSS segments */ + 0, /* task return gate */ +}; + +static unsigned long long gdtr; + +void fault_entry(void); + +static __attribute__((used, regparm(1))) void +fault_handler(unsigned long error_code) +{ + unsigned short *desc; + + printf("fault at %x:%x, prev task %x, error code %x\n", + main_tss.cs, main_tss.eip, fault_tss.prev, error_code); + + main_tss.eip += 2; + + desc = (unsigned short *)&gdt[3]; + desc[2] &= ~0x0200; + + desc = (unsigned short *)&gdt[5]; + desc[0] = 0; + desc[1] = fault_tss.prev; + desc[2] = 0x8500; + desc[3] = 0; +} + +asm ( + "fault_entry:\n" + " mov (%esp),%eax\n" + " call fault_handler\n" + " jmp $0x28, $0\n" +); + +static void setup_tss(struct tss32 *tss, void *entry, + void *stack_base, unsigned long stack_size) +{ + unsigned long cr3; + unsigned short cs, ds; + + asm ("mov %%cr3,%0" : "=r" (cr3)); + asm ("mov %%cs,%0" : "=r" (cs)); + asm ("mov %%ds,%0" : "=r" (ds)); + + tss->ss0 = tss->ss1 = tss->ss2 = tss->ss = ds; + tss->esp0 = tss->esp1 = tss->esp2 = tss->esp = + (unsigned long)stack_base + stack_size; + tss->ds = tss->es = tss->fs = tss->gs = ds; + tss->cs = cs; + tss->eip = (unsigned long)entry; + tss->cr3 = cr3; +} + +static void setup_tss_desc(unsigned short tss_sel, struct tss32 *tss) +{ + unsigned long addr = (unsigned long)tss; + unsigned short *desc; + + desc = (unsigned short *)&gdt[tss_sel/8]; + desc[0] = sizeof(*tss) - 1; + desc[1] = addr; + desc[2] = 0x8900 | ((addr & 0x00ff0000) >> 16); + desc[3] = (addr & 0xff000000) >> 16; +} + +static void set_intr_task(unsigned short tss_sel, int intr, struct tss32 *tss) +{ + unsigned short *desc = (void *)(intr* sizeof(long) * 2); + + setup_tss_desc(tss_sel, tss); + + desc[0] = 0; + desc[1] = tss_sel; + desc[2] = 0x8500; + desc[3] = 0; +} + +int main(int ac, char **av) +{ + const long invalid_segment = 0x1234; + + gdtr = ((unsigned long long)(unsigned long)&gdt << 16) | + (sizeof(gdt) - 1); + asm ("lgdt %0" : : "m" (gdtr)); + + setup_tss(&main_tss, 0, main_stack, sizeof(main_stack)); + setup_tss_desc(FIRST_SPARE_SEL, &main_tss); + asm ("ltr %0" : : "r" ((unsigned short)FIRST_SPARE_SEL)); + + setup_tss(&fault_tss, fault_entry, fault_stack, sizeof(fault_stack)); + set_intr_task(FIRST_SPARE_SEL+8, 13, &fault_tss); + + asm ( + "mov %0,%%es\n" + : : "r" (invalid_segment) : "edi" + ); + + printf("post fault\n"); + + return 0; +} -- To unsubscribe from this list: send the line "unsubscribe kvm-commits" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
