打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
中断代码跟踪
 

init/main.c

arch/x86/kernel/traps.c

trap_init(void)

arch/x86/kernel/traps.c

set_intr_gate(0, &divide_error);

set_intr_gate(19, &simd_coprocessor_error);

 

arch/x86/include/asm/irq_vectors.h

# define SYSCALL_VECTOR         0x80

set_system_trap_gate(SYSCALL_VECTOR, &system_call);

/*

 * This needs to use 'idt_table' rather than 'idt', and

 * thus use the _nonmapped_ version of the IDT, as the

 * Pentium F0 0F bugfix can have resulted in the mapped

 * IDT being write-protected.

 */

arch/x86/include/asm/desc.h

static inline void set_intr_gate(unsigned int n, void *addr)

{

BUG_ON((unsigned)n > 0xFF);

_set_gate(n, GATE_INTERRUPT, addr, 0, 0, __KERNEL_CS);

}

 

arch/x86/include/asm/desc_defs.h

#ifdef CONFIG_X86_64

typedef struct gate_struct64 gate_desc;

#else

 typedef struct desc_struct gate_desc;

#endif

 

arch/x86/include/asm/irq_vectors.h

#define NR_VECTORS                         256

arch/x86/kernel/traps.c

 gate_desc idt_table[NR_VECTORS] __page_aligned_data = { { { { 0, 0 } } }, };

static inline void _set_gate(int gate, unsigned type, void *addr,

                   unsigned dpl, unsigned ist, unsigned seg)

{

gate_desc s;

pack_gate(&s, type, (unsigned long)addr, dpl, ist, seg);

 /* 

* does not need to be atomic because it is only done once at

* setup time

 */

write_idt_entry(idt_table, gate, &s);

}

 

arch/x86/include/asm/desc.h

#define write_idt_entry(dt, entry, g)       \

native_write_idt_entry(dt, entry, g)

 

static inline void native_write_idt_entry(gate_desc *idt, int entry,

                        const gate_desc *gate)

{

memcpy(&idt[entry], gate, sizeof(*gate));

}

 

arch/x86/kernel/irqinit.c

init_IRQ(void)

x86_init.irqs.intr_init();

arch/x86/kernel/x86_init.c

.pre_vector_init    = init_ISA_irqs,

.intr_init      = native_init_IRQ,

 

void __init native_init_IRQ(void)

x86_init.irqs.pre_vector_init();

arch/x86/kernel/irqinit.c

void __init init_ISA_irqs(void)

      for (i = 0; i < legacy_pic->nr_legacy_irqs; i++) {

          struct irq_desc *desc = irq_to_desc(i);

         

          desc->status = IRQ_DISABLED;

          desc->action = NULL;

          desc->depth = 1;

 

          kernel/irq/chip.c

          set_irq_chip_and_handler_name(i, &i8259A_chip,

                            handle_level_irq, "XT");

      set_irq_chip(irq, chip);

      __set_irq_handler(irq, handle, 0, name);

 

      }

 

arch/x86/include/asm/irq_vectors.h

#define FIRST_EXTERNAL_VECTOR       0x20

for (i = FIRST_EXTERNAL_VECTOR; i < NR_VECTORS; i++) {

if (!test_bit(i, used_vectors))

set_intr_gate(i, interrupt[i-FIRST_EXTERNAL_VECTOR]);//中断处理函数

}

 

