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:

  1. 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. The NONSECURE 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. The SROM_LOAD region is located in Secure memory, and the gic.o and BOOT sections of the code are also in this region. The EL3 stack has been placed in Secure SRAM. 
  2. 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. Modify timer.s to replace accesses to CNTPS_TVAL_EL1 and CNTPS_CTL_EL1 with CNTP_TVAL_EL0 and CNTP_CTL_EL0.
  3. Define the EL1 and EL2 entry functions in startup.s, and wrap them into the section named NONSECURE, 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
  4. 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. In gic.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
  5. 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.

Previous Next