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