5.純シミュレーション

4543 ワード

ハードウェア割込み発生(qemuアナログ装置)1.ハードウェアが中断したインタフェースvoid qemu_set_irq(qemu_irq irq, int level);
2.割り込みプロセスvoid qemu_set_irq(qemu_irq irq, int level) { if (!irq) return;
irq->handler(irq->opaque, irq->n, level);

}割り込みコントローラhanderを設定、大きく3つのケースに分けられる.cpu_IRqのhander==>pic_irq_request 2.カーネルシミュレーション割り込みコントローラのhander==>kvm_i8259_set_irq 3.ユーザアナログ割り込みコントローラのhander==>i 8259_set_irq
/* PC hardware initialisation */static void pc_init1() { cpu_irq = qemu_allocate_irqs(pic_irq_request, NULL, 1); #ifdef KVM_CAP_IRQCHIP if (kvm_enabled() && kvm_irqchip_in_kernel()) { isa_irq_state = qemu_mallocz(sizeof(*isa_irq_state)); isa_irq = i8259 = kvm_i8259_init(cpu_irq[0]); } else #endif { i8259 = i8259_init(cpu_irq[0]); isa_irq_state = qemu_mallocz(sizeof(*isa_irq_state)); isa_irq_state->i8259 = i8259; isa_irq = qemu_allocate_irqs(isa_irq_handler, isa_irq_state, 24); } ユーザ空間割り込みコントローラの割り込み発生過程static void i 8259_を先に検討するset_irq(void *opaque, int irq, int level) { PicState2 s = opaque; pic_set_irq1(&s->pics[irq >> 3], irq & 7, level); pic_update_irq(s); } 割り込みトリガ方式はレベルトリガとエッジトリガに分けられ、isaデバイスの多くはエッジトリガを採用し、pciデバイスはレベルトリガを採用する.エッジトリガを用いる場合、leveldが1に等しく、待機割り込み要求(pending割り込み要求なし)がない場合、割り込み要求レジスタを1とし、pending割り込み要求を1とする.pending割り込み要求がある場合、割り込み要求レジスタが設定されていない場合、割り込み損失が許可されていることがわかります./set irq level. If an edge is detected, then the IRR is set to 1 */static inline void pic_set_irq1(PicState s, int irq, int level) { int mask; mask = 1 << irq; if (s->elcr & mask) {/level triggered/if (level) { s->irr |= mask; s->last_irr |= mask; } else { s->irr &= ~mask; s->last_irr &= ~mask; } } else {/edge triggered/if (level) { if ((s->last_irr & mask) == 0) s->irr |= mask; s->last_irr |= mask; } else { s->last_irr &= ~mask; } } } 割り込み要求があるたびに、関数を呼び出す必要があります.この関数呼び出しにより、ネストが中断されます.また必ず注入中断しなければなりません.どんな場合に必須ですか?具体的にはpic_参照get_IRq()関数.この関数は、割り込みの発生優先度と、割り込みの処理中優先度を比較し、それより大きい場合は、注入要求割り込みを実行します.注入中断タイミングはqemu_irq_raiseがトリガーした関数は、以下にリストされます./raise irq to CPU if necessary. must be called every time the active irq may change */void pic_update_irq(PicState2 s) {/look at requested irq */irq = pic_get_irq(&s->pics[0]); if (irq >= 0) { qemu_irq_raise(s->parent_irq); } } また割り込みエントリにループしているように思わないでください.実際にはありません.パラメータs->parent_が鍵です.IRq,このパラメータは実際にcpu_を呼び出すIRqのhander==>pic_irq_request static inline void qemu_irq_raise(qemu_irq irq) { qemu_set_irq(irq, 1); } 現在、ユーザ状態シミュレーション割り込みコントローラi 8259(KVMシミュレーションとapic割り込みコントローラを除く)のみが検討されており、cpu_interrupt関数は、現在の仮想プロセッサの動作を実際に中断し、ハードウェアの注入を中断する準備をしています.現在は注入を中断するタイミングです.仮想プロセッサの動作を中断(一時停止)するには、この関数pthread_を使用します.kill(env->kvm_cpu_state.thread,SIG_IPI)はプロセッサの動作を中断する.static void pic_irq_request(void *opaque, int irq, int level) { CPUState *env = first_cpu; if (level) cpu_interrupt(env, CPU_INTERRUPT_HARD); else cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); } }
割り込み注入割り込み注入は、仮想割り込みコントローラが収集した割り込み要求を仮想プロセッサに注入する責任を負います.2つの問題を処理する必要があります.いつ注入しますか.どのように注入しますか.static int kvm_main_loop_cpu(CPUState *env) { while (1) { int run_cpu = !is_cpu_stopped(env); if (run_cpu && !kvm_irqchip_in_kernel()) { process_irqchip_events(env); run_cpu = !env->halted; } if (run_cpu) { kvm_cpu_exec(env); kvm_main_loop_wait(env, 0); } else { kvm_main_loop_wait(env, 1000); } } pthread_mutex_unlock(&qemu_mutex); return 0; } 割り込みコントローラがカーネル空間シミュレーション(ユーザ空間シミュレーション)でない場合は、割り込み注入を行います.
kvm_main_loop_cpu-->kvm_cpu_exec-->kvm_run
int kvm_run(CPUState *env)
{

#if !defined(__s390__)
    if (!kvm->irqchip_in_kernel)
        run->request_interrupt_window = kvm_arch_try_push_interrupts(env);
#endif
}

1.まず、3つの条件を満たす1)カーネルkvmは、割り込み注入を受ける2)割り込み要求を用意し、ハードウェア割り込み要求3)仮想プロセッサに対して割り込み(割り込み)を実行する.割り込み要求番号3を取得する.kvm注入中断要求
int kvm_arch_try_push_interrupts(void *opaque)
{
    CPUState *env = cpu_single_env;
    int r, irq;

    if (kvm_is_ready_for_interrupt_injection(env) && 
        (env->interrupt_request & CPU_INTERRUPT_HARD) &&
        (env->eflags & IF_MASK)) { 
            env->interrupt_request &= ~CPU_INTERRUPT_HARD;
            irq = cpu_get_pic_interrupt(env);
            if (irq >= 0) { 
                r = kvm_inject_irq(env, irq);
                if (r < 0)
                    printf("cpu %d fail inject %x
", env->cpu_index, irq); } } return (env->interrupt_request & CPU_INTERRUPT_HARD) != 0; }