感谢支持
我们一直在努力

Linux中断导读之一–初始化<1>

看一下linux中断部分,分为三部分,初始化,处理流程以及注册流程。


相关阅读:


Linux中断导读之一–初始化<2> http://www.linuxidc.com/Linux/2012-01/52837.htm


Linux中断导读之一–注册部分<3> http://www.linuxidc.com/Linux/2012-01/52838.htm


Linux中断导读之一–处理流程<4> http://www.linuxidc.com/Linux/2012-01/52839.htm


先看第一部分初始化:



=======


   init/main.c
  setup_arch(&command_line);
  arch/arm/kernel/setup.c
void __init setup_arch(char **cmdline_p)
{
    struct machine_desc *mdesc;
 
    setup_processor();
    mdesc = setup_machine_fdt(__atags_pointer);
    if (!mdesc)
        mdesc = setup_machine_tags(machine_arch_type);
    machine_desc = mdesc;
    machine_name = mdesc->name;




=======


同文件下


static struct machine_desc * __init setup_machine_tags(unsigned int nr)
{
    struct tag *tags = (struct tag *)&init_tags;
    struct machine_desc *mdesc = NULL, *p;
    char *from = default_command_line;
 
    init_tags.mem.start = PHYS_OFFSET;
 
    /*
     * locate machine in the list of supported machines.
     */
    for_each_machine_desc(p)
        if (nr == p->nr) {
            printk(“Machine: %s\n”, p->name);
            mdesc = p;
            break;
        }


在arch/arm/include/asm/mach/arch.h 中
/*
 * Machine type table – also only accessible during boot
 */
extern struct machine_desc __arch_info_begin[], __arch_info_end[];
#define for_each_machine_desc(p)            \
    for (p = __arch_info_begin; p < __arch_info_end; p++)
__arch_info_begin和__arch_info_end在arch/arm/kernel/vmlinux.lds.s 中
.init.arch.info : {
        __arch_info_begin = .;
        *(.arch.info.init)
        __arch_info_end = .;
    }


/*
 * Set of macros to define architecture features.  This is built into
 * a table by the linker.
 */
#define MACHINE_START(_type,_name)            \
static const struct machine_desc __mach_desc_##_type    \
 __used                            \
 __attribute__((__section__(“.arch.info.init”))) = {    \
    .nr        = MACH_TYPE_##_type,        \
    .name        = _name,
 
#define MACHINE_END                \
};




这里.arch.info.init这个段是静态被填充的,一般都是和具体片子相关的代码,通常对应于文件


目录arch/arm/ 目录,拿最常见的2440来说,那么对应于arch/arm/mach-s3c2440/mach-mini2440.c


在该文件下你可以找到:


MACHINE_START(MINI2440, “MINI2440”)
    /* Maintainer: Michel Pollet <buserror@gmail.com> */
    .atag_offset    = 0x100,
    .map_io        = mini2440_map_io,
    .init_machine    = mini2440_init,
    .init_irq    = s3c24xx_init_irq,
    .timer        = &s3c24xx_timer,
MACHINE_END


=======================


回到start_kernel函数,


继续往下找到init_irq函数,


arch/arm/kernel/irq.c
void __init init_IRQ(void)
{
    machine_desc->init_irq();
}


即使对应于上面的 .init_irq    = s3c24xx_init_irq,


即/arch/arm/plat-s3c24xx/irq.c中:


================


/* s3c24xx_init_irq
 *
 * Initialise S3C2410 IRQ system
*/

void __init s3c24xx_init_irq(void)
{
    unsigned long pend;
    unsigned long last;
    int irqno;
    int i;

#ifdef CONFIG_FIQ
    init_FIQ();                                                    //fiq可选
#endif

    irqdbf(“s3c2410_init_irq: clearing interrupt status flags\n”);

    /* first, clear all interrupts pending… */                                       //清空所有irq状态

    last = 0;
    for (i = 0; i < 4; i++) {
        pend = __raw_readl(S3C24XX_EINTPEND);

        if (pend == 0 || pend == last)
            break;

        __raw_writel(pend, S3C24XX_EINTPEND);
        printk(“irq: clearing pending ext status %08x\n”, (int)pend);
        last = pend;
    }

    last = 0;
    for (i = 0; i < 4; i++) {
        pend = __raw_readl(S3C2410_INTPND);

        if (pend == 0 || pend == last)
            break;

        __raw_writel(pend, S3C2410_SRCPND);
        __raw_writel(pend, S3C2410_INTPND);
        printk(“irq: clearing pending status %08x\n”, (int)pend);
        last = pend;
    }

    last = 0;
    for (i = 0; i < 4; i++) {
        pend = __raw_readl(S3C2410_SUBSRCPND);

        if (pend == 0 || pend == last)
            break;

        printk(“irq: clearing subpending status %08x\n”, (int)pend);
        __raw_writel(pend, S3C2410_SUBSRCPND);
        last = pend;
    }

    /* register the main interrupts */

    irqdbf(“s3c2410_init_irq: registering s3c2410 interrupt handlers\n”);

    for (irqno = IRQ_EINT4t7; irqno <= IRQ_ADCPARENT; irqno++) {       //注册所有中断,这里是从4—31,可以有选择性的处理
        /* 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:
            irq_set_chip_and_handler(irqno, &s3c_irq_level_chip,
                         handle_level_irq);
            break;

        case IRQ_RESERVED6:
        case IRQ_RESERVED24:
            /* no IRQ here */
            break;

        default:
            //irqdbf(“registering irq %d (s3c irq)\n”, irqno);
            irq_set_chip_and_handler(irqno, &s3c_irq_chip,
                         handle_edge_irq);
            set_irq_flags(irqno, IRQF_VALID);
        }
    }

    /* setup the cascade irq handlers */

    irq_set_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint4t7);
    irq_set_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint8);

    irq_set_chained_handler(IRQ_UART0, s3c_irq_demux_uart0);
    irq_set_chained_handler(IRQ_UART1, s3c_irq_demux_uart1);
    irq_set_chained_handler(IRQ_UART2, s3c_irq_demux_uart2);
    irq_set_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_adc);

    /* external interrupts */

    for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
        irqdbf(“registering irq %d (ext int)\n”, irqno);
        irq_set_chip_and_handler(irqno, &s3c_irq_eint0t4,
                     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);
        irq_set_chip_and_handler(irqno, &s3c_irqext_chip,
                     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);
        irq_set_chip_and_handler(irqno, &s3c_irq_uart0,
                     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);
        irq_set_chip_and_handler(irqno, &s3c_irq_uart1,
                     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);
        irq_set_chip_and_handler(irqno, &s3c_irq_uart2,
                     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);
        irq_set_chip_and_handler(irqno, &s3c_irq_adc, handle_edge_irq);
        set_irq_flags(irqno, IRQF_VALID);
    }

    irqdbf(“s3c2410: registered interrupt handlers\n”);
}


