1 #include "libcflat.h" 2 #include "desc.h" 3 #include "apic-defs.h" 4 #include "apic.h" 5 #include "processor.h" 6 7 #define xstr(s) str(s) 8 #define str(s) #s 9 10 static volatile int test_count; 11 static volatile unsigned int test_divider; 12 13 static int g_fail; 14 static int g_tests; 15 16 static inline void io_delay(void) 17 { 18 } 19 20 static void report(const char *msg, int pass) 21 { 22 ++g_tests; 23 printf("%s: %s\n", msg, (pass ? "PASS" : "FAIL")); 24 if (!pass) 25 ++g_fail; 26 } 27 28 static void nmi_tss(void) 29 { 30 start: 31 printf("NMI task is running\n"); 32 print_current_tss_info(); 33 test_count++; 34 asm volatile ("iret"); 35 goto start; 36 } 37 38 static void de_tss(void) 39 { 40 start: 41 printf("DE task is running\n"); 42 print_current_tss_info(); 43 test_divider = 10; 44 test_count++; 45 asm volatile ("iret"); 46 goto start; 47 } 48 49 static void of_tss(void) 50 { 51 start: 52 printf("OF task is running\n"); 53 print_current_tss_info(); 54 test_count++; 55 asm volatile ("iret"); 56 goto start; 57 } 58 59 static void bp_tss(void) 60 { 61 start: 62 printf("BP task is running\n"); 63 print_current_tss_info(); 64 test_count++; 65 asm volatile ("iret"); 66 goto start; 67 } 68 69 static void jmp_tss(void) 70 { 71 start: 72 printf("JMP to task succeeded\n"); 73 print_current_tss_info(); 74 test_count++; 75 asm volatile ("ljmp $" xstr(TSS_MAIN) ", $0"); 76 goto start; 77 } 78 79 static void irq_tss(void) 80 { 81 start: 82 printf("IRQ task is running\n"); 83 print_current_tss_info(); 84 test_count++; 85 asm volatile ("iret"); 86 test_count++; 87 printf("IRQ task restarts after iret.\n"); 88 goto start; 89 } 90 91 int main() 92 { 93 unsigned int res; 94 95 setup_idt(); 96 setup_gdt(); 97 setup_tss32(); 98 99 /* test that int $2 triggers task gate */ 100 test_count = 0; 101 set_intr_task_gate(2, nmi_tss); 102 printf("Triggering nmi 2\n"); 103 asm volatile ("int $2"); 104 printf("Return from nmi %d\n", test_count); 105 report("NMI int $2", test_count == 1); 106 107 /* test that external NMI triggers task gate */ 108 test_count = 0; 109 set_intr_task_gate(2, nmi_tss); 110 printf("Triggering nmi through APIC\n"); 111 apic_icr_write(APIC_DEST_PHYSICAL | APIC_DM_NMI | APIC_INT_ASSERT, 0); 112 io_delay(); 113 printf("Return from APIC nmi\n"); 114 report("NMI external", test_count == 1); 115 116 /* test that external interrupt triggesr task gate */ 117 test_count = 0; 118 printf("Trigger IRQ from APIC\n"); 119 set_intr_task_gate(0xf0, irq_tss); 120 irq_enable(); 121 apic_icr_write(APIC_DEST_SELF | APIC_DEST_PHYSICAL | APIC_DM_FIXED | APIC_INT_ASSERT | 0xf0, 0); 122 io_delay(); 123 irq_disable(); 124 printf("Return from APIC IRQ\n"); 125 report("IRQ external", test_count == 1); 126 127 /* test that HW exception triggesr task gate */ 128 set_intr_task_gate(0, de_tss); 129 printf("Try to devide by 0\n"); 130 asm volatile ("divl %3": "=a"(res) 131 : "d"(0), "a"(1500), "m"(test_divider)); 132 printf("Result is %d\n", res); 133 report("DE exeption", res == 150); 134 135 /* test if call HW exeption DE by int $0 triggers task gate */ 136 test_count = 0; 137 set_intr_task_gate(0, de_tss); 138 printf("Call int 0\n"); 139 asm volatile ("int $0"); 140 printf("Return from int 0\n"); 141 report("int $0", test_count == 1); 142 143 /* test if HW exception OF triggers task gate */ 144 test_count = 0; 145 set_intr_task_gate(4, of_tss); 146 printf("Call into\n"); 147 asm volatile ("addb $127, %b0\ninto"::"a"(127)); 148 printf("Return from into\n"); 149 report("OF exeption", test_count); 150 151 /* test if HW exception BP triggers task gate */ 152 test_count = 0; 153 set_intr_task_gate(3, bp_tss); 154 printf("Call int 3\n"); 155 asm volatile ("int $3"); 156 printf("Return from int 3\n"); 157 report("BP exeption", test_count == 1); 158 159 /* test that calling a task by lcall works */ 160 test_count = 0; 161 set_intr_task_gate(0, irq_tss); 162 printf("Calling task by lcall\n"); 163 /* hlt opcode is 0xf4 I use destination IP 0xf4f4f4f4 to catch 164 incorrect instruction length calculation */ 165 asm volatile("lcall $" xstr(TSS_INTR) ", $0xf4f4f4f4"); 166 printf("Return from call\n"); 167 report("lcall", test_count == 1); 168 169 /* call the same task again and check that it restarted after iret */ 170 test_count = 0; 171 asm volatile("lcall $" xstr(TSS_INTR) ", $0xf4f4f4f4"); 172 report("lcall2", test_count == 2); 173 174 /* test that calling a task by ljmp works */ 175 test_count = 0; 176 set_intr_task_gate(0, jmp_tss); 177 printf("Jumping to a task by ljmp\n"); 178 asm volatile ("ljmp $" xstr(TSS_INTR) ", $0xf4f4f4f4"); 179 printf("Jump back succeeded\n"); 180 report("ljmp", test_count == 1); 181 182 printf("\nsummary: %d tests, %d failures\n", g_tests, g_fail); 183 184 return g_fail != 0; 185 } 186