Skip to contentSkip to conversation

Add support for Cortex-A15 and vexpress-a15

PATCH Applied Peter Maydell 8 participants 39 messages Jan 13, 2012 – Feb 21, 2012

Subsystems (2)

v1 of 5 Applied Latest is v5 →

lore
Share & tools
lore
#0 lore raw

cover Add support for Cortex-A15 and vexpress-a15

This patchset adds support for (a rather limited version of) the Cortex-A15 CPU and the Versatile Express A15 daughterboard. The resulting model is capable of booting a Linux kernel which has been configured for Cortex-A15 with the Versatile Express "extended memory map" and without support for LPAE.

The series includes three patches at the start which have been on the list already but which this series depends on:
* arm: make the number of GIC interrupts configurable
* vexpress, realview: Add (dummy) L2 cache controller
* hw/arm_boot.c: Make SMP boards specify address to poll in bootup loop
The first two of these are already queued in my arm-devs.next; the arm_boot.c patch is in the Exynos4 patchset but is one of the reviewed and good-to-go ones. [Evgeny: you should keep the arm_boot.c patch in your patchset until/unless it makes it into master.] Basically I've included these for convenience of anybody who wants to test this patchset; I'm assuming this set will need a v2 and that in the meantime the dependencies will make their way into master. But I just got this all working this week so I thought I'd put the whole thing up for review before I went home for the week :-)

PS for the folk on android-virt: I haven't yet tested these patches with KVM, only with TCG, but there is no inherent reason for them not to work...

Evgeny Voevodin (1):
hw/arm_boot.c: Make SMP boards specify address to poll in bootup loop

Mark Langsdorf (1):
arm: make the number of GIC interrupts configurable

Peter Maydell (10):
vexpress, realview: Add (dummy) L2 cache controller hw/vexpress.c: Make motherboard peripheral memory map table-driven hw/vexpress.c: Move secondary CPU boot code to SRAM hw/vexpress.c: Factor out daughterboard-specific initialization hw/vexpress.c: Instantiate the motherboard CLCD hw/a15mpcore.c: Add Cortex-A15 private peripheral model Add dummy implementation of generic timer cp15 registers Add Cortex-A15 CPU definition
arm_boot: Pass base address of GIC CPU interface, not whole GIC hw/vexpress.c: Add vexpress-a15 machine

 Makefile.target     |    2 +-
 hw/a15mpcore.c      |   93 ++++++++++++
 hw/a9mpcore.c       |   13 ++-
 hw/arm-misc.h       |    3 +-
 hw/arm11mpcore.c    |   17 ++-
 hw/arm_boot.c       |   18 ++-
 hw/arm_gic.c        |   68 +++++----
 hw/armv7m_nvic.c    |   31 +++-
 hw/realview.c       |   12 +-
 hw/realview_gic.c   |    7 +-
 hw/vexpress.c       |  407 +++++++++++++++++++++++++++++++++++++++++----------
 target-arm/cpu.h    |    2 +
 target-arm/helper.c |   68 ++++++++-
 13 files changed, 594 insertions(+), 147 deletions(-)
 create mode 100644 hw/a15mpcore.c
#1 lore raw

PATCH 1/12 vexpress, realview: Add (dummy) L2 cache controller

Instantiate the L2 cache controller on the ARM devboards which have one, since we have a dummy model of it now. Note that the only non-MP board with an L2x0 is the PB1176, which we don't model.

---
 hw/realview.c |    2 ++
 hw/vexpress.c |    1 +
 2 files changed, 3 insertions(+), 0 deletions(-)
