diff options
author | Peter Maydell | 2017-02-28 15:50:15 +0100 |
---|---|---|
committer | Peter Maydell | 2017-02-28 15:50:17 +0100 |
commit | 1bbe5dc66b770d7bedd1d51d7935da948a510dd6 (patch) | |
tree | 4fd4e08fe462461373606d16d7e3ccc397f8d2dc /hw/intc | |
parent | Merge remote-tracking branch 'remotes/cody/tags/block-pull-request' into staging (diff) | |
parent | hw/arm/exynos: Fix proper mapping of CPUs by providing real cluster ID (diff) | |
download | qemu-1bbe5dc66b770d7bedd1d51d7935da948a510dd6.tar.gz qemu-1bbe5dc66b770d7bedd1d51d7935da948a510dd6.tar.xz qemu-1bbe5dc66b770d7bedd1d51d7935da948a510dd6.zip |
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20170228' into staging
target-arm queue:
* raspi2: implement RNG module
* raspi2: implement new SD card controller (but don't wire it up)
* sdhci: bugfixes for block transfers
* virt: fix cpu object reference leak
* Add missing fp_access_check() to aarch64 crypto instructions
* cputlb: Don't assume do_unassigned_access() never returns
* virt: Add a user option to disallow ITS instantiation
* i.MX timers: fix reset handling
* ARMv7M NVIC: rewrite to fix broken priority handling and masking
* exynos: Fix proper mapping of CPUs by providing real cluster ID
* exynos: Fix Linux kernel division by zero for PLLs
# gpg: Signature made Tue 28 Feb 2017 12:40:51 GMT
# gpg: using RSA key 0x3C2525ED14360CDE
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>"
# gpg: aka "Peter Maydell <pmaydell@gmail.com>"
# gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>"
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE
* remotes/pmaydell/tags/pull-target-arm-20170228: (27 commits)
hw/arm/exynos: Fix proper mapping of CPUs by providing real cluster ID
hw/arm/exynos: Fix Linux kernel division by zero for PLLs
bcm2835_sdhost: add bcm2835 sdhost controller
armv7m: Allow SHCSR writes to change pending and active bits
armv7m: Raise correct kind of UsageFault for attempts to execute ARM code
armv7m: Check exception return consistency
armv7m: Extract "exception taken" code into functions
armv7m: VECTCLRACTIVE and VECTRESET are UNPREDICTABLE
armv7m: Simpler and faster exception start
armv7m: Remove unused armv7m_nvic_acknowledge_irq() return value
armv7m: Escalate exceptions to HardFault if necessary
arm: gic: Remove references to NVIC
armv7m: Fix condition check for taking exceptions
armv7m: Rewrite NVIC to not use any GIC code
armv7m: Implement reading and writing of PRIGROUP
armv7m: Rename nvic_state to NVICState
ARM i.MX timers: fix reset handling
hw/arm/virt: Add a user option to disallow ITS instantiation
cputlb: Don't assume do_unassigned_access() never returns
Add missing fp_access_check() to aarch64 crypto instructions
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/intc')
-rw-r--r-- | hw/intc/arm_gic.c | 31 | ||||
-rw-r--r-- | hw/intc/arm_gic_common.c | 23 | ||||
-rw-r--r-- | hw/intc/armv7m_nvic.c | 885 | ||||
-rw-r--r-- | hw/intc/gic_internal.h | 7 | ||||
-rw-r--r-- | hw/intc/trace-events | 15 |
5 files changed, 739 insertions, 222 deletions
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 521aac3cc6..8e5a9d8a3e 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -156,17 +156,6 @@ static void gic_set_irq_11mpcore(GICState *s, int irq, int level, } } -static void gic_set_irq_nvic(GICState *s, int irq, int level, - int cm, int target) -{ - if (level) { - GIC_SET_LEVEL(irq, cm); - GIC_SET_PENDING(irq, target); - } else { - GIC_CLEAR_LEVEL(irq, cm); - } -} - static void gic_set_irq_generic(GICState *s, int irq, int level, int cm, int target) { @@ -214,8 +203,6 @@ static void gic_set_irq(void *opaque, int irq, int level) if (s->revision == REV_11MPCORE) { gic_set_irq_11mpcore(s, irq, level, cm, target); - } else if (s->revision == REV_NVIC) { - gic_set_irq_nvic(s, irq, level, cm, target); } else { gic_set_irq_generic(s, irq, level, cm, target); } @@ -367,7 +354,7 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs) return 1023; } - if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) { + if (s->revision == REV_11MPCORE) { /* Clear pending flags for both level and edge triggered interrupts. * Level triggered IRQs will be reasserted once they become inactive. */ @@ -589,11 +576,6 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) DPRINTF("Set %d pending mask %x\n", irq, cm); GIC_SET_PENDING(irq, cm); } - } else if (s->revision == REV_NVIC) { - if (GIC_TEST_LEVEL(irq, cm)) { - DPRINTF("Set nvic %d pending mask %x\n", irq, cm); - GIC_SET_PENDING(irq, cm); - } } group = gic_has_groups(s) && GIC_TEST_GROUP(irq, cm); @@ -768,7 +750,7 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs) } else if (offset < 0xf10) { goto bad_reg; } else if (offset < 0xf30) { - if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) { + if (s->revision == REV_11MPCORE) { goto bad_reg; } @@ -802,9 +784,6 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs) case 2: res = gic_id_gicv2[(offset - 0xfd0) >> 2]; break; - case REV_NVIC: - /* Shouldn't be able to get here */ - abort(); default: res = 0; } @@ -1028,7 +1007,7 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, continue; /* Ignore Non-secure access of Group0 IRQ */ } - if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) { + if (s->revision == REV_11MPCORE) { if (value & (1 << (i * 2))) { GIC_SET_MODEL(irq + i); } else { @@ -1046,7 +1025,7 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, goto bad_reg; } else if (offset < 0xf20) { /* GICD_CPENDSGIRn */ - if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) { + if (s->revision == REV_11MPCORE) { goto bad_reg; } irq = (offset - 0xf10); @@ -1060,7 +1039,7 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, } } else if (offset < 0xf30) { /* GICD_SPENDSGIRn */ - if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) { + if (s->revision == REV_11MPCORE) { goto bad_reg; } irq = (offset - 0xf20); diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index 4a8df44fb1..70f1134823 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -99,9 +99,7 @@ void gic_init_irqs_and_mmio(GICState *s, qemu_irq_handler handler, * [N+32..N+63] PPIs for CPU 1 * ... */ - if (s->revision != REV_NVIC) { - i += (GIC_INTERNAL * s->num_cpu); - } + i += (GIC_INTERNAL * s->num_cpu); qdev_init_gpio_in(DEVICE(s), handler, i); for (i = 0; i < s->num_cpu; i++) { @@ -121,16 +119,12 @@ void gic_init_irqs_and_mmio(GICState *s, qemu_irq_handler handler, memory_region_init_io(&s->iomem, OBJECT(s), ops, s, "gic_dist", 0x1000); sysbus_init_mmio(sbd, &s->iomem); - if (s->revision != REV_NVIC) { - /* This is the main CPU interface "for this core". It is always - * present because it is required by both software emulation and KVM. - * NVIC is not handled here because its CPU interface is different, - * neither it can use KVM. - */ - memory_region_init_io(&s->cpuiomem[0], OBJECT(s), ops ? &ops[1] : NULL, - s, "gic_cpu", s->revision == 2 ? 0x2000 : 0x100); - sysbus_init_mmio(sbd, &s->cpuiomem[0]); - } + /* This is the main CPU interface "for this core". It is always + * present because it is required by both software emulation and KVM. + */ + memory_region_init_io(&s->cpuiomem[0], OBJECT(s), ops ? &ops[1] : NULL, + s, "gic_cpu", s->revision == 2 ? 0x2000 : 0x100); + sysbus_init_mmio(sbd, &s->cpuiomem[0]); } static void arm_gic_common_realize(DeviceState *dev, Error **errp) @@ -162,7 +156,7 @@ static void arm_gic_common_realize(DeviceState *dev, Error **errp) } if (s->security_extn && - (s->revision == REV_11MPCORE || s->revision == REV_NVIC)) { + (s->revision == REV_11MPCORE)) { error_setg(errp, "this GIC revision does not implement " "the security extensions"); return; @@ -255,7 +249,6 @@ static Property arm_gic_common_properties[] = { DEFINE_PROP_UINT32("num-irq", GICState, 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", GICState, revision, 1), /* True if the GIC should implement the security extensions */ diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index fe5c303de9..76097b4830 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -17,47 +17,90 @@ #include "hw/sysbus.h" #include "qemu/timer.h" #include "hw/arm/arm.h" +#include "target/arm/cpu.h" #include "exec/address-spaces.h" -#include "gic_internal.h" #include "qemu/log.h" +#include "trace.h" + +/* IRQ number counting: + * + * the num-irq property counts the number of external IRQ lines + * + * NVICState::num_irq counts the total number of exceptions + * (external IRQs, the 15 internal exceptions including reset, + * and one for the unused exception number 0). + * + * NVIC_MAX_IRQ is the highest permitted number of external IRQ lines. + * + * NVIC_MAX_VECTORS is the highest permitted number of exceptions. + * + * Iterating through all exceptions should typically be done with + * for (i = 1; i < s->num_irq; i++) to avoid the unused slot 0. + * + * The external qemu_irq lines are the NVIC's external IRQ lines, + * so line 0 is exception 16. + * + * In the terminology of the architecture manual, "interrupts" are + * a subcategory of exception referring to the external interrupts + * (which are exception numbers NVIC_FIRST_IRQ and upward). + * For historical reasons QEMU tends to use "interrupt" and + * "exception" more or less interchangeably. + */ +#define NVIC_FIRST_IRQ 16 +#define NVIC_MAX_VECTORS 512 +#define NVIC_MAX_IRQ (NVIC_MAX_VECTORS - NVIC_FIRST_IRQ) + +/* Effective running priority of the CPU when no exception is active + * (higher than the highest possible priority value) + */ +#define NVIC_NOEXC_PRIO 0x100 + +typedef struct VecInfo { + /* Exception priorities can range from -3 to 255; only the unmodifiable + * priority values for RESET, NMI and HardFault can be negative. + */ + int16_t prio; + uint8_t enabled; + uint8_t pending; + uint8_t active; + uint8_t level; /* exceptions <=15 never set level */ +} VecInfo; + +typedef struct NVICState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ -typedef struct { - GICState gic; ARMCPU *cpu; + + VecInfo vectors[NVIC_MAX_VECTORS]; + uint32_t prigroup; + + /* vectpending and exception_prio are both cached state that can + * be recalculated from the vectors[] array and the prigroup field. + */ + unsigned int vectpending; /* highest prio pending enabled exception */ + int exception_prio; /* group prio of the highest prio active exception */ + struct { uint32_t control; uint32_t reload; int64_t tick; QEMUTimer *timer; } systick; + MemoryRegion sysregmem; - MemoryRegion gic_iomem_alias; MemoryRegion container; + uint32_t num_irq; + qemu_irq excpout; qemu_irq sysresetreq; -} nvic_state; +} NVICState; #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 >*/ - DeviceRealize parent_realize; - 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) + OBJECT_CHECK(NVICState, (obj), TYPE_NVIC) static const uint8_t nvic_id[] = { 0x00, 0xb0, 0x1b, 0x00, 0x0d, 0xe0, 0x05, 0xb1 @@ -74,7 +117,7 @@ static const uint8_t nvic_id[] = { int system_clock_scale; /* Conversion factor from qemu timer to SysTick frequencies. */ -static inline int64_t systick_scale(nvic_state *s) +static inline int64_t systick_scale(NVICState *s) { if (s->systick.control & SYSTICK_CLKSOURCE) return system_clock_scale; @@ -82,7 +125,7 @@ static inline int64_t systick_scale(nvic_state *s) return 1000; } -static void systick_reload(nvic_state *s, int reset) +static void systick_reload(NVICState *s, int reset) { /* The Cortex-M3 Devices Generic User Guide says that "When the * ENABLE bit is set to 1, the counter loads the RELOAD value from the @@ -101,7 +144,7 @@ static void systick_reload(nvic_state *s, int reset) static void systick_timer_tick(void * opaque) { - nvic_state *s = (nvic_state *)opaque; + NVICState *s = (NVICState *)opaque; s->systick.control |= SYSTICK_COUNTFLAG; if (s->systick.control & SYSTICK_TICKINT) { /* Trigger the interrupt. */ @@ -114,7 +157,7 @@ static void systick_timer_tick(void * opaque) } } -static void systick_reset(nvic_state *s) +static void systick_reset(NVICState *s) { s->systick.control = 0; s->systick.reload = 0; @@ -122,47 +165,351 @@ static void systick_reset(nvic_state *s) timer_del(s->systick.timer); } -/* The external routines use the hardware vector numbering, ie. the first - IRQ is #16. The internal GIC routines use #32 as the first IRQ. */ +static int nvic_pending_prio(NVICState *s) +{ + /* return the priority of the current pending interrupt, + * or NVIC_NOEXC_PRIO if no interrupt is pending + */ + return s->vectpending ? s->vectors[s->vectpending].prio : NVIC_NOEXC_PRIO; +} + +/* Return the value of the ISCR RETTOBASE bit: + * 1 if there is exactly one active exception + * 0 if there is more than one active exception + * UNKNOWN if there are no active exceptions (we choose 1, + * which matches the choice Cortex-M3 is documented as making). + * + * NB: some versions of the documentation talk about this + * counting "active exceptions other than the one shown by IPSR"; + * this is only different in the obscure corner case where guest + * code has manually deactivated an exception and is about + * to fail an exception-return integrity check. The definition + * above is the one from the v8M ARM ARM and is also in line + * with the behaviour documented for the Cortex-M3. + */ +static bool nvic_rettobase(NVICState *s) +{ + int irq, nhand = 0; + + for (irq = ARMV7M_EXCP_RESET; irq < s->num_irq; irq++) { + if (s->vectors[irq].active) { + nhand++; + if (nhand == 2) { + return 0; + } + } + } + + return 1; +} + +/* Return the value of the ISCR ISRPENDING bit: + * 1 if an external interrupt is pending + * 0 if no external interrupt is pending + */ +static bool nvic_isrpending(NVICState *s) +{ + int irq; + + /* We can shortcut if the highest priority pending interrupt + * happens to be external or if there is nothing pending. + */ + if (s->vectpending > NVIC_FIRST_IRQ) { + return true; + } + if (s->vectpending == 0) { + return false; + } + + for (irq = NVIC_FIRST_IRQ; irq < s->num_irq; irq++) { + if (s->vectors[irq].pending) { + return true; + } + } + return false; +} + +/* Return a mask word which clears the subpriority bits from + * a priority value for an M-profile exception, leaving only + * the group priority. + */ +static inline uint32_t nvic_gprio_mask(NVICState *s) +{ + return ~0U << (s->prigroup + 1); +} + +/* Recompute vectpending and exception_prio */ +static void nvic_recompute_state(NVICState *s) +{ + int i; + int pend_prio = NVIC_NOEXC_PRIO; + int active_prio = NVIC_NOEXC_PRIO; + int pend_irq = 0; + + for (i = 1; i < s->num_irq; i++) { + VecInfo *vec = &s->vectors[i]; + + if (vec->enabled && vec->pending && vec->prio < pend_prio) { + pend_prio = vec->prio; + pend_irq = i; + } + if (vec->active && vec->prio < active_prio) { + active_prio = vec->prio; + } + } + + s->vectpending = pend_irq; + s->exception_prio = active_prio & nvic_gprio_mask(s); + + trace_nvic_recompute_state(s->vectpending, s->exception_prio); +} + +/* Return the current execution priority of the CPU + * (equivalent to the pseudocode ExecutionPriority function). + * This is a value between -2 (NMI priority) and NVIC_NOEXC_PRIO. + */ +static inline int nvic_exec_prio(NVICState *s) +{ + CPUARMState *env = &s->cpu->env; + int running; + + if (env->daif & PSTATE_F) { /* FAULTMASK */ + running = -1; + } else if (env->daif & PSTATE_I) { /* PRIMASK */ + running = 0; + } else if (env->v7m.basepri > 0) { + running = env->v7m.basepri & nvic_gprio_mask(s); + } else { + running = NVIC_NOEXC_PRIO; /* lower than any possible priority */ + } + /* consider priority of active handler */ + return MIN(running, s->exception_prio); +} + +bool armv7m_nvic_can_take_pending_exception(void *opaque) +{ + NVICState *s = opaque; + + return nvic_exec_prio(s) > nvic_pending_prio(s); +} + +/* caller must call nvic_irq_update() after this */ +static void set_prio(NVICState *s, unsigned irq, uint8_t prio) +{ + assert(irq > ARMV7M_EXCP_NMI); /* only use for configurable prios */ + assert(irq < s->num_irq); + + s->vectors[irq].prio = prio; + + trace_nvic_set_prio(irq, prio); +} + +/* Recompute state and assert irq line accordingly. + * Must be called after changes to: + * vec->active, vec->enabled, vec->pending or vec->prio for any vector + * prigroup + */ +static void nvic_irq_update(NVICState *s) +{ + int lvl; + int pend_prio; + + nvic_recompute_state(s); + pend_prio = nvic_pending_prio(s); + + /* Raise NVIC output if this IRQ would be taken, except that we + * ignore the effects of the BASEPRI, FAULTMASK and PRIMASK (which + * will be checked for in arm_v7m_cpu_exec_interrupt()); changes + * to those CPU registers don't cause us to recalculate the NVIC + * pending info. + */ + lvl = (pend_prio < s->exception_prio); + trace_nvic_irq_update(s->vectpending, pend_prio, s->exception_prio, lvl); + qemu_set_irq(s->excpout, lvl); +} + +static void armv7m_nvic_clear_pending(void *opaque, int irq) +{ + NVICState *s = (NVICState *)opaque; + VecInfo *vec; + + assert(irq > ARMV7M_EXCP_RESET && irq < s->num_irq); + + vec = &s->vectors[irq]; + trace_nvic_clear_pending(irq, vec->enabled, vec->prio); + if (vec->pending) { + vec->pending = 0; + nvic_irq_update(s); + } +} + void armv7m_nvic_set_pending(void *opaque, int irq) { - nvic_state *s = (nvic_state *)opaque; - if (irq >= 16) - irq += 16; - gic_set_pending_private(&s->gic, 0, irq); + NVICState *s = (NVICState *)opaque; + VecInfo *vec; + + assert(irq > ARMV7M_EXCP_RESET && irq < s->num_irq); + + vec = &s->vectors[irq]; + trace_nvic_set_pending(irq, vec->enabled, vec->prio); + + + if (irq >= ARMV7M_EXCP_HARD && irq < ARMV7M_EXCP_PENDSV) { + /* If a synchronous exception is pending then it may be + * escalated to HardFault if: + * * it is equal or lower priority to current execution + * * it is disabled + * (ie we need to take it immediately but we can't do so). + * Asynchronous exceptions (and interrupts) simply remain pending. + * + * For QEMU, we don't have any imprecise (asynchronous) faults, + * so we can assume that PREFETCH_ABORT and DATA_ABORT are always + * synchronous. + * Debug exceptions are awkward because only Debug exceptions + * resulting from the BKPT instruction should be escalated, + * but we don't currently implement any Debug exceptions other + * than those that result from BKPT, so we treat all debug exceptions + * as needing escalation. + * + * This all means we can identify whether to escalate based only on + * the exception number and don't (yet) need the caller to explicitly + * tell us whether this exception is synchronous or not. + */ + int running = nvic_exec_prio(s); + bool escalate = false; + + if (vec->prio >= running) { + trace_nvic_escalate_prio(irq, vec->prio, running); + escalate = true; + } else if (!vec->enabled) { + trace_nvic_escalate_disabled(irq); + escalate = true; + } + + if (escalate) { + if (running < 0) { + /* We want to escalate to HardFault but we can't take a + * synchronous HardFault at this point either. This is a + * Lockup condition due to a guest bug. We don't model + * Lockup, so report via cpu_abort() instead. + */ + cpu_abort(&s->cpu->parent_obj, + "Lockup: can't escalate %d to HardFault " + "(current priority %d)\n", irq, running); + } + + /* We can do the escalation, so we take HardFault instead */ + irq = ARMV7M_EXCP_HARD; + vec = &s->vectors[irq]; + s->cpu->env.v7m.hfsr |= R_V7M_HFSR_FORCED_MASK; + } + } + + if (!vec->pending) { + vec->pending = 1; + nvic_irq_update(s); + } } /* Make pending IRQ active. */ -int armv7m_nvic_acknowledge_irq(void *opaque) +void armv7m_nvic_acknowledge_irq(void *opaque) { - nvic_state *s = (nvic_state *)opaque; - uint32_t irq; - - irq = gic_acknowledge_irq(&s->gic, 0, MEMTXATTRS_UNSPECIFIED); - if (irq == 1023) - hw_error("Interrupt but no vector\n"); - if (irq >= 32) - irq -= 16; - return irq; + NVICState *s = (NVICState *)opaque; + CPUARMState *env = &s->cpu->env; + const int pending = s->vectpending; + const int running = nvic_exec_prio(s); + int pendgroupprio; + VecInfo *vec; + + assert(pending > ARMV7M_EXCP_RESET && pending < s->num_irq); + + vec = &s->vectors[pending]; + + assert(vec->enabled); + assert(vec->pending); + + pendgroupprio = vec->prio & nvic_gprio_mask(s); + assert(pendgroupprio < running); + + trace_nvic_acknowledge_irq(pending, vec->prio); + + vec->active = 1; + vec->pending = 0; + + env->v7m.exception = s->vectpending; + + nvic_irq_update(s); } -void armv7m_nvic_complete_irq(void *opaque, int irq) +int armv7m_nvic_complete_irq(void *opaque, int irq) { - nvic_state *s = (nvic_state *)opaque; - if (irq >= 16) - irq += 16; - gic_complete_irq(&s->gic, 0, irq, MEMTXATTRS_UNSPECIFIED); + NVICState *s = (NVICState *)opaque; + VecInfo *vec; + int ret; + + assert(irq > ARMV7M_EXCP_RESET && irq < s->num_irq); + + vec = &s->vectors[irq]; + + trace_nvic_complete_irq(irq); + + if (!vec->active) { + /* Tell the caller this was an illegal exception return */ + return -1; + } + + ret = nvic_rettobase(s); + + vec->active = 0; + if (vec->level) { + /* Re-pend the exception if it's still held high; only + * happens for extenal IRQs + */ + assert(irq >= NVIC_FIRST_IRQ); + vec->pending = 1; + } + + nvic_irq_update(s); + + return ret; } -static uint32_t nvic_readl(nvic_state *s, uint32_t offset) +/* callback when external interrupt line is changed */ +static void set_irq_level(void *opaque, int n, int level) +{ + NVICState *s = opaque; + VecInfo *vec; + + n += NVIC_FIRST_IRQ; + + assert(n >= NVIC_FIRST_IRQ && n < s->num_irq); + + trace_nvic_set_irq_level(n, level); + + /* The pending status of an external interrupt is + * latched on rising edge and exception handler return. + * + * Pulsing the IRQ will always run the handler + * once, and the handler will re-run until the + * level is low when the handler completes. + */ + vec = &s->vectors[n]; + if (level != vec->level) { + vec->level = level; + if (level) { + armv7m_nvic_set_pending(s, n); + } + } +} + +static uint32_t nvic_readl(NVICState *s, uint32_t offset) { ARMCPU *cpu = s->cpu; uint32_t val; - int irq; switch (offset) { case 4: /* Interrupt Control Type. */ - return (s->num_irq / 32) - 1; + return ((s->num_irq - NVIC_FIRST_IRQ) / 32) - 1; case 0x10: /* SysTick Control and Status. */ val = s->systick.control; s->systick.control &= ~SYSTICK_COUNTFLAG; @@ -192,38 +539,34 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset) case 0xd04: /* Interrupt Control State. */ /* VECTACTIVE */ val = cpu->env.v7m.exception; - if (val == 1023) { - val = 0; - } else if (val >= 32) { - val -= 16; - } /* VECTPENDING */ - if (s->gic.current_pending[0] != 1023) - val |= (s->gic.current_pending[0] << 12); - /* ISRPENDING and RETTOBASE */ - for (irq = 32; irq < s->num_irq; irq++) { - if (s->gic.irq_state[irq].pending) { - val |= (1 << 22); - break; - } - if (irq != cpu->env.v7m.exception && s->gic.irq_state[irq].active) { - val |= (1 << 11); - } + val |= (s->vectpending & 0xff) << 12; + /* ISRPENDING - set if any external IRQ is pending */ + if (nvic_isrpending(s)) { + val |= (1 << 22); + } + /* RETTOBASE - set if only one handler is active */ + if (nvic_rettobase(s)) { + val |= (1 << 11); } /* PENDSTSET */ - if (s->gic.irq_state[ARMV7M_EXCP_SYSTICK].pending) + if (s->vectors[ARMV7M_EXCP_SYSTICK].pending) { val |= (1 << 26); + } /* PENDSVSET */ - if (s->gic.irq_state[ARMV7M_EXCP_PENDSV].pending) + if (s->vectors[ARMV7M_EXCP_PENDSV].pending) { val |= (1 << 28); + } /* NMIPENDSET */ - if (s->gic.irq_state[ARMV7M_EXCP_NMI].pending) + if (s->vectors[ARMV7M_EXCP_NMI].pending) { val |= (1 << 31); + } + /* ISRPREEMPT not implemented */ return val; case 0xd08: /* Vector Table Offset. */ return cpu->env.v7m.vecbase; case 0xd0c: /* Application Interrupt/Reset Control. */ - return 0xfa050000; + return 0xfa050000 | (s->prigroup << 8); case 0xd10: /* System Control. */ /* TODO: Implement SLEEPONEXIT. */ return 0; @@ -231,20 +574,48 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset) return cpu->env.v7m.ccr; case 0xd24: /* System Handler Status. */ val = 0; - if (s->gic.irq_state[ARMV7M_EXCP_MEM].active) val |= (1 << 0); - if (s->gic.irq_state[ARMV7M_EXCP_BUS].active) val |= (1 << 1); - if (s->gic.irq_state[ARMV7M_EXCP_USAGE].active) val |= (1 << 3); - if (s->gic.irq_state[ARMV7M_EXCP_SVC].active) val |= (1 << 7); - if (s->gic.irq_state[ARMV7M_EXCP_DEBUG].active) val |= (1 << 8); - if (s->gic.irq_state[ARMV7M_EXCP_PENDSV].active) val |= (1 << 10); - if (s->gic.irq_state[ARMV7M_EXCP_SYSTICK].active) val |= (1 << 11); - if (s->gic.irq_state[ARMV7M_EXCP_USAGE].pending) val |= (1 << 12); - if (s->gic.irq_state[ARMV7M_EXCP_MEM].pending) val |= (1 << 13); - if (s->gic.irq_state[ARMV7M_EXCP_BUS].pending) val |= (1 << 14); - if (s->gic.irq_state[ARMV7M_EXCP_SVC].pending) val |= (1 << 15); - if (s->gic.irq_state[ARMV7M_EXCP_MEM].enabled) val |= (1 << 16); - if (s->gic.irq_state[ARMV7M_EXCP_BUS].enabled) val |= (1 << 17); - if (s->gic.irq_state[ARMV7M_EXCP_USAGE].enabled) val |= (1 << 18); + if (s->vectors[ARMV7M_EXCP_MEM].active) { + val |= (1 << 0); + } + if (s->vectors[ARMV7M_EXCP_BUS].active) { + val |= (1 << 1); + } + if (s->vectors[ARMV7M_EXCP_USAGE].active) { + val |= (1 << 3); + } + if (s->vectors[ARMV7M_EXCP_SVC].active) { + val |= (1 << 7); + } + if (s->vectors[ARMV7M_EXCP_DEBUG].active) { + val |= (1 << 8); + } + if (s->vectors[ARMV7M_EXCP_PENDSV].active) { + val |= (1 << 10); + } + if (s->vectors[ARMV7M_EXCP_SYSTICK].active) { + val |= (1 << 11); + } + if (s->vectors[ARMV7M_EXCP_USAGE].pending) { + val |= (1 << 12); + } + if (s->vectors[ARMV7M_EXCP_MEM].pending) { + val |= (1 << 13); + } + if (s->vectors[ARMV7M_EXCP_BUS].pending) { + val |= (1 << 14); + } + if (s->vectors[ARMV7M_EXCP_SVC].pending) { + val |= (1 << 15); + } + if (s->vectors[ARMV7M_EXCP_MEM].enabled) { + val |= (1 << 16); + } + if (s->vectors[ARMV7M_EXCP_BUS].enabled) { + val |= (1 << 17); + } + if (s->vectors[ARMV7M_EXCP_USAGE].enabled) { + val |= (1 << 18); + } return val; case 0xd28: /* Configurable Fault Status. */ return cpu->env.v7m.cfsr; @@ -294,7 +665,7 @@ static uint32_t nvic_readl(nvic_state *s, uint32_t offset) } } -static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value) +static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value) { ARMCPU *cpu = s->cpu; uint32_t oldval; @@ -338,14 +709,12 @@ static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value) if (value & (1 << 28)) { armv7m_nvic_set_pending(s, ARMV7M_EXCP_PENDSV); } else if (value & (1 << 27)) { - s->gic.irq_state[ARMV7M_EXCP_PENDSV].pending = 0; - gic_update(&s->gic); + armv7m_nvic_clear_pending(s, ARMV7M_EXCP_PENDSV); } if (value & (1 << 26)) { armv7m_nvic_set_pending(s, ARMV7M_EXCP_SYSTICK); } else if (value & (1 << 25)) { - s->gic.irq_state[ARMV7M_EXCP_SYSTICK].pending = 0; - gic_update(&s->gic); + armv7m_nvic_clear_pending(s, ARMV7M_EXCP_SYSTICK); } break; case 0xd08: /* Vector Table Offset. */ @@ -357,14 +726,17 @@ static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value) qemu_irq_pulse(s->sysresetreq); } if (value & 2) { - qemu_log_mask(LOG_UNIMP, "VECTCLRACTIVE unimplemented\n"); + qemu_log_mask(LOG_GUEST_ERROR, + "Setting VECTCLRACTIVE when not in DEBUG mode " + "is UNPREDICTABLE\n"); } if (value & 1) { - qemu_log_mask(LOG_UNIMP, "AIRCR system reset unimplemented\n"); - } - if (value & 0x700) { - qemu_log_mask(LOG_UNIMP, "PRIGROUP unimplemented\n"); + qemu_log_mask(LOG_GUEST_ERROR, + "Setting VECTRESET when not in DEBUG mode " + "is UNPREDICTABLE\n"); } + s->prigroup = extract32(value, 8, 3); + nvic_irq_update(s); } break; case 0xd10: /* System Control. */ @@ -383,11 +755,21 @@ static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value) cpu->env.v7m.ccr = value; break; case 0xd24: /* System Handler Control. */ - /* TODO: Real hardware allows you to set/clear the active bits - under some circumstances. We don't implement this. */ - s->gic.irq_state[ARMV7M_EXCP_MEM].enabled = (value & (1 << 16)) != 0; - s->gic.irq_state[ARMV7M_EXCP_BUS].enabled = (value & (1 << 17)) != 0; - s->gic.irq_state[ARMV7M_EXCP_USAGE].enabled = (value & (1 << 18)) != 0; + s->vectors[ARMV7M_EXCP_MEM].active = (value & (1 << 0)) != 0; + s->vectors[ARMV7M_EXCP_BUS].active = (value & (1 << 1)) != 0; + s->vectors[ARMV7M_EXCP_USAGE].active = (value & (1 << 3)) != 0; + s->vectors[ARMV7M_EXCP_SVC].active = (value & (1 << 7)) != 0; + s->vectors[ARMV7M_EXCP_DEBUG].active = (value & (1 << 8)) != 0; + s->vectors[ARMV7M_EXCP_PENDSV].active = (value & (1 << 10)) != 0; + s->vectors[ARMV7M_EXCP_SYSTICK].active = (value & (1 << 11)) != 0; + s->vectors[ARMV7M_EXCP_USAGE].pending = (value & (1 << 12)) != 0; + s->vectors[ARMV7M_EXCP_MEM].pending = (value & (1 << 13)) != 0; + s->vectors[ARMV7M_EXCP_BUS].pending = (value & (1 << 14)) != 0; + s->vectors[ARMV7M_EXCP_SVC].pending = (value & (1 << 15)) != 0; + s->vectors[ARMV7M_EXCP_MEM].enabled = (value & (1 << 16)) != 0; + s->vectors[ARMV7M_EXCP_BUS].enabled = (value & (1 << 17)) != 0; + s->vectors[ARMV7M_EXCP_USAGE].enabled = (value & (1 << 18)) != 0; + nvic_irq_update(s); break; case 0xd28: /* Configurable Fault Status. */ cpu->env.v7m.cfsr &= ~value; /* W1C */ @@ -409,13 +791,16 @@ static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value) "NVIC: Aux fault status registers unimplemented\n"); break; case 0xf00: /* Software Triggered Interrupt Register */ + { /* user mode can only write to STIR if CCR.USERSETMPEND permits it */ - if ((value & 0x1ff) < s->num_irq && + int excnum = (value & 0x1ff) + NVIC_FIRST_IRQ; + if (excnum < s->num_irq && (arm_current_el(&cpu->env) || (cpu->env.v7m.ccr & R_V7M_CCR_USERSETMPEND_MASK))) { - gic_set_pending_private(&s->gic, 0, value & 0x1ff); + armv7m_nvic_set_pending(s, excnum); } break; + } default: qemu_log_mask(LOG_GUEST_ERROR, "NVIC: Bad write offset 0x%x\n", offset); @@ -425,46 +810,142 @@ static void nvic_writel(nvic_state *s, uint32_t offset, uint32_t value) static uint64_t nvic_sysreg_read(void *opaque, hwaddr addr, unsigned size) { - nvic_state *s = (nvic_state *)opaque; + NVICState *s = (NVICState *)opaque; uint32_t offset = addr; - int i; + unsigned i, startvec, end; uint32_t val; switch (offset) { + /* reads of set and clear both return the status */ + case 0x100 ... 0x13f: /* NVIC Set enable */ + offset += 0x80; + /* fall through */ + case 0x180 ... 0x1bf: /* NVIC Clear enable */ + val = 0; + startvec = offset - 0x180 + NVIC_FIRST_IRQ; /* vector # */ + + for (i = 0, end = size * 8; i < end && startvec + i < s->num_irq; i++) { + if (s->vectors[startvec + i].enabled) { + val |= (1 << i); + } + } + break; + case 0x200 ... 0x23f: /* NVIC Set pend */ + offset += 0x80; + /* fall through */ + case 0x280 ... 0x2bf: /* NVIC Clear pend */ + val = 0; + startvec = offset - 0x280 + NVIC_FIRST_IRQ; /* vector # */ + for (i = 0, end = size * 8; i < end && startvec + i < s->num_irq; i++) { + if (s->vectors[startvec + i].pending) { + val |= (1 << i); + } + } + break; + case 0x300 ... 0x33f: /* NVIC Active */ + val = 0; + startvec = offset - 0x300 + NVIC_FIRST_IRQ; /* vector # */ + + for (i = 0, end = size * 8; i < end && startvec + i < s->num_irq; i++) { + if (s->vectors[startvec + i].active) { + val |= (1 << i); + } + } + break; + case 0x400 ... 0x5ef: /* NVIC Priority */ + val = 0; + startvec = offset - 0x400 + NVIC_FIRST_IRQ; /* vector # */ + + for (i = 0; i < size && startvec + i < s->num_irq; i++) { + val |= s->vectors[startvec + i].prio << (8 * i); + } + break; case 0xd18 ... 0xd23: /* System Handler Priority. */ val = 0; for (i = 0; i < size; i++) { - val |= s->gic.priority1[(offset - 0xd14) + i][0] << (i * 8); + val |= s->vectors[(offset - 0xd14) + i].prio << (i * 8); } - return val; + break; case 0xfe0 ... 0xfff: /* ID. */ if (offset & 3) { - return 0; + val = 0; + } else { + val = nvic_id[(offset - 0xfe0) >> 2]; + } + break; + default: + if (size == 4) { + val = nvic_readl(s, offset); + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "NVIC: Bad read of size %d at offset 0x%x\n", + size, offset); + val = 0; } - return nvic_id[(offset - 0xfe0) >> 2]; - } - if (size == 4) { - return nvic_readl(s, offset); } - qemu_log_mask(LOG_GUEST_ERROR, - "NVIC: Bad read of size %d at offset 0x%x\n", size, offset); - return 0; + + trace_nvic_sysreg_read(addr, val, size); + return val; } static void nvic_sysreg_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { - nvic_state *s = (nvic_state *)opaque; + NVICState *s = (NVICState *)opaque; uint32_t offset = addr; - int i; + unsigned i, startvec, end; + unsigned setval = 0; + + trace_nvic_sysreg_write(addr, value, size); switch (offset) { + case 0x100 ... 0x13f: /* NVIC Set enable */ + offset += 0x80; + setval = 1; + /* fall through */ + case 0x180 ... 0x1bf: /* NVIC Clear enable */ + startvec = 8 * (offset - 0x180) + NVIC_FIRST_IRQ; + + for (i = 0, end = size * 8; i < end && startvec + i < s->num_irq; i++) { + if (value & (1 << i)) { + s->vectors[startvec + i].enabled = setval; + } + } + nvic_irq_update(s); + return; + case 0x200 ... 0x23f: /* NVIC Set pend */ + /* the special logic in armv7m_nvic_set_pending() + * is not needed since IRQs are never escalated + */ + offset += 0x80; + setval = 1; + /* fall through */ + case 0x280 ... 0x2bf: /* NVIC Clear pend */ + startvec = 8 * (offset - 0x280) + NVIC_FIRST_IRQ; /* vector # */ + + for (i = 0, end = size * 8; i < end && startvec + i < s->num_irq; i++) { + if (value & (1 << i)) { + s->vectors[startvec + i].pending = setval; + } + } + nvic_irq_update(s); + return; + case 0x300 ... 0x33f: /* NVIC Active */ + return; /* R/O */ + case 0x400 ... 0x5ef: /* NVIC Priority */ + startvec = 8 * (offset - 0x400) + NVIC_FIRST_IRQ; /* vector # */ + + for (i = 0; i < size && startvec + i < s->num_irq; i++) { + set_prio(s, startvec + i, (value >> (i * 8)) & 0xff); + } + nvic_irq_update(s); + return; case 0xd18 ... 0xd23: /* System Handler Priority. */ for (i = 0; i < size; i++) { - s->gic.priority1[(offset - 0xd14) + i][0] = - (value >> (i * 8)) & 0xff; + unsigned hdlidx = (offset - 0xd14) + i; + set_prio(s, hdlidx, (value >> (i * 8)) & 0xff); } - gic_update(&s->gic); + nvic_irq_update(s); return; } if (size == 4) { @@ -481,61 +962,125 @@ static const MemoryRegionOps nvic_sysreg_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static const VMStateDescription vmstate_nvic = { - .name = "armv7m_nvic", +static int nvic_post_load(void *opaque, int version_id) +{ + NVICState *s = opaque; + unsigned i; + + /* Check for out of range priority settings */ + if (s->vectors[ARMV7M_EXCP_RESET].prio != -3 || + s->vectors[ARMV7M_EXCP_NMI].prio != -2 || + s->vectors[ARMV7M_EXCP_HARD].prio != -1) { + return 1; + } + for (i = ARMV7M_EXCP_MEM; i < s->num_irq; i++) { + if (s->vectors[i].prio & ~0xff) { + return 1; + } + } + + nvic_recompute_state(s); + + return 0; +} + +static const VMStateDescription vmstate_VecInfo = { + .name = "armv7m_nvic_info", .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_UINT32(systick.control, nvic_state), - VMSTATE_UINT32(systick.reload, nvic_state), - VMSTATE_INT64(systick.tick, nvic_state), - VMSTATE_TIMER_PTR(systick.timer, nvic_state), + VMSTATE_INT16(prio, VecInfo), + VMSTATE_UINT8(enabled, VecInfo), + VMSTATE_UINT8(pending, VecInfo), + VMSTATE_UINT8(active, VecInfo), + VMSTATE_UINT8(level, VecInfo), VMSTATE_END_OF_LIST() } }; +static const VMStateDescription vmstate_nvic = { + .name = "armv7m_nvic", + .version_id = 3, + .minimum_version_id = 3, + .post_load = &nvic_post_load, + .fields = (VMStateField[]) { + VMSTATE_STRUCT_ARRAY(vectors, NVICState, NVIC_MAX_VECTORS, 1, + vmstate_VecInfo, VecInfo), + VMSTATE_UINT32(systick.control, NVICState), + VMSTATE_UINT32(systick.reload, NVICState), + VMSTATE_INT64(systick.tick, NVICState), + VMSTATE_TIMER_PTR(systick.timer, NVICState), + VMSTATE_UINT32(prigroup, NVICState), + VMSTATE_END_OF_LIST() + } +}; + +static Property props_nvic[] = { + /* Number of external IRQ lines (so excluding the 16 internal exceptions) */ + DEFINE_PROP_UINT32("num-irq", NVICState, num_irq, 64), + DEFINE_PROP_END_OF_LIST() +}; + static void armv7m_nvic_reset(DeviceState *dev) { - 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, and with a priority mask which allows - * all interrupts through. + NVICState *s = NVIC(dev); + + s->vectors[ARMV7M_EXCP_NMI].enabled = 1; + s->vectors[ARMV7M_EXCP_HARD].enabled = 1; + /* MEM, BUS, and USAGE are enabled through + * the System Handler Control register + */ + s->vectors[ARMV7M_EXCP_SVC].enabled = 1; + s->vectors[ARMV7M_EXCP_DEBUG].enabled = 1; + s->vectors[ARMV7M_EXCP_PENDSV].enabled = 1; + s->vectors[ARMV7M_EXCP_SYSTICK].enabled = 1; + + s->vectors[ARMV7M_EXCP_RESET].prio = -3; + s->vectors[ARMV7M_EXCP_NMI].prio = -2; + s->vectors[ARMV7M_EXCP_HARD].prio = -1; + + /* Strictly speaking the reset handler should be enabled. + * However, we don't simulate soft resets through the NVIC, + * and the reset vector should never be pended. + * So we leave it disabled to catch logic errors. */ - s->gic.cpu_ctlr[0] = GICC_CTLR_EN_GRP0; - s->gic.priority_mask[0] = 0x100; - /* The NVIC as a whole is always enabled. */ - s->gic.ctlr = 1; + + s->exception_prio = NVIC_NOEXC_PRIO; + s->vectpending = 0; + systick_reset(s); } static void armv7m_nvic_realize(DeviceState *dev, Error **errp) { - nvic_state *s = NVIC(dev); - NVICClass *nc = NVIC_GET_CLASS(s); - Error *local_err = NULL; + NVICState *s = NVIC(dev); s->cpu = ARM_CPU(qemu_get_cpu(0)); assert(s->cpu); - /* 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->num_irq = s->gic.num_irq; - nc->parent_realize(dev, &local_err); - if (local_err) { - error_propagate(errp, local_err); + + if (s->num_irq > NVIC_MAX_IRQ) { + error_setg(errp, "num-irq %d exceeds NVIC maximum", s->num_irq); return; } - gic_init_irqs_and_distributor(&s->gic); - /* 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. + + qdev_init_gpio_in(dev, set_irq_level, s->num_irq); + + /* include space for internal exception vectors */ + s->num_irq += NVIC_FIRST_IRQ; + + /* The NVIC and System Control Space (SCS) starts at 0xe000e000 + * and looks like this: + * 0x004 - ICTR + * 0x010 - 0x1c - systick + * 0x100..0x7ec - NVIC + * 0x7f0..0xcff - Reserved + * 0xd00..0xd3c - SCS registers + * 0xd40..0xeff - Reserved or Not implemented + * 0xf00 - STIR + * + * At the moment there is only one thing in the container region, + * but we leave it in place to allow us to pull systick out into + * its own device object later. */ memory_region_init(&s->container, OBJECT(s), "nvic", 0x1000); /* The system register region goes at the bottom of the priority @@ -544,14 +1089,7 @@ static void armv7m_nvic_realize(DeviceState *dev, Error **errp) memory_region_init_io(&s->sysregmem, OBJECT(s), &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, OBJECT(s), - "nvic-gic", &s->gic.iomem, - 0x100, 0xc00); - memory_region_add_subregion_overlap(&s->container, 0x100, - &s->gic_iomem_alias, 1); + /* Map the whole thing into system memory at the location required * by the v7M architecture. */ @@ -567,36 +1105,31 @@ static void armv7m_nvic_instance_init(Object *obj) * any user-specified property setting, so just modify the * value in the GICState struct. */ - GICState *s = ARM_GIC_COMMON(obj); DeviceState *dev = DEVICE(obj); - nvic_state *nvic = NVIC(obj); - /* 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 the num-irq property appropriately. - */ - s->num_irq = 64; + NVICState *nvic = NVIC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + sysbus_init_irq(sbd, &nvic->excpout); qdev_init_gpio_out_named(dev, &nvic->sysresetreq, "SYSRESETREQ", 1); } static void armv7m_nvic_class_init(ObjectClass *klass, void *data) { - NVICClass *nc = NVIC_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - nc->parent_reset = dc->reset; - nc->parent_realize = dc->realize; dc->vmsd = &vmstate_nvic; + dc->props = props_nvic; dc->reset = armv7m_nvic_reset; dc->realize = armv7m_nvic_realize; } static const TypeInfo armv7m_nvic_info = { .name = TYPE_NVIC, - .parent = TYPE_ARM_GIC_COMMON, + .parent = TYPE_SYS_BUS_DEVICE, .instance_init = armv7m_nvic_instance_init, - .instance_size = sizeof(nvic_state), + .instance_size = sizeof(NVICState), .class_init = armv7m_nvic_class_init, - .class_size = sizeof(NVICClass), + .class_size = sizeof(SysBusDeviceClass), }; static void armv7m_nvic_register_types(void) diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h index 3f311740da..7fe87b13de 100644 --- a/hw/intc/gic_internal.h +++ b/hw/intc/gic_internal.h @@ -25,9 +25,7 @@ #define ALL_CPU_MASK ((unsigned)(((1 << GIC_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_BASE_IRQ 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) @@ -75,7 +73,6 @@ /* The special cases for the revision property: */ #define REV_11MPCORE 0 -#define REV_NVIC 0xffffffff void gic_set_pending_private(GICState *s, int cpu, int irq); uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs); @@ -87,7 +84,7 @@ void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val, static inline bool gic_test_pending(GICState *s, int irq, int cm) { - if (s->revision == REV_NVIC || s->revision == REV_11MPCORE) { + if (s->revision == REV_11MPCORE) { return s->irq_state[irq].pending & cm; } else { /* Edge-triggered interrupts are marked pending on a rising edge, but diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 39a538d048..729c1288f1 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -161,3 +161,18 @@ gicv3_redist_write(uint32_t cpu, uint64_t offset, uint64_t data, unsigned size, gicv3_redist_badwrite(uint32_t cpu, uint64_t offset, uint64_t data, unsigned size, bool secure) "GICv3 redistributor %x write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u secure %d: error" gicv3_redist_set_irq(uint32_t cpu, int irq, int level) "GICv3 redistributor %x interrupt %d level changed to %d" gicv3_redist_send_sgi(uint32_t cpu, int irq) "GICv3 redistributor %x pending SGI %d" + +# hw/intc/armv7m_nvic.c +nvic_recompute_state(int vectpending, int exception_prio) "NVIC state recomputed: vectpending %d exception_prio %d" +nvic_set_prio(int irq, uint8_t prio) "NVIC set irq %d priority %d" +nvic_irq_update(int vectpending, int pendprio, int exception_prio, int level) "NVIC vectpending %d pending prio %d exception_prio %d: setting irq line to %d" +nvic_escalate_prio(int irq, int irqprio, int runprio) "NVIC escalating irq %d to HardFault: insufficient priority %d >= %d" +nvic_escalate_disabled(int irq) "NVIC escalating irq %d to HardFault: disabled" +nvic_set_pending(int irq, int en, int prio) "NVIC set pending irq %d (enabled: %d priority %d)" +nvic_clear_pending(int irq, int en, int prio) "NVIC clear pending irq %d (enabled: %d priority %d)" +nvic_set_pending_level(int irq) "NVIC set pending: irq %d higher prio than vectpending: setting irq line to 1" +nvic_acknowledge_irq(int irq, int prio) "NVIC acknowledge IRQ: %d now active (prio %d)" +nvic_complete_irq(int irq) "NVIC complete IRQ %d" +nvic_set_irq_level(int irq, int level) "NVIC external irq %d level set to %d" +nvic_sysreg_read(uint64_t addr, uint32_t value, unsigned size) "NVIC sysreg read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u" +nvic_sysreg_write(uint64_t addr, uint32_t value, unsigned size) "NVIC sysreg write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u" |