====================


具体看一下irq_set_chip_and_handler函数,


static inline void irq_set_chip_and_handler(unsigned int irq, struct irq_chip *chip,
                        irq_flow_handler_t handle)
{
    irq_set_chip_and_handler_name(irq, chip, handle, NULL);
}


参数分别为irq number,irq chip结构,中断处理函数,


irq chip结构:


static struct irq_chip s3c_irq_adc = {
    .name        = “s3c-adc”,
    .irq_mask    = s3c_irq_adc_mask,
    .irq_unmask    = s3c_irq_adc_unmask,
    .irq_ack    = s3c_irq_adc_ack,
};


对中断的一下描述以及操作该中断的功能函数,也就是该中断所在chip;


继续跟进该函数:


void
irq_set_chip_and_handler_name(unsigned int irq, struct irq_chip *chip,
                  irq_flow_handler_t handle, const char *name)
{
    irq_set_chip(irq, chip);
    __irq_set_handler(irq, handle, 0, name);
}


其中:


/**
 *    irq_set_chip – set the irq chip for an irq
 *    @irq:    irq number
 *    @chip:    pointer to irq chip description structure
 */
int irq_set_chip(unsigned int irq, struct irq_chip *chip)
{
    unsigned long flags;
    struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
 
    if (!desc)
        return -EINVAL;
 
    if (!chip)
        chip = &no_irq_chip;
 
    desc->irq_data.chip = chip;         //*****
    irq_put_desc_unlock(desc, flags);
    /*
     * For !CONFIG_SPARSE_IRQ make the irq show up in
     * allocated_irqs. For the CONFIG_SPARSE_IRQ case, it is
     * already marked, and this call is harmless.
     */
    irq_reserve_irq(irq);
    return 0;
}


