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"
7efd8e5aaSPaolo Bonzini #include "vmalloc.h"
85aca024eSPaolo Bonzini #include "alloc_page.h"
9e0e5509bSNadav Amit #include "delay.h"
10d21b4f12SGleb Natapov
1167961d18SPaolo Bonzini #define MAIN_TSS_SEL (FIRST_SPARE_SEL + 0)
1267961d18SPaolo Bonzini #define VM86_TSS_SEL (FIRST_SPARE_SEL + 8)
133cdcf179SNadav Amit #define CONFORM_CS_SEL (FIRST_SPARE_SEL + 16)
14b01c8823SKevin Wolf
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
2106846df5SThomas Huth void do_pf_tss(ulong *error_code);
2206846df5SThomas Huth
nmi_tss(void)23d21b4f12SGleb Natapov static void nmi_tss(void)
24d21b4f12SGleb Natapov {
25d21b4f12SGleb Natapov start:
26d21b4f12SGleb Natapov printf("NMI task is running\n");
27d21b4f12SGleb Natapov print_current_tss_info();
28d21b4f12SGleb Natapov test_count++;
29d21b4f12SGleb Natapov asm volatile ("iret");
30d21b4f12SGleb Natapov goto start;
31d21b4f12SGleb Natapov }
32d21b4f12SGleb Natapov
de_tss(void)33d21b4f12SGleb Natapov static void de_tss(void)
34d21b4f12SGleb Natapov {
35d21b4f12SGleb Natapov start:
36d21b4f12SGleb Natapov printf("DE task is running\n");
37d21b4f12SGleb Natapov print_current_tss_info();
38d21b4f12SGleb Natapov test_divider = 10;
39d21b4f12SGleb Natapov test_count++;
40d21b4f12SGleb Natapov asm volatile ("iret");
41d21b4f12SGleb Natapov goto start;
42d21b4f12SGleb Natapov }
43d21b4f12SGleb Natapov
of_tss(void)44d21b4f12SGleb Natapov static void of_tss(void)
45d21b4f12SGleb Natapov {
46d21b4f12SGleb Natapov start:
47d21b4f12SGleb Natapov printf("OF task is running\n");
48d21b4f12SGleb Natapov print_current_tss_info();
49d21b4f12SGleb Natapov test_count++;
50d21b4f12SGleb Natapov asm volatile ("iret");
51d21b4f12SGleb Natapov goto start;
52d21b4f12SGleb Natapov }
53d21b4f12SGleb Natapov
bp_tss(void)54d21b4f12SGleb Natapov static void bp_tss(void)
55d21b4f12SGleb Natapov {
56d21b4f12SGleb Natapov start:
57d21b4f12SGleb Natapov printf("BP task is running\n");
58d21b4f12SGleb Natapov print_current_tss_info();
59d21b4f12SGleb Natapov test_count++;
60d21b4f12SGleb Natapov asm volatile ("iret");
61d21b4f12SGleb Natapov goto start;
62d21b4f12SGleb Natapov }
63d21b4f12SGleb Natapov
do_pf_tss(ulong * error_code)64ce71ddc7SGleb Natapov void do_pf_tss(ulong *error_code)
65ce71ddc7SGleb Natapov {
66b006d7ebSAndrew Jones printf("PF task is running %p %lx\n", error_code, *error_code);
67ce71ddc7SGleb Natapov print_current_tss_info();
68b006d7ebSAndrew Jones if (*error_code == 0x2) /* write access, not present */
69ce71ddc7SGleb Natapov test_count++;
70ce71ddc7SGleb Natapov install_pte(phys_to_virt(read_cr3()), 1, fault_addr,
71d10d16e1SAlexander Gordeev fault_phys | PT_PRESENT_MASK | PT_WRITABLE_MASK, 0);
72ce71ddc7SGleb Natapov }
73ce71ddc7SGleb Natapov
74ce71ddc7SGleb Natapov extern void pf_tss(void);
75ce71ddc7SGleb Natapov
76ce71ddc7SGleb Natapov asm (
77ce71ddc7SGleb Natapov "pf_tss: \n\t"
78ce71ddc7SGleb Natapov "push %esp \n\t"
79ce71ddc7SGleb Natapov "call do_pf_tss \n\t"
80ce71ddc7SGleb Natapov "add $4, %esp \n\t"
81ce71ddc7SGleb Natapov "iret\n\t"
82ce71ddc7SGleb Natapov "jmp pf_tss\n\t"
83ce71ddc7SGleb Natapov );
84ce71ddc7SGleb Natapov
jmp_tss(void)85d21b4f12SGleb Natapov static void jmp_tss(void)
86d21b4f12SGleb Natapov {
87d21b4f12SGleb Natapov start:
88d21b4f12SGleb Natapov printf("JMP to task succeeded\n");
89d21b4f12SGleb Natapov print_current_tss_info();
90d21b4f12SGleb Natapov test_count++;
91d21b4f12SGleb Natapov asm volatile ("ljmp $" xstr(TSS_MAIN) ", $0");
92d21b4f12SGleb Natapov goto start;
93d21b4f12SGleb Natapov }
94d21b4f12SGleb Natapov
irq_tss(void)95d21b4f12SGleb Natapov static void irq_tss(void)
96d21b4f12SGleb Natapov {
97d21b4f12SGleb Natapov start:
98d21b4f12SGleb Natapov printf("IRQ task is running\n");
99d21b4f12SGleb Natapov print_current_tss_info();
100d21b4f12SGleb Natapov test_count++;
101d21b4f12SGleb Natapov asm volatile ("iret");
102d21b4f12SGleb Natapov test_count++;
103d21b4f12SGleb Natapov printf("IRQ task restarts after iret.\n");
104d21b4f12SGleb Natapov goto start;
105d21b4f12SGleb Natapov }
106d21b4f12SGleb Natapov
user_tss(void)1073cdcf179SNadav Amit static void user_tss(void)
1083cdcf179SNadav Amit {
1093cdcf179SNadav Amit start:
110aa5d8048SPaolo Bonzini printf("Conforming task is running\n");
111aa5d8048SPaolo Bonzini print_current_tss_info();
1123cdcf179SNadav Amit test_count++;
1133cdcf179SNadav Amit asm volatile ("iret");
1143cdcf179SNadav Amit goto start;
1153cdcf179SNadav Amit }
1163cdcf179SNadav Amit
test_kernel_mode_int(void)11706846df5SThomas Huth static void test_kernel_mode_int(void)
118d21b4f12SGleb Natapov {
119d21b4f12SGleb Natapov unsigned int res;
120d21b4f12SGleb Natapov
121d21b4f12SGleb Natapov /* test that int $2 triggers task gate */
122d21b4f12SGleb Natapov test_count = 0;
123d21b4f12SGleb Natapov set_intr_task_gate(2, nmi_tss);
124d21b4f12SGleb Natapov printf("Triggering nmi 2\n");
125d21b4f12SGleb Natapov asm volatile ("int $2");
126d21b4f12SGleb Natapov printf("Return from nmi %d\n", test_count);
127a299895bSThomas Huth report(test_count == 1, "NMI int $2");
128d21b4f12SGleb Natapov
129d21b4f12SGleb Natapov /* test that external NMI triggers task gate */
130d21b4f12SGleb Natapov test_count = 0;
131d21b4f12SGleb Natapov set_intr_task_gate(2, nmi_tss);
132d21b4f12SGleb Natapov printf("Triggering nmi through APIC\n");
133d21b4f12SGleb Natapov apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0);
134d21b4f12SGleb Natapov io_delay();
135d21b4f12SGleb Natapov printf("Return from APIC nmi\n");
136a299895bSThomas Huth report(test_count == 1, "NMI external");
137d21b4f12SGleb Natapov
138d21b4f12SGleb Natapov /* test that external interrupt triggesr task gate */
139d21b4f12SGleb Natapov test_count = 0;
140d21b4f12SGleb Natapov printf("Trigger IRQ from APIC\n");
141d21b4f12SGleb Natapov set_intr_task_gate(0xf0, irq_tss);
142*787f0aebSMaxim Levitsky sti();
143d21b4f12SGleb Natapov apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT | 0xf0, 0);
144d21b4f12SGleb Natapov io_delay();
145*787f0aebSMaxim Levitsky cli();
146d21b4f12SGleb Natapov printf("Return from APIC IRQ\n");
147a299895bSThomas Huth report(test_count == 1, "IRQ external");
148d21b4f12SGleb Natapov
149d21b4f12SGleb Natapov /* test that HW exception triggesr task gate */
150d21b4f12SGleb Natapov set_intr_task_gate(0, de_tss);
151912c0d72SThomas Huth printf("Try to divide by 0\n");
152d21b4f12SGleb Natapov asm volatile ("divl %3": "=a"(res)
153d21b4f12SGleb Natapov : "d"(0), "a"(1500), "m"(test_divider));
154d21b4f12SGleb Natapov printf("Result is %d\n", res);
155912c0d72SThomas Huth report(res == 150, "DE exception");
156d21b4f12SGleb Natapov
157912c0d72SThomas Huth /* test if call HW exception DE by int $0 triggers task gate */
158d21b4f12SGleb Natapov test_count = 0;
159d21b4f12SGleb Natapov set_intr_task_gate(0, de_tss);
160d21b4f12SGleb Natapov printf("Call int 0\n");
161d21b4f12SGleb Natapov asm volatile ("int $0");
162d21b4f12SGleb Natapov printf("Return from int 0\n");
163a299895bSThomas Huth report(test_count == 1, "int $0");
164d21b4f12SGleb Natapov
165d21b4f12SGleb Natapov /* test if HW exception OF triggers task gate */
166d21b4f12SGleb Natapov test_count = 0;
167d21b4f12SGleb Natapov set_intr_task_gate(4, of_tss);
168d21b4f12SGleb Natapov printf("Call into\n");
169d21b4f12SGleb Natapov asm volatile ("addb $127, %b0\ninto"::"a"(127));
170d21b4f12SGleb Natapov printf("Return from into\n");
171912c0d72SThomas Huth report(test_count, "OF exception");
172d21b4f12SGleb Natapov
173d21b4f12SGleb Natapov /* test if HW exception BP triggers task gate */
174d21b4f12SGleb Natapov test_count = 0;
175d21b4f12SGleb Natapov set_intr_task_gate(3, bp_tss);
176d21b4f12SGleb Natapov printf("Call int 3\n");
177d21b4f12SGleb Natapov asm volatile ("int $3");
178d21b4f12SGleb Natapov printf("Return from int 3\n");
179912c0d72SThomas Huth report(test_count == 1, "BP exception");
180d21b4f12SGleb Natapov
181ce71ddc7SGleb Natapov /*
182ce71ddc7SGleb Natapov * test that PF triggers task gate and error code is placed on
183ce71ddc7SGleb Natapov * exception task's stack
184ce71ddc7SGleb Natapov */
185ce71ddc7SGleb Natapov fault_addr = alloc_vpage();
186ce71ddc7SGleb Natapov fault_phys = (ulong)virt_to_phys(alloc_page());
187ce71ddc7SGleb Natapov test_count = 0;
188ce71ddc7SGleb Natapov set_intr_task_gate(14, pf_tss);
189ce71ddc7SGleb Natapov printf("Access unmapped page\n");
190ce71ddc7SGleb Natapov *fault_addr = 0;
191ce71ddc7SGleb Natapov printf("Return from pf tss\n");
192912c0d72SThomas Huth report(test_count == 1, "PF exception");
19367af69b7SPaolo Bonzini }
194ce71ddc7SGleb Natapov
test_gdt_task_gate(void)19506846df5SThomas Huth static void test_gdt_task_gate(void)
19667af69b7SPaolo Bonzini {
197d21b4f12SGleb Natapov /* test that calling a task by lcall works */
198d21b4f12SGleb Natapov test_count = 0;
19967af69b7SPaolo Bonzini tss_intr.eip = (u32)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");
205a299895bSThomas Huth report(test_count == 1, "lcall");
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");
210a299895bSThomas Huth report(test_count == 2, "lcall2");
211d21b4f12SGleb Natapov
212d21b4f12SGleb Natapov /* test that calling a task by ljmp works */
213d21b4f12SGleb Natapov test_count = 0;
21467af69b7SPaolo Bonzini tss_intr.eip = (u32)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");
218a299895bSThomas Huth report(test_count == 1, "ljmp");
219b01c8823SKevin Wolf }
220b01c8823SKevin Wolf
test_vm86_switch(void)22106846df5SThomas Huth static void test_vm86_switch(void)
222b01c8823SKevin Wolf {
223b01c8823SKevin Wolf static tss32_t main_tss;
224b01c8823SKevin Wolf static tss32_t vm86_tss;
225b01c8823SKevin Wolf
226b01c8823SKevin Wolf u8 *vm86_start;
227b01c8823SKevin Wolf
228b01c8823SKevin Wolf /* Write a 'ud2' instruction somewhere below 1 MB */
229b01c8823SKevin Wolf vm86_start = (void*) 0x42000;
230b01c8823SKevin Wolf vm86_start[0] = 0x0f;
231b01c8823SKevin Wolf vm86_start[1] = 0x0b;
232b01c8823SKevin Wolf
233b01c8823SKevin Wolf /* Main TSS */
23467961d18SPaolo Bonzini set_gdt_entry(MAIN_TSS_SEL, (u32)&main_tss, sizeof(tss32_t) - 1, 0x89, 0);
23567961d18SPaolo Bonzini ltr(MAIN_TSS_SEL);
236b01c8823SKevin Wolf main_tss = (tss32_t) {
23767961d18SPaolo Bonzini .prev = VM86_TSS_SEL,
238b01c8823SKevin Wolf .cr3 = read_cr3(),
239b01c8823SKevin Wolf };
240b01c8823SKevin Wolf
241b01c8823SKevin Wolf /* VM86 TSS (marked as busy, so we can iret to it) */
24267961d18SPaolo Bonzini set_gdt_entry(VM86_TSS_SEL, (u32)&vm86_tss, sizeof(tss32_t) - 1, 0x8b, 0);
243b01c8823SKevin Wolf vm86_tss = (tss32_t) {
244b01c8823SKevin Wolf .eflags = 0x20002,
245b01c8823SKevin Wolf .cr3 = read_cr3(),
246b01c8823SKevin Wolf .eip = (u32) vm86_start & 0x0f,
247b01c8823SKevin Wolf .cs = (u32) vm86_start >> 4,
248b01c8823SKevin Wolf .ds = 0x1234,
249b01c8823SKevin Wolf .es = 0x2345,
250b01c8823SKevin Wolf };
251b01c8823SKevin Wolf
252b01c8823SKevin Wolf /* Setup task gate to main TSS for #UD */
25367961d18SPaolo Bonzini set_idt_task_gate(6, MAIN_TSS_SEL);
254b01c8823SKevin Wolf
255b01c8823SKevin Wolf /* Jump into VM86 task with iret, #UD lets it come back immediately */
256b01c8823SKevin Wolf printf("Switch to VM86 task and back\n");
257b01c8823SKevin Wolf asm volatile(
258b01c8823SKevin Wolf "pushf\n"
259b01c8823SKevin Wolf "orw $0x4000, (%esp)\n"
260b01c8823SKevin Wolf "popf\n"
261b01c8823SKevin Wolf "iret\n"
262b01c8823SKevin Wolf );
2635c3582f0SJanis Schoetterl-Glausch report_pass("VM86");
264b01c8823SKevin Wolf }
265b01c8823SKevin Wolf
266aa5d8048SPaolo Bonzini #define IOPL_SHIFT 12
267aa5d8048SPaolo Bonzini
test_conforming_switch(void)26806846df5SThomas Huth static void test_conforming_switch(void)
2693cdcf179SNadav Amit {
2703cdcf179SNadav Amit /* test lcall with conforming segment, cs.dpl != cs.rpl */
2713cdcf179SNadav Amit test_count = 0;
2723cdcf179SNadav Amit
2733cdcf179SNadav Amit tss_intr.cs = CONFORM_CS_SEL | 3;
2743cdcf179SNadav Amit tss_intr.eip = (u32)user_tss;
2751034d60aSNadav Amit tss_intr.ss = USER_DS;
2761034d60aSNadav Amit tss_intr.ds = tss_intr.gs = tss_intr.es = tss_intr.fs = tss_intr.ss;
277aa5d8048SPaolo Bonzini tss_intr.eflags |= 3 << IOPL_SHIFT;
2783cdcf179SNadav Amit set_gdt_entry(CONFORM_CS_SEL, 0, 0xffffffff, 0x9f, 0xc0);
2793cdcf179SNadav Amit asm volatile("lcall $" xstr(TSS_INTR) ", $0xf4f4f4f4");
280a299895bSThomas Huth report(test_count == 1, "lcall with cs.rpl != cs.dpl");
2813cdcf179SNadav Amit }
2823cdcf179SNadav Amit
main(void)28306846df5SThomas Huth int main(void)
284b01c8823SKevin Wolf {
285b01c8823SKevin Wolf setup_vm();
286b01c8823SKevin Wolf setup_tss32();
287b01c8823SKevin Wolf
28867af69b7SPaolo Bonzini test_gdt_task_gate();
289b01c8823SKevin Wolf test_kernel_mode_int();
290b01c8823SKevin Wolf test_vm86_switch();
2913cdcf179SNadav Amit test_conforming_switch();
292d21b4f12SGleb Natapov
293f3cdd159SJan Kiszka return report_summary();
294d21b4f12SGleb Natapov }
295