Switching Security state
Control over Security state is performed at EL3, which sets the Security state of lower exception levels.
Specifically, setting the leading bit of the Secure Configuration Register SCR_EL3
, will put the system into a Non-secure state, after the system returns to a lower exception level. However, this is not the only change that you will have to make, because the Non-secure state introduces some complexities. Follow these steps to ensure that any instructions that are executed while they are in Non-secure state are in Non-secure memory:
- Modify
scatter.txt
:SROM_LOAD 0x00000000 { SROM_EXEC +0 { startup.o(BOOT, +FIRST) gic.o } STACK_EL3 0x04000000 ALIGN 64 EMPTY 0x10000 {} } NSROM_LOAD 0x80000000 { ROM_EXEC +0 0x10000 { startup.o(NONSECURE) * (+RO) } RAM_EXEC +0 0x10000 { * (+RW, +ZI) } ARM_LIB_STACKHEAP +0 ALIGN 64 EMPTY 0x10000 {} STACK_EL2 +0 ALIGN 64 EMPTY 0x10000 {} }
This scatter file defines a new region of memory,NSROM_LOAD
, starting at the Non-secure DRAM portion of the memory in the model. TheNONSECURE
section of our startup code, which you will define later, is placed in this region. Wildcard data has been placed in this region, so that all data which is not placed elsewhere will be placed in the relevant regions here. You have also defined a stack for EL2, and moved the library stack-heap here. TheSROM_LOAD
region is located in Secure memory, and thegic.o
andBOOT
sections of the code are also in this region. The EL3 stack has been placed in Secure SRAM. - Because the code branches to
__main
in Non-secure EL1, you must change references to the Secure timer registers to the Non-secure timer registers. Modifytimer.s
to replace accesses toCNTPS_TVAL_EL1
andCNTPS_CTL_EL1
withCNTP_TVAL_EL0
andCNTP_CTL_EL0
. - Define the EL1 and EL2 entry functions in
startup.s
, and wrap them into the section namedNONSECURE
, so that they are placed in Non-secure memory.// ------------------------------------------------------------ // EL2 AArch64 // ------------------------------------------------------------ .section NONSECURE, "ax" .align 3 .global el2_entry_aarch64 .type el2_entry_aarch64, "function" el2_entry_aarch64: NOP ADRP x0, Image$$STACK_EL2$$ZI$$Limit MOV sp, x0 // Configure HCR_EL2 - the hypervisor configuration register // --------------------------------------------------------- NOP MRS x0, HCR_EL2 MOV x1, #(1 << 31) ORR x0, x0, x1 MSR HCR_EL2, x0 // Configure CNTHCTL_EL2 - the Counter-timer Hypervisor Control register // --------------------------------------------------------------------- // Enable timer register access for lower EL levels MRS x0, CNTHCTL_EL2 ORR x0, x0, #(1 << 1) ORR x0, x0, #1 MSR CNTHCTL_EL2, x0 // Possible to use the same vector table in this example, but in general // each combination of Exception level, Security state, and Execution state // will need a new vector table // ADD YOUR CODE HERE LDR x0, =vectors MSR VBAR_EL2, x0 // Initialize SCTLR_EL1 // -------------------- // SCTLR_EL1 has an unknown reset value and must be configured // before entering EL1 MSR SCTLR_EL1, xzr // Enter EL1 // --------- LDR x0, =el1_entry_aarch64 LDR x1, =AArch64_EL1_SP1 MSR ELR_EL2, x0 MSR SPSR_EL2, x1 ERET // ------------------------------------------------------------ // EL1 AArch64 // ------------------------------------------------------------ .global el1_entry_aarch64 .type el1_entry_aarch64, "function" el1_entry_aarch64: // Can use the same vector table in this example, but in general // each combination of Exception level, Security state, and Execution state // will need a new vector table LDR x0, =vectors MSR VBAR_EL1, x0 //Ensure that floating point register accesses are not trapped //since the c library for AArch64-v8A uses them MOV x0, #(0x3 << 20) MSR CPACR_EL1, x0 // ISB ensures that all instructions complete before this instruction ISB // Branch to scatter loading and C library init code .global __main B __main
- The comments in the code explain the modifications to system registers. It is not necessary to change exception level incrementally. Configuration of the registers in
el2_entry_aarch64
could have been done at EL3, to return from EL3 directly to EL1. Instead, the state of EL1 is configured at EL2. Now that the entry points have been defined, let's turn our attention to the interrupt controller. Ingic.s
:MOV x0, #ICC_SRE_ELn.Enable ORR x0, x0, #ICC_SRE_ELn.SRE MSR ICC_SRE_EL3, x0 ISB MSR ICC_SRE_EL2, x0 ISB MSR ICC_SRE_EL1, x0 // Set the Secure version of ICC_SRE_EL1 ISB MRS x1, SCR_EL3 BIC w1, w1, #1 // Set NS bit (lower EL in Secure state) MSR SCR_EL3, x1 ISB MSR ICC_SRE_EL1, x0 MRS x1, SCR_EL3 ORR w1, w1, #1 // Set NS bit (lower EL in non Secure state) MSR SCR_EL3, x1 ISB MOV x0, #0xFF MSR ICC_PMR_EL1, x0 // Set PMR to lowest priority ISB MOV x0, #3 MSR ICC_IGRPEN1_EL3, x0 ISB MOV x0, #1 MSR ICC_IGRPEN1_EL1, x0 MSR ICC_IGRPEN0_EL1, x0 ISB
- Build the image, then run the model:
$ FVP_Base_Cortex-A73x2-A53x4 -C bp.refcounter.non_arch_start_at_default=1 -a __image.axf
This generates the same Telnet messages that we saw in the Retargeting output to UART guide.
One change that we have not discussed is the redefinition of the interrupts. In this guide, the configuration of the timer interrupt is left as Secure Group 0. However, in the second example it would be appropriate to have the timer interrupt set as Non-secure Group 1. The code for this has been included in the download. Noting the differences between the source files is something that you can do outside the scope of this guide.