您的位置:首页 > 运维架构 > Linux

linux外部中断过程

2016-05-05 11:37 507 查看
内核在linux-2.6.22.6\init\main.c:start_kernel函数中调用trap_init、init_IRQ 两个函数来设置异常的处理函数。

asmlinkage void __init start_kernel(void)
{
...
trap_init();
...
init_IRQ();
...
}


1.linux-2.6.22.6\arch\arm\kernel\traps.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_\name:
.if \correction
sub lr, lr, #\correction
.endif

@
@ Save r0, lr_<exception> (parent PC) and spsr_<exception>
@ (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.6\arch\arm\kernel\irq.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.6\arch\arm\kernel\irq.c:asm_do_IRQ☞☞☞linux-2.6.22.6\include\asm-arm\mach\irq.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.6\arch\arm\plat-s3c24xx\irq.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)\n", 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)\n", 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)\n", 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\n");

for (irqno = IRQ_S3CUART_RX0; irqno <= IRQ_S3CUART_ERR0; irqno++) {
irqdbf("registering irq %d (s3c uart0 irq)\n", 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)\n", 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)\n", 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)\n", 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.6\arch\arm\plat-s3c24xx\irq.c:s3c24xx_init_irq☞☞☞linux-2.6.22.6\include\linux\irq.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.6\kernel\irq\chip.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.6\kernel\irq\chip.c:handle_edge_irq中断处理函数主要做以下两方面工作:

清中断

desc->chip->ack(irq);
//Start handling the irq

处理中断

action_ret = handle_IRQ_event(irq, action);


linux-2.6.22.6\kernel\irq\handle.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.6\kernel\irq\manage.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.6\kernel\irq\manage.c:request_irq☞☞☞linux-2.6.22.6\kernel\irq\manage.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)\n", 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.6\kernel\irq\manage.c:free_irq函数

2.linux-2.6.22.6\arch\arm\kernel\irq.c:init_IRQ函数分析

init_IRQ函数被用来初始化中断的处理框架,设置各种中断的默认处理函数。当发生中断时,中断总入口函数asm_do_IRQ就可以调用这些函数作进一步处理
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux 内核