arch/x86/kernel/entry_32.S

  /*

   * Build the entry stubs and pointer table with some assembler magic.

   * We pack 7 stubs into a single 32-byte chunk, which will fit in a

   * single cache line on all modern x86 implementations.

   */

  .section .init.rodata,"a"

  ENTRY(interrupt)

  .text

      .p2align 5

      .p2align CONFIG_X86_L1_CACHE_SHIFT

  ENTRY(irq_entries_start) 

      RING0_INT_FRAME

  vector=FIRST_EXTERNAL_VECTOR

  .rept (NR_VECTORS-FIRST_EXTERNAL_VECTOR+6)/7

      .balign 32

    .rept 7

      .if vector < NR_VECTORS

        .if vector <> FIRST_EXTERNAL_VECTOR

      CFI_ADJUST_CFA_OFFSET -4

        .endif

  1:  pushl $(~vector+0x80)   /* Note: always in signed byte range */

      CFI_ADJUST_CFA_OFFSET 4

        .if ((vector-FIRST_EXTERNAL_VECTOR)%7) <> 6

      jmp 2f

        .endif

        .previous

      .long 1b

        .text

  vector=vector+1

      .endif

    .endr

  2:  jmp common_interrupt

  .endr

  END(irq_entries_start)

 

  .previous

  END(interrupt)

  .previous

 

  /*

   * the CPU automatically disables interrupts when executing an IRQ vector,

   * so IRQ-flags tracing has to follow that:

   */

      .p2align CONFIG_X86_L1_CACHE_SHIFT

  common_interrupt:

      addl $-0x80,(%esp)  /* Adjust vector into the [-256,-1] range */

      SAVE_ALL

      TRACE_IRQS_OFF

      movl %esp,%eax

      call do_IRQ

      jmp ret_from_intr

  ENDPROC(common_interrupt)

 

中断处理入口http://www.cnblogs.com/hustcat/archive/2009/08/14/1546011.html

由上节可知,中断向量的对应的处理程序位于interrupt数组中,下面来看看interrupt:

341 .data #数据段

342 ENTRY(interrupt)

343 .text

344

345 vector=0

346 ENTRY(irq_entries_start)

347 .rept NR_IRQS #348-354行重复NR_IRQS次

348 ALIGN

349 1: pushl $vector-256 #vector在354行递增

350 jmp common_interrupt #所有的外部中断处理函数的统一部分,以后再讲述

351 .data

352 .long 1b #存储着指向349行的地址,但是随着348行-354被gcc展开,每次的值都不同

353 .text

354 vector=vector+1

355 .endr #与347行呼应

356

357 ALIGN

 

#公共处理函数

common_interrupt:

SAVE_ALL /*寄存器值入栈*/

movl %esp,%eax /*栈顶指针保存到eax*/

call do_IRQ /*处理中断*/

jmp ret_from_intr /*从中断返回*/

分析如下:

首先342行和352行都处于.data段,虽然看起来它们是隔开的,但实际上被gcc安排在了连续的数据段内存中,同理在代码段内存中,354行与350行的指令序列也是连续存储的。另外,348-354行会被gcc展开NR_IRQS次,因此每次352行都会存储一个新的指针,该指针指向每个349行展开的新对象。最后在代码段内存中连续存储了NR_IRQS个代码片断,首地址由 irq_entries_start指向。而在数据段内存中连续存储了NR_IRQS个指针,首址存储在interrupt这个全局变量中。这样,例如 IRQ号是0 (从init_IRQ()调用,它对应的中断向量是FIRST_EXTERNAL_VECTOR)的中断通过中断门后会触发 interrput[0],从而执行:

pushl 0-256

jmp common_interrupt

的代码片断,进入到Linux内核安排好的中断入口路径。

 

中断回调函数基本格式如下

1:  pushl $(~vector+0x80)

jmp common_interrupt

SAVE_ALL

call do_IRQ

jmp ret_from_intr

 

arch/x86/kernel/irq.c

  /*

   * do_IRQ handles all normal device IRQ's (the special

   * SMP cross-CPU interrupts have their own specific

   * handlers).

   */

  unsigned int __irq_entry do_IRQ(struct pt_regs *regs)

irq_enter();

arch/x86/kernel/irq_32.c

handle_irq(irq, regs)

desc = irq_to_desc(irq);

if (!execute_on_irq_stack(overflow, desc, irq)) {

          if (unlikely(overflow))

              print_stack_overflow();

          desc->handle_irq(irq, desc);

      } 

 

