diff --git a/cpu-exec.c b/cpu-exec.c index 634f1ba875..d124c4059b 100644 --- a/cpu-exec.c +++ b/cpu-exec.c @@ -256,8 +256,7 @@ int cpu_exec(CPUState *env1) #elif defined(TARGET_PPC) if (env1->halted) { if (env1->msr[MSR_EE] && - (env1->interrupt_request & - (CPU_INTERRUPT_HARD | CPU_INTERRUPT_TIMER))) { + (env1->interrupt_request & CPU_INTERRUPT_HARD)) { env1->halted = 0; } else { return EXCP_HALTED; @@ -448,24 +447,11 @@ int cpu_exec(CPUState *env1) cpu_ppc_reset(env); } #endif - if (msr_ee != 0) { - if ((interrupt_request & CPU_INTERRUPT_HARD)) { - /* Raise it */ - env->exception_index = EXCP_EXTERNAL; - env->error_code = 0; - do_interrupt(env); - env->interrupt_request &= ~CPU_INTERRUPT_HARD; -#if defined(__sparc__) && !defined(HOST_SOLARIS) - tmp_T0 = 0; -#else - T0 = 0; -#endif - } else if ((interrupt_request & CPU_INTERRUPT_TIMER)) { - /* Raise it */ - env->exception_index = EXCP_DECR; - env->error_code = 0; - do_interrupt(env); - env->interrupt_request &= ~CPU_INTERRUPT_TIMER; + if (interrupt_request & CPU_INTERRUPT_HARD) { + if (ppc_hw_interrupt(env) == 1) { + /* Some exception was raised */ + if (env->pending_interrupts == 0) + env->interrupt_request &= ~CPU_INTERRUPT_HARD; #if defined(__sparc__) && !defined(HOST_SOLARIS) tmp_T0 = 0; #else diff --git a/hw/openpic.c b/hw/openpic.c index 31773373ac..7565b1def6 100644 --- a/hw/openpic.c +++ b/hw/openpic.c @@ -164,6 +164,7 @@ typedef struct IRQ_dst_t { struct openpic_t { PCIDevice pci_dev; + SetIRQFunc *set_irq; int mem_index; /* Global registers */ uint32_t frep; /* Feature reporting register */ @@ -264,8 +265,8 @@ static void IRQ_local_pipe (openpic_t *opp, int n_CPU, int n_IRQ) IRQ_setbit(&dst->raised, n_IRQ); if (priority > dst->raised.priority) { IRQ_get_next(opp, &dst->raised); - DPRINTF("Raise CPU IRQ\n"); - cpu_interrupt(dst->env, CPU_INTERRUPT_HARD); + DPRINTF("Raise CPU IRQ fn %p env %p\n", opp->set_irq, dst->env); + opp->set_irq(dst->env, OPENPIC_EVT_INT, 1); } } @@ -532,7 +533,7 @@ static void openpic_gbl_write (void *opaque, uint32_t addr, uint32_t val) /* XXX: Should be able to reset any CPU */ if (val & 1) { DPRINTF("Reset CPU IRQ\n"); - // cpu_interrupt(first_cpu, CPU_INTERRUPT_RESET); + // opp->set_irq(dst->env, OPENPIC_EVT_RESET, 1); } break; #if MAX_IPI > 0 @@ -781,7 +782,7 @@ static void openpic_cpu_write (void *opaque, uint32_t addr, uint32_t val) src = &opp->src[n_IRQ]; if (IPVP_PRIORITY(src->ipvp) > dst->servicing.priority) { DPRINTF("Raise CPU IRQ\n"); - cpu_interrupt(dst->env, CPU_INTERRUPT_HARD); + opp->set_irq(dst->env, OPENPIC_EVT_INT, 1); } } break; @@ -963,8 +964,8 @@ static void openpic_map(PCIDevice *pci_dev, int region_num, #endif } -openpic_t *openpic_init (PCIBus *bus, int *pmem_index, int nb_cpus, - CPUPPCState **envp) +openpic_t *openpic_init (PCIBus *bus, SetIRQFunc *set_irq, + int *pmem_index, int nb_cpus, CPUPPCState **envp) { openpic_t *opp; uint8_t *pci_conf; @@ -994,7 +995,7 @@ openpic_t *openpic_init (PCIBus *bus, int *pmem_index, int nb_cpus, } else { opp = qemu_mallocz(sizeof(openpic_t)); } - + opp->set_irq = set_irq; opp->mem_index = cpu_register_io_memory(0, openpic_read, openpic_write, opp); diff --git a/hw/ppc.c b/hw/ppc.c index c910cb9f72..04242ac90a 100644 --- a/hw/ppc.c +++ b/hw/ppc.c @@ -24,6 +24,57 @@ #include "vl.h" #include "m48t59.h" +extern FILE *logfile; +extern int loglevel; + +/*****************************************************************************/ +/* PowerPC internal fake IRQ controller + * used to manage multiple sources hardware events + */ +/* XXX: should be protected */ +void ppc_set_irq (void *opaque, int n_IRQ, int level) +{ + CPUState *env; + + env = opaque; + if (level) { + env->pending_interrupts |= 1 << n_IRQ; + cpu_interrupt(env, CPU_INTERRUPT_HARD); + } else { + env->pending_interrupts &= ~(1 << n_IRQ); + if (env->pending_interrupts == 0) + cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); + } +#if 0 + printf("%s: %p n_IRQ %d level %d => pending %08x req %08x\n", __func__, + env, n_IRQ, level, env->pending_interrupts, env->interrupt_request); +#endif +} + +/* External IRQ callback from OpenPIC IRQ controller */ +void ppc_openpic_irq (void *opaque, int n_IRQ, int level) +{ + switch (n_IRQ) { + case OPENPIC_EVT_INT: + n_IRQ = PPC_INTERRUPT_EXT; + break; + case OPENPIC_EVT_CINT: + /* On PowerPC BookE, critical input use vector 0 */ + n_IRQ = PPC_INTERRUPT_RESET; + break; + case OPENPIC_EVT_MCK: + n_IRQ = PPC_INTERRUPT_MCK; + break; + case OPENPIC_EVT_DEBUG: + n_IRQ = PPC_INTERRUPT_DEBUG; + break; + case OPENPIC_EVT_RESET: + qemu_system_reset_request(); + return; + } + ppc_set_irq(opaque, n_IRQ, level); +} + /*****************************************************************************/ /* PPC time base and decrementer emulation */ //#define DEBUG_TB @@ -35,6 +86,7 @@ struct ppc_tb_t { /* Decrementer management */ uint64_t decr_next; /* Tick for next decr interrupt */ struct QEMUTimer *decr_timer; + void *opaque; }; static inline uint64_t cpu_ppc_get_tb (ppc_tb_t *tb_env) @@ -131,7 +183,7 @@ static inline void cpu_ppc_decr_excp (CPUState *env) #ifdef DEBUG_TB printf("raise decrementer exception\n"); #endif - cpu_interrupt(env, CPU_INTERRUPT_TIMER); + ppc_set_irq(env, PPC_INTERRUPT_DECR, 1); } static void _cpu_ppc_store_decr (CPUState *env, uint32_t decr, diff --git a/hw/ppc_chrp.c b/hw/ppc_chrp.c index b1199e2cfe..e9d6670e43 100644 --- a/hw/ppc_chrp.c +++ b/hw/ppc_chrp.c @@ -1,7 +1,7 @@ /* * QEMU PPC CHRP/PMAC hardware System Emulator * - * Copyright (c) 2004 Fabrice Bellard + * Copyright (c) 2004-2007 Fabrice Bellard * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -449,21 +449,21 @@ static void ppc_chrp_init (int ram_size, int vga_ram_size, int boot_device, } macio_init(pci_bus, 0x0017); - + nvram = m48t59_init(8, 0xFFF04000, 0x0074, NVRAM_SIZE, 59); - + arch_name = "HEATHROW"; } else { isa_mem_base = 0x80000000; - + /* Register 8 MB of ISA IO space */ isa_mmio_init(0xf2000000, 0x00800000); - + /* UniN init */ unin_memory = cpu_register_io_memory(0, unin_read, unin_write, NULL); cpu_register_physical_memory(0xf8000000, 0x00001000, unin_memory); - pic = openpic_init(NULL, &openpic_mem_index, 1, &env); + pic = openpic_init(NULL, &ppc_openpic_irq, &openpic_mem_index, 1, &env); set_irq = openpic_set_irq; pci_bus = pci_pmac_init(pic); /* init basic PC hardware */ diff --git a/hw/ppc_prep.c b/hw/ppc_prep.c index 32a3e52307..d504b1c6d8 100644 --- a/hw/ppc_prep.c +++ b/hw/ppc_prep.c @@ -1,7 +1,7 @@ /* * QEMU PPC PREP hardware System Emulator * - * Copyright (c) 2003-2004 Jocelyn Mayer + * Copyright (c) 2003-2007 Jocelyn Mayer * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -84,29 +84,27 @@ static void speaker_ioport_write(void *opaque, uint32_t addr, uint32_t val) #endif } -static uint32_t speaker_ioport_read(void *opaque, uint32_t addr) +static uint32_t speaker_ioport_read (void *opaque, uint32_t addr) { #if 0 int out; out = pit_get_out(pit, 2, qemu_get_clock(vm_clock)); dummy_refresh_clock ^= 1; return (speaker_data_on << 1) | pit_get_gate(pit, 2) | (out << 5) | - (dummy_refresh_clock << 4); + (dummy_refresh_clock << 4); #endif return 0; } -static void pic_irq_request(void *opaque, int level) +static void pic_irq_request (void *opaque, int level) { - if (level) - cpu_interrupt(first_cpu, CPU_INTERRUPT_HARD); - else - cpu_reset_interrupt(first_cpu, CPU_INTERRUPT_HARD); + ppc_set_irq(opaque, PPC_INTERRUPT_EXT, level); } /* PCI intack register */ /* Read-only register (?) */ -static void _PPC_intack_write (void *opaque, target_phys_addr_t addr, uint32_t value) +static void _PPC_intack_write (void *opaque, + target_phys_addr_t addr, uint32_t value) { // printf("%s: 0x%08x => 0x%08x\n", __func__, addr, value); } @@ -294,7 +292,7 @@ static void PREP_io_800_writeb (void *opaque, uint32_t addr, uint32_t val) /* Special port 92 */ /* Check soft reset asked */ if (val & 0x01) { - // cpu_interrupt(first_cpu, CPU_INTERRUPT_RESET); + // cpu_interrupt(first_cpu, PPC_INTERRUPT_RESET); } /* Check LE mode */ if (val & 0x02) { diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index b21d6b19fb..ef02f10968 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -740,6 +740,7 @@ struct CPUPPCState { int exception_index; int error_code; int interrupt_request; + uint32_t pending_interrupts; /* Those resources are used only during code translation */ /* Next instruction pointer */ @@ -1267,6 +1268,21 @@ enum { EXCP_TRAP = 0x40, }; +/* Hardware interruption sources: + * all those exception can be raised simulteaneously + */ +enum { + PPC_INTERRUPT_RESET = 0, /* Reset / critical input */ + PPC_INTERRUPT_MCK = 1, /* Machine check exception */ + PPC_INTERRUPT_EXT = 2, /* External interrupt */ + PPC_INTERRUPT_DECR = 3, /* Decrementer exception */ + PPC_INTERRUPT_HDECR = 4, /* Hypervisor decrementer exception */ + PPC_INTERRUPT_PIT = 5, /* Programmable inteval timer interrupt */ + PPC_INTERRUPT_FIT = 6, /* Fixed interval timer interrupt */ + PPC_INTERRUPT_WDT = 7, /* Watchdog timer interrupt */ + PPC_INTERRUPT_DEBUG = 8, /* External debug exception */ +}; + /*****************************************************************************/ #endif /* !defined (__CPU_PPC_H__) */ diff --git a/target-ppc/helper.c b/target-ppc/helper.c index b86f8234e6..4356edc36c 100644 --- a/target-ppc/helper.c +++ b/target-ppc/helper.c @@ -1229,6 +1229,13 @@ void do_interrupt (CPUState *env) { env->exception_index = -1; } + +int ppc_hw_interrupt (CPUState *env) +{ + env->exception_index = -1; + + return 0; +} #else /* defined (CONFIG_USER_ONLY) */ static void dump_syscall(CPUState *env) { @@ -1753,4 +1760,80 @@ void do_interrupt (CPUState *env) env->nip = excp; env->exception_index = EXCP_NONE; } + +int ppc_hw_interrupt (CPUState *env) +{ + int raised = 0; + +#if 0 + printf("%s: %p pending %08x req %08x %08x me %d ee %d\n", + __func__, env, env->pending_interrupts, + env->interrupt_request, interrupt_request, + msr_me, msr_ee); +#endif + /* Raise it */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_RESET)) { + /* External reset / critical input */ + env->exception_index = EXCP_RESET; + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_RESET); + raised = 1; + } + if (raised == 0 && msr_me != 0) { + /* Machine check exception */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_MCK)) { + env->exception_index = EXCP_MACHINE_CHECK; + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_MCK); + raised = 1; + } + } + if (raised == 0 && msr_ee != 0) { +#if defined(TARGET_PPC64H) /* PowerPC 64 with hypervisor mode support */ + /* Hypervisor decrementer exception */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDECR)) { + env->exception_index = EXCP_HDECR; + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR); + raised = 1; + } else +#endif + /* Decrementer exception */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_DECR)) { + env->exception_index = EXCP_DECR; + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DECR); + raised = 1; + /* Programmable interval timer on embedded PowerPC */ + } else if (env->pending_interrupts & (1 << PPC_INTERRUPT_PIT)) { + env->exception_index = EXCP_40x_PIT; + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_PIT); + raised = 1; + /* Fixed interval timer on embedded PowerPC */ + } else if (env->pending_interrupts & (1 << PPC_INTERRUPT_FIT)) { + env->exception_index = EXCP_40x_FIT; + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_FIT); + raised = 1; + /* Watchdog timer on embedded PowerPC */ + } else if (env->pending_interrupts & (1 << PPC_INTERRUPT_WDT)) { + env->exception_index = EXCP_40x_WATCHDOG; + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_WDT); + raised = 1; + /* External interrupt */ + } else if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) { + env->exception_index = EXCP_EXTERNAL; + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_EXT); + raised = 1; + } +#if 0 // TODO + /* External debug exception */ + } else if (env->pending_interrupts & (1 << PPC_INTERRUPT_DEBUG)) { + env->exception_index = EXCP_xxx; + env->pending_interrupts &= ~(1 << PPC_INTERRUPT_DEBUG); + raised = 1; +#endif + } + if (raised != 0) { + env->error_code = 0; + do_interrupt(env); + } + + return raised; +} #endif /* !CONFIG_USER_ONLY */ diff --git a/vl.h b/vl.h index 61b5b80f75..2bcf81ada2 100644 --- a/vl.h +++ b/vl.h @@ -852,9 +852,16 @@ int piix4_init(PCIBus *bus, int devfn); /* openpic.c */ typedef struct openpic_t openpic_t; +enum { + OPENPIC_EVT_INT = 0, /* IRQ */ + OPENPIC_EVT_CINT, /* critical IRQ */ + OPENPIC_EVT_MCK, /* Machine check event */ + OPENPIC_EVT_DEBUG, /* Inconditional debug event */ + OPENPIC_EVT_RESET, /* Core reset event */ +}; void openpic_set_irq(void *opaque, int n_IRQ, int level); -openpic_t *openpic_init (PCIBus *bus, int *pmem_index, int nb_cpus, - CPUState **envp); +openpic_t *openpic_init (PCIBus *bus, SetIRQFunc *set_irq, + int *pmem_index, int nb_cpus, CPUPPCState **envp); /* heathrow_pic.c */ typedef struct HeathrowPICS HeathrowPICS; @@ -1115,6 +1122,10 @@ extern void cpu_mips_irqctrl_init (void); extern QEMUMachine shix_machine; #ifdef TARGET_PPC +/* PowerPC hardware exceptions management helpers */ +void ppc_set_irq (void *opaque, int n_IRQ, int level); +void ppc_openpic_irq (void *opaque, int n_IRQ, int level); +int ppc_hw_interrupt (CPUState *env); ppc_tb_t *cpu_ppc_tb_init (CPUState *env, uint32_t freq); #endif void PREP_debug_write (void *opaque, uint32_t addr, uint32_t val);