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