linux之中断过程(含内核部分)(一)

1.uboot启动kernel入口函数是start_kernel()
start_kernel()
   --->early_trap_init()
       --->unsigned long vectors = CONFIG_VECTORS_BASE;
           memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
           memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);     //初始化阶段完成
在arch/arm/configs/s3c2410_defconfig 中定义 CONFIG_VECTORS_BASE=0xffff0000
__vectors_start和__stubs_start处的代码在内核初始化过程中从原来的内核代码段拷贝到了0xffff0000和0xffff0200
 
2.当中断产生直接跳转到中断跳转的一行
__vectors_start定义如下:
arch/arm/kernel/entry-armv.S
.equ     stubs_offset, __vectors_start + 0x200 - __stubs_start。
.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_start和__stubs_start搬迁后的地址分别是0xffff0000和0xffff0200,相差0x200,搬迁前是stubs_start - vectors_start,
注意__vectors_start和__stubs_start均为搬迁前地址。搬迁前的跳转只需要跳转长度为vector_irq,但由于映射搬迁后两个地址对应的新地址
又偏移了stubs_offset[0x200 - (stubs_start - vectors_start)],所以欲使搬迁后仍然能够跳转到相应位置,需要在vector_irq的基础上再跳转
stubs_offset,也就是跳转vector_irq+stubs_offset。
 
3  vector_irq 为宏定义具体如下:   arch/arm/kernel/entry-armv.S
    .macro    vector_stub, name, mode, correction=0
    .align    5
 
@ 发生中断后处理器响应中断的伪代码如下
@ 1. lr_<exception> = return link
@ 2. spsr_<exception> = cpsr
@ 3. cpsr[4:0] = exception mode number
@ 4. cpsr[5] = 0                             @ if run in ARM
@ 5. if (fiq or reset) then cpsr[6] = 1      @ disable F
@ 6. cpsr[7] = 1                             @ disable I
@ 7. pc = exception vector address
 
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
 
 
    .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)
    (省略后12个__irq_invalid)
 
带入具体参数:
 
vector_irq:
  
    @ 根据进入irq中断前处理器所处的模式(IRQ_MODE),将紧接着其下边的16个地址池中对应位置的处理向量,
    @ 取出来赋给PC,完成进一步跳转。这里我们选择让程序跳转到__irq_usr代码段继续执行
 
    stmia    sp, {r0, lr}        @ save r0, lr
    mrs    lr, spsr
    str    lr, [sp, #8]        @ save spsr
 
    mrs    r0, cpsr
    eor    r0, r0, #(IRQ_MODE ^ SVC_MODE)
    msr    spsr_cxsf, r0
 
    and    lr, lr, #0x0f
    mov    r0, sp
    ldr    lr, [pc, lr, lsl #2]
    movs    pc, lr            @ branch to handler in SVC mode
    .endm
    
    @ 16个地址池中对应位置的处理向量
    .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)
    (省略后12个__irq_invalid)
 
4  然后__irq_usr 如下:     #暂时不执行的代码没有贴出来
__irq_usr:
    usr_entry              @将usr模式下的寄存器、中断返回地址保存到堆栈中  @save register
    get_thread_info tsk    @获取当前进程的进程描述符中的成员变量thread_info的地址,并将该地址保存到寄存器tsk等于r9
    irq_handler            @中断处理
    mov    why, #0
    b    ret_to_user       @中断处理完成,返回中断产生的位置
 
5  irq_handler   @中断处理函数的主函数
 
/*
* Interrupt handling.  Preserves r7, r8, r9
*/
    .macro    irq_handler
    get_irqnr_preamble r5, lr
1:  get_irqnr_and_base r0, r6, r5, lr
    movne    r1, sp
    
    @ routine called with r0 = irq number, r1 = struct pt_regs *
    adrne    lr, 1b
    bne    asm_do_IRQ     @接下来执行的函数是用C编写的  asm_do_IRQ是中断的C语言总入口函数
 
6 asm_do_IRQ
带的第一个参数就是对应的中断号    中断号对应着发生了什么样的中断事件,就可以采取什么样的中断服务程序进行处理。
asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)  //
{
    struct pt_regs *old_regs = set_irq_regs(regs);
    struct irq_desc *desc = irq_desc + irq;  //中断数组第irq个中断  也就是irq_desc[irq]
 
    /*Some hardware gives randomly wrong interrupts.  Ratherthan crashing, do something sensible.*/
    if (irq >= NR_IRQS)
        desc = &bad_irq_desc;
 
    irq_enter();  //进中断
 
    desc_handle_irq(irq, desc);
 
    /* AT91 specific workaround */
    irq_finish(irq);
 
    irq_exit();   //退出中断
    set_irq_regs(old_regs);
}
 
