1. 程式人生 > >Linux核心中斷機制(三):中斷處理上

Linux核心中斷機制(三):中斷處理上

核心中斷處理過程

S3C2410Linux2.6.26核心為例講解處理過程

1.中斷向量表arch\arm\kernel\entry-armv.S

__vectors_start:

swi SYS_ERROR0

b vector_und + stubs_offset

ldr pc, .LCvswi + stubs_offset

b vector_pabt + stubs_offset

b vector_dabt + stubs_offset

b vector_addrexcptn + stubs_offset

b vector_irq + stubs_offset

b vector_fiq + stubs_offset

.globl __vectors_end

__vectors_end:

中斷髮生後,跳轉到 b vector_irq + stubs_offset 的位置執行。注意現在的向

量表的初始位置是 0xffff0000

2.向量表中找到入口位置(同一個檔案中)

.globl __stubs_start

__stubs_start:

/*

* Interrupt dispatcher

*/

vector_stub irq, IRQ_MODE, 4 @IRQ_MODE

include\asm\ptrace.h 中定義:0x12

.long __irq_usr @ 0 (USR_26 / USR_32)

.long __irq_invalid @ 1 (FIQ_26 / FIQ_32)

.long __irq_invalid @ 2 (IRQ_26 / IRQ_32)

.long __irq_svc @ 3 (SVC_26 / SVC_32)

.long __irq_invalid @ 4

.long __irq_invalid @ 5

.long __irq_invalid @ 6

.long __irq_invalid @ 7

.long __irq_invalid @ 8

.long __irq_invalid @ 9

.long __irq_invalid @ a

.long __irq_invalid @ b

.long __irq_invalid @ c

.long __irq_invalid @ d

.long __irq_invalid @ e

.long __irq_invalid @ f

上面程式碼中 vector_stub 巨集的定義為:

.macro vector_stub, name, mode, correction=0

.align 5(就在該檔案呢)

vector_\name:    

.if \correction

sub lr, lr, #\correction

.endif

@

@ Save r0, lr_<exception> (parent PC) and spsr_<exception>

@ (parent CPSR)

@

stmia sp, {r0, lr} @ save r0, lr

mrs lr, spsr

