浅析Arm Linux中断Vector向量表的建立流程

来源:互联网 发布:网络创业园 编辑:程序博客网 时间:2024/05/23 16:56

Linux混入了mmu内存管理之后,ARM的中断是怎么样的呢?和我们在裸板上的中断有没有区别?让我们从源代码入手,做一个粗略的分析:

init/main.c->start_kernel()->trap_init()//-----------------------------------------------1.trap_init()//gliethttp函数位于arch/arm/kernel/traps.cvoid __init trap_init(void){    extern void __trap_init(unsigned long);    unsigned long base = vectors_base();           //返回中断base基址0xffff0000    __trap_init(base);                             //以base为vector基址,初始化中断向量表    if (base != 0)        printk(KERN_DEBUG "Relocating machine vectors to 0x%08lx\n",            base);#ifdef CONFIG_CPU_32    modify_domain(DOMAIN_USER, DOMAIN_CLIENT);#endif}//--------------------------------------2.vectors_base()//gliethttp include/arch/asm-arm/proc-armv/system.hextern unsigned long cr_alignment;#if __LINUX_ARM_ARCH__ >= 4                           //at91rm9200是ARMV4结构#define vectors_base()    ((cr_alignment & CR_V) ? 0xffff0000 : 0)#else#define vectors_base()    (0)#endif

可以看到ARMv4以下的版本,该地址固定为0;ARMv4及以上版本,ARM中断向量表的地址由CP15协处理器c1寄存器中V位(bit[13])控制,V和中断向量表的对应关系如下:

