linux中断导读之--初始化<2>

==================================
本文系本站原创,欢迎转载!
转载请注明出处:http://blog.csdn.net/gdt_a20

==================================

继续上一篇,看一下中断向量表初始化部分:

代码在init/main.c-start_kernel/setup_arch(&command_line);

early_trap_init

调用arch/arm/kernel/traps.c中

void __init early_trap_init(void)
{
#if defined(CONFIG_CPU_USE_DOMAINS)
    unsigned long vectors = CONFIG_VECTORS_BASE;         //可以配置trap基地址
#else
    unsigned long vectors = (unsigned long)vectors_page;
#endif
    extern char __stubs_start[], __stubs_end[];
    extern char __vectors_start[], __vectors_end[];
    extern char __kuser_helper_start[], __kuser_helper_end[];
    int kuser_sz = __kuser_helper_end - __kuser_helper_start;

    /*
     * Copy the vectors, stubs and kuser helpers (in entry-armv.S)
     * into the vector page, mapped at 0xffff0000, and ensure these
     * are visible to the instruction stream.
     */

        //将entry-armv.s对应部分copy到该地址,
    memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
    memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
    memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);

    /*
     * Do processor specific fixups for the kuser helpers
     */
    kuser_get_tls_init(vectors);

    /*
     * Copy signal return handlers into the vector page, and
     * set sigreturn to be a pointer to these.
     */
    memcpy((void *)(vectors + KERN_SIGRETURN_CODE - CONFIG_VECTORS_BASE),
           sigreturn_codes, sizeof(sigreturn_codes));
    memcpy((void *)(vectors + KERN_RESTART_CODE - CONFIG_VECTORS_BASE),
           syscall_restart_code, sizeof(syscall_restart_code));

    flush_icache_range(vectors, vectors + PAGE_SIZE);
    modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
}

#在arch/arm/kernel/entry-armv.s中
__vectors_start:
 ARM(    swi    SYS_ERROR0    )
 THUMB(    svc    #0        )
 THUMB(    nop            )                        //复位
    W(b)    vector_und + stubs_offset       //未定义模式
    W(ldr)    pc, .LCvswi + stubs_offset   //swi,系统调用进入
    W(b)    vector_pabt + stubs_offset       //指令预取
    W(b)    vector_dabt + stubs_offset       //data
    W(b)    vector_addrexcptn + stubs_offset //reserve
    W(b)    vector_irq + stubs_offset       //irq
    W(b)    vector_fiq + stubs_offset       //fiq,没有用到

    .globl    __vectors_end
__vectors_end:

.equ    stubs_offset, __vectors_start + 0x200 - __stubs_start

#先看宏:
/*
 * 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

#对应irq定义
/*
 * 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_irq:
    .if 4
    sub    lr, lr, #4
    .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, #(\IRQ_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]    ) @如果进入中断前是usr,
                                                            则取出PC+4*0的内容,即__irq_usr @如果进入中断前 是svc,则取出PC+4*3的内容,即__irq_svc
                                                        @根据进入前的状态判断进入哪个处理流程
    movs    pc, lr                               @ branch to handler in SVC mode
ENDPROC(vector_irq)

=================

#__irq_usr:
    usr_entry
    kuser_cmpxchg_check
    irq_handler
    get_thread_info tsk    @得到当前进程信息
    mov    why, #0

    b    ret_to_user_from_irq

=================

/*
 * Interrupt handling.
 */
    .macro    irq_handler
#ifdef CONFIG_MULTI_IRQ_HANDLER
    ldr    r1, =handle_arch_irq
    mov    r0, sp
    ldr    r1, [r1]
    adr    lr, BSYM(9997f)
    teq    r1, #0
    movne    pc, r1
#endif
    arch_irq_handler_default
9997:
    .endm

=================

###arch/arm/kernel/setup.c
handle_arch_irq = mdesc->handle_irq;
####arch/arm/include/asm/entry-macro-multi.s
/*
 * Interrupt handling.  Preserves r7, r8, r9
 */
    .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

##arm/kernel/irq.c

继续##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);
}

/*
 *  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)
{
    struct pt_regs *old_regs = set_irq_regs(regs);

    irq_enter();

    /*
     * Some hardware gives randomly wrong interrupts.  Rather
     * than crashing, do something sensible.
     */
    if (unlikely(irq >= nr_irqs)) {
        if (printk_ratelimit())
            printk(KERN_WARNING "Bad IRQ%u\n", irq);
        ack_bad_irq(irq);
    } else {
        generic_handle_irq(irq);
    }

    /* AT91 specific workaround */
    irq_finish(irq);

    irq_exit();
    set_irq_regs(old_regs);
}

#kernel/irq/irqdesc.c
/**
 * 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;
}
EXPORT_SYMBOL_GPL(generic_handle_irq);
#include/linux/irqdesc.h
static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
    desc->handle_irq(irq, desc);
}

最后会调用handle_irq。

你可能感兴趣的:(linux,exception,struct,vector,domain,branch)