str lr, [sp, #8] @ save spsr

@

@ Prepare for SVC32 mode.  IRQs remain disabled.

@

mrs r0, cpsr

eor r0, r0, #(\mode ^ SVC_MODE)

msr spsr_cxsf, r0   

@

@ the branch table must immediately follow this code

@

andlr, lr, #0x0f  @進入中斷前的 mode 的後 4

@#define USR_MODE 0x00000010

@#define FIQ_MODE 0x00000011

@#define IRQ_MODE 0x00000012

@#define SVC_MODE 0x00000013

@#define ABT_MODE 0x00000017

@#define UND_MODE 0x0000001b

@#define SYSTEM_MODE 0x0000001f

mov r0, sp

ldrlr, [pc, lr, lsl #2]   //根據user還是svc模式而不同,決定進入_irq_user還是_irq_svc

movs pc, lr @ branch to handler in SVC mode

.endm

.globl __stubs_start

__stubs_start:

/*

* Interrupt dispatcher

*/

vector_stub irq, IRQ_MODE, 4

.long __irq_usr @ 0 (USR_26 / USR_32)

.long __irq_invalid @ 1 (FIQ_26 / FIQ_32)

.long __irq_invalid @ 2 (IRQ_26 / IRQ_32)

.long __irq_svc @ 3 (SVC_26 / SVC_32)

irq, IRQ_MODE, 4”代替巨集 vector_stub 中的“name, mode, correction”,

找到了我們中斷處理的入口位置為 vector_irq(巨集裡面的 vector_\name)。

從上面程式碼中的註釋可以看出,根據進入中斷前的工作模式不同,程式下一步將

跳轉到_irq_usr 、或__irq_svc 等位置。我們先選擇__irq_usr 作為下一步跟蹤

的目標。

3.__irq_usr的實現(同樣的位置)

__irq_usr:

usr_entry

#ifdef CONFIG_TRACE_IRQFLAGS

bl trace_hardirqs_off

#endif

get_thread_info tsk        //獲取程序中描述符threa_info的地址,存到暫存器裡面

#ifdef CONFIG_PREEMPT

ldr r8, [tsk, #TI_PREEMPT] @ get preempt count

add r7, r8, #1 @ increment it

str r7, [tsk, #TI_PREEMPT]

#endif

irq_handler           //重要的處理過程

#ifdef CONFIG_PREEMPT

ldr r0, [tsk, #TI_PREEMPT]

str r8, [tsk, #TI_PREEMPT]

teq r0, r7

strne r0, [r0, -r0]

#endif

#ifdef CONFIG_TRACE_IRQFLAGS

bl trace_hardirqs_on

#endif

mov why, #0

bret_to_user   //中斷處理完成,返回中斷產生位置

.ltorg

.align 5

usr_entry

是一個巨集:將usr模式下的暫存器和中斷返回地址儲存到堆疊(保護現場)

ret_to_user中斷返回過程 /arch/arm/kernel/entry-common.S

ENTRY(ret_to_user)

ret_slow_syscall:

disable_irq @ disable interrupts

ldr r1, [tsk, #TI_FLAGS]

tst r1, #_TIF_WORK_MASK

bne work_pending

no_work_pending:

/* perform architecture specific actions before user

return */

arch_ret_to_user r1, lr

@ slow_restore_user_regs

ldr r1, [sp, #S_PSR] @ get calling cpsr

ldr lr, [sp, #S_PC]! @ get pc

msr spsr_cxsf, r1 @ save in spsr_svc

ldmdb sp, {r0 - lr}^ @ get calling r0 - lr

mov r0, r0

add sp, sp, #S_FRAME_SIZE - S_PC

movs pc, lr @ return & move spsr_svc into cpsr

4.irq_handler的實現(同一個地址)

.macro irq_handler

get_irqnr_preamble r5, lr  //include/asm/arch-s3c2410/entry-macro.s 中定義,為空操作

1: get_irqnr_and_base r0, r6, r5, lr //判斷中斷號,R0返回

movne r1, sp

@

@ routine called with r0 = irq number, r1 = struct pt_regs *

@

adrne lr, 1b

bneasm_do_IRQ  //進入中斷處理

①判斷中斷號

include/asm/arch-s3c2410/entry-macro.s

.macro get_irqnr_and_base, irqnr, irqstat, base, tmp

mov \base, #S3C24XX_VA_IRQ

@@ try the interrupt offset register, since it is there

ldr \irqstat, [ \base, #INTPND ]

teq \irqstat, #0

beq 1002f

ldr \irqnr, [ \base, #INTOFFSET ] @通過判斷 INTOFFSET

存器得到中斷位置

mov \tmp, #1

tst \irqstat, \tmp, lsl \irqnr

bne 1001f

@@ the number specified is not a valid irq, so try

@@ and work it out for ourselves

mov \irqnr, #0 @@ start here

@@ work out which irq (if any) we got

movs \tmp, \irqstat, lsl#16

addeq \irqnr, \irqnr, #16

moveq \irqstat, \irqstat, lsr#16

tst \irqstat, #0xff

addeq \irqnr, \irqnr, #8

moveq \irqstat, \irqstat, lsr#8

tst \irqstat, #0xf

addeq \irqnr, \irqnr, #4

moveq \irqstat, \irqstat, lsr#4

tst \irqstat, #0x3

addeq \irqnr, \irqnr, #2

moveq \irqstat, \irqstat, lsr#2

tst \irqstat, #0x1

addeq \irqnr, \irqnr, #1

@@ we have the value

1001:

adds \irqnr, \irqnr, #IRQ_EINT0 @加上中斷號的基準 數值,得到最

終的中斷號,注意:此時沒有考慮子中斷的具體情況,(子中斷的問題後面會

有講解)。IRQ_EINT0 include/asm/arch- s3c2410/irqs.h 中定義.從這裡可

以看出,中斷號的具體值是有平臺相關的程式碼決定的,和硬體中斷掛起暫存器

中的中斷號是不等的。

1002:

@@ exit here, Z flag unset if IRQ

.endm

5.asm_do_IRQ 實現過程 arch/arm/kernel/irq.c

asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)

{

struct pt_regs *old_regs = set_irq_regs(regs);

struct irq_desc *desc = irq_desc + irq; //根據引數irq找到具體的中斷號

/*

 * Some hardware gives randomly wrong interrupts.  Rather

 * than crashing, do something sensible.

 */

if (irq >= NR_IRQS)

desc = &bad_irq_desc;trap_init

irq_enter(); //沒用

desc_handle_irq(irq, desc);  //根據中斷號和desc結構進入中斷處理

/* AT91 specific workaround */

irq_finish(irq);

irq_exit();

set_irq_regs(old_regs);

}

static inline void desc_handle_irq(unsigned int irq, struct irq_desc

*desc)

{

desc->handle_irq(irq, desc);//中斷處理

}

asmlinkage標誌含義:

#include <asm/linkage.h>//各個具體處理器在此檔案中定義 asmlinkage

#ifdef __cplusplus

#define CPP_ASMLINKAGE extern "C"

#else

#define CPP_ASMLINKAGE

#endif

#ifndef asmlinkage//如果以前沒有定義 asmlinkage

#define asmlinkage CPP_ASMLINKAGE

#endif

對於 ARM 處理器的<asm/linkage.h>,沒有定義 asmlinkage,所以沒有意義(不

要以為引數是從堆疊傳遞的,對於 ARM 平臺來說還是符合 ATPCS 過程呼叫標準,

通過暫存器傳遞的)。

但對於 X86 處理器的<asm/linkage.h>中是這樣定義的:

#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0)))

表示函式的引數傳遞是通過堆疊完成的。