Skip to content

Commit

Permalink
hw/arm/virt: Support TCG GICv4
Browse files Browse the repository at this point in the history
Add support for the TCG GICv4 to the virt board. For the board,
the GICv4 is very similar to the GICv3, with the only difference
being the size of the redistributor frame. The changes here are thus:
 * calculating virt_redist_capacity correctly for GICv4
 * changing various places which were "if GICv3" to be "if not GICv2"
 * the commandline option handling

Note that using GICv4 reduces the maximum possible number of CPUs on
the virt board from 512 to 317, because we can now only fit half as
many redistributors into the redistributor regions we have defined.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20220408141550.1271295-42-peter.maydell@linaro.org
  • Loading branch information
pm215 committed Apr 22, 2022
1 parent e741a80 commit ad4659a
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 20 deletions.
5 changes: 4 additions & 1 deletion docs/system/arm/virt.rst
Expand Up @@ -99,11 +99,14 @@ gic-version
GICv2. Note that this limits the number of CPUs to 8.
``3``
GICv3. This allows up to 512 CPUs.
``4``
GICv4. Requires ``virtualization`` to be ``on``; allows up to 317 CPUs.
``host``
Use the same GIC version the host provides, when using KVM
``max``
Use the best GIC version possible (same as host when using KVM;
currently same as ``3``` for TCG, but this may change in future)
with TCG this is currently ``3`` if ``virtualization`` is ``off`` and
``4`` if ``virtualization`` is ``on``, but this may change in future)

its
Set ``on``/``off`` to enable/disable ITS instantiation. The default is ``on``
Expand Down
70 changes: 53 additions & 17 deletions hw/arm/virt.c
Expand Up @@ -522,7 +522,7 @@ static void fdt_add_gic_node(VirtMachineState *vms)
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);
if (vms->gic_version == VIRT_GIC_VERSION_3) {
if (vms->gic_version != VIRT_GIC_VERSION_2) {
int nb_redist_regions = virt_gicv3_redist_region_count(vms);

qemu_fdt_setprop_string(ms->fdt, nodename, "compatible",
Expand Down Expand Up @@ -708,6 +708,9 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
case VIRT_GIC_VERSION_3:
revision = 3;
break;
case VIRT_GIC_VERSION_4:
revision = 4;
break;
default:
g_assert_not_reached();
}
Expand All @@ -722,7 +725,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
qdev_prop_set_bit(vms->gic, "has-security-extensions", vms->secure);
}

if (vms->gic_version == VIRT_GIC_VERSION_3) {
if (vms->gic_version != VIRT_GIC_VERSION_2) {
uint32_t redist0_capacity = virt_redist_capacity(vms, VIRT_GIC_REDIST);
uint32_t redist0_count = MIN(smp_cpus, redist0_capacity);

Expand Down Expand Up @@ -756,7 +759,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
gicbusdev = SYS_BUS_DEVICE(vms->gic);
sysbus_realize_and_unref(gicbusdev, &error_fatal);
sysbus_mmio_map(gicbusdev, 0, vms->memmap[VIRT_GIC_DIST].base);
if (vms->gic_version == VIRT_GIC_VERSION_3) {
if (vms->gic_version != VIRT_GIC_VERSION_2) {
sysbus_mmio_map(gicbusdev, 1, vms->memmap[VIRT_GIC_REDIST].base);
if (nb_redist_regions == 2) {
sysbus_mmio_map(gicbusdev, 2,
Expand Down Expand Up @@ -794,7 +797,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)
ppibase + timer_irq[irq]));
}

if (vms->gic_version == VIRT_GIC_VERSION_3) {
if (vms->gic_version != VIRT_GIC_VERSION_2) {
qemu_irq irq = qdev_get_gpio_in(vms->gic,
ppibase + ARCH_GIC_MAINT_IRQ);
qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt",
Expand All @@ -820,7 +823,7 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem)

fdt_add_gic_node(vms);

if (vms->gic_version == VIRT_GIC_VERSION_3 && vms->its) {
if (vms->gic_version != VIRT_GIC_VERSION_2 && vms->its) {
create_its(vms);
} else if (vms->gic_version == VIRT_GIC_VERSION_2) {
create_v2m(vms);
Expand Down Expand Up @@ -1672,10 +1675,10 @@ static uint64_t virt_cpu_mp_affinity(VirtMachineState *vms, int idx)
* purposes are to make TCG consistent (with 64-bit KVM hosts)
* and to improve SGI efficiency.
*/
if (vms->gic_version == VIRT_GIC_VERSION_3) {
clustersz = GICV3_TARGETLIST_BITS;
} else {
if (vms->gic_version == VIRT_GIC_VERSION_2) {
clustersz = GIC_TARGETLIST_BITS;
} else {
clustersz = GICV3_TARGETLIST_BITS;
}
}
return arm_cpu_mp_affinity(idx, clustersz);
Expand Down Expand Up @@ -1808,6 +1811,10 @@ static void finalize_gic_version(VirtMachineState *vms)
error_report(
"gic-version=3 is not supported with kernel-irqchip=off");
exit(1);
case VIRT_GIC_VERSION_4:
error_report(
"gic-version=4 is not supported with kernel-irqchip=off");
exit(1);
}
}

Expand Down Expand Up @@ -1845,6 +1852,9 @@ static void finalize_gic_version(VirtMachineState *vms)
case VIRT_GIC_VERSION_2:
case VIRT_GIC_VERSION_3:
break;
case VIRT_GIC_VERSION_4:
error_report("gic-version=4 is not supported with KVM");
exit(1);
}

/* Check chosen version is effectively supported by the host */
Expand All @@ -1868,14 +1878,25 @@ static void finalize_gic_version(VirtMachineState *vms)
case VIRT_GIC_VERSION_MAX:
if (module_object_class_by_name("arm-gicv3")) {
/* CONFIG_ARM_GICV3_TCG was set */
vms->gic_version = VIRT_GIC_VERSION_3;
if (vms->virt) {
/* GICv4 only makes sense if CPU has EL2 */
vms->gic_version = VIRT_GIC_VERSION_4;
} else {
vms->gic_version = VIRT_GIC_VERSION_3;
}
} else {
vms->gic_version = VIRT_GIC_VERSION_2;
}
break;
case VIRT_GIC_VERSION_HOST:
error_report("gic-version=host requires KVM");
exit(1);
case VIRT_GIC_VERSION_4:
if (!vms->virt) {
error_report("gic-version=4 requires virtualization enabled");
exit(1);
}
break;
case VIRT_GIC_VERSION_2:
case VIRT_GIC_VERSION_3:
break;
Expand Down Expand Up @@ -2043,14 +2064,16 @@ static void machvirt_init(MachineState *machine)
vms->psci_conduit = QEMU_PSCI_CONDUIT_HVC;
}

/* The maximum number of CPUs depends on the GIC version, or on how
* many redistributors we can fit into the memory map.
/*
* The maximum number of CPUs depends on the GIC version, or on how
* many redistributors we can fit into the memory map (which in turn
* depends on whether this is a GICv3 or v4).
*/
if (vms->gic_version == VIRT_GIC_VERSION_3) {
if (vms->gic_version == VIRT_GIC_VERSION_2) {
virt_max_cpus = GIC_NCPU;
} else {
virt_max_cpus = virt_redist_capacity(vms, VIRT_GIC_REDIST) +
virt_redist_capacity(vms, VIRT_HIGH_GIC_REDIST2);
} else {
virt_max_cpus = GIC_NCPU;
}

if (max_cpus > virt_max_cpus) {
Expand Down Expand Up @@ -2438,16 +2461,29 @@ static void virt_set_mte(Object *obj, bool value, Error **errp)
static char *virt_get_gic_version(Object *obj, Error **errp)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
const char *val = vms->gic_version == VIRT_GIC_VERSION_3 ? "3" : "2";
const char *val;

switch (vms->gic_version) {
case VIRT_GIC_VERSION_4:
val = "4";
break;
case VIRT_GIC_VERSION_3:
val = "3";
break;
default:
val = "2";
break;
}
return g_strdup(val);
}

static void virt_set_gic_version(Object *obj, const char *value, Error **errp)
{
VirtMachineState *vms = VIRT_MACHINE(obj);

if (!strcmp(value, "3")) {
if (!strcmp(value, "4")) {
vms->gic_version = VIRT_GIC_VERSION_4;
} else if (!strcmp(value, "3")) {
vms->gic_version = VIRT_GIC_VERSION_3;
} else if (!strcmp(value, "2")) {
vms->gic_version = VIRT_GIC_VERSION_2;
Expand Down Expand Up @@ -2905,7 +2941,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
virt_set_gic_version);
object_class_property_set_description(oc, "gic-version",
"Set GIC version. "
"Valid values are 2, 3, host and max");
"Valid values are 2, 3, 4, host and max");

object_class_property_add_str(oc, "iommu", virt_get_iommu, virt_set_iommu);
object_class_property_set_description(oc, "iommu",
Expand Down
12 changes: 10 additions & 2 deletions include/hw/arm/virt.h
Expand Up @@ -113,6 +113,7 @@ typedef enum VirtGICType {
VIRT_GIC_VERSION_HOST,
VIRT_GIC_VERSION_2,
VIRT_GIC_VERSION_3,
VIRT_GIC_VERSION_4,
VIRT_GIC_VERSION_NOSEL,
} VirtGICType;

Expand Down Expand Up @@ -188,15 +189,22 @@ bool virt_is_acpi_enabled(VirtMachineState *vms);
/* Return number of redistributors that fit in the specified region */
static uint32_t virt_redist_capacity(VirtMachineState *vms, int region)
{
return vms->memmap[region].size / GICV3_REDIST_SIZE;
uint32_t redist_size;

if (vms->gic_version == VIRT_GIC_VERSION_3) {
redist_size = GICV3_REDIST_SIZE;
} else {
redist_size = GICV4_REDIST_SIZE;
}
return vms->memmap[region].size / redist_size;
}

/* Return the number of used redistributor regions */
static inline int virt_gicv3_redist_region_count(VirtMachineState *vms)
{
uint32_t redist0_capacity = virt_redist_capacity(vms, VIRT_GIC_REDIST);

assert(vms->gic_version == VIRT_GIC_VERSION_3);
assert(vms->gic_version != VIRT_GIC_VERSION_2);

return (MACHINE(vms)->smp.cpus > redist0_capacity &&
vms->highmem_redists) ? 2 : 1;
Expand Down

0 comments on commit ad4659a

Please sign in to comment.