[PATCH] kvm test: Add 32-bit task switch micro-test

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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 <jan.kiszka@xxxxxxxxxxx>
---

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

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" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux