December 9, 2025

Using the extracted GICv3 driver

See this link for the discussion of extracting the gic v3 driver from the ATF sources: At this point, we have a clean compile, but hardly a clue about how to use the driver.

It turns out, as you will see below, all of the public entry points are in the file gicv3_main.c

Search for calls to the initialization entry points

As a starting point, I search for a call to gicv3_driver_init() and I find it in plat/rockchip/common/rockchip_gicv3.c as:

gicv3_driver_init(&rockchip_gic_data);
Calls to functions named plat_rockchip_gic_*() are found in:
plat/rockchip/rk3399/drivers/pmu/pmu.c
plat/rockchip/common/sp_min_plat_setup.c
plat/rockchip/common/plat_pm.c
plat/rockchip/common/bl31_plat_setup.c
plat/rockchip/common/rockchip_gicv3.c (the routines themselves)
The functions called are:
plat_rockchip_gic_cpuif_enable()
plat_rockchip_gic_init()
plat_rockchip_gic_driver_init()
plat_rockchip_gic_cpuif_disable()
plat_rockchip_gic_pcpu_init()

How do we initialize the driver?

This is largely answered from the above, but we make a call to: gicv3_driver_init() in gicv3_main.c
void __init gicv3_driver_init(const gicv3_driver_data_t *plat_driver_data)

What about calls to enable a specific interrupt?

In my old GIC v2 code, I called this "intcon_ena(irq)" and the game was to set a bit in a distributor "eset" register.

In the ATF gic driver, the register layout is established and manipulated by code in gicv3_private.h. We have these functions to set and clear bits in the enable registers. Notice that these are now in the R (redistributor) rather than the D (distributor).

void gicr_set_isenabler(uintptr_t base, unsigned int id);
void gicr_set_icenabler(uintptr_t base, unsigned int id);
A pair of routines in gicv3_main.c look like the jackpot:
void gicv3_enable_interrupt(unsigned int id, unsigned int proc_num)
void gicv3_disable_interrupt(unsigned int id, unsigned int proc_num)
Here "id" is what I call IRQ and we specify the processor that should handle the interrupt.

Here is another handy jackpot in the same file:

void gicv3_raise_sgi(unsigned int sgi_num, gicv3_irq_group_t group, u_register_t target)

What about calls used in the interrupt handler?

I had "intcon_irqwho()" and "intcon_irqack(irq)".

The intcon_irqwho() call simply returned the value of the "iack" register in the CPU section of the GIC.

The intcon_irqack(irq) call wrote the IRQ number to the CPU "eoi" register then called "gic_unpend(irq)".

The gic_unpend(irq) call wrote a bit to a "pclear" register in the distributor. This bit corresponded to the bit set by intcon_ena();

Two calls look interesting in gicv3_main.c

unsigned int gicv3_get_pending_interrupt_id(void)
unsigned int gicv3_get_pending_interrupt_type(void)
The first looks like what I want. It returns the ID of the highest priority pending interrupt at the CPU interface. The second returns the "group" which involves the secure/non-secure grouping. This would very well be relevant for code running at EL3, but probably not for me.

The "unpend" call looks like the following routine in gicv3_main.c

void gicv3_clear_interrupt_pending(unsigned int id, unsigned int proc_num)

Don't forget to zero the bss area!

I look at the spinlock code, expecting to find an initialization routine. It simply uses an "int" variable, which is uninitialized and thus expected to be zero. I have been thus far playing fast and loose with regard to BSS initialization, but this reminds me to add that code to start.S

What about initialized variables.

Synchronous exceptions

I use the SVC instruction to provoke a synchronous exception. This is useful because it happens independently from the GIC and can demonstrate that the vector table at least is set up properly.

The ESR (exception syndrome register) will tell you what kind of synchronous exception took place. The upper 6 bits of this register have the EC (exception class) value and this is what you want.

The values here seem to be a well guarded secret. Some fellow wrote a decoder in rust:

Thus far, this is the only way I have found to find our what the EC values are. They claim you can dig this information out of the ARM Architecture Manual.

Look at the file src/esr/mod.rs -- here are some values:

0x15 - SVC in aarch64 state
0x20 - Instruction abort from lower EL
0x21 - Instruction abort from same EL
0x24 - Data abort from lower EL
0x25 - Data abort from same EL

This is also handy and somewhat related:


Have any comments? Questions? Drop me a line!

Tom's electronics pages / tom@mmto.org