可编程控制器APIC的作用就是观察IRQ线,并且将接收的IRQ信号转换为中断向量.
IRQ描述符(irq_desc_t)
irq_desc[]数组包含NR_IRQS个IRQ描述符,和IDT中的门描述符一一对应,IRQ描述符提供服务的PIC电路信息,当前IRQ的状态.
typedef struct irq_desc {
unsigned int status; /* IRQ status */
hw_irq_controller *handler;//指向与pic电路的I/O交互函数
struct msi_desc *msi_desc;
struct irqaction *action; /* IRQ action list */
int irq;//irq号
spinlock_t lock;
struct arch_irq_desc arch;
cpumask_var_t affinity;
/* irq ratelimit */
s_time_t rl_quantum_start;
unsigned int rl_cnt;
struct list_head rl_link;
} __cacheline_aligned irq_desc_t;
IDT的建立,IDT的第一次初始化是在进入保护模式之前.
/* Initialise IDT with simple error defaults. */
leaq ignore_int(%rip),%rcx
movl %ecx,%eax
andl $0xFFFF0000,%eax
orl $0x00008E00,%eax
shlq $32,%rax
movl %ecx,%edx
andl $0x0000FFFF,%edx
orl $(__HYPERVISOR_CS64<<16),%edx
orq %rdx,%rax
shrq $32,%rcx
movl %ecx,%edx
leaq idt_table(%rip),%rdi
movl $256,%ecx
1: movq %rax,(%rdi)
movq %rdx,8(%rdi)
addq $16,%rdi
loop 1b
第二次初始化
void __init init_IRQ(void)
{
int vector, irq, cpu = smp_processor_id();
init_bsp_APIC();
init_8259A(0);
BUG_ON(init_irq_data() < 0);
for ( vector = FIRST_DYNAMIC_VECTOR; vector < NR_VECTORS; vector++ )
{
if (vector == HYPERCALL_VECTOR || vector == LEGACY_SYSCALL_VECTOR)
continue;
BUG_ON(!interrupt[vector]);
set_intr_gate(vector, interrupt[vector]);
}
for (irq = 0; platform_legacy_irq(irq); irq++) {
struct irq_desc *desc = irq_to_desc(irq);
if ( irq == 2 ) /* IRQ2 doesn't exist */
continue;
desc->handler = &i8259A_irq_type;
per_cpu(vector_irq, cpu)[FIRST_LEGACY_VECTOR + irq] = irq;
cpumask_copy(desc->arch.cpu_mask, cpumask_of(cpu));
desc->arch.vector = FIRST_LEGACY_VECTOR + irq;
}
per_cpu(vector_irq, cpu)[IRQ0_VECTOR] = 0;
apic_intr_init();
/* Set the clock to HZ Hz */
#define CLOCK_TICK_RATE 1193182 /* crystal freq (Hz) */
#define LATCH (((CLOCK_TICK_RATE)+(HZ/2))/HZ)
outb_p(0x34, PIT_MODE); /* binary, mode 2, LSB/MSB, ch 0 */
outb_p(LATCH & 0xff, PIT_CH0); /* LSB */
outb(LATCH >> 8, PIT_CH0); /* MSB */
setup_irq(2, &cascade);
}
第二次初始化为中断设置了一个公共的处理函数common_interrupt,该函数主要完成3项工作,保存寄存器的值,调用do_IRQ,跳转到中断返回函数ret_from_intr
#define BUILD_COMMON_IRQ() \
__asm__( \
"\n" __ALIGN_STR"\n" \
"common_interrupt:\n\t" \
STR(SAVE_ALL) "\n\t" \
"movq %rsp,%rdi\n\t" \
"callq " STR(do_IRQ) "\n\t" \
"jmp ret_from_intr\n");
#define BUILD_IRQ(nr) \
"pushq $0\n\t" \
"movl $"#nr",4(%rsp)\n\t" \
"jmp common_interrupt"
整个中断的初始化构成就完成了.
看看一个物理中断发生的响应过程是怎样的
一个中断的发生首先APIC将IRQ转化为中断向量,根据中断向量来调用IDT中的中断处理函数,即SAVE_ALL do_IRQ,ret_from_intr
do_IRQ将中断分发给每个domain去处理,domain中的物理中断和事件通道绑定,即事件通道会被设置为1,
evtchn_do_upcall----->do_IRQ---->__do_IRQ
虚拟中断没有APIC的那一部分,直接设置对应事件通道的pending位
中断由设备来申请,申请中会填写中断号对应的irq_desc结构,并将处理历程加入到中断的处理队列中.
xen中异常的处理
在xen的启动阶段,xen会设置异常的处理函数trap_init,其中会调用percpu_traps_init来完成超级调用,双重错误,NMI延迟的异常的初始化.处理程序的功能是:
保存寄存器的值,跳转到异常处理函数,返回.
guestOS的异常处理
启动时候通过超级调用初始化VIDT,异常发生的时候先由xen处理异常,如果是guest_mode则交给do_guest_trap来处理