December 14, 2025

The GICv3 driver - more on initialization

I believe that I got led astray trying to incorporate the file gicv3_base.c

When I built bl31 for the RK3399, I did a "grep" for "gic" on the build log and found only these files:

  CC      drivers/arm/gic/v3/arm_gicv3_common.c
  CC      drivers/arm/gic/v3/gic-x00.c
  CC      drivers/arm/gic/v3/gicdv3_helpers.c
  CC      drivers/arm/gic/v3/gicrv3_helpers.c
  CC      drivers/arm/gic/v3/gicv3_helpers.c
  CC      drivers/arm/gic/v3/gicv3_main.c
No mention of gicv3_base.c in that build.
Here is the entire output from the build process:
  • BL31 build

    Then I discovered the routine gicv3_rdistif_probe() in gicv3_main.c. I searched for where this was called from. This led me to gic_pcpu_init() in gicv3_base.c I searched for where this was called from, and this led me to bl31/bl31_main.c

    The calls in bl31_main.c are conditional on "USE_GIC_DRIVER" and look like this:

    #if USE_GIC_DRIVER
        /*
         * Initialize the GIC driver as well as per-cpu and global interfaces.
         * Platform has had an opportunity to initialise specifics.
         */
        gic_init(core_pos);
        gic_pcpu_init(core_pos);
        gic_cpuif_enable(core_pos);
    #endif /* USE_GIC_DRIVER */
    
    Prior to this, I had discovered code in plat/rockchip/common/rockchip_gicv3.c that initializes the driver, but I don't see this file included in the build list either!

    Rebuild bl31 and take a second look

    I need to do this:
    cd /Projects/OrangePi/r1plus/sources/arm-trusted-firmware
    make distclean
    make PLAT="rk3399" DEBUG=1 bl31
    
    I do this, save the build messages, and search them. I see file files I mention above in drivers/arm/gic/v3, but also:
    plat/common/plat_gicv3.c
    plat/rockchip/common/rockchip_gicv3.c
    
    But no mention of gicv3_base.c with the probing.

    A new discovery - I should shift to EL1

    I am trying to find the base address of the GIC CPU interface. the GIC has 3 main components I care about, the DIST (distributor), RDIST (redistributor), and CPU interface.

    The DIST and RDIST have clearly designated base addresses. I don't find any base address for the CPU interface! I would like to read the "HPPIR" register to ask about the highest priority pending interrupt. I see it available, not as a memory mapped register, but as a system register. But I only see an "ICC_HPPIR0_EL1" register and no EL2 counterpart.

    I look at my routine intcon_irqwho() which I use when I get an interrupt to ask exactly which IRQ it was that interrupted. This calls gicv3_get_pending_interrupt_id(), which in turn calls read_icc_hppir0_el1() to find out.

    I see someone asking about interrupts, the gic, and EL2 and the answer is: "I do not think EL2 is meant to handle interrupt itself. It will direct them to the respective "guest""

    As I read more I learn that interrupts at EL2 expect to use various HCR registers (Hypervisor control registers) like ICH_HCR_EL2 and ICH_LR_EL2 and the documentation talks about these as "virtual interrupts".

    Conclusion

    I am barking up the wrong tree trying to get interrupts to work at EL2 with the GICv3. The fact that I have no access to the CPU interface via system registers at EL2 underscores this. I can make interrupts work at EL2 with the older GICv2 (and I did on the RK3328).

    I should write the code to do the EL2 to EL1 transition and see what happens then. Linux runs at EL1 after all, and this is the use case that software like ATF and U-boot is written to support.

    What is interesting is that with GIC v2 I had very little trouble getting interrupts to work at EL2. In that case, the CPU interface was memory mapped and there was no need to use system registers. This was for the RK3328.


    Have any comments? Questions? Drop me a line!

    Tom's electronics pages / tom@mmto.org