diff options
-rw-r--r-- | cpu-exec.c | 3 | ||||
-rw-r--r-- | hw/slavio_intctl.c | 55 | ||||
-rw-r--r-- | hw/sun4m.c | 37 | ||||
-rw-r--r-- | target-sparc/cpu.h | 4 |
4 files changed, 55 insertions, 44 deletions
diff --git a/cpu-exec.c b/cpu-exec.c index 4f7f215c01..543ec09ef7 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -461,6 +461,9 @@ int cpu_exec(CPUState *env1) env->interrupt_request &= ~CPU_INTERRUPT_HARD; do_interrupt(env->interrupt_index); env->interrupt_index = 0; +#if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) + cpu_check_irqs(env); +#endif #if defined(__sparc__) && !defined(HOST_SOLARIS) tmp_T0 = 0; #else diff --git a/hw/slavio_intctl.c b/hw/slavio_intctl.c index acde370e5c..9550c0064f 100644 --- a/hw/slavio_intctl.c +++ b/hw/slavio_intctl.c @@ -104,6 +104,7 @@ static void slavio_intctl_mem_writel(void *opaque, target_phys_addr_t addr, uint val |= 80000000; val &= 0xfffe0000; s->intreg_pending[cpu] &= ~val; + slavio_check_interrupts(s); DPRINTF("Cleared cpu %d irq mask %x, curmask %x\n", cpu, val, s->intreg_pending[cpu]); break; case 2: // set softint @@ -175,10 +176,12 @@ static void slavio_intctlm_mem_writel(void *opaque, target_phys_addr_t addr, uin val &= ~0x4fb2007f; s->intregm_disabled |= val; s->intregm_pending &= ~val; + slavio_check_interrupts(s); DPRINTF("Disabled master irq mask %x, curmask %x\n", val, s->intregm_disabled); break; case 4: s->target_cpu = val & (MAX_CPUS - 1); + slavio_check_interrupts(s); DPRINTF("Set master irq cpu %d\n", s->target_cpu); break; default: @@ -227,53 +230,36 @@ void slavio_irq_info(void *opaque) #endif } -static void raise_pil(SLAVIO_INTCTLState *s, unsigned int pil, - unsigned int cpu) -{ - qemu_irq irq; - unsigned int oldmax; - - irq = s->cpu_irqs[cpu][pil]; - -#ifdef DEBUG_IRQ_COUNT - s->irq_count[pil]++; -#endif - oldmax = s->pil_out[cpu]; - if (oldmax > 0 && oldmax != pil) - qemu_irq_lower(s->cpu_irqs[cpu][oldmax]); - s->pil_out[cpu] = pil; - if (pil > 0) - qemu_irq_raise(irq); - DPRINTF("cpu %d pil %d\n", cpu, pil); -} - static void slavio_check_interrupts(void *opaque) { SLAVIO_INTCTLState *s = opaque; - uint32_t pending = s->intregm_pending; - unsigned int i, j, max = 0; + uint32_t pending = s->intregm_pending, pil_pending; + unsigned int i, j; pending &= ~s->intregm_disabled; DPRINTF("pending %x disabled %x\n", pending, s->intregm_disabled); for (i = 0; i < MAX_CPUS; i++) { - max = 0; + pil_pending = 0; if (pending && !(s->intregm_disabled & 0x80000000) && (i == s->target_cpu)) { for (j = 0; j < 32; j++) { - if (pending & (1 << j)) { - if (max < s->intbit_to_level[j]) - max = s->intbit_to_level[j]; - } + if (pending & (1 << j)) + pil_pending |= 1 << s->intbit_to_level[j]; } } - for (j = 17; j < 32; j++) { - if (s->intreg_pending[i] & (1 << j)) { - if (max < j - 16) - max = j - 16; + pil_pending |= (s->intreg_pending[i] >> 16) & 0xfffe; + + for (j = 0; j < MAX_PILS; j++) { + if (pil_pending & (1 << j)) { + if (!(s->pil_out[i] & (1 << j))) + qemu_irq_raise(s->cpu_irqs[i][j]); + } else { + if (s->pil_out[i] & (1 << j)) + qemu_irq_lower(s->cpu_irqs[i][j]); } } - raise_pil(s, max, i); + s->pil_out[i] = pil_pending; } } @@ -291,6 +277,9 @@ static void slavio_set_irq(void *opaque, int irq, int level) level); if (pil > 0) { if (level) { +#ifdef DEBUG_IRQ_COUNT + s->irq_count[pil]++; +#endif s->intregm_pending |= mask; s->intreg_pending[s->target_cpu] |= 1 << pil; } else { @@ -342,6 +331,7 @@ static int slavio_intctl_load(QEMUFile *f, void *opaque, int version_id) qemu_get_be32s(f, &s->intregm_pending); qemu_get_be32s(f, &s->intregm_disabled); qemu_get_be32s(f, &s->target_cpu); + slavio_check_interrupts(s); return 0; } @@ -356,6 +346,7 @@ static void slavio_intctl_reset(void *opaque) s->intregm_disabled = ~0xffb2007f; s->intregm_pending = 0; s->target_cpu = 0; + slavio_check_interrupts(s); } void *slavio_intctl_init(target_phys_addr_t addr, target_phys_addr_t addrg, diff --git a/hw/sun4m.c b/hw/sun4m.c index eb69ef8e58..5974812670 100644 --- a/hw/sun4m.c +++ b/hw/sun4m.c @@ -240,26 +240,41 @@ void irq_info() slavio_irq_info(slavio_intctl); } +void cpu_check_irqs(CPUState *env) +{ + if (env->pil_in && (env->interrupt_index == 0 || + (env->interrupt_index & ~15) == TT_EXTINT)) { + unsigned int i; + + for (i = 15; i > 0; i--) { + if (env->pil_in & (1 << i)) { + int old_interrupt = env->interrupt_index; + + env->interrupt_index = TT_EXTINT | i; + if (old_interrupt != env->interrupt_index) + cpu_interrupt(env, CPU_INTERRUPT_HARD); + break; + } + } + } else if (!env->pil_in && (env->interrupt_index & ~15) == TT_EXTINT) { + env->interrupt_index = 0; + cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + } +} + static void cpu_set_irq(void *opaque, int irq, int level) { CPUState *env = opaque; if (level) { DPRINTF("Raise CPU IRQ %d\n", irq); - env->halted = 0; - - if (env->interrupt_index == 0 || - ((env->interrupt_index & ~15) == TT_EXTINT && - (env->interrupt_index & 15) < irq)) { - env->interrupt_index = TT_EXTINT | irq; - cpu_interrupt(env, CPU_INTERRUPT_HARD); - } else { - DPRINTF("Not triggered, pending exception %d\n", - env->interrupt_index); - } + env->pil_in |= 1 << irq; + cpu_check_irqs(env); } else { DPRINTF("Lower CPU IRQ %d\n", irq); + env->pil_in &= ~(1 << irq); + cpu_check_irqs(env); } } diff --git a/target-sparc/cpu.h b/target-sparc/cpu.h index 600b37ba5e..e469f041a1 100644 --- a/target-sparc/cpu.h +++ b/target-sparc/cpu.h @@ -181,7 +181,8 @@ typedef struct CPUSPARCState { int psrs; /* supervisor mode (extracted from PSR) */ int psrps; /* previous supervisor mode */ int psret; /* enable traps */ - uint32_t psrpil; /* interrupt level */ + uint32_t psrpil; /* interrupt blocking level */ + uint32_t pil_in; /* incoming interrupt level bitmap */ int psref; /* enable fpu */ target_ulong version; jmp_buf jmp_env; @@ -306,6 +307,7 @@ void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec, void do_tick_set_count(void *opaque, uint64_t count); uint64_t do_tick_get_count(void *opaque); void do_tick_set_limit(void *opaque, uint64_t limit); +void cpu_check_irqs(CPUSPARCState *env); #define CPUState CPUSPARCState #define cpu_init cpu_sparc_init |