前回の記事の続き。
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;}
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);}}
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]);}
/* 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 件のコメント:
コメントを投稿