#先看irq_get_desc_lock函数


struct irq_desc *
__irq_get_desc_lock(unsigned int irq, unsigned long *flags, bool bus,
            unsigned int check)
{
    struct irq_desc *desc = irq_to_desc(irq);
 
    if (desc) {
        if (check & _IRQ_DESC_CHECK) {
            if ((check & _IRQ_DESC_PERCPU) &&
                !irq_settings_is_per_cpu_devid(desc))
                return NULL;
 
            if (!(check & _IRQ_DESC_PERCPU) &&
                irq_settings_is_per_cpu_devid(desc))
                return NULL;
        }
 
        if (bus)
            chip_bus_lock(desc);
        raw_spin_lock_irqsave(&desc->lock, *flags);
    }
    return desc;
}


重点看一下,irq_to_desc 函数在include/linux/irqnr.c中。


#define irq_to_desc(irq) (&irq_desc[irq])


每个irq都有一个描述结构struct irq_desc,详细描述了该irq的状态,


是个静态数组,


可以从include/linux/irqdesc.h 中找到其定义:


/**
 * struct irq_desc – interrupt descriptor
 * @irq_data:        per irq and chip data passed down to chip functions
 * @timer_rand_state:    pointer to timer rand state struct
 * @kstat_irqs:        irq stats per cpu
 * @handle_irq:        highlevel irq-events handler
 * @preflow_handler:    handler called before the flow handler (currently used by sparc)
 * @action:        the irq action chain
 * @status:        status information
 * @core_internal_state__do_not_mess_with_it: core internal status information
 * @depth:        disable-depth, for nested irq_disable() calls
 * @wake_depth:        enable depth, for multiple irq_set_irq_wake() callers
 * @irq_count:        stats field to detect stalled irqs
 * @last_unhandled:    aging timer for unhandled count
 * @irqs_unhandled:    stats field for spurious unhandled interrupts
 * @lock:        locking for SMP
 * @affinity_hint:    hint to user space for preferred irq affinity
 * @affinity_notify:    context for notification of affinity changes
 * @pending_mask:    pending rebalanced interrupts
 * @threads_oneshot:    bitfield to handle shared oneshot threads
 * @threads_active:    number of irqaction threads currently running
 * @wait_for_threads:    wait queue for sync_irq to wait for threaded handlers
 * @dir:        /proc/irq/ procfs entry
 * @name:        flow handler name for /proc/interrupts output
 */
struct irq_desc {
    struct irq_data        irq_data;
    struct timer_rand_state *timer_rand_state;
    unsigned int __percpu    *kstat_irqs;
    irq_flow_handler_t    handle_irq;
#ifdef CONFIG_IRQ_PREFLOW_FASTEOI
    irq_preflow_handler_t    preflow_handler;
#endif
    struct irqaction    *action;    /* IRQ action list */                               //handle链表,挂接的irq handle
    unsigned int        status_use_accessors;
    unsigned int        core_internal_state__do_not_mess_with_it;
    unsigned int        depth;        /* nested irq disables */
    unsigned int        wake_depth;    /* nested wake enables */
    unsigned int        irq_count;    /* For detecting broken IRQs */
    unsigned long        last_unhandled;    /* Aging timer for unhandled count */
    unsigned int        irqs_unhandled;
    raw_spinlock_t        lock;
    struct cpumask        *percpu_enabled;
#ifdef CONFIG_SMP
    const struct cpumask    *affinity_hint;
    struct irq_affinity_notify *affinity_notify;
#ifdef CONFIG_GENERIC_PENDING_IRQ
    cpumask_var_t        pending_mask;
#endif
#endif
    unsigned long        threads_oneshot;
    atomic_t        threads_active;
    wait_queue_head_t       wait_for_threads;
#ifdef CONFIG_PROC_FS
    struct proc_dir_entry    *dir;
#endif
    struct module        *owner;
    const char        *name;
} ____cacheline_internodealigned_in_smp;


#回到irq_set_chip函数,继续往下


    desc->irq_data.chip = chip;
把刚才的irq芯片操作函数填充进来,


#回到irq_set_chip_and_handler_name函数


继续往下