V=0    ~    0x00000000~0x0000001CV=1    ~    0xffff0000~0xffff001C//------------------------------------------2.1 cr_alignment//gliethttp arch/arm/kernel/entry-armv.SENTRY(stext)        mov    r12, r0        mov    r0, #F_BIT | I_BIT | MODE_SVC @ make sure svc mode        msr    cpsr_c, r0                @ and all irqs disabled//__lookup_processor_type 查询处理器类型,[gliethttp 以后补上<浅析head-armv.S>]返回值//2007-07-04//r9 = processor ID                                  //读取cp15的c0寄存器//r10 = pointer to processor structure               //下面会add pc, r10, #12,跳转到__arm920_setup//gliethttp 在vmlinux-armv.lds.in中//__proc_info_begin = .;//             *(.proc.info)// __proc_info_end = .;//见2.2        bl    __lookup_processor_type                            teq    r10, #0                   @ invalid processor?        moveq    r0, #'p'                @ yes, error 'p'        beq    __error        bl    __lookup_architecture_type        teq    r7, #0                    @ invalid architecture?        moveq    r0, #'a'                @ yes, error 'a'        beq    __error//__create_page_tables 创建arm启动临时使用的前4M页表        bl    __create_page_tables        adr    lr, __ret                 @ return address        add    pc, r10, #12              @ initialise processor        .type    __switch_data, %object__switch_data:    .long    __mmap_switched        .long    SYMBOL_NAME(__bss_start)        .long    SYMBOL_NAME(_end)        .long    SYMBOL_NAME(processor_id)        .long    SYMBOL_NAME(__machine_arch_type)        .long    SYMBOL_NAME(cr_alignment)        .long    SYMBOL_NAME(init_task_union)+8192/* * Enable the MMU. This completely changes the structure of the visible * memory space. You will not be able to trace execution through this. * If you have an enquiry about this, *please* check the linux-arm-kernel * mailing list archives BEFORE sending another post to the list. */        .type    __ret, %function__ret:        ldr    lr, __switch_data        mcr    p15, 0, r0, c1, c0       //将__arm920_setup中设置的r0值,置入cp15协处理器c1寄存器中        mrc    p15, 0, r0, c1, c0, 0     @ read it back.        mov    r0, r0                                //填充armv4中的三级流水线:mov r0,r0 对应一个nop,所以对应2个nop和一个mov pc,lr刚好三个"无用"操作        mov    r0, r0        mov    pc, lr                 //跳转到__mmap_switched函数 gliethtttp/* * The following fragment of code is executed with the MMU on, and uses * absolute addresses; this is not position independent. * * r0 = processor control register * r1 = machine ID * r9 = processor ID */        .align    5__mmap_switched:        adr    r3, __switch_data + 4        ldmia    r3, {r4, r5, r6, r7, r8, sp}@ r2 = compat//2007-07-04 gliethttp//r4    ~    __bss_start//r5    ~    _end//r6    ~    processor_id//r7    ~    __machine_arch_type//r8    ~    cr_alignment//sp    ~    (init_task_union)+8192//以下几步操作对processor_id,__machine_arch_type,cr_alignment赋值gliethttp        mov    fp, #0                                @ Clear BSS (and zero fp)1:      cmp    r4, r5                                //bss区清0        strcc    fp, [r4],#4        bcc    1b        str    r9, [r6]                              @ Save processor ID        str    r1, [r7]                              @ Save machine type#ifdef CONFIG_ALIGNMENT_TRAP        orr    r0, r0, #2                            @ ...........A.#endif        bic    r2, r0, #2                            @ Clear 'A' bit//r2存放 禁用TRAP队列故障 后的r0值//r8->cr_alignment,cr_no_alignment//所以stmia    r8, {r0, r2}后,cr_alignment = r0,cr_no_alignment = r2        stmia    r8, {r0, r2}                        @ Save control register values        b    SYMBOL_NAME(start_kernel)               //进入内核C程序//--------------------------------------------------------------------------------2.2 __arm920_proc_info//gliethttp arch/arm/mm/proc-arm920.S.section ".proc.info", #alloc, #execinstr    .type    __arm920_proc_info,#object__arm920_proc_info:                                  //该地址存储到r10中    .long    0x41009200    .long    0xff00fff0    .long    0x00000c1e                               @ mmuflags    b    __arm920_setup                              //add pc, r10, #12 gliethttp将使cpu执行b __arm920_setup跳转指令    .long    cpu_arch_name    .long    cpu_elf_name    .long    HWCAP_SWP | HWCAP_HALF | HWCAP_THUMB    .long    cpu_arm920_info    .long    arm920_processor_functions    .size    __arm920_proc_info, . - __arm920_proc_info//--------------------------------------------------------------------------------2.3 __arm920_setup    .section ".text.init", #alloc, #execinstr__arm920_setup:    mov    r0, #0    mcr    p15, 0, r0, c7, c7        @ invalidate I,D caches on v4    mcr    p15, 0, r0, c7, c10, 4    @ drain write buffer on v4    mcr    p15, 0, r0, c8, c7        @ invalidate I,D TLBs on v4    mcr    p15, 0, r4, c2, c0        @ load page table pointer    mov    r0, #0x1f                 @ Domains 0, 1 = client    mcr    p15, 0, r0, c3, c0        @ load domain access register    mrc    p15, 0, r0, c1, c0        @ get control register v4/* * Clear out 'unwanted' bits (then put them in if we need them) *///gliethttp r0单元存放了cp15协处理器c1寄存器的值,如下代码对该值进行加工                                          @ VI ZFRS BLDP WCAM    bic    r0, r0, #0x0e00                           //清0 bit[9..11]    bic    r0, r0, #0x0002                           //清0 bit[1]    bic    r0, r0, #0x000c                    bic    r0, r0, #0x1000                @ ...0 000. .... 000./* * Turn on what we want */    orr    r0, r0, #0x0031                           //bit0=1 使能mmu    orr    r0, r0, #0x2100                @ ..1. ...1 ..11 ...1//bit13=1 中断向量表基址为0xFFFF0000#ifndef CONFIG_CPU_DCACHE_DISABLE    orr    r0, r0, #0x0004                @ .... .... .... .1..#endif#ifndef CONFIG_CPU_ICACHE_DISABLE    orr    r0, r0, #0x1000                @ ...1 .... .... ....#endif    mov    pc, lr

小结:通过以上的源码分析,我们可以清楚的看到vectors_base()返回的中断向量基址值为0xFFFF0000,接下来我们继续分析下面的源码:

