Add code to the GICv5 skeleton which creates the QOM properties which
the board or SoC can use to configure the GIC, and the validation
code to check they are in range. Generally these correspond to
fields in the IRS ID registers, and the properties are named
correspondingly.
Notable here is that unlike the GICv3 (which assumes its connected
CPUs are the system's CPUs starting from 0), we define a QOM array
property which is an array of pointers to the CPUs, and a QOM array
property which is an array of integers telling the GIC what the
IAFFID (interrupt affinity ID) for each CPU is; so a board or SoC
which wants to connect multiple CPUs to this GICv5 would do something
like:
QList *cpulist = qlist_new(), *iaffidlist = qlist_new();
for (int i = 0; i < ms->smp.cpus; i++) {
qlist_append_link(cpulist, OBJECT(qemu_get_cpu(i)));
qlist_append_int(iaffidlist, i);
}
qdev_prop_set_array(vms->gic, "cpus", cpulist);
qdev_prop_set_array(vms->gic, "cpu-iaffids", iaffidlist);
Signed-off-by: Peter Maydell <[email protected]>
---
hw/intc/arm_gicv5_common.c | 80 ++++++++++++++++++++++++++++++
hw/intc/trace-events | 3 ++
include/hw/intc/arm_gicv5_common.h | 25 ++++++++++
3 files changed, 108 insertions(+)
diff --git a/hw/intc/arm_gicv5_common.c b/hw/intc/arm_gicv5_common.c
index cb80c8821c..c8b6704fe4 100644
--- a/hw/intc/arm_gicv5_common.c
+++ b/hw/intc/arm_gicv5_common.c
@@ -8,9 +8,15 @@
#include "qemu/osdep.h"
#include "hw/intc/arm_gicv5_common.h"
+#include "hw/core/qdev-properties.h"
+#include "qapi/error.h"
+#include "trace.h"
OBJECT_DEFINE_ABSTRACT_TYPE(GICv5Common, gicv5_common, ARM_GICV5_COMMON,
SYS_BUS_DEVICE)
+/* Any value > 2^24 is out of the valid range for this property */
+#define GICV5_SPI_IRS_RANGE_NOT_SET 0xffffffff
+
static bool bad_frame_accepts(void *opaque, hwaddr addr, unsigned size,
bool is_write, MemTxAttrs attrs)
{
@@ -58,9 +64,83 @@ static void gicv5_common_finalize(Object *obj)
{
}
+static void gicv5_common_realize(DeviceState *dev, Error **errp)
+{
+ GICv5Common *cs = ARM_GICV5_COMMON(dev);
+
+ if (cs->num_cpus == 0) {
+ error_setg(errp, "The cpus array property must have at least one CPU");
+ return;
+ }
+ if (cs->num_cpus >= (1 << 16)) {
+ /* We'll hit other QEMU limits long before this one :-) */
+ error_setg(errp, "Number of CPUs exceeds GICv5 architectural maximum");
+ return;
+ }
+ if (cs->num_cpus != cs->num_cpu_iaffids) {
+ error_setg(errp, "The cpu-iaffids array property must be the same size
"
+ "as the cpus array property");
+ return;
+ }
+ if (cs->irsid >= (1 << 16)) {
+ error_setg(errp, "irsid (%u) is more than 2^16-1", cs->irsid);
+ return;
+ }
+ if (cs->spi_range > (1 << 24)) {
+ /*
+ * Note that IRS_IDR5.SPI_RANGE is a 25 bit field but the largest
+ * architecturally permitted value is 2^24 (not 2^25-1), hence
+ * use of > in the range check.
+ */
+ error_setg(errp, "spi-range (%u) is more than 2^24", cs->spi_range);
+ return;
+ }
+ if (cs->spi_irs_range == GICV5_SPI_IRS_RANGE_NOT_SET) {
+ /* spi-irs-range defaults to same as spi-range */
+ cs->spi_irs_range = cs->spi_range;
+ }
+ if (cs->spi_irs_range > (1 << 24)) {
+ /* Similarly IRS_IDR6.SPI_IRS_RANGE */
+ error_setg(errp, "spi-irs-range (%u) is more than 2^24",
+ cs->spi_irs_range);
+ return;
+ }
+ if (cs->spi_base >= (1 << 24)) {
+ /* IRS_IDR7.SPI_BASE is a 24-bit field, so range check is >= */
+ error_setg(errp, "spi-base (%u) is more than 2^24-1", cs->spi_base);
+ return;
+ }
+ /* range checks above mean we know this addition won't overflow */
+ if (cs->spi_base + cs->spi_irs_range > cs->spi_range) {
+ error_setg(errp, "spi-base (%u) + spi-irs-range (%u) is "
+ "more than spi-range (%u)",
+ cs->spi_base, cs->spi_irs_range, cs->spi_range);
+ return;
+ }
+
+ trace_gicv5_common_realize(cs->irsid, cs->num_cpus,
+ cs->spi_base, cs->spi_irs_range, cs->spi_range);
+}
+
+static const Property arm_gicv5_common_properties[] = {
+ DEFINE_PROP_LINK_ARRAY("cpus", GICv5Common, num_cpus,
+ cpus, TYPE_ARM_CPU, ARMCPU *),
+ DEFINE_PROP_ARRAY("cpu-iaffids", GICv5Common, num_cpu_iaffids,
+ cpu_iaffids, qdev_prop_uint32, uint32_t),
+ DEFINE_PROP_UINT32("irsid", GICv5Common, irsid, 0),
+ DEFINE_PROP_UINT32("spi-range", GICv5Common, spi_range, 0),
+ DEFINE_PROP_UINT32("spi-base", GICv5Common, spi_base, 0),
+ DEFINE_PROP_UINT32("spi-irs-range", GICv5Common, spi_irs_range,
+ GICV5_SPI_IRS_RANGE_NOT_SET),
+};
+
static void gicv5_common_class_init(ObjectClass *oc, const void *data)
{
ResettableClass *rc = RESETTABLE_CLASS(oc);
+ DeviceClass *dc = DEVICE_CLASS(oc);
rc->phases.hold = gicv5_common_reset_hold;
+
+ dc->realize = gicv5_common_realize;
+ device_class_set_props(dc, arm_gicv5_common_properties);
}
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index edd3c49c5f..54777f6da3 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -233,6 +233,9 @@ gicv5_badread(const char *domain, uint64_t offset, unsigned
size) "GICv5 IRS %s
gicv5_write(const char *domain, uint64_t offset, uint64_t data, unsigned size)
"GICv5 IRS %s config frame write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size
%u"
gicv5_badwrite(const char *domain, uint64_t offset, uint64_t data, unsigned
size) "GICv5 IRS %s config frame write: offset 0x%" PRIx64 " data 0x%" PRIx64 "
size %u: error"
+# arm_gicv5_common.c
+gicv5_common_realize(uint32_t irsid, uint32_t num_cpus, uint32_t spi_base,
uint32_t spi_irs_range, uint32_t spi_range) "GICv5 IRS realized: IRS ID %u, %u
CPUs, SPI base %u, SPI IRS range %u, SPI range %u"
+
# armv7m_nvic.c
nvic_recompute_state(int vectpending, int vectpending_prio, int
exception_prio) "NVIC state recomputed: vectpending %d vectpending_prio %d
exception_prio %d"
nvic_recompute_state_secure(int vectpending, bool vectpending_is_s_banked, int
vectpending_prio, int exception_prio) "NVIC state recomputed: vectpending %d
is_s_banked %d vectpending_prio %d exception_prio %d"
diff --git a/include/hw/intc/arm_gicv5_common.h
b/include/hw/intc/arm_gicv5_common.h
index 8ba2e5c879..b22f020a53 100644
--- a/include/hw/intc/arm_gicv5_common.h
+++ b/include/hw/intc/arm_gicv5_common.h
@@ -12,10 +12,24 @@
#include "qom/object.h"
#include "hw/core/sysbus.h"
#include "hw/intc/arm_gicv5_types.h"
+#include "target/arm/cpu-qom.h"
/*
* QEMU interface:
*
+ * + QOM array property "cpus": CPUState pointers to each CPU
+ * connected to this IRS.
+ * + QOM array property "cpu-iaffids": array of uint32_t giving the
+ * IAFFID for each CPU in the "cpus" property array
+ * + QOM property "irsid": unique identifier for this IRS in the system
+ * (this is IRS_IDR0.IRSID); default is 0
+ * + QOM property "spi-range": total number of SPIs in the system
+ * IRS (this is IRS_IDR5.SPI_RANGE); must be set
+ * + QOM property "spi-base": minimum SPI INTID.ID implemented on this
+ * IRS (this is IRS_IDR7.SPI_BASE); default is 0
+ * + QOM property "spi-irs-range": number of SPI INTID.ID managed on this
+ * IRS (this is IRS_IDR6.SPI_IRS_RANGE); defaults to value of spi-range
+ *
* sysbus MMIO regions (in order matching IRS_IDR0.INT_DOM encoding):
* - IRS config frame for the Secure Interrupt Domain
* - IRS config frame for the Non-secure Interrupt Domain
@@ -47,6 +61,17 @@ struct GICv5Common {
/* Bits here are set for each physical interrupt domain implemented */
uint8_t implemented_domains;
+
+ /* Properties */
+ uint32_t num_cpus;
+ ARMCPU **cpus;
+ uint32_t num_cpu_iaffids;
+ uint32_t *cpu_iaffids;
+
+ uint32_t irsid;
+ uint32_t spi_base;
+ uint32_t spi_irs_range;
+ uint32_t spi_range;
};
struct GICv5CommonClass {
--
2.43.0