From 61558e7a75e6f6d579e636ca5a5b79c68654f07c Mon Sep 17 00:00:00 2001 From: Evgeny Voevodin Date: Mon, 28 May 2012 04:11:49 +0000 Subject: [PATCH 01/16] ARM: Exynos4210 IRQ: Introduce new IRQ gate functionality. New IRQ gate consists of n_in input qdev gpio lines and one output sysbus IRQ line. The output IRQ level is formed as OR between all gpio inputs. Signed-off-by: Evgeny Voevodin Signed-off-by: Peter Maydell --- hw/exynos4210.c | 32 ++++++++++--------- hw/exynos4210.h | 2 +- hw/exynos4210_gic.c | 78 ++++++++++++++++++++++----------------------- 3 files changed, 57 insertions(+), 55 deletions(-) diff --git a/hw/exynos4210.c b/hw/exynos4210.c index dd14d01b01..9c20b3f22d 100644 --- a/hw/exynos4210.c +++ b/hw/exynos4210.c @@ -97,11 +97,11 @@ void exynos4210_write_secondary(ARMCPU *cpu, Exynos4210State *exynos4210_init(MemoryRegion *system_mem, unsigned long ram_size) { - qemu_irq cpu_irq[4]; - int n; + qemu_irq cpu_irq[EXYNOS4210_NCPUS]; + int i, n; Exynos4210State *s = g_new(Exynos4210State, 1); qemu_irq *irqp; - qemu_irq gate_irq[EXYNOS4210_IRQ_GATE_NINPUTS]; + qemu_irq gate_irq[EXYNOS4210_NCPUS][EXYNOS4210_IRQ_GATE_NINPUTS]; unsigned long mem_size; DeviceState *dev; SysBusDevice *busdev; @@ -128,16 +128,18 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, s->irq_table = exynos4210_init_irq(&s->irqs); /* IRQ Gate */ - dev = qdev_create(NULL, "exynos4210.irq_gate"); - qdev_init_nofail(dev); - /* Get IRQ Gate input in gate_irq */ - for (n = 0; n < EXYNOS4210_IRQ_GATE_NINPUTS; n++) { - gate_irq[n] = qdev_get_gpio_in(dev, n); - } - busdev = sysbus_from_qdev(dev); - /* Connect IRQ Gate output to cpu_irq */ - for (n = 0; n < EXYNOS4210_NCPUS; n++) { - sysbus_connect_irq(busdev, n, cpu_irq[n]); + for (i = 0; i < EXYNOS4210_NCPUS; i++) { + dev = qdev_create(NULL, "exynos4210.irq_gate"); + qdev_prop_set_uint32(dev, "n_in", EXYNOS4210_IRQ_GATE_NINPUTS); + qdev_init_nofail(dev); + /* Get IRQ Gate input in gate_irq */ + for (n = 0; n < EXYNOS4210_IRQ_GATE_NINPUTS; n++) { + gate_irq[i][n] = qdev_get_gpio_in(dev, n); + } + busdev = sysbus_from_qdev(dev); + + /* Connect IRQ Gate output to cpu_irq */ + sysbus_connect_irq(busdev, 0, cpu_irq[i]); } /* Private memory region and Internal GIC */ @@ -147,7 +149,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, busdev = sysbus_from_qdev(dev); sysbus_mmio_map(busdev, 0, EXYNOS4210_SMP_PRIVATE_BASE_ADDR); for (n = 0; n < EXYNOS4210_NCPUS; n++) { - sysbus_connect_irq(busdev, n, gate_irq[n * 2]); + sysbus_connect_irq(busdev, n, gate_irq[n][0]); } for (n = 0; n < EXYNOS4210_INT_GIC_NIRQ; n++) { s->irqs.int_gic_irq[n] = qdev_get_gpio_in(dev, n); @@ -166,7 +168,7 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem, /* Map Distributer interface */ sysbus_mmio_map(busdev, 1, EXYNOS4210_EXT_GIC_DIST_BASE_ADDR); for (n = 0; n < EXYNOS4210_NCPUS; n++) { - sysbus_connect_irq(busdev, n, gate_irq[n * 2 + 1]); + sysbus_connect_irq(busdev, n, gate_irq[n][1]); } for (n = 0; n < EXYNOS4210_EXT_GIC_NIRQ; n++) { s->irqs.ext_gic_irq[n] = qdev_get_gpio_in(dev, n); diff --git a/hw/exynos4210.h b/hw/exynos4210.h index b1b4609054..9b1ae4c8b1 100644 --- a/hw/exynos4210.h +++ b/hw/exynos4210.h @@ -56,7 +56,7 @@ /* * exynos4210 IRQ subsystem stub definitions. */ -#define EXYNOS4210_IRQ_GATE_NINPUTS 8 +#define EXYNOS4210_IRQ_GATE_NINPUTS 2 /* Internal and External GIC */ #define EXYNOS4210_MAX_INT_COMBINER_OUT_IRQ 64 #define EXYNOS4210_MAX_EXT_COMBINER_OUT_IRQ 16 diff --git a/hw/exynos4210_gic.c b/hw/exynos4210_gic.c index e1b215eff0..7d03dd9ae3 100644 --- a/hw/exynos4210_gic.c +++ b/hw/exynos4210_gic.c @@ -362,61 +362,64 @@ static void exynos4210_gic_register_types(void) type_init(exynos4210_gic_register_types) -/* - * IRQGate struct. - * IRQ Gate represents OR gate between GICs to pass IRQ to PIC. +/* IRQ OR Gate struct. + * + * This device models an OR gate. There are n_in input qdev gpio lines and one + * output sysbus IRQ line. The output IRQ level is formed as OR between all + * gpio inputs. */ typedef struct { SysBusDevice busdev; - qemu_irq pic_irq[EXYNOS4210_NCPUS]; /* output IRQs to PICs */ - uint32_t gpio_level[EXYNOS4210_IRQ_GATE_NINPUTS]; /* Input levels */ + uint32_t n_in; /* inputs amount */ + uint32_t *level; /* input levels */ + qemu_irq out; /* output IRQ */ } Exynos4210IRQGateState; +static Property exynos4210_irq_gate_properties[] = { + DEFINE_PROP_UINT32("n_in", Exynos4210IRQGateState, n_in, 1), + DEFINE_PROP_END_OF_LIST(), +}; + static const VMStateDescription vmstate_exynos4210_irq_gate = { .name = "exynos4210.irq_gate", - .version_id = 1, - .minimum_version_id = 1, - .minimum_version_id_old = 1, + .version_id = 2, + .minimum_version_id = 2, + .minimum_version_id_old = 2, .fields = (VMStateField[]) { - VMSTATE_UINT32_ARRAY(gpio_level, Exynos4210IRQGateState, - EXYNOS4210_IRQ_GATE_NINPUTS), + VMSTATE_VBUFFER_UINT32(level, Exynos4210IRQGateState, 1, NULL, 0, n_in), VMSTATE_END_OF_LIST() } }; -/* Process a change in an external IRQ input. */ +/* Process a change in IRQ input. */ static void exynos4210_irq_gate_handler(void *opaque, int irq, int level) { - Exynos4210IRQGateState *s = - (Exynos4210IRQGateState *)opaque; - uint32_t odd, even; + Exynos4210IRQGateState *s = (Exynos4210IRQGateState *)opaque; + uint32_t i; - if (irq & 1) { - odd = irq; - even = irq & ~1; - } else { - even = irq; - odd = irq | 1; + assert(irq < s->n_in); + + s->level[irq] = level; + + for (i = 0; i < s->n_in; i++) { + if (s->level[i] >= 1) { + qemu_irq_raise(s->out); + return; + } } - assert(irq < EXYNOS4210_IRQ_GATE_NINPUTS); - s->gpio_level[irq] = level; - - if (s->gpio_level[odd] >= 1 || s->gpio_level[even] >= 1) { - qemu_irq_raise(s->pic_irq[even >> 1]); - } else { - qemu_irq_lower(s->pic_irq[even >> 1]); - } + qemu_irq_lower(s->out); return; } static void exynos4210_irq_gate_reset(DeviceState *d) { - Exynos4210IRQGateState *s = (Exynos4210IRQGateState *)d; + Exynos4210IRQGateState *s = + DO_UPCAST(Exynos4210IRQGateState, busdev.qdev, d); - memset(&s->gpio_level, 0, sizeof(s->gpio_level)); + memset(s->level, 0, s->n_in * sizeof(*s->level)); } /* @@ -424,19 +427,15 @@ static void exynos4210_irq_gate_reset(DeviceState *d) */ static int exynos4210_irq_gate_init(SysBusDevice *dev) { - unsigned int i; - Exynos4210IRQGateState *s = - FROM_SYSBUS(Exynos4210IRQGateState, dev); + Exynos4210IRQGateState *s = FROM_SYSBUS(Exynos4210IRQGateState, dev); /* Allocate general purpose input signals and connect a handler to each of * them */ - qdev_init_gpio_in(&s->busdev.qdev, exynos4210_irq_gate_handler, - EXYNOS4210_IRQ_GATE_NINPUTS); + qdev_init_gpio_in(&s->busdev.qdev, exynos4210_irq_gate_handler, s->n_in); - /* Connect SysBusDev irqs to device specific irqs */ - for (i = 0; i < EXYNOS4210_NCPUS; i++) { - sysbus_init_irq(dev, &s->pic_irq[i]); - } + s->level = g_malloc0(s->n_in * sizeof(*s->level)); + + sysbus_init_irq(dev, &s->out); return 0; } @@ -449,6 +448,7 @@ static void exynos4210_irq_gate_class_init(ObjectClass *klass, void *data) k->init = exynos4210_irq_gate_init; dc->reset = exynos4210_irq_gate_reset; dc->vmsd = &vmstate_exynos4210_irq_gate; + dc->props = exynos4210_irq_gate_properties; } static TypeInfo exynos4210_irq_gate_info = { From 9b574c29c199dbcf5f81fa14d1c692a23463ca7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20F=C3=A4rber?= Date: Mon, 28 May 2012 04:11:49 +0000 Subject: [PATCH 02/16] arm_boot: Fix typos in comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mimicing -> mimicking thei -> the Signed-off-by: Andreas Färber Reviewed-by: Stefan Weil Signed-off-by: Peter Maydell --- hw/arm-misc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/arm-misc.h b/hw/arm-misc.h index 1d51570c88..1f96229d3c 100644 --- a/hw/arm-misc.h +++ b/hw/arm-misc.h @@ -45,9 +45,9 @@ struct arm_boot_info { /* multicore boards that use the default secondary core boot functions * can ignore these two function calls. If the default functions won't * work, then write_secondary_boot() should write a suitable blob of - * code mimicing the secondary CPU startup process used by the board's + * code mimicking the secondary CPU startup process used by the board's * boot loader/boot ROM code, and secondary_cpu_reset_hook() should - * perform any necessary CPU reset handling and set the PC for thei + * perform any necessary CPU reset handling and set the PC for the * secondary CPUs to point at this boot blob. */ void (*write_secondary_boot)(ARMCPU *cpu, From acd684280f9e91e8199d0b2126d4b057676dafec Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 2 May 2012 16:49:38 +0000 Subject: [PATCH 03/16] hw/arm_gic: Remove NVIC ifdefs from gic_state struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove some NVIC ifdefs from the gic_state struct and its state save/load functions. This means there are some fields in it which are present for the NVIC but not used, but means it always has the same layout and can be pulled out into a common subclass. Note that the addition of irq_target[] to the save/load struct for the NVIC requires a vmstate version bump. Signed-off-by: Peter Maydell Reviewed-by: Andreas Färber --- hw/arm_gic.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/hw/arm_gic.c b/hw/arm_gic.c index 72298b4b41..17b2eba789 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -108,9 +108,7 @@ typedef struct gic_state int cpu_enabled[NCPU]; gic_irq_state irq_state[GIC_MAXIRQ]; -#ifndef NVIC int irq_target[GIC_MAXIRQ]; -#endif int priority1[GIC_INTERNAL][NCPU]; int priority2[GIC_MAXIRQ - GIC_INTERNAL]; int last_active[GIC_MAXIRQ][NCPU]; @@ -120,18 +118,14 @@ typedef struct gic_state int running_priority[NCPU]; int current_pending[NCPU]; -#if NCPU > 1 uint32_t num_cpu; -#endif MemoryRegion iomem; /* Distributor */ -#ifndef NVIC /* This is just so we can have an opaque pointer which identifies * both this GIC and which CPU interface we should be accessing. */ struct gic_state *backref[NCPU]; MemoryRegion cpuiomem[NCPU+1]; /* CPU interfaces */ -#endif uint32_t num_irq; } gic_state; @@ -800,9 +794,7 @@ static void gic_save(QEMUFile *f, void *opaque) qemu_put_be32(f, s->priority2[i]); } for (i = 0; i < s->num_irq; i++) { -#ifndef NVIC qemu_put_be32(f, s->irq_target[i]); -#endif qemu_put_byte(f, s->irq_state[i].enabled); qemu_put_byte(f, s->irq_state[i].pending); qemu_put_byte(f, s->irq_state[i].active); @@ -818,8 +810,9 @@ static int gic_load(QEMUFile *f, void *opaque, int version_id) int i; int j; - if (version_id != 2) + if (version_id != 3) { return -EINVAL; + } s->enabled = qemu_get_be32(f); for (i = 0; i < NUM_CPU(s); i++) { @@ -837,9 +830,7 @@ static int gic_load(QEMUFile *f, void *opaque, int version_id) s->priority2[i] = qemu_get_be32(f); } for (i = 0; i < s->num_irq; i++) { -#ifndef NVIC s->irq_target[i] = qemu_get_be32(f); -#endif s->irq_state[i].enabled = qemu_get_byte(f); s->irq_state[i].pending = qemu_get_byte(f); s->irq_state[i].active = qemu_get_byte(f); @@ -914,7 +905,7 @@ static void gic_init(gic_state *s, int num_irq) } #endif - register_savevm(NULL, "arm_gic", -1, 2, gic_save, gic_load, s); + register_savevm(NULL, "arm_gic", -1, 3, gic_save, gic_load, s); } #ifndef NVIC From c48c6522f550b9b704f7324164b00b5770ec7345 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 2 May 2012 16:49:39 +0000 Subject: [PATCH 04/16] hw/arm_gic: Remove the special casing of NCPU for the NVIC Drop the special casing of NCPU=1 for the NVIC. This slightly increases the amount of memory used by its state structure, but removes some ifdeffery and means we can safely move the GIC state into a common subclass structure. Signed-off-by: Peter Maydell --- hw/arm_gic.c | 23 +++-------------------- hw/armv7m_nvic.c | 5 ++--- 2 files changed, 5 insertions(+), 23 deletions(-) diff --git a/hw/arm_gic.c b/hw/arm_gic.c index 17b2eba789..2d8ceb80f1 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -25,11 +25,7 @@ /* First 32 are private to each CPU (SGIs and PPIs). */ #define GIC_INTERNAL 32 /* Maximum number of possible CPU interfaces, determined by GIC architecture */ -#ifdef NVIC -#define NCPU 1 -#else #define NCPU 8 -#endif //#define DEBUG_GIC @@ -67,11 +63,7 @@ typedef struct gic_irq_state } gic_irq_state; #define ALL_CPU_MASK ((unsigned)(((1 << NCPU) - 1))) -#if NCPU > 1 #define NUM_CPU(s) ((s)->num_cpu) -#else -#define NUM_CPU(s) 1 -#endif #define GIC_SET_ENABLED(irq, cm) s->irq_state[irq].enabled |= (cm) #define GIC_CLEAR_ENABLED(irq, cm) s->irq_state[irq].enabled &= ~(cm) @@ -131,11 +123,9 @@ typedef struct gic_state static inline int gic_get_current_cpu(gic_state *s) { -#if NCPU > 1 if (s->num_cpu > 1) { return cpu_single_env->cpu_index; } -#endif return 0; } @@ -842,21 +832,14 @@ static int gic_load(QEMUFile *f, void *opaque, int version_id) return 0; } -#if NCPU > 1 -static void gic_init(gic_state *s, int num_cpu, int num_irq) -#else static void gic_init(gic_state *s, int num_irq) -#endif { int i; -#if NCPU > 1 - s->num_cpu = num_cpu; if (s->num_cpu > NCPU) { hw_error("requested %u CPUs exceeds GIC maximum %d\n", - num_cpu, NCPU); + s->num_cpu, NCPU); } -#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", @@ -880,7 +863,7 @@ static void gic_init(gic_state *s, int num_irq) * [N+32..N+63] PPIs for CPU 1 * ... */ - i += (GIC_INTERNAL * num_cpu); + i += (GIC_INTERNAL * s->num_cpu); #endif qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, i); for (i = 0; i < NUM_CPU(s); i++) { @@ -915,7 +898,7 @@ static int arm_gic_init(SysBusDevice *dev) /* Device instance init function for the GIC sysbus device */ int i; gic_state *s = FROM_SYSBUS(gic_state, dev); - gic_init(s, s->num_cpu, s->num_irq); + gic_init(s, s->num_irq); /* Distributor */ sysbus_init_mmio(dev, &s->iomem); /* cpu interfaces (one for "current cpu" plus one per cpu) */ diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c index 986a6bbd0c..99a87a28bf 100644 --- a/hw/armv7m_nvic.c +++ b/hw/armv7m_nvic.c @@ -389,9 +389,8 @@ static int armv7m_nvic_init(SysBusDevice *dev) { nvic_state *s= FROM_SYSBUSGIC(nvic_state, dev); - /* note that for the M profile gic_init() takes the number of external - * interrupt lines only. - */ + /* The NVIC always has only one CPU */ + s->gic.num_cpu = 1; 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); From b3387ede2f2b8a6ba3d5e922492f85a34db6f191 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 2 May 2012 16:49:39 +0000 Subject: [PATCH 05/16] hw/arm_gic: Move NVIC specific reset to armv7m_nvic_reset Move the NVIC specific bits of reset to the NVIC's own reset function, rather than using ifdefs in the common arm_gic reset. Signed-off-by: Peter Maydell --- hw/arm_gic.c | 10 ---------- hw/armv7m_nvic.c | 7 +++++++ 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/hw/arm_gic.c b/hw/arm_gic.c index 2d8ceb80f1..3293ae4c7b 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -743,23 +743,13 @@ static void gic_reset(DeviceState *dev) s->current_pending[i] = 1023; s->running_irq[i] = 1023; s->running_priority[i] = 0x100; -#ifdef NVIC - /* The NVIC doesn't have per-cpu interfaces, so enable by default. */ - s->cpu_enabled[i] = 1; -#else s->cpu_enabled[i] = 0; -#endif } for (i = 0; i < 16; i++) { GIC_SET_ENABLED(i, ALL_CPU_MASK); GIC_SET_TRIGGER(i); } -#ifdef NVIC - /* The NVIC is always enabled. */ - s->enabled = 1; -#else s->enabled = 0; -#endif } static void gic_save(QEMUFile *f, void *opaque) diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c index 99a87a28bf..653c011839 100644 --- a/hw/armv7m_nvic.c +++ b/hw/armv7m_nvic.c @@ -382,6 +382,13 @@ static void armv7m_nvic_reset(DeviceState *dev) { nvic_state *s = FROM_SYSBUSGIC(nvic_state, sysbus_from_qdev(dev)); gic_reset(&s->gic.busdev.qdev); + /* Common GIC reset resets to disabled; the NVIC doesn't have + * per-CPU interfaces so mark our non-existent CPU interface + * as enabled by default. + */ + s->gic.cpu_enabled[0] = 1; + /* The NVIC as a whole is always enabled. */ + s->gic.enabled = 1; systick_reset(s); } From 2a29ddee82029580fa85276767f73fedc30c8a0a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 2 May 2012 16:49:39 +0000 Subject: [PATCH 06/16] hw/armv7m_nvic: Use MemoryRegions for NVIC specific registers Implement the NVIC specific register areas using a set of overlaid MemoryRegions in a container, rather than by having the arm_gic read/write functions use special purpose callbacks. Signed-off-by: Peter Maydell --- hw/arm_gic.c | 33 ++++----------------- hw/armv7m_nvic.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 79 insertions(+), 28 deletions(-) diff --git a/hw/arm_gic.c b/hw/arm_gic.c index 3293ae4c7b..2ec10ce457 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -37,17 +37,17 @@ do { printf("arm_gic: " fmt , ## __VA_ARGS__); } while (0) #endif #ifdef NVIC -static const uint8_t gic_id[] = -{ 0x00, 0xb0, 0x1b, 0x00, 0x0d, 0xe0, 0x05, 0xb1 }; /* The NVIC has 16 internal vectors. However these are not exposed through the normal GIC interface. */ #define GIC_BASE_IRQ 32 #else -static const uint8_t gic_id[] = -{ 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; #define GIC_BASE_IRQ 0 #endif +static const uint8_t gic_id[] = { + 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 +}; + #define FROM_SYSBUSGIC(type, dev) \ DO_UPCAST(type, gic, FROM_SYSBUS(gic_state, dev)) @@ -312,7 +312,6 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) cpu = gic_get_current_cpu(s); cm = 1 << cpu; if (offset < 0x100) { -#ifndef NVIC if (offset == 0) return s->enabled; if (offset == 4) @@ -323,7 +322,6 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) /* Interrupt Security , RAZ/WI */ return 0; } -#endif goto bad_reg; } else if (offset < 0x200) { /* Interrupt Set/Clear Enable. */ @@ -385,6 +383,7 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) } else { res = GIC_TARGET(irq); } +#endif } else if (offset < 0xf00) { /* Interrupt Configuration. */ irq = (offset - 0xc00) * 2 + GIC_BASE_IRQ; @@ -397,7 +396,6 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) if (GIC_TEST_TRIGGER(irq + i)) res |= (2 << (i * 2)); } -#endif } else if (offset < 0xfe0) { goto bad_reg; } else /* offset >= 0xfe0 */ { @@ -424,13 +422,6 @@ static uint32_t gic_dist_readw(void *opaque, target_phys_addr_t offset) static uint32_t gic_dist_readl(void *opaque, target_phys_addr_t offset) { uint32_t val; -#ifdef NVIC - gic_state *s = (gic_state *)opaque; - uint32_t addr; - addr = offset; - if (addr < 0x100 || addr > 0xd00) - return nvic_readl(s, addr); -#endif val = gic_dist_readw(opaque, offset); val |= gic_dist_readw(opaque, offset + 2) << 16; return val; @@ -446,9 +437,6 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, cpu = gic_get_current_cpu(s); if (offset < 0x100) { -#ifdef NVIC - goto bad_reg; -#else if (offset == 0) { s->enabled = (value & 1); DPRINTF("Distribution %sabled\n", s->enabled ? "En" : "Dis"); @@ -459,7 +447,6 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, } else { goto bad_reg; } -#endif } else if (offset < 0x180) { /* Interrupt Set Enable. */ irq = (offset - 0x100) * 8 + GIC_BASE_IRQ; @@ -552,6 +539,7 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, else if (irq < GIC_INTERNAL) value = ALL_CPU_MASK; s->irq_target[irq] = value & ALL_CPU_MASK; +#endif } else if (offset < 0xf00) { /* Interrupt Configuration. */ irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ; @@ -571,7 +559,6 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, GIC_CLEAR_TRIGGER(irq + i); } } -#endif } else { /* 0xf00 is only handled for 32-bit writes. */ goto bad_reg; @@ -593,14 +580,6 @@ static void gic_dist_writel(void *opaque, target_phys_addr_t offset, uint32_t value) { gic_state *s = (gic_state *)opaque; -#ifdef NVIC - uint32_t addr; - addr = offset; - if (addr < 0x100 || (addr > 0xd00 && addr != 0xf00)) { - nvic_writel(s, addr, value); - return; - } -#endif if (offset == 0xf00) { int cpu; int irq; diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c index 653c011839..747e245c2a 100644 --- a/hw/armv7m_nvic.c +++ b/hw/armv7m_nvic.c @@ -30,9 +30,16 @@ typedef struct { int64_t tick; QEMUTimer *timer; } systick; + MemoryRegion sysregmem; + MemoryRegion gic_iomem_alias; + MemoryRegion container; uint32_t num_irq; } nvic_state; +static const uint8_t nvic_id[] = { + 0x00, 0xb0, 0x1b, 0x00, 0x0d, 0xe0, 0x05, 0xb1 +}; + /* qemu timers run at 1GHz. We want something closer to 1MHz. */ #define SYSTICK_SCALE 1000ULL @@ -358,12 +365,54 @@ static void nvic_writel(void *opaque, uint32_t offset, uint32_t value) case 0xd38: /* Bus Fault Address. */ case 0xd3c: /* Aux Fault Status. */ goto bad_reg; + case 0xf00: /* Software Triggered Interrupt Register */ + if ((value & 0x1ff) < s->num_irq) { + gic_set_pending_private(&s->gic, 0, value & 0x1ff); + } + break; default: bad_reg: hw_error("NVIC: Bad write offset 0x%x\n", offset); } } +static uint64_t nvic_sysreg_read(void *opaque, target_phys_addr_t addr, + unsigned size) +{ + /* At the moment we only support the ID registers for byte/word access. + * This is not strictly correct as a few of the other registers also + * allow byte access. + */ + uint32_t offset = addr; + if (offset >= 0xfe0) { + if (offset & 3) { + return 0; + } + return nvic_id[(offset - 0xfe0) >> 2]; + } + if (size == 4) { + return nvic_readl(opaque, offset); + } + hw_error("NVIC: Bad read of size %d at offset 0x%x\n", size, offset); +} + +static void nvic_sysreg_write(void *opaque, target_phys_addr_t addr, + uint64_t value, unsigned size) +{ + uint32_t offset = addr; + if (size == 4) { + nvic_writel(opaque, offset, value); + return; + } + hw_error("NVIC: Bad write of size %d at offset 0x%x\n", size, offset); +} + +static const MemoryRegionOps nvic_sysreg_ops = { + .read = nvic_sysreg_read, + .write = nvic_sysreg_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + static const VMStateDescription vmstate_nvic = { .name = "armv7m_nvic", .version_id = 1, @@ -399,7 +448,30 @@ static int armv7m_nvic_init(SysBusDevice *dev) /* The NVIC always has only one CPU */ s->gic.num_cpu = 1; gic_init(&s->gic, s->num_irq); - memory_region_add_subregion(get_system_memory(), 0xe000e000, &s->gic.iomem); + /* The NVIC and system controller register area looks like this: + * 0..0xff : system control registers, including systick + * 0x100..0xcff : GIC-like registers + * 0xd00..0xfff : system control registers + * We use overlaying to put the GIC like registers + * over the top of the system control register region. + */ + memory_region_init(&s->container, "nvic", 0x1000); + /* The system register region goes at the bottom of the priority + * stack as it covers the whole page. + */ + memory_region_init_io(&s->sysregmem, &nvic_sysreg_ops, s, + "nvic_sysregs", 0x1000); + memory_region_add_subregion(&s->container, 0, &s->sysregmem); + /* Alias the GIC region so we can get only the section of it + * we need, and layer it on top of the system register region. + */ + memory_region_init_alias(&s->gic_iomem_alias, "nvic-gic", &s->gic.iomem, + 0x100, 0xc00); + memory_region_add_subregion_overlap(&s->container, 0x100, &s->gic.iomem, 1); + /* Map the whole thing into system memory at the location required + * by the v7M architecture. + */ + memory_region_add_subregion(get_system_memory(), 0xe000e000, &s->container); s->systick.timer = qemu_new_timer_ns(vm_clock, systick_timer_tick, s); return 0; } From 306a571a2d75e32cd2eae5486c2714b7b7792a63 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 2 May 2012 16:49:40 +0000 Subject: [PATCH 07/16] hw/arm_gic: Add qdev property for GIC revision GIC behaviour can be different between revision 1 and 2 of the architectural GIC specification; we also have to handle the legacy 11MPCore GIC, which is different again in some places. Introduce a qdev property so we can behave appropriately. Signed-off-by: Peter Maydell --- hw/a15mpcore.c | 1 + hw/arm11mpcore.c | 2 ++ hw/arm_gic.c | 10 ++++++++++ hw/armv7m_nvic.c | 2 ++ 4 files changed, 15 insertions(+) diff --git a/hw/a15mpcore.c b/hw/a15mpcore.c index 5a7b365548..fc0a02ae86 100644 --- a/hw/a15mpcore.c +++ b/hw/a15mpcore.c @@ -44,6 +44,7 @@ static int a15mp_priv_init(SysBusDevice *dev) s->gic = qdev_create(NULL, "arm_gic"); qdev_prop_set_uint32(s->gic, "num-cpu", s->num_cpu); qdev_prop_set_uint32(s->gic, "num-irq", s->num_irq); + qdev_prop_set_uint32(s->gic, "revision", 2); qdev_init_nofail(s->gic); busdev = sysbus_from_qdev(s->gic); diff --git a/hw/arm11mpcore.c b/hw/arm11mpcore.c index c528d7aa01..1bff3d3282 100644 --- a/hw/arm11mpcore.c +++ b/hw/arm11mpcore.c @@ -123,6 +123,8 @@ static int mpcore_priv_init(SysBusDevice *dev) s->gic = qdev_create(NULL, "arm_gic"); qdev_prop_set_uint32(s->gic, "num-cpu", s->num_cpu); qdev_prop_set_uint32(s->gic, "num-irq", s->num_irq); + /* Request the legacy 11MPCore GIC behaviour: */ + qdev_prop_set_uint32(s->gic, "revision", 0); qdev_init_nofail(s->gic); /* Pass through outbound IRQ lines from the GIC */ diff --git a/hw/arm_gic.c b/hw/arm_gic.c index 2ec10ce457..ad72ac65a9 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -119,8 +119,13 @@ typedef struct gic_state struct gic_state *backref[NCPU]; MemoryRegion cpuiomem[NCPU+1]; /* CPU interfaces */ uint32_t num_irq; + uint32_t revision; } gic_state; +/* The special cases for the revision property: */ +#define REV_11MPCORE 0 +#define REV_NVIC 0xffffffff + static inline int gic_get_current_cpu(gic_state *s) { if (s->num_cpu > 1) { @@ -880,6 +885,11 @@ static int arm_gic_init(SysBusDevice *dev) static Property arm_gic_properties[] = { DEFINE_PROP_UINT32("num-cpu", gic_state, num_cpu, 1), DEFINE_PROP_UINT32("num-irq", gic_state, num_irq, 32), + /* Revision can be 1 or 2 for GIC architecture specification + * versions 1 or 2, or 0 to indicate the legacy 11MPCore GIC. + * (Internally, 0xffffffff also indicates "not a GIC but an NVIC".) + */ + DEFINE_PROP_UINT32("revision", gic_state, revision, 1), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c index 747e245c2a..4c130f113c 100644 --- a/hw/armv7m_nvic.c +++ b/hw/armv7m_nvic.c @@ -447,6 +447,8 @@ static int armv7m_nvic_init(SysBusDevice *dev) /* The NVIC always has only one CPU */ s->gic.num_cpu = 1; + /* Tell the common code we're an NVIC */ + s->gic.revision = 0xffffffff; gic_init(&s->gic, s->num_irq); /* The NVIC and system controller register area looks like this: * 0..0xff : system control registers, including systick From 6b9680bb5826dcf0ead42bafb62946f472466d63 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 2 May 2012 16:49:40 +0000 Subject: [PATCH 08/16] hw/arm_gic: Make CPU target registers RAZ/WI on uniprocessor The GIC spec says that the CPU target registers should RAZ/WI for uniprocessor implementations. Implement this, which also conveniently lets us drop an NVIC ifdef. Annoyingly, the 11MPCore's GIC is the odd one out, since it always has these registers, even in uniprocessor configs. Signed-off-by: Peter Maydell --- hw/arm_gic.c | 56 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/hw/arm_gic.c b/hw/arm_gic.c index ad72ac65a9..a6e2431c20 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -86,11 +86,7 @@ typedef struct gic_irq_state #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 #define GIC_TARGET(irq) s->irq_target[irq] -#endif typedef struct gic_state { @@ -377,18 +373,22 @@ static uint32_t gic_dist_readb(void *opaque, target_phys_addr_t offset) if (irq >= s->num_irq) goto bad_reg; res = GIC_GET_PRIORITY(irq, cpu); -#ifndef NVIC } else if (offset < 0xc00) { /* Interrupt CPU Target. */ - irq = (offset - 0x800) + GIC_BASE_IRQ; - if (irq >= s->num_irq) - goto bad_reg; - if (irq >= 29 && irq <= 31) { - res = cm; + if (s->num_cpu == 1 && s->revision != REV_11MPCORE) { + /* For uniprocessor GICs these RAZ/WI */ + res = 0; } else { - res = GIC_TARGET(irq); + irq = (offset - 0x800) + GIC_BASE_IRQ; + if (irq >= s->num_irq) { + goto bad_reg; + } + if (irq >= 29 && irq <= 31) { + res = cm; + } else { + res = GIC_TARGET(irq); + } } -#endif } else if (offset < 0xf00) { /* Interrupt Configuration. */ irq = (offset - 0xc00) * 2 + GIC_BASE_IRQ; @@ -533,18 +533,22 @@ static void gic_dist_writeb(void *opaque, target_phys_addr_t offset, } else { s->priority2[irq - GIC_INTERNAL] = value; } -#ifndef NVIC } else if (offset < 0xc00) { - /* Interrupt CPU Target. */ - irq = (offset - 0x800) + GIC_BASE_IRQ; - if (irq >= s->num_irq) - goto bad_reg; - if (irq < 29) - value = 0; - else if (irq < GIC_INTERNAL) - value = ALL_CPU_MASK; - s->irq_target[irq] = value & ALL_CPU_MASK; -#endif + /* Interrupt CPU Target. RAZ/WI on uniprocessor GICs, with the + * annoying exception of the 11MPCore's GIC. + */ + if (s->num_cpu != 1 || s->revision == REV_11MPCORE) { + irq = (offset - 0x800) + GIC_BASE_IRQ; + if (irq >= s->num_irq) { + goto bad_reg; + } + if (irq < 29) { + value = 0; + } else if (irq < GIC_INTERNAL) { + value = ALL_CPU_MASK; + } + s->irq_target[irq] = value & ALL_CPU_MASK; + } } else if (offset < 0xf00) { /* Interrupt Configuration. */ irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ; @@ -733,6 +737,12 @@ static void gic_reset(DeviceState *dev) GIC_SET_ENABLED(i, ALL_CPU_MASK); GIC_SET_TRIGGER(i); } + if (s->num_cpu == 1) { + /* For uniprocessor GICs all interrupts always target the sole CPU */ + for (i = 0; i < GIC_MAXIRQ; i++) { + s->irq_target[i] = 1; + } + } s->enabled = 0; } From 84e4fccb7f63ee2b9ac82c299d29b2a723479e3d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 2 May 2012 16:49:41 +0000 Subject: [PATCH 09/16] hw/arm_gic.c: Make NVIC interrupt numbering a runtime setting Make the minor tweaks to interrupt numbering used by the NVIC a runtime setting rather than a compile time one, so we can drop more NVIC ifdefs. Signed-off-by: Peter Maydell --- hw/arm_gic.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/hw/arm_gic.c b/hw/arm_gic.c index a6e2431c20..c288bc5db8 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -36,13 +36,9 @@ do { printf("arm_gic: " fmt , ## __VA_ARGS__); } while (0) #define DPRINTF(fmt, ...) do {} while(0) #endif -#ifdef NVIC /* The NVIC has 16 internal vectors. However these are not exposed through the normal GIC interface. */ -#define GIC_BASE_IRQ 32 -#else -#define GIC_BASE_IRQ 0 -#endif +#define GIC_BASE_IRQ ((s->revision == REV_NVIC) ? 32 : 0) static const uint8_t gic_id[] = { 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 @@ -839,7 +835,6 @@ static void gic_init(gic_state *s, int num_irq) } i = s->num_irq - GIC_INTERNAL; -#ifndef NVIC /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU. * GPIO array layout is thus: * [0..N-1] SPIs @@ -847,8 +842,9 @@ static void gic_init(gic_state *s, int num_irq) * [N+32..N+63] PPIs for CPU 1 * ... */ - i += (GIC_INTERNAL * s->num_cpu); -#endif + if (s->revision != REV_NVIC) { + i += (GIC_INTERNAL * s->num_cpu); + } qdev_init_gpio_in(&s->busdev.qdev, gic_set_irq, i); for (i = 0; i < NUM_CPU(s); i++) { sysbus_init_irq(&s->busdev, &s->parent_irq[i]); From 2b518c56a6cb7e58477942896bf626f14a112a90 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 2 May 2012 16:49:41 +0000 Subject: [PATCH 10/16] hw/arm_gic: Move CPU interface memory region setup into arm_gic_init Remove more NVIC ifdefs by moving the code to setup the CPU interface memory regions into the GIC specific arm_gic_init() function rather than the gic_init() function. Rename the latter to more closely reflect what it's now actually doing. Signed-off-by: Peter Maydell --- hw/arm_gic.c | 26 +++++++++++++------------- hw/armv7m_nvic.c | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/hw/arm_gic.c b/hw/arm_gic.c index c288bc5db8..ad5ab3c1ba 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -812,7 +812,7 @@ static int gic_load(QEMUFile *f, void *opaque, int version_id) return 0; } -static void gic_init(gic_state *s, int num_irq) +static void gic_init_irqs_and_distributor(gic_state *s, int num_irq) { int i; @@ -850,7 +850,19 @@ static void gic_init(gic_state *s, int num_irq) sysbus_init_irq(&s->busdev, &s->parent_irq[i]); } memory_region_init_io(&s->iomem, &gic_dist_ops, s, "gic_dist", 0x1000); + + register_savevm(NULL, "arm_gic", -1, 3, gic_save, gic_load, s); +} + #ifndef NVIC + +static int arm_gic_init(SysBusDevice *dev) +{ + /* Device instance init function for the GIC sysbus device */ + int i; + gic_state *s = FROM_SYSBUS(gic_state, dev); + gic_init_irqs_and_distributor(s, s->num_irq); + /* Memory regions for the CPU interfaces (NVIC doesn't have these): * a region for "CPU interface for this core", then a region for * "CPU interface for core 0", "for core 1", ... @@ -866,19 +878,7 @@ static void gic_init(gic_state *s, int num_irq) memory_region_init_io(&s->cpuiomem[i+1], &gic_cpu_ops, &s->backref[i], "gic_cpu", 0x100); } -#endif - register_savevm(NULL, "arm_gic", -1, 3, gic_save, gic_load, s); -} - -#ifndef NVIC - -static int arm_gic_init(SysBusDevice *dev) -{ - /* Device instance init function for the GIC sysbus device */ - int i; - gic_state *s = FROM_SYSBUS(gic_state, dev); - gic_init(s, s->num_irq); /* Distributor */ sysbus_init_mmio(dev, &s->iomem); /* cpu interfaces (one for "current cpu" plus one per cpu) */ diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c index 4c130f113c..031a7fd4d5 100644 --- a/hw/armv7m_nvic.c +++ b/hw/armv7m_nvic.c @@ -449,7 +449,7 @@ static int armv7m_nvic_init(SysBusDevice *dev) s->gic.num_cpu = 1; /* Tell the common code we're an NVIC */ s->gic.revision = 0xffffffff; - gic_init(&s->gic, s->num_irq); + gic_init_irqs_and_distributor(&s->gic, s->num_irq); /* The NVIC and system controller register area looks like this: * 0..0xff : system control registers, including systick * 0x100..0xcff : GIC-like registers From 1e8cae4dfea2bcc91d3820dcf4f9284e7b0abb28 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 2 May 2012 16:49:42 +0000 Subject: [PATCH 11/16] hw/armv7m_nvic: Make the NVIC a freestanding class Rearrange the GIC and NVIC so both are straightforward subclasses of a common class, rather than having the NVIC source file textually include arm_gic.c. Signed-off-by: Peter Maydell --- hw/arm/Makefile.objs | 2 +- hw/arm_gic.c | 241 +++--------------------------------------- hw/arm_gic_common.c | 184 ++++++++++++++++++++++++++++++++ hw/arm_gic_internal.h | 136 ++++++++++++++++++++++++ hw/armv7m_nvic.c | 48 ++++++--- 5 files changed, 371 insertions(+), 240 deletions(-) create mode 100644 hw/arm_gic_common.c create mode 100644 hw/arm_gic_internal.h diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index a0ff6a62d6..88ff47d95e 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -6,7 +6,7 @@ obj-y += cadence_uart.o obj-y += cadence_ttc.o obj-y += cadence_gem.o obj-y += xilinx_zynq.o zynq_slcr.o -obj-y += arm_gic.o +obj-y += arm_gic.o arm_gic_common.o obj-y += realview_gic.o realview.o arm_sysctl.o arm11mpcore.o a9mpcore.o obj-y += exynos4210_gic.o exynos4210_combiner.o exynos4210.o obj-y += exynos4_boards.o exynos4210_uart.o exynos4210_pwm.o diff --git a/hw/arm_gic.c b/hw/arm_gic.c index ad5ab3c1ba..ec22322930 100644 --- a/hw/arm_gic.c +++ b/hw/arm_gic.c @@ -19,13 +19,7 @@ */ #include "sysbus.h" - -/* 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 -/* Maximum number of possible CPU interfaces, determined by GIC architecture */ -#define NCPU 8 +#include "arm_gic_internal.h" //#define DEBUG_GIC @@ -36,88 +30,12 @@ do { printf("arm_gic: " fmt , ## __VA_ARGS__); } while (0) #define DPRINTF(fmt, ...) do {} while(0) #endif -/* The NVIC has 16 internal vectors. However these are not exposed - through the normal GIC interface. */ -#define GIC_BASE_IRQ ((s->revision == REV_NVIC) ? 32 : 0) - static const uint8_t gic_id[] = { 0x90, 0x13, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; -#define FROM_SYSBUSGIC(type, dev) \ - DO_UPCAST(type, gic, FROM_SYSBUS(gic_state, dev)) - -typedef struct gic_irq_state -{ - /* The enable bits are only banked for per-cpu interrupts. */ - unsigned enabled:NCPU; - unsigned pending:NCPU; - unsigned active:NCPU; - unsigned level:NCPU; - unsigned model:1; /* 0 = N:N, 1 = 1:N */ - unsigned trigger:1; /* nonzero = edge triggered. */ -} gic_irq_state; - -#define ALL_CPU_MASK ((unsigned)(((1 << NCPU) - 1))) #define NUM_CPU(s) ((s)->num_cpu) -#define GIC_SET_ENABLED(irq, cm) s->irq_state[irq].enabled |= (cm) -#define GIC_CLEAR_ENABLED(irq, cm) s->irq_state[irq].enabled &= ~(cm) -#define GIC_TEST_ENABLED(irq, cm) ((s->irq_state[irq].enabled & (cm)) != 0) -#define GIC_SET_PENDING(irq, cm) s->irq_state[irq].pending |= (cm) -#define GIC_CLEAR_PENDING(irq, cm) s->irq_state[irq].pending &= ~(cm) -#define GIC_TEST_PENDING(irq, cm) ((s->irq_state[irq].pending & (cm)) != 0) -#define GIC_SET_ACTIVE(irq, cm) s->irq_state[irq].active |= (cm) -#define GIC_CLEAR_ACTIVE(irq, cm) s->irq_state[irq].active &= ~(cm) -#define GIC_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0) -#define GIC_SET_MODEL(irq) s->irq_state[irq].model = 1 -#define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = 0 -#define GIC_TEST_MODEL(irq) s->irq_state[irq].model -#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level = (cm) -#define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm) -#define GIC_TEST_LEVEL(irq, cm) ((s->irq_state[irq].level & (cm)) != 0) -#define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1 -#define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0 -#define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger -#define GIC_GET_PRIORITY(irq, cpu) (((irq) < GIC_INTERNAL) ? \ - s->priority1[irq][cpu] : \ - s->priority2[(irq) - GIC_INTERNAL]) -#define GIC_TARGET(irq) s->irq_target[irq] - -typedef struct gic_state -{ - SysBusDevice busdev; - qemu_irq parent_irq[NCPU]; - int enabled; - int cpu_enabled[NCPU]; - - gic_irq_state irq_state[GIC_MAXIRQ]; - int irq_target[GIC_MAXIRQ]; - int priority1[GIC_INTERNAL][NCPU]; - int priority2[GIC_MAXIRQ - GIC_INTERNAL]; - int last_active[GIC_MAXIRQ][NCPU]; - - int priority_mask[NCPU]; - int running_irq[NCPU]; - int running_priority[NCPU]; - int current_pending[NCPU]; - - uint32_t num_cpu; - - MemoryRegion iomem; /* Distributor */ - /* This is just so we can have an opaque pointer which identifies - * both this GIC and which CPU interface we should be accessing. - */ - struct gic_state *backref[NCPU]; - MemoryRegion cpuiomem[NCPU+1]; /* CPU interfaces */ - uint32_t num_irq; - uint32_t revision; -} gic_state; - -/* The special cases for the revision property: */ -#define REV_11MPCORE 0 -#define REV_NVIC 0xffffffff - static inline int gic_get_current_cpu(gic_state *s) { if (s->num_cpu > 1) { @@ -128,7 +46,7 @@ static inline int gic_get_current_cpu(gic_state *s) /* TODO: Many places that call this routine could be optimized. */ /* Update interrupt status after enabled or pending bits have been changed. */ -static void gic_update(gic_state *s) +void gic_update(gic_state *s) { int best_irq; int best_prio; @@ -166,8 +84,7 @@ static void gic_update(gic_state *s) } } -#ifdef NVIC -static void gic_set_pending_private(gic_state *s, int cpu, int irq) +void gic_set_pending_private(gic_state *s, int cpu, int irq) { int cm = 1 << cpu; @@ -178,7 +95,6 @@ static void gic_set_pending_private(gic_state *s, int cpu, int irq) GIC_SET_PENDING(irq, cm); gic_update(s); } -#endif /* Process a change in an external IRQ input. */ static void gic_set_irq(void *opaque, int irq, int level) @@ -232,7 +148,7 @@ static void gic_set_running_irq(gic_state *s, int cpu, int irq) gic_update(s); } -static uint32_t gic_acknowledge_irq(gic_state *s, int cpu) +uint32_t gic_acknowledge_irq(gic_state *s, int cpu) { int new_irq; int cm = 1 << cpu; @@ -251,7 +167,7 @@ static uint32_t gic_acknowledge_irq(gic_state *s, int cpu) return new_irq; } -static void gic_complete_irq(gic_state * s, int cpu, int irq) +void gic_complete_irq(gic_state *s, int cpu, int irq) { int update = 0; int cm = 1 << cpu; @@ -623,7 +539,6 @@ static const MemoryRegionOps gic_dist_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -#ifndef NVIC static uint32_t gic_cpu_read(gic_state *s, int cpu, int offset) { switch (offset) { @@ -715,125 +630,11 @@ static const MemoryRegionOps gic_cpu_ops = { .write = gic_do_cpu_write, .endianness = DEVICE_NATIVE_ENDIAN, }; -#endif -static void gic_reset(DeviceState *dev) -{ - gic_state *s = FROM_SYSBUS(gic_state, sysbus_from_qdev(dev)); - int i; - memset(s->irq_state, 0, GIC_MAXIRQ * sizeof(gic_irq_state)); - for (i = 0 ; i < NUM_CPU(s); i++) { - s->priority_mask[i] = 0xf0; - s->current_pending[i] = 1023; - s->running_irq[i] = 1023; - s->running_priority[i] = 0x100; - s->cpu_enabled[i] = 0; - } - for (i = 0; i < 16; i++) { - GIC_SET_ENABLED(i, ALL_CPU_MASK); - GIC_SET_TRIGGER(i); - } - if (s->num_cpu == 1) { - /* For uniprocessor GICs all interrupts always target the sole CPU */ - for (i = 0; i < GIC_MAXIRQ; i++) { - s->irq_target[i] = 1; - } - } - s->enabled = 0; -} - -static void gic_save(QEMUFile *f, void *opaque) -{ - gic_state *s = (gic_state *)opaque; - int i; - int j; - - 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 < 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]); - qemu_put_be32(f, s->priority_mask[i]); - qemu_put_be32(f, s->running_irq[i]); - qemu_put_be32(f, s->running_priority[i]); - qemu_put_be32(f, s->current_pending[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++) { - qemu_put_be32(f, s->irq_target[i]); - qemu_put_byte(f, s->irq_state[i].enabled); - qemu_put_byte(f, s->irq_state[i].pending); - qemu_put_byte(f, s->irq_state[i].active); - qemu_put_byte(f, s->irq_state[i].level); - qemu_put_byte(f, s->irq_state[i].model); - qemu_put_byte(f, s->irq_state[i].trigger); - } -} - -static int gic_load(QEMUFile *f, void *opaque, int version_id) -{ - gic_state *s = (gic_state *)opaque; - int i; - int j; - - if (version_id != 3) { - return -EINVAL; - } - - 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 < 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); - s->priority_mask[i] = qemu_get_be32(f); - s->running_irq[i] = qemu_get_be32(f); - s->running_priority[i] = qemu_get_be32(f); - s->current_pending[i] = qemu_get_be32(f); - } - 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++) { - s->irq_target[i] = qemu_get_be32(f); - s->irq_state[i].enabled = qemu_get_byte(f); - s->irq_state[i].pending = qemu_get_byte(f); - s->irq_state[i].active = qemu_get_byte(f); - s->irq_state[i].level = qemu_get_byte(f); - s->irq_state[i].model = qemu_get_byte(f); - s->irq_state[i].trigger = qemu_get_byte(f); - } - - return 0; -} - -static void gic_init_irqs_and_distributor(gic_state *s, int num_irq) +void gic_init_irqs_and_distributor(gic_state *s, int num_irq) { int i; - if (s->num_cpu > NCPU) { - hw_error("requested %u CPUs exceeds GIC maximum %d\n", - s->num_cpu, NCPU); - } - 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); - } - /* 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); - } - i = s->num_irq - GIC_INTERNAL; /* For the GIC, also expose incoming GPIO lines for PPIs for each CPU. * GPIO array layout is thus: @@ -850,17 +651,17 @@ static void gic_init_irqs_and_distributor(gic_state *s, int num_irq) sysbus_init_irq(&s->busdev, &s->parent_irq[i]); } memory_region_init_io(&s->iomem, &gic_dist_ops, s, "gic_dist", 0x1000); - - register_savevm(NULL, "arm_gic", -1, 3, gic_save, gic_load, s); } -#ifndef NVIC - static int arm_gic_init(SysBusDevice *dev) { /* Device instance init function for the GIC sysbus device */ int i; gic_state *s = FROM_SYSBUS(gic_state, dev); + ARMGICClass *agc = ARM_GIC_GET_CLASS(s); + + agc->parent_init(dev); + gic_init_irqs_and_distributor(s, s->num_irq); /* Memory regions for the CPU interfaces (NVIC doesn't have these): @@ -878,7 +679,6 @@ static int arm_gic_init(SysBusDevice *dev) memory_region_init_io(&s->cpuiomem[i+1], &gic_cpu_ops, &s->backref[i], "gic_cpu", 0x100); } - /* Distributor */ sysbus_init_mmio(dev, &s->iomem); /* cpu interfaces (one for "current cpu" plus one per cpu) */ @@ -888,30 +688,19 @@ static int arm_gic_init(SysBusDevice *dev) return 0; } -static Property arm_gic_properties[] = { - DEFINE_PROP_UINT32("num-cpu", gic_state, num_cpu, 1), - DEFINE_PROP_UINT32("num-irq", gic_state, num_irq, 32), - /* Revision can be 1 or 2 for GIC architecture specification - * versions 1 or 2, or 0 to indicate the legacy 11MPCore GIC. - * (Internally, 0xffffffff also indicates "not a GIC but an NVIC".) - */ - DEFINE_PROP_UINT32("revision", gic_state, revision, 1), - DEFINE_PROP_END_OF_LIST(), -}; - static void arm_gic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); + ARMGICClass *agc = ARM_GIC_CLASS(klass); + agc->parent_init = sbc->init; sbc->init = arm_gic_init; - dc->props = arm_gic_properties; - dc->reset = gic_reset; dc->no_user = 1; } static TypeInfo arm_gic_info = { - .name = "arm_gic", - .parent = TYPE_SYS_BUS_DEVICE, + .name = TYPE_ARM_GIC, + .parent = TYPE_ARM_GIC_COMMON, .instance_size = sizeof(gic_state), .class_init = arm_gic_class_init, }; @@ -922,5 +711,3 @@ static void arm_gic_register_types(void) } type_init(arm_gic_register_types) - -#endif diff --git a/hw/arm_gic_common.c b/hw/arm_gic_common.c new file mode 100644 index 0000000000..360e7823f7 --- /dev/null +++ b/hw/arm_gic_common.c @@ -0,0 +1,184 @@ +/* + * ARM GIC support - common bits of emulated and KVM kernel model + * + * Copyright (c) 2012 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "arm_gic_internal.h" + +static void gic_save(QEMUFile *f, void *opaque) +{ + gic_state *s = (gic_state *)opaque; + int i; + int j; + + qemu_put_be32(f, s->enabled); + for (i = 0; i < s->num_cpu; i++) { + qemu_put_be32(f, s->cpu_enabled[i]); + 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]); + } + qemu_put_be32(f, s->priority_mask[i]); + qemu_put_be32(f, s->running_irq[i]); + qemu_put_be32(f, s->running_priority[i]); + qemu_put_be32(f, s->current_pending[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++) { + qemu_put_be32(f, s->irq_target[i]); + qemu_put_byte(f, s->irq_state[i].enabled); + qemu_put_byte(f, s->irq_state[i].pending); + qemu_put_byte(f, s->irq_state[i].active); + qemu_put_byte(f, s->irq_state[i].level); + qemu_put_byte(f, s->irq_state[i].model); + qemu_put_byte(f, s->irq_state[i].trigger); + } +} + +static int gic_load(QEMUFile *f, void *opaque, int version_id) +{ + gic_state *s = (gic_state *)opaque; + int i; + int j; + + if (version_id != 3) { + return -EINVAL; + } + + s->enabled = qemu_get_be32(f); + for (i = 0; i < s->num_cpu; i++) { + s->cpu_enabled[i] = qemu_get_be32(f); + 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); + } + s->priority_mask[i] = qemu_get_be32(f); + s->running_irq[i] = qemu_get_be32(f); + s->running_priority[i] = qemu_get_be32(f); + s->current_pending[i] = qemu_get_be32(f); + } + 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++) { + s->irq_target[i] = qemu_get_be32(f); + s->irq_state[i].enabled = qemu_get_byte(f); + s->irq_state[i].pending = qemu_get_byte(f); + s->irq_state[i].active = qemu_get_byte(f); + s->irq_state[i].level = qemu_get_byte(f); + s->irq_state[i].model = qemu_get_byte(f); + s->irq_state[i].trigger = qemu_get_byte(f); + } + + return 0; +} + +static int arm_gic_common_init(SysBusDevice *dev) +{ + gic_state *s = FROM_SYSBUS(gic_state, dev); + int num_irq = s->num_irq; + + if (s->num_cpu > NCPU) { + hw_error("requested %u CPUs exceeds GIC maximum %d\n", + s->num_cpu, NCPU); + } + s->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); + } + /* 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); + } + + register_savevm(NULL, "arm_gic", -1, 3, gic_save, gic_load, s); + return 0; +} + +static void arm_gic_common_reset(DeviceState *dev) +{ + gic_state *s = FROM_SYSBUS(gic_state, sysbus_from_qdev(dev)); + int i; + memset(s->irq_state, 0, GIC_MAXIRQ * sizeof(gic_irq_state)); + for (i = 0 ; i < s->num_cpu; i++) { + s->priority_mask[i] = 0xf0; + s->current_pending[i] = 1023; + s->running_irq[i] = 1023; + s->running_priority[i] = 0x100; + s->cpu_enabled[i] = 0; + } + for (i = 0; i < 16; i++) { + GIC_SET_ENABLED(i, ALL_CPU_MASK); + GIC_SET_TRIGGER(i); + } + if (s->num_cpu == 1) { + /* For uniprocessor GICs all interrupts always target the sole CPU */ + for (i = 0; i < GIC_MAXIRQ; i++) { + s->irq_target[i] = 1; + } + } + s->enabled = 0; +} + +static Property arm_gic_common_properties[] = { + DEFINE_PROP_UINT32("num-cpu", gic_state, num_cpu, 1), + DEFINE_PROP_UINT32("num-irq", gic_state, num_irq, 32), + /* Revision can be 1 or 2 for GIC architecture specification + * versions 1 or 2, or 0 to indicate the legacy 11MPCore GIC. + * (Internally, 0xffffffff also indicates "not a GIC but an NVIC".) + */ + DEFINE_PROP_UINT32("revision", gic_state, revision, 1), + DEFINE_PROP_END_OF_LIST(), +}; + +static void arm_gic_common_class_init(ObjectClass *klass, void *data) +{ + SysBusDeviceClass *sc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + dc->reset = arm_gic_common_reset; + dc->props = arm_gic_common_properties; + dc->no_user = 1; + sc->init = arm_gic_common_init; +} + +static TypeInfo arm_gic_common_type = { + .name = TYPE_ARM_GIC_COMMON, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(gic_state), + .class_size = sizeof(ARMGICCommonClass), + .class_init = arm_gic_common_class_init, + .abstract = true, +}; + +static void register_types(void) +{ + type_register_static(&arm_gic_common_type); +} + +type_init(register_types) diff --git a/hw/arm_gic_internal.h b/hw/arm_gic_internal.h new file mode 100644 index 0000000000..db4fad564f --- /dev/null +++ b/hw/arm_gic_internal.h @@ -0,0 +1,136 @@ +/* + * ARM GIC support - internal interfaces + * + * Copyright (c) 2012 Linaro Limited + * Written by Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef QEMU_ARM_GIC_INTERNAL_H +#define QEMU_ARM_GIC_INTERNAL_H + +#include "sysbus.h" + +/* 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 +/* Maximum number of possible CPU interfaces, determined by GIC architecture */ +#define NCPU 8 + +#define ALL_CPU_MASK ((unsigned)(((1 << NCPU) - 1))) + +/* The NVIC has 16 internal vectors. However these are not exposed + through the normal GIC interface. */ +#define GIC_BASE_IRQ ((s->revision == REV_NVIC) ? 32 : 0) + +#define GIC_SET_ENABLED(irq, cm) s->irq_state[irq].enabled |= (cm) +#define GIC_CLEAR_ENABLED(irq, cm) s->irq_state[irq].enabled &= ~(cm) +#define GIC_TEST_ENABLED(irq, cm) ((s->irq_state[irq].enabled & (cm)) != 0) +#define GIC_SET_PENDING(irq, cm) s->irq_state[irq].pending |= (cm) +#define GIC_CLEAR_PENDING(irq, cm) s->irq_state[irq].pending &= ~(cm) +#define GIC_TEST_PENDING(irq, cm) ((s->irq_state[irq].pending & (cm)) != 0) +#define GIC_SET_ACTIVE(irq, cm) s->irq_state[irq].active |= (cm) +#define GIC_CLEAR_ACTIVE(irq, cm) s->irq_state[irq].active &= ~(cm) +#define GIC_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0) +#define GIC_SET_MODEL(irq) s->irq_state[irq].model = 1 +#define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = 0 +#define GIC_TEST_MODEL(irq) s->irq_state[irq].model +#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level = (cm) +#define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm) +#define GIC_TEST_LEVEL(irq, cm) ((s->irq_state[irq].level & (cm)) != 0) +#define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = 1 +#define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = 0 +#define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger +#define GIC_GET_PRIORITY(irq, cpu) (((irq) < GIC_INTERNAL) ? \ + s->priority1[irq][cpu] : \ + s->priority2[(irq) - GIC_INTERNAL]) +#define GIC_TARGET(irq) s->irq_target[irq] + +typedef struct gic_irq_state { + /* The enable bits are only banked for per-cpu interrupts. */ + unsigned enabled:NCPU; + unsigned pending:NCPU; + unsigned active:NCPU; + unsigned level:NCPU; + unsigned model:1; /* 0 = N:N, 1 = 1:N */ + unsigned trigger:1; /* nonzero = edge triggered. */ +} gic_irq_state; + +typedef struct gic_state { + SysBusDevice busdev; + qemu_irq parent_irq[NCPU]; + int enabled; + int cpu_enabled[NCPU]; + + gic_irq_state irq_state[GIC_MAXIRQ]; + int irq_target[GIC_MAXIRQ]; + int priority1[GIC_INTERNAL][NCPU]; + int priority2[GIC_MAXIRQ - GIC_INTERNAL]; + int last_active[GIC_MAXIRQ][NCPU]; + + int priority_mask[NCPU]; + int running_irq[NCPU]; + int running_priority[NCPU]; + int current_pending[NCPU]; + + uint32_t num_cpu; + + MemoryRegion iomem; /* Distributor */ + /* This is just so we can have an opaque pointer which identifies + * both this GIC and which CPU interface we should be accessing. + */ + struct gic_state *backref[NCPU]; + MemoryRegion cpuiomem[NCPU+1]; /* CPU interfaces */ + uint32_t num_irq; + uint32_t revision; +} gic_state; + +/* The special cases for the revision property: */ +#define REV_11MPCORE 0 +#define REV_NVIC 0xffffffff + +void gic_set_pending_private(gic_state *s, int cpu, int irq); +uint32_t gic_acknowledge_irq(gic_state *s, int cpu); +void gic_complete_irq(gic_state *s, int cpu, int irq); +void gic_update(gic_state *s); +void gic_init_irqs_and_distributor(gic_state *s, int num_irq); + +#define TYPE_ARM_GIC_COMMON "arm_gic_common" +#define ARM_GIC_COMMON(obj) \ + OBJECT_CHECK(gic_state, (obj), TYPE_ARM_GIC_COMMON) +#define ARM_GIC_COMMON_CLASS(klass) \ + OBJECT_CLASS_CHECK(ARMGICCommonClass, (klass), TYPE_ARM_GIC_COMMON) +#define ARM_GIC_COMMON_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ARMGICCommonClass, (obj), TYPE_ARM_GIC_COMMON) + +typedef struct ARMGICCommonClass { + SysBusDeviceClass parent_class; +} ARMGICCommonClass; + +#define TYPE_ARM_GIC "arm_gic" +#define ARM_GIC(obj) \ + OBJECT_CHECK(gic_state, (obj), TYPE_ARM_GIC) +#define ARM_GIC_CLASS(klass) \ + OBJECT_CLASS_CHECK(ARMGICClass, (klass), TYPE_ARM_GIC) +#define ARM_GIC_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ARMGICClass, (obj), TYPE_ARM_GIC) + +typedef struct ARMGICClass { + ARMGICCommonClass parent_class; + int (*parent_init)(SysBusDevice *dev); +} ARMGICClass; + +#endif /* !QEMU_ARM_GIC_INTERNAL_H */ diff --git a/hw/armv7m_nvic.c b/hw/armv7m_nvic.c index 031a7fd4d5..4867c1d5fa 100644 --- a/hw/armv7m_nvic.c +++ b/hw/armv7m_nvic.c @@ -14,13 +14,7 @@ #include "qemu-timer.h" #include "arm-misc.h" #include "exec-memory.h" - -#define NVIC 1 - -static uint32_t nvic_readl(void *opaque, uint32_t offset); -static void nvic_writel(void *opaque, uint32_t offset, uint32_t value); - -#include "arm_gic.c" +#include "arm_gic_internal.h" typedef struct { gic_state gic; @@ -36,6 +30,28 @@ typedef struct { uint32_t num_irq; } nvic_state; +#define TYPE_NVIC "armv7m_nvic" +/** + * NVICClass: + * @parent_reset: the parent class' reset handler. + * + * A model of the v7M NVIC and System Controller + */ +typedef struct NVICClass { + /*< private >*/ + ARMGICClass parent_class; + /*< public >*/ + int (*parent_init)(SysBusDevice *dev); + void (*parent_reset)(DeviceState *dev); +} NVICClass; + +#define NVIC_CLASS(klass) \ + OBJECT_CLASS_CHECK(NVICClass, (klass), TYPE_NVIC) +#define NVIC_GET_CLASS(obj) \ + OBJECT_GET_CLASS(NVICClass, (obj), TYPE_NVIC) +#define NVIC(obj) \ + OBJECT_CHECK(nvic_state, (obj), TYPE_NVIC) + static const uint8_t nvic_id[] = { 0x00, 0xb0, 0x1b, 0x00, 0x0d, 0xe0, 0x05, 0xb1 }; @@ -429,8 +445,9 @@ static const VMStateDescription vmstate_nvic = { static void armv7m_nvic_reset(DeviceState *dev) { - nvic_state *s = FROM_SYSBUSGIC(nvic_state, sysbus_from_qdev(dev)); - gic_reset(&s->gic.busdev.qdev); + nvic_state *s = NVIC(dev); + NVICClass *nc = NVIC_GET_CLASS(s); + nc->parent_reset(dev); /* Common GIC reset resets to disabled; the NVIC doesn't have * per-CPU interfaces so mark our non-existent CPU interface * as enabled by default. @@ -443,12 +460,15 @@ static void armv7m_nvic_reset(DeviceState *dev) static int armv7m_nvic_init(SysBusDevice *dev) { - nvic_state *s= FROM_SYSBUSGIC(nvic_state, dev); + nvic_state *s = NVIC(dev); + NVICClass *nc = NVIC_GET_CLASS(s); /* The NVIC always has only one CPU */ s->gic.num_cpu = 1; /* Tell the common code we're an NVIC */ s->gic.revision = 0xffffffff; + s->gic.num_irq = s->num_irq; + nc->parent_init(dev); gic_init_irqs_and_distributor(&s->gic, s->num_irq); /* The NVIC and system controller register area looks like this: * 0..0xff : system control registers, including systick @@ -489,9 +509,12 @@ static Property armv7m_nvic_properties[] = { static void armv7m_nvic_class_init(ObjectClass *klass, void *data) { + NVICClass *nc = NVIC_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + nc->parent_reset = dc->reset; + nc->parent_init = sdc->init; sdc->init = armv7m_nvic_init; dc->vmsd = &vmstate_nvic; dc->reset = armv7m_nvic_reset; @@ -499,10 +522,11 @@ static void armv7m_nvic_class_init(ObjectClass *klass, void *data) } static TypeInfo armv7m_nvic_info = { - .name = "armv7m_nvic", - .parent = TYPE_SYS_BUS_DEVICE, + .name = TYPE_NVIC, + .parent = TYPE_ARM_GIC_COMMON, .instance_size = sizeof(nvic_state), .class_init = armv7m_nvic_class_init, + .class_size = sizeof(NVICClass), }; static void armv7m_nvic_register_types(void) From cff0cfbed5b8c41938af3d134381ea76ee0242f7 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 15 May 2012 16:46:26 +0000 Subject: [PATCH 12/16] hw/omap.h: Drop broken MEM_VERBOSE tracing Remove the MEM_VERBOSE tracing option from omap.h. This worked by intercepting cpu_register_io_memory() calls; it has been broken since cpu_register_io_memory() was removed in favour of the MemoryRegion API. Signed-off-by: Peter Maydell --- hw/omap.h | 95 ------------------------------------------------------- 1 file changed, 95 deletions(-) diff --git a/hw/omap.h b/hw/omap.h index 2819e5df9a..3d98941b72 100644 --- a/hw/omap.h +++ b/hw/omap.h @@ -998,7 +998,6 @@ enum { #define OMAP_GPIOSW_OUTPUT 0x0002 # define TCMI_VERBOSE 1 -//# define MEM_VERBOSE 1 # ifdef TCMI_VERBOSE # define OMAP_8B_REG(paddr) \ @@ -1018,98 +1017,4 @@ enum { # define OMAP_MPUI_REG_MASK 0x000007ff -# ifdef MEM_VERBOSE -struct io_fn { - CPUReadMemoryFunc * const *mem_read; - CPUWriteMemoryFunc * const *mem_write; - void *opaque; - int in; -}; - -static uint32_t io_readb(void *opaque, target_phys_addr_t addr) -{ - struct io_fn *s = opaque; - uint32_t ret; - - s->in ++; - ret = s->mem_read[0](s->opaque, addr); - s->in --; - if (!s->in) - fprintf(stderr, "%08x ---> %02x\n", (uint32_t) addr, ret); - return ret; -} -static uint32_t io_readh(void *opaque, target_phys_addr_t addr) -{ - struct io_fn *s = opaque; - uint32_t ret; - - s->in ++; - ret = s->mem_read[1](s->opaque, addr); - s->in --; - if (!s->in) - fprintf(stderr, "%08x ---> %04x\n", (uint32_t) addr, ret); - return ret; -} -static uint32_t io_readw(void *opaque, target_phys_addr_t addr) -{ - struct io_fn *s = opaque; - uint32_t ret; - - s->in ++; - ret = s->mem_read[2](s->opaque, addr); - s->in --; - if (!s->in) - fprintf(stderr, "%08x ---> %08x\n", (uint32_t) addr, ret); - return ret; -} -static void io_writeb(void *opaque, target_phys_addr_t addr, uint32_t value) -{ - struct io_fn *s = opaque; - - if (!s->in) - fprintf(stderr, "%08x <--- %02x\n", (uint32_t) addr, value); - s->in ++; - s->mem_write[0](s->opaque, addr, value); - s->in --; -} -static void io_writeh(void *opaque, target_phys_addr_t addr, uint32_t value) -{ - struct io_fn *s = opaque; - - if (!s->in) - fprintf(stderr, "%08x <--- %04x\n", (uint32_t) addr, value); - s->in ++; - s->mem_write[1](s->opaque, addr, value); - s->in --; -} -static void io_writew(void *opaque, target_phys_addr_t addr, uint32_t value) -{ - struct io_fn *s = opaque; - - if (!s->in) - fprintf(stderr, "%08x <--- %08x\n", (uint32_t) addr, value); - s->in ++; - s->mem_write[2](s->opaque, addr, value); - s->in --; -} - -static CPUReadMemoryFunc * const io_readfn[] = { io_readb, io_readh, io_readw, }; -static CPUWriteMemoryFunc * const io_writefn[] = { io_writeb, io_writeh, io_writew, }; - -inline static int debug_register_io_memory(CPUReadMemoryFunc * const *mem_read, - CPUWriteMemoryFunc * const *mem_write, - void *opaque) -{ - struct io_fn *s = g_malloc(sizeof(struct io_fn)); - - s->mem_read = mem_read; - s->mem_write = mem_write; - s->opaque = opaque; - s->in = 0; - return cpu_register_io_memory(io_readfn, io_writefn, s, - DEVICE_NATIVE_ENDIAN); -} -# define cpu_register_io_memory debug_register_io_memory -# endif - #endif /* hw_omap_h */ From c97338dca0197abad7f0c789ad61d45940f67011 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 22 May 2012 14:29:52 +0000 Subject: [PATCH 13/16] hw/a9mpcore: Fix compilation failure if physaddrs are 64 bit Add a cast to a logging printf to avoid a compilation failure if target_phys_addr_t is a 64 bit type. (This is better than using TARGET_FMT_plx because we really don't need a full 16 digit hex string to print the offset into a device.) Signed-off-by: Peter Maydell Reviewed-by: Eric Blake --- hw/a9mpcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/a9mpcore.c b/hw/a9mpcore.c index c2ff74d4b6..ebd5b29173 100644 --- a/hw/a9mpcore.c +++ b/hw/a9mpcore.c @@ -75,7 +75,7 @@ static void a9_scu_write(void *opaque, target_phys_addr_t offset, break; default: fprintf(stderr, "Invalid size %u in write to a9 scu register %x\n", - size, offset); + size, (unsigned)offset); return; } From 5fbe02e8bb7c62ee55b8edc5fd688c369164c49c Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Thu, 10 May 2012 06:19:48 +0000 Subject: [PATCH 14/16] cadence_gem: avoid stack-writing buffer-overrun Use sizeof(rxbuf)-size (not sizeof(rxbuf-size)) as the number of bytes to clear. The latter would always clear 4 or 8 bytes, possibly writing beyond the end of that stack buffer. Alternatively, depending on the value of the "size" parameter, it could fail to initialize the end of "rxbuf". Spotted by coverity. Signed-off-by: Jim Meyering Reviewed-by: Peter A.G. Crosthwaite Signed-off-by: Peter Maydell --- hw/cadence_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/cadence_gem.c b/hw/cadence_gem.c index e2140aea2b..dbde3920d0 100644 --- a/hw/cadence_gem.c +++ b/hw/cadence_gem.c @@ -664,7 +664,7 @@ static ssize_t gem_receive(VLANClientState *nc, const uint8_t *buf, size_t size) */ memcpy(rxbuf, buf, size); - memset(rxbuf + size, 0, sizeof(rxbuf - size)); + memset(rxbuf + size, 0, sizeof(rxbuf) - size); rxbuf_ptr = rxbuf; crc_val = cpu_to_le32(crc32(0, rxbuf, MAX(size, 60))); if (size < 60) { From 69efc0265f3447a891fabe941208b1e11d6091af Mon Sep 17 00:00:00 2001 From: "Peter A. G. Crosthwaite" Date: Mon, 28 May 2012 17:16:28 +0000 Subject: [PATCH 15/16] cadence_ttc: changed master clock frequency Change the timer clock frequency to 133MHz which is correct. the old 2.5MHz value was for the pre-silicon emulation platform. Signed-off-by: Peter A. G. Crosthwaite Signed-off-by: Peter Maydell --- hw/cadence_ttc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/cadence_ttc.c b/hw/cadence_ttc.c index 2b5477b688..dd02f86eb9 100644 --- a/hw/cadence_ttc.c +++ b/hw/cadence_ttc.c @@ -405,7 +405,7 @@ static int cadence_ttc_init(SysBusDevice *dev) int i; for (i = 0; i < 3; ++i) { - cadence_timer_init(2500000, &s->timer[i]); + cadence_timer_init(133000000, &s->timer[i]); sysbus_init_irq(dev, &s->timer[i].irq); } From 5e87975c87b35c66be3f33080c0e5cf6c6d451a5 Mon Sep 17 00:00:00 2001 From: "Peter A. G. Crosthwaite" Date: Sun, 17 Jun 2012 15:35:36 +0000 Subject: [PATCH 16/16] arm_boot: Conditionalised DTB command line update The DTB command line should only be overwritten if the user provides a command line with -append. Otherwise whatever command line was in the DTB should stay unchanged. Signed-off-by: Peter A. G. Crosthwaite Signed-off-by: Peter Maydell --- hw/arm_boot.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/hw/arm_boot.c b/hw/arm_boot.c index d0e643ba11..a1e6ddbc1c 100644 --- a/hw/arm_boot.c +++ b/hw/arm_boot.c @@ -242,10 +242,12 @@ static int load_dtb(target_phys_addr_t addr, const struct arm_boot_info *binfo) fprintf(stderr, "couldn't set /memory/reg\n"); } - rc = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", - binfo->kernel_cmdline); - if (rc < 0) { - fprintf(stderr, "couldn't set /chosen/bootargs\n"); + if (binfo->kernel_cmdline && *binfo->kernel_cmdline) { + rc = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", + binfo->kernel_cmdline); + if (rc < 0) { + fprintf(stderr, "couldn't set /chosen/bootargs\n"); + } } if (binfo->initrd_size) {