xref: /kvm-unit-tests/x86/taskswitch2.c (revision aa5d8048890c130136b2cbe21f82bcbe264400b0)
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 
867961d18SPaolo Bonzini #define MAIN_TSS_SEL (FIRST_SPARE_SEL + 0)
967961d18SPaolo Bonzini #define VM86_TSS_SEL (FIRST_SPARE_SEL + 8)
103cdcf179SNadav Amit #define CONFORM_CS_SEL  (FIRST_SPARE_SEL + 16)
11b01c8823SKevin Wolf 
12d21b4f12SGleb Natapov static volatile int test_count;
13d21b4f12SGleb Natapov static volatile unsigned int test_divider;
14d21b4f12SGleb Natapov 
15ce71ddc7SGleb Natapov static char *fault_addr;
16ce71ddc7SGleb Natapov static ulong fault_phys;
17ce71ddc7SGleb Natapov 
18d21b4f12SGleb Natapov static inline void io_delay(void)
19d21b4f12SGleb Natapov {
20d21b4f12SGleb Natapov }
21d21b4f12SGleb Natapov 
22d21b4f12SGleb Natapov static void nmi_tss(void)
23d21b4f12SGleb Natapov {
24d21b4f12SGleb Natapov start:
25d21b4f12SGleb Natapov 	printf("NMI task is running\n");
26d21b4f12SGleb Natapov 	print_current_tss_info();
27d21b4f12SGleb Natapov 	test_count++;
28d21b4f12SGleb Natapov 	asm volatile ("iret");
29d21b4f12SGleb Natapov 	goto start;
30d21b4f12SGleb Natapov }
31d21b4f12SGleb Natapov 
32d21b4f12SGleb Natapov static void de_tss(void)
33d21b4f12SGleb Natapov {
34d21b4f12SGleb Natapov start:
35d21b4f12SGleb Natapov 	printf("DE task is running\n");
36d21b4f12SGleb Natapov 	print_current_tss_info();
37d21b4f12SGleb Natapov 	test_divider = 10;
38d21b4f12SGleb Natapov 	test_count++;
39d21b4f12SGleb Natapov 	asm volatile ("iret");
40d21b4f12SGleb Natapov 	goto start;
41d21b4f12SGleb Natapov }
42d21b4f12SGleb Natapov 
43d21b4f12SGleb Natapov static void of_tss(void)
44d21b4f12SGleb Natapov {
45d21b4f12SGleb Natapov start:
46d21b4f12SGleb Natapov 	printf("OF task is running\n");
47d21b4f12SGleb Natapov 	print_current_tss_info();
48d21b4f12SGleb Natapov 	test_count++;
49d21b4f12SGleb Natapov 	asm volatile ("iret");
50d21b4f12SGleb Natapov 	goto start;
51d21b4f12SGleb Natapov }
52d21b4f12SGleb Natapov 
53d21b4f12SGleb Natapov static void bp_tss(void)
54d21b4f12SGleb Natapov {
55d21b4f12SGleb Natapov start:
56d21b4f12SGleb Natapov 	printf("BP 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 
63ce71ddc7SGleb Natapov void do_pf_tss(ulong *error_code)
64ce71ddc7SGleb Natapov {
65ce71ddc7SGleb Natapov 	printf("PF task is running %x %x\n", error_code, *(ulong*)error_code);
66ce71ddc7SGleb Natapov 	print_current_tss_info();
67ce71ddc7SGleb Natapov 	if (*(ulong*)error_code == 0x2) /* write access, not present */
68ce71ddc7SGleb Natapov 		test_count++;
69ce71ddc7SGleb Natapov 	install_pte(phys_to_virt(read_cr3()), 1, fault_addr,
70ce71ddc7SGleb Natapov 		    fault_phys | PTE_PRESENT | PTE_WRITE, 0);
71ce71ddc7SGleb Natapov }
72ce71ddc7SGleb Natapov 
73ce71ddc7SGleb Natapov extern void pf_tss(void);
74ce71ddc7SGleb Natapov 
75ce71ddc7SGleb Natapov asm (
76ce71ddc7SGleb Natapov 	"pf_tss: \n\t"
77ce71ddc7SGleb Natapov 	"push %esp \n\t"
78ce71ddc7SGleb Natapov 	"call do_pf_tss \n\t"
79ce71ddc7SGleb Natapov 	"add $4, %esp \n\t"
80ce71ddc7SGleb Natapov 	"iret\n\t"
81ce71ddc7SGleb Natapov 	"jmp pf_tss\n\t"
82ce71ddc7SGleb Natapov     );
83ce71ddc7SGleb Natapov 
84d21b4f12SGleb Natapov static void jmp_tss(void)
85d21b4f12SGleb Natapov {
86d21b4f12SGleb Natapov start:
87d21b4f12SGleb Natapov 	printf("JMP to task succeeded\n");
88d21b4f12SGleb Natapov 	print_current_tss_info();
89d21b4f12SGleb Natapov 	test_count++;
90d21b4f12SGleb Natapov 	asm volatile ("ljmp $" xstr(TSS_MAIN) ", $0");
91d21b4f12SGleb Natapov 	goto start;
92d21b4f12SGleb Natapov }
93d21b4f12SGleb Natapov 
94d21b4f12SGleb Natapov static void irq_tss(void)
95d21b4f12SGleb Natapov {
96d21b4f12SGleb Natapov start:
97d21b4f12SGleb Natapov 	printf("IRQ task is running\n");
98d21b4f12SGleb Natapov 	print_current_tss_info();
99d21b4f12SGleb Natapov 	test_count++;
100d21b4f12SGleb Natapov 	asm volatile ("iret");
101d21b4f12SGleb Natapov 	test_count++;
102d21b4f12SGleb Natapov 	printf("IRQ task restarts after iret.\n");
103d21b4f12SGleb Natapov 	goto start;
104d21b4f12SGleb Natapov }
105d21b4f12SGleb Natapov 
1063cdcf179SNadav Amit static void user_tss(void)
1073cdcf179SNadav Amit {
1083cdcf179SNadav Amit start:
109*aa5d8048SPaolo Bonzini 	printf("Conforming task is running\n");
110*aa5d8048SPaolo Bonzini 	print_current_tss_info();
1113cdcf179SNadav Amit 	test_count++;
1123cdcf179SNadav Amit 	asm volatile ("iret");
1133cdcf179SNadav Amit 	goto start;
1143cdcf179SNadav Amit }
1153cdcf179SNadav Amit 
116b01c8823SKevin Wolf void test_kernel_mode_int()
117d21b4f12SGleb Natapov {
118d21b4f12SGleb Natapov 	unsigned int res;
119d21b4f12SGleb Natapov 
120d21b4f12SGleb Natapov 	/* test that int $2 triggers task gate */
121d21b4f12SGleb Natapov 	test_count = 0;
122d21b4f12SGleb Natapov 	set_intr_task_gate(2, nmi_tss);
123d21b4f12SGleb Natapov 	printf("Triggering nmi 2\n");
124d21b4f12SGleb Natapov 	asm volatile ("int $2");
125d21b4f12SGleb Natapov 	printf("Return from nmi %d\n", test_count);
126d21b4f12SGleb Natapov 	report("NMI int $2", test_count == 1);
127d21b4f12SGleb Natapov 
128d21b4f12SGleb Natapov 	/* test that external NMI triggers task gate */
129d21b4f12SGleb Natapov 	test_count = 0;
130d21b4f12SGleb Natapov 	set_intr_task_gate(2, nmi_tss);
131d21b4f12SGleb Natapov 	printf("Triggering nmi through APIC\n");
132d21b4f12SGleb Natapov 	apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
133d21b4f12SGleb Natapov 	io_delay();
134d21b4f12SGleb Natapov 	printf("Return from APIC nmi\n");
135d21b4f12SGleb Natapov 	report("NMI external", test_count == 1);
136d21b4f12SGleb Natapov 
137d21b4f12SGleb Natapov 	/* test that external interrupt triggesr task gate */
138d21b4f12SGleb Natapov 	test_count = 0;
139d21b4f12SGleb Natapov 	printf("Trigger IRQ from APIC\n");
140d21b4f12SGleb Natapov 	set_intr_task_gate(0xf0, irq_tss);
141d21b4f12SGleb Natapov 	irq_enable();
142d21b4f12SGleb Natapov 	apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT | 0xf0, 0);
143d21b4f12SGleb Natapov 	io_delay();
144d21b4f12SGleb Natapov 	irq_disable();
145d21b4f12SGleb Natapov 	printf("Return from APIC IRQ\n");
146d21b4f12SGleb Natapov 	report("IRQ external", test_count == 1);
147d21b4f12SGleb Natapov 
148d21b4f12SGleb Natapov 	/* test that HW exception triggesr task gate */
149d21b4f12SGleb Natapov 	set_intr_task_gate(0, de_tss);
150d21b4f12SGleb Natapov 	printf("Try to devide by 0\n");
151d21b4f12SGleb Natapov 	asm volatile ("divl %3": "=a"(res)
152d21b4f12SGleb Natapov 		      : "d"(0), "a"(1500), "m"(test_divider));
153d21b4f12SGleb Natapov 	printf("Result is %d\n", res);
154d21b4f12SGleb Natapov 	report("DE exeption", res == 150);
155d21b4f12SGleb Natapov 
156d21b4f12SGleb Natapov 	/* test if call HW exeption DE by int $0 triggers task gate */
157d21b4f12SGleb Natapov 	test_count = 0;
158d21b4f12SGleb Natapov 	set_intr_task_gate(0, de_tss);
159d21b4f12SGleb Natapov 	printf("Call int 0\n");
160d21b4f12SGleb Natapov 	asm volatile ("int $0");
161d21b4f12SGleb Natapov 	printf("Return from int 0\n");
162d21b4f12SGleb Natapov 	report("int $0", test_count == 1);
163d21b4f12SGleb Natapov 
164d21b4f12SGleb Natapov 	/* test if HW exception OF triggers task gate */
165d21b4f12SGleb Natapov 	test_count = 0;
166d21b4f12SGleb Natapov 	set_intr_task_gate(4, of_tss);
167d21b4f12SGleb Natapov 	printf("Call into\n");
168d21b4f12SGleb Natapov 	asm volatile ("addb $127, %b0\ninto"::"a"(127));
169d21b4f12SGleb Natapov 	printf("Return from into\n");
170d21b4f12SGleb Natapov 	report("OF exeption", test_count);
171d21b4f12SGleb Natapov 
172d21b4f12SGleb Natapov 	/* test if HW exception BP triggers task gate */
173d21b4f12SGleb Natapov 	test_count = 0;
174d21b4f12SGleb Natapov 	set_intr_task_gate(3, bp_tss);
175d21b4f12SGleb Natapov 	printf("Call int 3\n");
176d21b4f12SGleb Natapov 	asm volatile ("int $3");
177d21b4f12SGleb Natapov 	printf("Return from int 3\n");
178d21b4f12SGleb Natapov 	report("BP exeption", test_count == 1);
179d21b4f12SGleb Natapov 
180ce71ddc7SGleb Natapov 	/*
181ce71ddc7SGleb Natapov 	 * test that PF triggers task gate and error code is placed on
182ce71ddc7SGleb Natapov 	 * exception task's stack
183ce71ddc7SGleb Natapov 	 */
184ce71ddc7SGleb Natapov 	fault_addr = alloc_vpage();
185ce71ddc7SGleb Natapov 	fault_phys = (ulong)virt_to_phys(alloc_page());
186ce71ddc7SGleb Natapov 	test_count = 0;
187ce71ddc7SGleb Natapov 	set_intr_task_gate(14, pf_tss);
188ce71ddc7SGleb Natapov 	printf("Access unmapped page\n");
189ce71ddc7SGleb Natapov 	*fault_addr = 0;
190ce71ddc7SGleb Natapov 	printf("Return from pf tss\n");
191ce71ddc7SGleb Natapov 	report("PF exeption", test_count == 1);
19267af69b7SPaolo Bonzini }
193ce71ddc7SGleb Natapov 
19467af69b7SPaolo Bonzini void test_gdt_task_gate(void)
19567af69b7SPaolo Bonzini {
196d21b4f12SGleb Natapov 	/* test that calling a task by lcall works */
197d21b4f12SGleb Natapov 	test_count = 0;
19867af69b7SPaolo Bonzini 	tss_intr.eip = (u32)irq_tss;
199d21b4f12SGleb Natapov 	printf("Calling task by lcall\n");
200d21b4f12SGleb Natapov 	/* hlt opcode is 0xf4 I use destination IP 0xf4f4f4f4 to catch
201d21b4f12SGleb Natapov 	   incorrect instruction length calculation */
202d21b4f12SGleb Natapov 	asm volatile("lcall $" xstr(TSS_INTR) ", $0xf4f4f4f4");
203d21b4f12SGleb Natapov 	printf("Return from call\n");
204d21b4f12SGleb Natapov 	report("lcall", test_count == 1);
205d21b4f12SGleb Natapov 
206d21b4f12SGleb Natapov 	/* call the same task again and check that it restarted after iret */
207d21b4f12SGleb Natapov 	test_count = 0;
208d21b4f12SGleb Natapov 	asm volatile("lcall $" xstr(TSS_INTR) ", $0xf4f4f4f4");
209d21b4f12SGleb Natapov 	report("lcall2", test_count == 2);
210d21b4f12SGleb Natapov 
211d21b4f12SGleb Natapov 	/* test that calling a task by ljmp works */
212d21b4f12SGleb Natapov 	test_count = 0;
21367af69b7SPaolo Bonzini 	tss_intr.eip = (u32)jmp_tss;
214d21b4f12SGleb Natapov 	printf("Jumping to a task by ljmp\n");
215d21b4f12SGleb Natapov 	asm volatile ("ljmp $" xstr(TSS_INTR) ", $0xf4f4f4f4");
216d21b4f12SGleb Natapov 	printf("Jump back succeeded\n");
217d21b4f12SGleb Natapov 	report("ljmp", test_count == 1);
218b01c8823SKevin Wolf }
219b01c8823SKevin Wolf 
220b01c8823SKevin Wolf void test_vm86_switch(void)
221b01c8823SKevin Wolf {
222b01c8823SKevin Wolf     static tss32_t main_tss;
223b01c8823SKevin Wolf     static tss32_t vm86_tss;
224b01c8823SKevin Wolf 
225b01c8823SKevin Wolf     u8 *vm86_start;
226b01c8823SKevin Wolf 
227b01c8823SKevin Wolf     /* Write a 'ud2' instruction somewhere below 1 MB */
228b01c8823SKevin Wolf     vm86_start = (void*) 0x42000;
229b01c8823SKevin Wolf     vm86_start[0] = 0x0f;
230b01c8823SKevin Wolf     vm86_start[1] = 0x0b;
231b01c8823SKevin Wolf 
232b01c8823SKevin Wolf     /* Main TSS */
23367961d18SPaolo Bonzini     set_gdt_entry(MAIN_TSS_SEL, (u32)&main_tss, sizeof(tss32_t) - 1, 0x89, 0);
23467961d18SPaolo Bonzini     ltr(MAIN_TSS_SEL);
235b01c8823SKevin Wolf     main_tss = (tss32_t) {
23667961d18SPaolo Bonzini         .prev   = VM86_TSS_SEL,
237b01c8823SKevin Wolf         .cr3    = read_cr3(),
238b01c8823SKevin Wolf     };
239b01c8823SKevin Wolf 
240b01c8823SKevin Wolf     /* VM86 TSS (marked as busy, so we can iret to it) */
24167961d18SPaolo Bonzini     set_gdt_entry(VM86_TSS_SEL, (u32)&vm86_tss, sizeof(tss32_t) - 1, 0x8b, 0);
242b01c8823SKevin Wolf     vm86_tss = (tss32_t) {
243b01c8823SKevin Wolf         .eflags = 0x20002,
244b01c8823SKevin Wolf         .cr3    = read_cr3(),
245b01c8823SKevin Wolf         .eip    = (u32) vm86_start & 0x0f,
246b01c8823SKevin Wolf         .cs     = (u32) vm86_start >> 4,
247b01c8823SKevin Wolf         .ds     = 0x1234,
248b01c8823SKevin Wolf         .es     = 0x2345,
249b01c8823SKevin Wolf     };
250b01c8823SKevin Wolf 
251b01c8823SKevin Wolf     /* Setup task gate to main TSS for #UD */
25267961d18SPaolo Bonzini     set_idt_task_gate(6, MAIN_TSS_SEL);
253b01c8823SKevin Wolf 
254b01c8823SKevin Wolf     /* Jump into VM86 task with iret, #UD lets it come back immediately */
255b01c8823SKevin Wolf     printf("Switch to VM86 task and back\n");
256b01c8823SKevin Wolf     asm volatile(
257b01c8823SKevin Wolf         "pushf\n"
258b01c8823SKevin Wolf         "orw $0x4000, (%esp)\n"
259b01c8823SKevin Wolf         "popf\n"
260b01c8823SKevin Wolf         "iret\n"
261b01c8823SKevin Wolf     );
262b01c8823SKevin Wolf     report("VM86", 1);
263b01c8823SKevin Wolf }
264b01c8823SKevin Wolf 
265*aa5d8048SPaolo Bonzini #define IOPL_SHIFT 12
266*aa5d8048SPaolo Bonzini 
2673cdcf179SNadav Amit void test_conforming_switch(void)
2683cdcf179SNadav Amit {
2693cdcf179SNadav Amit 	/* test lcall with conforming segment, cs.dpl != cs.rpl */
2703cdcf179SNadav Amit 	test_count = 0;
2713cdcf179SNadav Amit 
2723cdcf179SNadav Amit 	tss_intr.cs = CONFORM_CS_SEL | 3;
2733cdcf179SNadav Amit 	tss_intr.eip = (u32)user_tss;
2743cdcf179SNadav Amit 	tss_intr.ds = tss_intr.gs = tss_intr.fs = tss_intr.ss = USER_DS;
275*aa5d8048SPaolo Bonzini 	tss_intr.eflags |= 3 << IOPL_SHIFT;
2763cdcf179SNadav Amit 	set_gdt_entry(CONFORM_CS_SEL, 0, 0xffffffff, 0x9f, 0xc0);
2773cdcf179SNadav Amit 	asm volatile("lcall $" xstr(TSS_INTR) ", $0xf4f4f4f4");
2783cdcf179SNadav Amit 	report("lcall with cs.rpl != cs.dpl", test_count == 1);
2793cdcf179SNadav Amit }
2803cdcf179SNadav Amit 
281b01c8823SKevin Wolf int main()
282b01c8823SKevin Wolf {
283b01c8823SKevin Wolf 	setup_vm();
284b01c8823SKevin Wolf 	setup_idt();
285b01c8823SKevin Wolf 	setup_tss32();
286b01c8823SKevin Wolf 
28767af69b7SPaolo Bonzini 	test_gdt_task_gate();
288b01c8823SKevin Wolf 	test_kernel_mode_int();
289b01c8823SKevin Wolf 	test_vm86_switch();
2903cdcf179SNadav Amit 	test_conforming_switch();
291d21b4f12SGleb Natapov 
292f3cdd159SJan Kiszka 	return report_summary();
293d21b4f12SGleb Natapov }
294