irq_exit();

 

  arch/x86/kernel/irq_32.c

     execute_on_irq_stack(int overflow, struct irq_desc *desc, int irq)

  {

      union irq_ctx *curctx, *irqctx;

      u32 *isp, arg1, arg2;

 

      curctx = (union irq_ctx *) current_thread_info();

      irqctx = __get_cpu_var(hardirq_ctx);

     

      /* 

       * this is where we switch to the IRQ stack. However, if we are

       * already using the IRQ stack (because we interrupted a hardirq

       * handler) we can't do that and just have to keep using the

       * current stack (which is the irq stack already after all)

       */

      if (unlikely(curctx == irqctx))

          return 0;

         

      /* build the stack frame on the IRQ stack */

      isp = (u32 *) ((char *)irqctx + sizeof(*irqctx));

      irqctx->tinfo.task = curctx->tinfo.task;

      irqctx->tinfo.previous_esp = current_stack_pointer;

     

      /*

        * Copy the softirq bits in preempt_count so that the

       * softirq checks work in the hardirq context.

       */

      irqctx->tinfo.preempt_count =

          (irqctx->tinfo.preempt_count & ~SOFTIRQ_MASK) |

          (curctx->tinfo.preempt_count & SOFTIRQ_MASK);

 

      if (unlikely(overflow))

          call_on_stack(print_stack_overflow, isp);

/*

  *  isp为上边计算出的栈地址,将其存入ebx,并通过xchgl存入esp,从而将原来的栈地址保存到

  *  ebx,而新栈变为isp, 在新栈上执行desc->handle_irq,执行结束后,从ebx中恢复原栈。

  */

      asm volatile("xchgl %%ebx,%%esp \n"

               "call  *%%edi      \n"

               "movl  %%ebx,%%esp \n"

               : "=a" (arg1), "=d" (arg2), "=b" (isp)

               :  "0" (irq),   "1" (desc),  "2" (isp),

              "D" (desc->handle_irq)

               : "memory", "cc", "ecx");

      return 1;

  }

 

typedef struct irq_desc {

unsigned int status; /* IRQ status */

hw_irq_controller *handler;

struct irqaction *action; /* IRQ action list */

unsigned int depth; /* nested irq disables */

unsigned int irq_count; /* For detecting broken interrupts */

unsigned int irqs_unhandled;

spinlock_t lock;

} ____cacheline_aligned irq_desc_t;

 

desc->handle_irq的初始化过程如下:

arch/x86/kernel/irqinit.c

init_IRQ(void)

x86_init.irqs.intr_init();

void __init native_init_IRQ(void)

x86_init.irqs.pre_vector_init();

arch/x86/kernel/irqinit.c

void __init init_ISA_irqs(void)

set_irq_chip_and_handler_name

desc->handle_irq=handle_level_irq

 

handle_level_irq(unsigned int irq, struct irq_desc *desc)

mask_ack_irq(desc, irq);

desc->chip->mask_ack(irq);

if (unlikely(desc->status & IRQ_INPROGRESS))

goto out_unlock;

action = desc->action;

desc->status |= IRQ_INPROGRESS;

 

kernel/irq/handle.c

handle_IRQ_event(irq, action);

if (!(action->flags & IRQF_DISABLED))

          local_irq_enable_in_hardirq();

do {

action->handler(irq, action->dev_id);

action = action->next;

} while (action);

desc->status &= ~IRQ_INPROGRESS;

if (!(desc->status & (IRQ_DISABLED | IRQ_ONESHOT)))

          unmask_irq(desc, irq);

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Linux中断处理体系结构分析
基于ARM Linux中断、异常的处理分析
【转】ARM Linux静态映射分析
POWERPC中断
x86_64 kernel stacks (kernel 4.16)
嵌入式Linux内核移植相关代码分析(zz)
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服