在线看毛片网站电影-亚洲国产欧美日韩精品一区二区三区,国产欧美乱夫不卡无乱码,国产精品欧美久久久天天影视,精品一区二区三区视频在线观看,亚洲国产精品人成乱码天天看,日韩久久久一区,91精品国产91免费

<menu id="6qfwx"><li id="6qfwx"></li></menu>
    1. <menu id="6qfwx"><dl id="6qfwx"></dl></menu>

      <label id="6qfwx"><ol id="6qfwx"></ol></label><menu id="6qfwx"></menu><object id="6qfwx"><strike id="6qfwx"><noscript id="6qfwx"></noscript></strike></object>
        1. <center id="6qfwx"><dl id="6qfwx"></dl></center>

            新聞中心

            EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > ARM Linux 中斷向量表建立流程

            ARM Linux 中斷向量表建立流程

            作者: 時(shí)間:2016-11-09 來源:網(wǎng)絡(luò) 收藏
            一般編寫arm的裸機(jī)程序的時(shí)候,創(chuàng)建中斷向量表就把它放在0x00000000~0x0000001c中,一般都放在這個(gè)位置上。但是中斷向量表也可以放在0xffff0000~0xffff001c中,知道這是怎么設(shè)置的么?開始看到的時(shí)候真的有點(diǎn)奇怪,因?yàn)樵趯W(xué)習(xí)arm的時(shí)候,根本沒去看arm中的協(xié)處理器CP15中的c1控制寄存器中的v位來控制,我們一般都使用默認(rèn)的值0,則必須將中斷向量表放在0x00000000~0x0000001c中。

            在看Linux內(nèi)核對(duì)arm中的中斷的初始化的時(shí)候,就一直對(duì)0xffff0000的地址有點(diǎn)懷疑,果然在網(wǎng)上發(fā)現(xiàn)這個(gè)地址不是隨便寫的,當(dāng)我看到arm的協(xié)處理器進(jìn)行控制,中斷向量表的地址的時(shí)候,真的是哭笑不得?。?!
            有人肯定會(huì)問?v位是什么時(shí)候設(shè)置的呢?其實(shí)仔細(xì)的朋友就知道在head.S中,在創(chuàng)建完頁表的時(shí)候,如add pc,r10,#PROCINFO_INITFUNC
            別急,r10保存在前面設(shè)置的procinfo的地址,但是很多人就覺得PROCINFO_INITFUNC的宏定義就不知道在哪找了,在include/asm/asm-offset.h中有定義。
            這些搞懂了,首先必須將中斷向量表拷貝到0xffff0000的地址上去,把中斷處理函數(shù)也拷貝到0xffff0200的地址上去,那么在中斷向量表進(jìn)行跳轉(zhuǎn)的時(shí)候,如b vector_irq+stubs_offset,但是stubs_offset的偏移怎么設(shè)置呢?如果用b vector_irq的話,它就會(huì)跳轉(zhuǎn)到原先的中斷處理函數(shù)中去,因?yàn)樗部截惖搅?xffff0200的地址上去,所以將__vector_start-_stubs_start+0x200的話就轉(zhuǎn)移到拷貝后的地址上去執(zhí)行了。
            很多人應(yīng)該會(huì)有點(diǎn)疑問吧,vector_irq好像找不到,別急,細(xì)心點(diǎn),就在宏定義.macro vector_stubs,name,mode,correction中對(duì)各種處理函數(shù)有定義,所以很快就將中斷向量表創(chuàng)建好了。

            本文引用地址:http://www.biyoush.com/article/201611/317933.htm

            Linux Version : 2.6.29

            1. start_kernel-->setup_arch-->early_trap_init

            1:        memcpy((void   *)vectors, __vectors_start, __vectors_end - __vectors_start);
            2:        memcpy((void   *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
            3:        memcpy((void   *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);

            對(duì)于第一行:

            __vectors_start 和 __vectors_end 定義在 arch/arm/kernel/entry-armv.S , 它們之間保存了中斷向量表。

            1:        .globl    __vectors_start
            2:    __vectors_start:
            3:        swi    SYS_ERROR0 
            4:        b    vector_und + stubs_offset
            5:        ldr    pc, .LCvswi + stubs_offset
            6:        b    vector_pabt + stubs_offset
            7:        b    vector_dabt + stubs_offset
            8:        b    vector_addrexcptn + stubs_offset
            9:        b    vector_irq + stubs_offset
            10:        b    vector_fiq + stubs_offset
            11:    
            12:        .globl    __vectors_end
            13:    __vectors_end:

            vectors 的地址為CONFIG_VECTORS_BASE , 在.config中定義為0xffff0000

            所以 第1行就是把中斷向量表拷貝到0xffff0000

            對(duì)于第二行:

            vector_stub是一個(gè)帶參數(shù)的宏,第一個(gè)是name,第二個(gè)是arm excepiton mode,第三個(gè)是為了得到返回地址,lr需要減去的偏移

            1:        .macro    vector_stub, name, mode, correction=0
            2:        .align    5
            3:    
            4:    vector_/name:
            5:        .if   /correction
            6:        sub    lr, lr, #/correction          @得到正確的返回地址
            7:        .endif
            8:    
            9:        @
            10:        @ Save r0, lr_ (parent PC) and spsr_
            11:        @ (parent CPSR)
            12:        @
            13:        stmia    sp, {r0, lr}        @ save r0, lr
            14:        mrs    lr, spsr
            15:        str    lr, [sp, #8]        @ save spsr
            16:    
            17:        @
            18:        @ Prepare for   SVC32 mode.  IRQs remain disabled.
            19:        @ 
            20:        mrs    r0, cpsr
            21:        eor    r0, r0, #(/mode ^ SVC_MODE) @把cpsr內(nèi)容與(mode^SVC_mode)異或,即r0里為SVC_MODE 
            22:        msr    spsr_cxsf, r0  @把r0的值寫入整個(gè)spsr寄存器(cxsf表示要往哪個(gè)字節(jié)寫入)
            23:    
            24:        @
            25:        @ the branch table must immediately follow this   code
            26:        @
            27:        and    lr, lr, #0x0f @lr為spsr_的值,此語句取到進(jìn)入異常前的mode
            28:        mov    r0, sp         @ 
            29:        ldr    lr, [pc, lr, lsl #2] @lr=pc+mode*4,其中pc為緊接著30的指令,即vector_stub后的第一條指令
            30:        movs    pc, lr            @ movs會(huì)把spsr的值賦給cpsr,所以branch to handler in   SVC mode
            31:    ENDPROC(vector_/name)
            32:        .endm

            再來看下vector 跳轉(zhuǎn)表

            1:        .long      __irq_usr            @  0  (USR_26 / USR_32)
            2:        .long      __irq_invalid            @  1  (FIQ_26 / FIQ_32)
            3:        .long      __irq_invalid            @  2  (IRQ_26 / IRQ_32)
            4:        .long      __irq_svc            @  3  (SVC_26 / SVC_32)
            5:        .long      __irq_invalid            @  4
            6:        .long      __irq_invalid            @  5
            7:        .long      __irq_invalid            @  6
            8:        .long      __irq_invalid            @  7
            9:        .long      __irq_invalid            @  8
            10:        .long      __irq_invalid            @  9
            11:        .long      __irq_invalid            @  a
            12:        .long      __irq_invalid            @  b
            13:        .long      __irq_invalid            @  c
            14:        .long      __irq_invalid            @  d
            15:        .long      __irq_invalid            @  e
            16:        .long      __irq_invalid            @  f

            這里只有usr 和svc 有入口,而其他都是invalid ,是因?yàn)閘inux只會(huì)從usr(application) 和svc(kernel)兩種mode跳轉(zhuǎn)到exception來

            __stubs_start 和 __stubs_end 之間的代碼簡化后為:

            1:    __stubs_start:
            2:       vector_irq:    @vector_stub    irq, IRQ_MODE, 4
            3:       vector_dabt: @vector_stub    dabt, ABT_MODE, 8
            4:       vector_pabt:   @vector_stub    pabt, ABT_MODE, 4
            5:       vector_und: @vector_stub    und, UND_MODE
            6:       vector_fiq:
            7:       vector_addrexcptn:
            8:       .LCvswi:
            9:    __stubs_end:

            由此可以知道 __stubs_start 和 __stubs_end 之間定義了各種異常的入口

            我們?cè)賮砜礊槭裁串惓H肟谑?ldquo;b vector_und + stubs_offset”, 同時(shí)為什么stubs_offset 的定義如下

            .equ    stubs_offset, __vectors_start + 0x200 - __stubs_start

            arm 的跳轉(zhuǎn)指令b 是跳轉(zhuǎn)到相對(duì)于PC的一個(gè)偏移地址( offset ),匯編器在編譯時(shí)會(huì)對(duì)label 減去PC 得到offset,同時(shí)vector 拷貝后是如下排列的

            __vectors_start

            B vector_

            __vectors_end

            +0x200

            __stubs_start

            vector_

            __stubs_end

            因此,"b vector_" 的label –PC = offset, 而offset 為 b 指令與vector的offset,即

            vector_-__stubs_start + ( 0x200 – ( PC_old – __vectors_start ) )

            = vector_ + __vectors_start + 0x200 – __stubs_start – PC_old

            所以異常入口為“b vector_und + stubs_offset”, 同時(shí)stubs_offset= __vectors_start + 0x200 – __stubs_start

            我們可以通過objdump反匯編來驗(yàn)證:

            00000060 :
            .globl __stubs_start
            __stubs_start:
            /*
            * Interrupt dispatcher
            */
            vector_stub irq, IRQ_MODE, 4
            60: e24ee004 sub lr, lr, #4 ; 0x4
            64: e88d4001 stm sp, {r0, lr}

            1d4: e1a00000 .word 0xe1a00000
            1d8: e1a00000 .word 0xe1a00000
            1dc: e1a00000 .word 0xe1a00000

            000001e0 :

            /*
            * Undef instr entry dispatcher
            * Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
            */

            __vectors_start:
            swi SYS_ERROR0
            284: ef9f0000 svc 0x009f0000
            b vector_und + stubs_offset
            288: ea0000dd b 604
            ldr pc, .LCvswi + stubs_offset
            28c: e59ff410 ldr pc, [pc, #1040] ; 6a4
            b vector_pabt + stubs_offset
            290: ea0000bb b 584
            b vector_dabt + stubs_offset
            294: ea00009a b 504
            b vector_addrexcptn + stubs_offset
            298: ea0000fa b 688
            b vector_irq + stubs_offset
            29c: ea000078 b 484
            b vector_fiq + stubs_offset
            2a0: ea0000f7 b 684

            0x1e0 – 0x60 + 0x200 – ( 0x288 + 8 ) – 0x284 = 0xdd*4

            ARM Linux外部中斷處理過程

            最近在學(xué)習(xí)arm linux的整套外部中斷的處理過程,在網(wǎng)上匯總了一些資料,整個(gè)過程差不多都了解到了。如果沒有這些資料我真是沒信心從匯編開始讀代碼,感謝 奔騰年代的jimmy.lee和 linux論壇的bx_bird。
            在下面的的注釋中有一些我讀代碼時(shí)遇到的問題,要是大家知道是怎么回事,希望多多回復(fù)。
            =============================================
            一.ARM linux的中斷向量表初始化分析
            ARM linux內(nèi)核啟動(dòng)時(shí),通過start_kernel()->trap_init()的調(diào)用關(guān)系,初始化內(nèi)核的中斷異常向量表.
            /* 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是一個(gè)宏,它的作用是獲取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
              對(duì)于ARMv4以下的版本,這個(gè)地址固定為0;ARMv4及其以上的版本,ARM異常向量表的地址受協(xié)處理器CP15的c1寄存器(control register)中V位(bit[13])的控制,如果V=1,則異常向量表的地址為0x00000000~0x0000001C;如果V=0,則為:0xffff0000~0xffff001C。(詳情請(qǐng)參考ARM Architecture Reference Manual)
              下面分析一下cr_alginment的值是在哪確定的,我們?cè)赼rch/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):
            .space 4
              分析過head-armv.S文件的朋友都會(huì)知道,head-armv.S是非壓縮內(nèi)核的入口:
            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
            這里我們關(guān)心的是從17行開始,17行code處將lr放置為__ret標(biāo)號(hào)處的相對(duì)地址,以便將來某處返回時(shí)跳轉(zhuǎn)到31行繼續(xù)運(yùn)行18行,對(duì)于我所分析的pxa270平臺(tái),它將是跳轉(zhuǎn)到arch/arm/mm/proc-xscale.S中執(zhí)行__xscale_setup函數(shù),(在s3c2410平臺(tái)中,它跳轉(zhuǎn)到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個(gè)指令,執(zhí)行b _arm920_setup
            在arm920_setup設(shè)置完協(xié)處理器和返回寄存器r0之后,跳回到__ret:(31行)。
            在__xscale_setup中會(huì)讀取CP15的control register(c1)的值到r1寄存器,并在r1寄存器中設(shè)置相應(yīng)的標(biāo)志位(其中包括設(shè)置V位=1),但在__xscale_setup中,r1寄存器并不立即寫回到Cp15的control register中,而是在返回后的某個(gè)地方,接下來會(huì)慢慢分析到。__xscale_setup調(diào)用move pc, lr指令返回跳轉(zhuǎn)到31行。
              31行,在lr寄存器中放置__switch_data中的數(shù)據(jù)__mmap_switched,在36行程序會(huì)跳轉(zhuǎn)到__mmap_switched處。
              32,33行,把r0寄存器中的值寫回到cp15的control register(c1)中,再讀出來放在r0中。
              
              接下來再來看一下跳轉(zhuǎn)到__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行的結(jié)果是: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()函數(shù),經(jīng)過上面的分析,我們知道vectors_base返回0xffff0000。函數(shù)__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} /* 壓棧,保存數(shù)據(jù)*/
                /* 復(fù)制異常向量表(.LCvectors起始的8個(gè)地址)到r0指向的地址(異常向量地址),r0就是__trap_init(base)函數(shù)調(diào)用時(shí)傳遞的參數(shù),不明白的請(qǐng)參考ATPCS*/(傳遞參數(shù)順次利用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偏移處,放置散轉(zhuǎn)代碼,即__stubs_start~__stubs_end之間的各個(gè)異常處理代碼*/
                 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}) /*出棧,恢復(fù)數(shù)據(jù),函數(shù)__trap_init返回*/
            __trap_init函數(shù)填充后的向量表如下:
            虛擬地址 異常 處理代碼
            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 數(shù)據(jù)異常 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)
               當(dāng)有異常發(fā)生時(shí),處理器會(huì)跳轉(zhuǎn)到對(duì)應(yīng)的0xffff0000起始的向量處取指令,然后,通過b指令散轉(zhuǎn)到異常處理代碼.因?yàn)锳RM中b指令是相對(duì)跳轉(zhuǎn),而且只有+/-32MB的尋址范圍,所以把__stubs_start~__stubs_end之間的異常處理代碼復(fù)制到了0xffff0200起始處.這里可直接用b指令跳轉(zhuǎn)過去,這樣比使用絕對(duì)跳轉(zhuǎn)(ldr)效率高。
            二.ARM Linux中斷處理過程分析(1)
            在我的上一篇文章(ARM linux的中斷向量表初始化分析)中已經(jīng)分析了ARM Linux中斷向量表是如何建立的,在這篇文章中,我將分析一下Linux內(nèi)核的ARM體系下,中斷處理是如何響應(yīng)的一個(gè)過程。
            在ARM體系架構(gòu)下,定義了7種異常,每一種異常都有自己的入口地址,即異常向量表,當(dāng)異常發(fā)生時(shí),處理器會(huì)自動(dòng)跳轉(zhuǎn)到相應(yīng)的入口處執(zhí)行。對(duì)于ARMv4及其以上的版本,異常向量表的起始位置由協(xié)處理器15(cp15)的控制寄存器(c1)里的V位(bit13)有關(guān),當(dāng)V=0時(shí),異常向量表的起始位置在0x00000000,而當(dāng)V=1時(shí),異常向量表就起始于0xffff0000位置。在上一篇文章中,我們已經(jīng)分析知道異常向量表放置于0xffff0000起始位置,而IRQ中斷處理入口地址為:0xffff0018,所以當(dāng)發(fā)生一IRQ中斷異常時(shí),處理器會(huì)自動(dòng)跳轉(zhuǎn)到0xffff0018這個(gè)虛擬地址上。
            0xffff0018這個(gè)虛擬地址上是一條跳轉(zhuǎn)指令:
            b __real_stubs_start + (vector_IRQ - __stubs_start)
            所以對(duì)于IRQ的處理就是從vector_IRQ標(biāo)號(hào)處開始的。在linux2.4.19內(nèi)核中相應(yīng)代碼如下:
            __stubs_start:
            /*
            * Interrupt dispatcher
            * Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
            */說明其實(shí)linux只用到了arm的svc和usr模式,其他的幾個(gè)模式都沒怎么用。
            1 vector_IRQ: @
            2 @ save mode specific registers
            3 @
            4 ldr r13, .LCsirq
            5 sub lr, lr, #4
            6 str lr, [r13] @ save lr_IRQ
            7 mrs lr, spsr
            8 str lr, [r13, #4] @ save spsr_IRQ
            9 @
            10 @ now branch to the relevent MODE handling routine
            11 @
            12 mrs r13, cpsr
            13 bic r13, r13, #MODE_MASK
            14 orr r13, r13, #I_BIT | MODE_SVC
            15 msr spsr_c, r13 @ switch to SVC_32 mode
            16
            17 and lr, lr, #15
            18 ldr lr, [pc, lr, lsl #2]
            19 movs pc, lr @ Changes mode and branches
            20
            21.LCtab_irq: .word __irq_usr @ 0 (USR_26 / USR_32)
            22 .word __irq_invalid @ 1 (FIQ_26 / FIQ_32)
            23 .word __irq_invalid @ 2 (IRQ_26 / IRQ_32)
            24 .word __irq_svc @ 3 (SVC_26 / SVC_32)
            25 .word __irq_invalid @ 4
            26 .word __irq_invalid @ 5
            27 .word __irq_invalid @ 6
            28 .word __irq_invalid @ 7
            29 .word __irq_invalid @ 8
            30 .word __irq_invalid @ 9
            31 .word __irq_invalid @ a
            32 .word __irq_invalid @ b
            33 .word __irq_invalid @ c
            34 .word __irq_invalid @ d
            35 .word __irq_invalid @ e
            36 .word __irq_invalid @ f
            首先,行4~8是保存進(jìn)入IRQ模式之前的pc指針(在lr_IRQ)和CPSR(在SPSR_IRQ)到.LCsirq所指向的地址中。.LCsirq相關(guān)代碼也是位于entry-armv.S中:
            .LCsirq: .word __temp_irq

            __temp_irq: .word 0 @ saved lr_irq
            .word 0 @ saved spsr_irq
            .word -1 @ old_r0
            在這里補(bǔ)充一下ARM對(duì)于異常的處理過程,可以用下面的一段偽碼來表示:
            r14_<異常模式> = return link
            SPSR_<異常模式> = CPSR
            CPSR[4:0] = 異常模式編碼
            CPSR[5] = 0 ;運(yùn)行于ARM狀態(tài)
            If<異常模式> == Reset or FIQ then{
            ;當(dāng)復(fù)位或響應(yīng)FIQ異常時(shí),禁止新的fiq和irq異常
            CPSR[6] = 1;
            CPSR[7] = 1;
            }else if<異常模式> == IRQ then{
            ;當(dāng)響應(yīng)IRQ異常時(shí),禁止新的IRQ異常
            CPSR[7] = 1;
            }
            PC = 異常向量地址
            所以在運(yùn)行到行4~8之前時(shí),lr為進(jìn)入IRQ之前的pc指針,spsr為進(jìn)入IRQ之前的cpsr指針。
            接著,行12~15更新spsr寄存器為SVR模式,并關(guān)閉IRQ,為從IRQ模式切換到SVR模式做準(zhǔn)備。
            行17,根據(jù)進(jìn)入IRQ模式之前的psr(因?yàn)樵谛?,lr已經(jīng)被置以spsr_irq),獲取之前的處理器模式(psr &0b1111)。
            行18,根據(jù)獲取的進(jìn)入IRQ之前的處理器模式,查找相應(yīng)的跳轉(zhuǎn)入口(__irq_usr 對(duì)應(yīng)于之前是USR模式,__irq_svc對(duì)于之前是SVC模式,對(duì)于其它模式均跳轉(zhuǎn)到__irq_invalid,在linux系統(tǒng)中處理器進(jìn)入IRQ之前只有usr和svc兩種模式,其它模式均不允許開啟IRQ)。此行實(shí)際上是:lr = pc+lr<<2,pc指向當(dāng)前指令地址值加8個(gè)字節(jié)的地址,即pc指向當(dāng)前指令的下兩條指令的地址,所以pc在此時(shí)指向的是.LCtab_irq地址。
            (這里有點(diǎn)疑惑要進(jìn)入__irq_usr,則18行l(wèi)r應(yīng)該為pc+4那么向回推算第7行的mrs lr, spsr中spsr[3:0]應(yīng)該為0b0001;如果要進(jìn)入__irq_svc,則18行l(wèi)r應(yīng)該為pc+16,那么spsr[3:0]應(yīng)該為0b0100;
            而cprs[4:0]=
            10000 User 模式
            10011 SVC 模式
            請(qǐng)達(dá)人指點(diǎn)迷津。。。。)
            行19,跳轉(zhuǎn)到相應(yīng)入口,并且ARM寄存器r13和r14則切換到了SVC模式下的寄存器

            三.ARM Linux中斷處理過程分析(2)
            續(xù)前文,讓我們先分析進(jìn)入IRQ之前的處理器模式為SVC時(shí)的情況,程序會(huì)跳轉(zhuǎn)到__irq_svc繼續(xù)運(yùn)行,其相應(yīng)代碼如下:
            20__irq_svc: sub sp, sp, #S_FRAME_SIZE
            21 stmia sp, {r0 - r12} @ save r0 - r12
            22 ldr r7, .LCirq
            23 add r5, sp, #S_FRAME_SIZE
            24 ldmia r7, {r7 - r9}
            25 add r4, sp, #S_SP
            26 mov r6, lr
            27 stmia r4, {r5, r6, r7, r8, r9} @ save sp_SVC, lr_SVC, pc, cpsr, old_ro
            28 1: get_irqnr_and_base r0, r6, r5, lr
            29 movne r1, sp
            30 @
            31 @ routine called with r0 = irq number, r1 = struct pt_regs *
            32 @
            33 adrsvc ne, lr, 1b
            34 bne asm_do_IRQ
            35 ldr r0, [sp, #S_PSR] @ irqs are already disabled
            36 msr spsr, r0
            37 ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
            行20~27:保存進(jìn)入中斷之前的寄存器,把它們放在堆棧中。其中#S_FRAME_SIZE和#S_SP的定義在arch/arm/kernel/entry-header.S中:
            #ifdef CONFIG_CPU_32
            #define S_FRAME_SIZE 72
            #define S_OLD_R0 68
            #define S_PSR 64
            #else
            #define S_FRAME_SIZE 68
            #define S_OLD_R0 64
            #define S_PSR 60
            #endif
            #define S_PC 60
            #define S_LR 56
            #define S_SP 52
            #define S_IP 48
            #define S_FP 44
            #define S_R10 40
            #define S_R9 36
            #define S_R8 32
            #define S_R7 28
            #define S_R6 24
            #define S_R5 20
            #define S_R4 16
            #define S_R3 12
            #define S_R2 8
            #define S_R1 4
            #define S_R0 0
            #define S_OFF 8
            .LCirq在entry-armv.S中是這樣定義的:
            .LCirq: .word __temp_irq
            這與行4處的.LCsirq定義是一樣的,可見整個(gè)過程利用__temp_irq作為中轉(zhuǎn),把進(jìn)入中斷之前的CPSR和PC(中斷處理結(jié)束后要返回的地址)放入堆棧,以便中斷返回時(shí)直接恢復(fù)。
            行20~27執(zhí)行的結(jié)果是:
            r5-> old_r0
            cpsr
            pc
            lr_svc
            r4-> sp_svc
            r12
            r11

            r1
            sp-> r0
            行28的get_irqnr_and_base,它是一個(gè)宏定義,作用是獲取中斷號(hào)(irq number),它將被保存在r0中。另外,get_irqnr_and_base還會(huì)改變cpsr寄存器中的Z位,如果確實(shí)找到了發(fā)生的中斷號(hào),則Z位被清除,否則Z位被置位。get_irqnr_and_base這個(gè)宏定義的實(shí)現(xiàn)是依賴具體的硬件的,對(duì)于pxa270 cpu,其實(shí)現(xiàn)如下:
            .macro get_irqnr_and_base, irqnr, irqstat, base, tmp
            mov base, #io_p2v(0x40000000) @ IIR Ctl = 0x40d00000
            add base, base, #0x00d00000
            ldr irqstat, [base, #0] @ ICIP
            ldr irqnr, [base, #4] @ ICMR
            ands irqstat, irqstat, irqnr
            beq 1001f /* 沒找到中斷,跳轉(zhuǎn)*/
            rsb irqnr, irqstat, #0
            and irqstat, irqstat, irqnr
            clz irqnr, irqstat
            rsb irqnr, irqnr, #(31 - PXA_IRQ_SKIP)
            #ifdef CONFIG_CPU_BULVERDE
            b 1002f
            #endif
            1001:
            1002:
            .endm
            .macro irq_prio_table
            .endm
            bics irqstat, irqstat, irqnr 對(duì)照intmsk將intpnd中禁止的中斷清0。因?yàn)閕ntpnd在某一時(shí)刻只可以有一位為1,所以有一位被bics清0了,就會(huì)影響標(biāo)志位從而beq跳轉(zhuǎn),return r0=0;從1001:開始所作的事情是循環(huán)查intpnd哪一位置為了1。有點(diǎn)疑惑的是tst 指令:
            tst 類似于 CMP,不產(chǎn)生放置到目的寄存器中的結(jié)果。而是在給出的兩個(gè)操作數(shù)上進(jìn)行操作并把結(jié)果反映到狀態(tài)標(biāo)志上。使用 tst 來檢查是否設(shè)置了特定的位。操作數(shù) 1 是要測試的數(shù)據(jù)字而操作數(shù) 2 是一個(gè)位掩碼。經(jīng)過測試后,如果匹配則設(shè)置 Zero 標(biāo)志,否則清除它。
            那么這里的tst irqstat, #1,當(dāng)zero置1了表示有中斷位,為什么下面是bne 1002f而不是beq?請(qǐng)教請(qǐng)教。。。。。。。)
            asm_do_IRQ是用C語言編碼的函數(shù),它在arch/arm/kernel/irq.c中被定義,其原型為:
            asmlinkage void asm_do_IRQ(int irq, struct pt_regs *regs);
            這里牽扯到一個(gè)問題就是,在匯編中如何調(diào)用C語言的函數(shù),參數(shù)是如何傳遞的?為了讓ARM的匯編代碼可與C代碼一起連接,在編寫ARM匯編時(shí),應(yīng)遵循一套標(biāo)準(zhǔn),這就是ATPCS(The ARM-Thumb Procedure Call Standard)。ATPCS定義{r0~r3}為參數(shù)傳遞和結(jié)果返回寄存器;若參數(shù)超過4個(gè)字型(32bit),則使用堆棧進(jìn)行傳遞;頭4個(gè)參數(shù)依次存于r0...r3,大于4個(gè)的后續(xù)字型參數(shù)通過棧傳送。關(guān)于棧的使用,是使用滿遞減的堆棧標(biāo)準(zhǔn),也就是棧是從高地址向低地址方向增長的(遞減堆棧),棧指針寄存器指向的數(shù)據(jù)是最后壓入堆棧內(nèi)的有效數(shù)據(jù)(滿堆棧)。
            所以在跳轉(zhuǎn)到asm_do_IRQ函數(shù)之前,r0就必須設(shè)置為中斷號(hào)(行28get_irqnr_and_base把中斷號(hào)放置于r0),r1就必須是指向pt_regs這樣結(jié)構(gòu)(定義于include/asm-arm/proc-armv/ptrace.h)的指針,而行29把sp指針賦予r1,就完成了這樣的一個(gè)調(diào)用準(zhǔn)備。
            行35~37:恢復(fù)寄存器,返回到發(fā)生中斷之前的代碼中繼續(xù)執(zhí)行。
            這就是整個(gè)ARM linux中斷處理的過程。以后有時(shí)間,再繼續(xù)展開asm_do_IRQ繼續(xù)分析。對(duì)于進(jìn)入中斷前處理器模式是USR的中斷處理過程(__irq_usr),這里就不再做分析,這與__irq_svc基本相同
            asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
            {
            struct irqdesc * desc;
            struct irqaction * action;
            int cpu;
            irq = fixup_irq(irq);// 查找子中斷號(hào),如無子中斷return 原irq
            /*
            * Some hardware gives randomly wrong interrupts. Rather
            * than crashing, do something sensible.
            */
            if (irq >= NR_IRQS)
            goto bad_irq;
            desc = irq_desc + irq;
            spin_lock(&irq_controller_lock);
            desc->mask_ack(irq);
            /*----------------------------------
            void __init init_IRQ(void)
            {
            extern void init_dma(void);
            int irq;
            for (irq = 0; irq < NR_IRQS; irq++) {
            irq_desc[irq].probe_ok = 0;
            irq_desc[irq].valid = 0;
            irq_desc[irq].noautoenable = 0;
            irq_desc[irq].mask_ack = dummy_mask_unmask_irq;
            irq_desc[irq].mask = dummy_mask_unmask_irq;
            irq_desc[irq].unmask = dummy_mask_unmask_irq;
            }
            init_arch_irq();
            init_dma();
            }
            init_arch_irq(); init_dma();最后被指向/mach-s3c2410中的s3c2410_init_irq(void)和s3c2410_init_dma(void), desc->mask_ack(irq);將在那里被填充。
            --------------------------------*/
            spin_unlock(&irq_controller_lock);
            cpu = smp_processor_id(); //#define smp_processor_id() 0
            irq_enter(cpu, irq);
            kstat.irqs[cpu][irq]++;
            desc->triggered = 1;
            /* Return with this interrupt masked if no action */
            action = desc->action;
            /* 這個(gè)結(jié)構(gòu)由driver通過request_irq()掛入,包括了具體的中斷處理程序入口和flags.一個(gè)中斷的irq_desc下面可能會(huì)掛幾個(gè)action(一個(gè)action隊(duì)列)來實(shí)現(xiàn)中斷的復(fù)用。也就是說幾個(gè)driver可以公用一個(gè)中斷號(hào)。*/
            if (action) {
            int status = 0;
            if (desc->nomask) {
            spin_lock(&irq_controller_lock);
            desc->unmask(irq);
            spin_unlock(&irq_controller_lock);
            }
            if (!(action->flags & SA_INTERRUPT))
            /* SA_INTERRUPT Disable local interrupts while processing
            SA_SHIRQ is shared
            這個(gè)flag可以一直追到request irq的action->flags = irq_flags(傳遞參數(shù));
            */
            __sti();//清除cpsr的I_bit,開中斷。
            /*如果在上面的nomask處判斷后,沒有執(zhí)行unmask動(dòng)作,那么這里的__sti只是允許不同中斷通道(即icip上不同的位)上的嵌套*/
            do {
            status |= action->flags;
            action->handler(irq, action->dev_id, regs);
            action = action->next;
            } while (action);
            /*值得注意的是:整個(gè)action隊(duì)列都會(huì)被調(diào)用,所以在driver里要判定是否是屬于自己的中斷*/
            if (status & SA_SAMPLE_RANDOM)
            add_interrupt_randomness(irq);
            __cli();
            if (!desc->nomask && desc->enabled) {
            spin_lock(&irq_controller_lock);
            desc->unmask(irq);
            spin_unlock(&irq_controller_lock);
            }
            }
            unsigned int fixup_irq(int irq) {
            unsigned int ret;
            unsigned long sub_mask, ext_mask;
            if (irq == OS_TIMER)
            return irq;
            switch (irq) {
            case IRQ_UART0:
            sub_mask = SUBSRCPND & ~INTSUBMSK;
            ret = get_subIRQ(sub_mask, 0, 2, irq);
            break;
            case IRQ_UART1:
            sub_mask = SUBSRCPND & ~INTSUBMSK;
            ret = get_subIRQ(sub_mask, 3, 5, irq);
            break;
            case IRQ_UART2:
            sub_mask = SUBSRCPND & ~INTSUBMSK;
            ret = get_subIRQ(sub_mask, 6, 8, irq);
            break;
            case IRQ_ADCTC:
            sub_mask = SUBSRCPND & ~INTSUBMSK;
            ret = get_subIRQ(sub_mask, 9, 10, irq);
            break;
            case IRQ_EINT4_7:
            ext_mask = EINTPEND & ~EINTMASK;
            ret = get_extIRQ(ext_mask, 4, 7, irq);
            break;
            case IRQ_EINT8_23:
            ext_mask = EINTPEND & ~EINTMASK;
            ret = get_extIRQ(ext_mask, 8, 23, irq);
            break;
            default:
            ret = irq;
            }
            這個(gè)函數(shù)一看就知道是找子中斷號(hào)的,
            inline unsigned int get_subIRQ(int irq, int begin, int end, int fail_irq) {
            int i;
            for(i=begin; i <= end; i++) {
            if (irq & (1 << i))
            return (EXT_IRQ_OFFSET + i);
            }
            return fail_irq;
            }
            inline unsigned int get_extIRQ(int irq, int begin, int end, int fail_irq) {
            int i;
            for(i=begin; i <= end; i++) {
            if (irq & (1 << i))
            return (NORMAL_IRQ_OFFSET - 4 + i);
            }
            return fail_irq;
            }
            #define NORMAL_IRQ_OFFSET 32
            #define EXT_IRQ_OFFSET (20 +NORMAL_IRQ_OFFSET)
            =========================================
            申請(qǐng)中斷:
            int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
            unsigned long irq_flags, const char * devname, void *dev_id)
            {
            unsigned long retval;
            struct irqaction *action;
            if (irq >= NR_IRQS || !irq_desc[irq].valid || !handler ||
            (irq_flags & SA_SHIRQ && !dev_id))
            return -EINVAL;
            action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);
            if (!action)
            return -ENOMEM;
            action->handler = handler;
            action->flags = irq_flags;
            action->mask = 0;
            action->name = devname;
            action->next = NULL;
            action->dev_id = dev_id;
            retval = setup_arm_irq(irq, action); /* 把這個(gè)action掛到對(duì)應(yīng)irq的action鏈表中*/
            if (retval)
            kfree(action);
            return retval;
            }
            int setup_arm_irq(int irq, struct irqaction * new)
            {
            int shared = 0;
            struct irqaction *old, **p; /*這里的**p 用的太妙了*/
            unsigned long flags;
            struct irqdesc *desc;
            /*
            * Some drivers like serial.c use request_irq() heavily,
            * so we have to be careful not to interfere with a
            * running system.
            */
            if (new->flags & SA_SAMPLE_RANDOM) {
            /*
            * This function might sleep, we want to call it first,
            * outside of the atomic block.
            * Yes, this might clear the entropy pool if the wrong
            * driver is attempted to be loaded, without actually
            * installing a new handler, but is this really a problem,
            * only the sysadmin is able to do this.
            */
            rand_initialize_irq(irq); /*這個(gè)函數(shù)的作用是利用中斷的隨機(jī)性來產(chǎn)生隨機(jī)數(shù)列*/
            }
            /*
            * The following block of code has to be executed atomically
            */
            desc = irq_desc + irq;
            spin_lock_irqsave(&irq_controller_lock, flags);
            p = &desc->action;
            if ((old = *p) != NULL) {
            注意/* Cant share interrupts unless both agree to */
            if (!(old->flags & new->flags & SA_SHIRQ)) {
            spin_unlock_irqrestore(&irq_controller_lock, flags);
            return -EBUSY;
            }
            /* add new interrupt at end of irq queue */
            do {
            p = &old->next;
            old = *p;
            } while (old);/*當(dāng)沒有下一個(gè)irqaction鏈表元素時(shí),next就位null*/
            shared = 1;
            }
            *p = new;
            if (!shared) {
            desc->nomask = (new->flags & SA_IRQNOMASK) ? 1 : 0;
            desc->probing = 0;
            if (!desc->noautoenable) {
            desc->enabled = 1;
            desc->unmask(irq);
            }
            }
            spin_unlock_irqrestore(&irq_controller_lock, flags);
            return 0;
            }
            四.ARM Linux中斷處理過程分析(3)
            在之前的文章中,我分析了進(jìn)入IRQ之前處理器模式為SVC的情況,在本篇文章中,將要討論的是進(jìn)入IRQ之前處理器模式為USR的情形。
            843 __irq_usr: sub sp, sp, #S_FRAME_SIZE
            844 stmia sp, {r0 - r12} @ save r0 - r12
            845 ldr r4, .LCirq
            846 add r8, sp, #S_PC
            847 ldmia r4, {r5 - r7} @ get saved PC, SPSR
            848 stmia r8, {r5 - r7} @ save pc, psr, old_r0
            849 stmdb r8, {sp, lr}^
            850 alignment_trap r4, r7, __temp_irq
            851 zero_fp
            852 1: get_irqnr_and_base r0, r6, r5, lr
            853 movne r1, sp
            854 adrsvc ne, lr, 1b
            855 @
            856 @ routine called with r0 = irq number, r1 = struct pt_regs *
            857 @
            858 bne asm_do_IRQ
            859 mov why, #0
            860 get_current_task tsk
            861 b ret_to_user
            __irq_usr關(guān)于中斷處理的過程大體與__irq_svc是一樣的,這里我們重點(diǎn)要分析中斷處理返回時(shí)的不同。
            研讀過linux內(nèi)核進(jìn)程調(diào)度的朋友都知道,進(jìn)程的調(diào)度可以自愿的方式隨時(shí)進(jìn)行(內(nèi)核里:schedule、schedule_timeout;用戶空間:pause、nanosleep),還可以非自愿的發(fā)生,即強(qiáng)制地發(fā)生在每次系統(tǒng)調(diào)用返回的前夕,以及每次從中斷或異常處理返回到用戶空間的前夕(只有在用戶空間發(fā)生的中斷或異常才會(huì)引起調(diào)度)??蓞㈤喢虏俚摹禠inux內(nèi)核源代碼情景分析》上冊(cè)的第4章關(guān)于進(jìn)程調(diào)度的相關(guān)地方。
            那我們就來看一下,__irq_usr在返回到usr模式(用戶空間)前夕是如何強(qiáng)制進(jìn)行進(jìn)程調(diào)度的。
            Line860,這是中斷處理返回后,獲取當(dāng)前進(jìn)程的task_struct指針,get_current_task是一個(gè)宏,它定義于arch/arm/kernel/entry-header.S中:
            .macro get_current_task, rd
            mov rd, sp, lsr #13
            mov rd, rd, lsl #13
            .endm
            該宏是先將sp的值右移13位,再左移13位,把結(jié)果返回給參數(shù),其實(shí)也就是只保留sp值的高19位,這代表著把堆棧指針的地址round到8K地址邊界上,這樣它認(rèn)為就得到了當(dāng)前進(jìn)程的task_struct數(shù)據(jù)結(jié)構(gòu)了。它是因?yàn)閮?nèi)核在為每個(gè)進(jìn)程分配一個(gè)task_struct結(jié)構(gòu)時(shí),實(shí)際上是分配兩個(gè)連續(xù)的物理頁面的(共8K),這兩個(gè)頁面的底部是用作進(jìn)程的task_struct結(jié)構(gòu),而在結(jié)構(gòu)的上面就用作進(jìn)程的系統(tǒng)空間堆棧;數(shù)據(jù)結(jié)構(gòu)task_struct的大小約為1K,進(jìn)程系統(tǒng)空間堆棧大小就約為7K。當(dāng)進(jìn)程在系統(tǒng)空間運(yùn)行時(shí),常常需要訪問當(dāng)前進(jìn)程自身的task_struct數(shù)據(jù)結(jié)構(gòu),為此內(nèi)核中定義了一個(gè)宏操作current,提供指向當(dāng)前進(jìn)程task_struct結(jié)構(gòu)的指針,它的實(shí)現(xiàn)實(shí)際上也與這里的get_current_task宏是差不多的。
            /* include/asm-arm/current.h */
            static inline struct task_struct *get_current(void)
            {
            register unsigned long sp asm ("sp");
            return (struct task_struct *)(sp & ~0x1fff);
            }
            #define current (get_current())
            再回到lin860,get_current_task的參數(shù)是tsk,它實(shí)際上是r9寄存器,它也是定義于arch/arm/kernel/entry-header.S中的:
            tsk .req r9 @ current task
            這樣r9寄存器就保存了當(dāng)前進(jìn)程的task_struct結(jié)構(gòu)的指針了。
            Line861,程序跳轉(zhuǎn)到ret_to_user,以完成從中斷處理到返回用戶空間的過程,前面提到的進(jìn)程重新調(diào)度將在那里得以體現(xiàn)。ret_to_user定義于arch/arm/entry-common.S中:
            55 reschedule:
            56 bl SYMBOL_NAME(schedule)
            57 ret_disable_irq:
            58 disable_irq r1 @ ensure IRQs are disabled
            59 ENTRY(ret_to_user)
            60 ret_slow_syscall:
            61 ldr r1, [tsk, #TSK_NEED_RESCHED]
            62 ldr r2, [tsk, #TSK_SIGPENDING]
            63 teq r1, #0 @ need_resched => schedule()
            64 bne reschedule
            65 1: teq r2, #0 @ sigpending => do_signal()
            66 bne __do_signal
            67 restore:
            68 restore_user_regs
            69
            70 __do_signal:
            71 enable_irq r1
            72 mov r0, #0 @ NULL oldset
            73 mov r1, sp @ regs
            74 mov r2, why @ syscall
            75 bl SYMBOL_NAME(do_signal) @ note the bl above sets lr
            76 disable_irq r1 @ ensure IRQs are disabled
            77 b restore
            Line61,TSK_NEED_RESCHED值為20,它是task_struct結(jié)構(gòu)中其成員變量need_resched相對(duì)于結(jié)構(gòu)首地址的偏移量,所以此時(shí)r1的值就是當(dāng)前進(jìn)程task_struct結(jié)構(gòu)里need_resched變量的值。同理在line62,r2存儲(chǔ)就是task_struct->sigpenging的值。
            從line63~64可見,只有在當(dāng)前進(jìn)程的task_struct結(jié)構(gòu)中的need_resched字段為非0時(shí)才會(huì)轉(zhuǎn)到reschedule處去調(diào)用schedule,那么,誰來設(shè)置這個(gè)字段呢?當(dāng)然是內(nèi)核,從用戶空間是訪問不到進(jìn)程的task_struct結(jié)構(gòu)的,那么,內(nèi)核又是在什么情況下設(shè)置這個(gè)字段的呢?除當(dāng)前進(jìn)程通過系統(tǒng)調(diào)用自愿讓出運(yùn)行以及在系統(tǒng)調(diào)用中因某種原因受阻以外,主要就是當(dāng)因某種原因喚醒一個(gè)進(jìn)程的時(shí)候,以及在時(shí)鐘中斷服務(wù)程序發(fā)現(xiàn)當(dāng)前進(jìn)程已經(jīng)連續(xù)運(yùn)行太久的時(shí)候。(此段摘抄于Linux內(nèi)核源代碼情景分析》)
            Line65~66,如果當(dāng)前進(jìn)程的task_struct結(jié)構(gòu)中的sigpedding字段為非0時(shí)才會(huì)轉(zhuǎn)到__do_signal處去調(diào)用do_signal處理信號(hào)。
            Line68, restore_user_regs,它是一個(gè)宏定義于arch/arm/kernel/head-header.S中:
            102 /*
            103 * Must be called with IRQs already disabled.
            104 */
            105 .macro restore_user_regs
            106 ldr r1, [sp, #S_PSR] @ Get calling cpsr
            107 ldr lr, [sp, #S_PC]! @ Get PC
            108 msr spsr, r1 @ save in spsr_svc
            109 ldmdb sp, {r0 - lr}^ @ Get calling r0 - lr
            110 mov r0, r0
            111 add sp, sp, #S_FRAME_SIZE - S_PC
            112 movs pc, lr @ return & move spsr_svc into cpsr
            113 .endm
            17 and lr, lr, #15
            18 ldr lr, [pc, lr, lsl #2]
            19 movs pc, lr @ Changes mode and branches
            20
            21.LCtab_irq: .word __irq_usr @ 0 (USR_26 / USR_32)
            22 .word __irq_invalid @ 1 (FIQ_26 / FIQ_32)
            23 .word __irq_invalid @ 2 (IRQ_26 / IRQ_32)
            24 .word __irq_svc @ 3 (SVC_26 / SVC_32)
            這里有點(diǎn)疑惑要進(jìn)入__irq_usr,則18行l(wèi)r應(yīng)該為pc+4那么向回推算第7行的mrs lr, spsr中spsr[3:0]應(yīng)該為0b0001;如果要進(jìn)入__irq_svc,則18行l(wèi)r應(yīng)該為pc+16,那么spsr[3:0]應(yīng)該為0b0100;
            而cprs[4:0]=
            10000 User 模式
            10011 SVC 模式
            請(qǐng)達(dá)人指點(diǎn)迷津。。。。)
            行19,跳轉(zhuǎn)到相應(yīng)入口,并且ARM寄存器r13和r14則切換到了SVC模式下的寄存器
            這里第18行中的pc值正好是21行的.LCtab_irq,如果是在用戶空間,User模式10000,邏輯左移兩位為0x0=0b0000,即pc+0x0,恰好到了.word __irq_usr ,如果是在內(nèi)核空間,svc模式10011,移位后為0xc=0b1100,及pc+0xc,正好到了.word __irq_svc,一點(diǎn)都沒錯(cuò)(當(dāng)然不可能錯(cuò),系統(tǒng)不是跑得好好的嗎)
            注意,pc值是當(dāng)前指令地址+8
            關(guān)于get_irqnr_and_base宏中:
            bics irqstat, irqstat, irqnr 對(duì)照intmsk將intpnd中禁止的中斷清0。因?yàn)閕ntpnd在某一時(shí)刻只可以有一位為1,所以有一位被bics清0了,就會(huì)影響標(biāo)志位從而beq跳轉(zhuǎn),return r0=0;從1001:開始所作的事情是循環(huán)查intpnd哪一位置為了1。有點(diǎn)疑惑的是tst 指令:
            tst 類似于 CMP,不產(chǎn)生放置到目的寄存器中的結(jié)果。而是在給出的兩個(gè)操作數(shù)上進(jìn)行操作并把結(jié)果反映到狀態(tài)標(biāo)志上。使用 tst 來檢查是否設(shè)置了特定的位。操作數(shù) 1 是要測試的數(shù)據(jù)字而操作數(shù) 2 是一個(gè)位掩碼。經(jīng)過測試后,如果匹配則設(shè)置 Zero 標(biāo)志,否則清除它。
            那么這里的tst irqstat, #1,當(dāng)zero置1了表示有中斷位,為什么下面是bne 1002f而不是beq?請(qǐng)教請(qǐng)教。。。。。。。)
            沒找到你看的內(nèi)核版本中該宏的詳細(xì)定義,我在我的2.6.12中pxa體系中的此宏中沒找到tst指令,但想你的問題估計(jì)還是對(duì)tst的誤解
            pc值是當(dāng)前指令地址+8
            是因?yàn)閍rmv5是三級(jí)流水線么?
            pxa的宏里面好像是沒用tst,這里我引申到s3c2410的宏里面。
            tst的定義我翻的是網(wǎng)上搜的arm指令集,里面是這么說的:
            TST : 測試位
            (Test bits)
            TST{條件}{P} ,
            Status = op_1 AND op_2
            TST 類似于 CMP,不產(chǎn)生放置到目的寄存器中的結(jié)果。而是在給出的兩個(gè)操作數(shù)上進(jìn)行操作并把結(jié)果反映到狀態(tài)標(biāo)志上。使用 TST 來檢查是否設(shè)置了特定的位。操作數(shù) 1 是要測試的數(shù)據(jù)字而操作數(shù) 2 是一個(gè)位掩碼。經(jīng)過測試后,如果匹配則設(shè)置 Zero 標(biāo)志,否則清除它。象 CMP 那樣,你不需要指定 S 后綴。
            TST R0, #%1 ; 測試在 R0 中是否設(shè)置了位 0。
            我覺得在這里是有點(diǎn)轉(zhuǎn)不過彎來了,,,
            ARM linux的中斷向量表初始化分析
            Author: jimmy.li
            Time: 2007-06-09

              本文分析基于linux2.4.19 source,pxa 270 cpu.

              ARM linux內(nèi)核啟動(dòng)時(shí),通過start_kernel()->trap_init()的調(diào)用關(guān)系,初始化內(nèi)核的中斷異常向量表.

            /* 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是一個(gè)宏,它的作用是獲取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

              對(duì)于ARMv4以下的版本,這個(gè)地址固定為0;ARMv4及其以上的版本,ARM異常向量表的地址受協(xié)處理器CP15的c1寄存器(control register)中V位(bit[13])的控制,如果V=1,則異常向量表的地址為0x00000000~0x0000001C;如果V=0,則為:0xffff0000~0xffff001C。(詳情請(qǐng)參考ARM Architecture Reference Manual)
              下面分析一下cr_alginment的值是在哪確定的,我們?cè)赼rch/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):
            .space 4
              分析過head-armv.S文件的朋友都會(huì)知道,head-armv.S是非壓縮內(nèi)核的入口:

            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

              這里我們關(guān)心的是從17行開始,17行code處將lr放置為__ret標(biāo)號(hào)處的相對(duì)地址,以便將來某處返回時(shí)跳轉(zhuǎn)到31行繼續(xù)運(yùn)行;
              18行,對(duì)于我所分析的pxa270平臺(tái),它將是跳轉(zhuǎn)到arch/arm/mm/proc-xscale.S中執(zhí)行__xscale_setup函數(shù),在__xscale_setup中會(huì)讀取CP15的control register(c1)的值到r1寄存器,并在r1寄存器中設(shè)置相應(yīng)的標(biāo)志位(其中包括設(shè)置V位=1),但在__xscale_setup中,r1寄存器并不立即寫回到Cp15的control register中,而是在返回后的某個(gè)地方,接下來會(huì)慢慢分析到。__xscale_setup調(diào)用move pc, lr指令返回跳轉(zhuǎn)到31行。
              31行,在lr寄存器中放置__switch_data中的數(shù)據(jù)__mmap_switched,在36行程序會(huì)跳轉(zhuǎn)到__mmap_switched處。
              32,33行,把r0寄存器中的值寫回到cp15的control register(c1)中,再讀出來放在r0中。
              
              接下來再來看一下跳轉(zhuǎn)到__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行的結(jié)果是: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()函數(shù),經(jīng)過上面的分析,我們知道vectors_base返回0xffff0000。函數(shù)__trap_init由匯編代碼編寫,在arch/arm/kernel/entry-arm.S:
                .align5
            __stubs_start:
            vector_IRQ:
                ...
            vector_data:
                ....
            vector_prefetch:
                ...
            vector_undefinstr:
                ...
            vector_FIQ:disable_fiq
                subspc, lr, #4
            vector_addrexcptn:
                bvector_addrexcptn
                ...
            __stubs_end:
                .equ__real_stubs_start, .LCvectors + 0x200

            .LCvectors:swiSYS_ERROR0
                b__real_stubs_start + (vector_undefinstr - __stubs_start)
                ldrpc, __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)
                stmfdsp!, {r4 - r6, lr}/* 壓棧,保存數(shù)據(jù)*/

                /* 復(fù)制異常向量表(.LCvectors起始的8個(gè)地址)到r0指向的地址(異常向量地址),r0就是__trap_init(base)函數(shù)調(diào)用時(shí)傳遞的參數(shù),不明白的請(qǐng)參考ATPCS*/
                adrr1, .LCvectors@ set up the vectors
                ldmiar1, {r1, r2, r3, r4, r5, r6, ip, lr}
                stmiar0, {r1, r2, r3, r4, r5, r6, ip, lr}

                /* 在異常向量地址后的0x200偏移處,放置散轉(zhuǎn)代碼,即__stubs_start~__stubs_end之間的各個(gè)異常處理代碼*/
                addr2, r0, #0x200
                adrr0, __stubs_start@ copy stubs to 0x200
                adrr1, __stubs_end
            1: ldrr3, [r0], #4
                strr3, [r2], #4
                cmpr0, r1
            blt1b
            LOADREGS(fd, sp!, {r4 - r6, pc})/*出棧,恢復(fù)數(shù)據(jù),函數(shù)__trap_init返回*/

            __trap_init函數(shù)填充后的向量表如下:
            虛擬地址異常 處理代碼
            0xffff0000reset swi SYS_ERROR0
            0xffff0004 undefined b__real_stubs_start + (vector_undefinstr - __stubs_start)
            0xffff0008 軟件中斷 ldrpc, __real_stubs_start + (.LCvswi - __stubs_start)
            0xffff000c 取指令異常b__real_stubs_start + (vector_prefetch - __stubs_start)
            0xffff0010 數(shù)據(jù)異常 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)

               當(dāng)有異常發(fā)生時(shí),處理器會(huì)跳轉(zhuǎn)到對(duì)應(yīng)的0xffff0000起始的向量處取指令,然后,通過b指令散轉(zhuǎn)到異常處理代碼.因?yàn)锳RM中b指令是相對(duì)跳轉(zhuǎn),而且只有+/-32MB的尋址范圍,所以把__stubs_start~__stubs_end之間的異常處理代碼復(fù)制到了0xffff0200起始處.這里可直接用b指令跳轉(zhuǎn)過去,這樣比使用絕對(duì)跳轉(zhuǎn)(ldr)效率高。

            -------------------------參考資料--------------------
            1, 劉淼,嵌入式系統(tǒng)接口設(shè)計(jì)與Linux驅(qū)動(dòng)程序開發(fā),北京航天航空大學(xué)出版社,2006.
            2, ARM Architecture Reference Manual, ARM limited,2000.
            http://jimmy-lee.blog.hexun.com/cate.aspx?cateid=7311&cate=ARM%26Linux




            關(guān)鍵詞: ARMLinux中斷向量

            評(píng)論


            技術(shù)專區(qū)

            關(guān)閉