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

Reply via email to