xref: /kvm-unit-tests/x86/taskswitch2.c (revision b01c882353a20133a3a9ad9bc2fa6fcce3b17471)
1d21b4f12SGleb Natapov #include "libcflat.h"
2d21b4f12SGleb Natapov #include "desc.h"
3d21b4f12SGleb Natapov #include "apic-defs.h"
4d21b4f12SGleb Natapov #include "apic.h"
5d21b4f12SGleb Natapov #include "processor.h"
6ce71ddc7SGleb Natapov #include "vm.h"
7d21b4f12SGleb Natapov 
8*b01c8823SKevin Wolf #define FREE_GDT_INDEX 6
9*b01c8823SKevin Wolf #define MAIN_TSS_INDEX (FREE_GDT_INDEX + 0)
10*b01c8823SKevin Wolf #define VM86_TSS_INDEX (FREE_GDT_INDEX + 1)
11*b01c8823SKevin Wolf 
12d21b4f12SGleb Natapov #define xstr(s) str(s)
13d21b4f12SGleb Natapov #define str(s) #s
14d21b4f12SGleb Natapov 
15d21b4f12SGleb Natapov static volatile int test_count;
16d21b4f12SGleb Natapov static volatile unsigned int test_divider;
17d21b4f12SGleb Natapov 
18ce71ddc7SGleb Natapov static char *fault_addr;
19ce71ddc7SGleb Natapov static ulong fault_phys;
20ce71ddc7SGleb Natapov 
21d21b4f12SGleb Natapov static int g_fail;
22d21b4f12SGleb Natapov static int g_tests;
23d21b4f12SGleb Natapov 
24d21b4f12SGleb Natapov static inline void io_delay(void)
25d21b4f12SGleb Natapov {
26d21b4f12SGleb Natapov }
27d21b4f12SGleb Natapov 
28d21b4f12SGleb Natapov static void report(const char *msg, int pass)
29d21b4f12SGleb Natapov {
30d21b4f12SGleb Natapov     ++g_tests;
31d21b4f12SGleb Natapov     printf("%s: %s\n", msg, (pass ? "PASS" : "FAIL"));
32d21b4f12SGleb Natapov     if (!pass)
33d21b4f12SGleb Natapov         ++g_fail;
34d21b4f12SGleb Natapov }
35d21b4f12SGleb Natapov 
36d21b4f12SGleb Natapov static void nmi_tss(void)
37d21b4f12SGleb Natapov {
38d21b4f12SGleb Natapov start:
39d21b4f12SGleb Natapov 	printf("NMI task is running\n");
40d21b4f12SGleb Natapov 	print_current_tss_info();
41d21b4f12SGleb Natapov 	test_count++;
42d21b4f12SGleb Natapov 	asm volatile ("iret");
43d21b4f12SGleb Natapov 	goto start;
44d21b4f12SGleb Natapov }
45d21b4f12SGleb Natapov 
46d21b4f12SGleb Natapov static void de_tss(void)
47d21b4f12SGleb Natapov {
48d21b4f12SGleb Natapov start:
49d21b4f12SGleb Natapov 	printf("DE task is running\n");
50d21b4f12SGleb Natapov 	print_current_tss_info();
51d21b4f12SGleb Natapov 	test_divider = 10;
52d21b4f12SGleb Natapov 	test_count++;
53d21b4f12SGleb Natapov 	asm volatile ("iret");
54d21b4f12SGleb Natapov 	goto start;
55d21b4f12SGleb Natapov }
56d21b4f12SGleb Natapov 
57d21b4f12SGleb Natapov static void of_tss(void)
58d21b4f12SGleb Natapov {
59d21b4f12SGleb Natapov start:
60d21b4f12SGleb Natapov 	printf("OF task is running\n");
61d21b4f12SGleb Natapov 	print_current_tss_info();
62d21b4f12SGleb Natapov 	test_count++;
63d21b4f12SGleb Natapov 	asm volatile ("iret");
64d21b4f12SGleb Natapov 	goto start;
65d21b4f12SGleb Natapov }
66d21b4f12SGleb Natapov 
67d21b4f12SGleb Natapov static void bp_tss(void)
68d21b4f12SGleb Natapov {
69d21b4f12SGleb Natapov start:
70d21b4f12SGleb Natapov 	printf("BP task is running\n");
71d21b4f12SGleb Natapov 	print_current_tss_info();
72d21b4f12SGleb Natapov 	test_count++;
73d21b4f12SGleb Natapov 	asm volatile ("iret");
74d21b4f12SGleb Natapov 	goto start;
75d21b4f12SGleb Natapov }
76d21b4f12SGleb Natapov 
77ce71ddc7SGleb Natapov void do_pf_tss(ulong *error_code)
78ce71ddc7SGleb Natapov {
79ce71ddc7SGleb Natapov 	printf("PF task is running %x %x\n", error_code, *(ulong*)error_code);
80ce71ddc7SGleb Natapov 	print_current_tss_info();
81ce71ddc7SGleb Natapov 	if (*(ulong*)error_code == 0x2) /* write access, not present */
82ce71ddc7SGleb Natapov 		test_count++;
83ce71ddc7SGleb Natapov 	install_pte(phys_to_virt(read_cr3()), 1, fault_addr,
84ce71ddc7SGleb Natapov 		    fault_phys | PTE_PRESENT | PTE_WRITE, 0);
85ce71ddc7SGleb Natapov }
86ce71ddc7SGleb Natapov 
87ce71ddc7SGleb Natapov extern void pf_tss(void);
88ce71ddc7SGleb Natapov 
89ce71ddc7SGleb Natapov asm (
90ce71ddc7SGleb Natapov 	"pf_tss: \n\t"
91ce71ddc7SGleb Natapov 	"push %esp \n\t"
92ce71ddc7SGleb Natapov 	"call do_pf_tss \n\t"
93ce71ddc7SGleb Natapov 	"add $4, %esp \n\t"
94ce71ddc7SGleb Natapov 	"iret\n\t"
95ce71ddc7SGleb Natapov 	"jmp pf_tss\n\t"
96ce71ddc7SGleb Natapov     );
97ce71ddc7SGleb Natapov 
98d21b4f12SGleb Natapov static void jmp_tss(void)
99d21b4f12SGleb Natapov {
100d21b4f12SGleb Natapov start:
101d21b4f12SGleb Natapov 	printf("JMP to task succeeded\n");
102d21b4f12SGleb Natapov 	print_current_tss_info();
103d21b4f12SGleb Natapov 	test_count++;
104d21b4f12SGleb Natapov 	asm volatile ("ljmp $" xstr(TSS_MAIN) ", $0");
105d21b4f12SGleb Natapov 	goto start;
106d21b4f12SGleb Natapov }
107d21b4f12SGleb Natapov 
108d21b4f12SGleb Natapov static void irq_tss(void)
109d21b4f12SGleb Natapov {
110d21b4f12SGleb Natapov start:
111d21b4f12SGleb Natapov 	printf("IRQ task is running\n");
112d21b4f12SGleb Natapov 	print_current_tss_info();
113d21b4f12SGleb Natapov 	test_count++;
114d21b4f12SGleb Natapov 	asm volatile ("iret");
115d21b4f12SGleb Natapov 	test_count++;
116d21b4f12SGleb Natapov 	printf("IRQ task restarts after iret.\n");
117d21b4f12SGleb Natapov 	goto start;
118d21b4f12SGleb Natapov }
119d21b4f12SGleb Natapov 
120*b01c8823SKevin Wolf void test_kernel_mode_int()
121d21b4f12SGleb Natapov {
122d21b4f12SGleb Natapov 	unsigned int res;
123d21b4f12SGleb Natapov 
124d21b4f12SGleb Natapov 	/* test that int $2 triggers task gate */
125d21b4f12SGleb Natapov 	test_count = 0;
126d21b4f12SGleb Natapov 	set_intr_task_gate(2, nmi_tss);
127d21b4f12SGleb Natapov 	printf("Triggering nmi 2\n");
128d21b4f12SGleb Natapov 	asm volatile ("int $2");
129d21b4f12SGleb Natapov 	printf("Return from nmi %d\n", test_count);
130d21b4f12SGleb Natapov 	report("NMI int $2", test_count == 1);
131d21b4f12SGleb Natapov 
132d21b4f12SGleb Natapov 	/* test that external NMI triggers task gate */
133d21b4f12SGleb Natapov 	test_count = 0;
134d21b4f12SGleb Natapov 	set_intr_task_gate(2, nmi_tss);
135d21b4f12SGleb Natapov 	printf("Triggering nmi through APIC\n");
136d21b4f12SGleb Natapov 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
137d21b4f12SGleb Natapov 	io_delay();
138d21b4f12SGleb Natapov 	printf("Return from APIC nmi\n");
139d21b4f12SGleb Natapov 	report("NMI external", test_count == 1);
140d21b4f12SGleb Natapov 
141d21b4f12SGleb Natapov 	/* test that external interrupt triggesr task gate */
142d21b4f12SGleb Natapov 	test_count = 0;
143d21b4f12SGleb Natapov 	printf("Trigger IRQ from APIC\n");
144d21b4f12SGleb Natapov 	set_intr_task_gate(0xf0, irq_tss);
145d21b4f12SGleb Natapov 	irq_enable();
146d21b4f12SGleb Natapov 	apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT | 0xf0, 0);
147d21b4f12SGleb Natapov 	io_delay();
148d21b4f12SGleb Natapov 	irq_disable();
149d21b4f12SGleb Natapov 	printf("Return from APIC IRQ\n");
150d21b4f12SGleb Natapov 	report("IRQ external", test_count == 1);
151d21b4f12SGleb Natapov 
152d21b4f12SGleb Natapov 	/* test that HW exception triggesr task gate */
153d21b4f12SGleb Natapov 	set_intr_task_gate(0, de_tss);
154d21b4f12SGleb Natapov 	printf("Try to devide by 0\n");
155d21b4f12SGleb Natapov 	asm volatile ("divl %3": "=a"(res)
156d21b4f12SGleb Natapov 		      : "d"(0), "a"(1500), "m"(test_divider));
157d21b4f12SGleb Natapov 	printf("Result is %d\n", res);
158d21b4f12SGleb Natapov 	report("DE exeption", res == 150);
159d21b4f12SGleb Natapov 
160d21b4f12SGleb Natapov 	/* test if call HW exeption DE by int $0 triggers task gate */
161d21b4f12SGleb Natapov 	test_count = 0;
162d21b4f12SGleb Natapov 	set_intr_task_gate(0, de_tss);
163d21b4f12SGleb Natapov 	printf("Call int 0\n");
164d21b4f12SGleb Natapov 	asm volatile ("int $0");
165d21b4f12SGleb Natapov 	printf("Return from int 0\n");
166d21b4f12SGleb Natapov 	report("int $0", test_count == 1);
167d21b4f12SGleb Natapov 
168d21b4f12SGleb Natapov 	/* test if HW exception OF triggers task gate */
169d21b4f12SGleb Natapov 	test_count = 0;
170d21b4f12SGleb Natapov 	set_intr_task_gate(4, of_tss);
171d21b4f12SGleb Natapov 	printf("Call into\n");
172d21b4f12SGleb Natapov 	asm volatile ("addb $127, %b0\ninto"::"a"(127));
173d21b4f12SGleb Natapov 	printf("Return from into\n");
174d21b4f12SGleb Natapov 	report("OF exeption", test_count);
175d21b4f12SGleb Natapov 
176d21b4f12SGleb Natapov 	/* test if HW exception BP triggers task gate */
177d21b4f12SGleb Natapov 	test_count = 0;
178d21b4f12SGleb Natapov 	set_intr_task_gate(3, bp_tss);
179d21b4f12SGleb Natapov 	printf("Call int 3\n");
180d21b4f12SGleb Natapov 	asm volatile ("int $3");
181d21b4f12SGleb Natapov 	printf("Return from int 3\n");
182d21b4f12SGleb Natapov 	report("BP exeption", test_count == 1);
183d21b4f12SGleb Natapov 
184ce71ddc7SGleb Natapov 	/*
185ce71ddc7SGleb Natapov 	 * test that PF triggers task gate and error code is placed on
186ce71ddc7SGleb Natapov 	 * exception task's stack
187ce71ddc7SGleb Natapov 	 */
188ce71ddc7SGleb Natapov 	fault_addr = alloc_vpage();
189ce71ddc7SGleb Natapov 	fault_phys = (ulong)virt_to_phys(alloc_page());
190ce71ddc7SGleb Natapov 	test_count = 0;
191ce71ddc7SGleb Natapov 	set_intr_task_gate(14, pf_tss);
192ce71ddc7SGleb Natapov 	printf("Access unmapped page\n");
193ce71ddc7SGleb Natapov 	*fault_addr = 0;
194ce71ddc7SGleb Natapov 	printf("Return from pf tss\n");
195ce71ddc7SGleb Natapov 	report("PF exeption", test_count == 1);
196ce71ddc7SGleb Natapov 
197d21b4f12SGleb Natapov 	/* test that calling a task by lcall works */
198d21b4f12SGleb Natapov 	test_count = 0;
199d21b4f12SGleb Natapov 	set_intr_task_gate(0, irq_tss);
200d21b4f12SGleb Natapov 	printf("Calling task by lcall\n");
201d21b4f12SGleb Natapov 	/* hlt opcode is 0xf4 I use destination IP 0xf4f4f4f4 to catch
202d21b4f12SGleb Natapov 	   incorrect instruction length calculation */
203d21b4f12SGleb Natapov 	asm volatile("lcall $" xstr(TSS_INTR) ", $0xf4f4f4f4");
204d21b4f12SGleb Natapov 	printf("Return from call\n");
205d21b4f12SGleb Natapov 	report("lcall", test_count == 1);
206d21b4f12SGleb Natapov 
207d21b4f12SGleb Natapov 	/* call the same task again and check that it restarted after iret */
208d21b4f12SGleb Natapov 	test_count = 0;
209d21b4f12SGleb Natapov 	asm volatile("lcall $" xstr(TSS_INTR) ", $0xf4f4f4f4");
210d21b4f12SGleb Natapov 	report("lcall2", test_count == 2);
211d21b4f12SGleb Natapov 
212d21b4f12SGleb Natapov 	/* test that calling a task by ljmp works */
213d21b4f12SGleb Natapov 	test_count = 0;
214d21b4f12SGleb Natapov 	set_intr_task_gate(0, jmp_tss);
215d21b4f12SGleb Natapov 	printf("Jumping to a task by ljmp\n");
216d21b4f12SGleb Natapov 	asm volatile ("ljmp $" xstr(TSS_INTR) ", $0xf4f4f4f4");
217d21b4f12SGleb Natapov 	printf("Jump back succeeded\n");
218d21b4f12SGleb Natapov 	report("ljmp", test_count == 1);
219*b01c8823SKevin Wolf }
220*b01c8823SKevin Wolf 
221*b01c8823SKevin Wolf void test_vm86_switch(void)
222*b01c8823SKevin Wolf {
223*b01c8823SKevin Wolf     static tss32_t main_tss;
224*b01c8823SKevin Wolf     static tss32_t vm86_tss;
225*b01c8823SKevin Wolf 
226*b01c8823SKevin Wolf     u8 *vm86_start;
227*b01c8823SKevin Wolf 
228*b01c8823SKevin Wolf     /* Write a 'ud2' instruction somewhere below 1 MB */
229*b01c8823SKevin Wolf     vm86_start = (void*) 0x42000;
230*b01c8823SKevin Wolf     vm86_start[0] = 0x0f;
231*b01c8823SKevin Wolf     vm86_start[1] = 0x0b;
232*b01c8823SKevin Wolf 
233*b01c8823SKevin Wolf     /* Main TSS */
234*b01c8823SKevin Wolf     set_gdt_entry(MAIN_TSS_INDEX, (u32)&main_tss, sizeof(tss32_t) - 1, 0x89, 0);
235*b01c8823SKevin Wolf     ltr(MAIN_TSS_INDEX << 3);
236*b01c8823SKevin Wolf     main_tss = (tss32_t) {
237*b01c8823SKevin Wolf         .prev   = VM86_TSS_INDEX << 3,
238*b01c8823SKevin Wolf         .cr3    = read_cr3(),
239*b01c8823SKevin Wolf     };
240*b01c8823SKevin Wolf 
241*b01c8823SKevin Wolf     /* VM86 TSS (marked as busy, so we can iret to it) */
242*b01c8823SKevin Wolf     set_gdt_entry(VM86_TSS_INDEX, (u32)&vm86_tss, sizeof(tss32_t) - 1, 0x8b, 0);
243*b01c8823SKevin Wolf     vm86_tss = (tss32_t) {
244*b01c8823SKevin Wolf         .eflags = 0x20002,
245*b01c8823SKevin Wolf         .cr3    = read_cr3(),
246*b01c8823SKevin Wolf         .eip    = (u32) vm86_start & 0x0f,
247*b01c8823SKevin Wolf         .cs     = (u32) vm86_start >> 4,
248*b01c8823SKevin Wolf         .ds     = 0x1234,
249*b01c8823SKevin Wolf         .es     = 0x2345,
250*b01c8823SKevin Wolf     };
251*b01c8823SKevin Wolf 
252*b01c8823SKevin Wolf     /* Setup task gate to main TSS for #UD */
253*b01c8823SKevin Wolf     set_idt_task_gate(6, MAIN_TSS_INDEX << 3);
254*b01c8823SKevin Wolf 
255*b01c8823SKevin Wolf     /* Jump into VM86 task with iret, #UD lets it come back immediately */
256*b01c8823SKevin Wolf     printf("Switch to VM86 task and back\n");
257*b01c8823SKevin Wolf     asm volatile(
258*b01c8823SKevin Wolf         "pushf\n"
259*b01c8823SKevin Wolf         "orw $0x4000, (%esp)\n"
260*b01c8823SKevin Wolf         "popf\n"
261*b01c8823SKevin Wolf         "iret\n"
262*b01c8823SKevin Wolf     );
263*b01c8823SKevin Wolf     report("VM86", 1);
264*b01c8823SKevin Wolf }
265*b01c8823SKevin Wolf 
266*b01c8823SKevin Wolf int main()
267*b01c8823SKevin Wolf {
268*b01c8823SKevin Wolf 	setup_vm();
269*b01c8823SKevin Wolf 	setup_idt();
270*b01c8823SKevin Wolf 	setup_gdt();
271*b01c8823SKevin Wolf 	setup_tss32();
272*b01c8823SKevin Wolf 
273*b01c8823SKevin Wolf 	test_kernel_mode_int();
274*b01c8823SKevin Wolf 	test_vm86_switch();
275d21b4f12SGleb Natapov 
276d21b4f12SGleb Natapov 	printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail);
277d21b4f12SGleb Natapov 
278d21b4f12SGleb Natapov 	return g_fail != 0;
279d21b4f12SGleb Natapov }
280