xref: /kvm-unit-tests/x86/taskswitch2.c (revision d21b4f1216592171a358c7d17f81b4aab08aebea)
1 #include "libcflat.h"
2 #include "desc.h"
3 #include "apic-defs.h"
4 #include "apic.h"
5 #include "processor.h"
6 
7 #define xstr(s) str(s)
8 #define str(s) #s
9 
10 static volatile int test_count;
11 static volatile unsigned int test_divider;
12 
13 static int g_fail;
14 static int g_tests;
15 
16 static inline void io_delay(void)
17 {
18 }
19 
20 static void report(const char *msg, int pass)
21 {
22     ++g_tests;
23     printf("%s: %s\n", msg, (pass ? "PASS" : "FAIL"));
24     if (!pass)
25         ++g_fail;
26 }
27 
28 static void nmi_tss(void)
29 {
30 start:
31 	printf("NMI task is running\n");
32 	print_current_tss_info();
33 	test_count++;
34 	asm volatile ("iret");
35 	goto start;
36 }
37 
38 static void de_tss(void)
39 {
40 start:
41 	printf("DE task is running\n");
42 	print_current_tss_info();
43 	test_divider = 10;
44 	test_count++;
45 	asm volatile ("iret");
46 	goto start;
47 }
48 
49 static void of_tss(void)
50 {
51 start:
52 	printf("OF task is running\n");
53 	print_current_tss_info();
54 	test_count++;
55 	asm volatile ("iret");
56 	goto start;
57 }
58 
59 static void bp_tss(void)
60 {
61 start:
62 	printf("BP task is running\n");
63 	print_current_tss_info();
64 	test_count++;
65 	asm volatile ("iret");
66 	goto start;
67 }
68 
69 static void jmp_tss(void)
70 {
71 start:
72 	printf("JMP to task succeeded\n");
73 	print_current_tss_info();
74 	test_count++;
75 	asm volatile ("ljmp $" xstr(TSS_MAIN) ", $0");
76 	goto start;
77 }
78 
79 static void irq_tss(void)
80 {
81 start:
82 	printf("IRQ task is running\n");
83 	print_current_tss_info();
84 	test_count++;
85 	asm volatile ("iret");
86 	test_count++;
87 	printf("IRQ task restarts after iret.\n");
88 	goto start;
89 }
90 
91 int main()
92 {
93 	unsigned int res;
94 
95 	setup_idt();
96 	setup_gdt();
97 	setup_tss32();
98 
99 	/* test that int $2 triggers task gate */
100 	test_count = 0;
101 	set_intr_task_gate(2, nmi_tss);
102 	printf("Triggering nmi 2\n");
103 	asm volatile ("int $2");
104 	printf("Return from nmi %d\n", test_count);
105 	report("NMI int $2", test_count == 1);
106 
107 	/* test that external NMI triggers task gate */
108 	test_count = 0;
109 	set_intr_task_gate(2, nmi_tss);
110 	printf("Triggering nmi through APIC\n");
111 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
112 	io_delay();
113 	printf("Return from APIC nmi\n");
114 	report("NMI external", test_count == 1);
115 
116 	/* test that external interrupt triggesr task gate */
117 	test_count = 0;
118 	printf("Trigger IRQ from APIC\n");
119 	set_intr_task_gate(0xf0, irq_tss);
120 	irq_enable();
121 	apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT | 0xf0, 0);
122 	io_delay();
123 	irq_disable();
124 	printf("Return from APIC IRQ\n");
125 	report("IRQ external", test_count == 1);
126 
127 	/* test that HW exception triggesr task gate */
128 	set_intr_task_gate(0, de_tss);
129 	printf("Try to devide by 0\n");
130 	asm volatile ("divl %3": "=a"(res)
131 		      : "d"(0), "a"(1500), "m"(test_divider));
132 	printf("Result is %d\n", res);
133 	report("DE exeption", res == 150);
134 
135 	/* test if call HW exeption DE by int $0 triggers task gate */
136 	test_count = 0;
137 	set_intr_task_gate(0, de_tss);
138 	printf("Call int 0\n");
139 	asm volatile ("int $0");
140 	printf("Return from int 0\n");
141 	report("int $0", test_count == 1);
142 
143 	/* test if HW exception OF triggers task gate */
144 	test_count = 0;
145 	set_intr_task_gate(4, of_tss);
146 	printf("Call into\n");
147 	asm volatile ("addb $127, %b0\ninto"::"a"(127));
148 	printf("Return from into\n");
149 	report("OF exeption", test_count);
150 
151 	/* test if HW exception BP triggers task gate */
152 	test_count = 0;
153 	set_intr_task_gate(3, bp_tss);
154 	printf("Call int 3\n");
155 	asm volatile ("int $3");
156 	printf("Return from int 3\n");
157 	report("BP exeption", test_count == 1);
158 
159 	/* test that calling a task by lcall works */
160 	test_count = 0;
161 	set_intr_task_gate(0, irq_tss);
162 	printf("Calling task by lcall\n");
163 	/* hlt opcode is 0xf4 I use destination IP 0xf4f4f4f4 to catch
164 	   incorrect instruction length calculation */
165 	asm volatile("lcall $" xstr(TSS_INTR) ", $0xf4f4f4f4");
166 	printf("Return from call\n");
167 	report("lcall", test_count == 1);
168 
169 	/* call the same task again and check that it restarted after iret */
170 	test_count = 0;
171 	asm volatile("lcall $" xstr(TSS_INTR) ", $0xf4f4f4f4");
172 	report("lcall2", test_count == 2);
173 
174 	/* test that calling a task by ljmp works */
175 	test_count = 0;
176 	set_intr_task_gate(0, jmp_tss);
177 	printf("Jumping to a task by ljmp\n");
178 	asm volatile ("ljmp $" xstr(TSS_INTR) ", $0xf4f4f4f4");
179 	printf("Jump back succeeded\n");
180 	report("ljmp", test_count == 1);
181 
182 	printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail);
183 
184 	return g_fail != 0;
185 }
186