Advertise the GICv5 in the DTB. This binding is final as it is in
the upstream Linux kernel as:
Documentation/devicetree/bindings/interrupt-controller/arm,gic-v5.yaml
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
---
hw/arm/virt.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 68 insertions(+)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index b6a04f868b..7a34af766a 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -794,6 +794,72 @@ static void create_v2m(VirtMachineState *vms)
vms->msi_controller = VIRT_MSI_CTRL_GICV2M;
}
+static void fdt_add_gicv5_node(VirtMachineState *vms)
+{
+ MachineState *ms = MACHINE(vms);
+ const char *nodename = "/intc";
+ g_autofree char *irsnodename = NULL;
+ g_autofree uint32_t *cpu_phandles = g_new(uint32_t, ms->smp.cpus);
+ g_autofree uint16_t *iaffids = g_new(uint16_t, ms->smp.cpus);
+
+ vms->gic_phandle = qemu_fdt_alloc_phandle(ms->fdt);
+ qemu_fdt_setprop_cell(ms->fdt, "/", "interrupt-parent", vms->gic_phandle);
+
+ qemu_fdt_add_subnode(ms->fdt, nodename);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "phandle", vms->gic_phandle);
+ qemu_fdt_setprop_string(ms->fdt, nodename, "compatible", "arm,gic-v5");
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "#interrupt-cells", 3);
+ qemu_fdt_setprop(ms->fdt, nodename, "interrupt-controller", NULL, 0);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "#address-cells", 0x2);
+ qemu_fdt_setprop_cell(ms->fdt, nodename, "#size-cells", 0x2);
+ qemu_fdt_setprop(ms->fdt, nodename, "ranges", NULL, 0);
+
+ /* The IRS node is a child of the top level /intc node */
+ irsnodename = g_strdup_printf("%s/irs@%" PRIx64,
+ nodename,
+ vms->memmap[VIRT_GICV5_IRS_NS].base);
+ qemu_fdt_add_subnode(ms->fdt, irsnodename);
+ qemu_fdt_setprop_string(ms->fdt, irsnodename, "compatible",
+ "arm,gic-v5-irs");
+ /*
+ * "reg-names" describes the frames whose address/size is in "reg";
+ * at the moment we have only the NS config register frame.
+ */
+ qemu_fdt_setprop_string(ms->fdt, irsnodename, "reg-names", "ns-config");
+ qemu_fdt_setprop_sized_cells(ms->fdt, irsnodename, "reg",
+ 2, vms->memmap[VIRT_GICV5_IRS_NS].base,
+ 2, vms->memmap[VIRT_GICV5_IRS_NS].size);
+ qemu_fdt_setprop_cell(ms->fdt, irsnodename, "#address-cells", 0x2);
+ qemu_fdt_setprop_cell(ms->fdt, irsnodename, "#size-cells", 0x2);
+ qemu_fdt_setprop(ms->fdt, irsnodename, "ranges", NULL, 0);
+
+ /*
+ * The "cpus" property is an array of phandles to the CPUs, and
+ * "iaffids" is an array of uint16 IAFFIDs. For virt, our IAFFIDs
+ * are the CPU indexes. This function is called after
+ * fdt_add_cpu_nodes(), which allocates the cpu_phandles array.
+ */
+ assert(vms->cpu_phandles);
+ for (int i = 0; i < ms->smp.cpus; i++) {
+ /*
+ * We have to byteswap each element here because we're setting the
+ * whole property value at once as a lump of raw data, not via a
+ * helper like qemu_fdt_setprop_cell() that does the swapping for us.
+ */
+ cpu_phandles[i] = cpu_to_be32(vms->cpu_phandles[i]);
+ iaffids[i] = cpu_to_be16(i);
+ }
+ qemu_fdt_setprop(ms->fdt, irsnodename, "cpus", cpu_phandles,
+ ms->smp.cpus * sizeof(*cpu_phandles));
+ qemu_fdt_setprop(ms->fdt, irsnodename, "arm,iaffids", iaffids,
+ ms->smp.cpus * sizeof(*iaffids));
+
+ /*
+ * When we implement the GICv5 IRS, it gets a DTB node which is a
+ * child of the IRS node.
+ */
+}
+
static void create_gicv5(VirtMachineState *vms, MemoryRegion *mem)
{
MachineState *ms = MACHINE(vms);
@@ -835,6 +901,8 @@ static void create_gicv5(VirtMachineState *vms, MemoryRegion *mem)
* that information is communicated directly between a GICv5 IRS and
* the GICv5 CPU interface via our equivalent of the stream protocol.
*/
+
+ fdt_add_gicv5_node(vms);
}
/*
--
2.43.0