[BOOTWRAPPER PATCH v3 2/2] Add support for GICv5
Sascha Bischoff
Sascha.Bischoff at arm.com
Fri May 29 08:34:40 PDT 2026
Hi Vladimir,
Thanks for the rapid turn-around. This is now looking good to me. I've
tested things again and have confirmed that they're working for me.
One small thing to point out is that I got this when applying:
Applying: Add support for GICv5
.git/rebase-apply/patch:203: new blank line at EOF.
+
warning: 1 line adds whitespace errors.
It looks like you have an extra newline, which must have been there
previously too. Sorry for missing it. In either case, this looks to be
in a good state now:
Reviewed-by: Sascha Bischoff <sascha.bischoff at arm.com>
Thanks,
Sascha
On Fri, 2026-05-29 at 10:50 +0100, Vladimir Murzin wrote:
> Performs the minimal initialization required for GICv5 support. GICv5
> support can be requested with --with-gic=v5.
>
> Signed-off-by: Vladimir Murzin <vladimir.murzin at arm.com>
> Tested-by: Sascha Bischoff <sascha.bischoff at arm.com>
> ---
> Makefile.am | 7 ++
> arch/aarch64/include/asm/cpu.h | 11 +++
> common/gic-v5.c | 132
> +++++++++++++++++++++++++++++++++
> configure.ac | 7 +-
> scripts/FDT.pm | 16 ++++
> scripts/findbase-by-regname.pl | 44 +++++++++++
> 6 files changed, 214 insertions(+), 3 deletions(-)
> create mode 100644 common/gic-v5.c
> create mode 100755 scripts/findbase-by-regname.pl
>
> diff --git a/Makefile.am b/Makefile.am
> index 2710494..aacd639 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -82,6 +82,13 @@ PSCI_NODE :=
> CPU_NODES :=
> endif
>
> +if GICV5
> +GIC_IRS_BASE := $(shell perl -I $(SCRIPT_DIR)
> $(SCRIPT_DIR)/findbase-by-regname.pl $(KERNEL_DTB) "el3-config"
> 'arm,gic-v5-irs')
> +GIC_IWB_BASE := $(shell perl -I $(SCRIPT_DIR)
> $(SCRIPT_DIR)/findbase.pl $(KERNEL_DTB) 0 'arm,gic-v5-iwb')
> +DEFINES += -DGIC_IRS_BASE=$(GIC_IRS_BASE)
> +DEFINES += -DGIC_IWB_BASE=$(GIC_IWB_BASE)
> +endif
> +
> if GICV3
> GIC_DIST_BASE := $(shell perl -I $(SCRIPT_DIR)
> $(SCRIPT_DIR)/findbase.pl $(KERNEL_DTB) 0 'arm,gic-v3')
> GIC_RDIST_BASE := $(shell perl -I $(SCRIPT_DIR)
> $(SCRIPT_DIR)/findbase.pl $(KERNEL_DTB) 1 'arm,gic-v3')
> diff --git a/arch/aarch64/include/asm/cpu.h
> b/arch/aarch64/include/asm/cpu.h
> index ac50474..af4191c 100644
> --- a/arch/aarch64/include/asm/cpu.h
> +++ b/arch/aarch64/include/asm/cpu.h
> @@ -128,6 +128,7 @@
> #define ID_AA64PFR1_EL1_THE BITS(51, 48)
>
> #define ID_AA64PFR2_EL1 s3_0_c0_c4_2
> +#define ID_AA64PFR2_EL1_GCIE BITS(15, 12)
> #define ID_AA64PFR2_EL1_FPMR BITS(35, 32)
>
> #define ID_AA64SMFR0_EL1 s3_0_c0_c4_5
> @@ -169,6 +170,11 @@
> #define ICC_CTLR_EL3 S3_6_C12_C12_4
> #define ICC_PMR_EL1 S3_0_C4_C6_0
>
> +#define ICC_PPI_DOMAINR0_EL3 S3_6_C12_C8_4
> +#define ICC_PPI_DOMAINR1_EL3 S3_6_C12_C8_5
> +#define ICC_PPI_DOMAINR2_EL3 S3_6_C12_C8_6
> +#define ICC_PPI_DOMAINR3_EL3 S3_6_C12_C8_7
> +
> #define VSTCR_EL2 s3_4_c2_c6_2
> #define VSCTLR_EL2 s3_4_c2_c0_0
>
> @@ -245,6 +251,11 @@ static inline int has_gicv3_sysreg(void)
> return !!mrs_field(ID_AA64PFR0_EL1, GIC);
> }
>
> +static inline int has_gicv5_sysreg(void)
> +{
> + return !!mrs_field(ID_AA64PFR2_EL1, GCIE);
> +}
> +
> #endif /* !__ASSEMBLY__ */
>
> #endif
> diff --git a/common/gic-v5.c b/common/gic-v5.c
> new file mode 100644
> index 0000000..49b7b0e
> --- /dev/null
> +++ b/common/gic-v5.c
> @@ -0,0 +1,132 @@
> +/*
> + * gic-v5.c
> + *
> + * Copyright (C) 2025 ARM Limited. All rights reserved.
> + *
> + * Use of this source code is governed by a BSD-style license that
> can be
> + * found in the LICENSE.txt file.
> + */
> +
> +#include <stdint.h>
> +
> +#include <cpu.h>
> +#include <gic.h>
> +#include <asm/io.h>
> +
> +#define IWB_IDR0 0x0
> +#define IWB_IDR0_IW_RANGE_SHIFT 0x0
> +#define IWB_IDR0_IW_RANGE_MASK 0x7ff
> +
> +#define IWB_CR0 0x80
> +#define IWB_CR0_IWBEN (1 << 0)
> +#define IWB_CR0_IDLE (1 << 1)
> +
> +#define IWB_WENABLE_STATUSR 0xc0
> +#define IWB_WENABLE_STATUSR_IDLE (1 << 0)
> +
> +#define IWB_WDOMAIN_STATUSR 0xc4
> +#define IWB_WDOMAIN_STATUSR_IDLE (1 << 0)
> +
> +#define IWB_WENABLER 0x2000
> +#define IWB_WDOMAINR 0x8000
> +
> +#define IRS_IDR6 0x0018
> +#define IRS_IDR6_SPI_IRS_RANGE_MASK 0x1ffffff
> +
> +#define IRS_IDR7 0x001c
> +#define IRS_IDR7_SPI_BASE_MASK 0xffffff
> +
> +#define IRS_SPI_SELR 0x108
> +#define IRS_SPI_DOMAINR 0x10c
> +
> +#define IRS_SPI_STATUSR 0x0118
> +#define IRS_SPI_STATUSR_IDLE (1 << 0)
> +
> +static void gic_iwb_init(void) {
> + void *iwb_ptr = (void *)GIC_IWB_BASE;
> + unsigned int num;
> + unsigned int i;
> +
> + /* Get number of implemented wire control registers */
> + num = ((raw_readl(iwb_ptr + IWB_IDR0) >>
> IWB_IDR0_IW_RANGE_SHIFT) & IWB_IDR0_IW_RANGE_MASK) + 1;
> +
> + /* Disable all wires */
> + for (i = 0; i < num; i++)
> + raw_writel(0, iwb_ptr + IWB_WENABLER + i * 4);
> +
> + while (!(raw_readl(iwb_ptr + IWB_WENABLE_STATUSR) &
> IWB_WENABLE_STATUSR_IDLE));
> +
> + /* Assign all wires to Non-Secure domain */
> + for (i = 0; i < num * 2; i++)
> + raw_writel(0x55555555, iwb_ptr + IWB_WDOMAINR + i *
> 4);
> +
> + while (!(raw_readl(iwb_ptr + IWB_WDOMAIN_STATUSR) &
> IWB_WDOMAIN_STATUSR_IDLE));
> +
> + /* Enable IWB */
> + raw_writel(IWB_CR0_IWBEN, iwb_ptr + IWB_CR0);
> +
> + while (!(raw_readl(iwb_ptr + IWB_CR0) & IWB_CR0_IDLE));
> +}
> +
> +static void gic_irs_init(void) {
> + void *irs_ptr = (void *)GIC_IRS_BASE;
> + unsigned int range;
> + unsigned int base;
> + unsigned int i;
> +
> + /* Get the range of implemented SPIs */
> + base = raw_readl(irs_ptr + IRS_IDR7) &
> IRS_IDR7_SPI_BASE_MASK;
> + range = raw_readl(irs_ptr + IRS_IDR6) &
> IRS_IDR6_SPI_IRS_RANGE_MASK;
> +
> + for (i = base; i < base + range; i++) {
> + /* Select SPI */
> + raw_writel(i, irs_ptr + IRS_SPI_SELR);
> + while (!(raw_readl(irs_ptr + IRS_SPI_STATUSR) &
> IRS_SPI_STATUSR_IDLE));
> +
> + /* Assign SPI to Non-Secure domain */
> + raw_writel(1, irs_ptr + IRS_SPI_DOMAINR);
> + while (!(raw_readl(irs_ptr + IRS_SPI_STATUSR) &
> IRS_SPI_STATUSR_IDLE));
> + }
> +}
> +
> +static void gic_ppi_init(void) {
> + uint64_t val = 0;
> +
> + val |= 1UL << (2 * 31); // Trace Buffer Unit
> + val |= 1UL << (2 * 30); // EL1 Physical Timer
> + val |= 1UL << (2 * 28); // Non-secure EL2 Virtual Timer
> + val |= 1UL << (2 * 27); // EL1 Virtual Timer
> + val |= 1UL << (2 * 26); // Non-secure EL2 Physical Timer
> + val |= 1UL << (2 * 25); // GIC maintenance interrupt
> + val |= 1UL << (2 * 24); // Generic CTI interrupt trigger
> event
> + val |= 1UL << (2 * 23); // PMU overflow interrupt request
> + val |= 1UL << (2 * 22); // Debug communication channel
> + val |= 1UL << (2 * 21); // Profiling Buffer management
> interrupt request
> + val |= 1UL << (2 * 15); // Hardware accelerator for cleaning
> Dirty state interrupt
> + val |= 1UL << (2 * 3); // Reserved for software usage
> +
> + /* Assign PPI to Non-Secure domain */
> + msr(ICC_PPI_DOMAINR0_EL3, val);
> + isb();
> +}
> +
> +void gic_secure_init(void)
> +{
> + /*
> + * If GICv5 is not available, skip initialisation. The OS
> will probably
> + * fail with a warning, but this should be easier to debug
> than a
> + * failure within the boot wrapper.
> + */
> + if (!has_gicv5_sysreg())
> + return;
> +
> + if (this_cpu_logical_id() == 0) {
> + gic_iwb_init();
> + gic_irs_init();
> + }
> +
> + gic_ppi_init();
> +
> + return;
> +}
> +
> diff --git a/configure.ac b/configure.ac
> index 6f486c4..f4faff7 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -141,18 +141,19 @@ AC_SUBST([XEN_CMDLINE], [$X_CMDLINE])
>
>
> AC_ARG_WITH([gic],
> - AS_HELP_STRING([--with-gic={v2|v3}], [select GIC version]),
> + AS_HELP_STRING([--with-gic={v2|v3|v5}], [select GIC version]),
> [GIC_VERSION=$withval],
> [GIC_VERSION=v2])
>
> AS_CASE([$GIC_VERSION],
> - [v2|v3], [],
> - [AC_MSG_ERROR([Invalid GIC version: $GIC_VERSION (use v2 or
> v3)])])
> + [v2|v3|v5], [],
> + [AC_MSG_ERROR([Invalid GIC version: $GIC_VERSION (use v2, v3, or
> v5)])])
>
> AC_SUBST([GIC_VERSION], [$GIC_VERSION])
>
> AM_CONDITIONAL([GICV2], [test "x$GIC_VERSION" = "xv2"])
> AM_CONDITIONAL([GICV3], [test "x$GIC_VERSION" = "xv3"])
> +AM_CONDITIONAL([GICV5], [test "x$GIC_VERSION" = "xv5"])
>
>
> # Ensure that we have all the needed programs
> diff --git a/scripts/FDT.pm b/scripts/FDT.pm
> index 9adf70b..3f49ba6 100755
> --- a/scripts/FDT.pm
> +++ b/scripts/FDT.pm
> @@ -322,6 +322,22 @@ sub get_num_reg_cells
> return ($ac, $sc);
> }
>
> +sub get_regname_idx
> +{
> + my $self = shift;
> + my $regname = shift;
> +
> + my $prop = $self->get_property("reg-names");
> +
> + return undef if (not defined($prop));
> +
> + my @names = $prop->read_strings();
> +
> + my ($idx) = grep { $names[$_] eq $regname } 0 .. $#names;
> +
> + return $idx;
> +}
> +
> sub translate_address
> {
> my $self = shift;
> diff --git a/scripts/findbase-by-regname.pl b/scripts/findbase-by-
> regname.pl
> new file mode 100755
> index 0000000..49cd0ce
> --- /dev/null
> +++ b/scripts/findbase-by-regname.pl
> @@ -0,0 +1,44 @@
> +#!/usr/bin/perl -w
> +# Find device register base addresses.
> +#
> +# Usage: ./$0 <DTB> <regname> <compatible ...>
> +#
> +# Copyright (C) 2026 ARM Limited. All rights reserved.
> +#
> +# Use of this source code is governed by a BSD-style license that
> can be
> +# found in the LICENSE.txt file.
> +
> +use warnings;
> +use strict;
> +
> +use FDT;
> +
> +my $filename = shift;
> +die("No filename provided") unless defined($filename);
> +
> +my $regname = shift;
> +die("no reg regname provided") unless defined($regname);
> +
> +my @compats = shift;
> +
> +open (my $fh, "<:raw", $filename) or die("Unable to open file
> '$filename'");
> +
> +my $fdt = FDT->parse($fh) or die("Unable to parse DTB");
> +
> +my $root = $fdt->get_root();
> +
> +my @devs = ();
> +for my $compat (@compats) {
> + push @devs, $root->find_compatible($compat);
> +}
> +
> +# We only care about finding the first matching device
> +my $dev = shift @devs;
> +die("No matching devices found") if (not defined($dev));
> +
> +my $idx = $dev->get_regname_idx($regname);
> +die("Cannot find reg name $regname") if (not defined($idx));
> +my ($addr, $size) = $dev->get_translated_reg($idx);
> +die("Cannot find reg entry $idx") if (not defined($addr) or not
> defined($size));
> +
> +printf("0x%016x\n", $addr);
More information about the linux-arm-kernel
mailing list