This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Confusion about exception level of ARMv8

Hi,

I am fairly new to ARM processor and start work with cortexA57 recently.  After reading the technical manual and programmer guide , I have some questions regarding the exception level of v8.

1. How does the exception level change from one to another ?

     According to the documents, the EL can only be changed on taking exception. And also when an exception happens, the EL can only changed to a higher level. Then , how is it changed from higher EL to lower EL ?  For example, on reset, the processor starts at EL3, then how does it switch to execute program on the other levels ? Could anyone give an example to show how the EL changes during a boot sequence ?

2. How to get the current EL value ?

    The document says the register PSTATE contains a field CurrentEL which indicates on which EL the processor is executing.  Is the PSTATE register only existed in Aarch64 execution state , or it is also existed in Aarch32 ?  If not, how to read current EL value from Aarch32 ?

Thanks in advance !

Best regards,

Xinwei

  • Hello,

    You may want to check out the freely available ARM Cortex-A Series Programmer's Guide for ARMv8-A, in particular section 3.2 regarding changing exception levels.

    To answer your first question, the exception level can only change on taking or returning from an exception, so to move down to EL2 from EL3, software running at EL3 needs to perform an exception return using the ERET instruction.

    When performing an exception return the processor restores state using system registers, for example when returning from EL3:

    • ELR_EL3 - The address to return to
    • SPSR_EL3 - The state to restore

    And these registers are writeable, allowing the desired entrypoint and state to be programmed manually.

    There are some other bits that you'll need to be aware of, such as the fact that the SCTLR_EL2.M bit has an architecturally unknown reset value. This bit determines whether the MMU is enabled at EL2, so software running at EL3 should clear it before entering EL2 for the first time; this way you avoid accidentally having the MMU enabled before EL2 has actually programmed its translation tables.

    Also be aware of the SCR_EL3.RW and HCR_EL2.RW bits, which determine whether EL2 is AArch32/AArch64 and whether EL1 is AArch32/AArch64 respectively.

    Again, I recommend you read through the programmer's guide that I linked above.

    As a quick example of going from EL3 to EL2 for the first time after reset:

    MSR   SCTLR_EL2, XZR             // Disable MMU at EL2
    
    MOV   X0, XZR
    ORR   X0, X0, #(1 << 10)         // .RW = 0b1  -->  EL2 is AArch64
    ORR   X0, X0, #(1 << 0)          // .NS = 0b1  -->  Non-secure state
    MSR   SCR_EL3, X0
    
    MOV   X0, XZR
    ORR   X0, X0, #(7 << 6)          // .A = .I = .F = 0b1  -->  SErrors, IRQs, and FIQs will all be masked
    ORR   X0, X0, #(0 << 4)          // .M[4] = 0b0  -->  Return to AArch64 state
    ORR   X0, X0, #(1 << 3)          // .M[3:1] = 0b100  -->  Return to EL2
    ORR   X0, X0, #(1 << 0)          // .M[0] = 0b1  -->  Use EL2's dedicated stack pointer
    MSR   SPSR_EL3, X0
    
    ADRP  X0, el2_entry              // Program EL2 entrypoint
    ADD   X0, X0, :lo12:el2_entry
    MSR   ELR_EL3, X0
    
    ERET                             // Perform exception return to EL2
    

    To answer your second question, you can access a number of PSTATE fields directly as if they were system registers.

    For example:

    MRS   X0, CurrentEL
    

    And for masking/unmasking asynchronous exceptions:

    MSR   DAIFSet, 0b0011   // Mask IRQs and FIQs
    MSR   DAIFClr, 0b0100   // Unmask SErrors
    

    Hope that helps.

  • Hi Ash,

    Thanks for your answers and examples!

    Since I am working with cortexA57 in aarch32 state, is it correct that Aarch32 doesn't really have the concept of  EL but only 'mode' ?   I was trying to use 'MSR r0, CurrentEL' to read the EL value, but it doesn't even compile.  I am using ARM C/C++ Compiler 4.9 . Could that be the problem ?

    Thanks and best regards,

    Xinwei

  • Hello Xinwei,

    No problem

    Since I am working with cortexA57 in aarch32 state, is it correct that Aarch32 doesn't really have the concept of  EL but only 'mode' ?   I was trying to use 'MSR r0, CurrentEL' to read the EL value, but it doesn't even compile.  I am using ARM C/C++ Compiler 4.9 . Could that be the problem ?

    Ah, things work differently in AArch32 state. AArch32 is backwards compatible with legacy code (i.e. ARMv7-A) but not compatible with AArch64, which is why MRS r0, CurrentEL doesn't work.

    AArch32's modes roughly map onto AArch64's exception levels, as shown by Figure 3.6 here, i.e.:

    • USR mode maps onto EL0
    • SVC, ABT, IRQ, FIQ, UND, and SYS modes map onto EL1
    • HYP mode maps onto EL2
    • MON mode maps onto EL3

    One key difference here, though, is that in AArch32 MON actually has the same level of privilege as SVC, ABT, IRQ, FIQ, UND, and SYS, whereas in AArch64 EL3 is more privileged than EL1.

    When in AArch32 state you can read the current mode by reading the CPSR.M field, i.e. bits [3:0]. You can access CPSR from any mode other than USR like this:

    MRS   R0, CPSR
    

    If you're running in USR mode then you can instead access a subset of the CPSR known as APSR:

    MRS   R0, APSR
    

    But bits [3:0] of APSR are RES0 since you're in USR mode.

    So let's say you want to drop from AArch64 EL3 to AArch32 EL2. You'd need to:

    • Clear SCR_EL3.RW to 0b0 so that EL2 is AArch32
    • Set SCR_EL3.NS to 0b1 so that you'll be in Non-secure state when you leave EL3 (there is no HYP / EL2 in Secure state)
    • Set SPSR_EL3.M[4] to 0b1 so that you're returning to an AArch32 mode
    • Set SPSR_EL3.M[3:0] to 0b1010 which corresponds to HYP mode (because HYP mode maps onto EL2)
    • Set ELR_EL3 to point to the entrypoint of your AArch32 HYP code
    • Execute an ERET instruction

    If you've instead configured your Cortex-A57 to reset into EL3 with EL3 being AArch32 (via the AA64nAA32[N:0] signal described here) then you'll need to follow the AArch32 / legacy ARMv7-A model for changing mode.

    We have an equivalent Cortex-A Series Programmer's Guide for ARMv7-A that you may find useful in conjunction with the one I linked earlier.

    Hope that helps,

    Ash.

  • Ash, Thanks a lot for your explanation !

    Best regards,

    Xinwei

  • Hello Ash,

    I am a current undergraduate research student, and I am working with an ARM Cortex A53 processor on the Raspberry Pi 3 platform.

    Right now I am trying to change exception levels from boot (EL3 to EL1). I tried to play around with the code you posted but I am not getting any results. I then tried to use your original code to see if I can change from EL3 to EL2 instead, and I still have no results.

    I have read the ARMv8 Technical Reference Manual and the ARMv8 Programmer's Guide, but am still having trouble on trying to get this code to work.

        /* Disable MMU at EL2 */
        msr sctlr_el2, xzr
    
        mov x0, xzr
        orr x0, x0, #(1 << 10)  // .RW = 0b1 --> EL2 is AArch64
        orr x0, x0, #(1 << 0)   // .NS = 0b1 --> Non-secure status
        msr scr_el3, x0
    
        mov x0, xzr
        orr x0, x0, #(7 << 6)   // .A = .I = .F = 0b1 --> SError, IRQ, FIQ enabled
    //  orr x0, x0, #(0 << 4)   // .M[4] = 0b0 --> Return to AArch64 state
        orr x0, x0, #(1 << 3)   // .M[3:1] = 0b100 --> Return to EL2
        orr x0, x0, #(1 << 0)   // .M[0] = 0b1 --> Use EL2's dedicated stack
        msr spsr_el3, x0
    
        adrp x0, reset_handler          // Program EL2 entrypoint
        add x0, x0, :lo12:reset_handler
        msr elr_el3, x0
    
        /* Perform exception return to EL2 */
        eret

    This is the current code that I am testing.

    Any help or direction to more information would be extremely helpful.

    Thanks,

    Rade

  • Hi Ash,

    Thank you for your support, by read over your answer, I can now understand more about changing execution/exception mode. But I still have some concerns, could you please teach me?

    --------------------------------------

    If the initial processor mode (execution mode) is AArch64 at EL3, then I change it to AArch32 at EL0 for 32bit application.

    • SPSR_EL3.M[4] = 'b1                (AArch32)
    • SPSR_EL3.M[2:0] = 'b0000      (User mode)

    So, what happen if an exception (ie. Abort) is taken at current mode (AArch32, EL0)? Follow your explanation, I guess that the exception mode will be changed into EL1, won't it? Or the processor will jump to exception code that defined by vector table of program running in EL0? (instead of jumping into EL1 vector table as Figure 3.6. AArch32 processor modes)

    And if so, when handling the exception (assume now we are in EL1), We have to specify for processor to return from EL1 to EL0, to continue the program at EL0 (AArch32), is it correct?

    --------------------------------------

    For your information, I am working on ARMv8.3 A76 core.