hw/realview.c +2 -0
index d4191e9..e52babc 100644
@@ -225,6 +225,8 @@ static void realview_init(ram_addr_t ram_size,
         for (n = 0; n < smp_cpus; n++) {
             sysbus_connect_irq(busdev, n, cpu_irq[n]);
         }
+        sysbus_create_varargs("l2x0", realview_binfo.smp_priv_base + 0x2000,
+                              NULL);
     } else {
         uint32_t gic_addr = is_pb ? 0x1e000000 : 0x10040000;
         /* For now just create the nIRQ GIC, and ignore the others.  */
hw/vexpress.c +1 -0
index 0f39d8d..613be65 100644
@@ -180,6 +180,7 @@ static void vexpress_a9_init(ram_addr_t ram_size,
     /* 0x100ec000 TrustZone Address Space Controller */
     /* 0x10200000 CoreSight debug APB */
     /* 0x1e00a000 PL310 L2 Cache Controller */
+    sysbus_create_varargs("l2x0", 0x1e00a000, NULL);
 
     /* CS0: NOR0 flash          : 0x40000000 .. 0x44000000 */
     /* CS4: NOR1 flash          : 0x44000000 .. 0x48000000 */
Signature · 1 line
1.7.1
#2 lore raw

PATCH 2/12 arm: make the number of GIC interrupts configurable

In reply to Peter Maydell

From: Mark Langsdorf <[email protected]>

Increase the maximum number of GIC interrupts for a9mp and a11mp to 1020, and create a configurable property for each defaulting to 96 and 64 (respectively) so that device modelers can set the value appropriately for their SoC. Other ARM processors also set their maximum number of used IRQs appropriately.

Set the maximum theoretical number of GIC interrupts to 1020 and update the save/restore code to only use the appropriate number for each SoC.

[Peter Maydell: fixed minor whitespace snafu]
---
 hw/a9mpcore.c     |   13 +++++++--
 hw/arm11mpcore.c  |   17 ++++++++----
 hw/arm_gic.c      |   68 +++++++++++++++++++++++++++++-----------------------
 hw/armv7m_nvic.c  |   31 ++++++++++++++++++------
 hw/realview_gic.c |    7 ++++-
 5 files changed, 87 insertions(+), 49 deletions(-)
hw/a9mpcore.c +10 -3
index 3ef0e13..521b8cc 100644
@@ -11,9 +11,8 @@
 #include "sysbus.h"
 
 /* Configuration for arm_gic.c:
- * number of external IRQ lines, max number of CPUs, how to ID current CPU
+ * max number of CPUs, how to ID current CPU
  */
-#define GIC_NIRQ 96
 #define NCPU 4
 
 static inline int
@@ -37,6 +36,7 @@ typedef struct a9mp_priv_state {
     MemoryRegion ptimer_iomem;
     MemoryRegion container;
     DeviceState *mptimer;
+    uint32_t num_irq;
 } a9mp_priv_state;
 
 static uint64_t a9_scu_read(void *opaque, target_phys_addr_t offset,
@@ -153,7 +153,7 @@ static int a9mp_priv_init(SysBusDevice *dev)
         hw_error("a9mp_priv_init: num-cpu may not be more than %d\n", NCPU);
     }
 
-    gic_init(&s->gic, s->num_cpu);
+    gic_init(&s->gic, s->num_cpu, s->num_irq);
 
     s->mptimer = qdev_create(NULL, "arm_mptimer");
     qdev_prop_set_uint32(s->mptimer, "num-cpu", s->num_cpu);
@@ -216,6 +216,13 @@ static SysBusDeviceInfo a9mp_priv_info = {
     .qdev.reset = a9mp_priv_reset,
     .qdev.props = (Property[]) {
         DEFINE_PROP_UINT32("num-cpu", a9mp_priv_state, num_cpu, 1),
+        /* The Cortex-A9MP may have anything from 0 to 224 external interrupt
+         * IRQ lines (with another 32 internal). We default to 64+32, which
+         * is the number provided by the Cortex-A9MP test chip in the
+         * Realview PBX-A9 and Versatile Express A9 development boards.
+         * Other boards may differ and should set this property appropriately.
+         */
+        DEFINE_PROP_UINT32("num-irq", a9mp_priv_state, num_irq, 96),
         DEFINE_PROP_END_OF_LIST(),
     }
 };
hw/arm11mpcore.c +11 -6
index bc0457e..f4d88dc 100644
@@ -10,11 +10,6 @@
 #include "sysbus.h"
 #include "qemu-timer.h"
 
-/* ??? 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
-
 #define NCPU 4
 
 static inline int
@@ -37,6 +32,7 @@ typedef struct mpcore_priv_state {
     MemoryRegion iomem;
     MemoryRegion container;
     DeviceState *mptimer;
+    uint32_t num_irq;
 } mpcore_priv_state;
 
 /* Per-CPU private memory mapped IO.  */
@@ -132,7 +128,7 @@ static int mpcore_priv_init(SysBusDevice *dev)
 {
     mpcore_priv_state *s = FROM_SYSBUSGIC(mpcore_priv_state, dev);
 
-    gic_init(&s->gic, s->num_cpu);
+    gic_init(&s->gic, s->num_cpu, s->num_irq);
     s->mptimer = qdev_create(NULL, "arm_mptimer");
     qdev_prop_set_uint32(s->mptimer, "num-cpu", s->num_cpu);
     qdev_init_nofail(s->mptimer);
@@ -221,6 +217,15 @@ static SysBusDeviceInfo mpcore_priv_info = {
     .qdev.size  = sizeof(mpcore_priv_state),
     .qdev.props = (Property[]) {
         DEFINE_PROP_UINT32("num-cpu", mpcore_priv_state, num_cpu, 1),
+        /* The ARM11 MPCORE TRM says the on-chip controller may have
+         * anything from 0 to 224 external interrupt IRQ lines (with another
+         * 32 internal). We default to 32+32, which is the number provided by
+         * the ARM11 MPCore test chip in the Realview Versatile Express
+         * coretile. Other boards may differ and should set this property
+         * appropriately. Some Linux kernels may not boot if the hardware
+         * has more IRQ lines than the kernel expects.
+         */
+        DEFINE_PROP_UINT32("num-irq", mpcore_priv_state, num_irq, 64),
         DEFINE_PROP_END_OF_LIST(),
     }
 };
hw/arm_gic.c +38 -30

232 lines not on this page · raw message

hw/armv7m_nvic.c +23 -8
index bf8c3c5..28f36ba 100644
@@ -15,9 +15,6 @@
 #include "arm-misc.h"
 #include "exec-memory.h"
 
-/* 32 internal lines (16 used for system exceptions) plus 64 external
-   interrupt lines.  */
-#define GIC_NIRQ 96
 #define NCPU 1
 #define NVIC 1
 
@@ -41,6 +38,7 @@ typedef struct {
         int64_t tick;
         QEMUTimer *timer;
     } systick;
+    uint32_t num_irq;
 } nvic_state;
 
 /* qemu timers run at 1GHz.   We want something closer to 1MHz.  */
@@ -125,7 +123,7 @@ static uint32_t nvic_readl(void *opaque, uint32_t offset)
 
     switch (offset) {
     case 4: /* Interrupt Control Type.  */
-        return (GIC_NIRQ / 32) - 1;
+        return (s->num_irq / 32) - 1;
     case 0x10: /* SysTick Control and Status.  */
         val = s->systick.control;
         s->systick.control &= ~SYSTICK_COUNTFLAG;
@@ -169,7 +167,7 @@ static uint32_t nvic_readl(void *opaque, uint32_t offset)
         if (s->gic.current_pending[0] != 1023)
             val |= (s->gic.current_pending[0] << 12);
         /* ISRPENDING */
-        for (irq = 32; irq < GIC_NIRQ; irq++) {
+        for (irq = 32; irq < s->num_irq; irq++) {
             if (s->gic.irq_state[irq].pending) {
                 val |= (1 << 22);
                 break;
@@ -384,16 +382,33 @@ static int armv7m_nvic_init(SysBusDevice *dev)
 {
     nvic_state *s= FROM_SYSBUSGIC(nvic_state, dev);
 
-    gic_init(&s->gic);
+   /* note that for the M profile gic_init() takes the number of external
+    * interrupt lines only.
+    */
+    gic_init(&s->gic, s->num_irq);
     memory_region_add_subregion(get_system_memory(), 0xe000e000, &s->gic.iomem);
     s->systick.timer = qemu_new_timer_ns(vm_clock, systick_timer_tick, s);
-    vmstate_register(&dev->qdev, -1, &vmstate_nvic, s);
     return 0;
 }
 
+static SysBusDeviceInfo armv7m_nvic_priv_info = {
+    .init = armv7m_nvic_init,
+    .qdev.name  = "armv7m_nvic",
+    .qdev.size  = sizeof(nvic_state),
+    .qdev.vmsd  = &vmstate_nvic,
+    .qdev.props = (Property[]) {
+        /* The ARM v7m may have anything from 0 to 496 external interrupt
+         * IRQ lines. We default to 64. Other boards may differ and should
+         * set this property appropriately.
+         */
+        DEFINE_PROP_UINT32("num-irq", nvic_state, num_irq, 64),
+        DEFINE_PROP_END_OF_LIST(),
+    }
+};
+
 static void armv7m_nvic_register_devices(void)
 {
-    sysbus_register_dev("armv7m_nvic", sizeof(nvic_state), armv7m_nvic_init);
+    sysbus_register_withprop(&armv7m_nvic_priv_info);
 }
 
 device_init(armv7m_nvic_register_devices)
hw/realview_gic.c +5 -2
index 8c4d509..7342ede 100644
@@ -9,7 +9,6 @@
 
 #include "sysbus.h"
 
-#define GIC_NIRQ 96
 #define NCPU 1
 
 /* Only a single "CPU" interface is present.  */
@@ -37,7 +36,11 @@ static int realview_gic_init(SysBusDevice *dev)
 {
     RealViewGICState *s = FROM_SYSBUSGIC(RealViewGICState, dev);
 
-    gic_init(&s->gic);
+    /* The GICs on the RealView boards have a fixed nonconfigurable
+     * number of interrupt lines, so we don't need to expose this as
+     * a qdev property.
+     */
+    gic_init(&s->gic, 96);
     realview_gic_map_setup(s);
     sysbus_init_mmio(dev, &s->container);
     return 0;
Signature · 1 line
1.7.1
#3 lore raw

Re: [Qemu-devel] [Android-virt] [PATCH 02/12] arm: make the number of GIC interrupts configurable

Quoted 11 lines · Peter Maydell

From: Mark Langsdorf <[email protected]>

Increase the maximum number of GIC interrupts for a9mp and a11mp to 1020, and create a configurable property for each defaulting to 96 and 64 (respectively) so that device modelers can set the value appropriately for their SoC. Other ARM processors also set their maximum number of used IRQs appropriately.

Set the maximum theoretical number of GIC interrupts to 1020 and update the save/restore code to only use the appropriate number for each SoC.

Reading through this, I see a lot of "- 32". Trivial patch follows, which applies to your rebasing branch:

Subject: ARM: clean up GIC constants.
From: Rusty Russell <[email protected]>

Interrupts numbers 0-31 are private to the processor interface, 32-1019 are general interrups. Add GIC_INTERNAL and substitute everywhere.

Also, add a check that the total number of interrupts is divisible by 32 (required for reporting interupt numbers, see gic_dist_readb(), and is greater than 32. And remove a single stray tab.

---
 hw/arm_gic.c |   48 ++++++++++++++++++++++++++----------------------
 1 files changed, 26 insertions(+), 22 deletions(-)
hw/arm_gic.c +26 -22
index cf582a5..a29eacb 100644
@@ -13,6 +13,8 @@
 
 /* Maximum number of possible interrupts, determined by the GIC architecture */
 #define GIC_MAXIRQ 1020
+/* First 32 are private to each CPU (SGIs and PPIs). */
+#define GIC_INTERNAL 32
 //#define DEBUG_GIC
 
 #ifdef DEBUG_GIC
@@ -74,7 +76,7 @@ typedef struct gic_irq_state
 #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])
+  (((irq) < GIC_INTERNAL) ? s->priority1[irq][cpu] : s->priority2[(irq) - GIC_INTERNAL])
 #ifdef NVIC
 #define GIC_TARGET(irq) 1
 #else
@@ -92,8 +94,8 @@ typedef struct gic_state
 #ifndef NVIC
     int irq_target[GIC_MAXIRQ];
 #endif
-    int priority1[32][NCPU];
-    int priority2[GIC_MAXIRQ - 32];
+    int priority1[GIC_INTERNAL][NCPU];
+    int priority2[GIC_MAXIRQ - GIC_INTERNAL];
     int last_active[GIC_MAXIRQ][NCPU];
 
     int priority_mask[NCPU];
@@ -131,7 +133,7 @@ static void gic_update(gic_state *s)
         cm = 1 << cpu;
         s->current_pending[cpu] = 1023;
         if (!s->enabled || !s->cpu_enabled[cpu]) {
-	    qemu_irq_lower(s->parent_irq[cpu]);
+            qemu_irq_lower(s->parent_irq[cpu]);
             return;
         }
         best_prio = 0x100;
@@ -174,7 +176,7 @@ 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;
+    irq += GIC_INTERNAL;
     if (level == GIC_TEST_LEVEL(irq, ALL_CPU_MASK))
         return;
 
@@ -316,7 +318,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
         if (irq >= s->num_irq)
             goto bad_reg;
         res = 0;
-        mask = (irq < 32) ?  cm : ALL_CPU_MASK;
+        mask = (irq < GIC_INTERNAL) ?  cm : ALL_CPU_MASK;
         for (i = 0; i < 8; i++) {
             if (GIC_TEST_PENDING(irq + i, mask)) {
                 res |= (1 << i);
@@ -328,7 +330,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
         if (irq >= s->num_irq)
             goto bad_reg;
         res = 0;
-        mask = (irq < 32) ?  cm : ALL_CPU_MASK;
+        mask = (irq < GIC_INTERNAL) ?  cm : ALL_CPU_MASK;
         for (i = 0; i < 8; i++) {
             if (GIC_TEST_ACTIVE(irq + i, mask)) {
                 res |= (1 << i);
@@ -435,8 +437,8 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
           value = 0xff;
         for (i = 0; i < 8; i++) {
             if (value & (1 << i)) {
-                int mask = (irq < 32) ? (1 << cpu) : GIC_TARGET(irq);
-                int cm = (irq < 32) ? (1 << cpu) : ALL_CPU_MASK;
+                int mask = (irq < GIC_INTERNAL) ? (1 << cpu) : GIC_TARGET(irq);
+                int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
 
                 if (!GIC_TEST_ENABLED(irq + i, cm)) {
                     DPRINTF("Enabled IRQ %d\n", irq + i);
@@ -460,7 +462,7 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
           value = 0;
         for (i = 0; i < 8; i++) {
             if (value & (1 << i)) {
-                int cm = (irq < 32) ? (1 << cpu) : ALL_CPU_MASK;
+                int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
 
                 if (GIC_TEST_ENABLED(irq + i, cm)) {
                     DPRINTF("Disabled IRQ %d\n", irq + i);
@@ -502,10 +504,10 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
         irq = (offset - 0x400) + GIC_BASE_IRQ;
         if (irq >= s->num_irq)
             goto bad_reg;
-        if (irq < 32) {
+        if (irq < GIC_INTERNAL) {
             s->priority1[irq][cpu] = value;
         } else {
-            s->priority2[irq - 32] = value;
+            s->priority2[irq - GIC_INTERNAL] = value;
         }
 #ifndef NVIC
     } else if (offset < 0xc00) {
@@ -515,7 +517,7 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
             goto bad_reg;
         if (irq < 29)
             value = 0;
-        else if (irq < 32)
+        else if (irq < GIC_INTERNAL)
             value = ALL_CPU_MASK;
         s->irq_target[irq] = value & ALL_CPU_MASK;
     } else if (offset < 0xf00) {
@@ -523,7 +525,7 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
         irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ;
         if (irq >= s->num_irq)
             goto bad_reg;
-        if (irq < 32)
+        if (irq < GIC_INTERNAL)
             value |= 0xaa;
         for (i = 0; i < 4; i++) {
             if (value & (1 << (i * 2))) {
@@ -736,7 +738,7 @@ static void gic_save(QEMUFile *f, void *opaque)
     qemu_put_be32(f, s->enabled);
     for (i = 0; i < NUM_CPU(s); i++) {
         qemu_put_be32(f, s->cpu_enabled[i]);
-        for (j = 0; j < 32; j++)
+        for (j = 0; j < GIC_INTERNAL; j++)
             qemu_put_be32(f, s->priority1[j][i]);
         for (j = 0; j < s->num_irq; j++)
             qemu_put_be32(f, s->last_active[j][i]);
@@ -745,7 +747,7 @@ static void gic_save(QEMUFile *f, void *opaque)
         qemu_put_be32(f, s->running_priority[i]);
         qemu_put_be32(f, s->current_pending[i]);
     }
-    for (i = 0; i < s->num_irq - 32; i++) {
+    for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) {
         qemu_put_be32(f, s->priority2[i]);
     }
     for (i = 0; i < s->num_irq; i++) {
@@ -773,7 +775,7 @@ static int gic_load(QEMUFile *f, void *opaque, int version_id)
     s->enabled = qemu_get_be32(f);
     for (i = 0; i < NUM_CPU(s); i++) {
         s->cpu_enabled[i] = qemu_get_be32(f);
-        for (j = 0; j < 32; j++)
+        for (j = 0; j < GIC_INTERNAL; j++)
             s->priority1[j][i] = qemu_get_be32(f);
         for (j = 0; j < s->num_irq; j++)
             s->last_active[j][i] = qemu_get_be32(f);
@@ -782,7 +784,7 @@ static int gic_load(QEMUFile *f, void *opaque, int version_id)
         s->running_priority[i] = qemu_get_be32(f);
         s->current_pending[i] = qemu_get_be32(f);
     }
-    for (i = 0; i < s->num_irq - 32; i++) {
+    for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) {
         s->priority2[i] = qemu_get_be32(f);
     }
     for (i = 0; i < s->num_irq; i++) {
@@ -812,11 +814,13 @@ static void gic_init(gic_state *s, int num_irq)
     s->num_cpu = num_cpu;
 #endif
     s->num_irq = num_irq + GIC_BASE_IRQ;
-    if (s->num_irq > GIC_MAXIRQ) {
-        hw_error("requested %u interrupt lines exceeds GIC maximum %d\n",
-                 num_irq, GIC_MAXIRQ);
+    if (s->num_irq > GIC_MAXIRQ
+        || s->num_irq < GIC_INTERNAL
+        || (s->num_irq % 32) != 0) {
+        hw_error("requested %u interrupt lines outside GIC range %d-%d\n",
+                 num_irq, GIC_INTERNAL, GIC_MAXIRQ);
     }
-    qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, s->num_irq - 32);
+    qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, s->num_irq - GIC_INTERNAL);
     for (i = 0; i < NUM_CPU(s); i++) {
         sysbus_init_irq(&s->busdev, &s->parent_irq[i]);
     }
#4 lore raw

Re: [Qemu-devel] [Android-virt] [PATCH 02/12] arm: make the number of GIC interrupts configurable

Quoted 9 lines · Rusty Russell
Quoted 8 lines · Peter Maydell

From: Mark Langsdorf <[email protected]>

Increase the maximum number of GIC interrupts for a9mp and a11mp to 1020, and create a configurable property for each defaulting to 96 and 64 (respectively) so that device modelers can set the value appropriately for their SoC. Other ARM processors also set their maximum number of used IRQs appropriately.

Quoted 6 lines
Quoted 3 lines

Set the maximum theoretical number of GIC interrupts to 1020 and update the save/restore code to only use the appropriate number for each SoC.

Reading through this, I see a lot of "- 32".  Trivial patch follows, which applies to your rebasing branch:

(If you send patches as fresh new emails then they just apply with git am without needing manual cleanup, appear with sensible subjects in patchwork/patches.linaro, etc.)

Quoted 9 lines

Subject: ARM: clean up GIC constants.
From: Rusty Russell <[email protected]>

Interrupts numbers 0-31 are private to the processor interface, 32-1019 are general interrups.  Add GIC_INTERNAL and substitute everywhere.

Also, add a check that the total number of interrupts is divisible by 32 (required for reporting interupt numbers, see gic_dist_readb(), and is greater than 32.  And remove a single stray tab.

I agree with Avi that the presence of "Also" in a git commit message is generally a sign you should have submitted more than one patch :-)

Quoted 19 lines

---
 hw/arm_gic.c |   48 ++++++++++++++++++++++++++----------------------

 1 files changed, 26 insertions(+), 22 deletions(-)
hw/arm_gic.c +2 -0
index cf582a5..a29eacb 100644
@@ -13,6 +13,8 @@
 
  /* Maximum number of possible interrupts, determined by the GIC architecture */
  #define GIC_MAXIRQ 1020
+/* First 32 are private to each CPU (SGIs and PPIs). */
+#define GIC_INTERNAL 32
  //#define DEBUG_GIC
 
  #ifdef DEBUG_GIC

hw/arm_gic.c typedef struct gic_irq_state · line 79

Quoted 5 lines
hw/arm_gic.c +1 -1
@@ -74,7 +76,7 @@ typedef struct gic_irq_state
  #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])
+  (((irq) < GIC_INTERNAL) ? s->priority1[irq][cpu] : s->priority2[(irq) - GIC_INTERNAL])

This line is now >80 characters and needs folding. (scripts/checkpatch.pl will catch this kind of nit.)

hw/arm_gic.c static void gic_init(gic_state *s, int num_irq) · line 819

Quoted 10 lines
hw/arm_gic.c +3 -3
@@ -812,11 +814,13 @@ static void gic_init(gic_state *s, int num_irq)
     s->num_cpu = num_cpu;
  #endif
     s->num_irq = num_irq + GIC_BASE_IRQ;
-    if (s->num_irq > GIC_MAXIRQ) {
-        hw_error("requested %u interrupt lines exceeds GIC maximum %d\n",
-                 num_irq, GIC_MAXIRQ);
+    if (s->num_irq > GIC_MAXIRQ
+        || s->num_irq < GIC_INTERNAL
+        || (s->num_irq % 32) != 0) {

So I guess our implementation isn't likely to work properly for a non-multiple-of-32 number of IRQs, but this isn't an architectural GIC restriction. (In fact the GIC architecture spec allows the supported interrupt IDs to not even be in a contiguous range, which we certainly don't support...)
I also think it's architecturally permitted that not all the internal (SPI/PPI) interrupts are implemented, ie that s->num_irq < 32 (you have to read the GIC architecture manually quite closely to deduce this, though).

Anyway, if we would otherwise die horribly later on we should catch these cases, but it would be good to have at least a comment saying that these are implementation limitations rather than architectural ones.

Beefing up the parameter check should be a separate patch.

Otherwise OK.

-- PMM

#5 lore raw

Re: [Qemu-devel] [Android-virt] [PATCH 02/12] arm: make the number of GIC interrupts configurable

Quoted 7 lines · Peter Maydell
Quoted 2 lines · Rusty Russell

Reading through this, I see a lot of "- 32".  Trivial patch follows, which applies to your rebasing branch:

(If you send patches as fresh new emails then they just apply with git am without needing manual cleanup, appear with sensible subjects in patchwork/patches.linaro, etc.)

Indeed, but it's so conversaionally gauche :( I thought git am did the Right Thing, but it doesn't, and --scissors doesn't help either (it gets the wrong Subject line). Oh well, I'll do it that way in future.

Quoted 6 lines
Quoted 6 lines

Subject: ARM: clean up GIC constants.
From: Rusty Russell <[email protected]>

Interrupts numbers 0-31 are private to the processor interface, 32-1019 are general interrups.  Add GIC_INTERNAL and substitute everywhere.

Quoted 7 lines
Quoted 3 lines

Also, add a check that the total number of interrupts is divisible by 32 (required for reporting interupt numbers, see gic_dist_readb(), and is greater than 32.  And remove a single stray tab.

I agree with Avi that the presence of "Also" in a git commit message is generally a sign you should have submitted more than one patch :-)

Indeed, guilty as charged :)

Quoted 17 lines
Quoted 17 lines

---
 hw/arm_gic.c |   48 ++++++++++++++++++++++++++----------------------

 1 files changed, 26 insertions(+), 22 deletions(-)
hw/arm_gic.c +2 -0
index cf582a5..a29eacb 100644
@@ -13,6 +13,8 @@
 
  /* Maximum number of possible interrupts, determined by the GIC architecture */
  #define GIC_MAXIRQ 1020
+/* First 32 are private to each CPU (SGIs and PPIs). */
+#define GIC_INTERNAL 32
  //#define DEBUG_GIC
 
Quoted 10 lines

hw/arm_gic.c typedef struct gic_irq_state · line 79

Quoted 7 lines
hw/arm_gic.c +1 -1
  #ifdef DEBUG_GIC
@@ -74,7 +76,7 @@ typedef struct gic_irq_state
  #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])
+  (((irq) < GIC_INTERNAL) ? s->priority1[irq][cpu] : s->priority2[(irq) - GIC_INTERNAL])

This line is now >80 characters and needs folding. (scripts/checkpatch.pl will catch this kind of nit.)

Will do.

Quoted 8 lines
Quoted 8 lines
hw/arm_gic.c +1 -3
@@ -812,11 +814,13 @@ static void gic_init(gic_state *s, int num_irq)
     s->num_cpu = num_cpu;
  #endif
     s->num_irq = num_irq + GIC_BASE_IRQ;
-    if (s->num_irq > GIC_MAXIRQ) {
-        hw_error("requested %u interrupt lines exceeds GIC maximum %d\n",
-                 num_irq, GIC_MAXIRQ);
+    if (s->num_irq > GIC_MAXIRQ
Quoted 8 lines

hw/arm_gic.c static void gic_init(gic_state *s, int num_irq) · line 819

Quoted 2 lines
hw/arm_gic.c +2 -0
+        || s->num_irq < GIC_INTERNAL
+        || (s->num_irq % 32) != 0) {

So I guess our implementation isn't likely to work properly for a non-multiple-of-32 number of IRQs, but this isn't an architectural GIC restriction. (In fact the GIC architecture spec allows the supported interrupt IDs to not even be in a contiguous range, which we certainly don't support...)

Yes, I intuited it from here:

static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) {
...

        if (offset == 4)
            return ((s->num_irq / 32) - 1) | ((NUM_CPU(s) - 1) << 5);

If want want to support non-32-divisible # irqs, we need at least:

        ((s->num_irq + 31) / 32 - 1)

Seemed easier to have an initialization-time assert than check everywhere else for overflows.

Quoted 4 lines

I also think it's architecturally permitted that not all the internal (SPI/PPI) interrupts are implemented, ie that s->num_irq < 32 (you have to read the GIC architecture manually quite closely to deduce this, though).

This made me read that part of the manual... interesting.

Quoted 4 lines

Anyway, if we would otherwise die horribly later on we should catch these cases, but it would be good to have at least a comment saying that these are implementation limitations rather than architectural ones.

Good point. If we add an "supported" bit to each irq, we could do weird things, but presumably ->num_irq would still correspond to ITLinesNumber.

I don't want to put too much of an essay in there. How's this:

        /* ITLinesNumber is represented as (N - 32) / 1.  See
          gic_dist_readb. */
        if (s->num_irq < 32 || (s->num_irq % 32)) {
                hw_error("%u interrupt lines unsupported: not divisible by 32\n",
                         num_irq);
Quoted 1 line

Beefing up the parameter check should be a separate patch.

Thanks, coming soon.

I should be getting access to git.linaro.org RSN, so I can post there for easier merging.

Thanks,
Rusty.

#6 lore raw

Re: [Qemu-devel] [Android-virt] [PATCH 02/12] arm: make the number of GIC interrupts configurable

Quoted 10 lines · Rusty Russell
Quoted 4 lines · Peter Maydell

Anyway, if we would otherwise die horribly later on we should catch these cases, but it would be good to have at least a comment saying that these are implementation limitations rather than architectural ones.

Good point.  If we add an "supported" bit to each irq, we could do weird things, but presumably ->num_irq would still correspond to ITLinesNumber.

Quoted 7 lines

I don't want to put too much of an essay in there.  How's this:

       /* ITLinesNumber is represented as (N - 32) / 1.  See gic_dist_readb. */
       if (s->num_irq < 32 || (s->num_irq % 32)) {
               hw_error("%u interrupt lines unsupported: not divisible by 32\n", num_irq);

I think that's a notch too terse for my taste. How about:

/* ITLinesNumber is represented as (N - 32) / 1 (see
* gic_dist_readb) so this is an implementation imposed
* restriction, not an architectural one:
*/

thanks
-- PMM

#7 lore raw

PATCH 1/1 arm: make sure that number of irqs can be represented in GICD_TYPER.

In reply to Rusty Russell

We currently assume that the number of interrupts (ITLinesNumber in the architecture reference manual) is divisible by 32, since we present it to the guest when it reads GICD_TYPER (in gic_dist_readb()) as (N - 32) / 1.

hw/arm_gic.c +9 -0
index fa6a60a..6446800 100644
@@ -818,6 +818,15 @@ static void gic_init(gic_state *s, int num_irq)
         hw_error("requested %u interrupt lines exceeds GIC maximum %d\n",
                  num_irq, GIC_MAXIRQ);
     }
+    /* ITLinesNumber is represented as (N - 32) / 1 (see
+     * gic_dist_readb) so this is an implementation imposed
+     * restriction, not an architectural one:
+     */
+    if (s->num_irq < 32 || (s->num_irq % 32)) {
+        hw_error("%d interrupt lines unsupported: not divisible by 32\n",
+                 num_irq);
+    }
+
     qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, s->num_irq - GIC_INTERNAL);
     for (i = 0; i < NUM_CPU(s); i++) {
         sysbus_init_irq(&s->busdev, &s->parent_irq[i]);
#8 lore raw

Re: [Qemu-devel] [Android-virt] [PATCH] arm: make sure that number of irqs can be represented in GICD_TYPER.

Quoted 12 lines · Rusty Russell

We currently assume that the number of interrupts (ITLinesNumber in the architecture reference manual) is divisible by 32, since we present it to the guest when it reads GICD_TYPER (in gic_dist_readb()) as (N - 32) / 1.

hw/arm_gic.c +0 -0
index fa6a60a..6446800 100644

hw/arm_gic.c static void gic_init(gic_state *s, int num_irq) · line 824

Quoted 7 lines
hw/arm_gic.c +4 -0
@@ -818,6 +818,15 @@ static void gic_init(gic_state *s, int num_irq)
         hw_error("requested %u interrupt lines exceeds GIC maximum %d\n",
                  num_irq, GIC_MAXIRQ);
     }
+    /* ITLinesNumber is represented as (N - 32) / 1 (see
+     * gic_dist_readb) so this is an implementation imposed
+     * restriction, not an architectural one:
+     */

What is this division by 1? Isn't (N - 32) / 1 == (N - 32) and how does that explain the division. Was it (N / 32) - 1? Or is this a cast? Or am I missing something completely?

Quoted 12 lines
hw/arm_gic.c +5 -0
+    if (s->num_irq < 32 || (s->num_irq % 32)) {
+        hw_error("%d interrupt lines unsupported: not divisible by 32\n",
+                 num_irq);
+    }
+
     qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, s->num_irq - GIC_INTERNAL);
     for (i = 0; i < NUM_CPU(s); i++) {
         sysbus_init_irq(&s->busdev, &s->parent_irq[i]);

_______________________________________________
Android-virt mailing list
[email protected]
https://lists.cs.columbia.edu/cucslists/listinfo/android-virt

#9 lore raw

[Qemu-devel] [PATCH 3/2] arm: make sure that number of irqs can be represented in GICD_TYPER.

We currently assume that the number of interrupts (ITLinesNumber in the architecture reference manual) is divisible by 32, since we present it to the guest when it reads GICD_TYPER (in gic_dist_readb()) as (N / 32) - 1.

Thanks to Christoffer Dall for undoing comment thinko.

hw/arm_gic.c +9 -0
index fa6a60a..6446800 100644
@@ -818,6 +818,15 @@ static void gic_init(gic_state *s, int num_irq)
         hw_error("requested %u interrupt lines exceeds GIC maximum %d\n",
                  num_irq, GIC_MAXIRQ);
     }
+    /* ITLinesNumber is represented as (N / 32) - 1 (see
+     * gic_dist_readb) so this is an implementation imposed
+     * restriction, not an architectural one:
+     */
+    if (s->num_irq < 32 || (s->num_irq % 32)) {
+        hw_error("%d interrupt lines unsupported: not divisible by 32\n",
+                 num_irq);
+    }
+
     qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, s->num_irq - GIC_INTERNAL);
     for (i = 0; i < NUM_CPU(s); i++) {
         sysbus_init_irq(&s->busdev, &s->parent_irq[i]);
#10 lore raw

Re: [Qemu-devel] [Android-virt] [PATCH] arm: make sure that number of irqs can be represented in GICD_TYPER.

In reply to Christoffer Dall
Quoted 8 lines · Christoffer Dall
Quoted 7 lines · Rusty Russell

We currently assume that the number of interrupts (ITLinesNumber in the architecture reference manual) is divisible by 32, since we present it to the guest when it reads GICD_TYPER (in gic_dist_readb()) as (N - 32) / 1.

Quoted 16 lines

hw/arm_gic.c static void gic_init(gic_state *s, int num_irq) · line 824

Quoted 12 lines
hw/arm_gic.c +4 -0
index fa6a60a..6446800 100644
@@ -818,6 +818,15 @@ static void gic_init(gic_state *s, int num_irq)
         hw_error("requested %u interrupt lines exceeds GIC maximum %d\n",
                  num_irq, GIC_MAXIRQ);
     }
+    /* ITLinesNumber is represented as (N - 32) / 1 (see
+     * gic_dist_readb) so this is an implementation imposed
+     * restriction, not an architectural one:
+     */

What is this division by 1? Isn't (N - 32) / 1 == (N - 32) and how does that explain the division. Was it (N / 32) - 1? Or is this a cast? Or am I missing something completely?

That's hilarious; we were concentrating so hard on the wording of the comment, we didn't notice that I completely screwed the content :)

It is, of course, (N / 32) - 1:

static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) {
...

        if (offset == 4)
            return ((GIC_NIRQ / 32) - 1) | ((NUM_CPU(s) - 1) << 5);

I'll fix it, thanks!
Rusty.

#11 lore raw

PATCH 1/2 arm: clean up GIC constants.

In reply to Rusty Russell

Interrupts numbers 0-31 are private to the processor interface, 32-1019 are general interrups. Add GIC_INTERNAL and substitute everywhere.

hw/arm_gic.c +20 -18
index cf582a5..fa6a60a 100644
@@ -13,6 +13,8 @@
 
 /* Maximum number of possible interrupts, determined by the GIC architecture */
 #define GIC_MAXIRQ 1020
+/* First 32 are private to each CPU (SGIs and PPIs). */
+#define GIC_INTERNAL 32
 //#define DEBUG_GIC
 
 #ifdef DEBUG_GIC
@@ -74,7 +76,7 @@ typedef struct gic_irq_state
 #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])
+  (((irq) < GIC_INTERNAL) ? s->priority1[irq][cpu] : s->priority2[(irq) - GIC_INTERNAL])
 #ifdef NVIC
 #define GIC_TARGET(irq) 1
 #else
@@ -92,8 +94,8 @@ typedef struct gic_state
 #ifndef NVIC
     int irq_target[GIC_MAXIRQ];
 #endif
-    int priority1[32][NCPU];
-    int priority2[GIC_MAXIRQ - 32];
+    int priority1[GIC_INTERNAL][NCPU];
+    int priority2[GIC_MAXIRQ - GIC_INTERNAL];
     int last_active[GIC_MAXIRQ][NCPU];
 
     int priority_mask[NCPU];
@@ -174,7 +176,7 @@ 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;
+    irq += GIC_INTERNAL;
     if (level == GIC_TEST_LEVEL(irq, ALL_CPU_MASK))
         return;
 
@@ -316,7 +318,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
         if (irq >= s->num_irq)
             goto bad_reg;
         res = 0;
-        mask = (irq < 32) ?  cm : ALL_CPU_MASK;
+        mask = (irq < GIC_INTERNAL) ?  cm : ALL_CPU_MASK;
         for (i = 0; i < 8; i++) {
             if (GIC_TEST_PENDING(irq + i, mask)) {
                 res |= (1 << i);
@@ -328,7 +330,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
         if (irq >= s->num_irq)
             goto bad_reg;
         res = 0;
-        mask = (irq < 32) ?  cm : ALL_CPU_MASK;
+        mask = (irq < GIC_INTERNAL) ?  cm : ALL_CPU_MASK;
         for (i = 0; i < 8; i++) {
             if (GIC_TEST_ACTIVE(irq + i, mask)) {
                 res |= (1 << i);
@@ -435,8 +437,8 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
           value = 0xff;
         for (i = 0; i < 8; i++) {
             if (value & (1 << i)) {
-                int mask = (irq < 32) ? (1 << cpu) : GIC_TARGET(irq);
-                int cm = (irq < 32) ? (1 << cpu) : ALL_CPU_MASK;
+                int mask = (irq < GIC_INTERNAL) ? (1 << cpu) : GIC_TARGET(irq);
+                int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
 
                 if (!GIC_TEST_ENABLED(irq + i, cm)) {
                     DPRINTF("Enabled IRQ %d\n", irq + i);
@@ -460,7 +462,7 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
           value = 0;
         for (i = 0; i < 8; i++) {
             if (value & (1 << i)) {
-                int cm = (irq < 32) ? (1 << cpu) : ALL_CPU_MASK;
+                int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
 
                 if (GIC_TEST_ENABLED(irq + i, cm)) {
                     DPRINTF("Disabled IRQ %d\n", irq + i);
@@ -502,10 +504,10 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
         irq = (offset - 0x400) + GIC_BASE_IRQ;
         if (irq >= s->num_irq)
             goto bad_reg;
-        if (irq < 32) {
+        if (irq < GIC_INTERNAL) {
             s->priority1[irq][cpu] = value;
         } else {
-            s->priority2[irq - 32] = value;
+            s->priority2[irq - GIC_INTERNAL] = value;
         }
 #ifndef NVIC
     } else if (offset < 0xc00) {
@@ -515,7 +517,7 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
             goto bad_reg;
         if (irq < 29)
             value = 0;
-        else if (irq < 32)
+        else if (irq < GIC_INTERNAL)
             value = ALL_CPU_MASK;
         s->irq_target[irq] = value & ALL_CPU_MASK;
     } else if (offset < 0xf00) {
@@ -523,7 +525,7 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
         irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ;
         if (irq >= s->num_irq)
             goto bad_reg;
-        if (irq < 32)
+        if (irq < GIC_INTERNAL)
             value |= 0xaa;
         for (i = 0; i < 4; i++) {
             if (value & (1 << (i * 2))) {
@@ -736,7 +738,7 @@ static void gic_save(QEMUFile *f, void *opaque)
     qemu_put_be32(f, s->enabled);
     for (i = 0; i < NUM_CPU(s); i++) {
         qemu_put_be32(f, s->cpu_enabled[i]);
-        for (j = 0; j < 32; j++)
+        for (j = 0; j < GIC_INTERNAL; j++)
             qemu_put_be32(f, s->priority1[j][i]);
         for (j = 0; j < s->num_irq; j++)
             qemu_put_be32(f, s->last_active[j][i]);
@@ -745,7 +747,7 @@ static void gic_save(QEMUFile *f, void *opaque)
         qemu_put_be32(f, s->running_priority[i]);
         qemu_put_be32(f, s->current_pending[i]);
     }
-    for (i = 0; i < s->num_irq - 32; i++) {
+    for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) {
         qemu_put_be32(f, s->priority2[i]);
     }
     for (i = 0; i < s->num_irq; i++) {
@@ -773,7 +775,7 @@ static int gic_load(QEMUFile *f, void *opaque, int version_id)
     s->enabled = qemu_get_be32(f);
     for (i = 0; i < NUM_CPU(s); i++) {
         s->cpu_enabled[i] = qemu_get_be32(f);
-        for (j = 0; j < 32; j++)
+        for (j = 0; j < GIC_INTERNAL; j++)
             s->priority1[j][i] = qemu_get_be32(f);
         for (j = 0; j < s->num_irq; j++)
             s->last_active[j][i] = qemu_get_be32(f);
@@ -782,7 +784,7 @@ static int gic_load(QEMUFile *f, void *opaque, int version_id)
         s->running_priority[i] = qemu_get_be32(f);
         s->current_pending[i] = qemu_get_be32(f);
     }
-    for (i = 0; i < s->num_irq - 32; i++) {
+    for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) {
         s->priority2[i] = qemu_get_be32(f);
     }
     for (i = 0; i < s->num_irq; i++) {
@@ -816,7 +818,7 @@ static void gic_init(gic_state *s, int num_irq)
         hw_error("requested %u interrupt lines exceeds GIC maximum %d\n",
                  num_irq, GIC_MAXIRQ);
     }
-    qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, s->num_irq - 32);
+    qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, s->num_irq - GIC_INTERNAL);
     for (i = 0; i < NUM_CPU(s); i++) {
         sysbus_init_irq(&s->busdev, &s->parent_irq[i]);
     }
#12 lore raw

Re: [Qemu-devel] [PATCH 1/2] arm: clean up GIC constants.

Quoted 2 lines · Rusty Russell

Interrupts numbers 0-31 are private to the processor interface, 32-1019 are general interrups.  Add GIC_INTERNAL and substitute everywhere.

"interrupts".

hw/arm_gic.c typedef struct gic_irq_state · line 79

Quoted 6 lines
hw/arm_gic.c +1 -1
@@ -74,7 +76,7 @@ typedef struct gic_irq_state
  #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])
+  (((irq) < GIC_INTERNAL) ? s->priority1[irq][cpu] : s->priority2[(irq) - GIC_INTERNAL])

This line is now >80 chars and needs folding (ISTR saying this for the first version of this patch).

Also, which patches are actually in this series? This is a 1/2, and I also see a patch with no series indication and a 3/2 which seems to be a respun version of that, but no 2/2...

-- PMM

#13 lore raw

PATCH 1/2 arm: clean up GIC constants.

Interrupts numbers 0-31 are private to the processor interface, 32-1019 are general interrups. Add GIC_INTERNAL and substitute everywhere.

---
 hw/arm_gic.c |   41 ++++++++++++++++++++++-------------------
 1 files changed, 22 insertions(+), 19 deletions(-)
hw/arm_gic.c +22 -19
index cf582a5..4b1b486 100644
@@ -13,6 +13,8 @@
 
 /* Maximum number of possible interrupts, determined by the GIC architecture */
 #define GIC_MAXIRQ 1020
+/* First 32 are private to each CPU (SGIs and PPIs). */
+#define GIC_INTERNAL 32
 //#define DEBUG_GIC
 
 #ifdef DEBUG_GIC
@@ -73,8 +75,9 @@ typedef struct gic_irq_state
 #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])
+#define GIC_GET_PRIORITY(irq, cpu) (((irq) < GIC_INTERNAL) ?		\
+				    s->priority1[irq][cpu] :		\
+				    s->priority2[(irq) - GIC_INTERNAL])
 #ifdef NVIC
 #define GIC_TARGET(irq) 1
 #else
@@ -92,8 +95,8 @@ typedef struct gic_state
 #ifndef NVIC
     int irq_target[GIC_MAXIRQ];
 #endif
-    int priority1[32][NCPU];
-    int priority2[GIC_MAXIRQ - 32];
+    int priority1[GIC_INTERNAL][NCPU];
+    int priority2[GIC_MAXIRQ - GIC_INTERNAL];
     int last_active[GIC_MAXIRQ][NCPU];
 
     int priority_mask[NCPU];
@@ -174,7 +177,7 @@ 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;
+    irq += GIC_INTERNAL;
     if (level == GIC_TEST_LEVEL(irq, ALL_CPU_MASK))
         return;
 
@@ -316,7 +319,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
         if (irq >= s->num_irq)
             goto bad_reg;
         res = 0;
-        mask = (irq < 32) ?  cm : ALL_CPU_MASK;
+        mask = (irq < GIC_INTERNAL) ?  cm : ALL_CPU_MASK;
         for (i = 0; i < 8; i++) {
             if (GIC_TEST_PENDING(irq + i, mask)) {
                 res |= (1 << i);
@@ -328,7 +331,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset)
         if (irq >= s->num_irq)
             goto bad_reg;
         res = 0;
-        mask = (irq < 32) ?  cm : ALL_CPU_MASK;
+        mask = (irq < GIC_INTERNAL) ?  cm : ALL_CPU_MASK;
         for (i = 0; i < 8; i++) {
             if (GIC_TEST_ACTIVE(irq + i, mask)) {
                 res |= (1 << i);
@@ -435,8 +438,8 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
           value = 0xff;
         for (i = 0; i < 8; i++) {
             if (value & (1 << i)) {
-                int mask = (irq < 32) ? (1 << cpu) : GIC_TARGET(irq);
-                int cm = (irq < 32) ? (1 << cpu) : ALL_CPU_MASK;
+                int mask = (irq < GIC_INTERNAL) ? (1 << cpu) : GIC_TARGET(irq);
+                int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
 
                 if (!GIC_TEST_ENABLED(irq + i, cm)) {
                     DPRINTF("Enabled IRQ %d\n", irq + i);
@@ -460,7 +463,7 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
           value = 0;
         for (i = 0; i < 8; i++) {
             if (value & (1 << i)) {
-                int cm = (irq < 32) ? (1 << cpu) : ALL_CPU_MASK;
+                int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
 
                 if (GIC_TEST_ENABLED(irq + i, cm)) {
                     DPRINTF("Disabled IRQ %d\n", irq + i);
@@ -502,10 +505,10 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
         irq = (offset - 0x400) + GIC_BASE_IRQ;
         if (irq >= s->num_irq)
             goto bad_reg;
-        if (irq < 32) {
+        if (irq < GIC_INTERNAL) {
             s->priority1[irq][cpu] = value;
         } else {
-            s->priority2[irq - 32] = value;
+            s->priority2[irq - GIC_INTERNAL] = value;
         }
 #ifndef NVIC
     } else if (offset < 0xc00) {
@@ -515,7 +518,7 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
             goto bad_reg;
         if (irq < 29)
             value = 0;
-        else if (irq < 32)
+        else if (irq < GIC_INTERNAL)
             value = ALL_CPU_MASK;
         s->irq_target[irq] = value & ALL_CPU_MASK;
     } else if (offset < 0xf00) {
@@ -523,7 +526,7 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset,
         irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ;
         if (irq >= s->num_irq)
             goto bad_reg;
-        if (irq < 32)
+        if (irq < GIC_INTERNAL)
             value |= 0xaa;
         for (i = 0; i < 4; i++) {
             if (value & (1 << (i * 2))) {
@@ -736,7 +739,7 @@ static void gic_save(QEMUFile *f, void *opaque)
     qemu_put_be32(f, s->enabled);
     for (i = 0; i < NUM_CPU(s); i++) {
         qemu_put_be32(f, s->cpu_enabled[i]);
-        for (j = 0; j < 32; j++)
+        for (j = 0; j < GIC_INTERNAL; j++)
             qemu_put_be32(f, s->priority1[j][i]);
         for (j = 0; j < s->num_irq; j++)
             qemu_put_be32(f, s->last_active[j][i]);
@@ -745,7 +748,7 @@ static void gic_save(QEMUFile *f, void *opaque)
         qemu_put_be32(f, s->running_priority[i]);
         qemu_put_be32(f, s->current_pending[i]);
     }
-    for (i = 0; i < s->num_irq - 32; i++) {
+    for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) {
         qemu_put_be32(f, s->priority2[i]);
     }
     for (i = 0; i < s->num_irq; i++) {
@@ -773,7 +776,7 @@ static int gic_load(QEMUFile *f, void *opaque, int version_id)
     s->enabled = qemu_get_be32(f);
     for (i = 0; i < NUM_CPU(s); i++) {
         s->cpu_enabled[i] = qemu_get_be32(f);
-        for (j = 0; j < 32; j++)
+        for (j = 0; j < GIC_INTERNAL; j++)
             s->priority1[j][i] = qemu_get_be32(f);
         for (j = 0; j < s->num_irq; j++)
             s->last_active[j][i] = qemu_get_be32(f);
@@ -782,7 +785,7 @@ static int gic_load(QEMUFile *f, void *opaque, int version_id)
         s->running_priority[i] = qemu_get_be32(f);
         s->current_pending[i] = qemu_get_be32(f);
     }
-    for (i = 0; i < s->num_irq - 32; i++) {
+    for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) {
         s->priority2[i] = qemu_get_be32(f);
     }
     for (i = 0; i < s->num_irq; i++) {
@@ -816,7 +819,7 @@ static void gic_init(gic_state *s, int num_irq)
         hw_error("requested %u interrupt lines exceeds GIC maximum %d\n",
                  num_irq, GIC_MAXIRQ);
     }
-    qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, s->num_irq - 32);
+    qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, s->num_irq - GIC_INTERNAL);
     for (i = 0; i < NUM_CPU(s); i++) {
         sysbus_init_irq(&s->busdev, &s->parent_irq[i]);
     }
#14 lore raw

Re: [Qemu-devel] [PATCH 1/2] arm: clean up GIC constants.

hw/arm_gic.c typedef struct gic_irq_state · line 80

Quoted 11 lines · Rusty Russell

Interrupts numbers 0-31 are private to the processor interface, 32-1019 are general interrups.  Add GIC_INTERNAL and substitute everywhere.

hw/arm_gic.c +3 -2
@@ -73,8 +75,9 @@ typedef struct gic_irq_state
  #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])
+#define GIC_GET_PRIORITY(irq, cpu) (((irq) < GIC_INTERNAL) ?           \
+                                   s->priority1[irq][cpu] :            \
+                                   s->priority2[(irq) - GIC_INTERNAL])

Hardcoded tabs are against coding style. In the interests of not taking another round for what's really not a very significant patch, I've fixed this (and the typo in the commit message) and put the fixed result into arm-devs.next.

(http://git.linaro.org/gitweb?p=people/pmaydell/qemu-arm.git;a=shortlog;h=refs/heads/arm-devs.next)

-- PMM

#15 lore raw

PATCH 2/2 arm: make sure that number of irqs can be represented in GICD_TYPER.

In reply to Peter Maydell

We currently assume that the number of interrupts (ITLinesNumber in the architecture reference manual) is divisible by 32, since we present it to the guest when it reads GICD_TYPER (in gic_dist_readb()) as (N / 32) - 1.

---
 hw/arm_gic.c |    9 +++++++++
 1 files changed, 9 insertions(+), 0 deletions(-)
hw/arm_gic.c +9 -0
index 4b1b486..a19212e 100644
@@ -819,6 +819,15 @@ static void gic_init(gic_state *s, int num_irq)
         hw_error("requested %u interrupt lines exceeds GIC maximum %d\n",
                  num_irq, GIC_MAXIRQ);
     }
+    /* ITLinesNumber is represented as (N / 32) - 1 (see
+     * gic_dist_readb) so this is an implementation imposed
+     * restriction, not an architectural one:
+     */
+    if (s->num_irq < 32 || (s->num_irq % 32)) {
+        hw_error("%d interrupt lines unsupported: not divisible by 32\n",
+                 num_irq);
+    }
+
     qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, s->num_irq - GIC_INTERNAL);
     for (i = 0; i < NUM_CPU(s); i++) {
         sysbus_init_irq(&s->busdev, &s->parent_irq[i]);
author Reviewed-by on v1 2/2
#16 lore raw

Re: [Qemu-devel] [PATCH 2/2] arm: make sure that number of irqs can be represented in GICD_TYPER.

Quoted 6 lines · Rusty Russell

We currently assume that the number of interrupts (ITLinesNumber in the architecture reference manual) is divisible by 32, since we present it to the guest when it reads GICD_TYPER (in gic_dist_readb()) as (N / 32) - 1.

and put into arm-devs.next.

-- PMM

#17 lore raw

PATCH 3/12 hw/arm_boot.c: Make SMP boards specify address to poll in bootup loop

In reply to Peter Maydell

From: Evgeny Voevodin <[email protected]>

The secondary CPU bootloader in arm_boot.c holds secondary CPUs in a pen until the primary CPU releases them. Make boards specify the address to be polled to determine whether to leave the pen (it was previously hardcoded to 0x10000030, which is a Versatile Express/ Realview specific system register address).

---
 hw/arm-misc.h |    1 +
 hw/arm_boot.c |   18 ++++++++++--------
 hw/realview.c |    2 ++
 hw/vexpress.c |    2 ++
 4 files changed, 15 insertions(+), 8 deletions(-)
hw/arm-misc.h +1 -0
index af403a1..6e8ae6b 100644
@@ -31,6 +31,7 @@ struct arm_boot_info {
     const char *initrd_filename;
     target_phys_addr_t loader_start;
     target_phys_addr_t smp_loader_start;
+    target_phys_addr_t smp_bootreg_addr;
     target_phys_addr_t smp_priv_base;
     int nb_cpus;
     int board_id;
hw/arm_boot.c +10 -8
index 215d5de..bf509a8 100644
@@ -31,17 +31,17 @@ static uint32_t bootloader[] = {
 /* Entry point for secondary CPUs.  Enable interrupt controller and
    Issue WFI until start address is written to system controller.  */
 static uint32_t smpboot[] = {
-  0xe59f0020, /* ldr     r0, privbase */
-  0xe3a01001, /* mov     r1, #1 */
-  0xe5801100, /* str     r1, [r0, #0x100] */
-  0xe3a00201, /* mov     r0, #0x10000000 */
-  0xe3800030, /* orr     r0, #0x30 */
+  0xe59f201c, /* ldr r2, privbase */
+  0xe59f001c, /* ldr r0, startaddr */
+  0xe3a01001, /* mov r1, #1 */
+  0xe5821100, /* str r1, [r2, #256] */
   0xe320f003, /* wfi */
   0xe5901000, /* ldr     r1, [r0] */
   0xe1110001, /* tst     r1, r1 */
   0x0afffffb, /* beq     <wfi> */
   0xe12fff11, /* bx      r1 */
-  0 /* privbase: Private memory region base address.  */
+  0,          /* privbase: Private memory region base address.  */
+  0           /* bootreg: Boot register address is held here */
 };
 
 #define WRITE_WORD(p, value) do { \
@@ -197,6 +197,7 @@ static void do_cpu_reset(void *opaque)
                                     info->loader_start);
                 }
             } else {
+                stl_phys_notdirty(info->smp_bootreg_addr, 0);
                 env->regs[15] = info->smp_loader_start;
             }
         }
@@ -272,8 +273,9 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info *info)
         rom_add_blob_fixed("bootloader", bootloader, sizeof(bootloader),
                            info->loader_start);
         if (info->nb_cpus > 1) {
-            smpboot[10] = info->smp_priv_base;
-            for (n = 0; n < sizeof(smpboot) / 4; n++) {
+            smpboot[ARRAY_SIZE(smpboot) - 1] = info->smp_bootreg_addr;
+            smpboot[ARRAY_SIZE(smpboot) - 2] = info->smp_priv_base;
+            for (n = 0; n < ARRAY_SIZE(smpboot); n++) {
                 smpboot[n] = tswap32(smpboot[n]);
             }
             rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot),
hw/realview.c +2 -0
index e52babc..d2fde44 100644
@@ -21,6 +21,7 @@
 #include "exec-memory.h"
 
 #define SMP_BOOT_ADDR 0xe0000000
+#define SMP_BOOTREG_ADDR 0x10000030
 
 typedef struct {
     SysBusDevice busdev;
@@ -96,6 +97,7 @@ static void realview_register_devices(void)
 
 static struct arm_boot_info realview_binfo = {
     .smp_loader_start = SMP_BOOT_ADDR,
+    .smp_bootreg_addr = SMP_BOOTREG_ADDR,
 };
 
 /* The following two lists must be consistent.  */
hw/vexpress.c +2 -0
index 613be65..64fab45 100644
@@ -31,11 +31,13 @@
 #include "exec-memory.h"
 
 #define SMP_BOOT_ADDR 0xe0000000
+#define SMP_BOOTREG_ADDR 0x10000030
 
 #define VEXPRESS_BOARD_ID 0x8e0
 
 static struct arm_boot_info vexpress_binfo = {
     .smp_loader_start = SMP_BOOT_ADDR,
+    .smp_bootreg_addr = SMP_BOOTREG_ADDR,
 };
 
 static void vexpress_a9_init(ram_addr_t ram_size,
Signature · 1 line
1.7.1
#18 lore raw

Re: [Qemu-devel] [Android-virt] [PATCH 03/12] hw/arm_boot.c: Make SMP boards specify address to poll in bootup loop

Quoted 7 lines · Peter Maydell

From: Evgeny Voevodin <[email protected]>

The secondary CPU bootloader in arm_boot.c holds secondary CPUs in a pen until the primary CPU releases them. Make boards specify the address to be polled to determine whether to leave the pen (it was previously hardcoded to 0x10000030, which is a Versatile Express/ Realview specific system register address).

Is smp_boot implementing the same logic as hw/ppce500_spin.c? It looks like the normal u-boot way of waiting for a magic address to be written with boot info. What I don't understand is the WFI. How can you wait for an interrupt if the trigger is a memory write? Or are you actually getting IPIs?

Alex

#19 lore raw

Re: [Qemu-devel] [Android-virt] [PATCH 03/12] hw/arm_boot.c: Make SMP boards specify address to poll in bootup loop

Quoted 11 lines · Alexander Graf
Quoted 7 lines · Peter Maydell

From: Evgeny Voevodin <[email protected]>

The secondary CPU bootloader in arm_boot.c holds secondary CPUs in a pen until the primary CPU releases them. Make boards specify the address to be polled to determine whether to leave the pen (it was previously hardcoded to 0x10000030, which is a Versatile Express/ Realview specific system register address).

Quoted 5 lines

Is smp_boot implementing the same logic as hw/ppce500_spin.c? It looks like the normal u-boot way of waiting for a magic address to be written with boot info. What I don't understand is the WFI. How can you wait for an interrupt if the trigger is a memory write? Or are you actually getting IPIs?

Basically this code is implementing the boot-rom half of what is technically a platform-specific contract[*] between the boot rom and the Linux kernel. The kernel end has to
(a) write the secondary CPU's entry point into the register being polled and (b) send the CPU a softirq (an IPI, in other words). The WFI basically allows h/w to avoid running 3 cores at 100% when we're booting a uniprocessor OS. So the arm_boot end has to (a) enable the CPU's interrupt controller, (b) wait for interrupt, (c) get the entry point and jump to it.

[*] realview, vexpress, highbank and exynos4 all do something that's sufficiently similar that we can handle them all by parameterising the secondary boot code a bit. omap is kinda different but then we don't support -kernel with omap anyway.

-- PMM

#20 lore raw

Re: [Qemu-devel] [Android-virt] [PATCH 03/12] hw/arm_boot.c: Make SMP boards specify address to poll in bootup loop

Quoted 29 lines · Peter Maydell
Quoted 11 lines · Alexander Graf
Quoted 7 lines · Peter Maydell

From: Evgeny Voevodin <[email protected]>

The secondary CPU bootloader in arm_boot.c holds secondary CPUs in a pen until the primary CPU releases them. Make boards specify the address to be polled to determine whether to leave the pen (it was previously hardcoded to 0x10000030, which is a Versatile Express/ Realview specific system register address).

Quoted 5 lines

Is smp_boot implementing the same logic as hw/ppce500_spin.c? It looks like the normal u-boot way of waiting for a magic address to be written with boot info. What I don't understand is the WFI. How can you wait for an interrupt if the trigger is a memory write? Or are you actually getting IPIs?

Basically this code is implementing the boot-rom half of what is technically a platform-specific contract[*] between the boot rom and the Linux kernel. The kernel end has to
(a) write the secondary CPU's entry point into the register being polled and (b) send the CPU a softirq (an IPI, in other words). The WFI basically allows h/w to avoid running 3 cores at 100% when we're booting a uniprocessor OS. So the arm_boot end has to (a) enable the CPU's interrupt controller, (b) wait for interrupt, (c) get the entry point and jump to it.

Quoted 4 lines

[*] realview, vexpress, highbank and exynos4 all do something that's sufficiently similar that we can handle them all by parameterising the secondary boot code a bit. omap is kinda different but then we don't support -kernel with omap anyway.

In the last sentence you mean the SMP case, right?

Cheers

#21 lore raw

Re: [Qemu-devel] [Android-virt] [PATCH 03/12] hw/arm_boot.c: Make SMP boards specify address to poll in bootup loop

Quoted 7 lines · andrzej zaborowski
Quoted 4 lines · Peter Maydell

[*] realview, vexpress, highbank and exynos4 all do something that's sufficiently similar that we can handle them all by parameterising the secondary boot code a bit. omap is kinda different but then we don't support -kernel with omap anyway.

In the last sentence you mean the SMP case, right?

Hmm, I think I was a bit confused there. So (a) you're right that the non-SMP case doesn't have a platform dependency so it will work on anything that wants to call arm_load_kernel (including omap boards). (b) omap1-3 are all single core anyhow, and I don't know of anybody who's proposing to write an omap4 model right now
(c) the currently-out-of-tree omap3 models don't call arm_load_kernel but instead have a model of the omap3 boot rom and just boot from SD card images like the real hardware. [It's (c) that I was vaguely thinking of when I wrote that remark.]

-- PMM

#22 lore raw

Re: [Qemu-devel] [PATCH 03/12] hw/arm_boot.c: Make SMP boards specify address to poll in bootup loop

In reply to Peter Maydell
Quoted 7 lines · Peter Maydell

From: Evgeny Voevodin <[email protected]>

The secondary CPU bootloader in arm_boot.c holds secondary CPUs in a pen until the primary CPU releases them. Make boards specify the address to be polled to determine whether to leave the pen (it was previously hardcoded to 0x10000030, which is a Versatile Express/ Realview specific system register address).

I applied this patch out of the series to reduce cross dependency, thanks.

Cheers

#23 lore raw

PATCH 4/12 hw/vexpress.c: Make motherboard peripheral memory map table-driven

In reply to Peter Maydell

Pull the addresses used for mapping motherboard peripherals into memory out into a table. This will allow us to simply provide a second table to implement the "Cortex-A Series" memory map used by the A15 variant of Versatile Express, as well as the current "Legacy" map used by A9.

---
 hw/vexpress.c |  137 +++++++++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 103 insertions(+), 34 deletions(-)
hw/vexpress.c +103 -34
index 64fab45..8c4d3b3 100644
@@ -31,13 +31,80 @@
 #include "exec-memory.h"
 
 #define SMP_BOOT_ADDR 0xe0000000
-#define SMP_BOOTREG_ADDR 0x10000030
 
 #define VEXPRESS_BOARD_ID 0x8e0
 
 static struct arm_boot_info vexpress_binfo = {
     .smp_loader_start = SMP_BOOT_ADDR,
-    .smp_bootreg_addr = SMP_BOOTREG_ADDR,
+};
+
+/* Address maps for peripherals:
+ * the Versatile Express motherboard has two possible maps,
+ * the "legacy" one (used for A9) and the "Cortex-A Series"
+ * map (used for newer cores).
+ * Individual daughterboards can also have different maps for
+ * their peripherals.
+ */
+
+enum {
+    VE_SYSREGS,
+    VE_SP810,
+    VE_SERIALPCI,
+    VE_PL041,
+    VE_MMCI,
+    VE_KMI0,
+    VE_KMI1,
+    VE_UART0,
+    VE_UART1,
+    VE_UART2,
+    VE_UART3,
+    VE_WDT,
+    VE_TIMER01,
+    VE_TIMER23,
+    VE_SERIALDVI,
+    VE_RTC,
+    VE_COMPACTFLASH,
+    VE_CLCD,
+    VE_NORFLASH0,
+    VE_NORFLASH0ALIAS,
+    VE_NORFLASH1,
+    VE_SRAM,
+    VE_VIDEORAM,
+    VE_ETHERNET,
+    VE_USB,
+    VE_DAPROM,
+};
+
+static target_phys_addr_t motherboard_legacy_map[] = {
+    /* CS7: 0x10000000 .. 0x10020000 */
+    [VE_SYSREGS] = 0x10000000,
+    [VE_SP810] = 0x10001000,
+    [VE_SERIALPCI] = 0x10002000,
+    [VE_PL041] = 0x10004000,
+    [VE_MMCI] = 0x10005000,
+    [VE_KMI0] = 0x10006000,
+    [VE_KMI1] = 0x10007000,
+    [VE_UART0] = 0x10009000,
+    [VE_UART1] = 0x1000a000,
+    [VE_UART2] = 0x1000b000,
+    [VE_UART3] = 0x1000c000,
+    [VE_WDT] = 0x1000f000,
+    [VE_TIMER01] = 0x10011000,
+    [VE_TIMER23] = 0x10012000,
+    [VE_SERIALDVI] = 0x10016000,
+    [VE_RTC] = 0x10017000,
+    [VE_COMPACTFLASH] = 0x1001a000,
+    [VE_CLCD] = 0x1001f000,
+    /* CS0: 0x40000000 .. 0x44000000 */
+    [VE_NORFLASH0] = 0x40000000,
+    /* CS1: 0x44000000 .. 0x48000000 */
+    [VE_NORFLASH1] = 0x44000000,
+    /* CS2: 0x48000000 .. 0x4a000000 */
+    [VE_SRAM] = 0x48000000,
+    /* CS3: 0x4c000000 .. 0x50000000 */
+    [VE_VIDEORAM] = 0x4c000000,
+    [VE_ETHERNET] = 0x4e000000,
+    [VE_USB] = 0x4f000000,
 };
 
 static void vexpress_a9_init(ram_addr_t ram_size,
@@ -61,6 +128,7 @@ static void vexpress_a9_init(ram_addr_t ram_size,
     uint32_t proc_id;
     uint32_t sys_id;
     ram_addr_t low_ram_size, vram_size, sram_size;
+    target_phys_addr_t *map = motherboard_legacy_map;
 
     if (!cpu_model) {
         cpu_model = "cortex-a9";
@@ -116,53 +184,53 @@ static void vexpress_a9_init(ram_addr_t ram_size,
         pic[n] = qdev_get_gpio_in(dev, n);
     }
 
-    /* Motherboard peripherals CS7 : 0x10000000 .. 0x10020000 */
+    /* Motherboard peripherals: the wiring is the same but the
+     * addresses vary between the legacy and A-Series memory maps.
+     */
+
     sys_id = 0x1190f500;
     proc_id = 0x0c000191;
 
-    /* 0x10000000 System registers */
     sysctl = qdev_create(NULL, "realview_sysctl");
     qdev_prop_set_uint32(sysctl, "sys_id", sys_id);
     qdev_init_nofail(sysctl);
     qdev_prop_set_uint32(sysctl, "proc_id", proc_id);
-    sysbus_mmio_map(sysbus_from_qdev(sysctl), 0, 0x10000000);
+    sysbus_mmio_map(sysbus_from_qdev(sysctl), 0, map[VE_SYSREGS]);
+
+    /* VE_SP810: not modelled */
+    /* VE_SERIALPCI: not modelled */
 
-    /* 0x10001000 SP810 system control */
-    /* 0x10002000 serial bus PCI */
-    /* 0x10004000 PL041 audio */
     pl041 = qdev_create(NULL, "pl041");
     qdev_prop_set_uint32(pl041, "nc_fifo_depth", 512);
     qdev_init_nofail(pl041);
-    sysbus_mmio_map(sysbus_from_qdev(pl041), 0, 0x10004000);
+    sysbus_mmio_map(sysbus_from_qdev(pl041), 0, map[VE_PL041]);
     sysbus_connect_irq(sysbus_from_qdev(pl041), 0, pic[11]);
 
-    dev = sysbus_create_varargs("pl181", 0x10005000, pic[9], pic[10], NULL);
+    dev = sysbus_create_varargs("pl181", map[VE_MMCI], pic[9], pic[10], NULL);
     /* Wire up MMC card detect and read-only signals */
     qdev_connect_gpio_out(dev, 0,
                           qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_WPROT));
     qdev_connect_gpio_out(dev, 1,
                           qdev_get_gpio_in(sysctl, ARM_SYSCTL_GPIO_MMC_CARDIN));
 
-    sysbus_create_simple("pl050_keyboard", 0x10006000, pic[12]);
-    sysbus_create_simple("pl050_mouse", 0x10007000, pic[13]);
-
-    sysbus_create_simple("pl011", 0x10009000, pic[5]);
-    sysbus_create_simple("pl011", 0x1000a000, pic[6]);
-    sysbus_create_simple("pl011", 0x1000b000, pic[7]);
-    sysbus_create_simple("pl011", 0x1000c000, pic[8]);
+    sysbus_create_simple("pl050_keyboard", map[VE_KMI0], pic[12]);
+    sysbus_create_simple("pl050_mouse", map[VE_KMI1], pic[13]);
 
-    /* 0x1000f000 SP805 WDT */
+    sysbus_create_simple("pl011", map[VE_UART0], pic[5]);
+    sysbus_create_simple("pl011", map[VE_UART1], pic[6]);
+    sysbus_create_simple("pl011", map[VE_UART2], pic[7]);
+    sysbus_create_simple("pl011", map[VE_UART3], pic[8]);
 
-    sysbus_create_simple("sp804", 0x10011000, pic[2]);
-    sysbus_create_simple("sp804", 0x10012000, pic[3]);
+    sysbus_create_simple("sp804", map[VE_TIMER01], pic[2]);
+    sysbus_create_simple("sp804", map[VE_TIMER23], pic[3]);
 
-    /* 0x10016000 Serial Bus DVI */
+    /* VE_SERIALDVI: not modelled */
 
-    sysbus_create_simple("pl031", 0x10017000, pic[4]); /* RTC */
+    sysbus_create_simple("pl031", map[VE_RTC], pic[4]); /* RTC */
 
-    /* 0x1001a000 Compact Flash */
+    /* VE_COMPACTFLASH: not modelled */
 
-    /* 0x1001f000 PL111 CLCD (motherboard) */
+    /* VE_CLCD: not modelled (we use the daughterboard CLCD only) */
 
     /* Daughterboard peripherals : 0x10020000 .. 0x20000000 */
 
@@ -184,28 +252,28 @@ static void vexpress_a9_init(ram_addr_t ram_size,
     /* 0x1e00a000 PL310 L2 Cache Controller */
     sysbus_create_varargs("l2x0", 0x1e00a000, NULL);
 
-    /* CS0: NOR0 flash          : 0x40000000 .. 0x44000000 */
-    /* CS4: NOR1 flash          : 0x44000000 .. 0x48000000 */
-    /* CS2: SRAM                : 0x48000000 .. 0x4a000000 */
+    /* VE_NORFLASH0: not modelled */
+    /* VE_NORFLASH0ALIAS: not modelled */
+    /* VE_NORFLASH1: not modelled */
+
     sram_size = 0x2000000;
     memory_region_init_ram(sram, "vexpress.sram", sram_size);
     vmstate_register_ram_global(sram);
-    memory_region_add_subregion(sysmem, 0x48000000, sram);
-
-    /* CS3: USB, ethernet, VRAM : 0x4c000000 .. 0x50000000 */
+    memory_region_add_subregion(sysmem, map[VE_SRAM], sram);
 
-    /* 0x4c000000 Video RAM */
     vram_size = 0x800000;
     memory_region_init_ram(vram, "vexpress.vram", vram_size);
     vmstate_register_ram_global(vram);
-    memory_region_add_subregion(sysmem, 0x4c000000, vram);
+    memory_region_add_subregion(sysmem, map[VE_VIDEORAM], vram);
 
     /* 0x4e000000 LAN9118 Ethernet */
     if (nd_table[0].vlan) {
-        lan9118_init(&nd_table[0], 0x4e000000, pic[15]);
+        lan9118_init(&nd_table[0], map[VE_ETHERNET], pic[15]);
     }
 
-    /* 0x4f000000 ISP1761 USB */
+    /* VE_USB: not modelled */
+
+    /* VE_DAPROM: not modelled */
 
     /* ??? Hack to map an additional page of ram for the secondary CPU
        startup code.  I guess this works on real hardware because the
@@ -222,6 +290,7 @@ static void vexpress_a9_init(ram_addr_t ram_size,
     vexpress_binfo.nb_cpus = smp_cpus;
     vexpress_binfo.board_id = VEXPRESS_BOARD_ID;
     vexpress_binfo.loader_start = 0x60000000;
+    vexpress_binfo.smp_bootreg_addr = map[VE_SYSREGS] + 0x30;
     arm_load_kernel(first_cpu, &vexpress_binfo);
 }
 
Signature · 1 line
1.7.1
#24 lore raw

PATCH 5/12 hw/vexpress.c: Move secondary CPU boot code to SRAM

In reply to Peter Maydell

On real Versatile Express hardware, the boot ROM puts the secondary CPU bootcode/holding pen in SRAM. We can therefore rely on Linux not trashing this memory until secondary CPUs have booted up, and can put our QEMU-specific pen code in the same place. This allows us to drop the odd "hack" RAM page we were using before.

---
 hw/vexpress.c |   16 ++--------------
 1 files changed, 2 insertions(+), 14 deletions(-)
hw/vexpress.c +2 -14
index 8c4d3b3..99a9690 100644
@@ -30,13 +30,9 @@
 #include "boards.h"
 #include "exec-memory.h"
 
-#define SMP_BOOT_ADDR 0xe0000000
-
 #define VEXPRESS_BOARD_ID 0x8e0
 
-static struct arm_boot_info vexpress_binfo = {
-    .smp_loader_start = SMP_BOOT_ADDR,
-};
+static struct arm_boot_info vexpress_binfo;
 
 /* Address maps for peripherals:
  * the Versatile Express motherboard has two possible maps,
@@ -118,7 +114,6 @@ static void vexpress_a9_init(ram_addr_t ram_size,
     MemoryRegion *lowram = g_new(MemoryRegion, 1);
     MemoryRegion *vram = g_new(MemoryRegion, 1);
     MemoryRegion *sram = g_new(MemoryRegion, 1);
-    MemoryRegion *hackram = g_new(MemoryRegion, 1);
     DeviceState *dev, *sysctl, *pl041;
     SysBusDevice *busdev;
     qemu_irq *irqp;
@@ -275,14 +270,6 @@ static void vexpress_a9_init(ram_addr_t ram_size,
 
     /* VE_DAPROM: not modelled */
 
-    /* ??? 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.  */
-    memory_region_init_ram(hackram, "vexpress.hack", 0x1000);
-    vmstate_register_ram_global(hackram);
-    memory_region_add_subregion(sysmem, SMP_BOOT_ADDR, hackram);
-
     vexpress_binfo.ram_size = ram_size;
     vexpress_binfo.kernel_filename = kernel_filename;
     vexpress_binfo.kernel_cmdline = kernel_cmdline;
@@ -290,6 +277,7 @@ static void vexpress_a9_init(ram_addr_t ram_size,
     vexpress_binfo.nb_cpus = smp_cpus;
     vexpress_binfo.board_id = VEXPRESS_BOARD_ID;
     vexpress_binfo.loader_start = 0x60000000;
+    vexpress_binfo.smp_loader_start = map[VE_SRAM];
     vexpress_binfo.smp_bootreg_addr = map[VE_SYSREGS] + 0x30;
     arm_load_kernel(first_cpu, &vexpress_binfo);
 }
Signature · 1 line
1.7.1
#25 lore raw

PATCH 6/12 hw/vexpress.c: Factor out daughterboard-specific initialization

In reply to Peter Maydell

Factor out daughterboard specifics into a data structure and daughterboard initialization function, in preparation for adding vexpress-a15 support.

---
 hw/vexpress.c |  118 ++++++++++++++++++++++++++++++++++++++++-----------------
 1 files changed, 83 insertions(+), 35 deletions(-)
hw/vexpress.c +83 -35
index 99a9690..9033b50 100644
@@ -103,32 +103,43 @@ static target_phys_addr_t motherboard_legacy_map[] = {
     [VE_USB] = 0x4f000000,
 };
 
-static void vexpress_a9_init(ram_addr_t ram_size,
-                     const char *boot_device,
-                     const char *kernel_filename, const char *kernel_cmdline,
-                     const char *initrd_filename, const char *cpu_model)
+/* Structure defining the peculiarities of a specific daughterboard */
+
+typedef struct VEDBoardInfo VEDBoardInfo;
+
+typedef void DBoardInitFn(const VEDBoardInfo *daughterboard,
+                          ram_addr_t ram_size,
+                          const char *cpu_model,
+                          qemu_irq *pic, uint32_t *proc_id);
+
+struct VEDBoardInfo {
+    const target_phys_addr_t *motherboard_map;
+    const target_phys_addr_t loader_start;
+    DBoardInitFn *init;
+};
+
+static void a9_daughterboard_init(const VEDBoardInfo *daughterboard,
+                                  ram_addr_t ram_size,
+                                  const char *cpu_model,
+                                  qemu_irq *pic, uint32_t *proc_id)
 {
     CPUState *env = NULL;
     MemoryRegion *sysmem = get_system_memory();
     MemoryRegion *ram = g_new(MemoryRegion, 1);
     MemoryRegion *lowram = g_new(MemoryRegion, 1);
-    MemoryRegion *vram = g_new(MemoryRegion, 1);
-    MemoryRegion *sram = g_new(MemoryRegion, 1);
-    DeviceState *dev, *sysctl, *pl041;
+    DeviceState *dev;
     SysBusDevice *busdev;
     qemu_irq *irqp;
-    qemu_irq pic[64];
     int n;
     qemu_irq cpu_irq[4];
-    uint32_t proc_id;
-    uint32_t sys_id;
-    ram_addr_t low_ram_size, vram_size, sram_size;
-    target_phys_addr_t *map = motherboard_legacy_map;
+    ram_addr_t low_ram_size;
 
     if (!cpu_model) {
         cpu_model = "cortex-a9";
     }
 
+    *proc_id = 0x0c000191;
+
     for (n = 0; n < smp_cpus; n++) {
         env = cpu_init(cpu_model);
         if (!env) {
@@ -141,7 +152,7 @@ static void vexpress_a9_init(ram_addr_t ram_size,
 
     if (ram_size > 0x40000000) {
         /* 1GB is the maximum the address space permits */
-        fprintf(stderr, "vexpress: cannot model more than 1GB RAM\n");
+        fprintf(stderr, "vexpress-a9: cannot model more than 1GB RAM\n");
         exit(1);
     }
 
@@ -179,12 +190,58 @@ static void vexpress_a9_init(ram_addr_t ram_size,
         pic[n] = qdev_get_gpio_in(dev, n);
     }
 
+    /* Daughterboard peripherals : 0x10020000 .. 0x20000000 */
+
+    /* 0x10020000 PL111 CLCD (daughterboard) */
+    sysbus_create_simple("pl111", 0x10020000, pic[44]);
+
+    /* 0x10060000 AXI RAM */
+    /* 0x100e0000 PL341 Dynamic Memory Controller */
+    /* 0x100e1000 PL354 Static Memory Controller */
+    /* 0x100e2000 System Configuration Controller */
+
+    sysbus_create_simple("sp804", 0x100e4000, pic[48]);
+    /* 0x100e5000 SP805 Watchdog module */
+    /* 0x100e6000 BP147 TrustZone Protection Controller */
+    /* 0x100e9000 PL301 'Fast' AXI matrix */
+    /* 0x100ea000 PL301 'Slow' AXI matrix */
+    /* 0x100ec000 TrustZone Address Space Controller */
+    /* 0x10200000 CoreSight debug APB */
+    /* 0x1e00a000 PL310 L2 Cache Controller */
+    sysbus_create_varargs("l2x0", 0x1e00a000, NULL);
+}
+
+static const VEDBoardInfo a9_daughterboard = {
+    .motherboard_map = motherboard_legacy_map,
+    .loader_start = 0x60000000,
+    .init = a9_daughterboard_init,
+};
+
+static void vexpress_common_init(const VEDBoardInfo *daughterboard,
+                                 ram_addr_t ram_size,
+                                 const char *boot_device,
+                                 const char *kernel_filename,
+                                 const char *kernel_cmdline,
+                                 const char *initrd_filename,
+                                 const char *cpu_model)
+{
+    DeviceState *dev, *sysctl, *pl041;
+    qemu_irq pic[64];
+    uint32_t proc_id;
+    uint32_t sys_id;
+    ram_addr_t vram_size, sram_size;
+    MemoryRegion *sysmem = get_system_memory();
+    MemoryRegion *vram = g_new(MemoryRegion, 1);
+    MemoryRegion *sram = g_new(MemoryRegion, 1);
+    const target_phys_addr_t *map = daughterboard->motherboard_map;
+
+    daughterboard->init(daughterboard, ram_size, cpu_model, pic, &proc_id);
+
     /* Motherboard peripherals: the wiring is the same but the
      * addresses vary between the legacy and A-Series memory maps.
      */
 
     sys_id = 0x1190f500;
-    proc_id = 0x0c000191;
 
     sysctl = qdev_create(NULL, "realview_sysctl");
     qdev_prop_set_uint32(sysctl, "sys_id", sys_id);
@@ -227,26 +284,6 @@ static void vexpress_a9_init(ram_addr_t ram_size,
 
     /* VE_CLCD: not modelled (we use the daughterboard CLCD only) */
 
-    /* Daughterboard peripherals : 0x10020000 .. 0x20000000 */
-
-    /* 0x10020000 PL111 CLCD (daughterboard) */
-    sysbus_create_simple("pl111", 0x10020000, pic[44]);
-
-    /* 0x10060000 AXI RAM */
-    /* 0x100e0000 PL341 Dynamic Memory Controller */
-    /* 0x100e1000 PL354 Static Memory Controller */
-    /* 0x100e2000 System Configuration Controller */
-
-    sysbus_create_simple("sp804", 0x100e4000, pic[48]);
-    /* 0x100e5000 SP805 Watchdog module */
-    /* 0x100e6000 BP147 TrustZone Protection Controller */
-    /* 0x100e9000 PL301 'Fast' AXI matrix */
-    /* 0x100ea000 PL301 'Slow' AXI matrix */
-    /* 0x100ec000 TrustZone Address Space Controller */
-    /* 0x10200000 CoreSight debug APB */
-    /* 0x1e00a000 PL310 L2 Cache Controller */
-    sysbus_create_varargs("l2x0", 0x1e00a000, NULL);
-
     /* VE_NORFLASH0: not modelled */
     /* VE_NORFLASH0ALIAS: not modelled */
     /* VE_NORFLASH1: not modelled */
@@ -276,12 +313,23 @@ static void vexpress_a9_init(ram_addr_t ram_size,
     vexpress_binfo.initrd_filename = initrd_filename;
     vexpress_binfo.nb_cpus = smp_cpus;
     vexpress_binfo.board_id = VEXPRESS_BOARD_ID;
-    vexpress_binfo.loader_start = 0x60000000;
+    vexpress_binfo.loader_start = daughterboard->loader_start;
     vexpress_binfo.smp_loader_start = map[VE_SRAM];
     vexpress_binfo.smp_bootreg_addr = map[VE_SYSREGS] + 0x30;
     arm_load_kernel(first_cpu, &vexpress_binfo);
 }
 
+static void vexpress_a9_init(ram_addr_t ram_size,
+                             const char *boot_device,
+                             const char *kernel_filename,
+                             const char *kernel_cmdline,
+                             const char *initrd_filename,
+                             const char *cpu_model)
+{
+    vexpress_common_init(&a9_daughterboard,
+                         ram_size, boot_device, kernel_filename,
+                         kernel_cmdline, initrd_filename, cpu_model);
+}
 
 static QEMUMachine vexpress_a9_machine = {
     .name = "vexpress-a9",
Signature · 1 line
1.7.1
#26 lore raw

PATCH 7/12 hw/vexpress.c: Instantiate the motherboard CLCD

In reply to Peter Maydell

Instantiate the CLCD on the vexpress motherboard as well as one on the daughterboard -- the A15 daughterboard does not have a CLCD and so relies on the motherboard one.

At the moment QEMU doesn't provide infrastructure for selecting which display device gets to actually show graphics -- the first one registered is it. Fortunately this works for the major use case (Linux): if the daughterboard has a CLCD it will come first and be used, otherwise we fall back to the motherboard CLCD. So we don't (currently) need to implement the control register which allows software to tell the mux which video output to pass through to the outside world.

---
 hw/vexpress.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
hw/vexpress.c +1 -1
index 9033b50..9860085 100644
@@ -282,7 +282,7 @@ static void vexpress_common_init(const VEDBoardInfo *daughterboard,
 
     /* VE_COMPACTFLASH: not modelled */
 
-    /* VE_CLCD: not modelled (we use the daughterboard CLCD only) */
+    sysbus_create_simple("pl111", map[VE_CLCD], pic[14]);
 
     /* VE_NORFLASH0: not modelled */
     /* VE_NORFLASH0ALIAS: not modelled */
Signature · 1 line
1.7.1
#27 lore raw

PATCH 8/12 hw/a15mpcore.c: Add Cortex-A15 private peripheral model

In reply to Peter Maydell

Add a model of the Cortex-A15 memory mapped private peripheral space. This is fairly simple because the only memory mapped bit of the A15 is the GIC.

Note that we don't currently model a VGIC and therefore don't map the VGIC related bits of the GIC.

---
 Makefile.target |    2 +-
 hw/a15mpcore.c  |   93 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 94 insertions(+), 1 deletions(-)
 create mode 100644 hw/a15mpcore.c
Makefile.target +1 -1
index 06d79b8..92f133e 100644
@@ -340,7 +340,7 @@ obj-arm-y += arm_boot.o pl011.o pl031.o pl050.o pl080.o pl110.o pl181.o pl190.o
 obj-arm-y += versatile_pci.o
 obj-arm-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o
 obj-arm-y += arm_l2x0.o
-obj-arm-y += arm_mptimer.o
+obj-arm-y += arm_mptimer.o a15mpcore.o
 obj-arm-y += armv7m.o armv7m_nvic.o stellaris.o pl022.o stellaris_enet.o
 obj-arm-y += pl061.o
 obj-arm-y += arm-semi.o
hw/a15mpcore.c new file +93 -0

93 lines not on this page · raw message

Signature · 1 line
1.7.1
#28 lore raw

PATCH 9/12 Add dummy implementation of generic timer cp15 registers

In reply to Peter Maydell

Add a dummy implementation of the cp15 registers for the generic timer (found in the Cortex-A15), just sufficient for Linux to decide that it can't use it. This requires at least CNTP_CTL and CNTFRQ to be implemented as RAZ/WI; we RAZ/WI all of c14.

---
 target-arm/cpu.h    |    1 +
 target-arm/helper.c |   12 ++++++++++--
 2 files changed, 11 insertions(+), 2 deletions(-)
target-arm/cpu.h +1 -0
index 26b4981..d5403ea 100644
@@ -381,6 +381,7 @@ enum arm_features {
     ARM_FEATURE_VAPA, /* cp15 VA to PA lookups */
     ARM_FEATURE_ARM_DIV, /* divide supported in ARM encoding */
     ARM_FEATURE_VFP4, /* VFPv4 (implies that NEON is v2) */
+    ARM_FEATURE_GENERICTIMER,
 };
 
 static inline int arm_feature(CPUARMState *env, int feature)
target-arm/helper.c +10 -2
index fa42c64..de4a50f 100644
@@ -1757,7 +1757,11 @@ void HELPER(set_cp15)(CPUState *env, uint32_t insn, uint32_t val)
             goto bad_reg;
         }
         break;
-    case 14: /* Reserved.  */
+    case 14: /* Generic timer */
+        if (arm_feature(env, ARM_FEATURE_GENERICTIMER)) {
+            /* Dummy implementation: RAZ/WI for all */
+            break;
+        }
         goto bad_reg;
     case 15: /* Implementation specific.  */
         if (arm_feature(env, ARM_FEATURE_XSCALE)) {
@@ -2123,7 +2127,11 @@ uint32_t HELPER(get_cp15)(CPUState *env, uint32_t insn)
         default:
             goto bad_reg;
         }
-    case 14: /* Reserved.  */
+    case 14: /* Generic timer */
+        if (arm_feature(env, ARM_FEATURE_GENERICTIMER)) {
+            /* Dummy implementation: RAZ/WI for all */
+            return 0;
+        }
         goto bad_reg;
     case 15: /* Implementation specific.  */
         if (arm_feature(env, ARM_FEATURE_XSCALE)) {
Signature · 1 line
1.7.1
#29 lore raw

PATCH 10/12 Add Cortex-A15 CPU definition

In reply to Peter Maydell

Add a definition of a Cortex-A15 CPU. Note that for the moment we do not implement any of:
* Large Physical Address Extensions (LPAE)
* Virtualization Extensions
* Generic Timer
* TrustZone (this is also true of our existing Cortex-A9 model, etc)

This CPU model is sufficient to boot a Linux kernel which has been compiled for an A15 without LPAE enabled.

---
 target-arm/cpu.h    |    1 +
 target-arm/helper.c |   56 ++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 52 insertions(+), 5 deletions(-)
target-arm/cpu.h +1 -0
index d5403ea..4b8d680 100644
@@ -433,6 +433,7 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
 #define ARM_CPUID_CORTEXA8    0x410fc080
 #define ARM_CPUID_CORTEXA9    0x410fc090
 #define ARM_CPUID_CORTEXM3    0x410fc231
+#define ARM_CPUID_CORTEXA15   0x412fc0f1
 #define ARM_CPUID_ANY         0xffffffff
 
 #if defined(CONFIG_USER_ONLY)
target-arm/helper.c +51 -5

94 lines not on this page · raw message

Signature · 1 line
1.7.1
#30 lore raw

Re: [Qemu-devel] [Android-virt] [PATCH 10/12] Add Cortex-A15 CPU definition

Ping. Unless anybody has any review comments on this or patch 9 (generic timer dummy implementation) I plan to put them into my target-arm.next queue and send out a pullreq later this week.

-- PMM

Quoted 139 lines · Peter Maydell

Add a definition of a Cortex-A15 CPU. Note that for the moment we do not implement any of:
 * Large Physical Address Extensions (LPAE)
 * Virtualization Extensions
 * Generic Timer
 * TrustZone (this is also true of our existing Cortex-A9 model, etc)

This CPU model is sufficient to boot a Linux kernel which has been compiled for an A15 without LPAE enabled.

---
 target-arm/cpu.h    |    1 +
 target-arm/helper.c |   56 ++++++++++++++++++++++++++++++++++++++++++++++----

 2 files changed, 52 insertions(+), 5 deletions(-)
target-arm/cpu.h +1 -0
index d5403ea..4b8d680 100644
@@ -433,6 +433,7 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
  #define ARM_CPUID_CORTEXA8    0x410fc080
  #define ARM_CPUID_CORTEXA9    0x410fc090
  #define ARM_CPUID_CORTEXM3    0x410fc231
+#define ARM_CPUID_CORTEXA15   0x412fc0f1
  #define ARM_CPUID_ANY         0xffffffff
 
  #if defined(CONFIG_USER_ONLY)
target-arm/helper.c +10 -0
index de4a50f..a68dc5c 100644
@@ -10,6 +10,16 @@
  #if !defined(CONFIG_USER_ONLY)
  #include "hw/loader.h"
  #endif
+#include "sysemu.h"
+
+static uint32_t cortexa15_cp15_c0_c1[8] = {
+    0x00001131, 0x00011011, 0x02010555, 0x00000000,
+    0x10201105, 0x20000000, 0x01240000, 0x02102211
+};
+
+static uint32_t cortexa15_cp15_c0_c2[8] = {
+    0x02101110, 0x13112111, 0x21232041, 0x11112131, 0x10011142, 0, 0, 0
+};
 

 static uint32_t cortexa9_cp15_c0_c1[8] =
 { 0x1031, 0x11, 0x000, 0, 0x00100103, 0x20000000, 0x01230000, 0x00002111 };

target-arm/helper.c +0 -0
@@ -158,6 +168,27 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)

        env->cp15.c0_ccsid[1] = 0x200fe015; /* 16k L1 icache. */ env->cp15.c1_sys = 0x00c50078;
        break;
+    case ARM_CPUID_CORTEXA15:
+        set_feature(env, ARM_FEATURE_V7);
+        set_feature(env, ARM_FEATURE_VFP4);
+        set_feature(env, ARM_FEATURE_VFP_FP16);
+        set_feature(env, ARM_FEATURE_NEON);
+        set_feature(env, ARM_FEATURE_THUMB2EE);
+        set_feature(env, ARM_FEATURE_ARM_DIV);
+        set_feature(env, ARM_FEATURE_V7MP);
+        set_feature(env, ARM_FEATURE_GENERICTIMER);
+        env->vfp.xregs[ARM_VFP_FPSID] = 0x410430f0;
+        env->vfp.xregs[ARM_VFP_MVFR0] = 0x10110222;
+        env->vfp.xregs[ARM_VFP_MVFR1] = 0x11111111;
+        memcpy(env->cp15.c0_c1, cortexa15_cp15_c0_c1, 8 * sizeof(uint32_t));
+        memcpy(env->cp15.c0_c2, cortexa15_cp15_c0_c2, 8 * sizeof(uint32_t));
+        env->cp15.c0_cachetype = 0x8444c004;
+        env->cp15.c0_clid = 0x0a200023;
+        env->cp15.c0_ccsid[0] = 0x701fe00a; /* 32K L1 dcache */
+        env->cp15.c0_ccsid[1] = 0x201fe00a; /* 32K L1 icache */
+        env->cp15.c0_ccsid[2] = 0x711fe07a; /* 4096K L2 unified cache */
+        env->cp15.c1_sys = 0x00c50078;
+        break;
    case ARM_CPUID_CORTEXM3:
        set_feature(env, ARM_FEATURE_V7);
        set_feature(env, ARM_FEATURE_M);

target-arm/helper.c +1 -2
@@ -413,6 +444,7 @@ static const struct arm_cpu_t arm_cpu_names[] = {
     { ARM_CPUID_CORTEXM3, "cortex-m3"},
     { ARM_CPUID_CORTEXA8, "cortex-a8"},
     { ARM_CPUID_CORTEXA9, "cortex-a9"},
+    { ARM_CPUID_CORTEXA15, "cortex-a15"},
     { ARM_CPUID_TI925T, "ti925t" },
     { ARM_CPUID_PXA250, "pxa250" },
     { ARM_CPUID_SA1100,    "sa1100" },
@@ -667,8 +699,6 @@ uint32_t HELPER(get_r13_banked)(CPUState *env, uint32_t mode)
 
  #else
 
-extern int semihosting_enabled;
-
  /* Map CPU modes onto saved register banks.  */
  static inline int bank_number(CPUState *env, int mode)
  {
@@ -1934,6 +1964,7 @@ uint32_t HELPER(get_cp15)(CPUState *env, uint32_t insn)

            case ARM_CPUID_CORTEXA8:
                return 2;
            case ARM_CPUID_CORTEXA9:
+            case ARM_CPUID_CORTEXA15:
                return 0;
            default:
                goto bad_reg;

target-arm/helper.c +0 -0
@@ -2054,11 +2085,26 @@ uint32_t HELPER(get_cp15)(CPUState *env, uint32_t insn)
                     goto bad_reg;
                 }

            case 1: /* L2 cache */
-                if (crm != 0) {
+                /* L2 Lockdown and Auxiliary control.  */
+                switch (op2) {
+                case 0:
+                    /* L2 cache lockdown (A8 only) */
+                    return 0;
+                case 2:
+                    /* L2 cache auxiliary control (A8) or control (A15) */
+                    if (ARM_CPUID(env) == ARM_CPUID_CORTEXA15) {
+                        /* Linux wants the number of processors from here.
+                         * Might as well set the interrupt-controller bit too.
+                         */
+                        return ((smp_cpus - 1) << 24) | (1 << 23);
+                    }
+                    return 0;
+                case 3:
+                    /* L2 cache extended control (A15) */
+                    return 0;
+                default:
                    goto bad_reg;
                }
-                /* L2 Lockdown and Auxiliary control.  */
-                return 0;
            default:
                goto bad_reg;
            }
--
1.7.1

_______________________________________________
Android-virt mailing list
[email protected]
https://lists.cs.columbia.edu/cucslists/listinfo/android-virt

#31 lore raw

Re: [Qemu-devel] [PATCH 10/12] Add Cortex-A15 CPU definition

In reply to Peter Maydell

Am 13.01.2012 21:52, schrieb Peter Maydell:

Quoted 21 lines

Add a definition of a Cortex-A15 CPU. Note that for the moment we do not implement any of:
* Large Physical Address Extensions (LPAE)
* Virtualization Extensions
* Generic Timer
* TrustZone (this is also true of our existing Cortex-A9 model, etc)

This CPU model is sufficient to boot a Linux kernel which has been compiled for an A15 without LPAE enabled.

---
 target-arm/cpu.h    |    1 +
 target-arm/helper.c |   56 ++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 52 insertions(+), 5 deletions(-)
target-arm/cpu.h +0 -0
index d5403ea..4b8d680 100644

target-arm/cpu.h void cpu_arm_set_cp_io(CPUARMState *env, int cpnum, · line 436

Quoted 4 lines
target-arm/cpu.h +1 -0
@@ -433,6 +433,7 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
 #define ARM_CPUID_CORTEXA8    0x410fc080
 #define ARM_CPUID_CORTEXA9    0x410fc090
 #define ARM_CPUID_CORTEXM3    0x410fc231
+#define ARM_CPUID_CORTEXA15   0x412fc0f1

Reminder of our rnpn topic...

We don't seem to have a clear ordering system but I'd suggest to move A15 one line up, to group the As and Ms in ascending order respectively.

Quoted 27 lines
target-arm/cpu.h +0 -0
 #define ARM_CPUID_ANY         0xffffffff
 
 #if defined(CONFIG_USER_ONLY)
target-arm/helper.c +10 -0
index de4a50f..a68dc5c 100644
@@ -10,6 +10,16 @@
 #if !defined(CONFIG_USER_ONLY)
 #include "hw/loader.h"
 #endif
+#include "sysemu.h"
+
+static uint32_t cortexa15_cp15_c0_c1[8] = {
+    0x00001131, 0x00011011, 0x02010555, 0x00000000,
+    0x10201105, 0x20000000, 0x01240000, 0x02102211
+};
+
+static uint32_t cortexa15_cp15_c0_c2[8] = {
+    0x02101110, 0x13112111, 0x21232041, 0x11112131, 0x10011142, 0, 0, 0
+};
 
 static uint32_t cortexa9_cp15_c0_c1[8] =
 { 0x1031, 0x11, 0x000, 0, 0x00100103, 0x20000000, 0x01230000, 0x00002111 };
@@ -158,6 +168,27 @@ static void cpu_reset_model_id(CPUARMState *env, uint32_t id)
         env->cp15.c0_ccsid[1] = 0x200fe015; /* 16k L1 icache. */
         env->cp15.c1_sys = 0x00c50078;

target-arm/helper.c static void cpu_reset_model_id(CPUARMState *env, uint32_t id) · line 179

Quoted 10 lines
target-arm/helper.c +9 -0
         break;
+    case ARM_CPUID_CORTEXA15:
+        set_feature(env, ARM_FEATURE_V7);
+        set_feature(env, ARM_FEATURE_VFP4);
+        set_feature(env, ARM_FEATURE_VFP_FP16);
+        set_feature(env, ARM_FEATURE_NEON);
+        set_feature(env, ARM_FEATURE_THUMB2EE);
+        set_feature(env, ARM_FEATURE_ARM_DIV);
+        set_feature(env, ARM_FEATURE_V7MP);
+        set_feature(env, ARM_FEATURE_GENERICTIMER);

Features look sane.

target-arm/helper.c static const struct arm_cpu_t arm_cpu_names[] = { · line 447

Quoted 5 lines
target-arm/helper.c +1 -0
@@ -413,6 +444,7 @@ static const struct arm_cpu_t arm_cpu_names[] = {
     { ARM_CPUID_CORTEXM3, "cortex-m3"},
     { ARM_CPUID_CORTEXA8, "cortex-a8"},
     { ARM_CPUID_CORTEXA9, "cortex-a9"},
+    { ARM_CPUID_CORTEXA15, "cortex-a15"},

Space please.

Quoted 23 lines
target-arm/helper.c +1 -2
     { ARM_CPUID_TI925T, "ti925t" },
     { ARM_CPUID_PXA250, "pxa250" },
     { ARM_CPUID_SA1100,    "sa1100" },
@@ -667,8 +699,6 @@ uint32_t HELPER(get_r13_banked)(CPUState *env, uint32_t mode)
 
 #else
 
-extern int semihosting_enabled;
-
 /* Map CPU modes onto saved register banks.  */
 static inline int bank_number(CPUState *env, int mode)
 {
@@ -1934,6 +1964,7 @@ uint32_t HELPER(get_cp15)(CPUState *env, uint32_t insn)
             case ARM_CPUID_CORTEXA8:
                 return 2;
             case ARM_CPUID_CORTEXA9:
+            case ARM_CPUID_CORTEXA15:
                 return 0;
             default:
                 goto bad_reg;
@@ -2054,11 +2085,26 @@ uint32_t HELPER(get_cp15)(CPUState *env, uint32_t insn)
                     goto bad_reg;
                 }

target-arm/helper.c uint32_t HELPER(get_cp15)(CPUState *env, uint32_t insn) · line 2095

Quoted 10 lines
target-arm/helper.c +8 -1
             case 1: /* L2 cache */
-                if (crm != 0) {
+                /* L2 Lockdown and Auxiliary control.  */
+                switch (op2) {
+                case 0:
+                    /* L2 cache lockdown (A8 only) */
+                    return 0;
+                case 2:
+                    /* L2 cache auxiliary control (A8) or control (A15) */
+                    if (ARM_CPUID(env) == ARM_CPUID_CORTEXA15) {

Change of mind? You opposed uses of ARM_CPUID() for Cortex-R4F.

Andreas

Quoted 17 lines
target-arm/helper.c +10 -2
+                        /* Linux wants the number of processors from here.
+                         * Might as well set the interrupt-controller bit too.
+                         */
+                        return ((smp_cpus - 1) << 24) | (1 << 23);
+                    }
+                    return 0;
+                case 3:
+                    /* L2 cache extended control (A15) */
+                    return 0;
+                default:
                     goto bad_reg;
                 }
-                /* L2 Lockdown and Auxiliary control.  */
-                return 0;
             default:
                 goto bad_reg;
             }
Signature · 2 lines
SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg
#32 lore raw

Re: [Qemu-devel] [PATCH 10/12] Add Cortex-A15 CPU definition

Quoted 8 lines · Andreas Färber

Am 13.01.2012 21:52, schrieb Peter Maydell:

target-arm/cpu.h void cpu_arm_set_cp_io(CPUARMState *env, int cpnum, · line 436

Quoted 5 lines
target-arm/cpu.h +1 -0
@@ -433,6 +433,7 @@ void cpu_arm_set_cp_io(CPUARMState *env, int cpnum,
  #define ARM_CPUID_CORTEXA8    0x410fc080
  #define ARM_CPUID_CORTEXA9    0x410fc090
  #define ARM_CPUID_CORTEXM3    0x410fc231
+#define ARM_CPUID_CORTEXA15   0x412fc0f1

Reminder of our rnpn topic...

Yeah. I have a cp15-rework series brewing that might assist here.

Quoted 2 lines

We don't seem to have a clear ordering system but I'd suggest to move A15 one line up, to group the As and Ms in ascending order respectively.

Good idea.

Quoted 7 lines

target-arm/helper.c static const struct arm_cpu_t arm_cpu_names[] = { · line 447

Quoted 5 lines
target-arm/helper.c +1 -0
@@ -413,6 +444,7 @@ static const struct arm_cpu_t arm_cpu_names[] = {
      { ARM_CPUID_CORTEXM3, "cortex-m3"},
      { ARM_CPUID_CORTEXA8, "cortex-a8"},
      { ARM_CPUID_CORTEXA9, "cortex-a9"},
+    { ARM_CPUID_CORTEXA15, "cortex-a15"},

Space please.

OK. (I just copy-pasted the a9 line, as you've probably guessed.)

Quoted 5 lines

target-arm/helper.c uint32_t HELPER(get_cp15)(CPUState *env, uint32_t insn) · line 2095

Quoted 3 lines
target-arm/helper.c +3 -0
+                case 2:
+                    /* L2 cache auxiliary control (A8) or control (A15) */
+                    if (ARM_CPUID(env) == ARM_CPUID_CORTEXA15) {

Change of mind? You opposed uses of ARM_CPUID() for Cortex-R4F.

I kind of view this as a small interim bodge. My aim with my cp15 rework is that instead of all these switches we will just set up the cp15 registers for the CPU at cpu_init time, so A15 will set up one thing, A8 something else and A9 et al set up nothing at all [and so get the default UNDEF behaviour].

If you can fish the particular mail of mine you have in mind out of the archives I can reread it and see if I disagree with myself :-)

-- PMM

#33 lore raw

PATCH 11/12 arm_boot: Pass base address of GIC CPU interface, not whole GIC

In reply to Peter Maydell

The arm_boot secondary boot loader code needs the address of the GIC CPU interface. Obtaining this from the base address of the private peripheral region was possible for A9 and 11MPcore, but the A15 puts the GIC CPU interface in a different place. So make boards pass in the GIC CPU interface address directly.

---
 hw/arm-misc.h |    2 +-
 hw/arm_boot.c |    8 ++++----
 hw/realview.c |   12 +++++++-----
 hw/vexpress.c |    6 ++++--
 4 files changed, 16 insertions(+), 12 deletions(-)
hw/arm-misc.h +1 -1
index 6e8ae6b..a360512 100644
@@ -32,7 +32,7 @@ struct arm_boot_info {
     target_phys_addr_t loader_start;
     target_phys_addr_t smp_loader_start;
     target_phys_addr_t smp_bootreg_addr;
-    target_phys_addr_t smp_priv_base;
+    target_phys_addr_t gic_cpu_if_addr;
     int nb_cpus;
     int board_id;
     int (*atag_board)(const struct arm_boot_info *info, void *p);
hw/arm_boot.c +4 -4
index bf509a8..89463a3 100644
@@ -31,16 +31,16 @@ static uint32_t bootloader[] = {
 /* Entry point for secondary CPUs.  Enable interrupt controller and
    Issue WFI until start address is written to system controller.  */
 static uint32_t smpboot[] = {
-  0xe59f201c, /* ldr r2, privbase */
+  0xe59f201c, /* ldr r2, gic_cpu_if */
   0xe59f001c, /* ldr r0, startaddr */
   0xe3a01001, /* mov r1, #1 */
-  0xe5821100, /* str r1, [r2, #256] */
+  0xe5821000, /* str r1, [r2] */
   0xe320f003, /* wfi */
   0xe5901000, /* ldr     r1, [r0] */
   0xe1110001, /* tst     r1, r1 */
   0x0afffffb, /* beq     <wfi> */
   0xe12fff11, /* bx      r1 */
-  0,          /* privbase: Private memory region base address.  */
+  0,          /* gic_cpu_if: base address of GIC CPU interface */
   0           /* bootreg: Boot register address is held here */
 };
 
@@ -274,7 +274,7 @@ void arm_load_kernel(CPUState *env, struct arm_boot_info *info)
                            info->loader_start);
         if (info->nb_cpus > 1) {
             smpboot[ARRAY_SIZE(smpboot) - 1] = info->smp_bootreg_addr;
-            smpboot[ARRAY_SIZE(smpboot) - 2] = info->smp_priv_base;
+            smpboot[ARRAY_SIZE(smpboot) - 2] = info->gic_cpu_if_addr;
             for (n = 0; n < ARRAY_SIZE(smpboot); n++) {
                 smpboot[n] = tswap32(smpboot[n]);
             }
hw/realview.c +7 -5
index d2fde44..9adc46d 100644
@@ -214,21 +214,23 @@ static void realview_init(ram_addr_t ram_size,
     sysbus_mmio_map(sysbus_from_qdev(sysctl), 0, 0x10000000);
 
     if (is_mpcore) {
+        target_phys_addr_t periphbase;
         dev = qdev_create(NULL, is_pb ? "a9mpcore_priv": "realview_mpcore");
         qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
         qdev_init_nofail(dev);
         busdev = sysbus_from_qdev(dev);
         if (is_pb) {
-            realview_binfo.smp_priv_base = 0x1f000000;
+            periphbase = 0x1f000000;
         } else {
-            realview_binfo.smp_priv_base = 0x10100000;
+            periphbase = 0x10100000;
         }
-        sysbus_mmio_map(busdev, 0, realview_binfo.smp_priv_base);
+        sysbus_mmio_map(busdev, 0, periphbase);
         for (n = 0; n < smp_cpus; n++) {
             sysbus_connect_irq(busdev, n, cpu_irq[n]);
         }
-        sysbus_create_varargs("l2x0", realview_binfo.smp_priv_base + 0x2000,
-                              NULL);
+        sysbus_create_varargs("l2x0", periphbase + 0x2000, NULL);
+        /* Both A9 and 11MPCore put the GIC CPU i/f at base + 0x100 */
+        realview_binfo.gic_cpu_if_addr = periphbase + 0x100;
     } else {
         uint32_t gic_addr = is_pb ? 0x1e000000 : 0x10040000;
         /* For now just create the nIRQ GIC, and ignore the others.  */
hw/vexpress.c +4 -2
index 9860085..4b9454f 100644
@@ -115,6 +115,7 @@ typedef void DBoardInitFn(const VEDBoardInfo *daughterboard,
 struct VEDBoardInfo {
     const target_phys_addr_t *motherboard_map;
     const target_phys_addr_t loader_start;
+    const target_phys_addr_t gic_cpu_if_addr;
     DBoardInitFn *init;
 };
 
@@ -175,8 +176,7 @@ static void a9_daughterboard_init(const VEDBoardInfo *daughterboard,
     qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
     qdev_init_nofail(dev);
     busdev = sysbus_from_qdev(dev);
-    vexpress_binfo.smp_priv_base = 0x1e000000;
-    sysbus_mmio_map(busdev, 0, vexpress_binfo.smp_priv_base);
+    sysbus_mmio_map(busdev, 0, 0x1e000000);
     for (n = 0; n < smp_cpus; n++) {
         sysbus_connect_irq(busdev, n, cpu_irq[n]);
     }
@@ -214,6 +214,7 @@ static void a9_daughterboard_init(const VEDBoardInfo *daughterboard,
 static const VEDBoardInfo a9_daughterboard = {
     .motherboard_map = motherboard_legacy_map,
     .loader_start = 0x60000000,
+    .gic_cpu_if_addr = 0x1e000100,
     .init = a9_daughterboard_init,
 };
 
@@ -316,6 +317,7 @@ static void vexpress_common_init(const VEDBoardInfo *daughterboard,
     vexpress_binfo.loader_start = daughterboard->loader_start;
     vexpress_binfo.smp_loader_start = map[VE_SRAM];
     vexpress_binfo.smp_bootreg_addr = map[VE_SYSREGS] + 0x30;
+    vexpress_binfo.gic_cpu_if_addr = daughterboard->gic_cpu_if_addr;
     arm_load_kernel(first_cpu, &vexpress_binfo);
 }
 
Signature · 1 line
1.7.1
#34 lore raw

PATCH 12/12 hw/vexpress.c: Add vexpress-a15 machine

In reply to Peter Maydell

Add the vexpress-a15 machine, and the A-Series memory map it uses.

---
 hw/vexpress.c |  141 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 141 insertions(+), 0 deletions(-)
hw/vexpress.c +141 -0
index 4b9454f..9d16fbf 100644
@@ -103,6 +103,41 @@ static target_phys_addr_t motherboard_legacy_map[] = {
     [VE_USB] = 0x4f000000,
 };
 
+static target_phys_addr_t motherboard_aseries_map[] = {
+    /* CS0: 0x00000000 .. 0x0c000000 */
+    [VE_NORFLASH0] = 0x00000000,
+    [VE_NORFLASH0ALIAS] = 0x08000000,
+    /* CS4: 0x0c000000 .. 0x10000000 */
+    [VE_NORFLASH1] = 0x0c000000,
+    /* CS5: 0x10000000 .. 0x14000000 */
+    /* CS1: 0x14000000 .. 0x18000000 */
+    [VE_SRAM] = 0x14000000,
+    /* CS2: 0x18000000 .. 0x1c000000 */
+    [VE_VIDEORAM] = 0x18000000,
+    [VE_ETHERNET] = 0x1a000000,
+    [VE_USB] = 0x1b000000,
+    /* CS3: 0x1c000000 .. 0x20000000 */
+    [VE_DAPROM] = 0x1c000000,
+    [VE_SYSREGS] = 0x1c010000,
+    [VE_SP810] = 0x1c020000,
+    [VE_SERIALPCI] = 0x1c030000,
+    [VE_PL041] = 0x1c040000,
+    [VE_MMCI] = 0x1c050000,
+    [VE_KMI0] = 0x1c060000,
+    [VE_KMI1] = 0x1c070000,
+    [VE_UART0] = 0x1c090000,
+    [VE_UART1] = 0x1c0a0000,
+    [VE_UART2] = 0x1c0b0000,
+    [VE_UART3] = 0x1c0c0000,
+    [VE_WDT] = 0x1c0f0000,
+    [VE_TIMER01] = 0x1c110000,
+    [VE_TIMER23] = 0x1c120000,
+    [VE_SERIALDVI] = 0x1c160000,
+    [VE_RTC] = 0x1c170000,
+    [VE_COMPACTFLASH] = 0x1c1a0000,
+    [VE_CLCD] = 0x1c1f0000,
+};
+
 /* Structure defining the peculiarities of a specific daughterboard */
 
 typedef struct VEDBoardInfo VEDBoardInfo;
@@ -218,6 +253,91 @@ static const VEDBoardInfo a9_daughterboard = {
     .init = a9_daughterboard_init,
 };
 
+static void a15_daughterboard_init(const VEDBoardInfo *daughterboard,
+                                   ram_addr_t ram_size,
+                                   const char *cpu_model,
+                                   qemu_irq *pic, uint32_t *proc_id)
+{
+    int n;
+    CPUState *env = NULL;
+    MemoryRegion *sysmem = get_system_memory();
+    MemoryRegion *ram = g_new(MemoryRegion, 1);
+    MemoryRegion *sram = g_new(MemoryRegion, 1);
+    qemu_irq cpu_irq[4];
+    DeviceState *dev;
+    SysBusDevice *busdev;
+
+    if (!cpu_model) {
+        cpu_model = "cortex-a15";
+    }
+
+    *proc_id = 0x14000217;
+
+    for (n = 0; n < smp_cpus; n++) {
+        qemu_irq *irqp;
+        env = cpu_init(cpu_model);
+        if (!env) {
+            fprintf(stderr, "Unable to find CPU definition\n");
+            exit(1);
+        }
+        irqp = arm_pic_init_cpu(env);
+        cpu_irq[n] = irqp[ARM_PIC_CPU_IRQ];
+    }
+
+    if (ram_size > 0x80000000) {
+        fprintf(stderr, "vexpress-a15: cannot model more than 2GB RAM\n");
+        exit(1);
+    }
+
+    memory_region_init_ram(ram, "vexpress.highmem", ram_size);
+    vmstate_register_ram_global(ram);
+    /* RAM is from 0x80000000 upwards; there is no low-memory alias for it. */
+    memory_region_add_subregion(sysmem, 0x80000000, ram);
+
+    /* 0x2c000000 A15MPCore private memory region (GIC) */
+    dev = qdev_create(NULL, "a15mpcore_priv");
+    qdev_prop_set_uint32(dev, "num-cpu", smp_cpus);
+    qdev_init_nofail(dev);
+    busdev = sysbus_from_qdev(dev);
+    sysbus_mmio_map(busdev, 0, 0x2c000000);
+    for (n = 0; n < smp_cpus; n++) {
+        sysbus_connect_irq(busdev, n, cpu_irq[n]);
+    }
+    /* Interrupts [42:0] are from the motherboard;
+     * [47:43] are reserved; [63:48] are daughterboard
+     * peripherals. Note that some documentation numbers
+     * external interrupts starting from 32 (because there
+     * are internal interrupts 0..31).
+     */
+    for (n = 0; n < 64; n++) {
+        pic[n] = qdev_get_gpio_in(dev, n);
+    }
+
+    /* A15 daughterboard peripherals: */
+
+    /* 0x20000000: CoreSight interfaces: not modelled */
+    /* 0x2a000000: PL301 AXI interconnect: not modelled */
+    /* 0x2a420000: SCC: not modelled */
+    /* 0x2a430000: system counter: not modelled */
+    /* 0x2b000000: HDLCD controller: not modelled */
+    /* 0x2b060000: SP805 watchdog: not modelled */
+    /* 0x2b0a0000: PL341 dynamic memory controller: not modelled */
+    /* 0x2e000000: system SRAM */
+    memory_region_init_ram(sram, "vexpress.a15sram", 0x10000);
+    vmstate_register_ram_global(sram);
+    memory_region_add_subregion(sysmem, 0x2e000000, sram);
+
+    /* 0x7ffb0000: DMA330 DMA controller: not modelled */
+    /* 0x7ffd0000: PL354 static memory controller: not modelled */
+}
+
+static const VEDBoardInfo a15_daughterboard = {
+    .motherboard_map = motherboard_aseries_map,
+    .loader_start = 0x80000000,
+    .gic_cpu_if_addr = 0x2c002000,
+    .init = a15_daughterboard_init,
+};
+
 static void vexpress_common_init(const VEDBoardInfo *daughterboard,
                                  ram_addr_t ram_size,
                                  const char *boot_device,
@@ -333,6 +453,18 @@ static void vexpress_a9_init(ram_addr_t ram_size,
                          kernel_cmdline, initrd_filename, cpu_model);
 }
 
+static void vexpress_a15_init(ram_addr_t ram_size,
+                              const char *boot_device,
+                              const char *kernel_filename,
+                              const char *kernel_cmdline,
+                              const char *initrd_filename,
+                              const char *cpu_model)
+{
+    vexpress_common_init(&a15_daughterboard,
+                         ram_size, boot_device, kernel_filename,
+                         kernel_cmdline, initrd_filename, cpu_model);
+}
+
 static QEMUMachine vexpress_a9_machine = {
     .name = "vexpress-a9",
     .desc = "ARM Versatile Express for Cortex-A9",
@@ -341,9 +473,18 @@ static QEMUMachine vexpress_a9_machine = {
     .max_cpus = 4,
 };
 
+static QEMUMachine vexpress_a15_machine = {
+    .name = "vexpress-a15",
+    .desc = "ARM Versatile Express for Cortex-A15",
+    .init = vexpress_a15_init,
+    .use_scsi = 1,
+    .max_cpus = 4,
+};
+
 static void vexpress_machine_init(void)
 {
     qemu_register_machine(&vexpress_a9_machine);
+    qemu_register_machine(&vexpress_a15_machine);
 }
 
 machine_init(vexpress_machine_init);
Signature · 1 line
1.7.1
#35 lore raw
In reply to Peter Maydell
Quoted 9 lines · Peter Maydell

This patchset adds support for (a rather limited version of) the Cortex-A15 CPU and the Versatile Express A15 daughterboard. The resulting model is capable of booting a Linux kernel which has been configured for Cortex-A15 with the Versatile Express "extended memory map" and without support for LPAE.

PS for the folk on android-virt: I haven't yet tested these patches with KVM, only with TCG, but there is no inherent reason for them not to work...

PPS: these patches are against qemu-master so for kvm you'd need to (a) rebase them on qemu-linaro (b) put the kvm patches on top of these (c) wait for me to do a. for you ;-)

-- PMM

#36 lore raw
Quoted 7 lines · Peter Maydell
Quoted 5 lines · Peter Maydell

This patchset adds support for (a rather limited version of) the Cortex-A15 CPU and the Versatile Express A15 daughterboard. The resulting model is capable of booting a Linux kernel which has been configured for Cortex-A15 with the Versatile Express "extended memory map" and without support for LPAE.

Quoted 8 lines
Quoted 3 lines

PS for the folk on android-virt: I haven't yet tested these patches with KVM, only with TCG, but there is no inherent reason for them not to work...

PPS: these patches are against qemu-master so for kvm you'd need to (a) rebase them on qemu-linaro (b) put the kvm patches on top of these (c) wait for me to do a. for you ;-)

ok, I'll test this with the most recent KVM changes soon and also look forward for your merge... :)

#37 lore raw
Quoted 7 lines · Christoffer Dall
Quoted 3 lines · Peter Maydell

PPS: these patches are against qemu-master so for kvm you'd need to (a) rebase them on qemu-linaro (b) put the kvm patches on top of these (c) wait for me to do a. for you ;-)

ok, I'll test this with the most recent KVM changes soon and also look forward for your merge... :)

vexpress-a15 now in qemu-linaro git tree. However it doesn't boot under KVM:

root@LinaroE102767:~# /usr/local/bin/qemu-system-arm  -enable-kvm
-kernel /a15-no-lpae-uImage  -m 128 -serial stdio -drive
if=sd,file=/maz/vex
press.img -M vexpress-a15 -display none -append "console=ttyAMA0
earlyprintk rootwait mem=128M root=/dev/mmcblk0p2 rw --debug"
Uncompressing Linux... done, booting the kernel.
Booting Linux on physical CPU 0
Linux version 3.1.0+ (petmay01@LinaroE102767) (gcc version 4.6.1
(Ubuntu/Linaro 4.6.1-9ubuntu3) ) #19 SMP Fri Jan 13 12:23:22 GMT 2012
CPU: ARMv7 Processor [412fc0f1] revision 1 (ARMv7), cr=10c5387f
CPU: PIPT / VIPT nonaliasing data cache, PIPT instruction cache
Machine: ARM-Versatile Express
SoC: ARM VE Platform
bootconsole [earlycon0] enabled
Memory policy: ECC disabled, Data cache writealloc
error: kvm run failed Invalid argument
Aborted

Under TCG the next thing printed is
ct_ca15x4_init_cpu_map: ncores 1
Architected timer frequency not available
sched_clock: 32 bits at 24MHz, resolution 41ns, wraps every 178956ms

...possibly the kernel side needs to implement the A15 L2 cache control register, which is where the guest kernel picks up the number of cores from; see patch 10 for the TCG version.

-- PMM

#38 lore raw
Quoted 30 lines · Peter Maydell
Quoted 7 lines · Christoffer Dall
Quoted 3 lines · Peter Maydell

PPS: these patches are against qemu-master so for kvm you'd need to (a) rebase them on qemu-linaro (b) put the kvm patches on top of these (c) wait for me to do a. for you ;-)

ok, I'll test this with the most recent KVM changes soon and also look forward for your merge... :)

vexpress-a15 now in qemu-linaro git tree. However it doesn't boot under KVM:

root@LinaroE102767:~# /usr/local/bin/qemu-system-arm  -enable-kvm
-kernel /a15-no-lpae-uImage  -m 128 -serial stdio -drive
if=sd,file=/maz/vex
press.img -M vexpress-a15 -display none -append "console=ttyAMA0
earlyprintk rootwait mem=128M root=/dev/mmcblk0p2 rw --debug"
Uncompressing Linux... done, booting the kernel.
Booting Linux on physical CPU 0
Linux version 3.1.0+ (petmay01@LinaroE102767) (gcc version 4.6.1
(Ubuntu/Linaro 4.6.1-9ubuntu3) ) #19 SMP Fri Jan 13 12:23:22 GMT 2012
CPU: ARMv7 Processor [412fc0f1] revision 1 (ARMv7), cr=10c5387f
CPU: PIPT / VIPT nonaliasing data cache, PIPT instruction cache
Machine: ARM-Versatile Express
SoC: ARM VE Platform
bootconsole [earlycon0] enabled
Memory policy: ECC disabled, Data cache writealloc
error: kvm run failed Invalid argument
Aborted
Quoted 8 lines

Under TCG the next thing printed is
ct_ca15x4_init_cpu_map: ncores 1
Architected timer frequency not available
sched_clock: 32 bits at 24MHz, resolution 41ns, wraps every 178956ms

...possibly the kernel side needs to implement the A15 L2 cache control register, which is where the guest kernel picks up the number of cores from; see patch 10 for the TCG version.

I've pushed a new branch (kvm-v3.3-rc1) to my arm-platforms repo. It contains a fix (or shall we call it a horrible hack?) for the above.

	M.
Signature · 1 line
Jazz is not dead. It just smells funny...