7. desc_handle_irq(irq,desc) 
    ----> desc->handle_irq(irq, desc); //此处为回调函数    
 
对应注册函数为:
s3c24xx_init_irq()      (arch/arm/plat-s3c24xx/irq.c)
         ---->  set_irq_handler(irqno, handle_edge_irq)
                ---->  __ set_irq_handler (irq, handle, 0, NULL)
                        ----> desc->handle_irq = handle;
 
          ---->  set_irq_chip(irqno, & s3c_irq_chip)
                ----> desc->chip = chip;
也就是说,在初始化irq中断时,desc->handle_irq和desc->chip已经分别指向了 handle_edge_irq和  s3c_irq_chip。
中断号为   EINT4t7 的中断触发后直接转到了相应的函数。
 
 
另外  通过  set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint4t7)
        使s3c_irq_demux_extint4t7()对应 __ set_irq_handler中的handle。
 
8  handle_edge_irq(irq,desc)    //清除中断,禁止中断,重新开启中断在此
void fastcall  handle_edge_irq(unsigned int irq, struct irq_desc *desc)
{
    ......
    kstat_cpu(cpu).irqs[irq]++;   //对应中断次数加1
 
    /* Start handling the irq */
    desc->chip->ack(irq);
    ......
    do {
        struct irqaction *action = desc->action;
        irqreturn_t action_ret;
        ......
        desc->status &= ~IRQ_PENDING;
        action_ret = handle_IRQ_event(irq, action);
       ......
    } while ((desc->status & (IRQ_PENDING | IRQ_DISABLED)) == IRQ_PENDING);
    ......
}
 
static struct irq_chip s3c_irq_chip = {
    .name        = "s3c",
    .ack        = s3c_irq_ack,
    .mask        = s3c_irq_mask,
    .unmask        = s3c_irq_unmask,
    .set_wake    = s3c_irq_wake
};
 
由第7步知道 desc->chip 对应的是结构体 s3c_irq_chip
 
desc->chip->ack 指向函数 s3c_irq_ack()
 
9 s3c_irq_ack()  对应寄存器值的修改
static inline void s3c_irq_ack(unsigned int irqno)
{
    unsigned long bitval = 1UL << (irqno - IRQ_EINT0);
 
    __raw_writel(bitval, S3C2410_SRCPND);
    __raw_writel(bitval, S3C2410_INTPND);
}
 
 10  handle_IRQ_event (irq, action)   这个函数就是将中断号为irq的中断响应函数desc->action放入到对应的中断链表中
irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
{
    irqreturn_t ret, retval = IRQ_NONE;
    unsigned int status = 0;
 
    handle_dynamic_tick(action);
 
    if (!(action->flags & IRQF_DISABLED))  //当调用action函数时中断不接收
        local_irq_enable_in_hardirq();     //local_irq_enable() 和ocal_irq_disable()对应
 
    do {
        ret = action->handler(irq, action->dev_id); /* 调用action->handler,找对应注册函数。即request_irq 时注册的handler 函数 */
        if (ret == IRQ_HANDLED)
            status |= action->flags;
        retval |= ret;
        action = action->next; 
    } while (action);    //中断响应函数链表
 
    if (status & IRQF_SAMPLE_RANDOM)
        add_interrupt_randomness(irq);//随机数生成器
    local_irq_disable();
 
    return retval;
}
 
11 action->handler 的注册函数 搜索结构体 irq_handler_t  找到  request_irq
request_irq( irq, handler,  irqflags,  *devname, *dev_id)  //注册中断处理函数
对应 free_irq(irq, *dev_id)                                                //释放中断处理函数
#define IRQF_TRIGGER_NONE          0x00000000
#define IRQF_TRIGGER_RISING        0x00000001  //上升沿
#define IRQF_TRIGGER_FALLING       0x00000002  //下降沿
#define IRQF_TRIGGER_HIGH          0x00000004  //高电平
#define IRQF_TRIGGER_LOW           0x00000008  //低电平
#define IRQF_TRIGGER_MASK          (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
#define IRQF_TRIGGER_PROBE         0x00000010
 
/*
* These flags used only by the kernel as part of the
* irq handling routines.
*
* IRQF_DISABLED                - keep irqs disabled when calling the action handler
* IRQF_SAMPLE_RANDOM           - irq is used to feed the random generator
* IRQF_SHARED                  - allow sharing the irq among several devices
* IRQF_PROBE_SHARED            - set by callers when they expect sharing mismatches to occur
* IRQF_TIMER                   - Flag to mark this interrupt as timer interrupt
* IRQF_PERCPU                  - Interrupt is per cpu
* IRQF_NOBALANCING             - Flag to exclude this interrupt from irq balancing
* IRQF_IRQPOLL                 - Interrupt is used for polling (only the interrupt that is
*  registered first in an shared interrupt is considered for performance reasons)
*/
#define IRQF_DISABLED         0x00000020
#define IRQF_SAMPLE_RANDOM    0x00000040
#define IRQF_SHARED           0x00000080
#define IRQF_PROBE_SHARED     0x00000100
#define IRQF_TIMER            0x00000200
#define IRQF_PERCPU           0x00000400
#define IRQF_NOBALANCING      0x00000800
#define IRQF_IRQPOLL          0x00001000
 
