2009年3月6日 星期五

Linux Interrupts and Exceptions - 2

下圖為 x86 處理器的 256 個中斷配置,其中 vector 編號 0-19 是前面提過的 Exception
編號 32-127 是硬體裝置的 I/O Interrupt,而編號 128 則是由 Linux 核心作為 System Call 之用


在 PC 架構中,有幾個裝置的中斷訊號必須分配到中斷控制器的固定 IRQ Line
如下圖的 IRQ 0、2 及 13
IRQ 欄位表示硬體的 IRQ Line,INT 欄位表示 Linux 核心使用的 IRQ Number

上述的 I/O Interrupt 及其處理函式被儲存於 Linux 核心的 irq_desc 陣列中
變數型態為 struct irq_desc (或 irq_desc_t),陣列大小為 NR_IRQS

定義於 kernel/irq/handle.c

irq_desc_t irq_desc[NR_IRQS] __cacheline_aligned = {
  [0 ... NR_IRQS-1] = {
    .handler = &no_irq_type,
    .lock = SPIN_LOCK_UNLOCKED
  }
};


定義於 include/linux/irq.h

typedef struct irq_desc {
  hw_irq_controller *handler;
  void *handler_data;
  struct irqaction *action;  /* IRQ action list */
  unsigned int status;    /* IRQ status */
  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;


定義於 include/asm-i386/mach-default/irq_vectors_limits.h

#ifdef CONFIG_X86_IO_APIC
#define NR_IRQS 224
# if (224 >= 32 * NR_CPUS)
# define NR_IRQ_VECTORS NR_IRQS
# else
# define NR_IRQ_VECTORS (32 * NR_CPUS)
# endif
#else
#define NR_IRQS 16
#define NR_IRQ_VECTORS NR_IRQS
#endif


以上可看出,若系統使用 APIC 來管理裝置中斷時,IRQ 的個數為 224 個,否則為 16 個
當系統初始化時,核心呼叫 init_IRQ() 函式將 irq_desc 陣列中的 status 欄位設定為 IRQ_DISABLED
並且呼叫 set_intr_gate() 函式設定 CPU 的 Interrupt Gate,這個動作就是設定中斷發生時的處理函式

定義於 arch/i386/kernel/i8259.c

void __init init_IRQ(void)
{
  int i;

  /* all the set up before the call gates are initialised */
  pre_intr_init_hook();

  /*
   * Cover the whole vector space, no vector can escape
   * us. (some of these will be overridden and become
   * 'special' SMP interrupts)
   */
  for (i = 0; i < (NR_VECTORS - FIRST_EXTERNAL_VECTOR); i++) {
    int vector = FIRST_EXTERNAL_VECTOR + i;
    if (i >= NR_IRQS)
      break;
    if (vector != SYSCALL_VECTOR)
      set_intr_gate(vector, interrupt[i]);
  }

  /* setup after call gates are initialised (usually add in
   * the architecture specific gates)
   */
  intr_init_hook();

  /*
   * Set the clock to HZ Hz, we already have a valid
   * vector now:
   */
  setup_pit_timer();

  /*
   * External FPU? Set up irq13 if so, for
   * original braindamaged IBM FERR coupling.
   */
  if (boot_cpu_data.hard_math && !cpu_has_fpu)
    setup_irq(FPU_IRQ, &fpu_irq);

  irq_ctx_init(smp_processor_id());
}


void __init init_ISA_irqs (void)
{
  int i;

#ifdef CONFIG_X86_LOCAL_APIC
  init_bsp_APIC();
#endif
  init_8259A(0);

  for (i = 0; i < NR_IRQS; i++) {
    irq_desc[i].status = IRQ_DISABLED;
    irq_desc[i].action = NULL;
    irq_desc[i].depth = 1;

    if (i < 16) {
      /*
       * 16 old-style INTA-cycle interrupts:
       */
      irq_desc[i].handler = &i8259A_irq_type;
    } else {
      /*
       * 'high' PCI IRQs filled in on demand
       */
      irq_desc[i].handler = &no_irq_type;
    }
  }
}

沒有留言: