这里分析的是 XtratuM 1.0 的代码。
关于 XM 中断接管的代码主要在 arch/$ARCH/kernel/irq.c ($ARCH = i386) 中。还有一部分在 patch 文件中,不过那个貌似关系不是很大,主要是替换了某些 cli 和 sti 指令,但是这些替换后的代码实质上和 cli、sti 的作用是一样的,所以我也有点奇怪为什么要有这样的替换,貌似不替换也是可以的……
在 irq.c 中,替换函数是 hw_irq_takeover():
/* filename: arch/i386/kernel/irq.c */ 193 int hw_irq_takeover (void) { 194 unsigned long hw_flags, vector, irq; 195 196 // Our irq and trap tables which will replace actual IDT table 197 198 irq_addr = (void (**) (void)) &__start_irq_handlers_addr; 199 trap_addr = (void (**) (void)) &__start_trap_handlers_addr; 200 201 real_idt_table = hw_get_idt_table_addr (); 202 203 hw_save_flags_and_cli(&hw_flags); 204 205 __root_sti = XM_root_func.__sti; 206 __root_cli = XM_root_func.__cli; 207 __root_save_flags = XM_root_func.__save_flags; 208 __root_restore_flags = XM_root_func.__restore_flags; 209 __root_is_cli = XM_root_func.__is_cli; 210 211 XM_root_func.__sti = vsti; 212 XM_root_func.__cli = vcli; 213 XM_root_func.__save_flags = vsave_flags; 214 XM_root_func.__restore_flags = vrestore_flags; 215 XM_root_func.__is_cli = vis_cli; 216 XM_root_func.__emulate_iret = emulate_iret; 217 218 for (vector = 0; vector < IDT_ENTRIES; vector ++) 219 root_idt_table [vector] = hw_get_gate_addr (vector); 220 221 222 for (irq = 0; irq < NR_IRQS; irq ++) { 223 hw_xpic [irq] = ((irq_desc_t *)XM_root_func.__irq_desc)[irq].handler; 224 ((irq_desc_t *)XM_root_func.__irq_desc)[irq].handler = &vpic; 225 } 226 227 // In an i386 there are 16 irqs, 0..15 (besides of the apic interrupt) 228 229 for (irq = 0; irq < NR_IRQS; irq++) { 230 vector = irq + FIRST_EXTERNAL_VECTOR; 231 232 // Replacing all hw irq gates for XtratuM routines 233 hw_set_irq_gate(vector, irq_addr [irq]); 234 } 235 236 hw_set_trap_gate(0, trap_addr[0]); 237 hw_set_trap_gate(1, trap_addr[1]); 238 hw_set_sys_gate(3, trap_addr[3]); 239 hw_set_sys_gate(4, trap_addr[4]); 240 hw_set_sys_gate(5, trap_addr[5]); 241 hw_set_trap_gate(6, trap_addr[6]); 242 hw_set_trap_gate(7, trap_addr[7]); 243 hw_set_trap_gate(8, trap_addr[8]); 244 hw_set_trap_gate(9, trap_addr[9]); 245 hw_set_trap_gate(10, trap_addr[10]); 246 hw_set_trap_gate(11, trap_addr[11]); 247 hw_set_trap_gate(12, trap_addr[12]); 248 hw_set_trap_gate(13, trap_addr[13]); 249 hw_set_irq_gate(14, trap_addr[14]); 250 hw_set_trap_gate(15, trap_addr[15]); 251 hw_set_trap_gate(16, trap_addr[16]); 252 hw_set_trap_gate(17, trap_addr[17]); 253 hw_set_trap_gate(18, trap_addr[18]); 254 hw_set_trap_gate(19, trap_addr[19]); 255 256 // The XM's syscall interrupt 257 hw_set_sys_gate (0x82, SYSTEM_CALL_HANDLER_ASM(0x82)); 258 259 // The Root OS sycall, it can not be called when it not in execution 260 hw_set_sys_gate (0x80, INTERCEPT_SYSTEM_CALL_HANDLER_ASM(0x80)); 261 262 hw_restore_flags(hw_flags); 263 264 return 0; 265 }
/* filename: include/i386/irqs.h */ 167 #define TRAP_ADDR_TABLE_START() \ 168 __asm__ (".section trap_handlers_addr,\"a\"\n\t" \ 169 "__start_trap_handlers_addr:\n\t" \ 170 ".previous\n\t"); 171 172 #define TRAP_ADDR_TABLE_END() \ 173 __asm__ (".section trap_handlers_addr,\"a\"\n\t" \ 174 "__end_trap_handlers_addr:\n\t" \ 175 ".long -1\n\t" \ 176 ".previous\n\t");
/* filename: arch/i386/kernel/irqs.c */ 360 // Trap table 361 TRAP_ADDR_TABLE_START(); 362 BUILD_TRAP_NOERRCODE(0x0); BUILD_TRAP_NOERRCODE(0x1); 363 BUILD_TRAP_NOERRCODE(0x2); BUILD_TRAP_NOERRCODE(0x3); 364 BUILD_TRAP_NOERRCODE(0x4); BUILD_TRAP_NOERRCODE(0x5); 365 BUILD_TRAP_NOERRCODE(0x6); BUILD_TRAP_NOERRCODE(0x7); 366 BUILD_TRAP_ERRCODE(0x8); BUILD_TRAP_NOERRCODE(0x9); 367 BUILD_TRAP_ERRCODE(0xa); BUILD_TRAP_ERRCODE(0xb); 368 BUILD_TRAP_ERRCODE(0xc); BUILD_TRAP_ERRCODE(0xd); 369 BUILD_TRAP_ERRCODE(0xe); BUILD_TRAP_NOERRCODE(0xf); 370 BUILD_TRAP_NOERRCODE(0x10); BUILD_TRAP_ERRCODE(0x11); 371 BUILD_TRAP_NOERRCODE(0x12); BUILD_TRAP_NOERRCODE(0x13); 372 BUILD_TRAP_ERRCODE(0x14); BUILD_TRAP_ERRCODE(0x15); 373 BUILD_TRAP_ERRCODE(0x16); BUILD_TRAP_ERRCODE(0x17); 374 BUILD_TRAP_ERRCODE(0x18); BUILD_TRAP_ERRCODE(0x19); 375 BUILD_TRAP_ERRCODE(0x1a); BUILD_TRAP_ERRCODE(0x1b); 376 BUILD_TRAP_ERRCODE(0x1c); BUILD_TRAP_ERRCODE(0x1d); 377 BUILD_TRAP_ERRCODE(0x1e); BUILD_TRAP_ERRCODE(0x1f); 378 TRAP_ADDR_TABLE_END();
/* filename: include/i386/irqs.h */ 181 #define BUILD_TRAP_NAME(trapnr) trap_##trapnr(void) 182 183 #define BUILD_TRAP_ERRCODE(trapnr) \ 184 asmlinkage void BUILD_TRAP_NAME(trapnr); \ 185 __asm__ (".section trap_handlers_addr,\"a\"\n\t" \ 186 ".align 4\n\t" \ 187 ".long "SYMBOL_NAME_STR(trap_) #trapnr "\n\t" \ 188 ".text\n\t" \ 189 "\n" __ALIGN_STR"\n\t" \ 190 SYMBOL_NAME_STR(trap_) #trapnr ":\n\t" \ 191 "cld\n\t" \ 192 HW_SAVE_ALL \ 193 "pushl $"#trapnr"\n\t" \ 194 "call " SYMBOL_NAME_STR(trap_handler) "\n\t" \ 195 "addl $4,%esp\n\t" /* popl trapnr */ \ 196 "testl %eax,%eax\n\t" \ 197 "popl %ebx\n\t" \ 198 "popl %ecx\n\t" \ 199 "popl %edx\n\t" \ 200 "popl %esi\n\t" \ 201 "popl %edi\n\t" \ 202 "popl %ebp\n\t" \ 203 "jnz 1f\n\t" \ 204 "popl %eax\n\t" \ 205 "popl %ds\n\t" \ 206 "popl %es\n\t" \ 207 "addl $4,%esp\n\t" /* popl error code */ \ 208 "iret\n" \ 209 "1:\n\t" \ 210 "movl ("SYMBOL_NAME_STR(root_idt_table + 4 * trapnr)"),%eax\n\t" \ 211 "mov 8(%esp),%es\n\t" \ 212 "movl %eax,8(%esp)\n\t" \ 213 "popl %eax\n\t" \ 214 "popl %ds\n\t" \ 215 "ret\n\t")
/* filename: arch/i386/kernel/irqs.c */ 136 int trap_handler (int trap, struct pt_regs regs) { 137 if (xm_current_domain -> events -> trap_handler [trap]) 138 (*xm_current_domain -> events -> trap_handler [trap]) (trap, &s); 139 140 // return 1 if the root trap handler must to be executed 141 return (xm_current_domain == xm_root_domain); 142 }
对于 irq_addr,情形差不多也是这样的,它使用宏 BUILD_IRQ() 和 BUILD_COMMON_IRQ_BODY():
/* filename: include/i386/irqs.h */ 125 #define IRQ_NAME(irq) irq_handler_##irq(void) 126 127 #define BUILD_IRQ(irq) \ 128 asmlinkage void IRQ_NAME(irq); \ 129 __asm__ (".section irq_handlers_addr,\"a\"\n\t" \ 130 ".align 4\n\t" \ 131 ".long "SYMBOL_NAME_STR(irq_handler_) #irq "\n\t" \ 132 ".text\n\t" \ 133 "\n"__ALIGN_STR"\n" \ 134 SYMBOL_NAME_STR(irq_handler_) #irq ":\n\t" \ 135 "pushl $"#irq"-256\n\t" \ 136 "jmp " SYMBOL_NAME_STR(common_irq_body) "\n\t") 137 138 #define BUILD_COMMON_IRQ_BODY() \ 139 __asm__ (".text\n\t" \ 140 "\n" __ALIGN_STR"\n" \ 141 SYMBOL_NAME_STR(common_irq_body) ":\n\t" \ 142 "cld\n\t" \ 143 HW_SAVE_ALL \ 144 "call " SYMBOL_NAME_STR(irq_handler) "\n\t" \ 145 "testl %eax,%eax\n\t" \ 146 "jnz 1f\n\t" \ 147 HW_RESTORE_ALL \ 148 "1: cld\n\t" \ 149 "jmp *(" SYMBOL_NAME_STR(XM_root_func) " + 20)\n")
/* filename: arch/i386/kernel/irqs.c */ 115 int irq_handler (struct pt_regs regs) { 116 int irq = regs.orig_eax & 0xff; 117 int execute_root_ret_from_intr; 118 119 hw_xpic[irq] -> ack (irq); 120 if (irq != hwtimer.timer_event) { 121 set_bit (xm_domain_list -> events -> pending_events, irq); 122 } else { 123 timer_handler (); 124 hw_xpic[irq] -> end (irq); 125 } 126 // xm_sched is called to execute the suitable handlers 127 // it returns "1" if the root irq handler has been executed 128 execute_root_ret_from_intr = 129 (xm_sched () && (xm_current_domain == xm_root_domain)); 130 131 //hw_xpic[irq] -> end (irq); 132 133 return execute_root_ret_from_intr; 134 }
但是对于不同的架构,中断的接管有很大的不同。例如,在 PowerPC 中,貌似没有 IDT 这个东西,它使用 IOPR 和 IOVRx 寄存器来设置。在 PowerPC 中,系统调用也不是 0x80。对于一些处理器保留的中断,貌似没必要进行替换,因为这样的异常往往是具有重大问题的(例如,除零,缺页等),这些交给 Linux 处理就好了。