#include "qemu/osdep.h"
#include "hw/intc/arm_gicv5.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+#include "trace.h"
OBJECT_DEFINE_TYPE(GICv5, gicv5, ARM_GICV5, ARM_GICV5_COMMON)
+static const char *domain_name[] = {
+ [GICV5_ID_S] = "Secure",
+ [GICV5_ID_NS] = "NonSecure",
+ [GICV5_ID_EL3] = "EL3",
+ [GICV5_ID_REALM] = "Realm",
+};
+
+static bool config_readl(GICv5 *s, GICv5Domain domain, hwaddr offset,
+ uint64_t *data, MemTxAttrs attrs)
+{
+ return false;
+}
+
+static bool config_writel(GICv5 *s, GICv5Domain domain, hwaddr offset,
+ uint64_t data, MemTxAttrs attrs)
+{
+ return false;
+}
+
+static bool config_readll(GICv5 *s, GICv5Domain domain, hwaddr offset,
+ uint64_t *data, MemTxAttrs attrs)
+{
+ return false;
+}
+
+static bool config_writell(GICv5 *s, GICv5Domain domain, hwaddr offset,
+ uint64_t data, MemTxAttrs attrs)
+{
+ return false;
+}
+
+static MemTxResult config_read(void *opaque, GICv5Domain domain, hwaddr offset,
+ uint64_t *data, unsigned size,
+ MemTxAttrs attrs)
+{
+ GICv5 *s = ARM_GICV5(opaque);
+ bool result;
+
+ switch (size) {
+ case 4:
+ result = config_readl(s, domain, offset, data, attrs);
+ break;
+ case 8:
+ result = config_readll(s, domain, offset, data, attrs);
+ break;
+ default:
+ result = false;
+ break;
+ }
+
+ if (!result) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid guest read for IRS %s config frame "
+ "at offset " HWADDR_FMT_plx
+ " size %u\n", __func__, domain_name[domain],
+ offset, size);
+ trace_gicv5_badread(domain_name[domain], offset, size);
+ /*
+ * The spec requires that reserved registers are RAZ/WI; so we
+ * log the error but return MEMTX_OK so we don't cause a
+ * spurious data abort.
+ */
+ *data = 0;
+ } else {
+ trace_gicv5_read(domain_name[domain], offset, *data, size);
+ }
+
+ return MEMTX_OK;
+}
+
+static MemTxResult config_write(void *opaque, GICv5Domain domain,
+ hwaddr offset, uint64_t data, unsigned size,
+ MemTxAttrs attrs)
+{
+ GICv5 *s = ARM_GICV5(opaque);
+ bool result;
+
+ switch (size) {
+ case 4:
+ result = config_writel(s, domain, offset, data, attrs);
+ break;
+ case 8:
+ result = config_writell(s, domain, offset, data, attrs);
+ break;
+ default:
+ result = false;
+ break;
+ }
+
+ if (!result) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid guest write for IRS %s config frame "
+ "at offset " HWADDR_FMT_plx
+ " size %u\n", __func__, domain_name[domain],
+ offset, size);
+ trace_gicv5_badwrite(domain_name[domain], offset, data, size);
+ /*
+ * The spec requires that reserved registers are RAZ/WI; so we
+ * log the error but return MEMTX_OK so we don't cause a
+ * spurious data abort.
+ */
+ } else {
+ trace_gicv5_write(domain_name[domain], offset, data, size);
+ }
+
+ return MEMTX_OK;
+}
+
+#define DEFINE_READ_WRITE_WRAPPERS(NAME, DOMAIN) \
+ static MemTxResult config_##NAME##_read(void *opaque, hwaddr offset, \
+ uint64_t *data, unsigned size, \
+ MemTxAttrs attrs) \
+ { \
+ return config_read(opaque, DOMAIN, offset, data, size, attrs); \
+ } \
+ static MemTxResult config_##NAME##_write(void *opaque, hwaddr offset, \
+ uint64_t data, unsigned size, \
+ MemTxAttrs attrs) \
+ { \
+ return config_write(opaque, DOMAIN, offset, data, size, attrs); \
+ }
+
+DEFINE_READ_WRITE_WRAPPERS(ns, GICV5_ID_NS)
+DEFINE_READ_WRITE_WRAPPERS(realm, GICV5_ID_REALM)
+DEFINE_READ_WRITE_WRAPPERS(secure, GICV5_ID_S)
+DEFINE_READ_WRITE_WRAPPERS(el3, GICV5_ID_EL3)
+
+#define FRAME_OP_ENTRY(NAME, DOMAIN) \
+ [DOMAIN] = { \
+ .read_with_attrs = config_##NAME##_read, \
+ .write_with_attrs = config_##NAME##_write, \
+ .endianness = DEVICE_LITTLE_ENDIAN, \
+ .valid.min_access_size = 4, \
+ .valid.max_access_size = 8, \
+ .impl.min_access_size = 4, \
+ .impl.max_access_size = 8, \
+ }
+
+static const MemoryRegionOps config_frame_ops[NUM_GICV5_DOMAINS] = {
+ FRAME_OP_ENTRY(ns, GICV5_ID_NS),
+ FRAME_OP_ENTRY(realm, GICV5_ID_REALM),
+ FRAME_OP_ENTRY(secure, GICV5_ID_S),
+ FRAME_OP_ENTRY(el3, GICV5_ID_EL3),
+};
+
static void gicv5_reset_hold(Object *obj, ResetType type)
{
GICv5 *s = ARM_GICV5(obj);
}
}
+static void gicv5_realize(DeviceState *dev, Error **errp)
+{
+ GICv5Common *cs = ARM_GICV5_COMMON(dev);
+ GICv5Class *gc = ARM_GICV5_GET_CLASS(dev);
+
+ ERRP_GUARD();
+
+ gc->parent_realize(dev, errp);
+ if (*errp) {
+ return;
+ }
+
+ /*
+ * When we implement support for more than one interrupt domain,
+ * we will provide some QOM properties so the board can configure
+ * which domains are implemented. For now, we only implement the
+ * NS domain.
+ */
+ cs->implemented_domains = (1 << GICV5_ID_NS);
+ gicv5_common_init_irqs_and_mmio(cs, config_frame_ops);
+}
+
static void gicv5_init(Object *obj)
{
}
static void gicv5_class_init(ObjectClass *oc, const void *data)
{
ResettableClass *rc = RESETTABLE_CLASS(oc);
+ DeviceClass *dc = DEVICE_CLASS(oc);
GICv5Class *gc = ARM_GICV5_CLASS(oc);
+ device_class_set_parent_realize(dc, gicv5_realize, &gc->parent_realize);
resettable_class_set_parent_phases(rc, NULL, gicv5_reset_hold, NULL,
&gc->parent_phases);
}
OBJECT_DEFINE_ABSTRACT_TYPE(GICv5Common, gicv5_common, ARM_GICV5_COMMON, SYS_BUS_DEVICE)
+static bool bad_frame_accepts(void *opaque, hwaddr addr, unsigned size,
+ bool is_write, MemTxAttrs attrs)
+{
+ return false;
+}
+
+/*
+ * Used for the sysbus MMIO regions corresponding to IRS frames where
+ * this IRS does not implement the interrupt domain. It's probably a
+ * board/SoC error to create an IRS and try to wire up this MMIO
+ * region, but if it does then the region will behave as unassigned
+ * memory (generating a decode error). These frames are just here so
+ * that changing which domains are implemented doesn't reorder which
+ * sysbus MMIO region is which.
+ */
+static const MemoryRegionOps bad_frame_ops = {
+ .valid.accepts = bad_frame_accepts,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+void gicv5_common_init_irqs_and_mmio(GICv5Common *cs,
+ const MemoryRegionOps config_ops[NUM_GICV5_DOMAINS])
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(cs);
+
+ for (int i = 0; i < NUM_GICV5_DOMAINS; i++) {
+ g_autofree char *memname = g_strdup_printf("gicv5-irs-%d", i);
+ const MemoryRegionOps *ops = gicv5_domain_implemented(cs, i) ?
+ &config_ops[i] : &bad_frame_ops;
+ memory_region_init_io(&cs->iomem[i], OBJECT(cs), ops, cs,
+ memname, IRS_CONFIG_FRAME_SIZE);
+ sysbus_init_mmio(sbd, &cs->iomem[i]);
+ }
+}
+
static void gicv5_common_reset_hold(Object *obj, ResetType type)
{
}
#include "qom/object.h"
#include "hw/core/sysbus.h"
+#include "hw/intc/arm_gicv5_types.h"
+
+/*
+ * QEMU interface:
+ *
+ * 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
+ * - IRS config frame for the EL3 Interrupt Domain
+ * - IRS config frame for the Realm Interrupt Domain
+ *
+ * Note that even if this particular IRS does not implement all four
+ * interrupt domains it will still expose four sysbus MMIO regions.
+ * The regions corresponding to unimplemented domains will always fail
+ * accesses with a decode error. Generally the SoC/board should
+ * probably not map a region for a domain that it configured the IRS
+ * to not implement; the regions are only exposed so that changing
+ * which domains are implemented doesn't reorder which sysbus MMIO
+ * region is which (e.g. NS will always be 1 and EL3 will always be 2).
+ */
#define TYPE_ARM_GICV5_COMMON "arm-gicv5-common"
*/
struct GICv5Common {
SysBusDevice parent_obj;
+
+ MemoryRegion iomem[NUM_GICV5_DOMAINS];
+
+ /* Bits here are set for each physical interrupt domain implemented */
+ uint8_t implemented_domains;
};
struct GICv5CommonClass {
SysBusDeviceClass parent_class;
};
+
+#define IRS_CONFIG_FRAME_SIZE 0x10000
+
+/**
+ * gicv5_common_init_irqs_and_mmio: Create IRQs and MMIO regions for the GICv5
+ * @s: GIC object
+ * @ops: array of MemoryRegionOps that implement the config frames behaviour
+ *
+ * Subclasses of ARM_GICV5_COMMON should call this to create the sysbus
+ * MemoryRegions for the IRS config frames, passing in a four element array
+ * of MemoryRegionOps structs.
+ */
+void gicv5_common_init_irqs_and_mmio(GICv5Common *cs,
+ const MemoryRegionOps ops[NUM_GICV5_DOMAINS]);
+
+/**
+ * gicv5_domain_implemented: Return true if this IRS implements this domain
+ * @s: GIC object
+ * @domain: domain to check
+ */
+static inline bool gicv5_domain_implemented(GICv5Common *cs, GICv5Domain domain)
+{
+ return cs->implemented_domains & (1 << domain);
+}
+
#endif