void
__irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
          const char *name)
{
    unsigned long flags;
    struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);
 
    if (!desc)                                                //已填充
        return;
 
    if (!handle) {
        handle = handle_bad_irq;    //如果没有handle,那么赋予一个默认的handle,应当是个处理该错误的handle
    } else {
        if (WARN_ON(desc->irq_data.chip == &no_irq_chip))
            goto out;
    }
 
    /* Uninstall? */
    if (handle == handle_bad_irq) {
        if (desc->irq_data.chip != &no_irq_chip)                  //刚才填充的chip结构
            mask_ack_irq(desc);
        irq_state_set_disabled(desc);
        desc->depth = 1;
    }
    desc->handle_irq = handle;        //赋值为传入的handle
    desc->name = name;                    //********
 
    if (handle != handle_bad_irq && is_chained) {
        irq_settings_set_noprobe(desc);
        irq_settings_set_norequest(desc);
        irq_settings_set_nothread(desc);
        irq_startup(desc);
    }
out:
    irq_put_desc_busunlock(desc, flags);
}


看一下通用的几个handle,举个例子


handle_level_irq


kernel/irq/chip.c
/**
 *    handle_level_irq – Level type irq handler
 *    @irq:    the interrupt number
 *    @desc:    the interrupt description structure for this irq
 *
 *    Level type interrupts are active as long as the hardware line has
 *    the active level. This may require to mask the interrupt and unmask
 *    it after the associated handler has acknowledged the device, so the
 *    interrupt line is back to inactive.
 */
void
handle_level_irq(unsigned int irq, struct irq_desc *desc)
{
    raw_spin_lock(&desc->lock);   
    mask_ack_irq(desc);
 
    if (unlikely(irqd_irq_inprogress(&desc->irq_data)))
        if (!irq_check_poll(desc))
            goto out_unlock;
 
    desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);   //清空等待,replay标志,现在正在处理
    kstat_incr_irqs_this_cpu(irq, desc);
 
    /*
     * If its disabled or no action available
     * keep it masked and get out of here
     */
    if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data)))
        goto out_unlock;
 
    handle_irq_event(desc);
 
    if (!irqd_irq_disabled(&desc->irq_data) && !(desc->istate & IRQS_ONESHOT))
        unmask_irq(desc);
out_unlock:
    raw_spin_unlock(&desc->lock);
}


##########


irqreturn_t handle_irq_event(struct irq_desc *desc)
{
    struct irqaction *action = desc->action;
    irqreturn_t ret;
 
    desc->istate &= ~IRQS_PENDING;
    irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);  //处理中
    raw_spin_unlock(&desc->lock);
 
    ret = handle_irq_event_percpu(desc, action);
 
    raw_spin_lock(&desc->lock);
    irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);
    return ret;
}


###########


/kernel/irq/handle.c
irqreturn_t
handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action)
{
    irqreturn_t retval = IRQ_NONE;
    unsigned int random = 0, irq = desc->irq_data.irq;
 
    do {
        irqreturn_t res;
 
        trace_irq_handler_entry(irq, action);
        res = action->handler(irq, action->dev_id);             //回调action的hanler,即链表结构,这个结构是register的时候注册的
        trace_irq_handler_exit(irq, action, res);
 
        if (WARN_ONCE(!irqs_disabled(),”irq %u handler %pF enabled interrupts\n”,
                  irq, action->handler))
            local_irq_disable();
 
        switch (res) {
        case IRQ_WAKE_THREAD:
            /*
             * Catch drivers which return WAKE_THREAD but
             * did not set up a thread function
             */
            if (unlikely(!action->thread_fn)) {
                warn_no_thread(irq, action);
                break;
            }
 
            irq_wake_thread(desc, action);
 
            /* Fall through to add to randomness */
        case IRQ_HANDLED:
            random |= action->flags;
            break;
 
        default:
            break;
        }
 
        retval |= res;
        action = action->next;
    } while (action);
 
    if (random & IRQF_SAMPLE_RANDOM)
        add_interrupt_randomness(irq);
 
    if (!noirqdebug)
        note_interrupt(irq, desc, retval);
    return retval;
}


在start_kernel中还有一个


early_irq_init(); 函数


