IRQ中断处理流程

 基于Linux2.6.30.4分析IRQ中断的处理流程。

1.中断入口

/* arch/arm/kenel/entry-armv.S*/
b    vector_irq + stubs_offset

2.vector_irq

vector_stub 宏展开即为vector_irq, 参考Linux异常体系之vector_stub宏解析

/*
 * 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)

3.__irq_user

__irq_usr:
usr_entry      /*3.1*/
kuser_cmpxchg_check

#ifdef CONFIG_TRACE_IRQFLAGS
bl    trace_hardirqs_off          
#endif
get_thread_info tsk     /*3.2*/

#ifdef CONFIG_PREEMPT
/*
*r8<--old preempt_count
*r7<--preempt_count+1
*preempt_count<--r7
*/
ldr    r8, [tsk, #TI_PREEMPT]    @ get preempt count
add    r7, r8, #1    @ increment it
str    r7, [tsk, #TI_PREEMPT]
#endif

irq_handler     /*3.3*/
#ifdef CONFIG_PREEMPT
/*
*r0<--new preempt_count
*preempt<--old preempt_count
*/
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
b    ret_to_user    /*3.4*/
UNWIND(.fnend    )
ENDPROC(__irq_usr)

 3.1__user_entry 

.macro    usr_entry
UNWIND(.fnstart    )
UNWIND(.cantunwind    )    @ don't unwind the user space
/* DEFINE(S_FRAME_SIZE,    sizeof(struct pt_regs));*/
sub    sp, sp, #S_FRAME_SIZE    /**/
stmib    sp, {r1 - r12}

ldmia    r0, {r1 - r3}
add    r0, sp, #S_PC    @ here for interlock avoidance
mov    r4, #-1    @ "" "" "" ""

str    r1, [sp]    @ save the "real" r0 copied
@ from the exception stack

@
@ We are now ready to fill in the remaining blanks on the stack:
@
@ r2 - lr_<exception>, already fixed up for correct return/restart
@ r3 - spsr_<exception>
@ r4 - orig_r0 (see pt_regs definition in ptrace.h)
@
@ Also, separately save sp_usr and lr_usr
@
stmia    r0, {r2 - r4}
/*“^”符号表示访问user mode的寄存器*/
stmdb    r0, {sp, lr}^

@
@ Enable the alignment trap while in kernel mode
@
alignment_trap r0

@
@ Clear FP to mark the first stack frame
@
zero_fp
.endm

 这里面用到pt_regs结构保存栈上的数据,8字节对齐

/*
 * This struct defines the way the registers are stored on the
 * stack during a system call.  Note that sizeof(struct pt_regs)
 * has to be a multiple of 8.
 */
struct pt_regs {
    long uregs[18];
};

与之相关的宏定义如下

DEFINE(S_FRAME_SIZE,        sizeof(struct pt_regs));
  
DEFINE(S_R0,            offsetof(struct pt_regs, ARM_r0));
DEFINE(S_R1,            offsetof(struct pt_regs, ARM_r1));
DEFINE(S_R2,            offsetof(struct pt_regs, ARM_r2));
DEFINE(S_R3,            offsetof(struct pt_regs, ARM_r3));
DEFINE(S_R4,            offsetof(struct pt_regs, ARM_r4));
DEFINE(S_R5,            offsetof(struct pt_regs, ARM_r5));
DEFINE(S_R6,            offsetof(struct pt_regs, ARM_r6));
DEFINE(S_R7,            offsetof(struct pt_regs, ARM_r7));
DEFINE(S_R8,            offsetof(struct pt_regs, ARM_r8));
DEFINE(S_R9,            offsetof(struct pt_regs, ARM_r9));
DEFINE(S_R10,            offsetof(struct pt_regs, ARM_r10));
DEFINE(S_FP,            offsetof(struct pt_regs, ARM_fp));
DEFINE(S_IP,            offsetof(struct pt_regs, ARM_ip));
DEFINE(S_SP,            offsetof(struct pt_regs, ARM_sp));
DEFINE(S_LR,            offsetof(struct pt_regs, ARM_lr));
DEFINE(S_PC,            offsetof(struct pt_regs, ARM_pc));
DEFINE(S_PSR,            offsetof(struct pt_regs, ARM_cpsr));
DEFINE(S_OLD_R0,        offsetof(struct pt_regs, ARM_ORIG_r0));

#define ARM_cpsr    uregs[16]
#define ARM_pc        uregs[15]
#define ARM_lr        uregs[14]
#define ARM_sp        uregs[13]
#define ARM_ip        uregs[12]
#define ARM_fp        uregs[11]
#define ARM_r10        uregs[10]
#define ARM_r9        uregs[9]
#define ARM_r8        uregs[8]
#define ARM_r7        uregs[7]
#define ARM_r6        uregs[6]
#define ARM_r5        uregs[5]
#define ARM_r4        uregs[4]
#define ARM_r3        uregs[3]
#define ARM_r2        uregs[2]
#define ARM_r1        uregs[1]
#define ARM_r0        uregs[0]
#define ARM_ORIG_r0    uregs[17]
macos

 3.2 get_thread_info tsk

tsk即r9寄存器的别名,内核中为寄存器声明的别名如下

/*
 * These are the registers used in the syscall handler, and allow us to
 * have in theory up to 7 arguments to a function - r0 to r6.
 *
 * r7 is reserved for the system call number for thumb mode.
 *
 * Note that tbl == why is intentional.
 *
 * We must set at least "tsk" and "why" when calling ret_with_reschedule.
 */
scno    .req    r7        @ syscall number
tbl    .req    r8        @ syscall table pointer
why    .req    r8        @ Linux syscall (!= 0)
tsk    .req    r9        @ current thread_info

get_thread_info tsk的作用是获取sp地址保存在tsk(r9)中,即r9中保存当前任务的thread_info结构的地址。

.macro    get_thread_info, rd
mov    \rd, sp, lsr #13  /*获取sp地址*/
mov    \rd, \rd, lsl #13  /*8KBytes对齐*/
.endm

3.3 irq_handler

irq_handler函数调用分析以后再写。

/*
 * Interrupt handling.  Preserves r7, r8, r9
 */
    .macro    irq_handler
    get_irqnr_preamble r5, lr
1:    get_irqnr_and_base r0, r6, r5, lr
    movne    r1, sp
    @
    @ routine called with r0 = irq number, r1 = struct pt_regs *
    @
    adrne    lr, 1b
    bne    asm_do_IRQ

#ifdef CONFIG_SMP
    /*
     * XXX
     *
     * this macro assumes that irqstat (r6) and base (r5) are
     * preserved from get_irqnr_and_base above
     */
    test_for_ipi r0, r6, r5, lr
    movne    r0, sp
    adrne    lr, 1b
    bne    do_IPI

#ifdef CONFIG_LOCAL_TIMERS
    test_for_ltirq r0, r6, r5, lr
    movne    r0, sp
    adrne    lr, 1b
    bne    do_local_timer
#endif
#endif

    .endm

 3.4 ret_to_user

ret_to_user函数调用分析以后再写。

/*
 * "slow" syscall return path.  "why" tells us if this was a real syscall.
 */
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
ENDPROC(ret_to_user)

 

posted @ 2017-12-28 00:10  bluebluebluesky  阅读(2973)  评论(0编辑  收藏  举报