3.__trap_ini()//gliethttp函数位于arch/arm/kernel/entry-armv.S        .align    5__stubs_start:/* * Interrupt dispatcher * Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC */vector_IRQ:    @        @ save mode specific registers        @        ldr    r13, .LCsirq        sub    lr, lr, #4        str    lr, [r13]            @ save lr_IRQ        mrs    lr, spsr        str    lr, [r13, #4]        @ save spsr_IRQ...略...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.LCsirq:    .word    __temp_irq.LCsund:    .word    __temp_und.LCsabt:    .word    __temp_abt__stubs_end:        .equ    __real_stubs_start, .LCvectors + 0x200.LCvectors:    swi    SYS_ERROR0        b    __real_stubs_start + (vector_undefinstr - __stubs_start)        ldr    pc, __real_stubs_start + (.LCvswi - __stubs_start)        b    __real_stubs_start + (vector_prefetch - __stubs_start)        b    __real_stubs_start + (vector_data - __stubs_start)        b    __real_stubs_start + (vector_addrexcptn - __stubs_start)        b    __real_stubs_start + (vector_IRQ - __stubs_start)        b    __real_stubs_start + (vector_FIQ - __stubs_start)ENTRY(__trap_init)        stmfd     {r4 - r6, lr}        adr    r1, .LCvectors    @ set up the vectors//通过adr指令获得与pc地址为偏移地址数据,最后r1=pc-0x2c        ldmia    r1, {r1, r2, r3, r4, r5, r6, ip, lr}//将中断向量表跳转数据分别转存到r1,r2,r3,r4,r5,r6,ip,lr寄存器        stmia    r0, {r1, r2, r3, r4, r5, r6, ip, lr}//根据编译器规则r0存放了函数__trap_init(base)传入的参数值base,其值为//0xFFFF0000//将r1,r2,r3,r4,r5,r6,ip,lr数据顺序转储到以虚拟地址0xFFFF0000为起始地址的空间        add    r2, r0, #0x200                        //r2=0xFFFF0000+0x200=0xFFFF2000        adr    r0, __stubs_start@ copy stubs to 0x200//r0=pc相对地址=pc-0x26c        adr    r1, __stubs_end                       //r1=pc相对地址=pc-0x401:      ldr    r3, [r0], #4        str    r3, [r2], #4                          //将__stubs_start和__stubs_end之间的中断处理代码拷贝到以虚拟地址0xFFFF2000为起始地址的顺序空间        cmp    r0, r1        blt    1b        LOADREGS(fd, {r4 - r6, pc})

这样我们来看看空间分布图:

虚拟地址     异常              处理代码    0xffff0000   reset             swi SYS_ERROR0    0xffff0004   undefined         b __real_stubs_start + (vector_undefinstr - __stubs_start)    0xffff0008   软件中断          ldr pc, __real_stubs_start + (.LCvswi - __stubs_start)    0xffff000c   取指令异常        b __real_stubs_start + (vector_prefetch - __stubs_start)    0xffff0010   数据异常          b __real_stubs_start + (vector_data - __stubs_start)    0xffff0014   reserved          b __real_stubs_start + (vector_addrexcptn - __stubs_start)    0xffff0018   irq               b __real_stubs_start + (vector_IRQ - __stubs_start)    0xffff001c   fiq               b __real_stubs_start + (vector_FIQ - __stubs_start)       ...    0xffff2000   __stubs_start:    ldr    r13, .LCsirq    0xffff2004                     sub    lr, lr, #4    0xffff2008                     str    lr, [r13] @ save lr_IRQ       ...    0xffff21a4                     .LCsirq:    .word    __temp_irq    0xffff21a8                     .LCsund:    .word    __temp_und    0xffff21ac                     .LCsabt:    .word    __temp_abt

如果你现在有这样一种疑惑?程序为什么编译地址是0xc0008000,将其直接拷贝到0xffff0000和0xffff2000为什么还能顺利执行,请参考我的另一篇blog《arm相对跳转到底是怎么回事》,主要原因是b指令是相对跳转指令,adr也是基于pc的前后偏移指令,当然对于ldr pc, __real_stubs_start + (.LCvswi - __stubs_start)是绝对地址赋值,所以最后pc会跳转到0xc000xxxx空间执行代码,其他的跳转如:b __real_stubs_start + (vector_IRQ - __stubs_start)都会到0xffff2xxxx相应的vector_IRQ处执行向量中断处理函数,还有一个要分析的问题是:.equ __real_stubs_start,

.LCvectors + 0x200,语句b __real_stubs_start

+ (vector_undefinstr - __stubs_start)就是跳转到LCvectors+0x200空间执行。举一个例子:

org 0x8000        reset    b    InitRest        ...        InitRest:        ...        那么reset标号的地址为0x8000,他的意思是在0x8000处向前跳转到InitRest,我们也可以这样来构造跳转:        org 0x8000        reset b (0x8000+(.InitRest - .reset))        ...        InitRest:        ...        以上的构造语句同样实现了相对0x8000地址的跳转



from: http://www.xker.com/page/e2007/0705/27472.html



0 0
原创粉丝点击