/* Disable the legacy interrupt bypass */
icc_sre_el3 = ICC_SRE_DIB_BIT | ICC_SRE_DFB_BIT;
/*
* Enable system register access for EL3 and allow lower exception
* levels to configure the same for themselves. If the legacy mode is
* not supported, the SRE bit is RAO/WI
*/
icc_sre_el3 |= (ICC_SRE_EN_BIT | ICC_SRE_SRE_BIT);
write_icc_sre_el3(read_icc_sre_el3() | icc_sre_el3);
scr_el3 = read_scr_el3();
Note the routine read_icc_sre_el3()
You can look high and low for this and you won't find it.
Ctags cannot find it, even in the original ATF sources.
Both the read and write accessor functions get magically generated in arch_helpers.h by this line of code:
DEFINE_RENAME_SYSREG_RW_FUNCS(icc_sre_el1, ICC_SRE_EL1) DEFINE_RENAME_SYSREG_RW_FUNCS(icc_sre_el2, ICC_SRE_EL2) DEFINE_RENAME_SYSREG_RW_FUNCS(icc_sre_el3, ICC_SRE_EL3)I include the EL1 and EL2 versions also, just to be clear that they exist.
/* Define read & write function for renamed system register */
#define DEFINE_RENAME_SYSREG_RW_FUNCS(_name, _reg_name) \
_DEFINE_SYSREG_READ_FUNC(_name, _reg_name) \
_DEFINE_SYSREG_WRITE_FUNC(_name, _reg_name)
#define _DEFINE_SYSREG_READ_FUNC(_name, _reg_name) \
static inline u_register_t read_ ## _name(void) \
{ \
u_register_t v; \
__asm__ volatile ("mrs %0, " #_reg_name : "=r" (v)); \
return v; \
}
#define _DEFINE_SYSREG_READ_FUNC_NV(_name, _reg_name) \
static inline u_register_t read_ ## _name(void) \
{ \
u_register_t v; \
__asm__ ("mrs %0, " #_reg_name : "=r" (v)); \
return v; \
}
write_icc_sre_el3(read_icc_sre_el3() | icc_sre_el3);
Setting up inline accessor functions like this allows clean compact
and readable code to be written. I could review places where I
have used inline assembly to access system registers and revise
my code to use this methodology.
#define ICC_SRE_EN_BIT BIT_32(3) #define ICC_SRE_DIB_BIT BIT_32(2) #define ICC_SRE_DFB_BIT BIT_32(1) #define ICC_SRE_SRE_BIT BIT_32(0)The EN bit allows lower EL levels to access this register.
Note that the code above sets all of these bits. And given that the code shown actually runs at EL3 as part of BL31, this is the state we inherit things in.
The EL2 and EL1 flavors of this have the same bits, except that EL1 neither has nor needs the EN bit.
IS_IN_EL3() sre_el3 scr_el3 igrpen1_el3 sctlr_el3The first is easy, and obvious just by name IS_IN_EL3() -- it returns a boolean value to indicate whether or not we are running in EL3 and is used in assertions. It is used by assert(), it is in arch_helpers.h and boils down to this:
#define IS_IN_EL(x) \
(GET_EL(read_CurrentEl()) == MODE_EL##x)
#define IS_IN_EL1() IS_IN_EL(1)
#define IS_IN_EL2() IS_IN_EL(2)
#define IS_IN_EL3() IS_IN_EL(3)
Code in gicv3_cpuif_enable() manipulates a bit in the SCR register to transition to NS (non-secure) state in order to write to Non secure ICC_SRE_EL1 and ICC_SRE_EL2 registers. then it switches back to secure state. It also writes to the secure ICC_SRE_EL1 register. It writes to these registers to set the SRE bit, so that the switch is made to use system registers to access the GIC.
We won't need to do any of this as we will inherit this setup which will be done by BL31.
I note though in the code in gicv3_cpuif_enable() that it writes to certain el1 specific registers. No doubt the expectation is that things need to be set up for EL1. However the _el1 specific registers that are written are the secure mode versions. In particular "Group0" interrupts are enabled for EL1 -- for secure mode.
Both the IGRPEN1_EL3_ENABLE_G1NS_BIT and the IGRPEN1_EL3_ENABLE_G1S_BIT get cleared in gicv3_cpuif_disable(), which I don't particularly care about as I don't forsee disabling the interrupt controller at any time.
page 19 of the GICv3 manual talks about the concept of interrupt grouping.
We have Group0 and Group1. Group0 is handled by the highest active EL. Group 1 is split into secure and non-secure. Group1 secure can be handled at secure EL1 or EL2. Group1 non-secure will be handled at EL2 if a system is using virtualization, or at EL1 if not.
We will have to see how all this sorts out. I expect my code to run in EL2 non-secure. I might be wiser to drop from EL2 to EL1.
It is used in one line of code, in gicv3_rdistif_probe() as follows:
assert((read_sctlr_el3() & SCTLR_C_BIT) != 0U);This is called from gicv3_base.c (which I have not even copied to my collection). (See drivers/arm/gic/v3/gicv3_base.c) It is called from gic_pcpu_init() in that file.
This is called from bl31/bl31_main.c where we see:
unsigned int core_pos = plat_my_core_pos();
#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 */
Current EL = 2 Sctlr_el1 = 30D00800 Sctlr_el2 = 30C51835 MMU enabled D Cache enabled I Cache enabledNotice that the value for el1 is different and not relevant to us, as we are running in EL2. Also plain "sctlr" is meaningless on aarch64, it is entirely an aarch32 thing. There is also an sctlr2_elx (in 3 flavors), which we are not yet worring about.
The above tests bit 2 (the C bit). There is also an I bit (bit 12) that enables the I cache. Everything I read tells me that enabling the D cache (via the C bit) also enables the L2 cache.
It will be interesting to inspect the MMU tables and see how they are set up for the huge address space this 64 bit processor has.
Tom's electronics pages / tom@mmto.org