Alexander Zhak
Alexander Zhak

Reputation: 9272

AArch64 switch EL3 > non-secure EL1 problem

Cortex-A35 processor, AArch64 mode. Before setting up MMU and GIC, I'm trying to go from EL3 to non-secure EL1:

        msr VTTBR_EL2, xzr
        mov x0, SCR_EL3.RES1 or SCR_EL3.NS or SCR_EL3.RW or SCR_EL3.ST
        msr SCR_EL3, x0
        mov x1, SPSR.M.AArch64_EL1h or SPSR_EL3.A or SPSR_EL3.I or SPSR_EL3.F
        msr SPSR_EL3, x1
        adr x2, __el1
        msr ELR_EL3, x2
        ; all other system registers are set to their reset values. 
        ; SCTLR_EL1 = 0x00C50838
        ; HCR_EL2   = 0x0000000000000002

        eret

__el1:
        mov x10, 0xff220000  ; this simply turns on the LED on the board, 
        mov w11, 0x0020      ; for testing only
        str w11, [x10, 4]    ;
        str w11, [x10, 0]    ;
        b.al $ 

Switching to the secure EL1 (SCR_EL3.NS not set) works fine and the LED turns on. It doesn't work when I try to go to non-secure EL1.

Setting HCR_EL2.RW doesn't work either:

        mov x0, HCR_EL2.RW
        msr HCR_EL2, x0

What am I missing?

Upvotes: 0

Views: 1825

Answers (2)

Simon Willcocks
Simon Willcocks

Reputation: 328

I'm working with a Raspberry Pi 3, and finally got it right.

I can't see what difference there is to your code, except you don't set the stack pointer. The reset value of SP_EL1 is basically random, and not necessarily 16-byte aligned.

This works for me:

Setup:

#define get_system_reg( name, value ) asm ( "mrs %[v], "#name : [v] "=&r" (value) )
#define set_system_reg( name, value ) asm ( "msr "#name", %[v]" : : [v] "r" (value) )
#define modify_system_reg( name, bits, set ) asm ( "mrs x4, "#name \
                                                 "\nbic x4, x4, %[b]" \
                                                 "\norr x4, x4, %[s]" \
                                                 "\nmsr "#name", x4" \
                                                 : \
                                                 : [b] "r" (bits) \
                                                 , [s] "r" (set) \
                                                 : "x4" )

asm volatile ( "msr VBAR_EL3, %[table]\n" : : [table] "r" (VBAR_EL3) );
asm volatile ( "msr VBAR_EL2, %[table]\n" : : [table] "r" (VBAR_EL2) );
asm volatile ( "msr VBAR_EL1, %[table]\n" : : [table] "r" (VBAR_EL1) );

// Make lower levels 64-bit
modify_system_reg( scr_el3, (1 << 10), (1 << 10) ); // Set RW (64-bit)

// Some stacks at random memory locations
set_system_reg( sp_el2, 0x200000 );
set_system_reg( sp_el1, 0x300000 );
set_system_reg( sp_el0, 0x400000 );

To run aarch32 code at EL1 (M32_Svc = 0x1d3):

set_system_reg( elr_el3, example_a32 ); // Address of the code
set_system_reg( spsr_el3, M32_Svc );    // Mode to run at
modify_system_reg( scr_el3, 1, 1 ); // Set NS bit
modify_system_reg( hcr_el2, (1 << 31), (0 << 31) ); // Clear RW (32-bit)
asm ( "eret" );

To run aarch64 code at non-secure el1

set_system_reg( elr_el3, example_a64 );
set_system_reg( spsr_el3, EL1t );
modify_system_reg( scr_el3, 1, 1 ); // Set NS bit
modify_system_reg( hcr_el2, (1 << 31), (1 << 31) ); // Set RW (64-bit)
asm ( "eret" );

My example code consists of a single SVC instruction, which enters the VBAR_EL3 LOWER_AARCH64_SYNC exception vector in both cases, presumably because EL2, which gets skipped, is aarch64. The ESR_EL3 values are: 4e000000 (SMC instruction execution in AArch32), and 5e000000 (SMC instruction execution in AArch64 state).

Upvotes: 0

user3124812
user3124812

Reputation: 1986

You are missing Exception Levels and Secure States implementation of ARMv8.

enter image description here

You could not change EL3(secure) -> EL1(non-secure) directly.

There are 2 possible ways to reach EL1:

  • EL3(secure) -> EL1(secure)
  • EL3(secure) -> EL2(non-secure) -> EL1 (non-secure)

Upvotes: 2

Related Questions