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