diff mbox

kvm test: Add 32-bit task switch micro-test

Message ID 4BC5CD5E.6000608@siemens.com (mailing list archive)
State New, archived
Headers show

Commit Message

Jan Kiszka April 14, 2010, 2:12 p.m. UTC
None
diff mbox

Patch

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;
+}