ARM體系結(jié)構(gòu)中,把復(fù)位、中斷、快速中斷等都看作‘ARM-Linux異?!?,當(dāng)這些‘異常’發(fā)生時(shí),CPU會(huì)到固定地址處去找指令,他們對(duì)應(yīng)的地址如下: 地址 異常類型 進(jìn)入時(shí)的工作模式 0x00000000 Reset Supervisor 0x00000004 Und Undefined 0x00000008 Soft interupt Supervisor 0x0000000c Abort(prefetch) Abort 0x00000010 Abort(data) Abort 0x00000014 Reserved Reserved 0x00000018 IRQ IRQ 0x0000001c FIQ FIQ 首先要明確的一點(diǎn)就是,無論內(nèi)存地址空間是如何ARM-Linux映射的,以上這些地址都不會(huì)變,比如當(dāng)有快速中斷發(fā)生時(shí),ARM將鐵定到0X0000001C這個(gè)地址處取指令。這也是BOOTLOADER把操作系統(tǒng)引導(dǎo)以后,內(nèi)存必須重映射的原因!否則操作系統(tǒng)不能真正接管整套系統(tǒng)! LINUX啟動(dòng)以后要初始化這些區(qū)域,初始化代碼在main.c中ARM-Linux的start_kernel()中,具體是調(diào)用函數(shù)trap_ini()來實(shí)現(xiàn)的。如下面所示(具體可參照entry-armv.S): .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 ARM-Linux sp!, {r4 - r6, lr} 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}ARM-Linux add r2, r0, #0x200 adr r0, __stubs_start @ copy stubs to 0x200 adr r1, __stubs_end 1: ldr r3, [r0], #4ARM-Linux str r3, [r2], #4 cmp r0, r1 blt 1b LOADREGS(fd, sp!, {r4 - r6, pc}) 以上可以看出這個(gè)函數(shù)初始化了中斷向量,實(shí)際ARM-Linux上把相應(yīng)的跳轉(zhuǎn)指令拷貝到了對(duì)應(yīng)的地址。 當(dāng)發(fā)生中斷時(shí),不管是從ARM-Linux用戶模式還是管理模式調(diào)用的,最終都要調(diào)用do_IRQ(): __irq_usr: sub sp, sp, #S_FRAME_SIZE stmia sp, {r0 - r12} @ save r0 - r12 ldr r4, .LCirq add r8, sp, #S_PC ldmia r4, {r5 - r7} @ get saved PC, SPSR stmia r8, {r5 - r7} ARM-Linux @ save pc, psr, old_r0 stmdb r8, {sp, lr}^ alignment_trap r4, r7, __temp_irq zero_fp 1: get_irqnr_and_base r0, r6, r5, lr movne r1, sp adrsvc ne, lr, 1b @ @ routine called with r0 = irq number, r1 = struct pt_regs * @ bne do_IRQ @ 調(diào)用do_IRQ來實(shí)現(xiàn)具體的中斷ARM-Linux處理 mov why, #0 get_current_task tsk b ret_to_user 對(duì)于以上代碼,在很多文章中都有過分析,這里不再贅述。 ARM-Linux Linux每個(gè)中斷通過一個(gè)結(jié)構(gòu)irqdesc來描述,各中斷的信息都在這個(gè)結(jié)構(gòu)中得以ARM-Linux體現(xiàn): struct irqdesc { unsigned int nomask : 1; /* IRQ does not mask in IRQ */ unsigned int enabled : 1; /* IRQ is currently enabled */ unsigned int triggered: 1; /* IRQ has occurred */ unsigned int probing : 1; /* IRQ in use for a probe */ unsigned int probe_ok : 1; ARM-Linux /* IRQ can be used for probe */ unsigned int valid : 1; /* IRQ claimable */ unsigned int noautoenable : 1; /* don"t automatically enable IRQ */ unsigned int unused :25; void (*mask_ack)(unsigned int irq); /* Mask and acknowledge IRQ */ void (*mask)(unsigned int irq); /* Mask IRQ */ void (*unmask)(unsigned int irq); /* Unmask IRQ */ struct irqaction *action; /*ARM-Linux * IRQ lock detection */ unsigned int lck_cnt; unsigned int lck_pc; unsigned int lck_jif; }; 在具體ARM-Linux的ARM芯片中會(huì)有很多的中斷類型,每一種類型的中斷用以上結(jié)構(gòu)來表示: struct irqdesc irq_desc[NR_IRQS]; /* NR_IRQS根據(jù)不同的MCU會(huì)有所區(qū)別*/ 在通過request_irq()函數(shù)注冊(cè)中斷服務(wù)程序的時(shí)候ARM-Linux,將會(huì)把中斷向量和中斷服務(wù)程序?qū)?yīng)起來。 我們來看一下request_irq的源碼: 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;ARM-Linux if (irq >= NR_IRQS || !irq_desc[irq].valid || !handler || (irq_flags & SA_SHIRQ && !dev_id)) return -EINVAL; action = (struct irqaction *)kmallocARM-Linux(sizeof(struct irqaction), GFP_KERNEL); if (!action) /* 生成action結(jié)構(gòu)*/ return -ENOMEM; action->handler = handler; action->flags = irq_flags;ARM-Linux action->mask = 0; action->name = devname; action->next = NULL; action->dev_id = dev_id; retval = setup_arm_irq(irq, action); /*把中斷號(hào)irq和action 對(duì)應(yīng)ARM-Linux起來*/ if (retval) kfree(action);ARM-Linux return retval; } 其中第一個(gè)參數(shù)irq就是中斷向量,第二個(gè)參數(shù)即是要注冊(cè)的中斷服務(wù)程序。很多同仁可能疑惑的是,我們要注冊(cè)的中斷向量號(hào)是怎么確定的呢?這要根據(jù)具體芯片的中斷控制器,比如三星的S3C2410,需要 通過讀取其中的中斷狀態(tài)寄存器,來獲得是哪個(gè)設(shè)備發(fā)生了中斷: ARM-Linux if defined(CONFIG_ARCH_S3C2410) #include .macro disable_fiq .endm .macro get_irqnr_and_base, irqnr, irqstat, base, tmp mov r4, #INTBASE @ virtual address of IRQ registers ldr \irqnr,ARM-Linux [r4, #0x8] @ read INTMSK 中斷掩碼寄存器 ldr \irqstat, [r4, #0x10] @ read INTPND 中斷寄存器 bics \irqstat, \irqstat, \irqnr bics \irqstat, \irqstat, \irqnr beq 1002f mov \irqnr, #0ARM-Linux 1001: tst \irqstat, #1 bne 1002f @ found IRQ add \irqnr, \irqnr, #1 mov \irqstat, \irqstat, lsr #1 cmp \irqnr, #32 bcc 1001b 1002: .endm .macro irq_prio_table .endm 以上代碼也告訴了ARM-Linux我們,中斷號(hào)的確定,其實(shí)是和S3C2410手冊(cè)中SRCPND寄存器是一致的,即: /* Interrupt Controller */ #define IRQ_EINT0 0 /* External interrupt 0 */ #define IRQ_EINT1 1 /* External interrupt 1 */ #define IRQ_EINT2 2 /* External interrupt 2 */ #define IRQ_EINT3 3 ARM-Linux /* External interrupt 3 */ #define IRQ_EINT4_7 4 /* External interrupt 4 ~ 7 */ #define IRQ_EINT8_23 5 /* External interrupt 8 ~ 23 */ #define IRQ_RESERVED6 6 /* Reserved for future use */ #define IRQ_BAT_FLT 7 #define IRQ_TICK 8 /* RTC time tick interrupt */ #define IRQ_WDT 9 /* Watch-Dog timer interrupt */ #define IRQ_TIMER0 10 /* Timer 0 interrupt */ #define IRQ_TIMER1 11 /* Timer 1 interrupt */ARM-Linux #define IRQ_TIMER2 12 ARM-Linux /* Timer 2 interrupt */ #define IRQ_TIMER3 13 /* Timer 3 interrupt */ #define IRQ_TIMER4 1 |