aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cpu-exec.c26
-rw-r--r--hw/openpic.c15
-rw-r--r--hw/ppc.c54
-rw-r--r--hw/ppc_chrp.c12
-rw-r--r--hw/ppc_prep.c18
-rw-r--r--target-ppc/cpu.h16
-rw-r--r--target-ppc/helper.c83
-rw-r--r--vl.h15
8 files changed, 193 insertions, 46 deletions
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);