中断学习笔记整理之二 -- 中断的处理(一)

//前文提到发生了中断之后,会散转到下面的跳转语句
W(b)  vector_irq + stubs_offset
//那么vector_irq+stubs_offset指向的地址有是什么呢?


//首先看下vector_irq
//@arch/arm/kernel/entry-armv.S
 
 THUMB( .thumb  )

/*
 * Vector stubs.
 *
 * This code is copied to 0xffff0200 so we can use branches in the
 * vectors, rather than ldr's.  Note that this code must not
 * exceed 0x300 bytes.
 *
 * Common stub entry macro:
 *   Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
 *
 * SP points to a minimal amount of processor-private memory, the address
 * of which is copied into r0 for the mode specific abort handler.
 */
        .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 | PSR_ISETSTATE)
        msr     spsr_cxsf, r0

        @
        @ the branch table must immediately follow this code
        @
        and     lr, lr, #0x0f
 THUMB( adr     r0, 1f                  )
 THUMB( ldr     lr, [r0, lr, lsl #2]    )
        mov     r0, sp
 ARM(   ldr     lr, [pc, lr, lsl #2]    )
        movs    pc, lr                  @ branch to handler in SVC mode
ENDPROC(vector_\name)

        .align  2
        @ handler addresses follow this label
1:
        .endm

从entry-armv.S中的中断向量表定义可以看到,发生中断时系统会跳到 vector_irq + stubs_offset处运行,这个位置实际上就是中断入口函数。
 
vector_irq已经是中断的入口函数了,为什么又要加上 stubs_offset?是因为b指令实际上是相对当前PC的跳转,也就是说当汇编器看到B指令后会把要跳转的标签转化为相对于当前PC的偏移量写入指令码。从上面的代码可以看到中断向量表和stubs都发生了代码搬移,所以如果中断向量表中仍然写成b vector_irq,那么实际执行的时候就无法跳转到搬移后的vector_irq处,因为指令码里写的是原来的偏移量,所以需要把指令码中的偏移量写成搬移后的。
 
我们把搬移前的中断向量表中的irq入口地址记irq_PC,它在中断向量表的偏移量就是irq_PC-vectors_start, vector_irq在stubs中的偏移量是vector_irq-stubs_start,这两个偏移量在搬移前后是不变的。搬移后 vectors_start在0xffff0000处,而stubs_start在0xffff0200处,所以搬移后的vector_irq相对于中断向量中的中断入口地址的偏移量就是:200+vector_irq在stubs中的偏移量再减去中断入口在向量表中的偏移量,即200+ vector_irq-stubs_start-irq_PC +vectors_start = (vector_irq-irq_PC) + vectors_start+200-stubs_start,对于括号内的值实际上就是中断向量表中写的vector_irq,减去irq_PC是由汇编器完成的
为什么说:"减去irq_PC是由汇编器完成的"?
举个例子:
b vector_irq实际上实现的就是vector_irq-irq_PC这么大偏移量的相对跳转。
而要在这个位置上实现相对偏移量为"((vector_irq-irq_PC)+ (vectors_start + 200 - stubs_start))"这么大的相对跳转,b指令后的地址标识符也应加上一个"(vectors_start + 200 - stubs_start)",也就是stubs_offset,因此在entry-armv.S中将stubs_offset定义为"__vectors_start + 0x200 - __stubs_start":
.equ    stubs_offset, __vectors_start + 0x200 - __stubs_start
虽然在搬移前,这个跳转指令无法正确跳转,但是在搬移后,这个指令刚好令PC跳转到vector_irq_after_moved,进而执行vector_irq对应的代码

搬移到0xffff0000之前

            irq_PC           stubs_start                  vector_irq  
               |                          |                                    |     
----------|----+------------|~~~~|-----------------+--------------------------|
              | 
     vector_start                


          irq_PC - vector_start  
                 ___
                /   \
----------|----+------------|~~~~|-----------------+--------------------------|
                \________________________________________/
                        vector_irq - irq_PC


搬移到0xffff0000之后:
                                                vector_irq_after_moved
      0xffff0000                                                    |
----------|----+------------|~~|-----------------+--------------------------|
                \___________________/\________________/    
                        200                              |
                                                           V     
                                          vector_irq - stubs_start 

格式难以调整,回家画图上传!!!
              

//下面看看stubs_start后面的对各个异常是如何处理的:

        .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)
        .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

/*
 * Data abort dispatcher
 * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
 */
        vector_stub     dabt, ABT_MODE, 8

        .long   __dabt_usr                      @  0  (USR_26 / USR_32)
        .long   __dabt_invalid                  @  1  (FIQ_26 / FIQ_32)
        .long   __dabt_invalid                  @  2  (IRQ_26 / IRQ_32)
        .long   __dabt_svc                      @  3  (SVC_26 / SVC_32)
        .long   __dabt_invalid                  @  4
        .long   __dabt_invalid                  @  5
        .long   __dabt_invalid                  @  6
        .long   __dabt_invalid                  @  7
        .long   __dabt_invalid                  @  8
        .long   __dabt_invalid                  @  9
        .long   __dabt_invalid                  @  a
        .long   __dabt_invalid                  @  b
        .long   __dabt_invalid                  @  c

        .long   __dabt_invalid                  @  d
        .long   __dabt_invalid                  @  e
        .long   __dabt_invalid                  @  f

/*
 * Prefetch abort dispatcher
 * Enter in ABT mode, spsr = USR CPSR, lr = USR PC
 */
        vector_stub     pabt, ABT_MODE, 4


        .long   __pabt_usr                      @  0 (USR_26 / USR_32)
        .long   __pabt_invalid                  @  1 (FIQ_26 / FIQ_32)
        .long   __pabt_invalid                  @  2 (IRQ_26 / IRQ_32)
        .long   __pabt_svc                      @  3 (SVC_26 / SVC_32)
        .long   __pabt_invalid                  @  4
        .long   __pabt_invalid                  @  5
        .long   __pabt_invalid                  @  6
        .long   __pabt_invalid                  @  7
        .long   __pabt_invalid                  @  8
        .long   __pabt_invalid                  @  9
        .long   __pabt_invalid                  @  a
        .long   __pabt_invalid                  @  b
        .long   __pabt_invalid                  @  c
        .long   __pabt_invalid                  @  d
        .long   __pabt_invalid                  @  e
        .long   __pabt_invalid                  @  f


/*
 * Undef instr entry dispatcher
 * Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
 */
        vector_stub     und, UND_MODE

        .long   __und_usr                       @  0 (USR_26 / USR_32)
        .long   __und_invalid                   @  1 (FIQ_26 / FIQ_32)
        .long   __und_invalid                   @  2 (IRQ_26 / IRQ_32)
        .long   __und_svc                       @  3 (SVC_26 / SVC_32)
        .long   __und_invalid                   @  4
        .long   __und_invalid                   @  5
        .long   __und_invalid                   @  6
        .long   __und_invalid                   @  7
        .long   __und_invalid                   @  8
        .long   __und_invalid                   @  9
        .long   __und_invalid                   @  a
        .long   __und_invalid                   @  b
        .long   __und_invalid                   @  c
        .long   __und_invalid                   @  d
        .long   __und_invalid                   @  e
        .long   __und_invalid                   @  f

        .align  5

/*=============================================================================
 * Undefined FIQs
 *-----------------------------------------------------------------------------
 * Enter in FIQ mode, spsr = ANY CPSR, lr = ANY PC
 * MUST PRESERVE SVC SPSR, but need to switch to SVC mode to show our msg.
 * Basically to switch modes, we *HAVE* to clobber one register...  brain
 * damage alert!  I don't think that we can execute any code in here in any
 * other mode than FIQ...  Ok you can switch to another mode, but you can't
 * get out of that mode without clobbering one register.
 */
vector_fiq:
        subs    pc, lr, #4

/*=============================================================================
 * Address exception handler
 *-----------------------------------------------------------------------------
 * These aren't too critical.
 * (they're not supposed to happen, and won't happen in 32-bit data mode).
 */

vector_addrexcptn:
        b       vector_addrexcptn

/*
 * We group all the following data together to optimise
 * for CPUs with separate I & D caches.
 */
        .align  5

.LCvswi:
        .word   vector_swi

        .globl  __stubs_end
__stubs_end:


        .equ    stubs_offset, __vectors_start + 0x200 - __stubs_start

        .globl  __vectors_start
__vectors_start:
 ARM(   swi     SYS_ERROR0      )
 THUMB( svc     #0              )
 THUMB( nop                     )
        W(b)    vector_und + stubs_offset
        W(ldr)  pc, .LCvswi + stubs_offset
        W(b)    vector_pabt + stubs_offset
        W(b)    vector_dabt + stubs_offset
        W(b)    vector_addrexcptn + stubs_offset
        W(b)    vector_irq + stubs_offset
        W(b)    vector_fiq + stubs_offset

        .globl  __vectors_end
__vectors_end:
        .data

        .globl  cr_alignment
        .globl  cr_no_alignment
cr_alignment:
        .space  4
cr_no_alignment:
        .space  4

#ifdef CONFIG_MULTI_IRQ_HANDLER
        .globl  handle_arch_irq
handle_arch_irq:
        .space  4

//前面已经给出
//.macro  vector_stub, name, mode, correction=0
//        .align  5
//
//vector_\name:
//        ......
//ENDPROC(vector_\name)
//
//        .align  2
//        @ handler addresses follow this label
//1:
//        .endm

//因此第一个branch就是vector_irq

//        vector_stub     irq, IRQ_MODE, 4
//
//        .long   __irq_usr                       @  0  (USR_26 / USR_32)
//        ...
//        .long   __irq_svc                       @  3  (SVC_26 / SVC_32)
//        ...
//就是
//vector_\name:
//        .if \correction
//        sub     lr, lr, #\correction
//        .endif
//
//        @
//        @ Save r0, lr_<exception> (parent PC) and spsr_<exception>
//        @ (parent CPSR)
//        @保存前一个状态的lr,spsr,r0因为马要将返回模式改为SVC,防止中断嵌套
//
//        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 | PSR_ISETSTATE)
//        msr     spsr_cxsf, r0

//        @
//        @ the branch table must immediately follow this code
//        @
//        and     lr, lr, #0x0f
// THUMB( adr     r0, 1f                  )
// THUMB( ldr     lr, [r0, lr, lsl #2]    )
//        mov     r0, sp
// ARM(   ldr     lr, [pc, lr, lsl #2]    )
//        movs    pc, lr                  @ branch to handler in SVC mode
// ENDPROC(vector_\name)

//        .align  2
//        @ handler addresses follow this label
//1:
//        .long   __irq_usr                       @  0  (USR_26 / USR_32)
//        ...
//        .long   __irq_svc                       @  3  (SVC_26 / SVC_32)
//        ...

//vector_irq把发生中断时的r0,PC-4以及CPSR压栈(注意,压入的的irq模式的堆栈),把中断栈的栈指针赋给 r0,
//最后根据原来发生中断时CPU所处的工作模式(CPSR的低4位)找到相应的入口函数,在进入svc模式后进一步处理中断。

__irq_usr:
        usr_entry
        kuser_cmpxchg_check
        irq_handler
        get_thread_info tsk
        mov     why, #0
        b       ret_to_user_from_irq
 UNWIND(.fnend          )
ENDPROC(__irq_usr)


__irq_svc:
        svc_entry
        irq_handler

#ifdef CONFIG_PREEMPT
        get_thread_info tsk
        ldr     r8, [tsk, #TI_PREEMPT]          @ get preempt count
        ldr     r0, [tsk, #TI_FLAGS]            @ get flags
        teq     r8, #0                          @ if preempt count != 0
        movne   r0, #0                          @ force flags to 0
        tst     r0, #_TIF_NEED_RESCHED
        blne    svc_preempt
#endif

#ifdef CONFIG_TRACE_IRQFLAGS
        @ The parent context IRQs must have been enabled to get here in
        @ the first place, so there's no point checking the PSR I bit.
        bl      trace_hardirqs_on
#endif
        svc_exit r5                             @ return from exception
 UNWIND(.fnend          )
ENDPROC(__irq_svc)


//__irq_usr/__irq_svc主要通过调用宏usr_entry进一步保存现场,然后调用irq_handler进行中断处理
/*
 * Interrupt handling.
 */
        .macro  irq_handler
#ifdef CONFIG_MULTI_IRQ_HANDLER
        ldr     r1, =handle_arch_irq
        mov     r0, sp
        adr     lr, BSYM(9997f)
        ldr     pc, [r1]                       //调用handle_arch_irq
#else
        arch_irq_handler_default
#endif
9997:
        .endm

//如果CONFIG_MULTI_IRQ_HANDLER没有配置则调用arch_irq_handler_default
//arch/arm/include/asm/entry-macro-multi.S
/*
 * Interrupt handling.  Preserves r7, r8, r9
 */
        .macro  arch_irq_handler_default
        get_irqnr_preamble r6, lr
        //它首先通过宏 get_irqnr_and_base获得中断号,存在r0,然后把上面建立的pt_regs结构的指针,也就是sp值赋给r1,
        //把调用宏 get_irqnr_and_base的位置作为返回地址
1:      get_irqnr_and_base r0, r2, r6, lr 
        movne   r1, sp
        @
        @ routine called with r0 = irq number, r1 = struct pt_regs *
        @
        adrne   lr, BSYM(1b)
        bne     asm_do_IRQ                   //然后调用c语言定义的asm_do_IRQ()进一步处理中断

#ifdef CONFIG_SMP
        /*
         * XXX
         *
         * this macro assumes that irqstat (r2) and base (r6) are
         * preserved from get_irqnr_and_base above
         */
        ALT_SMP(test_for_ipi r0, r2, r6, lr)
        ALT_UP_B(9997f)
        movne   r1, sp
        adrne   lr, BSYM(1b)
        bne     do_IPI
#endif
9997:
        .endm

        .macro  arch_irq_handler, symbol_name
        .align  5
        .global \symbol_name
\symbol_name:
        mov     r8, lr
        arch_irq_handler_default
        mov     pc, r8
        .endm


//asm_do_IRQ()@arch/arm/kernel/irq.c定义如下:
/*
 * asm_do_IRQ is the interface to be used from assembly code.
 */
asmlinkage void __exception_irq_entry
asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
     handle_IRQ(irq, regs);
}




//如果CONFIG_MULTI_IRQ_HANDLER有配置,则调用handle_arch_irq
//handle_arch_irq是entry-armv.S里面定义的全局变量:
#ifdef CONFIG_MULTI_IRQ_HANDLER
        .globl  handle_arch_irq
handle_arch_irq:
        .space  4
#endif


//@arch/arm/include/asm/mach/irq.h:
extern void (*handle_arch_irq)(struct pt_regs *);

//@arch/arm/kernel/setup.c:
void __init setup_arch(char **cmdline_p)
+-- struct machine_desc *mdesc;
+-- setup_processor();
+-- if (!mdesc)
        mdesc = setup_machine_tags(machine_arch_type);
+-- ...
+-- paging_init(mdesc);
    +-- devicemaps_init(mdesc);  
        +-- vectors = early_alloc(PAGE_SIZE);  
        +-- early_trap_init(vectors);  
+-- ...
+-- handle_arch_irq = mdesc->handle_irq; //因此handle_arch_irq实际上就是machine_desc结构里面的handle_irq函数指针

//而machine_desc结构通过board-xxx.c中的MACHINE_START() ~ MACHINE_END 宏之间的代码进行初始化 
MACHINE_START(MSM8X60_RUMI3, "QCT MSM8X60 RUMI3")
        .fixup = msm8x60_fixup,
        .reserve = msm8x60_reserve,
        .map_io = msm8x60_map_io,
        .init_irq = msm8x60_init_irq,
        .handle_irq = gic_handle_irq,
        .init_machine = msm8x60_init,
        .timer = &msm_timer,
MACHINE_END

//@arch/arm/common/gic.c
asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
        u32 irqstat, irqnr;
        struct gic_chip_data *gic = &gic_data[0];
        void __iomem *cpu_base = gic_data_cpu_base(gic);

        do {
                irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
                irqnr = irqstat & ~0x1c00;

                if (likely(irqnr > 15 && irqnr < 1021)) {
                        irqnr = irq_find_mapping(gic->domain, irqnr);
                        handle_IRQ(irqnr, regs);                              //handle_IRQ()...
                        continue;
                }
                if (irqnr < 16) {
                        writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
#ifdef CONFIG_SMP
                        handle_IPI(irqnr, regs);                              //IPI:inter processor interrupt
#endif
                        continue;
                }
                break;
        } while (1);
}


//暂且抛开负责处理器间中断处理的handle_IPI()函数不管,两个调用链最终都归结到
//void handle_IRQ(unsigned int irq, struct pt_regs *regs)
//这个函数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值