在 kernel/irq/irqdesc.c
int __init early_irq_init(void)
{
    int count, i, node = first_online_node;
    struct irq_desc *desc;
 
    init_irq_default_affinity();
 
    printk(KERN_INFO “NR_IRQS:%d\n”, NR_IRQS);
 
    desc = irq_desc;
    count = ARRAY_SIZE(irq_desc);
 
    for (i = 0; i < count; i++) {
        desc[i].kstat_irqs = alloc_percpu(unsigned int);
        alloc_masks(&desc[i], GFP_KERNEL, node);
        raw_spin_lock_init(&desc[i].lock);
        lockdep_set_class(&desc[i].lock, &irq_desc_lock_class);
        desc_set_defaults(i, &desc[i], node, NULL);
    }
    return arch_early_irq_init();   //平台相关结构arm为NULL


//初始设置成默认值
#irqdesc.h  struct irq_desc irq_desc[NR_IRQS];
static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node,
        struct module *owner)
{
    int cpu;
 
    desc->irq_data.irq = irq;
    desc->irq_data.chip = &no_irq_chip;
    desc->irq_data.chip_data = NULL;
    desc->irq_data.handler_data = NULL;
    desc->irq_data.msi_desc = NULL;
    irq_settings_clr_and_set(desc, ~0, _IRQ_DEFAULT_INIT_FLAGS);
    irqd_set(&desc->irq_data, IRQD_IRQ_DISABLED);
    desc->handle_irq = handle_bad_irq;
    desc->depth = 1;
    desc->irq_count = 0;
    desc->irqs_unhandled = 0;
    desc->name = NULL;
    desc->owner = owner;
    for_each_possible_cpu(cpu)
        *per_cpu_ptr(desc->kstat_irqs, cpu) = 0;
    desc_smp_init(desc, node);
}


 总结:简单浏览了系统启动时候板级相关部分的c代码,下一篇会介绍一下向量表部分。


Thanks

继续上一篇,看一下中断向量表初始化部分:


代码在init/main.c-start_kernel/setup_arch(&command_line);


early_trap_init


调用arch/arm/kernel/traps.c中


void __init early_trap_init(void)
{
#if defined(CONFIG_CPU_USE_DOMAINS)
    unsigned long vectors = CONFIG_VECTORS_BASE;         //可以配置trap基地址
#else
    unsigned long vectors = (unsigned long)vectors_page;
#endif
    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;

    /*
     * 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.
     */

        //将entry-armv.s对应部分copy到该地址,
    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);

    /*
     * Do processor specific fixups for the kuser helpers
     */
    kuser_get_tls_init(vectors);

    /*
     * Copy signal return handlers into the vector page, and
     * set sigreturn to be a pointer to these.
     */
    memcpy((void *)(vectors + KERN_SIGRETURN_CODE – CONFIG_VECTORS_BASE),
           sigreturn_codes, sizeof(sigreturn_codes));
    memcpy((void *)(vectors + KERN_RESTART_CODE – CONFIG_VECTORS_BASE),
           syscall_restart_code, sizeof(syscall_restart_code));

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


