在线看毛片网站电影-亚洲国产欧美日韩精品一区二区三区,国产欧美乱夫不卡无乱码,国产精品欧美久久久天天影视,精品一区二区三区视频在线观看,亚洲国产精品人成乱码天天看,日韩久久久一区,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中斷機(jī)制之中斷處理

            ARM Linux中斷機(jī)制之中斷處理

            作者: 時(shí)間:2016-11-09 來源:網(wǎng)絡(luò) 收藏
            //現(xiàn)在來看看中斷初始化的另一個(gè)函數(shù)early_trap_init(),該函數(shù)在文件arch/arm/kernel/traps.c中實(shí)現(xiàn)。

            void __init early_trap_init(void)
            {

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

            //CONFIG_VECTORS_BASE在autoconf.h中定義(該文件自動(dòng)成生),值為0xffff0000,
            unsigned long vectors = CONFIG_VECTORS_BASE;
            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;

            /* 異常向量表拷貝到 0x0000_0000(或 0xFFFF_0000) ,
            異常處理程序的 stub 拷貝到 0x0000_0200(或 0xFFFF_0200) */
            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);

            /* 拷貝信號(hào)處理函數(shù) */
            memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes,
            sizeof(sigreturn_codes));

            /* 刷新 Cache,修改異常向量表占據(jù)的頁面的訪問權(quán)限*/

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

            這個(gè)函數(shù)把定義在 arch/arm/kernel/entry-armv.S 中的異常向量表和異常處理程序的 stub 進(jìn)行
            重定位:異常向量表拷貝到 0xFFFF_0000,異常向量處理程序的 stub 拷貝到 0xFFFF_0200。
            然后調(diào)用 modify_domain()修改了異常向量表所占據(jù)的頁面的訪問權(quán)限,這使得用戶態(tài)無法
            訪問該頁,只有核心態(tài)才可以訪問。

            arm處理器發(fā)生異常時(shí)總會(huì)跳轉(zhuǎn)到 0xFFFF_0000(設(shè)為“高端向量配置”時(shí))處的異常向量
            表,因此進(jìn)行這個(gè)重定位工作。

            異常向量表,在文件arch/arm/kernel/entry-armv.S 中

            .equstubs_offset, __vectors_start + 0x200 - __stubs_start

            .globl__vectors_start
            __vectors_start:
            swiSYS_ERROR0
            bvector_und + stubs_offset//復(fù)位異常:
            ldrpc, .LCvswi + stubs_offset//未定義指令異常:
            bvector_pabt + stubs_offset//軟件中斷異常:
            bvector_dabt + stubs_offset//數(shù)據(jù)異常:
            bvector_addrexcptn + stubs_offset//保留:
            bvector_irq + stubs_offset//普通中斷異常:
            bvector_fiq + stubs_offset//快速中斷異常:

            .globl__vectors_end
            __vectors_end:

            當(dāng) ARM 處理器發(fā)生異常(中斷是一種異常)時(shí),會(huì)跳轉(zhuǎn)到異常向量表,在向量表中找到相應(yīng)的異常,并跳轉(zhuǎn)到

            該異常處理程序處執(zhí)行。

            stubs_offset,定義為__vectors_start + 0x200 - __stubs_start。

            在中斷初始化函數(shù)early_trap_init()中向量表被拷到0xFFFF_0000處,異常處理程序段被拷到0xFFFF_0200處。

            比如此時(shí)發(fā)生中斷異常bvector_irq + stubs_offset 將跳轉(zhuǎn)到中斷異常處理程序段去執(zhí)行,由于vector_irq,

            在異常處理程序段__stubs_start到__stubs_end之間此時(shí)跳轉(zhuǎn)的位置將是__vectors_start + 0x200 + vector_irq - __stubs_start處。

            異常處理程序段如下:

            當(dāng) ARM 處理器發(fā)生異常(中斷是一種異常)時(shí),會(huì)跳轉(zhuǎn)到異常向量表,在向量表中找到相應(yīng)的異常,并跳轉(zhuǎn)到

            該異常處理程序處執(zhí)行,這些異常處理程序即是放在以下異常處理程序段中。

            .globl__stubs_start
            __stubs_start:

            //vector_stub是一個(gè)宏,它代表有一段程序放在此處。irq, IRQ_MODE, 4是傳遞給宏vector_stub的參數(shù)。
            vector_stubirq, IRQ_MODE, 4

            //以下是跳轉(zhuǎn)表,在宏vector_stub代表的程序段中要用到該表來查找程序要跳轉(zhuǎn)的位置。

            //如果在進(jìn)入終中斷時(shí)是用戶模式,則調(diào)用__irq_usr例程,如果為系統(tǒng)模式,則調(diào)用__irq_svc,如果是其他模式,則說明出錯(cuò)了,

            //則調(diào)用__irq_invalid。

            .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_stubdabt, ABT_MODE, 8

            .。。。。。。


            vector_stubpabt, ABT_MODE, 4

            。。。。。。


            vector_stubund, UND_MODE

            。。。。。。


            vector_fiq:
            disable_fiq
            subspc, lr, #4

            vector_addrexcptn:
            bvector_addrexcptn
            .align5

            .LCvswi:
            .wordvector_swi

            .globl__stubs_end
            __stubs_end:

            宏vector_stub代表的程序段如下:name, mode, correction存儲(chǔ)傳入的參數(shù)之

            .macrovector_stub, name, mode, correction=0
            .align5

            vector_name:
            .if correction
            sublr, lr, #correction//修正返回地址,也就是中斷處理完之后要執(zhí)行的指令的地址
            .endif

            @
            @ Save r0, lr_ (parent PC) and spsr_
            @ (parent CPSR)
            @

            ///保存返回地址到堆棧,因?yàn)楹芸煲褂胷0寄存器,所以也要保存r0。sp后沒有!所以sp指向的位置并沒有變化。

            stmiasp, {r0, lr}@ save r0, lr

            mrslr, spsr
            strlr, [sp, #8]@ save spsr

            // 向上增長的棧。

            // 此時(shí)的這個(gè)棧是中斷模式下的棧,ARM下中斷模式下和系統(tǒng)模式下的

            // 棧是不同的。雖然ARM提供了七個(gè)模式,但Linux只使用了兩個(gè),一

            // 個(gè)是用戶模式,另一個(gè)為系統(tǒng)模式,所以這個(gè)棧只是一個(gè)臨時(shí)性的棧。

            /*

            在arch/arm/include/asm/ptrace.h中有處理器的七種工作模式的定義

            #define USR_MODE0x00000010
            #define FIQ_MODE0x00000011
            #define IRQ_MODE0x00000012
            #define SVC_MODE0x00000013
            #define ABT_MODE0x00000017
            #define UND_MODE0x0000001b
            #define SYSTEM_MODE0x0000001f

            */
            mrsr0, cpsr
            eorr0, r0, #(mode ^ SVC_MODE)
            msrspsr_cxsf, r0////把spsr設(shè)置為管理模式。//對spsr的所有控制為進(jìn)行寫操作,將r0的值全部注入spsr

            @
            @ the branch table must immediately follow this code
            @
            //andlr, lr, #0x0f// 這條指令之后lr中位spsr的低4位,上面跳轉(zhuǎn)表有16項(xiàng)就是對應(yīng)這16個(gè)狀態(tài)
            //movr0, sp//用r0保存堆棧指針的地址

            //在對這段程序分析時(shí)要記住這段程序是以宏vector_stub的形式放在跳轉(zhuǎn)表前面的。

            //將跳轉(zhuǎn)表中對應(yīng)的地址條目存入lr。因?yàn)樘D(zhuǎn)表中每一個(gè)條目都是4個(gè)字節(jié)long,所以此處左移兩位
            ldrlr, [pc, lr, lsl #2]

            movspc, lr@ branch to handler in SVC mode//程序跳轉(zhuǎn)。
            ENDPROC(vector_name)
            .endm

            在此我們以在用戶空間發(fā)生中斷異常為例,即程序跳轉(zhuǎn)到__irq_usr處。

            .align5
            __irq_usr:
            usr_entry//usr_entry是一個(gè)宏代表一段程序插入此處,宏usr_entry所代表的程序段將在下面分析(1)

            kuser_cmpxchg_check

            #ifdef CONFIG_TRACE_IRQFLAGS
            bltrace_hardirqs_off
            #endif

            //接著看get_thread_info, 它也是個(gè)宏,用來獲取當(dāng)前線程的地址。也將在后續(xù)分析。tsk存放的是線程結(jié)構(gòu)體的地址。

            /*

            線程結(jié)構(gòu)體原型如下在文件include/linux/sched.h中

            struct thread_info {
            struct task_struct*task;/* main task structure */
            unsigned longflags;
            struct exec_domain*exec_domain;/* execution domain */
            intpreempt_count;/* 0 => preemptable, <0 => BUG */
            __u32 cpu; /* should always be 0 on m68k */
            struct restart_block restart_block;
            };

            */
            get_thread_info tsk(2)
            #ifdef CONFIG_PREEMPT

            //TI_PREEMPT在文件archarmkernelasm-offsets.c中定義是線程結(jié)構(gòu)體thread_info 的成員preempt_count在

            //結(jié)構(gòu)體thread_info中的偏移

            /*

            內(nèi)核態(tài)可剝奪內(nèi)核,只有在 preempt_count 為 0 時(shí), schedule() 才會(huì)被調(diào)用,其檢查
            是否需要進(jìn)行進(jìn)程切換,需要的話就切換。

            */
            ldrr8, [tsk, #TI_PREEMPT]//獲取preempt_count
            addr7, r8, #1@ increment it//將該成員加一
            strr7, [tsk, #TI_PREEMPT]//間改變后的值存入preempt_count
            #endif

            irq_handler//調(diào)用中斷操作函數(shù),irq_handler是一個(gè)宏,在后續(xù)描述(3)
            #ifdef CONFIG_PREEMPT
            ldrr0, [tsk, #TI_PREEMPT]
            strr8, [tsk, #TI_PREEMPT]
            teqr0, r7
            strner0, [r0, -r0]
            #endif
            #ifdef CONFIG_TRACE_IRQFLAGS
            bltrace_hardirqs_on
            #endif

            movwhy, #0//why在文件arch/arm/kernel/entry-header.S中定義為r8。:why.reqr8
            bret_to_user//返回到用戶態(tài),該宏在文件 linux/arch/arm/kernel/entry-common.S中定義。(4)
            UNWIND(.fnend)
            ENDPROC(__irq_usr)

            下面分別對上面四處宏進(jìn)行分析。(usr_entry,get_thread_info tsk,irq_handler,ret_to_user)

            (1)

            .macrousr_entry
            UNWIND(.fnstart)
            UNWIND(.cantunwind)@ dont unwind the user space

            //S_FRAME_SIZE在文件archarmkernelasm-offsets.c中定義表示 寄存器結(jié)構(gòu)體pt_regs的大小結(jié)構(gòu)體

            //pt_regs中有 r0~cpsr 18個(gè)寄存器即72個(gè)字節(jié)。
            subsp, sp, #S_FRAME_SIZE//為寄存器pt_regs結(jié)構(gòu)體建立堆??臻g,讓堆棧指針sp 指向r0 。

            //stmib為存儲(chǔ)前加,所以此處留出了用于存儲(chǔ)r0的空間,將r1 - r12存入堆棧。sp后沒加!

            //所以sp指向的堆棧位置沒有變,一直指向用于存儲(chǔ)r0的存儲(chǔ)空間。

            stmibsp, {r1 - r12}

            //將中斷前r0,lr,spsr的值取出存放在r1 - r3中,此時(shí)的r0是作為堆棧的sp在使用的。

            //它的值是指向中斷前r0的值在堆棧中存放的位置。在寄存器結(jié)構(gòu)體pt_regs在堆棧中的位置上面。

            ldmiar0, {r1 - r3}

            //S_PC即是pt_regs中的PC寄存器位置,讓r0指向該位置。雖然S_PC還沒有存入堆棧但它在堆棧中的位置存在
            addr0, sp, #S_PC

            movr4, #-1//在r4中放入一個(gè)無效值。

            strr1, [sp]//r1中存放的是中斷前r0的值,此時(shí)將該值存入堆棧,上面已解釋過在堆棧中流出r0的位置的問題。

            //此時(shí)r2-r4存放的是中斷前的lr, spsr的值和無效之。

            //此時(shí)將這些值存入pt_regs中寄存器在堆棧中對應(yīng)的位置,即此時(shí)將中斷前的lr, spsr的值和無效之

            //存入寄存器結(jié)構(gòu)體pt_regs的ARM_pc,ARM_cpsr,ARM_ORIG_r0中。
            stmiar0, {r2 - r4}
            stmdbr0, {sp, lr}^//stmdb是遞減取值,將ARM_lr,ARM_sp存入lr,sp中。


            alignment_trap r0

            //宏zero_fp在文件arch/arm/kernel/entry-header.S中定義,清零fp。
            zero_fp
            .endm

            上面的提到的struct pt_regs,在include/asm/ptrace.h中定義

            struct pt_regs {
            long uregs[18];
            };

            #define ARM_cpsruregs[16]
            #define ARM_pcuregs[15]
            #define ARM_lruregs[14]
            #define ARM_spuregs[13]
            #define ARM_ipuregs[12]
            #define ARM_fpuregs[11]
            #define ARM_r10uregs[10]
            #define ARM_r9uregs[9]
            #define ARM_r8uregs[8]
            #define ARM_r7uregs[7]
            #define ARM_r6uregs[6]
            #define ARM_r5uregs[5]
            #define ARM_r4uregs[4]
            #define ARM_r3uregs[3]
            #define ARM_r2uregs[2]
            #define ARM_r1uregs[1]
            #define ARM_r0uregs[0]
            #define ARM_ORIG_r0uregs[17]

            (2)

            //宏macroget_thread_info在文件arch/arm/kernel/entry-header.S中定義。用來獲取當(dāng)前線程的地址。

            /*

            include/linux/sched.h中:

            union thread_union {

            struct thread_info thread_info; // 線程屬性

            unsigned long stack[THREAD_SIZE/sizeof(long)]; // 棧

            };

            由它定義的線程是8K字節(jié)對齊的, 并且在這8K的最低地址處存放的就是thread_info對象,即該棧擁有者線程的對象,而get_thread_info就是通過把sp低13位清0(8K邊 界)來獲取當(dāng)前thread_info對象的地址。

            THREAD_SIZE在文件arch/arm/include/asm/thread_info.h中定義:#define THREAD_SIZE8192

            */

            .macroget_thread_info, rd
            movrd, sp, lsr #13
            movrd, rd, lsl #13
            .endm

            (3)

            //宏irq_handler文件arch/arm/kernel/entry-armv.S中定義:

            .macroirq_handler

            //宏get_irqnr_preamble是一個(gè)空操作,在文件 arch/arm/mach-s3c2410/include/mach/entry-macro.S中定義
            get_irqnr_preamble r5, lr

            //宏get_irqnr_and_base通過讀取寄存器INTPND來獲得中斷號(hào)。在該宏中獲取的一些參量將存于這些寄存器中r0, r6, r5, lr。

            //宏get_irqnr_and_base定義在文件 arch/arm/mach-s3c2410/include/mach/entry-macro.S,這個(gè)宏后續(xù)講到。
            1:get_irqnr_and_base r0, r6, r5, lr
            movner1, sp
            @
            @ routine called with r0 = irq number, r1 = struct pt_regs *
            @
            adrnelr, 1b

            /*

            // 通過上面的宏get_irqnr_and_base為調(diào)用asm_do_IRQ準(zhǔn)備了參數(shù)中斷號(hào)。

            于是調(diào)用asm_do_IRQ來處理中斷。函數(shù)asm_do_IRQ()是中斷處理函數(shù)的C語言入口。此函數(shù)將在后續(xù)討論。

            函數(shù)asm_do_IRQ()在文件linux/arch/arm/kernel/irq.c中實(shí)現(xiàn)。

            */
            bneasm_do_IRQ

            #ifdef CONFIG_SMP
            。。。。。。

            #endif

            .endm

            get_irqnr_and_base是平臺(tái)相關(guān)的,這個(gè)宏查詢ISPR(IRQ掛起中斷服務(wù)寄存器,當(dāng)有需要處理的中斷時(shí),這個(gè)寄存器的相應(yīng)位會(huì)置位,任意時(shí)刻,最多一個(gè)位會(huì)置位),計(jì)算出的中斷號(hào)放在irqnr指定的寄存器中;這個(gè)宏在不同的ARM芯片上是不一樣的,這個(gè)宏主要作用在于就是獲得發(fā)生中斷的中斷號(hào),對于s3c2440,代碼在arch/arm/mach-s3c2410/include/entry-macro.S里,該宏處理完后,r0 = 中斷號(hào)。

            .macroget_irqnr_and_base, irqnr, irqstat, base, tmp

            movbase, #S3C24XX_VA_IRQ

            @@ try the interrupt offset register, since it is there

            ldrirqstat, [ base, #INTPND ]
            teqirqstat, #0
            beq1002f
            ldrirqnr, [ base, #INTOFFSET ]
            movtmp, #1
            tstirqstat, tmp, lsl irqnr
            bne1001f

            @@ the number specified is not a valid irq, so try
            @@ and work it out for ourselves

            movirqnr, #0@@ start here

            @@ work out which irq (if any) we got

            movstmp, irqstat, lsl#16
            addeqirqnr, irqnr, #16
            moveqirqstat, irqstat, lsr#16
            tstirqstat, #0xff
            addeqirqnr, irqnr, #8
            moveqirqstat, irqstat, lsr#8
            tstirqstat, #0xf
            addeqirqnr, irqnr, #4
            moveqirqstat, irqstat, lsr#4
            tstirqstat, #0x3
            addeqirqnr, irqnr, #2
            moveqirqstat, irqstat, lsr#2
            tstirqstat, #0x1
            addeqirqnr, irqnr, #1

            @@ we have the value
            1001:
            addsirqnr, irqnr, #IRQ_EINT0
            1002:
            @@ exit here, Z flag unset if IRQ

            .endm

            (4)

            宏ret_to_user在文件arch/arm/kernel/entry-common.S下定義:

            ENTRY(ret_to_user)
            ret_slow_syscall:
            disable_irq//禁止中斷
            ldrr1, [tsk, #TI_FLAGS]//獲取線程結(jié)構(gòu)體thread_union的flags成員
            tstr1, #_TIF_WORK_MASK//判斷task是否被阻塞
            bnework_pending //根據(jù)需要進(jìn)行進(jìn)程的切換,該段代碼在下面講述。
            no_work_pending://不需要進(jìn)程切換
            /* perform architecture specific actions before user return */
            arch_ret_to_user r1, lr

            @ slow_restore_user_regs
            ldrr1, [sp, #S_PSR]@ get calling cpsr
            ldrlr, [sp, #S_PC]!@ get pc
            msrspsr_cxsf, r1@ save in spsr_svc //// spsr里保存好被中斷代碼處的狀態(tài)(cpsp)
            ldmdbsp, {r0 - lr}^//恢復(fù)中斷前寄存器的值恢復(fù)到各個(gè)寄存器。
            movr0, r0
            addsp, sp, #S_FRAME_SIZE - S_PC
            movspc, lr//返回用戶態(tài)
            ENDPROC(ret_to_user)

            在arch/arm/kernel/entry-common.S中

            work_pending:
            tstr1, #_TIF_NEED_RESCHED//判斷是否需要調(diào)度進(jìn)程
            bnework_resched//進(jìn)程調(diào)度
            tstr1, #_TIF_SIGPENDING
            beqno_work_pending//無需調(diào)度,返回
            movr0, sp@ regs
            movr2, why@ syscall
            bldo_notify_resume
            bret_slow_syscall@ Check work again

            work_resched:
            blschedule//調(diào)用進(jìn)程切換函數(shù)。

            這里只講了在用戶模式下的中斷處理,在內(nèi)核模式下的處理方式也大抵相仿,就不再贅言了。

            中斷處理函數(shù)的C語言入口

            asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
            {
            struct pt_regs *old_regs = set_irq_regs(regs);

            irq_enter();//進(jìn)入中斷上下文


            if (irq >= NR_IRQS)
            handle_bad_irq(irq, &bad_irq_desc);
            else
            generic_handle_irq(irq);//根據(jù)中斷號(hào)獲取中斷描述結(jié)構(gòu)體,并調(diào)用其中斷處理函數(shù)。


            irq_finish(irq);//退出中斷上下文

            irq_exit();
            set_irq_regs(old_regs);
            }

            //函數(shù)generic_handle_irq()是函數(shù)generic_handle_irq_desc()的包裝。

            static inline void generic_handle_irq(unsigned int irq)
            {
            generic_handle_irq_desc(irq, irq_to_desc(irq));
            }

            /*

            如果實(shí)現(xiàn)了上層中斷處理函數(shù)desc->handle_irq就調(diào)用它,實(shí)際上在中斷處理函數(shù)s3c24xx_init_irq()中已為每一個(gè)

            中斷線分配了一個(gè)上層中斷處理函數(shù)。

            如果desc->handle_irq為空就調(diào)用通用中斷處理函數(shù)__do_IRQ(irq);,在干函數(shù)中調(diào)用了函數(shù)handle_IRQ_event(),

            在函數(shù)handle_IRQ_event()中執(zhí)行了該條中斷線上的每一個(gè)中斷例程。

            */

            static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
            {
            #ifdef CONFIG_GENERIC_HARDIRQS_NO__DO_IRQ
            desc->handle_irq(irq, desc);
            #else
            if (likely(desc->handle_irq))
            desc->handle_irq(irq, desc);
            else
            __do_IRQ(irq);
            #endif
            }



            評論


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

            關(guān)閉