December 8, 2025

Rip off a GIC 500 driver

I say this in a more dramatic way than necessary. I am not stealing anything, this is open source code.

After spending several hours studying the 930 page GICv3 manual, I decided to take a look at some driver code. A likely source would be the ATF (Arm trusted firmware) sources. Indeed, when I build this for the RK3399, I see this in the build log:

  CC      drivers/arm/cci/cci.c
  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
The file "cci.c" may or may not be relevant.

After spending some time looking at this code, I had another idea. Why not extract the GICv3 driver from the ATF source tree and press it into service for my project? There is nothing sneaky or illegitemate about this -- this is what open source is all about, and I don't intend to misrepresent it as anything I wrote myself. Working with actual driver code will be at least as educational as studying the GIC document and will complement it nicely.

My own education is at least as important as getting things to work.

Get busy

I add a "gic" directory to my RK3399 "inter" project and get to work. I put a "fetch" shell script in there that copies files from the ATF source tree. I have a recently cloned local ATF repository that I got when working on things with the RK3328, so I will use that as the source. The fetch script provides a record of exactly what was copied and where from.

Of course, we almost immediately get into the need to drag in header files. It remains to be seen if header files in turn pull in more header files. It is useful to run the command "ls -lR >zzz" and have a file to search to find out where dependency files are located.

I begin with gicv3_main.c and keep pulling in header files until I get it to compile. We get into some difficult decisions almost right away.

We encounter things like the following. Do we create a multitude of subdirectories or edit the sources to get all include files from the current directory? We decide to edit the sources.

#include 
In debug.h, something more interesting comes up:
#include 
At first I was terrified thinking that the ATF build was referencing system include files, but I find this in "include/lib/libc". I am however starting to worry about a clutter of include files building up and obscuring the gic specific files when I run ls. I decide to go ahead and create an "include" directory and shove them all in there, modifying my fetch script accordingly. I will also need a -I./include line in my Makefile.

Another issue pops up. I notice that I am getting no complaints about files including stdint.h, and I have yet to copy that from the ATF sources. Now I see this error:

unknown type name ‘u_register_t’
And indeed this is defined in ATF/include/lib/libc/stdint.h -- so we want that, not the standard system stdint.h that the compiler is finding somewhere.
The gcc switch for this is "-nostdinc" - and this does the trick.

Code Monkey

After a morning of rinse-lather-repeat "code monkey" work, I have 45 files racked up in my include directory. Of course I could have copied the entire "include" directory from ATF and saved myself a lot of work -- but that isn't the point. I am not just aiming to get something working using any old method. I really want a minimal set of files that gives me a working gicv3 driver.

Without a doubt, I will be able to prune away a lot of these files, but that will be a different sort of work from the mindless copying I have been doing.

I now get these errors as it tries to compile gicv3_main.c:

error: ‘NR_OF_IMAGES_IN_FW_BANK’ undeclared here (not in a function); did you mean ‘NR_OF_MAX_FW_BANKS’?
error: ‘NR_OF_FW_BANKS’ undeclared here (not in a function); did you mean ‘NR_OF_MAX_FW_BANKS’?
   47 |         struct fwu_image_bank_info img_bank_info[NR_OF_FW_BANKS];
On one hand, I have to wonder what went wrong that something is missing from the set of include files I copied. On the other hand, I wonder what a "FWU" is and if it matters at all to a GIC driver.

I do a recursive grep and find that these thinks seem to be defined dynamically in several Makefiles. The file docs/getting_started/build-options.rst talks about these macros, and says:

-  ``NR_OF_FW_BANKS``: Define the number of firmware banks. This flag is used
   in defining the firmware update metadata structure. This flag is by default
   set to '2'.

-  ``NR_OF_IMAGES_IN_FW_BANK``: Define the number of firmware images in each
   firmware bank. Each firmware bank must have the same number of images as per
   the `PSA FW update specification`_.
   This flag is used in defining the firmware update metadata structure. This
   flag is by default set to '1'.
The file that is looking for these values is fwu_metadata.h and the quick and easy thing to do (for now) is to add these lines to that file:
/* TJT */
#define NR_OF_FW_BANKS			2
#define NR_OF_IMAGES_IN_FW_BANK	1
I add my initials, as shown, so I can track down any such edits I make, this one and any that arise in the future.

