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