linux在ARM平台上的中断流程

当发生中断时,系统跳转到ARM平台的异常向量表(vector_irq位置):

    .section .vectors, "ax", %progbits
__vectors_start:
    W(b)    vector_rst
    W(b)    vector_und
    W(ldr)  pc, __vectors_start + 0x1000
    W(b)    vector_pabt
    W(b)    vector_dabt
    W(b)    vector_addrexcptn
    W(b)    vector_irq
    W(b)    vector_fiq

以下的vector_stub irq位置为vector_irq跳转到的位置,后面跟着的是irq分发表,这里只区分了SVC和USR两种情况:

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

vector_stub宏的定义如下:

/*
 * Vector stubs.
 *
 * This code is copied to 0xffff1000 so we can use branches in the
 * vectors, rather than ldr's.  Note that this code must not exceed
 * a page size.
 *
 * 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)

分别可以由svc和usr模式进入:

    .align  5
__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

    svc_exit r5, irq = 1            @ return from exception
 UNWIND(.fnend      )
ENDPROC(__irq_svc)

    .align  5
__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)

不论SVC还是USR都会调用IRQ handler,这里可以根据配置选择1:1或者1:N的方式:

/*
 * 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]
#else
    arch_irq_handler_default
#endif
9997:
    .endm

当配置为CONFIG_MULTI_IRQ_HANDLER时,handle_arch_irq可以在2个地方初始化:

#ifdef CONFIG_MULTI_IRQ_HANDLER
void __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
{
    if (handle_arch_irq)
        return;

    handle_arch_irq = handle_irq;
}
#endif

void __init setup_arch(char **cmdline_p)
{
.....
#ifdef CONFIG_MULTI_IRQ_HANDLER
    handle_arch_irq = mdesc->handle_irq;
#endif
.....
}

如果CONFIG_MULTI_IRQ_HANDLER未打开,则:

    .macro  arch_irq_handler_default
    get_irqnr_preamble r6, lr
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

#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

get_irqnr_preamble和get_irqnr_and_base调用具体chip的实现来获取中断号,比如对于mach-davinci而言,其实现如下:

        .macro  get_irqnr_preamble, base, tmp
        ldr \base, =davinci_intc_base
        ldr \base, [\base]
        .endm

        .macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
#if defined(CONFIG_AINTC) && defined(CONFIG_CP_INTC)
        ldr \tmp, =davinci_intc_type
        ldr \tmp, [\tmp]
        cmp \tmp, #DAVINCI_INTC_TYPE_CP_INTC
        beq 1001f
#endif
#if defined(CONFIG_AINTC)
        ldr \tmp, [\base, #0x14]
        movs \tmp, \tmp, lsr #2
        sub \irqnr, \tmp, #1
        b 1002f
#endif
#if defined(CONFIG_CP_INTC)
1001:       ldr \irqnr, [\base, #0x80] /* get irq number */
        mov \tmp, \irqnr, lsr #31
        and \irqnr, \irqnr, #0xff  /* irq is in bits 0-9 */
        and \tmp, \tmp, #0x1
        cmp \tmp, #0x1
#endif
1002:
        .endm

然后就走到asm_do_IRQ这个C环境函数:

/*
 * handle_IRQ handles all hardware IRQ's.  Decoded IRQs should
 * not come via this function.  Instead, they should provide their
 * own 'handler'.  Used by platform code implementing C-based 1st
 * level decoding.
 */
void handle_IRQ(unsigned int irq, struct pt_regs *regs)
{
    __handle_domain_irq(NULL, irq, false, regs);
}

/*
 * 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);
}
int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
            bool lookup, struct pt_regs *regs)
{
    struct pt_regs *old_regs = set_irq_regs(regs);
    unsigned int irq = hwirq;
    int ret = 0;

    irq_enter();

#ifdef CONFIG_IRQ_DOMAIN
    if (lookup)
        irq = irq_find_mapping(domain, hwirq);
#endif

    /*
     * Some hardware gives randomly wrong interrupts.  Rather
     * than crashing, do something sensible.
     */
    if (unlikely(!irq || irq >= nr_irqs)) {
        ack_bad_irq(irq);
        ret = -EINVAL;
    } else {
        generic_handle_irq(irq);
    }

    irq_exit();
    set_irq_regs(old_regs);
    return ret;
}

以下是根据映射的虚拟中断号,找到对应的struct irq_desc,并执行里面注册的中断处理函数:

/**
 * generic_handle_irq - Invoke the handler for a particular irq
 * @irq:    The irq number to handle
 *
 */
int generic_handle_irq(unsigned int irq)
{
    struct irq_desc *desc = irq_to_desc(irq);

    if (!desc)
        return -EINVAL;
    generic_handle_irq_desc(irq, desc);
    return 0;
}

static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
    desc->handle_irq(irq, desc);
}

在中断处理函数执行前后分别有irq_enter函数和irq_exit函数:

/*
 * Enter an interrupt context.
 */
void irq_enter(void)
{
    rcu_irq_enter();
    if (is_idle_task(current) && !in_interrupt()) {
        /*
         * Prevent raise_softirq from needlessly waking up ksoftirqd
         * here, as softirq will be serviced on return from interrupt.
         */
        local_bh_disable();
        tick_irq_enter();
        _local_bh_enable();
    }

    __irq_enter();
}

/*
 * Exit an interrupt context. Process softirqs if needed and possible:
 */
void irq_exit(void)
{
#ifndef __ARCH_IRQ_EXIT_IRQS_DISABLED
    local_irq_disable();
#else
    WARN_ON_ONCE(!irqs_disabled());
#endif

    account_irq_exit_time(current);
    preempt_count_sub(HARDIRQ_OFFSET);
    if (!in_interrupt() && local_softirq_pending())
        invoke_softirq();

    tick_irq_exit();
    rcu_irq_exit();
    trace_hardirq_exit(); /* must be last! */
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值