A clean compile

Of gicv3_main.c anyway. There were two file ambiguities along the way where a file by the same name appeared in two different directories in the ATF hierarchy:
gic_common.h
sysreg128.h
I would be worth examing these in detail at some point and giving some thought. Knowing the include search path being used by gcc would resolve this, along perhaps with a careful examination of the locations where these files are included in case I made a mistake in my directory flattening.

Missing symbols

We may have found all the include file dependencies, but trying to link using this object file reveals a myriad of missing external references. Well, we knew this would be the next stage of this game.

Most of them are in other gic related C files. The list is:

$ATF/drivers/arm/gic/v3/gicv3_main.c
$ATF/drivers/arm/gic/v3/arm_gicv3_common.c
$ATF/drivers/arm/gic/v3/gic-x00.c
$ATF/drivers/arm/gic/v3/gicdv3_helpers.c
$ATF/drivers/arm/gic/v3/gicrv3_helpers.c
$ATF/drivers/arm/gic/v3/gicv3_helpers.c
The other missing references are interesting in their own right, if not directly related to the GIC driver.

gic_compat.c

I am adding this file as a glue layer. My existing code calls entry points in this file and it will make the appropriate calls to the ATF gicv3 driver.

el3_panic

I have added the various other gic*.c source files to the build and have gotten rid of most of the missing externals! I see a bunch of calls to console_flush() and el3_panic() coming from gicv3_main.c.

It turns out these come from a macro named panic() in debug.h. What I am going to do is to add a routine "gic_panic()" to my gic_compat.c. Then I'll change the panic() macros in debug.h to call this routine and things we be good. Or at least in my control.

references to RO_START and RO_END

These seem to come from bl_common.h -- and there is also RW_END. I edit bl_common.h and comment out the lines dealing with these. These are for big picture issues in a BLx build and aren't relevant to us at all. In fact it remains to be seen if we really need bl_common.h

reference to plat_my_core_pos from gicv3_main.c

This is defined in "plat/rockchip/common/aarch64/plat_helpers.S"
It looks like this:
func plat_my_core_pos
    mrs x0, mpidr_el1
    and x1, x0, #MPIDR_CPU_MASK
    and x0, x0, #MPIDR_CLUSTER_MASK
    add x0, x1, x0, LSR #PLAT_RK_CLST_TO_CPUID_SHIFT
    ret
endfunc plat_my_core_pos
The file is only 179 lines, and is quite interesting, but makes calls to a variety of other routines and pulls in its own collection of include files.

We just need to track down the MASK and SHIFT values and we can add just this routine to our own assembly startup file.

Here are the header files it pulls in:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
The file arch.h (include/arch/aarch64/arch.h) has the first two
#define MPIDR_AFFINITY_BITS U(8)
#define MPIDR_AFFLVL_MASK   ULL(0xff)

#define MPIDR_CPU_MASK      MPIDR_AFFLVL_MASK
#define MPIDR_CLUSTER_MASK  (MPIDR_AFFLVL_MASK << MPIDR_AFFINITY_BITS)
We find PLAT_RK_CLST_TO_CPUID_SHIFT in plat/rockchip/rk3399/include/platform_def.h
#define PLAT_RK_CLST_TO_CPUID_SHIFT	6
I put together a file "my_core.S" and we see to be OK.

flush_dcache_range

This is the last missing item. There are several calls to this from gicv3_main.c in the function gicv3_driver_init().

The code is in lib/aarch64/cache_helpers.S

We are done!

We are sort of done. It is 8 PM. Apart from a 2 hour break for a bike ride, I have been busy with this all day. We now have a complete and minimal collection of files, and we get a clean link with no missing symbols. No doubt we will be able to prune away a lot at this.
We have 17 files in "gic" and 49 files in "gic/include"

Consider though that this is not just about the gicv3 driver. There is a lot of high quality ARM code here that is worth looking at. This is also a good introduction to the ATF source tree, which has been one of my goals for some time.

The next step is to study this enough to make calls to it and get it to handle interrupts for me. Once I have working code with source that I know my way around in, I will be in a much better position to study the GIC document.


Have any comments? Questions? Drop me a line!

Tom's electronics pages / tom@mmto.org