新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > ARM Linux 外部中斷

        ARM Linux 外部中斷

        作者: 時間:2016-11-20 來源:網絡 收藏
        最近在學習arm linux的整套外部中斷的處理過程,在網上匯總了一些資料,整個過程差不多都了解到了。如果沒有這些資料我真是沒信心從匯編開始讀代碼,感謝 奔騰年代的jimmy.lee和 linux論壇的bx_bird。
        在下面的的注釋中有一些我讀代碼時遇到的問題,要是大家知道是怎么回事,希望多多。

        =============================================
        一.ARM linux的中斷向量表初始化分析

        ARM linux內核啟動時,通過start_kernel()->trap_init()的調用關系,初始化內核的中斷異常向量表.

        /* arch/arm/kernel/traps.c */
        void __init trap_init(void)
        {
        extern void __trap_init(unsigned long);
        unsigned long base = vectors_base();
        __trap_init(base);
        if (base != 0)
        oopsprintk(KERN_DEBUG "Relocating machine vectors to 0x%08lxn", base);
        #ifdef CONFIG_CPU_32
        modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
        #endif
        }
        vectors_base是一個宏,它的作用是獲取ARM異常向量的地址,該宏在include/arch/asm-arm/proc-armv/system.h中定義:

        extern unsigned long cr_no_alignment; /* defined in entry-armv.S */
        extern unsigned long cr_alignment; /* defined in entry-armv.S */
        #if __LINUX_ARM_ARCH__ >= 4
        #define vectors_base() ((cr_alignment & CR_V) ? 0xffff0000 : 0)
        #else
        #define vectors_base() (0)
        #endif
          對于ARMv4以下的版本,這個地址固定為0;ARMv4及其以上的版本,ARM異常向量表的地址受協處理器CP15的c1寄存器(control register)中V位(bit[13])的控制,如果V=1,則異常向量表的地址為0x00000000~0x0000001C;如果V=0,則為:0xffff0000~0xffff001C。(詳情請參考ARM Architecture Reference Manual)
          下面分析一下cr_alginment的值是在哪確定的,我們在arch/arm/kernel/entry-armv.S找到cr_alignment的定義:

        .globl SYMBOL_NAME(cr_alignment)
        .globl SYMBOL_NAME(cr_no_alignment)
        SYMBOL_NAME(cr_alignment):
        .space 4

        SYMBOL_NAME(cr_no_alignment):

        本文引用地址:http://www.104case.com/article/201611/318689.htm

        .space 4

          分析過head-armv.S文件的朋友都會知道,head-armv.S是非壓縮內核的入口:

        1 .section ".text.init",#alloc,#execinstr
        2 .type stext, #function
        3ENTRY(stext)
        4 mov r12, r0

        6 mov r0, #F_BIT | I_BIT | MODE_SVC @ make sure svc mode
        7 msr cpsr_c, r0 @ and all irqs disabled
        8 bl __lookup_processor_type
        9 teq r10, #0 @ invalid processor?
        10 moveq r0, #p @ yes, error p
        11 beq __error
        12 bl __lookup_architecture_type
        13 teq r7, #0 @ invalid architecture?
        14 moveq r0, #a @ yes, error a
        15 beq __error
        16 bl __create_page_tables
        17 adr lr, __ret @ return address
        18 add pc, r10, #12 @ initialise processor
        19 @ (return control reg)
        20
        21 .type __switch_data, %object
        22__switch_data: .long __mmap_switched
        23 .long SYMBOL_NAME(__bss_start)
        24 .long SYMBOL_NAME(_end)
        25 .long SYMBOL_NAME(processor_id)
        26 .long SYMBOL_NAME(__machine_arch_type)
        27 .long SYMBOL_NAME(cr_alignment)
        28 .long SYMBOL_NAME(init_task_union)+8192
        29
        30 .type __ret, %function
        31__ret: ldr lr, __switch_data
        32 mcr p15, 0, r0, c1, c0
        33 mrc p15, 0, r0, c1, c0, 0 @ read it back.
        34 mov r0, r0
        35 mov r0, r0
        36 mov pc, lr
        這里我們關心的是從17行開始,17行code處將lr放置為__ret標號處的相對地址,以便將來某處返回時跳轉到31行繼續運行18行,對于我所分析的pxa270平臺,它將是跳轉到arch/arm/mm/proc-xscale.S中執行__xscale_setup函數,(在s3c2410平臺中,它跳轉到arch/arm/mm/proc-arm920.S,在
        type __arm920_proc_info,#object
        __arm920_proc_info:
        .long 0x41009200
        .long 0xff00fff0
        .long 0x00000c1e @ mmuflags
        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
        可以知道add pc, r10, #12 的#12意思是跳過3個指令,執行b _arm920_setup
        在arm920_setup設置完協處理器和返回寄存器r0之后,跳回到__ret:(31行)。
        在__xscale_setup中會讀取CP15的control register(c1)的值到r1寄存器,并在r1寄存器中設置相應的標志位(其中包括設置V位=1),但在__xscale_setup中,r1寄存器并不立即寫回到Cp15的control register中,而是在返回后的某個地方,接下來會慢慢分析到。__xscale_setup調用move pc, lr指令返回跳轉到31行。
          31行,在lr寄存器中放置__switch_data中的數據__mmap_switched,在36行程序會跳轉到__mmap_switched處。
          32,33行,把r0寄存器中的值寫回到cp15的control register(c1)中,再讀出來放在r0中。
          
          接下來再來看一下跳轉到__mmap_switched處的代碼:
        40 _mmap_switched:
        41 adr r3, __switch_data + 4
        42 ldmia r3, {r4, r5, r6, r7, r8, sp}@ r2 = compat
        43 @ sp = stack pointer
        44
        45 mov fp, #0 @ Clear BSS (and zero fp)
        46 1: cmp r4, r5
        47 strcc fp, [r4],#4
        48 bcc 1b
        49
        50 str r9, [r6] @ Save processor ID
        51 str r1, [r7] @ Save machine type
        52 bic r2, r0, #2 @ Clear A bit
        53 stmia r8, {r0, r2} @ Save control register values
        54 b SYMBOL_NAME(start_kernel)

        41~42行的結果是:r4=__bss_start,r5=__end,...,r8=cr_alignment,..,這里r8保存的是cr_alignment變量的地址.
          到了53行,由于之前r0保存的是cp15的control register(c1)的值,這里把r0的值寫入r8指向的地址,即cr_alignment=r0.到此為止,我們就看清楚了cr_alignment的賦值過程。
          
          讓我們回到trap_init()函數,經過上面的分析,我們知道vectors_base返回0xffff0000。函數__trap_init由匯編代碼編寫,在arch/arm/kernel/entry-arm.S:
            .align 5
        __stubs_start:
        vector_IRQ:
             ...
        vector_data:
            ....
        vector_prefetch:
             ...
        vector_undefinstr:
             ...
        vector_FIQ: disable_fiq
             subs pc, lr, #4
        vector_addrexcptn:
             b vector_addrexcptn
            ...
        __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 sp!, {r4 - r6, lr} /* 壓棧,保存數據*/
            /* 異常向量表(.LCvectors起始的8個地址)到r0指向的地址(異常向量地址),r0就是__trap_init(base)函數調用時傳遞的參數,不明白的請參考ATPCS*/(傳遞參數順次利用r0,r1,r2,r3)
            adr r1, .LCvectors @ set up the vectors
            ldmia r1, {r1, r2, r3, r4, r5, r6, ip, lr}
             stmia r0, {r1, r2, r3, r4, r5, r6, ip, lr}

        /* 在異常向量地址后的0x200偏移處,放置散轉代碼,即__stubs_start~__stubs_end之間的各個異常處理代碼*/
             add r2, r0, #0x200
             adr r0, __stubs_start @ copy stubs to 0x200
             adr r1, __stubs_end
        1: ldr r3, [r0], #4
             str r3, [r2], #4
             cmp r0, r1
        blt 1b
        LOADREGS(fd, sp!, {r4 - r6, pc}) /*出棧,恢復數據,函數__trap_init返回*/
        __trap_init函數填充后的向量表如下:
        虛擬地址 異常 處理代碼
        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)

           當有異常發生時,處理器會跳轉到對應的0xffff0000起始的向量處取指令,然后,通過b指令散轉到異常處理代碼.因為ARM中b指令是相對跳轉,而且只有+/-32MB的尋址范圍,所以把__stubs_start~__stubs_end之間的異常處理代碼到了0xffff0200起始處.這里可直接用b指令跳轉過去,這樣比使用絕對跳轉(ldr)效率高。



        關鍵詞: ARMLinux外部中

        評論


        技術專區

        關閉
        主站蜘蛛池模板: 洮南市| 凯里市| 凤台县| 高陵县| 苍山县| 大庆市| 夏河县| 珲春市| 宾阳县| 容城县| 冀州市| 无锡市| 宽甸| 盖州市| 新沂市| 巩义市| 东兴市| 安溪县| 龙南县| 明水县| 时尚| 浪卡子县| 兴仁县| 潍坊市| 安泽县| 民勤县| 开阳县| 海伦市| 隆化县| 武鸣县| 麻阳| 禹城市| 皋兰县| 屯留县| 商水县| 山丹县| 司法| 石林| 荥阳市| 内乡县| 英山县|