linux外部中断过程

2019-07-12 21:11发布

内核在linux-2.6.22.6initmain.c:start_kernel函数中调用trap_init、init_IRQ 两个函数来设置异常的处理函数。 asmlinkage void __init start_kernel(void) { ... trap_init(); ... init_IRQ(); ... } 1.linux-2.6.22.6archarmkernel raps.c:trap_init函数分析
trap_init函数被用来设置各种异常的处理向量,所谓向量,就是一些被安放在固定位置的代码,当发生异常时,CPU会自动执行这些固定位置上相应代码段,arm架构linux内核异常向量基址地0xffff0000,trap_init函数将异常向量复制到0ffff0000处: void __init trap_init(void) { ... unsigned long vectors = CONFIG_VECTORS_BASE; //CONFIG_VECTORS_BASE=0xffff0000 extern char __stubs_start[], __stubs_end[]; extern char __vectors_start[], __vectors_end[]; ... /* * Copy the vectors, stubs and kuser helpers (in entry-armv.S) * into the vector page, mapped at 0xffff0000, and ensure these * are visible to the instruction stream. */ memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start); memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start); ... } vectors等于0xffff0000是异常向量的基址,memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);将从__vectors_start地址开始的,大小为__vectors_end - __vectors_start的代码复制到0xffff0000地址上,__vectors_start~-_vectors_end之间的代码就是异常向量,在arch/arm/kernel/entry-armv.S中定义: .globl __vectors_start __vectors_start: swi SYS_ERROR0 b vector_und + stubs_offset ldr pc, .LCvswi + stubs_offset b vector_pabt + stubs_offset b vector_dabt + stubs_offset b vector_addrexcptn + stubs_offset b vector_irq + stubs_offset b vector_fiq + stubs_offset .globl __vectors_end __vectors_end: .data .globl cr_alignment .globl cr_no_alignment 其中的vector_und、vector_pabt、vector_irq等表示要跳转去执行的代码,以vector_irq为例,它在arch/arm/kernel/entry-armv.S中,通过宏指令来定义: vector_stub irq, IRQ_MODE, 4 .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_stub是一个带参宏指令,它根据后面的参数定义了以“vector_irq”为标号的一段代码,具体是: .macro vector_stub, name, mode, correction=0 .align 5 vector_ ame: .if correction sub lr, lr, #correction .endif @ @ Save r0, lr_ (parent PC) and spsr_ @ (parent CPSR) @ stmia sp, {r0, lr} @ save r0, lr mrs lr, spsr str lr, [sp, #8] @ save spsr @ @ Prepare for SVC32 mode. IRQs remain disabled. @ mrs r0, cpsr eor r0, r0, #(mode ^ SVC_MODE) msr spsr_cxsf, r0 @ @ the branch table must immediately follow this code @ and lr, lr, #0x0f mov r0, sp ldr lr, [pc, lr, lsl #2] movs pc, lr @ branch to handler in SVC mode .endm 异常向量的代码很简单,它们只是一些跳转指令。发生异常时,CPU自动执行这些指令,跳转去执行更复杂的代码,比如保存被中断程序的执行环境,调用异常处理函数,恢复被中断程序的执行环境并重新运行。这些“更复杂的代码”在地址__stubs_start~__stubs_end之间,被复制到了vectors + 0x200地址处,它们在arch/arm/kernel/entry-armv.S中定义: .globl __stubs_start __stubs_start: /* * Interrupt dispatcher */ vector_stub irq, IRQ_MODE, 4 .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 /* * Data abort dispatcher * Enter in ABT mode, spsr = USR CPSR, lr = USR PC */ vector_stub dabt, ABT_MODE, 8 .long __dabt_usr @ 0 (USR_26 / USR_32) .long __dabt_invalid @ 1 (FIQ_26 / FIQ_32) .long __dabt_invalid @ 2 (IRQ_26 / IRQ_32) .long __dabt_svc @ 3 (SVC_26 / SVC_32) .long __dabt_invalid @ 4 .long __dabt_invalid @ 5 .long __dabt_invalid @ 6 .long __dabt_invalid @ 7 .long __dabt_invalid @ 8 .long __dabt_invalid @ 9 .long __dabt_invalid @ a .long __dabt_invalid @ b .long __dabt_invalid @ c .long __dabt_invalid @ d .long __dabt_invalid @ e .long __dabt_invalid @ f /* * Prefetch abort dispatcher * Enter in ABT mode, spsr = USR CPSR, lr = USR PC */ vector_stub pabt, ABT_MODE, 4 .long __pabt_usr @ 0 (USR_26 / USR_32) .long __pabt_invalid @ 1 (FIQ_26 / FIQ_32) .long __pabt_invalid @ 2 (IRQ_26 / IRQ_32) .long __pabt_svc @ 3 (SVC_26 / SVC_32) .long __pabt_invalid @ 4 .long __pabt_invalid @ 5 .long __pabt_invalid @ 6 .long __pabt_invalid @ 7 .long __pabt_invalid @ 8 .long __pabt_invalid @ 9 .long __pabt_invalid @ a .long __pabt_invalid @ b .long __pabt_invalid @ c .long __pabt_invalid @ d .long __pabt_invalid @ e .long __pabt_invalid @ f /* * Undef instr entry dispatcher * Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC */ vector_stub und, UND_MODE .long __und_usr @ 0 (USR_26 / USR_32) .long __und_invalid @ 1 (FIQ_26 / FIQ_32) .long __und_invalid @ 2 (IRQ_26 / IRQ_32) .long __und_svc @ 3 (SVC_26 / SVC_32) .long __und_invalid @ 4 .long __und_invalid @ 5 .long __und_invalid @ 6 .long __und_invalid @ 7 .long __und_invalid @ 8 .long __und_invalid @ 9 .long __und_invalid @ a .long __und_invalid @ b .long __und_invalid @ c .long __und_invalid @ d .long __und_invalid @ e .long __und_invalid @ f .align 5 .globl __stubs_end __stubs_end: .equ stubs_offset, __vectors_start + 0x200 - __stubs_start __irq_usr: usr_entry ... irq_handler ... .ltorg .macro irq_handler ... bne asm_do_IRQ ... .endm arch/arm/kernel/entry-armv.S/irq_handler☞☞☞linux-2.6.22.6archarmkernelirq.c:asm_do_IRQ asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs) { ... struct irq_desc *desc = irq_desc + irq; ... desc_handle_irq(irq, desc); ... } linux-2.6.22.6archarmkernelirq.c:asm_do_IRQ☞☞☞linux-2.6.22.6includeasm-armmachirq.h:desc_handle_irq static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc) { desc->handle_irq(irq, desc); } handle_irq是指向中断处理函数的指针,由语句desc->handle_irq(irq, desc);跳转到中断处理函数,handle_irq指向irq中断的处理函数handle_edge_irq,通过由以下过程指定: 由以下初始化过程指定最终给handle_irq赋值的handle的值: linux-2.6.22.6archarmplat-s3c24xxirq.c: void __init s3c24xx_init_irq(void) { unsigned long pend; unsigned long last; int irqno; int i; ... /* register the main interrupts */ for (irqno = IRQ_EINT4t7; irqno <= IRQ_ADCPARENT; irqno++) { /* set all the s3c2410 internal irqs */ switch (irqno) { /* deal with the special IRQs (cascaded) */ case IRQ_EINT4t7: case IRQ_EINT8t23: case IRQ_UART0: case IRQ_UART1: case IRQ_UART2: case IRQ_ADCPARENT: set_irq_chip(irqno, &s3c_irq_level_chip); set_irq_handler(irqno, handle_level_irq); break; case IRQ_RESERVED6: case IRQ_RESERVED24: /* no IRQ here */ break; default: //irqdbf("registering irq %d (s3c irq) ", irqno); set_irq_chip(irqno, &s3c_irq_chip); set_irq_handler(irqno, handle_edge_irq); set_irq_flags(irqno, IRQF_VALID); } } ... /* external interrupts */ for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) { irqdbf("registering irq %d (ext int) ", irqno); set_irq_chip(irqno, &s3c_irq_eint0t4); set_irq_handler(irqno, handle_edge_irq); set_irq_flags(irqno, IRQF_VALID); } for (irqno = IRQ_EINT4; irqno <= IRQ_EINT23; irqno++) { irqdbf("registering irq %d (extended s3c irq) ", irqno); set_irq_chip(irqno, &s3c_irqext_chip); set_irq_handler(irqno, handle_edge_irq); set_irq_flags(irqno, IRQF_VALID); } /* register the uart interrupts */ irqdbf("s3c2410: registering external interrupts "); for (irqno = IRQ_S3CUART_RX0; irqno <= IRQ_S3CUART_ERR0; irqno++) { irqdbf("registering irq %d (s3c uart0 irq) ", irqno); set_irq_chip(irqno, &s3c_irq_uart0); set_irq_handler(irqno, handle_level_irq); set_irq_flags(irqno, IRQF_VALID); } for (irqno = IRQ_S3CUART_RX1; irqno <= IRQ_S3CUART_ERR1; irqno++) { irqdbf("registering irq %d (s3c uart1 irq) ", irqno); set_irq_chip(irqno, &s3c_irq_uart1); set_irq_handler(irqno, handle_level_irq); set_irq_flags(irqno, IRQF_VALID); } for (irqno = IRQ_S3CUART_RX2; irqno <= IRQ_S3CUART_ERR2; irqno++) { irqdbf("registering irq %d (s3c uart2 irq) ", irqno); set_irq_chip(irqno, &s3c_irq_uart2); set_irq_handler(irqno, handle_level_irq); set_irq_flags(irqno, IRQF_VALID); } for (irqno = IRQ_TC; irqno <= IRQ_ADC; irqno++) { irqdbf("registering irq %d (s3c adc irq) ", irqno); set_irq_chip(irqno, &s3c_irq_adc); set_irq_handler(irqno, handle_edge_irq); set_irq_flags(irqno, IRQF_VALID); } } linux-2.6.22.6archarmplat-s3c24xxirq.c:s3c24xx_init_irq☞☞☞linux-2.6.22.6includelinuxirq.h:set_irq_handler static inline void set_irq_handler(unsigned int irq, irq_flow_handler_t handle) { __set_irq_handler(irq, handle, 0, NULL); } handle_irq的值由以下linux-2.6.22.6kernelirqchip.c:__set_irq_handler函数指定,而handle的值指向handle_edge_irq函数,因此handle_irq也指向handle_edge_irq函数: void __set_irq_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, const char *name) { struct irq_desc *desc; unsigned long flags; ... desc = irq_desc + irq; ... desc->handle_irq = handle; ... } linux-2.6.22.6kernelirqchip.c:handle_edge_irq中断处理函数主要做以下两方面工作:
  • 清中断
    desc->chip->ack(irq); //Start handling the irq
  • 处理中断
    action_ret = handle_IRQ_event(irq, action);
linux-2.6.22.6kernelirqhandle.c:handle_IRQ_event函数取出action链表中的成员,一一执行它们的用户中断处理程序action->handler irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action) { irqreturn_t ret, retval = IRQ_NONE; unsigned int status = 0; ... do { ret = action->handler(irq, action->dev_id); if (ret == IRQ_HANDLED) status |= action->flags; retval |= ret; action = action->next; } while (action); ... } 我们希望的执行的中断处理程序通过linux-2.6.22.6kernelirqmanage.c:request_irq函数注册进action链表 int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id) { struct irqaction *action; int retval; ... retval = setup_irq(irq, action); ... } linux-2.6.22.6kernelirqmanage.c:request_irq☞☞☞linux-2.6.22.6kernelirqmanage.c:setup_irq,进入irq建立函数,将新的中断项加入链表,设置中断引脚类型,并使能中断 int setup_irq(unsigned int irq, struct irqaction *new) { struct irq_desc *desc = irq_desc + irq; struct irqaction *old, **p; const char *old_name = NULL; unsigned long flags; int shared = 0; ... if (old) { ... /* add new interrupt at end of irq queue */ do { p = &old->next; old = *p; } while (old); shared = 1; } *p = new; ... if (!shared) { ... /* Setup the type (level, edge polarity) if configured: */ if (new->flags & IRQF_TRIGGER_MASK) { if (desc->chip && desc->chip->set_type) desc->chip->set_type(irq, new->flags & IRQF_TRIGGER_MASK); else printk(KERN_WARNING "No IRQF_TRIGGER set_type " "function for IRQ %d (%s) ", irq, desc->chip ? desc->chip->name : "unknown"); } else compat_irq_chip_set_default_handler(desc); ... if (!(desc->status & IRQ_NOAUTOEN)) { desc->depth = 0; desc->status &= ~IRQ_DISABLED; if (desc->chip->startup) desc->chip->startup(irq); else desc->chip->enable(irq); } else /* Undo nested disables: */ desc->depth = 1; } } 解除中断用linux-2.6.22.6kernelirqmanage.c:free_irq函数
2.linux-2.6.22.6archarmkernelirq.c:init_IRQ函数分析
init_IRQ函数被用来初始化中断的处理框架,设置各种中断的默认处理函数。当发生中断时,中断总入口函数asm_do_IRQ就可以调用这些函数作进一步处理