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