2009年1月7日水曜日

タイマー割り込みが CPU に通知されるまで

前回の記事の続き。

main_loop_wait (vl.c) の中でイベントの有無がチェックされる。最後に bottom-halves がチェックされる。
void main_loop_wait(int timeout)
{


...

    /* Check bottom-halves last in case any of the earlier events triggered them. */
    qemu_bh_poll();
}

int qemu_bh_poll(void)
{
    QEMUBH *bh, **pbh;
    int ret;

    ret = 0;
    for(;;) {
        pbh = &first_bh;
        bh = *pbh;
        if (!bh)
            break;
        ret = 1;
        *pbh = bh->next;
        bh->scheduled = 0;
        bh->cb(bh->opaque);
    }
    return ret;
}
bh->cb(bh->opaque)  から sp804 という arm のタイマーモジュール (hw/arm_timer.c) に入っていく。cb は QEMUBHFunc * 型 (qemu-common.h:typedef void QEMUBHFunc(void *opaque);) の関数ポインタで、arm_timer_tick() が入ってる。opaque には arm_timer_state * が入ってる。
static void arm_timer_tick(void *opaque)
{
    arm_timer_state *s = (arm_timer_state *)opaque;
    s->int_level = 1;
    arm_timer_update(s);
}

typedef struct {
    ptimer_state *timer;
    uint32_t control;
    uint32_t limit;
    int freq;
    int int_level;
    qemu_irq irq;
} arm_timer_state;

/* Check all active timers, and schedule the next timer interrupt.  */

static void arm_timer_update(arm_timer_state *s)
{
    /* Update interrupts.  */
    if (s->int_level && (s->control & TIMER_CTRL_IE)) {
        qemu_irq_raise(s->irq);
    } else {
        qemu_irq_lower(s->irq);
    }
}
qemu_irq_raise は qemu_set_irq(irq, 1)。そして irq->handler(irq->opaque, irq->n, 1) となる。
handler には sp804_set_irq() 、opaque には sp804_state が入ってる。
typedef struct {
    void *timer[2];
    int level[2];
    uint32_t base;
    qemu_irq irq;
} sp804_state;

/* Merge the IRQs from the two component devices.  */
static void sp804_set_irq(void *opaque, int irq, int level)
{
    sp804_state *s = (sp804_state *)opaque;

    s->level[irq] = level;
    qemu_set_irq(s->irq, s->level[0] || s->level[1]);
}
そしてまた qemu_set_irq から handler が呼び出されるという流れで、ARM の GIC (Generic Interrupt Controller) の gic_set_irq (arm_gic.c) が呼び出される。
/* Process a change in an external IRQ input.  */
static void gic_set_irq(void *opaque, int irq, int level)
{
    gic_state *s = (gic_state *)opaque;
    /* The first external input line is internal interrupt 32.  */
    irq += 32;
    if (level == GIC_TEST_LEVEL(irq, ALL_CPU_MASK))
        return;

    if (level) {
        GIC_SET_LEVEL(irq, ALL_CPU_MASK);
        if (GIC_TEST_TRIGGER(irq) || GIC_TEST_ENABLED(irq)) {
            DPRINTF("Set %d pending mask %x\n", irq, GIC_TARGET(irq));
            GIC_SET_PENDING(irq, GIC_TARGET(irq));
        }
    } else {
        GIC_CLEAR_LEVEL(irq, ALL_CPU_MASK);
    }
    gic_update(s);
}
ここで GIC_SET_PENDING で gic_state を変化させて gic_update() に行く。
/* TODO: Many places that call this routine could be optimized.  */
/* Update interrupt status after enabled or pending bits have been changed.  */
static void gic_update(gic_state *s)
{
    int best_irq;
    int best_prio;
    int irq;
    int level;
    int cpu;
    int cm;

    for (cpu = 0; cpu <>
        cm = 1 <<>
        s->current_pending[cpu] = 1023;
        if (!s->enabled || !s->cpu_enabled[cpu]) {
   qemu_irq_lower(s->parent_irq[cpu]);
            return;
        }
        best_prio = 0x100;
        best_irq = 1023;
        for (irq = 0; irq <>
            if (GIC_TEST_ENABLED(irq) && GIC_TEST_PENDING(irq, cm)) {
                if (GIC_GET_PRIORITY(irq, cpu) <>
                    best_prio = GIC_GET_PRIORITY(irq, cpu);
                    best_irq = irq;
                }
            }
        }
        level = 0;
        if (best_prio <= s->priority_mask[cpu]) {
            s->current_pending[cpu] = best_irq;
            if (best_prio <>running_priority[cpu]) {
                DPRINTF("Raised pending IRQ %d\n", best_irq);
                level = 1;
            }
        }
        qemu_set_irq(s->parent_irq[cpu], level);
    }
}
qemu_set_irq(s->parent_irq[cpu], level); から、parent_irq[0] (CPU 1 つの場合) のハンドラを呼び出して、arm_pic_cpu_handler に行く。opaque は CPUState (ARMCPUState) で、これが最後。
最終的に cpu_interrupt(env, CPU_INTERRUPT_HARD) が呼ばれて、タイマー割り込みが CPU に通知される。

そして再び main_loop で cpu_exec(env) を実行して、その中で割り込み pending が入ってるから longjump で巻き戻して、という流れで割り込みを処理する。

0 件のコメント:

コメントを投稿