Kernel version v3.7.6 / ARMv7
I'd like to know how the kernel handles interrups from very bottom. I do know that the "gic_handle_irq" will be called to handler the irqs. That's the start point to look for.
The function is defined as follows.
asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
I couldn't find the one calls "gic_handle_irq()" directly.However, there is one sets the pointer of the function. That is the MACHINE_START macro.
MACHINE_START
MACHINE_START(...)
...
.handle_irq = gic_handle_irq
...
MACHINE_END
Absolutely, somebody should call it like (struct machine_desc*)var->handle_irq(). Unfortuneately, this is not the case. The function pointer is copied to the varaible called "handle_arch_irq" in the setup.c file.
arch/arm/kernel/setup.c
arch/arm/kernel/setup.c
775#ifdef CONFIG_MULTI_IRQ_HANDLER
776 handle_arch_irq = mdesc->handle_irq;
777#endif
Where is "handle_arch_irq" and how it is called? The varaible is defined in the "entry-armv.S". The line 1192 of the code below.
arch/arm/kernel/entry-armv.S
arch/arm/kernel/entry-armv.S
1034 .globl __stubs_start
1035 __stubs_start:
1036 /*
1037 * Interrupt dispatcher
1038 */
1039 vector_stub irq, IRQ_MODE, 4
1040
1041 .long __irq_usr @ 0 (USR_26 / USR_32)
1042 .long __irq_invalid @ 1 (FIQ_26 / FIQ_32)
1043 .long __irq_invalid @ 2 (IRQ_26 / IRQ_32)
1044 .long __irq_svc @ 3 (SVC_26 / SVC_32)
1045 .long __irq_invalid @ 4
1046 .long __irq_invalid @ 5
1047 .long __irq_invalid @ 6
1048 .long __irq_invalid @ 7
1049 .long __irq_invalid @ 8
1050 .long __irq_invalid @ 9
1051 .long __irq_invalid @ a
1052 .long __irq_invalid @ b
1053 .long __irq_invalid @ c
1054 .long __irq_invalid @ d
1055 .long __irq_invalid @ e
1056 .long __irq_invalid @ f
.
.
.
1142 /*=============================================================================
1143 * Address exception handler
1144 *-----------------------------------------------------------------------------
1145 * These aren't too critical.
1146 * (they're not supposed to happen, and won't happen in 32-bit data mode).
1147 */
1148
1149 vector_addrexcptn:
1150 b vector_addrexcptn
1151
1152 /*
1153 * We group all the following data together to optimise
1154 * for CPUs with separate I & D caches.
1155 */
1156 .align 5
1157
1158 .LCvswi:
1159 .word vector_swi
1160
1161 .globl __stubs_end
1162 __stubs_end:
1163
1164 .equ stubs_offset, __vectors_start + 0x200 - __stubs_start
1165
1166 .globl __vectors_start
1167 __vectors_start:
1168 ARM( swi SYS_ERROR0 )
1169 THUMB( svc #0 )
1170 THUMB( nop )
1171 W(b) vector_und + stubs_offset
1172 W(ldr) pc, .LCvswi + stubs_offset
1173 W(b) vector_pabt + stubs_offset
1174 W(b) vector_dabt + stubs_offset
1175 W(b) vector_addrexcptn + stubs_offset
1176 W(b) vector_irq + stubs_offset
1177 W(b) vector_fiq + stubs_offset
1178
1179 .globl __vectors_end
1180 __vectors_end:
1181
1182 .data
1183
1184 .globl cr_alignment
1185 .globl cr_no_alignment
1186 cr_alignment:
1187 .space 4
1188 cr_no_alignment:
1189 .space 4
1190
1191 #ifdef CONFIG_MULTI_IRQ_HANDLER
1192 .globl handle_arch_irq
1193 handle_arch_irq:
1194 .space 4
1195 #endif
The same file has the macro calls the "handle_arch_irq".
irq_handler macro
35 /*
36 * Interrupt handling.
37 */
38 .macro irq_handler
39 #ifdef CONFIG_MULTI_IRQ_HANDLER
40 ldr r1, =handle_arch_irq
41 mov r0, sp
42 adr lr, BSYM(9997f)
43 ldr pc, [r1]
44 #else
45 arch_irq_handler_default
46 #endif
47 9997:
48 .endm
Let's find the locations where use the macro. There are actually two.
__irq_## macro
212 __irq_svc:
213 svc_entry
214 irq_handler
215
216 #ifdef CONFIG_PREEMPT
217 get_thread_info tsk
218 ldr r8, [tsk, #TI_PREEMPT] @ get preempt count
219 ldr r0, [tsk, #TI_FLAGS] @ get flags
220 teq r8, #0 @ if preempt count != 0
221 movne r0, #0 @ force flags to 0
222 tst r0, #_TIF_NEED_RESCHED
223 blne svc_preempt
224 #endif
225
226 #ifdef CONFIG_TRACE_IRQFLAGS
227 @ The parent context IRQs must have been enabled to get here in
228 @ the first place, so there's no point checking the PSR I bit.
229 bl trace_hardirqs_on
230 #endif
231 svc_exit r5 @ return from exception
232 UNWIND(.fnend )
233 ENDPROC(__irq_svc)
427 __irq_usr:
428 usr_entry
429 kuser_cmpxchg_check
430 irq_handler
431 get_thread_info tsk
432 mov why, #0
433 b ret_to_user_from_irq
434 UNWIND(.fnend )
435 ENDPROC(__irq_usr)
As the proc name tells, the normal device interrupts will be passed via __irq_usr routine. At the line 1041 of the file, I could find the code calls it.
I can also find that line 1164 calculates the "__stubs_offset". The offset is added to the each vector bases. (line 1172 ~ line 1177)
The next question is where is the "vector_irq"? The answer is at line 1039. The "vector_stub" macro is expanded to "vector_irq".
vector_stub macro
982 /*
983 * Vector stubs.
984 *
985 * This code is copied to 0xffff0200 so we can use branches in the
986 * vectors, rather than ldr's. Note that this code must not
987 * exceed 0x300 bytes.
988 *
989 * Common stub entry macro:
990 * Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
991 *
992 * SP points to a minimal amount of processor-private memory, the address
993 * of which is copied into r0 for the mode specific abort handler.
994 */
995 .macro vector_stub, name, mode, correction=0
996 .align 5
997
998 vector_\name:
999 .if \correction
1000 sub lr, lr, #\correction
1001 .endif
1002
1003 @
1004 @ Save r0, lr_<exception> (parent PC) and spsr_<exception>
1005 @ (parent CPSR)
1006 @
1007 stmia sp, {r0, lr} @ save r0, lr
1008 mrs lr, spsr
1009 str lr, [sp, #8] @ save spsr
1010
1011 @
1012 @ Prepare for SVC32 mode. IRQs remain disabled.
1013 @
1014 mrs r0, cpsr
1015 eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE)
1016 msr spsr_cxsf, r0
1017
1018 @
1019 @ the branch table must immediately follow this code
1020 @
1021 and lr, lr, #0x0f
1022 THUMB( adr r0, 1f )
1023 THUMB( ldr lr, [r0, lr, lsl #2] )
1024 mov r0, sp
1025 ARM( ldr lr, [pc, lr, lsl #2] )
1026 movs pc, lr @ branch to handler in SVC mode
1027 ENDPROC(vector_\name)
1028
1029 .align 2
1030 @ handler addresses follow this label
1031 1:
1032 .endm
1033
1034 .globl __stubs_start
1035 __stubs_start:
...
In the macro above, the lr register holds the spsr (line 1008) initially and masked out bit 31~4 to get mode (M[4:0] or cpsr/spsr). The line 1025 performs left shift by 2 to call right handler based on the mode.
In example, if an interrupt is raised in service mode, the mode will be M[4:0]=10011. The mask out and left shift operations will generate M[4:0]=01100. Therefore, pc will points the 4th entry in the branch table because pc already points the first entry when it executes line 1026.
In short, once an irq is raised, the execution will branch to "vector_irq" which actually calls "__irq_usr" or "__irq_svc" depending on mode. In the "irq_handler" macro in "__irq_usr" or "__irq_svc"will jump to "handle_arch_irq". This variable holds the pointer of the handler I've set in MACHINE_START.