New model for PowerPC CPU hardware interrupt events:
authorj_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
Fri, 30 Mar 2007 09:38:04 +0000 (09:38 +0000)
committerj_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
Fri, 30 Mar 2007 09:38:04 +0000 (09:38 +0000)
move all PowerPC specific code into target-ppc/helper.c to avoid polluting
the common code in cpu-exec.c. This makes implementation of new features
(ie embedded PowerPC timers, critical interrupts, ...) easier.
This also avoid hardcoding the IRQ callback in the OpenPIC controller,
making it more easily reusable and allowing cascading.

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2542 c046a42c-6fe2-441c-8c8c-71466251a162

cpu-exec.c
hw/openpic.c
hw/ppc.c
hw/ppc_chrp.c
hw/ppc_prep.c
target-ppc/cpu.h
target-ppc/helper.c
vl.h

index 634f1ba..d124c40 100644 (file)
@@ -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
index 3177337..7565b1d 100644 (file)
@@ -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);
     
index c910cb9..04242ac 100644 (file)
--- a/hw/ppc.c
+++ b/hw/ppc.c
 #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,
index b1199e2..e9d6670 100644 (file)
@@ -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 */
index 32a3e52..d504b1c 100644 (file)
@@ -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) {
index b21d6b1..ef02f10 100644 (file)
@@ -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__) */
index b86f823..4356edc 100644 (file)
@@ -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 61b5b80..2bcf81a 100644 (file)
--- 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);