kvm test: Add 32-bit task switch micro-test
PATCH 1/1 kvm test: Add 32-bit task switch micro-test
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.
---
Gleb, you might want to have a look at this test. When using it with my 2.6.34 queue (or below or with QEMU), I get the following, expected output:
fault at 8:4002ef, prev task 18, error code 1234
post fault
When using it with master + my error-code patch, I get this:
fault at 8:4002ef, prev task 18, error code 1234
post fault
I.e. there is blank line, a repeated 0x0a character after returning from the fault handler. I'm suspecting that IO string rework triggers this. Instrumentation of the testdev showed that the spurious puts() was emitted over the instruction that the fault handler returns to. Any ideas?
kvm/user/config-i386.mak | 4 +- kvm/user/test/x86/taskswitch.c | 164 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+), 1 deletions(-) create mode 100644 kvm/user/test/x86/taskswitch.c
kvm/user/config-i386.mak +3 -1
@@ -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
kvm/user/test/x86/taskswitch.c new file +164 -0
164 lines not on this page · raw message
Quoted 4 lines · Jan Kiszka
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.
How do you compile this? I was sure kvm test suit is broken for 32 bit code.
Quoted 201 lines
Signed-off-by Jan Kiszka <[email protected]>---
Gleb, you might want to have a look at this test. When using it with my 2.6.34 queue (or below or with QEMU), I get the following, expected output:
fault at 8:4002ef, prev task 18, error code 1234
post faultWhen using it with master + my error-code patch, I get this:
fault at 8:4002ef, prev task 18, error code 1234
post fault
I.e. there is blank line, a repeated
0x0acharacter after returning from the fault handler. I'm suspecting that IO string rework triggers this. Instrumentation of the testdev showed that the spuriousputs()was emitted over the instruction that the fault handler returns to. Any ideas?kvm/user/config-i386.mak | 4 +- kvm/user/test/x86/taskswitch.c | 164 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 167 insertions(+), 1 deletions(-) create mode 100644 kvm/user/test/x86/taskswitch.ckvm/user/config-i386.mak +3
-1@@ -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.okvm/user/test/x86/taskswitch.c new file +154
-0@@ -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);
kvm/user/test/x86/taskswitch.c line 164
Quoted 10 lines
kvm/user/test/x86/taskswitch.c new file +10
-0+ + asm ( +"mov %0,%%es\n" + : :"r" (invalid_segment) :"edi" + ); + +printf ("post fault\n" ); + +return 0 ; +}
--
Gleb.
Quoted 7 lines · Gleb Natapov
Quoted 4 lines · Jan Kiszka
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.
How do you compile this? I was sure kvm test suit is broken for 32 bit code.
Apply my other patch (kvm test: Fix i386 crossbuild), then run "make ARCH=i386".
I was thinking about porting this test also to ARCH=x86_64, but then I became lazy again...
Jan
Signature · 2 lines
Siemens AG, Corporate Technology, CT T DE IT 1 Corporate Competence Center Embedded Linux
Quoted 6 lines · Jan Kiszka
Quoted 5 lines · Gleb Natapov
Quoted 4 lines · Jan Kiszka
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.
Quoted 6 lines
Quoted 2 lines
How do you compile this? I was sure kvm test suit is broken for 32 bit code.
Apply my other patch (kvm test: Fix i386 crossbuild), then run "make ARCH=i386".
Ah, I wasn't CCed on it, so I haven't got it yet :)
Quoted 8 lines
I was thinking about porting this test also to ARCH=x86_64, but then I became lazy again...
Jan
Signature · 2 lines
Siemens AG, Corporate Technology, CT T DE IT 1 Corporate Competence Center Embedded Linux
--
Gleb.
Quoted 11 lines · Jan Kiszka
Gleb, you might want to have a look at this test. When using it with my 2.6.34 queue (or below or with QEMU), I get the following, expected output:
fault at 8:4002ef, prev task 18, error code 1234
post faultWhen using it with master + my error-code patch, I get this:
fault at 8:4002ef, prev task 18, error code 1234
Quoted 8 lines
post fault
I.e. there is blank line, a repeated
0x0acharacter after returning from the fault handler. I'm suspecting that IO string rework triggers this. Instrumentation of the testdev showed that the spuriousputs()was emitted over the instruction that the fault handler returns to. Any ideas?
Yes, handle_task_switch() needlessly exits to userspace without setting exit reason, so last exit reason is reused (in your test case this is io write). This patch should fix the problem:
arch/x86/kvm/x86.c +1 -1
@@ -4740,7 +4740,7 @@ int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int reason)if (ret ==X86EMUL_CONTINUE ) kvm_x86_ops->set_rflags (vcpu, vcpu->arch.emulate_ctxt.eflags); -return (ret !=X86EMUL_CONTINUE ); +return (ret ==X86EMUL_CONTINUE ); }EXPORT_SYMBOL_GPL (kvm_task_switch);
--
Gleb.
Quoted 30 lines · Gleb Natapov
Quoted 11 lines · Jan Kiszka
Gleb, you might want to have a look at this test. When using it with my 2.6.34 queue (or below or with QEMU), I get the following, expected output:
fault at 8:4002ef, prev task 18, error code 1234
post faultWhen using it with master + my error-code patch, I get this:
fault at 8:4002ef, prev task 18, error code 1234
Quoted 8 lines
post fault
I.e. there is blank line, a repeated
0x0acharacter after returning from the fault handler. I'm suspecting that IO string rework triggers this. Instrumentation of the testdev showed that the spuriousputs()was emitted over the instruction that the fault handler returns to. Any ideas?Yes,
handle_task_switch()needlessly exits to userspace without setting exit reason, so last exit reason is reused (in your test case this is io write). This patch should fix the problem:arch/x86/kvm/x86.c +0
-0
arch/x86/kvm/x86.c int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int reason) · line 4746
Quoted 8 lines
arch/x86/kvm/x86.c +1
-1@@ -4740,7 +4740,7 @@ int kvm_task_switch(struct kvm_vcpu *vcpu, u16 tss_selector, int reason)if (ret ==X86EMUL_CONTINUE ) kvm_x86_ops->set_rflags (vcpu, vcpu->arch.emulate_ctxt.eflags); -return (ret !=X86EMUL_CONTINUE ); +return (ret ==X86EMUL_CONTINUE ); }EXPORT_SYMBOL_GPL (kvm_task_switch);
Yep, makes sense and works for me as well.
Jan
Marcelo Tosatti
AppliedQuoted 5 lines · Jan Kiszka
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]>
Applied, thanks.