In this section, I will cover and walk through the kernel code executed in interrupt context. I will be reffering the the code as per2.4.18 release of kernel.

Low Level Interrupt Stubs

Whenever an interrupt occurs, CPU performs the some [:KernelHacking-HOWTO/Overview of the Kernel Source Code/Internals of Interrupt Handling:hardware checks] and start executing the following assembly instructions inkernel, whose pointer (offest in kernel code segment) is storedcorrestonding IDT entry.

File: include/asm-i386/hw_irq.h

155 #define BUILD_COMMON_IRQ() 
156 asmlinkage void call_do_IRQ(void); 
157 __asm__( 
158 "\n" __ALIGN_STR"\n" 
159 "common_interrupt:\n\t" 
161 SYMBOL_NAME_STR(call_do_IRQ)":\n\t" 
162 "call " SYMBOL_NAME_STR(do_IRQ) "\n\t" 
163 "jmp ret_from_intr\n");

175 #define BUILD_IRQ(nr) 
176 asmlinkage void IRQ_NAME(nr); 
177 __asm__( 
178 "\n"__ALIGN_STR"\n" 
179 SYMBOL_NAME_STR(IRQ) #nr "_interrupt:\n\t" 
180 "pushl $"#nr"-256\n\t" 
181 "jmp common_interrupt");

This macros is used at the kernel initialization time to writeoutthelowest interrupt stubs, which can be called from IDT bysavingtheiroffsets (pointers) in IDT gates. Kernel maintains one globalarrayoffunction pointers (name of array is "interrupt") in which itstoresthepointer of these stubs. Code related to creation of thesestubs(usingabove mentioned BUILD_IRQ macro) and saving their pointersintheglobal array "interrupt[NR_IRQS]" can be seeninfile"arch/x86_64/kernel/i8259.c". In this file you will see theusageof BUILD_IRQ macro to create the interrupt stubs as follows:

File: arch/i386/kernel/i8259.c

40 #define BI(x,y) 
41 BUILD_IRQ(x##y)
43 #define BUILD_16_IRQS(x) 
44 BI(x,0) BI(x,1) BI(x,2) BI(x,3) 
45 BI(x,4) BI(x,5) BI(x,6) BI(x,7) 
46 BI(x,8) BI(x,9) BI(x,a) BI(x,b) 
47 BI(x,c) BI(x,d) BI(x,e) BI(x,f)
49 /*
50 * ISA PIC or low IO-APIC triggered (INTA-cycle or APIC) interrupts:
51 * (these are usually mapped to vectors 0x20-0x2f)
52 */
53 BUILD_16_IRQS(0x0)
55 #ifdef CONFIG_X86_IO_APIC
56 /*
57 * The IO-APIC gives us many more interrupt sources. Most of these
58 * are unused but an SMP system is supposed to have enough memory ...
59 * sometimes (mostly wrt. hw bugs) we get corrupted vectors all
60 * across the spectrum, so we really want to be prepared to get all
61 * of these. Plus, more powerful systems might have more than 64
62 * IO-APIC registers.
63 *
64 * (these are usually mapped into the 0x30-0xff vector range)
65 */
66 BUILD_16_IRQS(0x1) BUILD_16_IRQS(0x2) BUILD_16_IRQS(0x3)
67 BUILD_16_IRQS(0x4) BUILD_16_IRQS(0x5) BUILD_16_IRQS(0x6) BUILD_16_IRQS(0x7)
68 BUILD_16_IRQS(0x8) BUILD_16_IRQS(0x9) BUILD_16_IRQS(0xa) BUILD_16_IRQS(0xb)
69 BUILD_16_IRQS(0xc) BUILD_16_IRQS(0xd)
70 #endif
72 #undef BUILD_16_IRQS
73 #undef BI

Above code actually creates the interrupt stubs and do not place therepointers in interrupt[NR_IRQS] array. The code which places the pointersof these stubs in global array is as follows and can be found in samefile "arch/x86_64/kernel/i8259.c"

File: arch/i386/kernel/i8259.c

100 #define IRQ(x,y) 101 IRQ##x##y##_interrupt
103 #define IRQLIST_16(x) 
104 IRQ(x,0), IRQ(x,1), IRQ(x,2), IRQ(x,3), 
105 IRQ(x,4), IRQ(x,5), IRQ(x,6), IRQ(x,7), 
106 IRQ(x,8), IRQ(x,9), IRQ(x,a), IRQ(x,b), 
107 IRQ(x,c), IRQ(x,d), IRQ(x,e), IRQ(x,f)
109 void (*interrupt[NR_IRQS])(void) = {
110 IRQLIST_16(0x0),
112 #ifdef CONFIG_X86_IO_APIC
113 IRQLIST_16(0x1), IRQLIST_16(0x2), IRQLIST_16(0x3),
114 IRQLIST_16(0x4), IRQLIST_16(0x5), IRQLIST_16(0x6), IRQLIST_16(0x7),
115 IRQLIST_16(0x8), IRQLIST_16(0x9), IRQLIST_16(0xa), IRQLIST_16(0xb),
116 IRQLIST_16(0xc), IRQLIST_16(0xd)
117 #endif
118 };
120 #undef IRQ
121 #undef IRQLIST_16

Above code actually fills the global array of functionpointers(array name interrupt[NR_IRQS]). Once the global array isinitialized with the pointers to interrupt stubs, we initialize theIDT(Interrupt Descriptor Table) in function "init_IRQ()"using thisglobalarray as follows:

File: arch/i386/kernel/i8259.c, Function: init_IRQ()

457 for (i = 0; i < NR_IRQS; i++) {
458   int vector = FIRST_EXTERNAL_VECTOR + i;
459   if (vector != SYSCALL_VECTOR) 
460     set_intr_gate(vector, interrupt[i]);
461 }

In above loop, we loop over all the IDT enteries staringfrom"FIRST_EXTERNAL_VECTOR" (32, because first 32 enteries areforexception) and call "set_intr_gate()" function which actually settheinterrupt gate descriptor. For entry 128, which is for systemcallinvocation, interrupt gte is not set, for this rather trap gate issetand that is done in function trap_init(). In the samefunctioninit_IRQ(), after this looping, we initialize the IPI (Inter Processor Interrupts). These interrupts are sent from one CPU toanother CPU inSMP machines.

Now we can see once these IDTeneries are set,whenever an interrupt occurs, CPU directly jumps to thecode given inBUILD_IRQ macro. Now lets analyse what this macro do.Following is thecode for BUILD_IRQ macro:

File: include/asm-i386/hw_irq.h

175 #define BUILD_IRQ(nr) \
176 asmlinkage void IRQ_NAME(nr); \
177 __asm__( \
178 "\n"__ALIGN_STR"\n" \
179 SYMBOL_NAME_STR(IRQ) #nr "_interrupt:\n\t" \
180         "pushl $"#nr"-256\n\t" \
181         "jmp common_interrupt");

This assembly code first subtracts the IRQ number from 256 and pushes the result on kernel stack. After doing this it jumps to "common_interrupt" assembly label, which simply saves the context of interrupted process (CPU resigters) on to kernel stack and then calls the C language function "do_IRQ()".

KernelNewbies: KernelHacking-HOWTO/Overview_of_the_Kernel_Source_Code/Internals_of_Interrupt_Handling/Details_of_do_IRQ()_function (last edited 2006-09-19 14:41:02 by nnc1)