int request_irq(unsigned int irq, irq_handler_t handler,unsigned long irqflags, const char *devname, void *dev_id)
{
    struct irqaction *action;
    int retval;
    ......
    action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC); //分配要保存的中断处理函数空间
 
    action->handler = handler;   //对应handle的注册
    action->flags = irqflags;
    cpus_clear(action->mask);
    action->name = devname;
    action->next = NULL;
    action->dev_id = dev_id;    //最主要是用于注销对应中断处理函数
 
    select_smp_affinity(irq);    //中断分配到不同的CPU
 
#ifdef CONFIG_DEBUG_SHIRQ
    if (irqflags & IRQF_SHARED) {
        /*
         * It's a shared IRQ -- the driver ought to be prepared for it
         * to happen immediately, so let's make sure....
         * We do this before actually registering it, to make sure that
         * a 'real' IRQ doesn't run in parallel with our fake
         */
        if (irqflags & IRQF_DISABLED) {
            unsigned long flags;
 
            local_irq_save(flags);
            handler(irq, dev_id);
            local_irq_restore(flags);
        } else
            handler(irq, dev_id);
    }
#endif
 
    retval = setup_irq(irq, action);
    if (retval)
        kfree(action);  //对应上文kmalloc
 
    return retval;
}
 
12 setup_irq (irq,action)     Internal function to register an irqaction
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;
 
    /*The following block of code has to be executed atomically*/
    spin_lock_irqsave(&desc->lock, flags);
    p = &desc->action;
    old = *p;
    if (old)    //  shared = 1;
    {
        /*
         * Can't share interrupts unless both agree to and are
         * the same type (level, edge, polarity). So both flag
         * fields must have IRQF_SHARED set and the bits which
         * set the trigger type must match.
         */
        if (!((old->flags & new->flags) & IRQF_SHARED) ||
            ((old->flags ^ new->flags) & IRQF_TRIGGER_MASK)) {
            old_name = old->name;
            goto mismatch;
        }
 
#if defined(CONFIG_IRQ_PER_CPU)
        /* All handlers must agree on per-cpuness */
        if ((old->flags & IRQF_PERCPU) !=
            (new->flags & IRQF_PERCPU))
            goto mismatch;
#endif
 
        /* add new interrupt at end of irq queue */
        do {
            p = &old->next;      //中断添加到结构体irqaction
            old = *p;
        } while (old);
        shared = 1;                
    }
    *p = new;
 
    /* Exclude IRQ from balancing */
    if (new->flags & IRQF_NOBALANCING)
        desc->status |= IRQ_NO_BALANCING;
 
    if (!shared) 
    {
        irq_chip_set_defaults(desc->chip);                                     //按默认的写入
#if defined(CONFIG_IRQ_PER_CPU)
        if (new->flags & IRQF_PERCPU)
            desc->status |= IRQ_PER_CPU;
#endif
        /* 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);     //设置type
            else
                /* IRQF_TRIGGER_* but the PIC does not support multiple flow-types?*/
                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);
 
        desc->status &= ~(IRQ_AUTODETECT | IRQ_WAITING | IRQ_INPROGRESS);
        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
            desc->depth = 1;   /* Undo nested disables: */
    }
    /* Reset broken irq detection when installing new handler */
    desc->irq_count = 0;
    desc->irqs_unhandled = 0;
    spin_unlock_irqrestore(&desc->lock, flags);
 
    new->irq = irq;
    register_irq_proc(irq);
    new->dir = NULL;
    register_handler_proc(irq, new);                                            //创建文件mkdir
    return 0;
mismatch:
#ifdef CONFIG_DEBUG_SHIRQ
    if (!(new->flags & IRQF_PROBE_SHARED)) 
    {
        printk(KERN_ERR "IRQ handler type mismatch for IRQ %d\n", irq);
        if (old_name)
            printk(KERN_ERR "current handler: %s\n", old_name);
        dump_stack();
    }
#endif
    spin_unlock_irqrestore(&desc->lock, flags);
    return -EBUSY;
}
 
中断体系结构的初始化,就是构造irq_desc[NR_IRQS]这个数据结构;用户注册中断就是构造action链表;用户卸载中断就是从action链表中去除对应的项
 
 
 
 
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

怀想天空2011

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值