ARMv7 support.


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3572 c046a42c-6fe2-441c-8c8c-71466251a162
diff --git a/hw/arm_boot.c b/hw/arm_boot.c
index 7a99b41..8ef14ab 100644
--- a/hw/arm_boot.c
+++ b/hw/arm_boot.c
@@ -1,7 +1,7 @@
 /*
  * ARM kernel loader.
  *
- * Copyright (c) 2006 CodeSourcery.
+ * Copyright (c) 2006-2007 CodeSourcery.
  * Written by Paul Brook
  *
  * This code is licenced under the GPL.
@@ -24,6 +24,22 @@
   0  /* Kernel entry point.  Set by integratorcp_init.  */
 };
 
+/* Entry point for secondary CPUs.  Enable interrupt controller and
+   Issue WFI until start address is written to system controller.  */
+static uint32_t smpboot[] = {
+  0xe3a00201, /* mov     r0, #0x10000000 */
+  0xe3800601, /* orr     r0, r0, #0x001000000 */
+  0xe3a01001, /* mov     r1, #1 */
+  0xe5801100, /* str     r1, [r0, #0x100] */
+  0xe3a00201, /* mov     r0, #0x10000000 */
+  0xe3800030, /* orr     r0, #0x30 */
+  0xe320f003, /* wfi */
+  0xe5901000, /* ldr     r1, [r0] */
+  0xe3110003, /* tst     r1, #3 */
+  0x1afffffb, /* bne     <wfi> */
+  0xe12fff11  /* bx      r1 */
+};
+
 static void main_cpu_reset(void *opaque)
 {
     CPUState *env = opaque;
@@ -33,6 +49,8 @@
         arm_load_kernel(env, env->ram_size, env->kernel_filename,
                         env->kernel_cmdline, env->initrd_filename,
                         env->board_id, env->loader_start);
+
+    /* TODO:  Reset secondary CPUs.  */
 }
 
 static void set_kernel_args(uint32_t ram_size, int initrd_size,
@@ -211,6 +229,8 @@
         bootloader[6] = entry;
         for (n = 0; n < sizeof(bootloader) / 4; n++)
             stl_raw(phys_ram_base + (n * 4), bootloader[n]);
+        for (n = 0; n < sizeof(smpboot) / 4; n++)
+            stl_raw(phys_ram_base + ram_size + (n * 4), smpboot[n]);
         if (old_param)
             set_kernel_args_old(ram_size, initrd_size,
                                 kernel_cmdline, loader_start);
diff --git a/hw/arm_gic.c b/hw/arm_gic.c
index 8cd7182..774b79b 100644
--- a/hw/arm_gic.c
+++ b/hw/arm_gic.c
@@ -1,17 +1,15 @@
 /*
- * ARM AMBA Generic/Distributed Interrupt Controller
+ * ARM Generic/Distributed Interrupt Controller
  *
- * Copyright (c) 2006 CodeSourcery.
+ * Copyright (c) 2006-2007 CodeSourcery.
  * Written by Paul Brook
  *
  * This code is licenced under the GPL.
  */
 
-/* TODO: Some variants of this controller can handle multiple CPUs.
-   Currently only single CPU operation is implemented.  */
-
-#include "vl.h"
-#include "arm_pic.h"
+/* This file contains implementation code for the RealView EB interrupt
+   controller, MPCore distributed interrupt controller and ARMv7-M
+   Nested Vectored Interrupt Controller.  */
 
 //#define DEBUG_GIC
 
@@ -22,58 +20,84 @@
 #define DPRINTF(fmt, args...) do {} while(0)
 #endif
 
-/* Distributed interrupt controller.  */
-
+#ifdef NVIC
+static const uint8_t gic_id[] =
+{ 0x00, 0xb0, 0x1b, 0x00, 0x0d, 0xe0, 0x05, 0xb1 };
+#define GIC_DIST_OFFSET 0
+/* The NVIC has 16 internal vectors.  However these are not exposed
+   through the normal GIC interface.  */
+#define GIC_BASE_IRQ    32
+#else
 static const uint8_t gic_id[] =
 { 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
-
-#define GIC_NIRQ 96
+#define GIC_DIST_OFFSET 0x1000
+#define GIC_BASE_IRQ    0
+#endif
 
 typedef struct gic_irq_state
 {
+    /* ??? The documentation seems to imply the enable bits are global, even
+       for per-cpu interrupts.  This seems strange.  */
     unsigned enabled:1;
-    unsigned pending:1;
-    unsigned active:1;
+    unsigned pending:NCPU;
+    unsigned active:NCPU;
     unsigned level:1;
-    unsigned model:1; /* 0 = 1:N, 1 = N:N */
+    unsigned model:1; /* 0 = N:N, 1 = 1:N */
     unsigned trigger:1; /* nonzero = edge triggered.  */
 } gic_irq_state;
 
+#define ALL_CPU_MASK ((1 << NCPU) - 1)
+
 #define GIC_SET_ENABLED(irq) s->irq_state[irq].enabled = 1
 #define GIC_CLEAR_ENABLED(irq) s->irq_state[irq].enabled = 0
 #define GIC_TEST_ENABLED(irq) s->irq_state[irq].enabled
-#define GIC_SET_PENDING(irq) s->irq_state[irq].pending = 1
-#define GIC_CLEAR_PENDING(irq) s->irq_state[irq].pending = 0
-#define GIC_TEST_PENDING(irq) s->irq_state[irq].pending
-#define GIC_SET_ACTIVE(irq) s->irq_state[irq].active = 1
-#define GIC_CLEAR_ACTIVE(irq) s->irq_state[irq].active = 0
-#define GIC_TEST_ACTIVE(irq) s->irq_state[irq].active
+#define GIC_SET_PENDING(irq, cm) s->irq_state[irq].pending |= (cm)
+#define GIC_CLEAR_PENDING(irq, cm) s->irq_state[irq].pending &= ~(cm)
+#define GIC_TEST_PENDING(irq, cm) ((s->irq_state[irq].pending & (cm)) != 0)
+#define GIC_SET_ACTIVE(irq, cm) s->irq_state[irq].active |= (cm)
+#define GIC_CLEAR_ACTIVE(irq, cm) s->irq_state[irq].active &= ~(cm)
+#define GIC_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0)
 #define GIC_SET_MODEL(irq) s->irq_state[irq].model = 1
 #define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = 0
 #define GIC_TEST_MODEL(irq) s->irq_state[irq].model
-#define GIC_SET_LEVEL(irq) s->irq_state[irq].level = 1
-#define GIC_CLEAR_LEVEL(irq) s->irq_state[irq].level = 0
-#define GIC_TEST_LEVEL(irq) s->irq_state[irq].level
+#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level = (cm)
+#define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm)
+#define GIC_TEST_LEVEL(irq, cm) (s->irq_state[irq].level & (cm)) != 0
 #define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1
 #define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0
 #define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger
+#define GIC_GET_PRIORITY(irq, cpu) \
+  (((irq) < 32) ? s->priority1[irq][cpu] : s->priority2[(irq) - 32])
+#ifdef NVIC
+#define GIC_TARGET(irq) 1
+#else
+#define GIC_TARGET(irq) s->irq_target[irq]
+#endif
 
 typedef struct gic_state
 {
     uint32_t base;
-    qemu_irq parent_irq;
+    qemu_irq parent_irq[NCPU];
     int enabled;
-    int cpu_enabled;
+    int cpu_enabled[NCPU];
 
     gic_irq_state irq_state[GIC_NIRQ];
+#ifndef NVIC
     int irq_target[GIC_NIRQ];
-    int priority[GIC_NIRQ];
-    int last_active[GIC_NIRQ];
+#endif
+    int priority1[32][NCPU];
+    int priority2[GIC_NIRQ - 32];
+    int last_active[GIC_NIRQ][NCPU];
 
-    int priority_mask;
-    int running_irq;
-    int running_priority;
-    int current_pending;
+    int priority_mask[NCPU];
+    int running_irq[NCPU];
+    int running_priority[NCPU];
+    int current_pending[NCPU];
+
+    qemu_irq *in;
+#ifdef NVIC
+    void *nvic;
+#endif
 } gic_state;
 
 /* TODO: Many places that call this routine could be optimized.  */
@@ -83,112 +107,136 @@
     int best_irq;
     int best_prio;
     int irq;
+    int level;
+    int cpu;
+    int cm;
 
-    s->current_pending = 1023;
-    if (!s->enabled || !s->cpu_enabled) {
-        qemu_irq_lower(s->parent_irq);
-        return;
-    }
-    best_prio = 0x100;
-    best_irq = 1023;
-    for (irq = 0; irq < 96; irq++) {
-        if (GIC_TEST_ENABLED(irq) && GIC_TEST_PENDING(irq)) {
-            if (s->priority[irq] < best_prio) {
-                best_prio = s->priority[irq];
-                best_irq = irq;
+    for (cpu = 0; cpu < NCPU; cpu++) {
+        cm = 1 << cpu;
+        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 < GIC_NIRQ; irq++) {
+            if (GIC_TEST_ENABLED(irq) && GIC_TEST_PENDING(irq, cm)) {
+                if (GIC_GET_PRIORITY(irq, cpu) < best_prio) {
+                    best_prio = GIC_GET_PRIORITY(irq, cpu);
+                    best_irq = irq;
+                }
             }
         }
-    }
-    if (best_prio > s->priority_mask) {
-        qemu_irq_lower(s->parent_irq);
-    } else {
-        s->current_pending = best_irq;
-        if (best_prio < s->running_priority) {
-            DPRINTF("Raised pending IRQ %d\n", best_irq);
-            qemu_irq_raise(s->parent_irq);
+        level = 0;
+        if (best_prio <= s->priority_mask[cpu]) {
+            s->current_pending[cpu] = best_irq;
+            if (best_prio < s->running_priority[cpu]) {
+                DPRINTF("Raised pending IRQ %d\n", best_irq);
+                level = 1;
+            }
         }
+        qemu_set_irq(s->parent_irq[cpu], level);
     }
 }
 
+static void __attribute__((unused))
+gic_set_pending_private(gic_state *s, int cpu, int irq)
+{
+    int cm = 1 << cpu;
+
+    if (GIC_TEST_PENDING(irq, cm))
+        return;
+
+    DPRINTF("Set %d pending cpu %d\n", irq, cpu);
+    GIC_SET_PENDING(irq, cm);
+    gic_update(s);
+}
+
+/* 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))
+    if (level == GIC_TEST_LEVEL(irq, ALL_CPU_MASK))
         return;
 
     if (level) {
-        GIC_SET_LEVEL(irq);
+        GIC_SET_LEVEL(irq, ALL_CPU_MASK);
         if (GIC_TEST_TRIGGER(irq) || GIC_TEST_ENABLED(irq)) {
-            DPRINTF("Set %d pending\n", irq);
-            GIC_SET_PENDING(irq);
+            DPRINTF("Set %d pending mask %x\n", irq, GIC_TARGET(irq));
+            GIC_SET_PENDING(irq, GIC_TARGET(irq));
         }
     } else {
-        GIC_CLEAR_LEVEL(irq);
+        GIC_CLEAR_LEVEL(irq, ALL_CPU_MASK);
     }
     gic_update(s);
 }
 
-static void gic_set_running_irq(gic_state *s, int irq)
+static void gic_set_running_irq(gic_state *s, int cpu, int irq)
 {
-    s->running_irq = irq;
-    if (irq == 1023)
-        s->running_priority = 0x100;
-    else
-        s->running_priority = s->priority[irq];
+    s->running_irq[cpu] = irq;
+    if (irq == 1023) {
+        s->running_priority[cpu] = 0x100;
+    } else {
+        s->running_priority[cpu] = GIC_GET_PRIORITY(irq, cpu);
+    }
     gic_update(s);
 }
 
-static uint32_t gic_acknowledge_irq(gic_state *s)
+static uint32_t gic_acknowledge_irq(gic_state *s, int cpu)
 {
     int new_irq;
-    new_irq = s->current_pending;
-    if (new_irq == 1023 || s->priority[new_irq] >= s->running_priority) {
+    int cm = 1 << cpu;
+    new_irq = s->current_pending[cpu];
+    if (new_irq == 1023
+            || GIC_GET_PRIORITY(new_irq, cpu) >= s->running_priority[cpu]) {
         DPRINTF("ACK no pending IRQ\n");
         return 1023;
     }
-    qemu_irq_lower(s->parent_irq);
-    s->last_active[new_irq] = s->running_irq;
-    /* For level triggered interrupts we clear the pending bit while
-       the interrupt is active.  */
-    GIC_CLEAR_PENDING(new_irq);
-    gic_set_running_irq(s, new_irq);
+    s->last_active[new_irq][cpu] = s->running_irq[cpu];
+    /* Clear pending flags for both level and edge triggered interrupts.
+       Level triggered IRQs will be reasserted once they become inactive.  */
+    GIC_CLEAR_PENDING(new_irq, GIC_TEST_MODEL(new_irq) ? ALL_CPU_MASK : cm);
+    gic_set_running_irq(s, cpu, new_irq);
     DPRINTF("ACK %d\n", new_irq);
     return new_irq;
 }
 
-static void gic_complete_irq(gic_state * s, int irq)
+static void gic_complete_irq(gic_state * s, int cpu, int irq)
 {
     int update = 0;
+    int cm = 1 << cpu;
     DPRINTF("EOI %d\n", irq);
-    if (s->running_irq == 1023)
+    if (s->running_irq[cpu] == 1023)
         return; /* No active IRQ.  */
     if (irq != 1023) {
         /* Mark level triggered interrupts as pending if they are still
            raised.  */
         if (!GIC_TEST_TRIGGER(irq) && GIC_TEST_ENABLED(irq)
-                && GIC_TEST_LEVEL(irq)) {
-            GIC_SET_PENDING(irq);
+                && GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) {
+            DPRINTF("Set %d pending mask %x\n", irq, cm);
+            GIC_SET_PENDING(irq, cm);
             update = 1;
         }
     }
-    if (irq != s->running_irq) {
+    if (irq != s->running_irq[cpu]) {
         /* Complete an IRQ that is not currently running.  */
-        int tmp = s->running_irq;
-        while (s->last_active[tmp] != 1023) {
-            if (s->last_active[tmp] == irq) {
-                s->last_active[tmp] = s->last_active[irq];
+        int tmp = s->running_irq[cpu];
+        while (s->last_active[tmp][cpu] != 1023) {
+            if (s->last_active[tmp][cpu] == irq) {
+                s->last_active[tmp][cpu] = s->last_active[irq][cpu];
                 break;
             }
-            tmp = s->last_active[tmp];
+            tmp = s->last_active[tmp][cpu];
         }
         if (update) {
             gic_update(s);
         }
     } else {
         /* Complete the current running IRQ.  */
-        gic_set_running_irq(s, s->last_active[s->running_irq]);
+        gic_set_running_irq(s, cpu, s->last_active[s->running_irq[cpu]][cpu]);
     }
 }
 
@@ -198,15 +246,22 @@
     uint32_t res;
     int irq;
     int i;
+    int cpu;
+    int cm;
+    int mask;
 
-    offset -= s->base + 0x1000;
+    cpu = gic_get_current_cpu();
+    cm = 1 << cpu;
+    offset -= s->base + GIC_DIST_OFFSET;
     if (offset < 0x100) {
+#ifndef NVIC
         if (offset == 0)
             return s->enabled;
         if (offset == 4)
-            return (GIC_NIRQ / 32) - 1;
+            return ((GIC_NIRQ / 32) - 1) | ((NCPU - 1) << 5);
         if (offset < 0x08)
             return 0;
+#endif
         goto bad_reg;
     } else if (offset < 0x200) {
         /* Interrupt Set/Clear Enable.  */
@@ -214,6 +269,7 @@
             irq = (offset - 0x100) * 8;
         else
             irq = (offset - 0x180) * 8;
+        irq += GIC_BASE_IRQ;
         if (irq >= GIC_NIRQ)
             goto bad_reg;
         res = 0;
@@ -228,40 +284,48 @@
             irq = (offset - 0x200) * 8;
         else
             irq = (offset - 0x280) * 8;
+        irq += GIC_BASE_IRQ;
         if (irq >= GIC_NIRQ)
             goto bad_reg;
         res = 0;
+        mask = (irq < 32) ?  cm : ALL_CPU_MASK;
         for (i = 0; i < 8; i++) {
-            if (GIC_TEST_PENDING(irq + i)) {
+            if (GIC_TEST_PENDING(irq + i, mask)) {
                 res |= (1 << i);
             }
         }
     } else if (offset < 0x400) {
         /* Interrupt Active.  */
-        irq = (offset - 0x300) * 8;
+        irq = (offset - 0x300) * 8 + GIC_BASE_IRQ;
         if (irq >= GIC_NIRQ)
             goto bad_reg;
         res = 0;
+        mask = (irq < 32) ?  cm : ALL_CPU_MASK;
         for (i = 0; i < 8; i++) {
-            if (GIC_TEST_ACTIVE(irq + i)) {
+            if (GIC_TEST_ACTIVE(irq + i, mask)) {
                 res |= (1 << i);
             }
         }
     } else if (offset < 0x800) {
         /* Interrupt Priority.  */
-        irq = offset - 0x400;
+        irq = (offset - 0x400) + GIC_BASE_IRQ;
         if (irq >= GIC_NIRQ)
             goto bad_reg;
-        res = s->priority[irq];
+        res = GIC_GET_PRIORITY(irq, cpu);
+#ifndef NVIC
     } else if (offset < 0xc00) {
         /* Interrupt CPU Target.  */
-        irq = offset - 0x800;
+        irq = (offset - 0x800) + GIC_BASE_IRQ;
         if (irq >= GIC_NIRQ)
             goto bad_reg;
-        res = s->irq_target[irq];
+        if (irq >= 29 && irq <= 31) {
+            res = cm;
+        } else {
+            res = GIC_TARGET(irq);
+        }
     } else if (offset < 0xf00) {
         /* Interrupt Configuration.  */
-        irq = (offset - 0xc00) * 2;
+        irq = (offset - 0xc00) * 2 + GIC_BASE_IRQ;
         if (irq >= GIC_NIRQ)
             goto bad_reg;
         res = 0;
@@ -271,6 +335,7 @@
             if (GIC_TEST_TRIGGER(irq + i))
                 res |= (2 << (i * 2));
         }
+#endif
     } else if (offset < 0xfe0) {
         goto bad_reg;
     } else /* offset >= 0xfe0 */ {
@@ -282,7 +347,7 @@
     }
     return res;
 bad_reg:
-    cpu_abort (cpu_single_env, "gic_dist_readb: Bad offset %x\n", offset);
+    cpu_abort(cpu_single_env, "gic_dist_readb: Bad offset %x\n", (int)offset);
     return 0;
 }
 
@@ -297,6 +362,13 @@
 static uint32_t gic_dist_readl(void *opaque, target_phys_addr_t offset)
 {
     uint32_t val;
+#ifdef NVIC
+    gic_state *s = (gic_state *)opaque;
+    uint32_t addr;
+    addr = offset - s->base;
+    if (addr < 0x100 || addr > 0xd00)
+        return nvic_readl(s->nvic, addr);
+#endif
     val = gic_dist_readw(opaque, offset);
     val |= gic_dist_readw(opaque, offset + 2) << 16;
     return val;
@@ -308,9 +380,14 @@
     gic_state *s = (gic_state *)opaque;
     int irq;
     int i;
+    int cpu;
 
-    offset -= s->base + 0x1000;
+    cpu = gic_get_current_cpu();
+    offset -= s->base + GIC_DIST_OFFSET;
     if (offset < 0x100) {
+#ifdef NVIC
+        goto bad_reg;
+#else
         if (offset == 0) {
             s->enabled = (value & 1);
             DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis");
@@ -319,27 +396,36 @@
         } else {
             goto bad_reg;
         }
+#endif
     } else if (offset < 0x180) {
         /* Interrupt Set Enable.  */
-        irq = (offset - 0x100) * 8;
+        irq = (offset - 0x100) * 8 + GIC_BASE_IRQ;
         if (irq >= GIC_NIRQ)
             goto bad_reg;
+        if (irq < 16)
+          value = 0xff;
         for (i = 0; i < 8; i++) {
             if (value & (1 << i)) {
+                int mask = (irq < 32) ? (1 << cpu) : GIC_TARGET(irq);
                 if (!GIC_TEST_ENABLED(irq + i))
                     DPRINTF("Enabled IRQ %d\n", irq + i);
                 GIC_SET_ENABLED(irq + i);
                 /* If a raised level triggered IRQ enabled then mark
                    is as pending.  */
-                if (GIC_TEST_LEVEL(irq + i) && !GIC_TEST_TRIGGER(irq + i))
-                    GIC_SET_PENDING(irq + i);
+                if (GIC_TEST_LEVEL(irq + i, mask)
+                        && !GIC_TEST_TRIGGER(irq + i)) {
+                    DPRINTF("Set %d pending mask %x\n", irq + i, mask);
+                    GIC_SET_PENDING(irq + i, mask);
+                }
             }
         }
     } else if (offset < 0x200) {
         /* Interrupt Clear Enable.  */
-        irq = (offset - 0x180) * 8;
+        irq = (offset - 0x180) * 8 + GIC_BASE_IRQ;
         if (irq >= GIC_NIRQ)
             goto bad_reg;
+        if (irq < 16)
+          value = 0;
         for (i = 0; i < 8; i++) {
             if (value & (1 << i)) {
                 if (GIC_TEST_ENABLED(irq + i))
@@ -349,22 +435,28 @@
         }
     } else if (offset < 0x280) {
         /* Interrupt Set Pending.  */
-        irq = (offset - 0x200) * 8;
+        irq = (offset - 0x200) * 8 + GIC_BASE_IRQ;
         if (irq >= GIC_NIRQ)
             goto bad_reg;
+        if (irq < 16)
+          irq = 0;
+
         for (i = 0; i < 8; i++) {
             if (value & (1 << i)) {
-                GIC_SET_PENDING(irq + i);
+                GIC_SET_PENDING(irq + i, GIC_TARGET(irq));
             }
         }
     } else if (offset < 0x300) {
         /* Interrupt Clear Pending.  */
-        irq = (offset - 0x280) * 8;
+        irq = (offset - 0x280) * 8 + GIC_BASE_IRQ;
         if (irq >= GIC_NIRQ)
             goto bad_reg;
         for (i = 0; i < 8; i++) {
+            /* ??? This currently clears the pending bit for all CPUs, even
+               for per-CPU interrupts.  It's unclear whether this is the
+               corect behavior.  */
             if (value & (1 << i)) {
-                GIC_CLEAR_PENDING(irq + i);
+                GIC_CLEAR_PENDING(irq + i, ALL_CPU_MASK);
             }
         }
     } else if (offset < 0x400) {
@@ -372,21 +464,32 @@
         goto bad_reg;
     } else if (offset < 0x800) {
         /* Interrupt Priority.  */
-        irq = offset - 0x400;
+        irq = (offset - 0x400) + GIC_BASE_IRQ;
         if (irq >= GIC_NIRQ)
             goto bad_reg;
-        s->priority[irq] = value;
+        if (irq < 32) {
+            s->priority1[irq][cpu] = value;
+        } else {
+            s->priority2[irq - 32] = value;
+        }
+#ifndef NVIC
     } else if (offset < 0xc00) {
         /* Interrupt CPU Target.  */
-        irq = offset - 0x800;
+        irq = (offset - 0x800) + GIC_BASE_IRQ;
         if (irq >= GIC_NIRQ)
             goto bad_reg;
-        s->irq_target[irq] = value;
+        if (irq < 29)
+            value = 0;
+        else if (irq < 32)
+            value = ALL_CPU_MASK;
+        s->irq_target[irq] = value & ALL_CPU_MASK;
     } else if (offset < 0xf00) {
         /* Interrupt Configuration.  */
-        irq = (offset - 0xc00) * 4;
+        irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ;
         if (irq >= GIC_NIRQ)
             goto bad_reg;
+        if (irq < 32)
+            value |= 0xaa;
         for (i = 0; i < 4; i++) {
             if (value & (1 << (i * 2))) {
                 GIC_SET_MODEL(irq + i);
@@ -399,25 +502,20 @@
                 GIC_CLEAR_TRIGGER(irq + i);
             }
         }
+#endif
     } else {
-        /* 0xf00 is only handled for word writes.  */
+        /* 0xf00 is only handled for 32-bit writes.  */
         goto bad_reg;
     }
     gic_update(s);
     return;
 bad_reg:
-    cpu_abort (cpu_single_env, "gic_dist_writeb: Bad offset %x\n", offset);
+    cpu_abort(cpu_single_env, "gic_dist_writeb: Bad offset %x\n", (int)offset);
 }
 
 static void gic_dist_writew(void *opaque, target_phys_addr_t offset,
                             uint32_t value)
 {
-    gic_state *s = (gic_state *)opaque;
-    if (offset - s->base == 0xf00) {
-        GIC_SET_PENDING(value & 0x3ff);
-        gic_update(s);
-        return;
-    }
     gic_dist_writeb(opaque, offset, value & 0xff);
     gic_dist_writeb(opaque, offset + 1, value >> 8);
 }
@@ -425,6 +523,41 @@
 static void gic_dist_writel(void *opaque, target_phys_addr_t offset,
                             uint32_t value)
 {
+    gic_state *s = (gic_state *)opaque;
+#ifdef NVIC
+    uint32_t addr;
+    addr = offset - s->base;
+    if (addr < 0x100 || (addr > 0xd00 && addr != 0xf00)) {
+        nvic_writel(s->nvic, addr, value);
+        return;
+    }
+#endif
+    if (offset - s->base == GIC_DIST_OFFSET + 0xf00) {
+        int cpu;
+        int irq;
+        int mask;
+
+        cpu = gic_get_current_cpu();
+        irq = value & 0x3ff;
+        switch ((value >> 24) & 3) {
+        case 0:
+            mask = (value >> 16) & ALL_CPU_MASK;
+            break;
+        case 1:
+            mask = 1 << cpu;
+            break;
+        case 2:
+            mask = ALL_CPU_MASK ^ (1 << cpu);
+            break;
+        default:
+            DPRINTF("Bad Soft Int target filter\n");
+            mask = ALL_CPU_MASK;
+            break;
+        }
+        GIC_SET_PENDING(irq, mask);
+        gic_update(s);
+        return;
+    }
     gic_dist_writew(opaque, offset, value & 0xffff);
     gic_dist_writew(opaque, offset + 2, value >> 16);
 }
@@ -441,105 +574,100 @@
    gic_dist_writel
 };
 
-static uint32_t gic_cpu_read(void *opaque, target_phys_addr_t offset)
+#ifndef NVIC
+static uint32_t gic_cpu_read(gic_state *s, int cpu, int offset)
 {
-    gic_state *s = (gic_state *)opaque;
-    offset -= s->base;
     switch (offset) {
     case 0x00: /* Control */
-        return s->cpu_enabled;
+        return s->cpu_enabled[cpu];
     case 0x04: /* Priority mask */
-        return s->priority_mask;
+        return s->priority_mask[cpu];
     case 0x08: /* Binary Point */
         /* ??? Not implemented.  */
         return 0;
     case 0x0c: /* Acknowledge */
-        return gic_acknowledge_irq(s);
+        return gic_acknowledge_irq(s, cpu);
     case 0x14: /* Runing Priority */
-        return s->running_priority;
+        return s->running_priority[cpu];
     case 0x18: /* Highest Pending Interrupt */
-        return s->current_pending;
+        return s->current_pending[cpu];
     default:
-        cpu_abort (cpu_single_env, "gic_cpu_read: Bad offset %x\n", offset);
+        cpu_abort(cpu_single_env, "gic_cpu_read: Bad offset %x\n",
+                  (int)offset);
         return 0;
     }
 }
 
-static void gic_cpu_write(void *opaque, target_phys_addr_t offset,
-                          uint32_t value)
+static void gic_cpu_write(gic_state *s, int cpu, int offset, uint32_t value)
 {
-    gic_state *s = (gic_state *)opaque;
-    offset -= s->base;
     switch (offset) {
     case 0x00: /* Control */
-        s->cpu_enabled = (value & 1);
+        s->cpu_enabled[cpu] = (value & 1);
         DPRINTF("CPU %sabled\n", s->cpu_enabled ? "En" : "Dis");
         break;
     case 0x04: /* Priority mask */
-        s->priority_mask = (value & 0x3ff);
+        s->priority_mask[cpu] = (value & 0xff);
         break;
     case 0x08: /* Binary Point */
         /* ??? Not implemented.  */
         break;
     case 0x10: /* End Of Interrupt */
-        return gic_complete_irq(s, value & 0x3ff);
+        return gic_complete_irq(s, cpu, value & 0x3ff);
     default:
-        cpu_abort (cpu_single_env, "gic_cpu_write: Bad offset %x\n", offset);
+        cpu_abort(cpu_single_env, "gic_cpu_write: Bad offset %x\n",
+                  (int)offset);
         return;
     }
     gic_update(s);
 }
-
-static CPUReadMemoryFunc *gic_cpu_readfn[] = {
-   gic_cpu_read,
-   gic_cpu_read,
-   gic_cpu_read
-};
-
-static CPUWriteMemoryFunc *gic_cpu_writefn[] = {
-   gic_cpu_write,
-   gic_cpu_write,
-   gic_cpu_write
-};
+#endif
 
 static void gic_reset(gic_state *s)
 {
     int i;
     memset(s->irq_state, 0, GIC_NIRQ * sizeof(gic_irq_state));
-    s->priority_mask = 0xf0;
-    s->current_pending = 1023;
-    s->running_irq = 1023;
-    s->running_priority = 0x100;
+    for (i = 0 ; i < NCPU; i++) {
+        s->priority_mask[i] = 0xf0;
+        s->current_pending[i] = 1023;
+        s->running_irq[i] = 1023;
+        s->running_priority[i] = 0x100;
+#ifdef NVIC
+        /* The NVIC doesn't have per-cpu interfaces, so enable by default.  */
+        s->cpu_enabled[i] = 1;
+#else
+        s->cpu_enabled[i] = 0;
+#endif
+    }
     for (i = 0; i < 15; i++) {
         GIC_SET_ENABLED(i);
         GIC_SET_TRIGGER(i);
     }
+#ifdef NVIC
+    /* The NVIC is always enabled.  */
+    s->enabled = 1;
+#else
     s->enabled = 0;
-    s->cpu_enabled = 0;
+#endif
 }
 
-qemu_irq *arm_gic_init(uint32_t base, qemu_irq parent_irq)
+static gic_state *gic_init(uint32_t base, qemu_irq *parent_irq)
 {
     gic_state *s;
-    qemu_irq *qi;
     int iomemtype;
+    int i;
 
     s = (gic_state *)qemu_mallocz(sizeof(gic_state));
     if (!s)
         return NULL;
-    qi = qemu_allocate_irqs(gic_set_irq, s, GIC_NIRQ);
-    s->parent_irq = parent_irq;
-    if (base != 0xffffffff) {
-        iomemtype = cpu_register_io_memory(0, gic_cpu_readfn,
-                                           gic_cpu_writefn, s);
-        cpu_register_physical_memory(base, 0x00001000, iomemtype);
-        iomemtype = cpu_register_io_memory(0, gic_dist_readfn,
-                                           gic_dist_writefn, s);
-        cpu_register_physical_memory(base + 0x1000, 0x00001000, iomemtype);
-        s->base = base;
-    } else {
-        s->base = 0;
+    s->in = qemu_allocate_irqs(gic_set_irq, s, GIC_NIRQ);
+    for (i = 0; i < NCPU; i++) {
+        s->parent_irq[i] = parent_irq[i];
     }
+    iomemtype = cpu_register_io_memory(0, gic_dist_readfn,
+                                       gic_dist_writefn, s);
+    cpu_register_physical_memory(base + GIC_DIST_OFFSET, 0x00001000,
+                                 iomemtype);
+    s->base = base;
     gic_reset(s);
-    return qi;
+    return s;
 }
diff --git a/hw/arm_sysctl.c b/hw/arm_sysctl.c
index 468a494..e3179e2 100644
--- a/hw/arm_sysctl.c
+++ b/hw/arm_sysctl.c
@@ -1,7 +1,7 @@
 /*
  * Status and system control registers for ARM RealView/Versatile boards.
  *
- * Copyright (c) 2006 CodeSourcery.
+ * Copyright (c) 2006-2007 CodeSourcery.
  * Written by Paul Brook
  *
  * This code is licenced under the GPL.
@@ -200,6 +200,9 @@
         return;
     s->base = base;
     s->sys_id = sys_id;
+    /* The MPcore bootloader uses these flags to start secondary CPUs.
+       We don't use a bootloader, so do this here.  */
+    s->flags = 3;
     iomemtype = cpu_register_io_memory(0, arm_sysctl_readfn,
                                        arm_sysctl_writefn, s);
     cpu_register_physical_memory(base, 0x00001000, iomemtype);
diff --git a/hw/armv7m.c b/hw/armv7m.c
new file mode 100644
index 0000000..f0a90e1
--- /dev/null
+++ b/hw/armv7m.c
@@ -0,0 +1,204 @@
+/*
+ * ARMV7M System emulation.
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+
+/* Bitbanded IO.  Each word corresponds to a single bit.  */
+
+/* Get the byte address of the real memory for a bitband acess.  */
+static inline uint32_t bitband_addr(uint32_t addr)
+{
+    uint32_t res;
+
+    res = addr & 0xe0000000;
+    res |= (addr & 0x1ffffff) >> 5;
+    return res;
+
+}
+
+static uint32_t bitband_readb(void *opaque, target_phys_addr_t offset)
+{
+    uint8_t v;
+    cpu_physical_memory_read(bitband_addr(offset), &v, 1);
+    return (v & (1 << ((offset >> 2) & 7))) != 0;
+}
+
+static void bitband_writeb(void *opaque, target_phys_addr_t offset,
+                           uint32_t value)
+{
+    uint32_t addr;
+    uint8_t mask;
+    uint8_t v;
+    addr = bitband_addr(offset);
+    mask = (1 << ((offset >> 2) & 7));
+    cpu_physical_memory_read(addr, &v, 1);
+    if (value & 1)
+        v |= mask;
+    else
+        v &= ~mask;
+    cpu_physical_memory_write(addr, &v, 1);
+}
+
+static uint32_t bitband_readw(void *opaque, target_phys_addr_t offset)
+{
+    uint32_t addr;
+    uint16_t mask;
+    uint16_t v;
+    addr = bitband_addr(offset) & ~1;
+    mask = (1 << ((offset >> 2) & 15));
+    mask = tswap16(mask);
+    cpu_physical_memory_read(addr, (uint8_t *)&v, 2);
+    return (v & mask) != 0;
+}
+
+static void bitband_writew(void *opaque, target_phys_addr_t offset,
+                           uint32_t value)
+{
+    uint32_t addr;
+    uint16_t mask;
+    uint16_t v;
+    addr = bitband_addr(offset) & ~1;
+    mask = (1 << ((offset >> 2) & 15));
+    mask = tswap16(mask);
+    cpu_physical_memory_read(addr, (uint8_t *)&v, 2);
+    if (value & 1)
+        v |= mask;
+    else
+        v &= ~mask;
+    cpu_physical_memory_write(addr, (uint8_t *)&v, 2);
+}
+
+static uint32_t bitband_readl(void *opaque, target_phys_addr_t offset)
+{
+    uint32_t addr;
+    uint32_t mask;
+    uint32_t v;
+    addr = bitband_addr(offset) & ~3;
+    mask = (1 << ((offset >> 2) & 31));
+    mask = tswap32(mask);
+    cpu_physical_memory_read(addr, (uint8_t *)&v, 4);
+    return (v & mask) != 0;
+}
+
+static void bitband_writel(void *opaque, target_phys_addr_t offset,
+                           uint32_t value)
+{
+    uint32_t addr;
+    uint32_t mask;
+    uint32_t v;
+    addr = bitband_addr(offset) & ~3;
+    mask = (1 << ((offset >> 2) & 31));
+    mask = tswap32(mask);
+    cpu_physical_memory_read(addr, (uint8_t *)&v, 4);
+    if (value & 1)
+        v |= mask;
+    else
+        v &= ~mask;
+    cpu_physical_memory_write(addr, (uint8_t *)&v, 4);
+}
+
+static CPUReadMemoryFunc *bitband_readfn[] = {
+   bitband_readb,
+   bitband_readw,
+   bitband_readl
+};
+
+static CPUWriteMemoryFunc *bitband_writefn[] = {
+   bitband_writeb,
+   bitband_writew,
+   bitband_writel
+};
+
+static void armv7m_bitband_init(void)
+{
+    int iomemtype;
+
+    iomemtype = cpu_register_io_memory(0, bitband_readfn, bitband_writefn,
+                                       NULL);
+    cpu_register_physical_memory(0x22000000, 0x02000000, iomemtype);
+    cpu_register_physical_memory(0x42000000, 0x02000000, iomemtype);
+}
+
+/* Board init.  */
+/* Init CPU and memory for a v7-M based board.
+   flash_size and sram_size are in kb.
+   Returns the NVIC array.  */
+
+qemu_irq *armv7m_init(int flash_size, int sram_size,
+                      const char *kernel_filename, const char *cpu_model)
+{
+    CPUState *env;
+    qemu_irq *pic;
+    uint32_t pc;
+    int image_size;
+    uint64_t entry;
+    uint64_t lowaddr;
+
+    flash_size *= 1024;
+    sram_size *= 1024;
+
+    if (!cpu_model)
+	cpu_model = "cortex-m3";
+    env = cpu_init(cpu_model);
+    if (!env) {
+        fprintf(stderr, "Unable to find CPU definition\n");
+        exit(1);
+    }
+
+#if 0
+    /* > 32Mb SRAM gets complicated because it overlaps the bitband area.
+       We don't have proper commandline options, so allocate half of memory
+       as SRAM, up to a maximum of 32Mb, and the rest as code.  */
+    if (ram_size > (512 + 32) * 1024 * 1024)
+        ram_size = (512 + 32) * 1024 * 1024;
+    sram_size = (ram_size / 2) & TARGET_PAGE_MASK;
+    if (sram_size > 32 * 1024 * 1024)
+        sram_size = 32 * 1024 * 1024;
+    code_size = ram_size - sram_size;
+#endif
+
+    /* Flash programming is done via the SCU, so pretend it is ROM.  */
+    cpu_register_physical_memory(0, flash_size, IO_MEM_ROM);
+    cpu_register_physical_memory(0x20000000, sram_size,
+                                 flash_size + IO_MEM_RAM);
+    armv7m_bitband_init();
+
+    pic = armv7m_nvic_init(env);
+
+    image_size = load_elf(kernel_filename, 0, &entry, &lowaddr, NULL);
+    if (image_size < 0) {
+        image_size = load_image(kernel_filename, phys_ram_base);
+	lowaddr = 0;
+    }
+    if (image_size < 0) {
+        fprintf(stderr, "qemu: could not load kernel '%s'\n",
+                kernel_filename);
+        exit(1);
+    }
+
+    /* If the image was loaded at address zero then assume it is a
+       regular ROM image and perform the normal CPU reset sequence.
+       Otherwise jump directly to the entry point.  */
+    if (lowaddr == 0) {
+	env->regs[13] = tswap32(*(uint32_t *)phys_ram_base);
+	pc = tswap32(*(uint32_t *)(phys_ram_base + 4));
+    } else {
+	pc = entry;
+    }
+    env->thumb = pc & 1;
+    env->regs[15] = pc & ~1;
+
+    /* Hack to map an additional page of ram at the top of the address
+       space.  This stops qemu complaining about executing code outside RAM
+       when returning from an exception.  */
+    cpu_register_physical_memory(0xfffff000, 0x1000, IO_MEM_RAM + ram_size);
+
+    return pic;
+}
+
diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c
new file mode 100644
index 0000000..d304082
--- /dev/null
+++ b/hw/armv7m_nvic.c
@@ -0,0 +1,381 @@
+/*
+ * ARM Nested Vectored Interrupt Controller
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ *
+ * The ARMv7M System controller is fairly tightly tied in with the
+ * NVIC.  Much of that is also implemented here.
+ */
+
+#include "vl.h"
+#include "arm_pic.h"
+
+#define GIC_NIRQ 64
+#define NCPU 1
+#define NVIC 1
+
+/* Only a single "CPU" interface is present.  */
+static inline int
+gic_get_current_cpu(void)
+{
+    return 0;
+}
+
+static uint32_t nvic_readl(void *opaque, uint32_t offset);
+static void nvic_writel(void *opaque, uint32_t offset, uint32_t value);
+
+#include "arm_gic.c"
+
+typedef struct {
+    struct {
+        uint32_t control;
+        uint32_t reload;
+        int64_t tick;
+        QEMUTimer *timer;
+    } systick;
+    gic_state *gic;
+} nvic_state;
+
+/* qemu timers run at 1GHz.   We want something closer to 1MHz.  */
+#define SYSTICK_SCALE 1000ULL
+
+#define SYSTICK_ENABLE    (1 << 0)
+#define SYSTICK_TICKINT   (1 << 1)
+#define SYSTICK_CLKSOURCE (1 << 2)
+#define SYSTICK_COUNTFLAG (1 << 16)
+
+/* Conversion factor from qemu timer to SysTick frequencies.
+   QEMU uses a base of 1GHz, so these give 20MHz and 1MHz for core and
+   reference frequencies.  */
+
+static inline int64_t systick_scale(nvic_state *s)
+{
+    if (s->systick.control & SYSTICK_CLKSOURCE)
+        return 50;
+    else
+        return 1000;
+}
+
+static void systick_reload(nvic_state *s, int reset)
+{
+    if (reset)
+        s->systick.tick = qemu_get_clock(vm_clock);
+    s->systick.tick += (s->systick.reload + 1) * systick_scale(s);
+    qemu_mod_timer(s->systick.timer, s->systick.tick);
+}
+
+static void systick_timer_tick(void * opaque)
+{
+    nvic_state *s = (nvic_state *)opaque;
+    s->systick.control |= SYSTICK_COUNTFLAG;
+    if (s->systick.control & SYSTICK_TICKINT) {
+        /* Trigger the interrupt.  */
+        armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK);
+    }
+    if (s->systick.reload == 0) {
+        s->systick.control &= ~SYSTICK_ENABLE;
+    } else {
+        systick_reload(s, 0);
+    }
+}
+
+/* The external routines use the hardware vector numbering, ie. the first
+   IRQ is #16.  The internal GIC routines use #32 as the first IRQ.  */
+void armv7m_nvic_set_pending(void *opaque, int irq)
+{
+    nvic_state *s = (nvic_state *)opaque;
+    if (irq >= 16)
+        irq += 16;
+    gic_set_pending_private(s->gic, 0, irq);
+}
+
+/* Make pending IRQ active.  */
+int armv7m_nvic_acknowledge_irq(void *opaque)
+{
+    nvic_state *s = (nvic_state *)opaque;
+    uint32_t irq;
+
+    irq = gic_acknowledge_irq(s->gic, 0);
+    if (irq == 1023)
+        cpu_abort(cpu_single_env, "Interrupt but no vector\n");
+    if (irq >= 32)
+        irq -= 16;
+    return irq;
+}
+
+void armv7m_nvic_complete_irq(void *opaque, int irq)
+{
+    nvic_state *s = (nvic_state *)opaque;
+    if (irq >= 16)
+        irq += 16;
+    gic_complete_irq(s->gic, 0, irq);
+}
+
+static uint32_t nvic_readl(void *opaque, uint32_t offset)
+{
+    nvic_state *s = (nvic_state *)opaque;
+    uint32_t val;
+    int irq;
+
+    switch (offset) {
+    case 4: /* Interrupt Control Type.  */
+        return (GIC_NIRQ / 32) - 1;
+    case 0x10: /* SysTick Control and Status.  */
+        val = s->systick.control;
+        s->systick.control &= ~SYSTICK_COUNTFLAG;
+        return val;
+    case 0x14: /* SysTick Reload Value.  */
+        return s->systick.reload;
+    case 0x18: /* SysTick Current Value.  */
+        {
+            int64_t t;
+            if ((s->systick.control & SYSTICK_ENABLE) == 0)
+                return 0;
+            t = qemu_get_clock(vm_clock);
+            if (t >= s->systick.tick)
+                return 0;
+            val = ((s->systick.tick - (t + 1)) / systick_scale(s)) + 1;
+            /* The interrupt in triggered when the timer reaches zero.
+               However the counter is not reloaded until the next clock
+               tick.  This is a hack to return zero during the first tick.  */
+            if (val > s->systick.reload)
+                val = 0;
+            return val;
+        }
+    case 0x1c: /* SysTick Calibration Value.  */
+        return 10000;
+    case 0xd00: /* CPUID Base.  */
+        return cpu_single_env->cp15.c0_cpuid;
+    case 0xd04: /* Interrypt Control State.  */
+        /* VECTACTIVE */
+        val = s->gic->running_irq[0];
+        if (val == 1023) {
+            val = 0;
+        } else if (val >= 32) {
+            val -= 16;
+        }
+        /* RETTOBASE */
+        if (s->gic->running_irq[0] == 1023
+                || s->gic->last_active[s->gic->running_irq[0]][0] == 1023) {
+            val |= (1 << 11);
+        }
+        /* VECTPENDING */
+        if (s->gic->current_pending[0] != 1023)
+            val |= (s->gic->current_pending[0] << 12);
+        /* ISRPENDING */
+        for (irq = 32; irq < GIC_NIRQ; irq++) {
+            if (s->gic->irq_state[irq].pending) {
+                val |= (1 << 22);
+                break;
+            }
+        }
+        /* PENDSTSET */
+        if (s->gic->irq_state[ARMV7M_EXCP_SYSTICK].pending)
+            val |= (1 << 26);
+        /* PENDSVSET */
+        if (s->gic->irq_state[ARMV7M_EXCP_PENDSV].pending)
+            val |= (1 << 28);
+        /* NMIPENDSET */
+        if (s->gic->irq_state[ARMV7M_EXCP_NMI].pending)
+            val |= (1 << 31);
+        return val;
+    case 0xd08: /* Vector Table Offset.  */
+        return cpu_single_env->v7m.vecbase;
+    case 0xd0c: /* Application Interrupt/Reset Control.  */
+        return 0xfa05000;
+    case 0xd10: /* System Control.  */
+        /* TODO: Implement SLEEPONEXIT.  */
+        return 0;
+    case 0xd14: /* Configuration Control.  */
+        /* TODO: Implement Configuration Control bits.  */
+        return 0;
+    case 0xd18: case 0xd1c: case 0xd20: /* System Handler Priority.  */
+        irq = offset - 0xd14;
+        val = 0;
+        val = s->gic->priority1[irq++][0];
+        val = s->gic->priority1[irq++][0] << 8;
+        val = s->gic->priority1[irq++][0] << 16;
+        val = s->gic->priority1[irq][0] << 24;
+        return val;
+    case 0xd24: /* System Handler Status.  */
+        val = 0;
+        if (s->gic->irq_state[ARMV7M_EXCP_MEM].active) val |= (1 << 0);
+        if (s->gic->irq_state[ARMV7M_EXCP_BUS].active) val |= (1 << 1);
+        if (s->gic->irq_state[ARMV7M_EXCP_USAGE].active) val |= (1 << 3);
+        if (s->gic->irq_state[ARMV7M_EXCP_SVC].active) val |= (1 << 7);
+        if (s->gic->irq_state[ARMV7M_EXCP_DEBUG].active) val |= (1 << 8);
+        if (s->gic->irq_state[ARMV7M_EXCP_PENDSV].active) val |= (1 << 10);
+        if (s->gic->irq_state[ARMV7M_EXCP_SYSTICK].active) val |= (1 << 11);
+        if (s->gic->irq_state[ARMV7M_EXCP_USAGE].pending) val |= (1 << 12);
+        if (s->gic->irq_state[ARMV7M_EXCP_MEM].pending) val |= (1 << 13);
+        if (s->gic->irq_state[ARMV7M_EXCP_BUS].pending) val |= (1 << 14);
+        if (s->gic->irq_state[ARMV7M_EXCP_SVC].pending) val |= (1 << 15);
+        if (s->gic->irq_state[ARMV7M_EXCP_MEM].enabled) val |= (1 << 16);
+        if (s->gic->irq_state[ARMV7M_EXCP_BUS].enabled) val |= (1 << 17);
+        if (s->gic->irq_state[ARMV7M_EXCP_USAGE].enabled) val |= (1 << 18);
+        return val;
+    case 0xd28: /* Configurable Fault Status.  */
+        /* TODO: Implement Fault Status.  */
+        cpu_abort(cpu_single_env,
+                  "Not implemented: Configurable Fault Status.");
+        return 0;
+    case 0xd2c: /* Hard Fault Status.  */
+    case 0xd30: /* Debug Fault Status.  */
+    case 0xd34: /* Mem Manage Address.  */
+    case 0xd38: /* Bus Fault Address.  */
+    case 0xd3c: /* Aux Fault Status.  */
+        /* TODO: Implement fault status registers.  */
+        goto bad_reg;
+    case 0xd40: /* PFR0.  */
+        return 0x00000030;
+    case 0xd44: /* PRF1.  */
+        return 0x00000200;
+    case 0xd48: /* DFR0.  */
+        return 0x00100000;
+    case 0xd4c: /* AFR0.  */
+        return 0x00000000;
+    case 0xd50: /* MMFR0.  */
+        return 0x00000030;
+    case 0xd54: /* MMFR1.  */
+        return 0x00000000;
+    case 0xd58: /* MMFR2.  */
+        return 0x00000000;
+    case 0xd5c: /* MMFR3.  */
+        return 0x00000000;
+    case 0xd60: /* ISAR0.  */
+        return 0x01141110;
+    case 0xd64: /* ISAR1.  */
+        return 0x02111000;
+    case 0xd68: /* ISAR2.  */
+        return 0x21112231;
+    case 0xd6c: /* ISAR3.  */
+        return 0x01111110;
+    case 0xd70: /* ISAR4.  */
+        return 0x01310102;
+    /* TODO: Implement debug registers.  */
+    default:
+    bad_reg:
+        cpu_abort(cpu_single_env, "NVIC: Bad read offset 0x%x\n", offset);
+    }
+}
+
+static void nvic_writel(void *opaque, uint32_t offset, uint32_t value)
+{
+    nvic_state *s = (nvic_state *)opaque;
+    uint32_t oldval;
+    switch (offset) {
+    case 0x10: /* SysTick Control and Status.  */
+        oldval = s->systick.control;
+        s->systick.control &= 0xfffffff8;
+        s->systick.control |= value & 7;
+        if ((oldval ^ value) & SYSTICK_ENABLE) {
+            int64_t now = qemu_get_clock(vm_clock);
+            if (value & SYSTICK_ENABLE) {
+                if (s->systick.tick) {
+                    s->systick.tick += now;
+                    qemu_mod_timer(s->systick.timer, s->systick.tick);
+                } else {
+                    systick_reload(s, 1);
+                }
+            } else {
+                qemu_del_timer(s->systick.timer);
+                s->systick.tick -= now;
+                if (s->systick.tick < 0)
+                  s->systick.tick = 0;
+            }
+        } else if ((oldval ^ value) & SYSTICK_CLKSOURCE) {
+            /* This is a hack. Force the timer to be reloaded
+               when the reference clock is changed.  */
+            systick_reload(s, 1);
+        }
+        break;
+    case 0x14: /* SysTick Reload Value.  */
+        s->systick.reload = value;
+        break;
+    case 0x18: /* SysTick Current Value.  Writes reload the timer.  */
+        systick_reload(s, 1);
+        s->systick.control &= ~SYSTICK_COUNTFLAG;
+        break;
+    case 0xd04: /* Interrupt Control State.  */
+        if (value & (1 << 31)) {
+            armv7m_nvic_set_pending(s, ARMV7M_EXCP_NMI);
+        }
+        if (value & (1 << 28)) {
+            armv7m_nvic_set_pending(s, ARMV7M_EXCP_PENDSV);
+        } else if (value & (1 << 27)) {
+            s->gic->irq_state[ARMV7M_EXCP_PENDSV].pending = 0;
+            gic_update(s->gic);
+        }
+        if (value & (1 << 26)) {
+            armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK);
+        } else if (value & (1 << 25)) {
+            s->gic->irq_state[ARMV7M_EXCP_SYSTICK].pending = 0;
+            gic_update(s->gic);
+        }
+        break;
+    case 0xd08: /* Vector Table Offset.  */
+        cpu_single_env->v7m.vecbase = value & 0xffffff80;
+        break;
+    case 0xd0c: /* Application Interrupt/Reset Control.  */
+        if ((value >> 16) == 0x05fa) {
+            if (value & 2) {
+                cpu_abort(cpu_single_env, "VECTCLRACTIVE not implemented");
+            }
+            if (value & 5) {
+                cpu_abort(cpu_single_env, "System reset");
+            }
+        }
+        break;
+    case 0xd10: /* System Control.  */
+    case 0xd14: /* Configuration Control.  */
+        /* TODO: Implement control registers.  */
+        goto bad_reg;
+    case 0xd18: case 0xd1c: case 0xd20: /* System Handler Priority.  */
+        {
+            int irq;
+            irq = offset - 0xd14;
+            s->gic->priority1[irq++][0] = value & 0xff;
+            s->gic->priority1[irq++][0] = (value >> 8) & 0xff;
+            s->gic->priority1[irq++][0] = (value >> 16) & 0xff;
+            s->gic->priority1[irq][0] = (value >> 24) & 0xff;
+            gic_update(s->gic);
+        }
+        break;
+    case 0xd24: /* System Handler Control.  */
+        /* TODO: Real hardware allows you to set/clear the active bits
+           under some circumstances.  We don't implement this.  */
+        s->gic->irq_state[ARMV7M_EXCP_MEM].enabled = (value & (1 << 16)) != 0;
+        s->gic->irq_state[ARMV7M_EXCP_BUS].enabled = (value & (1 << 17)) != 0;
+        s->gic->irq_state[ARMV7M_EXCP_USAGE].enabled = (value & (1 << 18)) != 0;
+        break;
+    case 0xd28: /* Configurable Fault Status.  */
+    case 0xd2c: /* Hard Fault Status.  */
+    case 0xd30: /* Debug Fault Status.  */
+    case 0xd34: /* Mem Manage Address.  */
+    case 0xd38: /* Bus Fault Address.  */
+    case 0xd3c: /* Aux Fault Status.  */
+        goto bad_reg;
+    default:
+    bad_reg:
+        cpu_abort(cpu_single_env, "NVIC: Bad write offset 0x%x\n", offset);
+    }
+}
+
+qemu_irq *armv7m_nvic_init(CPUState *env)
+{
+    nvic_state *s;
+    qemu_irq *parent;
+
+    parent = arm_pic_init_cpu(env);
+    s = (nvic_state *)qemu_mallocz(sizeof(nvic_state));
+    s->gic = gic_init(0xe000e000, &parent[ARM_PIC_CPU_IRQ]);
+    s->gic->nvic = s;
+    s->systick.timer = qemu_new_timer(vm_clock, systick_timer_tick, s);
+    if (env->v7m.nvic)
+        cpu_abort(env, "CPU can only have one NVIC\n");
+    env->v7m.nvic = s;
+    return s->gic->in;
+}
diff --git a/hw/integratorcp.c b/hw/integratorcp.c
index 75315a8..31e7d7d 100644
--- a/hw/integratorcp.c
+++ b/hw/integratorcp.c
@@ -497,8 +497,8 @@
     icp_pic_init(0xca000000, pic[26], NULL);
     icp_pit_init(0x13000000, pic, 5);
     pl031_init(0x15000000, pic[8]);
-    pl011_init(0x16000000, pic[1], serial_hds[0]);
-    pl011_init(0x17000000, pic[2], serial_hds[1]);
+    pl011_init(0x16000000, pic[1], serial_hds[0], PL011_ARM);
+    pl011_init(0x17000000, pic[2], serial_hds[1], PL011_ARM);
     icp_control_init(0xcb000000);
     pl050_init(0x18000000, pic[3], 0);
     pl050_init(0x19000000, pic[4], 1);
diff --git a/hw/mpcore.c b/hw/mpcore.c
new file mode 100644
index 0000000..cc33208
--- /dev/null
+++ b/hw/mpcore.c
@@ -0,0 +1,323 @@
+/*
+ * ARM MPCore internal peripheral emulation.
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+
+#define MPCORE_PRIV_BASE  0x10100000
+#define NCPU 4
+/* ??? The MPCore TRM says the on-chip controller has 224 external IRQ lines
+   (+ 32 internal).  However my test chip only exposes/reports 32.
+   More importantly Linux falls over if more than 32 are present!  */
+#define GIC_NIRQ 64
+
+static inline int
+gic_get_current_cpu(void)
+{
+  return cpu_single_env->cpu_index;
+}
+
+#include "arm_gic.c"
+
+/* MPCore private memory region.  */
+
+typedef struct {
+    uint32_t count;
+    uint32_t load;
+    uint32_t control;
+    uint32_t status;
+    uint32_t old_status;
+    int64_t tick;
+    QEMUTimer *timer;
+    struct mpcore_priv_state *mpcore;
+    int id; /* Encodes both timer/watchdog and CPU.  */
+} mpcore_timer_state;
+
+typedef struct mpcore_priv_state {
+    gic_state *gic;
+    uint32_t scu_control;
+    mpcore_timer_state timer[8];
+} mpcore_priv_state;
+
+/* Per-CPU Timers.  */
+
+static inline void mpcore_timer_update_irq(mpcore_timer_state *s)
+{
+    if (s->status & ~s->old_status) {
+        gic_set_pending_private(s->mpcore->gic, s->id >> 1, 29 + (s->id & 1));
+    }
+    s->old_status = s->status;
+}
+
+/* Return conversion factor from mpcore timer ticks to qemu timer ticks.  */
+static inline uint32_t mpcore_timer_scale(mpcore_timer_state *s)
+{
+    return (((s->control >> 8) & 0xff) + 1) * 10;
+}
+
+static void mpcore_timer_reload(mpcore_timer_state *s, int restart)
+{
+    if (s->count == 0)
+        return;
+    if (restart)
+        s->tick = qemu_get_clock(vm_clock);
+    s->tick += (int64_t)s->count * mpcore_timer_scale(s);
+    qemu_mod_timer(s->timer, s->tick);
+}
+
+static void mpcore_timer_tick(void *opaque)
+{
+    mpcore_timer_state *s = (mpcore_timer_state *)opaque;
+    s->status = 1;
+    if (s->control & 2) {
+        s->count = s->load;
+        mpcore_timer_reload(s, 0);
+    } else {
+        s->count = 0;
+    }
+    mpcore_timer_update_irq(s);
+}
+
+static uint32_t mpcore_timer_read(mpcore_timer_state *s, int offset)
+{
+    int64_t val;
+    switch (offset) {
+    case 0: /* Load */
+        return s->load;
+        /* Fall through.  */
+    case 4: /* Counter.  */
+        if (((s->control & 1) == 0) || (s->count == 0))
+            return 0;
+        /* Slow and ugly, but hopefully won't happen too often.  */
+        val = s->tick - qemu_get_clock(vm_clock);
+        val /= mpcore_timer_scale(s);
+        if (val < 0)
+            val = 0;
+        return val;
+    case 8: /* Control.  */
+        return s->control;
+    case 12: /* Interrupt status.  */
+        return s->status;
+    }
+}
+
+static void mpcore_timer_write(mpcore_timer_state *s, int offset,
+                               uint32_t value)
+{
+    int64_t old;
+    switch (offset) {
+    case 0: /* Load */
+        s->load = value;
+        /* Fall through.  */
+    case 4: /* Counter.  */
+        if ((s->control & 1) && s->count) {
+            /* Cancel the previous timer.  */
+            qemu_del_timer(s->timer);
+        }
+        s->count = value;
+        if (s->control & 1) {
+            mpcore_timer_reload(s, 1);
+        }
+        break;
+    case 8: /* Control.  */
+        old = s->control;
+        s->control = value;
+        if (((old & 1) == 0) && (value & 1)) {
+            if (s->count == 0 && (s->control & 2))
+                s->count = s->load;
+            mpcore_timer_reload(s, 1);
+        }
+        break;
+    case 12: /* Interrupt status.  */
+        s->status &= ~value;
+        mpcore_timer_update_irq(s);
+        break;
+    }
+}
+
+static void mpcore_timer_init(mpcore_priv_state *mpcore,
+                              mpcore_timer_state *s, int id)
+{
+    s->id = id;
+    s->mpcore = mpcore;
+    s->timer = qemu_new_timer(vm_clock, mpcore_timer_tick, s);
+}
+
+
+/* Per-CPU private memory mapped IO.  */
+
+static uint32_t mpcore_priv_read(void *opaque, target_phys_addr_t offset)
+{
+    mpcore_priv_state *s = (mpcore_priv_state *)opaque;
+    int id;
+    offset &= 0xfff;
+    if (offset < 0x100) {
+        /* SCU */
+        switch (offset) {
+        case 0x00: /* Control.  */
+            return s->scu_control;
+        case 0x04: /* Configuration.  */
+            return 0xf3;
+        case 0x08: /* CPU status.  */
+            return 0;
+        case 0x0c: /* Invalidate all.  */
+            return 0;
+        default:
+            goto bad_reg;
+        }
+    } else if (offset < 0x600) {
+        /* Interrupt controller.  */
+        if (offset < 0x200) {
+            id = gic_get_current_cpu();
+        } else {
+            id = (offset - 0x200) >> 8;
+        }
+        return gic_cpu_read(s->gic, id, offset & 0xff);
+    } else if (offset < 0xb00) {
+        /* Timers.  */
+        if (offset < 0x700) {
+            id = gic_get_current_cpu();
+        } else {
+            id = (offset - 0x700) >> 8;
+        }
+        id <<= 1;
+        if (offset & 0x20)
+          id++;
+        return mpcore_timer_read(&s->timer[id], offset & 0xf);
+    }
+bad_reg:
+    cpu_abort(cpu_single_env, "mpcore_priv_read: Bad offset %x\n",
+              (int)offset);
+    return 0;
+}
+
+static void mpcore_priv_write(void *opaque, target_phys_addr_t offset,
+                          uint32_t value)
+{
+    mpcore_priv_state *s = (mpcore_priv_state *)opaque;
+    int id;
+    offset &= 0xfff;
+    if (offset < 0x100) {
+        /* SCU */
+        switch (offset) {
+        case 0: /* Control register.  */
+            s->scu_control = value & 1;
+            break;
+        case 0x0c: /* Invalidate all.  */
+            /* This is a no-op as cache is not emulated.  */
+            break;
+        default:
+            goto bad_reg;
+        }
+    } else if (offset < 0x600) {
+        /* Interrupt controller.  */
+        if (offset < 0x200) {
+            id = gic_get_current_cpu();
+        } else {
+            id = (offset - 0x200) >> 8;
+        }
+        gic_cpu_write(s->gic, id, offset & 0xff, value);
+    } else if (offset < 0xb00) {
+        /* Timers.  */
+        if (offset < 0x700) {
+            id = gic_get_current_cpu();
+        } else {
+            id = (offset - 0x700) >> 8;
+        }
+        id <<= 1;
+        if (offset & 0x20)
+          id++;
+        mpcore_timer_write(&s->timer[id], offset & 0xf, value);
+        return;
+    }
+    return;
+bad_reg:
+    cpu_abort(cpu_single_env, "mpcore_priv_read: Bad offset %x\n",
+              (int)offset);
+}
+
+static CPUReadMemoryFunc *mpcore_priv_readfn[] = {
+   mpcore_priv_read,
+   mpcore_priv_read,
+   mpcore_priv_read
+};
+
+static CPUWriteMemoryFunc *mpcore_priv_writefn[] = {
+   mpcore_priv_write,
+   mpcore_priv_write,
+   mpcore_priv_write
+};
+
+
+static qemu_irq *mpcore_priv_init(uint32_t base, qemu_irq *pic_irq)
+{
+    mpcore_priv_state *s;
+    int iomemtype;
+    int i;
+
+    s = (mpcore_priv_state *)qemu_mallocz(sizeof(mpcore_priv_state));
+    if (!s)
+        return NULL;
+    s->gic = gic_init(base, pic_irq);
+    if (!s->gic)
+        return NULL;
+    iomemtype = cpu_register_io_memory(0, mpcore_priv_readfn,
+                                       mpcore_priv_writefn, s);
+    cpu_register_physical_memory(base, 0x00001000, iomemtype);
+    for (i = 0; i < 8; i++) {
+        mpcore_timer_init(s, &s->timer[i], i);
+    }
+    return s->gic->in;
+}
+
+/* Dummy PIC to route IRQ lines.  The baseboard has 4 independent IRQ
+   controllers.  The output of these, plus some of the raw input lines
+   are fed into a single SMP-aware interrupt controller on the CPU.  */
+typedef struct {
+    qemu_irq *cpuic;
+    qemu_irq *rvic[4];
+} mpcore_rirq_state;
+
+/* Map baseboard IRQs onto CPU IRQ lines.  */
+static const int mpcore_irq_map[32] = {
+    -1, -1, -1, -1,  1,  2, -1, -1,
+    -1, -1,  6, -1,  4,  5, -1, -1,
+    -1, 14, 15,  0,  7,  8, -1, -1,
+    -1, -1, -1, -1,  9,  3, -1, -1,
+};
+
+static void mpcore_rirq_set_irq(void *opaque, int irq, int level)
+{
+    mpcore_rirq_state *s = (mpcore_rirq_state *)opaque;
+    int i;
+
+    for (i = 0; i < 4; i++) {
+        qemu_set_irq(s->rvic[i][irq], level);
+    }
+    if (irq < 32) {
+        irq = mpcore_irq_map[irq];
+        if (irq >= 0) {
+            qemu_set_irq(s->cpuic[irq], level);
+        }
+    }
+}
+
+qemu_irq *mpcore_irq_init(qemu_irq *cpu_irq)
+{
+    mpcore_rirq_state *s;
+    int n;
+
+    /* ??? IRQ routing is hardcoded to "normal" mode.  */
+    s = qemu_mallocz(sizeof(mpcore_rirq_state));
+    s->cpuic = mpcore_priv_init(MPCORE_PRIV_BASE, cpu_irq);
+    for (n = 0; n < 4; n++) {
+        s->rvic[n] = realview_gic_init(0x10040000 + n * 0x10000,
+                                       s->cpuic[10 + n]);
+    }
+    return qemu_allocate_irqs(mpcore_rirq_set_irq, s, 64);
+}
diff --git a/hw/pl011.c b/hw/pl011.c
index df33491..9037554 100644
--- a/hw/pl011.c
+++ b/hw/pl011.c
@@ -28,6 +28,7 @@
     int read_trigger;
     CharDriverState *chr;
     qemu_irq irq;
+    enum pl011_type type;
 } pl011_state;
 
 #define PL011_INT_TX 0x20
@@ -38,8 +39,10 @@
 #define PL011_FLAG_TXFF 0x20
 #define PL011_FLAG_RXFE 0x10
 
-static const unsigned char pl011_id[] =
-{ 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+static const unsigned char pl011_id[2][8] = {
+  { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }, /* PL011_ARM */
+  { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 }, /* PL011_LUMINARY */
+};
 
 static void pl011_update(pl011_state *s)
 {
@@ -56,7 +59,7 @@
 
     offset -= s->base;
     if (offset >= 0xfe0 && offset < 0x1000) {
-        return pl011_id[(offset - 0xfe0) >> 2];
+        return pl011_id[s->type][(offset - 0xfe0) >> 2];
     }
     switch (offset >> 2) {
     case 0: /* UARTDR */
@@ -137,6 +140,9 @@
     case 1: /* UARTCR */
         s->cr = value;
         break;
+    case 6: /* UARTFR */
+        /* Writes to Flag register are ignored.  */
+        break;
     case 8: /* UARTUARTILPR */
         s->ilpr = value;
         break;
@@ -224,7 +230,7 @@
 };
 
 void pl011_init(uint32_t base, qemu_irq irq,
-                CharDriverState *chr)
+                CharDriverState *chr, enum pl011_type type)
 {
     int iomemtype;
     pl011_state *s;
@@ -235,6 +241,7 @@
     cpu_register_physical_memory(base, 0x00001000, iomemtype);
     s->base = base;
     s->irq = irq;
+    s->type = type;
     s->chr = chr;
     s->read_trigger = 1;
     s->ifl = 0x12;
diff --git a/hw/pl022.c b/hw/pl022.c
new file mode 100644
index 0000000..d7c735b
--- /dev/null
+++ b/hw/pl022.c
@@ -0,0 +1,264 @@
+/*
+ * Arm PrimeCell PL022 Synchronous Serial Port
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+
+//#define DEBUG_PL022 1
+
+#ifdef DEBUG_PL022
+#define DPRINTF(fmt, args...) \
+do { printf("pl022: " fmt , ##args); } while (0)
+#define BADF(fmt, args...) \
+do { fprintf(stderr, "pl022: error: " fmt , ##args); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while(0)
+#define BADF(fmt, args...) \
+do { fprintf(stderr, "pl022: error: " fmt , ##args);} while (0)
+#endif
+
+#define PL022_CR1_LBM 0x01
+#define PL022_CR1_SSE 0x02
+#define PL022_CR1_MS  0x04
+#define PL022_CR1_SDO 0x08
+
+#define PL022_SR_TFE  0x01
+#define PL022_SR_TNF  0x02
+#define PL022_SR_RNE  0x04
+#define PL022_SR_RFF  0x08
+#define PL022_SR_BSY  0x10
+
+#define PL022_INT_ROR 0x01
+#define PL022_INT_RT  0x04
+#define PL022_INT_RX  0x04
+#define PL022_INT_TX  0x08
+
+typedef struct {
+    uint32_t base;
+    uint32_t cr0;
+    uint32_t cr1;
+    uint32_t bitmask;
+    uint32_t sr;
+    uint32_t cpsr;
+    uint32_t is;
+    uint32_t im;
+    /* The FIFO head points to the next empty entry.  */
+    int tx_fifo_head;
+    int rx_fifo_head;
+    int tx_fifo_len;
+    int rx_fifo_len;
+    uint16_t tx_fifo[8];
+    uint16_t rx_fifo[8];
+    qemu_irq irq;
+    int (*xfer_cb)(void *, int);
+    void *opaque;
+} pl022_state;
+
+static const unsigned char pl022_id[8] =
+  { 0x22, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };
+
+static void pl022_update(pl022_state *s)
+{
+    s->sr = 0;
+    if (s->tx_fifo_len == 0)
+        s->sr |= PL022_SR_TFE;
+    if (s->tx_fifo_len != 8)
+        s->sr |= PL022_SR_TNF;
+    if (s->rx_fifo_len != 0)
+        s->sr |= PL022_SR_RNE;
+    if (s->rx_fifo_len == 8)
+        s->sr |= PL022_SR_RFF;
+    if (s->tx_fifo_len)
+        s->sr |= PL022_SR_BSY;
+    s->is = 0;
+    if (s->rx_fifo_len >= 4)
+        s->is |= PL022_INT_RX;
+    if (s->tx_fifo_len <= 4)
+        s->is |= PL022_INT_TX;
+
+    qemu_set_irq(s->irq, (s->is & s->im) != 0);
+}
+
+static void pl022_xfer(pl022_state *s)
+{
+    int i;
+    int o;
+    int val;
+
+    if ((s->cr1 & PL022_CR1_SSE) == 0) {
+        pl022_update(s);
+        DPRINTF("Disabled\n");
+        return;
+    }
+
+    DPRINTF("Maybe xfer %d/%d\n", s->tx_fifo_len, s->rx_fifo_len);
+    i = (s->tx_fifo_head - s->tx_fifo_len) & 7;
+    o = s->rx_fifo_head;
+    /* ??? We do not emulate the line speed.
+       This may break some applications.  The are two problematic cases:
+        (a) A driver feeds data into the TX FIFO until it is full,
+         and only then drains the RX FIFO.  On real hardware the CPU can
+         feed data fast enough that the RX fifo never gets chance to overflow.
+        (b) A driver transmits data, deliberately allowing the RX FIFO to
+         overflow because it ignores the RX data anyway.
+
+       We choose to support (a) by stalling the transmit engine if it would
+       cause the RX FIFO to overflow.  In practice much transmit-only code
+       falls into (a) because it flushes the RX FIFO to determine when
+       the transfer has completed.  */
+    while (s->tx_fifo_len && s->rx_fifo_len < 8) {
+        DPRINTF("xfer\n");
+        val = s->tx_fifo[i];
+        if (s->cr1 & PL022_CR1_LBM) {
+            /* Loopback mode.  */
+        } else if (s->xfer_cb) {
+            val = s->xfer_cb(s->opaque, val);
+        } else {
+            val = 0;
+        }
+        s->rx_fifo[o] = val & s->bitmask;
+        i = (i + 1) & 7;
+        o = (o + 1) & 7;
+        s->tx_fifo_len--;
+        s->rx_fifo_len++;
+    }
+    s->rx_fifo_head = o;
+    pl022_update(s);
+}
+
+static uint32_t pl022_read(void *opaque, target_phys_addr_t offset)
+{
+    pl022_state *s = (pl022_state *)opaque;
+    int val;
+
+    offset -= s->base;
+    if (offset >= 0xfe0 && offset < 0x1000) {
+        return pl022_id[(offset - 0xfe0) >> 2];
+    }
+    switch (offset) {
+    case 0x00: /* CR0 */
+      return s->cr0;
+    case 0x04: /* CR1 */
+      return s->cr1;
+    case 0x08: /* DR */
+        if (s->rx_fifo_len) {
+            val = s->rx_fifo[(s->rx_fifo_head - s->rx_fifo_len) & 7];
+            DPRINTF("RX %02x\n", val);
+            s->rx_fifo_len--;
+            pl022_xfer(s);
+        } else {
+            val = 0;
+        }
+        return val;
+    case 0x0c: /* SR */
+        return s->sr;
+    case 0x10: /* CPSR */
+        return s->cpsr;
+    case 0x14: /* IMSC */
+        return s->im;
+    case 0x18: /* RIS */
+        return s->is;
+    case 0x1c: /* MIS */
+        return s->im & s->is;
+    case 0x20: /* DMACR */
+        /* Not implemented.  */
+        return 0;
+    default:
+        cpu_abort (cpu_single_env, "pl022_read: Bad offset %x\n",
+                   (int)offset);
+        return 0;
+    }
+}
+
+static void pl022_write(void *opaque, target_phys_addr_t offset,
+                        uint32_t value)
+{
+    pl022_state *s = (pl022_state *)opaque;
+
+    offset -= s->base;
+    switch (offset) {
+    case 0x00: /* CR0 */
+        s->cr0 = value;
+        /* Clock rate and format are ignored.  */
+        s->bitmask = (1 << ((value & 15) + 1)) - 1;
+        break;
+    case 0x04: /* CR1 */
+        s->cr1 = value;
+        if ((s->cr1 & (PL022_CR1_MS | PL022_CR1_SSE))
+                   == (PL022_CR1_MS | PL022_CR1_SSE)) {
+            BADF("SPI slave mode not implemented\n");
+        }
+        pl022_xfer(s);
+        break;
+    case 0x08: /* DR */
+        if (s->tx_fifo_len < 8) {
+            DPRINTF("TX %02x\n", value);
+            s->tx_fifo[s->tx_fifo_head] = value & s->bitmask;
+            s->tx_fifo_head = (s->tx_fifo_head + 1) & 7;
+            s->tx_fifo_len++;
+            pl022_xfer(s);
+        }
+        break;
+    case 0x10: /* CPSR */
+        /* Prescaler.  Ignored.  */
+        s->cpsr = value & 0xff;
+        break;
+    case 0x14: /* IMSC */
+        s->im = value;
+        pl022_update(s);
+        break;
+    case 0x20: /* DMACR */
+        if (value)
+            cpu_abort (cpu_single_env, "pl022: DMA not implemented\n");
+        break;
+    default:
+        cpu_abort (cpu_single_env, "pl022_write: Bad offset %x\n",
+                   (int)offset);
+    }
+}
+
+static void pl022_reset(pl022_state *s)
+{
+    s->rx_fifo_len = 0;
+    s->tx_fifo_len = 0;
+    s->im = 0;
+    s->is = PL022_INT_TX;
+    s->sr = PL022_SR_TFE | PL022_SR_TNF;
+}
+
+static CPUReadMemoryFunc *pl022_readfn[] = {
+   pl022_read,
+   pl022_read,
+   pl022_read
+};
+
+static CPUWriteMemoryFunc *pl022_writefn[] = {
+   pl022_write,
+   pl022_write,
+   pl022_write
+};
+
+void pl022_init(uint32_t base, qemu_irq irq, int (*xfer_cb)(void *, int),
+                void * opaque)
+{
+    int iomemtype;
+    pl022_state *s;
+
+    s = (pl022_state *)qemu_mallocz(sizeof(pl022_state));
+    iomemtype = cpu_register_io_memory(0, pl022_readfn,
+                                       pl022_writefn, s);
+    cpu_register_physical_memory(base, 0x00001000, iomemtype);
+    s->base = base;
+    s->irq = irq;
+    s->xfer_cb = xfer_cb;
+    s->opaque = opaque;
+    pl022_reset(s);
+    /* ??? Save/restore.  */
+}
+
+
diff --git a/hw/pl061.c b/hw/pl061.c
new file mode 100644
index 0000000..fa5004a
--- /dev/null
+++ b/hw/pl061.c
@@ -0,0 +1,256 @@
+/*
+ * Arm PrimeCell PL061 General Purpose IO with additional
+ * Luminary Micro Stellaris bits.
+ *
+ * Copyright (c) 2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+
+//#define DEBUG_PL061 1
+
+#ifdef DEBUG_PL061
+#define DPRINTF(fmt, args...) \
+do { printf("pl061: " fmt , ##args); } while (0)
+#define BADF(fmt, args...) \
+do { fprintf(stderr, "pl061: error: " fmt , ##args); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while(0)
+#define BADF(fmt, args...) \
+do { fprintf(stderr, "pl061: error: " fmt , ##args);} while (0)
+#endif
+
+static const uint8_t pl061_id[12] =
+  { 0x00, 0x00, 0x00, 0x00, 0x61, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 };
+
+typedef struct {
+    uint32_t base;
+    int locked;
+    uint8_t data;
+    uint8_t old_data;
+    uint8_t dir;
+    uint8_t isense;
+    uint8_t ibe;
+    uint8_t iev;
+    uint8_t im;
+    uint8_t istate;
+    uint8_t afsel;
+    uint8_t dr2r;
+    uint8_t dr4r;
+    uint8_t dr8r;
+    uint8_t odr;
+    uint8_t pur;
+    uint8_t pdr;
+    uint8_t slr;
+    uint8_t den;
+    uint8_t cr;
+    qemu_irq irq;
+    qemu_irq out[8];
+} pl061_state;
+
+static void pl061_update(pl061_state *s)
+{
+    uint8_t changed;
+    uint8_t mask;
+    int i;
+
+    changed = s->old_data ^ s->data;
+    if (!changed)
+        return;
+
+    s->old_data = s->data;
+    for (i = 0; i < 8; i++) {
+        mask = 1 << i;
+        if ((changed & mask & s->dir) && s->out) {
+            DPRINTF("Set output %d = %d\n", i, (s->data & mask) != 0);
+            qemu_set_irq(s->out[i], (s->data & mask) != 0);
+        }
+    }
+
+    /* FIXME: Implement input interrupts.  */
+}
+
+static uint32_t pl061_read(void *opaque, target_phys_addr_t offset)
+{
+    pl061_state *s = (pl061_state *)opaque;
+
+    offset -= s->base;
+    if (offset >= 0xfd0 && offset < 0x1000) {
+        return pl061_id[(offset - 0xfd0) >> 2];
+    }
+    if (offset < 0x400) {
+        return s->data & (offset >> 2);
+    }
+    switch (offset) {
+    case 0x400: /* Direction */
+        return s->dir;
+    case 0x404: /* Interrupt sense */
+        return s->isense;
+    case 0x408: /* Interrupt both edges */
+        return s->ibe;
+    case 0x40c: /* Interupt event */
+        return s->iev;
+    case 0x410: /* Interrupt mask */
+        return s->im;
+    case 0x414: /* Raw interrupt status */
+        return s->istate;
+    case 0x418: /* Masked interrupt status */
+        return s->istate | s->im;
+    case 0x420: /* Alternate function select */
+        return s->afsel;
+    case 0x500: /* 2mA drive */
+        return s->dr2r;
+    case 0x504: /* 4mA drive */
+        return s->dr4r;
+    case 0x508: /* 8mA drive */
+        return s->dr8r;
+    case 0x50c: /* Open drain */
+        return s->odr;
+    case 0x510: /* Pull-up */
+        return s->pur;
+    case 0x514: /* Pull-down */
+        return s->pdr;
+    case 0x518: /* Slew rate control */
+        return s->slr;
+    case 0x51c: /* Digital enable */
+        return s->den;
+    case 0x520: /* Lock */
+        return s->locked;
+    case 0x524: /* Commit */
+        return s->cr;
+    default:
+        cpu_abort (cpu_single_env, "pl061_read: Bad offset %x\n",
+                   (int)offset);
+        return 0;
+    }
+}
+
+static void pl061_write(void *opaque, target_phys_addr_t offset,
+                        uint32_t value)
+{
+    pl061_state *s = (pl061_state *)opaque;
+    uint8_t mask;
+
+    offset -= s->base;
+    if (offset < 0x400) {
+        mask = (offset >> 2) & s->dir;
+        s->data = (s->data & ~mask) | (value & mask);
+        pl061_update(s);
+        return;
+    }
+    switch (offset) {
+    case 0x400: /* Direction */
+        s->dir = value;
+        break;
+    case 0x404: /* Interrupt sense */
+        s->isense = value;
+        break;
+    case 0x408: /* Interrupt both edges */
+        s->ibe = value;
+        break;
+    case 0x40c: /* Interupt event */
+        s->iev = value;
+        break;
+    case 0x410: /* Interrupt mask */
+        s->im = value;
+        break;
+    case 0x41c: /* Interrupt clear */
+        s->istate &= ~value;
+        break;
+    case 0x420: /* Alternate function select */
+        mask = s->cr;
+        s->afsel = (s->afsel & ~mask) | (value & mask);
+        break;
+    case 0x500: /* 2mA drive */
+        s->dr2r = value;
+        break;
+    case 0x504: /* 4mA drive */
+        s->dr4r = value;
+        break;
+    case 0x508: /* 8mA drive */
+        s->dr8r = value;
+        break;
+    case 0x50c: /* Open drain */
+        s->odr = value;
+        break;
+    case 0x510: /* Pull-up */
+        s->pur = value;
+        break;
+    case 0x514: /* Pull-down */
+        s->pdr = value;
+        break;
+    case 0x518: /* Slew rate control */
+        s->slr = value;
+        break;
+    case 0x51c: /* Digital enable */
+        s->den = value;
+        break;
+    case 0x520: /* Lock */
+        s->locked = (value != 0xacce551);
+        break;
+    case 0x524: /* Commit */
+        if (!s->locked)
+            s->cr = value;
+        break;
+    default:
+        cpu_abort (cpu_single_env, "pl061_write: Bad offset %x\n",
+                   (int)offset);
+    }
+    pl061_update(s);
+}
+
+static void pl061_reset(pl061_state *s)
+{
+  s->locked = 1;
+  s->cr = 0xff;
+}
+
+void pl061_set_irq(void * opaque, int irq, int level)
+{
+    pl061_state *s = (pl061_state *)opaque;
+    uint8_t mask;
+
+    mask = 1 << irq;
+    if ((s->dir & mask) == 0) {
+        s->data &= ~mask;
+        if (level)
+            s->data |= mask;
+        pl061_update(s);
+    }
+}
+
+static CPUReadMemoryFunc *pl061_readfn[] = {
+   pl061_read,
+   pl061_read,
+   pl061_read
+};
+
+static CPUWriteMemoryFunc *pl061_writefn[] = {
+   pl061_write,
+   pl061_write,
+   pl061_write
+};
+
+/* Returns an array of inputs.  */
+qemu_irq *pl061_init(uint32_t base, qemu_irq irq, qemu_irq **out)
+{
+    int iomemtype;
+    pl061_state *s;
+
+    s = (pl061_state *)qemu_mallocz(sizeof(pl061_state));
+    iomemtype = cpu_register_io_memory(0, pl061_readfn,
+                                       pl061_writefn, s);
+    cpu_register_physical_memory(base, 0x00001000, iomemtype);
+    s->base = base;
+    s->irq = irq;
+    pl061_reset(s);
+    if (out)
+        *out = s->out;
+
+    /* ??? Save/restore.  */
+    return qemu_allocate_irqs(pl061_set_irq, s, 8);
+}
+
diff --git a/hw/pxa2xx.c b/hw/pxa2xx.c
index ebaff13..3c10839 100644
--- a/hw/pxa2xx.c
+++ b/hw/pxa2xx.c
@@ -297,7 +297,7 @@
                     ARM_CPU_MODE_SVC | CPSR_A | CPSR_F | CPSR_I;
             s->env->cp15.c1_sys = 0;
             s->env->cp15.c1_coproc = 0;
-            s->env->cp15.c2_base = 0;
+            s->env->cp15.c2_base0 = 0;
             s->env->cp15.c3 = 0;
             s->pm_regs[PSSR >> 2] |= 0x8;	/* Set STS */
             s->pm_regs[RCSR >> 2] |= 0x8;	/* Set GPR */
@@ -2031,7 +2031,8 @@
         fprintf(stderr, "Unable to find CPU definition\n");
         exit(1);
     }
-    register_savevm("cpu", 0, 0, cpu_save, cpu_load, s->env);
+    register_savevm("cpu", 0, ARM_CPU_SAVE_VERSION, cpu_save, cpu_load,
+                    s->env);
 
     /* SDRAM & Internal Memory Storage */
     cpu_register_physical_memory(PXA2XX_SDRAM_BASE,
@@ -2145,7 +2146,8 @@
         fprintf(stderr, "Unable to find CPU definition\n");
         exit(1);
     }
-    register_savevm("cpu", 0, 0, cpu_save, cpu_load, s->env);
+    register_savevm("cpu", 0, ARM_CPU_SAVE_VERSION, cpu_save, cpu_load,
+                    s->env);
 
     /* SDRAM & Internal Memory Storage */
     cpu_register_physical_memory(PXA2XX_SDRAM_BASE, sdram_size,
diff --git a/hw/realview.c b/hw/realview.c
index f97d6e6..e02deee 100644
--- a/hw/realview.c
+++ b/hw/realview.c
@@ -25,13 +25,32 @@
     NICInfo *nd;
     int n;
     int done_smc = 0;
+    qemu_irq cpu_irq[4];
+    int ncpu;
 
     if (!cpu_model)
         cpu_model = "arm926";
-    env = cpu_init(cpu_model);
-    if (!env) {
-        fprintf(stderr, "Unable to find CPU definition\n");
-        exit(1);
+    /* FIXME: obey smp_cpus.  */
+    if (strcmp(cpu_model, "arm11mpcore") == 0) {
+        ncpu = 4;
+    } else {
+        ncpu = 1;
+    }
+
+    for (n = 0; n < ncpu; n++) {
+        env = cpu_init(cpu_model);
+        if (!env) {
+            fprintf(stderr, "Unable to find CPU definition\n");
+            exit(1);
+        }
+        pic = arm_pic_init_cpu(env);
+        cpu_irq[n] = pic[ARM_PIC_CPU_IRQ];
+        if (n > 0) {
+            /* Set entry point for secondary CPUs.  This assumes we're using
+               the init code from arm_boot.c.  Real hardware resets all CPUs
+               the same.  */
+            env->regs[15] = 0x80000000;
+        }
     }
 
     /* ??? RAM shoud repeat to fill physical memory space.  */
@@ -39,18 +58,23 @@
     cpu_register_physical_memory(0, ram_size, IO_MEM_RAM);
 
     arm_sysctl_init(0x10000000, 0xc1400400);
-    pic = arm_pic_init_cpu(env);
-    /* ??? The documentation says GIC1 is nFIQ and either GIC2 or GIC3
-       is nIRQ (there are inconsistencies).  However Linux 2.6.17 expects
-       GIC1 to be nIRQ and ignores all the others, so do that for now.  */
-    pic = arm_gic_init(0x10040000, pic[ARM_PIC_CPU_IRQ]);
+
+    if (ncpu == 1) {
+        /* ??? The documentation says GIC1 is nFIQ and either GIC2 or GIC3
+           is nIRQ (there are inconsistencies).  However Linux 2.6.17 expects
+           GIC1 to be nIRQ and ignores all the others, so do that for now.  */
+        pic = realview_gic_init(0x10040000, cpu_irq[0]);
+    } else {
+        pic = mpcore_irq_init(cpu_irq);
+    }
+
     pl050_init(0x10006000, pic[20], 0);
     pl050_init(0x10007000, pic[21], 1);
 
-    pl011_init(0x10009000, pic[12], serial_hds[0]);
-    pl011_init(0x1000a000, pic[13], serial_hds[1]);
-    pl011_init(0x1000b000, pic[14], serial_hds[2]);
-    pl011_init(0x1000c000, pic[15], serial_hds[3]);
+    pl011_init(0x10009000, pic[12], serial_hds[0], PL011_ARM);
+    pl011_init(0x1000a000, pic[13], serial_hds[1], PL011_ARM);
+    pl011_init(0x1000b000, pic[14], serial_hds[2], PL011_ARM);
+    pl011_init(0x1000c000, pic[15], serial_hds[3], PL011_ARM);
 
     /* DMA controller is optional, apparently.  */
     pl080_init(0x10030000, pic[24], 2);
@@ -114,10 +138,10 @@
     /*  0x10019000 PCI controller config.  */
     /*  0x10020000 CLCD.  */
     /* 0x10030000 DMA Controller.  */
-    /* 0x10040000 GIC1 (FIQ1).  */
-    /* 0x10050000 GIC2 (IRQ1).  */
-    /*  0x10060000 GIC3 (FIQ2).  */
-    /*  0x10070000 GIC4 (IRQ2).  */
+    /* 0x10040000 GIC1.  */
+    /* 0x10050000 GIC2.  */
+    /* 0x10060000 GIC3.  */
+    /* 0x10070000 GIC4.  */
     /*  0x10080000 SMC.  */
     /*  0x40000000 NOR flash.  */
     /*  0x44000000 DoC flash.  */
@@ -137,8 +161,14 @@
     /* 0x68000000 PCI mem 1.  */
     /* 0x6c000000 PCI mem 2.  */
 
-    arm_load_kernel(env, ram_size, kernel_filename, kernel_cmdline,
+    arm_load_kernel(first_cpu, ram_size, kernel_filename, kernel_cmdline,
                     initrd_filename, 0x33b, 0x0);
+
+    /* ??? Hack to map an additional page of ram for the secondary CPU
+       startup code.  I guess this works on real hardware because the
+       BootROM happens to be in ROM/flash or in memory that isn't clobbered
+       until after Linux boots the secondary CPUs.  */
+    cpu_register_physical_memory(0x80000000, 0x1000, IO_MEM_RAM + ram_size);
 }
 
 QEMUMachine realview_machine = {
diff --git a/hw/realview_gic.c b/hw/realview_gic.c
new file mode 100644
index 0000000..cbc9614
--- /dev/null
+++ b/hw/realview_gic.c
@@ -0,0 +1,64 @@
+/*
+ * ARM RealView Emulation Baseboard Interrupt Controller
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+#include "arm_pic.h"
+
+#define GIC_NIRQ 96
+#define NCPU 1
+
+/* Only a single "CPU" interface is present.  */
+static inline int
+gic_get_current_cpu(void)
+{
+  return 0;
+}
+
+#include "arm_gic.c"
+
+static uint32_t realview_gic_cpu_read(void *opaque, target_phys_addr_t offset)
+{
+    gic_state *s = (gic_state *)opaque;
+    offset -= s->base;
+    return gic_cpu_read(s, gic_get_current_cpu(), offset);
+}
+
+static void realview_gic_cpu_write(void *opaque, target_phys_addr_t offset,
+                          uint32_t value)
+{
+    gic_state *s = (gic_state *)opaque;
+    offset -= s->base;
+    gic_cpu_write(s, gic_get_current_cpu(), offset, value);
+}
+
+static CPUReadMemoryFunc *realview_gic_cpu_readfn[] = {
+   realview_gic_cpu_read,
+   realview_gic_cpu_read,
+   realview_gic_cpu_read
+};
+
+static CPUWriteMemoryFunc *realview_gic_cpu_writefn[] = {
+   realview_gic_cpu_write,
+   realview_gic_cpu_write,
+   realview_gic_cpu_write
+};
+
+qemu_irq *realview_gic_init(uint32_t base, qemu_irq parent_irq)
+{
+    gic_state *s;
+    int iomemtype;
+
+    s = gic_init(base, &parent_irq);
+    if (!s)
+        return NULL;
+    iomemtype = cpu_register_io_memory(0, realview_gic_cpu_readfn,
+                                       realview_gic_cpu_writefn, s);
+    cpu_register_physical_memory(base, 0x00001000, iomemtype);
+    return s->in;
+}
diff --git a/hw/ssd0303.c b/hw/ssd0303.c
new file mode 100644
index 0000000..138cfc7
--- /dev/null
+++ b/hw/ssd0303.c
@@ -0,0 +1,273 @@
+/*
+ * SSD0303 OLED controller with OSRAM Pictiva 96x16 display.
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+/* The controller can support a variety of different displays, but we only
+   implement one.  Most of the commends relating to brightness and geometry
+   setup are ignored. */
+#include "vl.h"
+
+//#define DEBUG_SSD0303 1
+
+#ifdef DEBUG_SSD0303
+#define DPRINTF(fmt, args...) \
+do { printf("ssd0303: " fmt , ##args); } while (0)
+#define BADF(fmt, args...) \
+do { fprintf(stderr, "ssd0303: error: " fmt , ##args); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while(0)
+#define BADF(fmt, args...) \
+do { fprintf(stderr, "ssd0303: error: " fmt , ##args);} while (0)
+#endif
+
+/* Scaling factor for pixels.  */
+#define MAGNIFY 4
+
+enum ssd0303_mode
+{
+    SSD0303_IDLE,
+    SSD0303_DATA,
+    SSD0303_CMD
+};
+
+enum ssd0303_cmd {
+    SSD0303_CMD_NONE,
+    SSD0303_CMD_SKIP1
+};
+
+typedef struct {
+    i2c_slave i2c;
+    DisplayState *ds;
+    int row;
+    int col;
+    int start_line;
+    int mirror;
+    int flash;
+    int enabled;
+    int inverse;
+    int redraw;
+    enum ssd0303_mode mode;
+    enum ssd0303_cmd cmd_state;
+    uint8_t framebuffer[132*8];
+} ssd0303_state;
+
+static int ssd0303_recv(i2c_slave *i2c)
+{
+    BADF("Reads not implemented\n");
+    return -1;
+}
+
+static int ssd0303_send(i2c_slave *i2c, uint8_t data)
+{
+    ssd0303_state *s = (ssd0303_state *)i2c;
+    enum ssd0303_cmd old_cmd_state;
+    switch (s->mode) {
+    case SSD0303_IDLE:
+        DPRINTF("byte 0x%02x\n", data);
+        if (data == 0x80)
+            s->mode = SSD0303_CMD;
+        else if (data == 0x40)
+            s->mode = SSD0303_DATA;
+        else
+            BADF("Unexpected byte 0x%x\n", data);
+        break;
+    case SSD0303_DATA:
+        DPRINTF("data 0x%02x\n", data);
+        if (s->col < 132) {
+            s->framebuffer[s->col + s->row * 132] = data;
+            s->col++;
+            s->redraw = 1;
+        }
+        break;
+    case SSD0303_CMD:
+        old_cmd_state = s->cmd_state;
+        s->cmd_state = SSD0303_CMD_NONE;
+        switch (old_cmd_state) {
+        case SSD0303_CMD_NONE:
+            DPRINTF("cmd 0x%02x\n", data);
+            s->mode = SSD0303_IDLE;
+            switch (data) {
+            case 0x00 ... 0x0f: /* Set lower colum address.  */
+                s->col = (s->col & 0xf0) | (data & 0xf);
+                break;
+            case 0x10 ... 0x20: /* Set higher column address.  */
+                s->col = (s->col & 0x0f) | ((data & 0xf) << 4);
+                break;
+            case 0x40 ... 0x7f: /* Set start line.  */
+                s->start_line = 0;
+                break;
+            case 0x81: /* Set contrast (Ignored).  */
+                s->cmd_state = SSD0303_CMD_SKIP1;
+                break;
+            case 0xa0: /* Mirror off.  */
+                s->mirror = 0;
+                break;
+            case 0xa1: /* Mirror off.  */
+                s->mirror = 1;
+                break;
+            case 0xa4: /* Entire display off.  */
+                s->flash = 0;
+                break;
+            case 0xa5: /* Entire display on.  */
+                s->flash = 1;
+                break;
+            case 0xa6: /* Inverse off.  */
+                s->inverse = 0;
+                break;
+            case 0xa7: /* Inverse on.  */
+                s->inverse = 1;
+                break;
+            case 0xa8: /* Set multipled ratio (Ignored).  */
+                s->cmd_state = SSD0303_CMD_SKIP1;
+                break;
+            case 0xad: /* DC-DC power control.  */
+                s->cmd_state = SSD0303_CMD_SKIP1;
+                break;
+            case 0xae: /* Display off.  */
+                s->enabled = 0;
+                break;
+            case 0xaf: /* Display on.  */
+                s->enabled = 1;
+                break;
+            case 0xb0 ... 0xbf: /* Set Page address.  */
+                s->row = data & 7;
+                break;
+            case 0xc0 ... 0xc8: /* Set COM output direction (Ignored).  */
+                break;
+            case 0xd3: /* Set display offset (Ignored).  */
+                s->cmd_state = SSD0303_CMD_SKIP1;
+                break;
+            case 0xd5: /* Set display clock (Ignored).  */
+                s->cmd_state = SSD0303_CMD_SKIP1;
+                break;
+            case 0xd8: /* Set color and power mode (Ignored).  */
+                s->cmd_state = SSD0303_CMD_SKIP1;
+                break;
+            case 0xd9: /* Set pre-charge period (Ignored).  */
+                s->cmd_state = SSD0303_CMD_SKIP1;
+                break;
+            case 0xda: /* Set COM pin configuration (Ignored).  */
+                s->cmd_state = SSD0303_CMD_SKIP1;
+                break;
+            case 0xdb: /* Set VCOM dselect level (Ignored).  */
+                s->cmd_state = SSD0303_CMD_SKIP1;
+                break;
+            case 0xe3: /* no-op.  */
+                break;
+            default:
+                BADF("Unknown command: 0x%x\n", data);
+            }
+            break;
+        case SSD0303_CMD_SKIP1:
+            DPRINTF("skip 0x%02x\n", data);
+            break;
+        }
+        break;
+    }
+    return 0;
+}
+
+static void ssd0303_event(i2c_slave *i2c, enum i2c_event event)
+{
+    ssd0303_state *s = (ssd0303_state *)i2c;
+    switch (event) {
+    case I2C_FINISH:
+        s->mode = SSD0303_IDLE;
+        break;
+    case I2C_START_RECV:
+    case I2C_START_SEND:
+    case I2C_NACK:
+        /* Nothing to do.  */
+        break;
+    }
+}
+
+static void ssd0303_update_display(void *opaque)
+{
+    ssd0303_state *s = (ssd0303_state *)opaque;
+    uint8_t *dest;
+    uint8_t *src;
+    int x;
+    int y;
+    int line;
+    char *colors[2];
+    char colortab[MAGNIFY * 8];
+    int dest_width;
+    uint8_t mask;
+
+    if (s->redraw) {
+        switch (s->ds->depth) {
+        case 0:
+            return;
+        case 15:
+            dest_width = 2;
+            break;
+        case 16:
+            dest_width = 2;
+            break;
+        case 24:
+            dest_width = 3;
+            break;
+        case 32:
+            dest_width = 4;
+            break;
+        default:
+            BADF("Bad color depth\n");
+            return;
+        }
+        dest_width *= MAGNIFY;
+        memset(colortab, 0xff, dest_width);
+        memset(colortab + dest_width, 0, dest_width);
+        if (s->flash) {
+            colors[0] = colortab;
+            colors[1] = colortab;
+        } else if (s->inverse) {
+            colors[0] = colortab;
+            colors[1] = colortab + dest_width;
+        } else {
+            colors[0] = colortab + dest_width;
+            colors[1] = colortab;
+        }
+        dest = s->ds->data;
+        for (y = 0; y < 16; y++) {
+            line = (y + s->start_line) & 63;
+            src = s->framebuffer + 132 * (line >> 3) + 36;
+            mask = 1 << (line & 7);
+            for (x = 0; x < 96; x++) {
+                memcpy(dest, colors[(*src & mask) != 0], dest_width);
+                dest += dest_width;
+                src++;
+            }
+            for (x = 1; x < MAGNIFY; x++) {
+                memcpy(dest, dest - dest_width * 96, dest_width * 96);
+                dest += dest_width * 96;
+            }
+        }
+    }
+    dpy_update(s->ds, 0, 0, 96 * MAGNIFY, 16 * MAGNIFY);
+}
+
+static void ssd0303_invalidate_display(void * opaque)
+{
+    ssd0303_state *s = (ssd0303_state *)opaque;
+    s->redraw = 1;
+}
+
+void ssd0303_init(DisplayState *ds, i2c_bus *bus, int address)
+{
+    ssd0303_state *s;
+
+    s = (ssd0303_state *)i2c_slave_init(bus, address, sizeof(ssd0303_state));
+    s->ds = ds;
+    s->i2c.event = ssd0303_event;
+    s->i2c.recv = ssd0303_recv;
+    s->i2c.send = ssd0303_send;
+    graphic_console_init(ds, ssd0303_update_display, ssd0303_invalidate_display,
+                         NULL, s);
+    dpy_resize(s->ds, 96 * MAGNIFY, 16 * MAGNIFY);
+}
diff --git a/hw/ssd0323.c b/hw/ssd0323.c
new file mode 100644
index 0000000..67361bc
--- /dev/null
+++ b/hw/ssd0323.c
@@ -0,0 +1,267 @@
+/*
+ * SSD0323 OLED controller with OSRAM Pictiva 128x64 display.
+ *
+ * Copyright (c) 2006-2007 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+/* The controller can support a variety of different displays, but we only
+   implement one.  Most of the commends relating to brightness and geometry
+   setup are ignored. */
+#include "vl.h"
+
+//#define DEBUG_SSD0323 1
+
+#ifdef DEBUG_SSD0323
+#define DPRINTF(fmt, args...) \
+do { printf("ssd0323: " fmt , ##args); } while (0)
+#define BADF(fmt, args...) \
+do { fprintf(stderr, "ssd0323: error: " fmt , ##args); exit(1);} while (0)
+#else
+#define DPRINTF(fmt, args...) do {} while(0)
+#define BADF(fmt, args...) \
+do { fprintf(stderr, "ssd0323: error: " fmt , ##args);} while (0)
+#endif
+
+/* Scaling factor for pixels.  */
+#define MAGNIFY 4
+
+enum ssd0323_mode
+{
+    SSD0323_CMD,
+    SSD0323_DATA
+};
+
+typedef struct {
+    DisplayState *ds;
+
+    int cmd_len;
+    int cmd;
+    int cmd_data[8];
+    int row;
+    int row_start;
+    int row_end;
+    int col;
+    int col_start;
+    int col_end;
+    int redraw;
+    enum ssd0323_mode mode;
+    uint8_t framebuffer[128 * 80 / 2];
+} ssd0323_state;
+
+int ssd0323_xfer_ssi(void *opaque, int data)
+{
+    ssd0323_state *s = (ssd0323_state *)opaque;
+    switch (s->mode) {
+    case SSD0323_DATA:
+        DPRINTF("data 0x%02x\n", data);
+        s->framebuffer[s->col + s->row * 64] = data;
+        s->col++;
+        if (s->col > s->col_end) {
+            s->row++;
+            s->col = s->col_start;
+        }
+        if (s->row > s->row_end) {
+            s->row = s->row_start;
+        }
+        s->redraw = 1;
+        break;
+    case SSD0323_CMD:
+        DPRINTF("cmd 0x%02x\n", data);
+        if (s->cmd_len == 0) {
+            s->cmd = data;
+        } else {
+            s->cmd_data[s->cmd_len - 1] = data;
+        }
+        s->cmd_len++;
+        switch (s->cmd) {
+#define DATA(x) if (s->cmd_len <= (x)) return 0
+        case 0x15: /* Set column.  */
+            DATA(2);
+            s->col_start = s->cmd_data[0] % 64;
+            s->col_end = s->cmd_data[1] % 64;
+            break;
+        case 0x75: /* Set row.  */
+            DATA(2);
+            s->row_start = s->cmd_data[0] % 80;
+            s->row_end = s->cmd_data[1] % 80;
+            break;
+        case 0x81: /* Set contrast */
+            DATA(1);
+            break;
+        case 0x84: case 0x85: case 0x86: /* Max current.  */
+            DATA(0);
+            break;
+        case 0xa0: /* Set remapping.  */
+            /* FIXME: Implement this.  */
+            DATA(1);
+            break;
+        case 0xa1: /* Set display start line.  */
+        case 0xa2: /* Set display offset.  */
+            /* FIXME: Implement these.  */
+            DATA(1);
+            break;
+        case 0xa4: /* Normal mode.  */
+        case 0xa5: /* All on.  */
+        case 0xa6: /* All off.  */
+        case 0xa7: /* Inverse.  */
+            /* FIXME: Implement these.  */
+            DATA(0);
+            break;
+        case 0xa8: /* Set multiplex ratio.  */
+        case 0xad: /* Set DC-DC converter.  */
+            DATA(1);
+            /* Ignored.  Don't care.  */
+            break;
+        case 0xae: /* Display off.  */
+        case 0xaf: /* Display on.  */
+            DATA(0);
+            /* TODO: Implement power control.  */
+            break;
+        case 0xb1: /* Set phase length.  */
+        case 0xb2: /* Set row period.  */
+        case 0xb3: /* Set clock rate.  */
+        case 0xbc: /* Set precharge.  */
+        case 0xbe: /* Set VCOMH.  */
+        case 0xbf: /* Set segment low.  */
+            DATA(1);
+            /* Ignored.  Don't care.  */
+            break;
+        case 0xb8: /* Set grey scale table.  */
+            /* FIXME: Implement this.  */
+            DATA(8);
+            break;
+        case 0xe3: /* NOP.  */
+            DATA(0);
+            break;
+        default:
+            BADF("Unknown command: 0x%x\n", data);
+        }
+        s->cmd_len = 0;
+        return 0;
+    }
+    return 0;
+}
+
+static void ssd0323_update_display(void *opaque)
+{
+    ssd0323_state *s = (ssd0323_state *)opaque;
+    uint8_t *dest;
+    uint8_t *src;
+    int x;
+    int y;
+    int i;
+    int line;
+    char *colors[16];
+    char colortab[MAGNIFY * 64];
+    char *p;
+    int dest_width;
+
+    if (s->redraw) {
+        switch (s->ds->depth) {
+        case 0:
+            return;
+        case 15:
+            dest_width = 2;
+            break;
+        case 16:
+            dest_width = 2;
+            break;
+        case 24:
+            dest_width = 3;
+            break;
+        case 32:
+            dest_width = 4;
+            break;
+        default:
+            BADF("Bad color depth\n");
+            return;
+        }
+        p = colortab;
+        for (i = 0; i < 16; i++) {
+            int n;
+            colors[i] = p;
+            switch (s->ds->depth) {
+            case 15:
+                n = i * 2 + (i >> 3);
+                p[0] = n | (n << 5);
+                p[1] = (n << 2) | (n >> 3);
+                break;
+            case 16:
+                n = i * 2 + (i >> 3);
+                p[0] = n | (n << 6) | ((n << 1) & 0x20);
+                p[1] = (n << 3) | (n >> 2);
+                break;
+            case 24:
+            case 32:
+                n = (i << 4) | i;
+                p[0] = p[1] = p[2] = n;
+                break;
+            default:
+                BADF("Bad color depth\n");
+                return;
+            }
+            p += dest_width;
+        }
+        dest = s->ds->data;
+        for (y = 0; y < 64; y++) {
+            line = y;
+            src = s->framebuffer + 64 * line;
+            for (x = 0; x < 64; x++) {
+                int val;
+                val = *src >> 4;
+                for (i = 0; i < MAGNIFY; i++) {
+                    memcpy(dest, colors[val], dest_width);
+                    dest += dest_width;
+                }
+                val = *src & 0xf;
+                for (i = 0; i < MAGNIFY; i++) {
+                    memcpy(dest, colors[val], dest_width);
+                    dest += dest_width;
+                }
+                src++;
+            }
+            for (i = 1; i < MAGNIFY; i++) {
+                memcpy(dest, dest - dest_width * MAGNIFY * 128,
+                       dest_width * 128 * MAGNIFY);
+                dest += dest_width * 128 * MAGNIFY;
+            }
+        }
+    }
+    dpy_update(s->ds, 0, 0, 128 * MAGNIFY, 64 * MAGNIFY);
+}
+
+static void ssd0323_invalidate_display(void * opaque)
+{
+    ssd0323_state *s = (ssd0323_state *)opaque;
+    s->redraw = 1;
+}
+
+/* Command/data input.  */
+static void ssd0323_cd(void *opaque, int n, int level)
+{
+    ssd0323_state *s = (ssd0323_state *)opaque;
+    DPRINTF("%s mode\n", level ? "Data" : "Command");
+    s->mode = level ? SSD0323_DATA : SSD0323_CMD;
+}
+
+void *ssd0323_init(DisplayState *ds, qemu_irq *cmd_p)
+{
+    ssd0323_state *s;
+    qemu_irq *cmd;
+
+    s = (ssd0323_state *)qemu_mallocz(sizeof(ssd0323_state));
+    s->ds = ds;
+    graphic_console_init(ds, ssd0323_update_display, ssd0323_invalidate_display,
+                         NULL, s);
+    dpy_resize(s->ds, 128 * MAGNIFY, 64 * MAGNIFY);
+    s->col_end = 63;
+    s->row_end = 79;
+
+    cmd = qemu_allocate_irqs(ssd0323_cd, s, 1);
+    *cmd_p = *cmd;
+
+    return s;
+}
diff --git a/hw/stellaris.c b/hw/stellaris.c
new file mode 100644
index 0000000..62f2c03
--- /dev/null
+++ b/hw/stellaris.c
@@ -0,0 +1,1101 @@
+/*
+ * Luminary Micro Stellaris preipherals
+ *
+ * Copyright (c) 2006 CodeSourcery.
+ * Written by Paul Brook
+ *
+ * This code is licenced under the GPL.
+ */
+
+#include "vl.h"
+#include "arm_pic.h"
+
+typedef const struct {
+    const char *name;
+    uint32_t did0;
+    uint32_t did1;
+    uint32_t dc0;
+    uint32_t dc1;
+    uint32_t dc2;
+    uint32_t dc3;
+    uint32_t dc4;
+    enum {OLED_I2C, OLED_SSI} oled;
+} stellaris_board_info;
+
+/* General purpose timer module.  */
+
+/* Multiplication factor to convert from GPTM timer ticks to qemu timer
+   ticks.  */
+static int stellaris_clock_scale;
+
+typedef struct gptm_state {
+    uint32_t config;
+    uint32_t mode[2];
+    uint32_t control;
+    uint32_t state;
+    uint32_t mask;
+    uint32_t load[2];
+    uint32_t match[2];
+    uint32_t prescale[2];
+    uint32_t match_prescale[2];
+    uint32_t rtc;
+    int64_t tick[2];
+    struct gptm_state *opaque[2];
+    uint32_t base;
+    QEMUTimer *timer[2];
+    /* The timers have an alternate output used to trigger the ADC.  */
+    qemu_irq trigger;
+    qemu_irq irq;
+} gptm_state;
+
+static void gptm_update_irq(gptm_state *s)
+{
+    int level;
+    level = (s->state & s->mask) != 0;
+    qemu_set_irq(s->irq, level);
+}
+
+static void gptm_stop(gptm_state *s, int n)
+{
+    qemu_del_timer(s->timer[n]);
+}
+
+static void gptm_reload(gptm_state *s, int n, int reset)
+{
+    int64_t tick;
+    if (reset)
+        tick = qemu_get_clock(vm_clock);
+    else
+        tick = s->tick[n];
+
+    if (s->config == 0) {
+        /* 32-bit CountDown.  */
+        uint32_t count;
+        count = s->load[0] | (s->load[1] << 16);
+        tick += (int64_t)count * stellaris_clock_scale;
+    } else if (s->config == 1) {
+        /* 32-bit RTC.  1Hz tick.  */
+        tick += ticks_per_sec;
+    } else if (s->mode[n] == 0xa) {
+        /* PWM mode.  Not implemented.  */
+    } else {
+        cpu_abort(cpu_single_env, "TODO: 16-bit timer mode 0x%x\n",
+                  s->mode[n]);
+    }
+    s->tick[n] = tick;
+    qemu_mod_timer(s->timer[n], tick);
+}
+
+static void gptm_tick(void *opaque)
+{
+    gptm_state **p = (gptm_state **)opaque;
+    gptm_state *s;
+    int n;
+
+    s = *p;
+    n = p - s->opaque;
+    if (s->config == 0) {
+        s->state |= 1;
+        if ((s->control & 0x20)) {
+            /* Output trigger.  */
+	    qemu_irq_raise(s->trigger);
+	    qemu_irq_lower(s->trigger);
+        }
+        if (s->mode[0] & 1) {
+            /* One-shot.  */
+            s->control &= ~1;
+        } else {
+            /* Periodic.  */
+            gptm_reload(s, 0, 0);
+        }
+    } else if (s->config == 1) {
+        /* RTC.  */
+        uint32_t match;
+        s->rtc++;
+        match = s->match[0] | (s->match[1] << 16);
+        if (s->rtc > match)
+            s->rtc = 0;
+        if (s->rtc == 0) {
+            s->state |= 8;
+        }
+        gptm_reload(s, 0, 0);
+    } else if (s->mode[n] == 0xa) {
+        /* PWM mode.  Not implemented.  */
+    } else {
+        cpu_abort(cpu_single_env, "TODO: 16-bit timer mode 0x%x\n",
+                  s->mode[n]);
+    }
+    gptm_update_irq(s);
+}
+
+static uint32_t gptm_read(void *opaque, target_phys_addr_t offset)
+{
+    gptm_state *s = (gptm_state *)opaque;
+
+    offset -= s->base;
+    switch (offset) {
+    case 0x00: /* CFG */
+        return s->config;
+    case 0x04: /* TAMR */
+        return s->mode[0];
+    case 0x08: /* TBMR */
+        return s->mode[1];
+    case 0x0c: /* CTL */
+        return s->control;
+    case 0x18: /* IMR */
+        return s->mask;
+    case 0x1c: /* RIS */
+        return s->state;
+    case 0x20: /* MIS */
+        return s->state & s->mask;
+    case 0x24: /* CR */
+        return 0;
+    case 0x28: /* TAILR */
+        return s->load[0] | ((s->config < 4) ? (s->load[1] << 16) : 0);
+    case 0x2c: /* TBILR */
+        return s->load[1];
+    case 0x30: /* TAMARCHR */
+        return s->match[0] | ((s->config < 4) ? (s->match[1] << 16) : 0);
+    case 0x34: /* TBMATCHR */
+        return s->match[1];
+    case 0x38: /* TAPR */
+        return s->prescale[0];
+    case 0x3c: /* TBPR */
+        return s->prescale[1];
+    case 0x40: /* TAPMR */
+        return s->match_prescale[0];
+    case 0x44: /* TBPMR */
+        return s->match_prescale[1];
+    case 0x48: /* TAR */
+        if (s->control == 1)
+            return s->rtc;
+    case 0x4c: /* TBR */
+        cpu_abort(cpu_single_env, "TODO: Timer value read\n");
+    default:
+        cpu_abort(cpu_single_env, "gptm_read: Bad offset 0x%x\n", (int)offset);
+        return 0;
+    }
+}
+
+static void gptm_write(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+    gptm_state *s = (gptm_state *)opaque;
+    uint32_t oldval;
+
+    offset -= s->base;
+    /* The timers should be disabled before changing the configuration.
+       We take advantage of this and defer everything until the timer
+       is enabled.  */
+    switch (offset) {
+    case 0x00: /* CFG */
+        s->config = value;
+        break;
+    case 0x04: /* TAMR */
+        s->mode[0] = value;
+        break;
+    case 0x08: /* TBMR */
+        s->mode[1] = value;
+        break;
+    case 0x0c: /* CTL */
+        oldval = s->control;
+        s->control = value;
+        /* TODO: Implement pause.  */
+        if ((oldval ^ value) & 1) {
+            if (value & 1) {
+                gptm_reload(s, 0, 1);
+            } else {
+                gptm_stop(s, 0);
+            }
+        }
+        if (((oldval ^ value) & 0x100) && s->config >= 4) {
+            if (value & 0x100) {
+                gptm_reload(s, 1, 1);
+            } else {
+                gptm_stop(s, 1);
+            }
+        }
+        break;
+    case 0x18: /* IMR */
+        s->mask = value & 0x77;
+        gptm_update_irq(s);
+        break;
+    case 0x24: /* CR */
+        s->state &= ~value;
+        break;
+    case 0x28: /* TAILR */
+        s->load[0] = value & 0xffff;
+        if (s->config < 4) {
+            s->load[1] = value >> 16;
+        }
+        break;
+    case 0x2c: /* TBILR */
+        s->load[1] = value & 0xffff;
+        break;
+    case 0x30: /* TAMARCHR */
+        s->match[0] = value & 0xffff;
+        if (s->config < 4) {
+            s->match[1] = value >> 16;
+        }
+        break;
+    case 0x34: /* TBMATCHR */
+        s->match[1] = value >> 16;
+        break;
+    case 0x38: /* TAPR */
+        s->prescale[0] = value;
+        break;
+    case 0x3c: /* TBPR */
+        s->prescale[1] = value;
+        break;
+    case 0x40: /* TAPMR */
+        s->match_prescale[0] = value;
+        break;
+    case 0x44: /* TBPMR */
+        s->match_prescale[0] = value;
+        break;
+    default:
+        cpu_abort(cpu_single_env, "gptm_write: Bad offset 0x%x\n", (int)offset);
+    }
+    gptm_update_irq(s);
+}
+
+static CPUReadMemoryFunc *gptm_readfn[] = {
+   gptm_read,
+   gptm_read,
+   gptm_read
+};
+
+static CPUWriteMemoryFunc *gptm_writefn[] = {
+   gptm_write,
+   gptm_write,
+   gptm_write
+};
+
+static void stellaris_gptm_init(uint32_t base, qemu_irq irq, qemu_irq trigger)
+{
+    int iomemtype;
+    gptm_state *s;
+
+    s = (gptm_state *)qemu_mallocz(sizeof(gptm_state));
+    s->base = base;
+    s->irq = irq;
+    s->trigger = trigger;
+    s->opaque[0] = s->opaque[1] = s;
+
+    iomemtype = cpu_register_io_memory(0, gptm_readfn,
+                                       gptm_writefn, s);
+    cpu_register_physical_memory(base, 0x00001000, iomemtype);
+    s->timer[0] = qemu_new_timer(vm_clock, gptm_tick, &s->opaque[0]);
+    s->timer[1] = qemu_new_timer(vm_clock, gptm_tick, &s->opaque[1]);
+    /* ??? Save/restore.  */
+}
+
+
+/* System controller.  */
+
+typedef struct {
+    uint32_t base;
+    uint32_t pborctl;
+    uint32_t ldopctl;
+    uint32_t int_status;
+    uint32_t int_mask;
+    uint32_t resc;
+    uint32_t rcc;
+    uint32_t rcgc[3];
+    uint32_t scgc[3];
+    uint32_t dcgc[3];
+    uint32_t clkvclr;
+    uint32_t ldoarst;
+    qemu_irq irq;
+    stellaris_board_info *board;
+} ssys_state;
+
+static void ssys_update(ssys_state *s)
+{
+  qemu_set_irq(s->irq, (s->int_status & s->int_mask) != 0);
+}
+
+static uint32_t pllcfg_sandstorm[16] = {
+    0x31c0, /* 1 Mhz */
+    0x1ae0, /* 1.8432 Mhz */
+    0x18c0, /* 2 Mhz */
+    0xd573, /* 2.4576 Mhz */
+    0x37a6, /* 3.57954 Mhz */
+    0x1ae2, /* 3.6864 Mhz */
+    0x0c40, /* 4 Mhz */
+    0x98bc, /* 4.906 Mhz */
+    0x935b, /* 4.9152 Mhz */
+    0x09c0, /* 5 Mhz */
+    0x4dee, /* 5.12 Mhz */
+    0x0c41, /* 6 Mhz */
+    0x75db, /* 6.144 Mhz */
+    0x1ae6, /* 7.3728 Mhz */
+    0x0600, /* 8 Mhz */
+    0x585b /* 8.192 Mhz */
+};
+
+static uint32_t pllcfg_fury[16] = {
+    0x3200, /* 1 Mhz */
+    0x1b20, /* 1.8432 Mhz */
+    0x1900, /* 2 Mhz */
+    0xf42b, /* 2.4576 Mhz */
+    0x37e3, /* 3.57954 Mhz */
+    0x1b21, /* 3.6864 Mhz */
+    0x0c80, /* 4 Mhz */
+    0x98ee, /* 4.906 Mhz */
+    0xd5b4, /* 4.9152 Mhz */
+    0x0a00, /* 5 Mhz */
+    0x4e27, /* 5.12 Mhz */
+    0x1902, /* 6 Mhz */
+    0xec1c, /* 6.144 Mhz */
+    0x1b23, /* 7.3728 Mhz */
+    0x0640, /* 8 Mhz */
+    0xb11c /* 8.192 Mhz */
+};
+
+static uint32_t ssys_read(void *opaque, target_phys_addr_t offset)
+{
+    ssys_state *s = (ssys_state *)opaque;
+
+    offset -= s->base;
+    switch (offset) {
+    case 0x000: /* DID0 */
+        return s->board->did0;
+    case 0x004: /* DID1 */
+        return s->board->did1;
+    case 0x008: /* DC0 */
+        return s->board->dc0;
+    case 0x010: /* DC1 */
+        return s->board->dc1;
+    case 0x014: /* DC2 */
+        return s->board->dc2;
+    case 0x018: /* DC3 */
+        return s->board->dc3;
+    case 0x01c: /* DC4 */
+        return s->board->dc4;
+    case 0x030: /* PBORCTL */
+        return s->pborctl;
+    case 0x034: /* LDOPCTL */
+        return s->ldopctl;
+    case 0x040: /* SRCR0 */
+        return 0;
+    case 0x044: /* SRCR1 */
+        return 0;
+    case 0x048: /* SRCR2 */
+        return 0;
+    case 0x050: /* RIS */
+        return s->int_status;
+    case 0x054: /* IMC */
+        return s->int_mask;
+    case 0x058: /* MISC */
+        return s->int_status & s->int_mask;
+    case 0x05c: /* RESC */
+        return s->resc;
+    case 0x060: /* RCC */
+        return s->rcc;
+    case 0x064: /* PLLCFG */
+        {
+            int xtal;
+            xtal = (s->rcc >> 6) & 0xf;
+            if (s->board->did0 & (1 << 16)) {
+                return pllcfg_fury[xtal];
+            } else {
+                return pllcfg_sandstorm[xtal];
+            }
+        }
+    case 0x100: /* RCGC0 */
+        return s->rcgc[0];
+    case 0x104: /* RCGC1 */
+        return s->rcgc[1];
+    case 0x108: /* RCGC2 */
+        return s->rcgc[2];
+    case 0x110: /* SCGC0 */
+        return s->scgc[0];
+    case 0x114: /* SCGC1 */
+        return s->scgc[1];
+    case 0x118: /* SCGC2 */
+        return s->scgc[2];
+    case 0x120: /* DCGC0 */
+        return s->dcgc[0];
+    case 0x124: /* DCGC1 */
+        return s->dcgc[1];
+    case 0x128: /* DCGC2 */
+        return s->dcgc[2];
+    case 0x150: /* CLKVCLR */
+        return s->clkvclr;
+    case 0x160: /* LDOARST */
+        return s->ldoarst;
+    default:
+        cpu_abort(cpu_single_env, "gptm_read: Bad offset 0x%x\n", (int)offset);
+        return 0;
+    }
+}
+
+static void ssys_write(void *opaque, target_phys_addr_t offset, uint32_t value)
+{
+    ssys_state *s = (ssys_state *)opaque;
+
+    offset -= s->base;
+    switch (offset) {
+    case 0x030: /* PBORCTL */
+        s->pborctl = value & 0xffff;
+        break;
+    case 0x034: /* LDOPCTL */
+        s->ldopctl = value & 0x1f;
+        break;
+    case 0x040: /* SRCR0 */
+    case 0x044: /* SRCR1 */
+    case 0x048: /* SRCR2 */
+        fprintf(stderr, "Peripheral reset not implemented\n");
+        break;
+    case 0x054: /* IMC */
+        s->int_mask = value & 0x7f;
+        break;
+    case 0x058: /* MISC */
+        s->int_status &= ~value;
+        break;
+    case 0x05c: /* RESC */
+        s->resc = value & 0x3f;
+        break;
+    case 0x060: /* RCC */
+        if ((s->rcc & (1 << 13)) != 0 && (value & (1 << 13)) == 0) {
+            /* PLL enable.  */
+            s->int_status |= (1 << 6);
+        }
+        s->rcc = value;
+        stellaris_clock_scale = 5 * (((s->rcc >> 23) & 0xf) + 1);
+        break;
+    case 0x100: /* RCGC0 */
+        s->rcgc[0] = value;
+        break;
+    case 0x104: /* RCGC1 */
+        s->rcgc[1] = value;
+        break;
+    case 0x108: /* RCGC2 */
+        s->rcgc[2] = value;
+        break;
+    case 0x110: /* SCGC0 */
+        s->scgc[0] = value;
+        break;
+    case 0x114: /* SCGC1 */
+        s->scgc[1] = value;
+        break;
+    case 0x118: /* SCGC2 */
+        s->scgc[2] = value;
+        break;
+    case 0x120: /* DCGC0 */
+        s->dcgc[0] = value;
+        break;
+    case 0x124: /* DCGC1 */
+        s->dcgc[1] = value;
+        break;
+    case 0x128: /* DCGC2 */
+        s->dcgc[2] = value;
+        break;
+    case 0x150: /* CLKVCLR */
+        s->clkvclr = value;
+        break;
+    case 0x160: /* LDOARST */
+        s->ldoarst = value;
+        break;
+    default:
+        cpu_abort(cpu_single_env, "gptm_write: Bad offset 0x%x\n", (int)offset);
+    }
+    ssys_update(s);
+}
+
+static CPUReadMemoryFunc *ssys_readfn[] = {
+   ssys_read,
+   ssys_read,
+   ssys_read
+};
+
+static CPUWriteMemoryFunc *ssys_writefn[] = {
+   ssys_write,
+   ssys_write,
+   ssys_write
+};
+
+void ssys_reset(void *opaque)
+{
+    ssys_state *s = (ssys_state *)opaque;
+
+    s->pborctl = 0x7ffd;
+    s->rcc = 0x078e3ac0;
+    s->rcgc[0] = 1;
+    s->scgc[0] = 1;
+    s->dcgc[0] = 1;
+}
+
+static void stellaris_sys_init(uint32_t base, qemu_irq irq,
+                               stellaris_board_info * board)
+{
+    int iomemtype;
+    ssys_state *s;
+
+    s = (ssys_state *)qemu_mallocz(sizeof(ssys_state));
+    s->base = base;
+    s->irq = irq;
+    s->board = board;
+
+    iomemtype = cpu_register_io_memory(0, ssys_readfn,
+                                       ssys_writefn, s);
+    cpu_register_physical_memory(base, 0x00001000, iomemtype);
+    ssys_reset(s);
+    /* ??? Save/restore.  */
+}
+
+
+/* I2C controller.  */
+
+typedef struct {
+    i2c_bus *bus;
+    qemu_irq irq;
+    uint32_t base;
+    uint32_t msa;
+    uint32_t mcs;
+    uint32_t mdr;
+    uint32_t mtpr;
+    uint32_t mimr;
+    uint32_t mris;
+    uint32_t mcr;
+} stellaris_i2c_state;
+
+#define STELLARIS_I2C_MCS_BUSY    0x01
+#define STELLARIS_I2C_MCS_ERROR   0x02
+#define STELLARIS_I2C_MCS_ADRACK  0x04
+#define STELLARIS_I2C_MCS_DATACK  0x08
+#define STELLARIS_I2C_MCS_ARBLST  0x10
+#define STELLARIS_I2C_MCS_IDLE    0x20
+#define STELLARIS_I2C_MCS_BUSBSY  0x40
+
+static uint32_t stellaris_i2c_read(void *opaque, target_phys_addr_t offset)
+{
+    stellaris_i2c_state *s = (stellaris_i2c_state *)opaque;
+
+    offset -= s->base;
+    switch (offset) {
+    case 0x00: /* MSA */
+        return s->msa;
+    case 0x04: /* MCS */
+        /* We don't emulate timing, so the controller is never busy.  */
+        return s->mcs | STELLARIS_I2C_MCS_IDLE;
+    case 0x08: /* MDR */
+        return s->mdr;
+    case 0x0c: /* MTPR */
+        return s->mtpr;
+    case 0x10: /* MIMR */
+        return s->mimr;
+    case 0x14: /* MRIS */
+        return s->mris;
+    case 0x18: /* MMIS */
+        return s->mris & s->mimr;
+    case 0x20: /* MCR */
+        return s->mcr;
+    default:
+        cpu_abort(cpu_single_env, "strllaris_i2c_read: Bad offset 0x%x\n",
+                  (int)offset);
+        return 0;
+    }
+}
+
+static void stellaris_i2c_update(stellaris_i2c_state *s)
+{
+    int level;
+
+    level = (s->mris & s->mimr) != 0;
+    qemu_set_irq(s->irq, level);
+}
+
+static void stellaris_i2c_write(void *opaque, target_phys_addr_t offset,
+                                uint32_t value)
+{
+    stellaris_i2c_state *s = (stellaris_i2c_state *)opaque;
+
+    offset -= s->base;
+    switch (offset) {
+    case 0x00: /* MSA */
+        s->msa = value & 0xff;
+        break;
+    case 0x04: /* MCS */
+        if ((s->mcr & 0x10) == 0) {
+            /* Disabled.  Do nothing.  */
+            break;
+        }
+        /* Grab the bus if this is starting a transfer.  */
+        if ((value & 2) && (s->mcs & STELLARIS_I2C_MCS_BUSBSY) == 0) {
+            if (i2c_start_transfer(s->bus, s->msa >> 1, s->msa & 1)) {
+                s->mcs |= STELLARIS_I2C_MCS_ARBLST;
+            } else {
+                s->mcs &= ~STELLARIS_I2C_MCS_ARBLST;
+                s->mcs |= STELLARIS_I2C_MCS_BUSBSY;
+            }
+        }
+        /* If we don't have the bus then indicate an error.  */
+        if (!i2c_bus_busy(s->bus)
+                || (s->mcs & STELLARIS_I2C_MCS_BUSBSY) == 0) {
+            s->mcs |= STELLARIS_I2C_MCS_ERROR;
+            break;
+        }
+        s->mcs &= ~STELLARIS_I2C_MCS_ERROR;
+        if (value & 1) {
+            /* Transfer a byte.  */
+            /* TODO: Handle errors.  */
+            if (s->msa & 1) {
+                /* Recv */
+                s->mdr = i2c_recv(s->bus) & 0xff;
+            } else {
+                /* Send */
+                i2c_send(s->bus, s->mdr);
+            }
+            /* Raise an interrupt.  */
+            s->mris |= 1;
+        }
+        if (value & 4) {
+            /* Finish transfer.  */
+            i2c_end_transfer(s->bus);
+            s->mcs &= ~STELLARIS_I2C_MCS_BUSBSY;
+        }
+        break;
+    case 0x08: /* MDR */
+        s->mdr = value & 0xff;
+        break;
+    case 0x0c: /* MTPR */
+        s->mtpr = value & 0xff;
+        break;
+    case 0x10: /* MIMR */
+        s->mimr = 1;
+        break;
+    case 0x1c: /* MICR */
+        s->mris &= ~value;
+        break;
+    case 0x20: /* MCR */
+        if (value & 1)
+            cpu_abort(cpu_single_env,
+                      "stellaris_i2c_write: Loopback not implemented\n");
+        if (value & 0x20)
+            cpu_abort(cpu_single_env,
+                      "stellaris_i2c_write: Slave mode not implemented\n");
+        s->mcr = value & 0x31;
+        break;
+    default:
+        cpu_abort(cpu_single_env, "stellaris_i2c_write: Bad offset 0x%x\n",
+                  (int)offset);
+    }
+    stellaris_i2c_update(s);
+}
+
+static void stellaris_i2c_reset(stellaris_i2c_state *s)
+{
+    if (s->mcs & STELLARIS_I2C_MCS_BUSBSY)
+        i2c_end_transfer(s->bus);
+
+    s->msa = 0;
+    s->mcs = 0;
+    s->mdr = 0;
+    s->mtpr = 1;
+    s->mimr = 0;
+    s->mris = 0;
+    s->mcr = 0;
+    stellaris_i2c_update(s);
+}
+
+static CPUReadMemoryFunc *stellaris_i2c_readfn[] = {
+   stellaris_i2c_read,
+   stellaris_i2c_read,
+   stellaris_i2c_read
+};
+
+static CPUWriteMemoryFunc *stellaris_i2c_writefn[] = {
+   stellaris_i2c_write,
+   stellaris_i2c_write,
+   stellaris_i2c_write
+};
+
+static void stellaris_i2c_init(uint32_t base, qemu_irq irq, i2c_bus *bus)
+{
+    stellaris_i2c_state *s;
+    int iomemtype;
+
+    s = (stellaris_i2c_state *)qemu_mallocz(sizeof(stellaris_i2c_state));
+    s->base = base;
+    s->irq = irq;
+    s->bus = bus;
+
+    iomemtype = cpu_register_io_memory(0, stellaris_i2c_readfn,
+                                       stellaris_i2c_writefn, s);
+    cpu_register_physical_memory(base, 0x00001000, iomemtype);
+    /* ??? For now we only implement the master interface.  */
+    stellaris_i2c_reset(s);
+}
+
+/* Analogue to Digital Converter.  This is only partially implemented,
+   enough for applications that use a combined ADC and timer tick.  */
+
+#define STELLARIS_ADC_EM_CONTROLLER 0
+#define STELLARIS_ADC_EM_COMP       1
+#define STELLARIS_ADC_EM_EXTERNAL   4
+#define STELLARIS_ADC_EM_TIMER      5
+#define STELLARIS_ADC_EM_PWM0       6
+#define STELLARIS_ADC_EM_PWM1       7
+#define STELLARIS_ADC_EM_PWM2       8
+
+#define STELLARIS_ADC_FIFO_EMPTY    0x0100
+#define STELLARIS_ADC_FIFO_FULL     0x1000
+
+typedef struct
+{
+    uint32_t base;
+    uint32_t actss;
+    uint32_t ris;
+    uint32_t im;
+    uint32_t emux;
+    uint32_t ostat;
+    uint32_t ustat;
+    uint32_t sspri;
+    uint32_t sac;
+    struct {
+        uint32_t state;
+        uint32_t data[16];
+    } fifo[4];
+    uint32_t ssmux[4];
+    uint32_t ssctl[4];
+    qemu_irq irq;
+} stellaris_adc_state;
+
+static uint32_t stellaris_adc_fifo_read(stellaris_adc_state *s, int n)
+{
+    int tail;
+
+    tail = s->fifo[n].state & 0xf;
+    if (s->fifo[n].state & STELLARIS_ADC_FIFO_EMPTY) {
+        s->ustat |= 1 << n;
+    } else {
+        s->fifo[n].state = (s->fifo[n].state & ~0xf) | ((tail + 1) & 0xf);
+        s->fifo[n].state &= ~STELLARIS_ADC_FIFO_FULL;
+        if (tail + 1 == ((s->fifo[n].state >> 4) & 0xf))
+            s->fifo[n].state |= STELLARIS_ADC_FIFO_EMPTY;
+    }
+    return s->fifo[n].data[tail];
+}
+
+static void stellaris_adc_fifo_write(stellaris_adc_state *s, int n,
+                                     uint32_t value)
+{
+    int head;
+
+    head = (s->fifo[n].state >> 4) & 0xf;
+    if (s->fifo[n].state & STELLARIS_ADC_FIFO_FULL) {
+        s->ostat |= 1 << n;
+        return;
+    }
+    s->fifo[n].data[head] = value;
+    head = (head + 1) & 0xf;
+    s->fifo[n].state &= ~STELLARIS_ADC_FIFO_EMPTY;
+    s->fifo[n].state = (s->fifo[n].state & ~0xf0) | (head << 4);
+    if ((s->fifo[n].state & 0xf) == head)
+        s->fifo[n].state |= STELLARIS_ADC_FIFO_FULL;
+}
+
+static void stellaris_adc_update(stellaris_adc_state *s)
+{
+    int level;
+
+    level = (s->ris & s->im) != 0;
+    qemu_set_irq(s->irq, level);
+}
+
+static void stellaris_adc_trigger(void *opaque, int irq, int level)
+{
+    stellaris_adc_state *s = (stellaris_adc_state *)opaque;
+    /* Some applications use the ADC as a random number source, so introduce
+       some variation into the signal.  */
+    static uint32_t noise = 0;
+
+    if ((s->actss & 1) == 0) {
+        return;
+    }
+
+    noise = noise * 314159 + 1;
+    /* ??? actual inputs not implemented.  Return an arbitrary value.  */
+    stellaris_adc_fifo_write(s, 0, 0x200 + ((noise >> 16) & 7));
+    s->ris |= 1;
+    stellaris_adc_update(s);
+}
+
+static void stellaris_adc_reset(stellaris_adc_state *s)
+{
+    int n;
+
+    for (n = 0; n < 4; n++) {
+        s->ssmux[n] = 0;
+        s->ssctl[n] = 0;
+        s->fifo[n].state = STELLARIS_ADC_FIFO_EMPTY;
+    }
+}
+
+static uint32_t stellaris_adc_read(void *opaque, target_phys_addr_t offset)
+{
+    stellaris_adc_state *s = (stellaris_adc_state *)opaque;
+
+    /* TODO: Implement this.  */
+    offset -= s->base;
+    if (offset >= 0x40 && offset < 0xc0) {
+        int n;
+        n = (offset - 0x40) >> 5;
+        switch (offset & 0x1f) {
+        case 0x00: /* SSMUX */
+            return s->ssmux[n];
+        case 0x04: /* SSCTL */
+            return s->ssctl[n];
+        case 0x08: /* SSFIFO */
+            return stellaris_adc_fifo_read(s, n);
+        case 0x0c: /* SSFSTAT */
+            return s->fifo[n].state;
+        default:
+            break;
+        }
+    }
+    switch (offset) {
+    case 0x00: /* ACTSS */
+        return s->actss;
+    case 0x04: /* RIS */
+        return s->ris;
+    case 0x08: /* IM */
+        return s->im;
+    case 0x0c: /* ISC */
+        return s->ris & s->im;
+    case 0x10: /* OSTAT */
+        return s->ostat;
+    case 0x14: /* EMUX */
+        return s->emux;
+    case 0x18: /* USTAT */
+        return s->ustat;
+    case 0x20: /* SSPRI */
+        return s->sspri;
+    case 0x30: /* SAC */
+        return s->sac;
+    default:
+        cpu_abort(cpu_single_env, "strllaris_adc_read: Bad offset 0x%x\n",
+                  (int)offset);
+        return 0;
+    }
+}
+
+static void stellaris_adc_write(void *opaque, target_phys_addr_t offset,
+                                uint32_t value)
+{
+    stellaris_adc_state *s = (stellaris_adc_state *)opaque;
+
+    /* TODO: Implement this.  */
+    offset -= s->base;
+    if (offset >= 0x40 && offset < 0xc0) {
+        int n;
+        n = (offset - 0x40) >> 5;
+        switch (offset & 0x1f) {
+        case 0x00: /* SSMUX */
+            s->ssmux[n] = value & 0x33333333;
+            return;
+        case 0x04: /* SSCTL */
+            if (value != 6) {
+                cpu_abort(cpu_single_env, "ADC: Unimplemented sequence %x\n",
+                          value);
+            }
+            s->ssctl[n] = value;
+            return;
+        default:
+            break;
+        }
+    }
+    switch (offset) {
+    case 0x00: /* ACTSS */
+        s->actss = value & 0xf;
+        if (value & 0xe) {
+            cpu_abort(cpu_single_env,
+                      "Not implemented:  ADC sequencers 1-3\n");
+        }
+        break;
+    case 0x08: /* IM */
+        s->im = value;
+        break;
+    case 0x0c: /* ISC */
+        s->ris &= ~value;
+        break;
+    case 0x10: /* OSTAT */
+        s->ostat &= ~value;
+        break;
+    case 0x14: /* EMUX */
+        s->emux = value;
+        break;
+    case 0x18: /* USTAT */
+        s->ustat &= ~value;
+        break;
+    case 0x20: /* SSPRI */
+        s->sspri = value;
+        break;
+    case 0x28: /* PSSI */
+        cpu_abort(cpu_single_env, "Not implemented:  ADC sample initiate\n");
+        break;
+    case 0x30: /* SAC */
+        s->sac = value;
+        break;
+    default:
+        cpu_abort(cpu_single_env, "stellaris_adc_write: Bad offset 0x%x\n",
+                  (int)offset);
+    }
+    stellaris_adc_update(s);
+}
+
+static CPUReadMemoryFunc *stellaris_adc_readfn[] = {
+   stellaris_adc_read,
+   stellaris_adc_read,
+   stellaris_adc_read
+};
+
+static CPUWriteMemoryFunc *stellaris_adc_writefn[] = {
+   stellaris_adc_write,
+   stellaris_adc_write,
+   stellaris_adc_write
+};
+
+static qemu_irq stellaris_adc_init(uint32_t base, qemu_irq irq)
+{
+    stellaris_adc_state *s;
+    int iomemtype;
+    qemu_irq *qi;
+
+    s = (stellaris_adc_state *)qemu_mallocz(sizeof(stellaris_adc_state));
+    s->base = base;
+    s->irq = irq;
+
+    iomemtype = cpu_register_io_memory(0, stellaris_adc_readfn,
+                                       stellaris_adc_writefn, s);
+    cpu_register_physical_memory(base, 0x00001000, iomemtype);
+    stellaris_adc_reset(s);
+    qi = qemu_allocate_irqs(stellaris_adc_trigger, s, 1);
+    return qi[0];
+}
+
+/* Board init.  */
+static stellaris_board_info stellaris_boards[] = {
+  { "LM3S811EVB",
+    0,
+    0x0032000e,
+    0x001f001f, /* dc0 */
+    0x001132bf,
+    0x01071013,
+    0x3f0f01ff,
+    0x0000001f,
+    OLED_I2C
+  },
+  { "LM3S6965EVB",
+    0x10010002,
+    0x1073402e,
+    0x00ff007f, /* dc0 */
+    0x001133ff,
+    0x030f5317,
+    0x0f0f87ff,
+    0x5000007f,
+    OLED_SSI
+  }
+};
+
+static void stellaris_init(const char *kernel_filename, const char *cpu_model,
+                           DisplayState *ds, stellaris_board_info *board)
+{
+    static const int uart_irq[] = {5, 6, 33, 34};
+    static const int timer_irq[] = {19, 21, 23, 35};
+    static const uint32_t gpio_addr[7] =
+      { 0x40004000, 0x40005000, 0x40006000, 0x40007000,
+        0x40024000, 0x40025000, 0x40026000};
+    static const int gpio_irq[7] = {0, 1, 2, 3, 4, 30, 31};
+
+    qemu_irq *pic;
+    qemu_irq *gpio_in[5];
+    qemu_irq *gpio_out[5];
+    qemu_irq adc;
+    int sram_size;
+    int flash_size;
+    i2c_bus *i2c;
+    int i;
+
+    flash_size = ((board->dc0 & 0xffff) + 1) << 1;
+    sram_size = (board->dc0 >> 18) + 1;
+    pic = armv7m_init(flash_size, sram_size, kernel_filename, cpu_model);
+
+    if (board->dc1 & (1 << 16)) {
+        adc = stellaris_adc_init(0x40038000, pic[14]);
+    } else {
+        adc = NULL;
+    }
+    for (i = 0; i < 4; i++) {
+        if (board->dc2 & (0x10000 << i)) {
+            stellaris_gptm_init(0x40030000 + i * 0x1000,
+                                pic[timer_irq[i]], adc);
+        }
+    }
+
+    stellaris_sys_init(0x400fe000, pic[28], board);
+
+    for (i = 0; i < 7; i++) {
+        if (board->dc4 & (1 << i)) {
+            gpio_in[i] = pl061_init(gpio_addr[i], pic[gpio_irq[i]],
+                                    &gpio_out[i]);
+        }
+    }
+
+    if (board->dc2 & (1 << 12)) {
+        i2c = i2c_init_bus();
+        stellaris_i2c_init(0x40020000, pic[8], i2c);
+        if (board->oled == OLED_I2C) {
+            ssd0303_init(ds, i2c, 0x3d);
+        }
+    }
+
+    for (i = 0; i < 4; i++) {
+        if (board->dc2 & (1 << i)) {
+            pl011_init(0x4000c000 + i * 0x1000, pic[uart_irq[i]],
+                       serial_hds[i], PL011_LUMINARY);
+        }
+    }
+    if (board->dc2 & (1 << 4)) {
+        if (board->oled == OLED_SSI) {
+            void * oled;
+            /* FIXME: Implement chip select for OLED/MMC.  */
+            oled = ssd0323_init(ds, &gpio_out[2][7]);
+            pl022_init(0x40008000, pic[7], ssd0323_xfer_ssi, oled);
+        } else {
+            pl022_init(0x40008000, pic[7], NULL, NULL);
+        }
+    }
+}
+
+/* FIXME: Figure out how to generate these from stellaris_boards.  */
+static void lm3s811evb_init(int ram_size, int vga_ram_size,
+                     const char *boot_device, DisplayState *ds,
+                     const char **fd_filename, int snapshot,
+                     const char *kernel_filename, const char *kernel_cmdline,
+                     const char *initrd_filename, const char *cpu_model)
+{
+    stellaris_init(kernel_filename, cpu_model, ds, &stellaris_boards[0]);
+}
+
+static void lm3s6965evb_init(int ram_size, int vga_ram_size,
+                     const char *boot_device, DisplayState *ds,
+                     const char **fd_filename, int snapshot,
+                     const char *kernel_filename, const char *kernel_cmdline,
+                     const char *initrd_filename, const char *cpu_model)
+{
+    stellaris_init(kernel_filename, cpu_model, ds, &stellaris_boards[1]);
+}
+
+QEMUMachine lm3s811evb_machine = {
+    "lm3s811evb",
+    "Stellaris LM3S811EVB",
+    lm3s811evb_init,
+};
+
+QEMUMachine lm3s6965evb_machine = {
+    "lm3s6965evb",
+    "Stellaris LM3S6965EVB",
+    lm3s6965evb_init,
+};
diff --git a/hw/versatilepb.c b/hw/versatilepb.c
index fc27c46..4e8e76e 100644
--- a/hw/versatilepb.c
+++ b/hw/versatilepb.c
@@ -208,10 +208,10 @@
         }
     }
 
-    pl011_init(0x101f1000, pic[12], serial_hds[0]);
-    pl011_init(0x101f2000, pic[13], serial_hds[1]);
-    pl011_init(0x101f3000, pic[14], serial_hds[2]);
-    pl011_init(0x10009000, sic[6], serial_hds[3]);
+    pl011_init(0x101f1000, pic[12], serial_hds[0], PL011_ARM);
+    pl011_init(0x101f2000, pic[13], serial_hds[1], PL011_ARM);
+    pl011_init(0x101f3000, pic[14], serial_hds[2], PL011_ARM);
+    pl011_init(0x10009000, sic[6], serial_hds[3], PL011_ARM);
 
     pl080_init(0x10130000, pic[17], 8);
     sp804_init(0x101e2000, pic[4]);