#在arch/arm/kernel/entry-armv.s中
__vectors_start:
 ARM(    swi    SYS_ERROR0    )
 THUMB(    svc    #0        )
 THUMB(    nop            )                        //复位
    W(b)    vector_und + stubs_offset       //未定义模式
    W(ldr)    pc, .LCvswi + stubs_offset   //swi,系统调用进入
    W(b)    vector_pabt + stubs_offset       //指令预取
    W(b)    vector_dabt + stubs_offset       //data
    W(b)    vector_addrexcptn + stubs_offset //reserve
    W(b)    vector_irq + stubs_offset       //irq
    W(b)    vector_fiq + stubs_offset       //fiq,没有用到

    .globl    __vectors_end
__vectors_end:

.equ    stubs_offset, __vectors_start + 0x200 – __stubs_start


#先看宏:
/*
 * Vector stubs.
 *
 * This code is copied to 0xffff0200 so we can use branches in the
 * vectors, rather than ldr’s.  Note that this code must not
 * exceed 0x300 bytes.
 *
 * Common stub entry macro:
 *   Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
 *
 * SP points to a minimal amount of processor-private memory, the address
 * of which is copied into r0 for the mode specific abort handler.
 */
    .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 | PSR_ISETSTATE)
    msr    spsr_cxsf, r0

    @
    @ the branch table must immediately follow this code
    @
    and    lr, lr, #0x0f
 THUMB(    adr    r0, 1f            )
 THUMB(    ldr    lr, [r0, lr, lsl #2]    )
    mov    r0, sp
 ARM(    ldr    lr, [pc, lr, lsl #2]    )
    movs    pc, lr            @ branch to handler in SVC mode

#对应irq定义
/*
 * 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


#代进去可以得到:
vector_irq:
    .if 4
    sub    lr, lr, #4
    .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, #(\IRQ_MODE ^ SVC_MODE | PSR_ISETSTATE)
    msr    spsr_cxsf, r0

    @
    @ the branch table must immediately follow this code
    @
    and    lr, lr, #0x0f
 THUMB(    adr    r0, 1f            )
 THUMB(    ldr    lr, [r0, lr, lsl #2]    )
    mov    r0, sp
 ARM(    ldr    lr, [pc, lr, lsl #2]    ) @如果进入中断前是usr,
                                                            则取出PC+4*0的内容,即__irq_usr @如果进入中断前 是svc,则取出PC+4*3的内容,即__irq_svc
                                                        @根据进入前的状态判断进入哪个处理流程
    movs    pc, lr                               @ branch to handler in SVC mode
ENDPROC(vector_irq)


=================

#__irq_usr:
    usr_entry
    kuser_cmpxchg_check
    irq_handler
    get_thread_info tsk    @得到当前进程信息
    mov    why, #0

    b    ret_to_user_from_irq


=================


/*
 * Interrupt handling.
 */
    .macro    irq_handler
#ifdef CONFIG_MULTI_IRQ_HANDLER
    ldr    r1, =handle_arch_irq
    mov    r0, sp
    ldr    r1, [r1]
    adr    lr, BSYM(9997f)
    teq    r1, #0
    movne    pc, r1
#endif
    arch_irq_handler_default
9997:
    .endm


=================


###arch/arm/kernel/setup.c
handle_arch_irq = mdesc->handle_irq;
####arch/arm/include/asm/entry-macro-multi.s
/*
 * Interrupt handling.  Preserves r7, r8, r9
 */
    .macro    arch_irq_handler_default
    get_irqnr_preamble r6, lr
1:    get_irqnr_and_base r0, r2, r6, lr                             //******取得中断号,根据平台相关中断状态寄存器地址
    movne    r1, sp
    @
    @ routine called with r0 = irq number, r1 = struct pt_regs *
    @
    adrne    lr, BSYM(1b)
    bne    asm_do_IRQ

##arm/kernel/irq.c


继续##arm/kernel/irq.c
/*
 * asm_do_IRQ is the interface to be used from assembly code.
 */
asmlinkage void __exception_irq_entry
asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{
    handle_IRQ(irq, regs);
}


/*
 *  handles all hardware IRQ’s.  Decoded IRQs should
 * not come via this function.  Instead, they should provide their
 * own ‘handler’.  Used by platform code implementing C-based 1st
 * level decoding.
 */
void handle_IRQ(unsigned int irq, struct pt_regs *regs)
{
    struct pt_regs *old_regs = set_irq_regs(regs);

    irq_enter();

    /*
     * Some hardware gives randomly wrong interrupts.  Rather
     * than crashing, do something sensible.
     */
    if (unlikely(irq >= nr_irqs)) {
        if (printk_ratelimit())
            printk(KERN_WARNING “Bad IRQ%u\n”, irq);
        ack_bad_irq(irq);
    } else {
        generic_handle_irq(irq);
    }

    /* AT91 specific workaround */
    irq_finish(irq);

    irq_exit();
    set_irq_regs(old_regs);
}

#kernel/irq/irqdesc.c
/**
 * generic_handle_irq – Invoke the handler for a particular irq
 * @irq:    The irq number to handle
 *
 */
int generic_handle_irq(unsigned int irq)
{
    struct irq_desc *desc = irq_to_desc(irq);

    if (!desc)
        return -EINVAL;
    generic_handle_irq_desc(irq, desc);
    return 0;
}
EXPORT_SYMBOL_GPL(generic_handle_irq);
#include/linux/irqdesc.h
static inline void generic_handle_irq_desc(unsigned int irq, struct irq_desc *desc)
{
    desc->handle_irq(irq, desc);
}

最后会调用handle_irq。

赞(0) 打赏
转载请注明出处:服务器评测 » Linux中断导读之一–初始化<1>
分享到: 更多 (0)

听说打赏我的人,都进福布斯排行榜啦!

支付宝扫一扫打赏

微信扫一扫打赏