これにより、複数のデバイスの IRQ を繋げることができるようだ。なるほど、よくできてる。
大抵は、初期化の時に cpuirq などの、CPU と直結する IRQ (ハンドラの中で cpu_interrupt() を呼ぶ) を作っておいて、これをトップレベルの parent_irq とする。
/* mem_read と mem_write は、byte にアクセスするための関数 (mem_*[0])、
word にアクセスするための関数 (mem_*[1])、そして double word にアクセス
するための関数の配列。関数は NULL 関数ポインタで省略することができる。
登録済みの関数は、後で動的に変更される可能性がある。
もし io_index が非ゼロならば、対応する I/O ゾーンが変更される。もしゼロならば、
新しい I/O ゾーンがアロケートされる。戻り値は cpu_register_physical_memory()
とともに使用することができる。もしエラーの場合は -1 が返る。*/int cpu_register_io_memory(int io_index,
CPUReadMemoryFunc **mem_read, CPUWriteMemoryFunc **mem_write,
void *opaque);
static inline void gen_op_stl_kernel(void)
{
*gen_opc_ptr++ = INDEX_op_stl_kernel;
}
...のようになっている。stl_kernel は、store long の kernel モード時の命令に対応する。72 は、オブジェクトファイル中の関数のサイズ。
DEF(stl_user, 0, 80)
...
DEF(stl_kernel, 0, 72)
enum {
#define DEF(s, n, copy_size) INDEX_op_ ## s,
#include "opc.h"
#undef DEF
NB_OPS,
};
#include "gen-op.h"
case INDEX_op_stl_kernel: {
extern void op_stl_kernel();
extern char __stl_mmu;
memcpy(gen_code_ptr, (void *)((char *)&op_stl_kernel+0), 72);
*(uint32_t *)(gen_code_ptr + 46) = (long)(&__stl_mmu) - (long)(gen_code_ptr + 46) + 0 -4;
gen_code_ptr += 72;
}
break;
void REGPARM(2) glue(glue(__st, SUFFIX), MMUSUFFIX)(target_ulong addr,static 関数の glue(io_write, SUFFIX)(physaddr, val, tlb_addr, retaddr); が呼ばれて (io_writel(...))、最終的に io_write[][](...) の形で、関数ポインタがコールされる。
DATA_TYPE val,
int mmu_idx)
{
target_phys_addr_t physaddr;
target_ulong tlb_addr;
void *retaddr;
int index;
index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
redo:
tlb_addr = env->tlb_table[mmu_idx][index].addr_write;
if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) {
physaddr = addr + env->tlb_table[mmu_idx][index].addend;
if (tlb_addr & ~TARGET_PAGE_MASK) {
/* IO access */
if ((addr & (DATA_SIZE - 1)) != 0)
goto do_unaligned_access;
retaddr = GETPC();
glue(io_write, SUFFIX)(physaddr, val, tlb_addr, retaddr);
} else if (((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1) >= TARGET_PAGE_SIZE) {
do_unaligned_access:
retaddr = GETPC();
#ifdef ALIGNED_ONLY
do_unaligned_access(addr, 1, mmu_idx, retaddr);
#endif
glue(glue(slow_st, SUFFIX), MMUSUFFIX)(addr, val,
mmu_idx, retaddr);
} else {
/* aligned/unaligned access in the same page */
#ifdef ALIGNED_ONLY
if ((addr & (DATA_SIZE - 1)) != 0) {
retaddr = GETPC();
do_unaligned_access(addr, 1, mmu_idx, retaddr);
}
#endif
glue(glue(st, SUFFIX), _raw)((uint8_t *)(long)physaddr, val);
}
} else {
/* the page is not in the TLB : fill it */
retaddr = GETPC();
#ifdef ALIGNED_ONLY
if ((addr & (DATA_SIZE - 1)) != 0)
do_unaligned_access(addr, 1, mmu_idx, retaddr);
#endif
tlb_fill(addr, 1, mmu_idx, retaddr);
goto redo;
}
}
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);}}
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);}
/* 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);}}