Reputation: 9272
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
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
Reputation: 1986
You are missing Exception Levels and Secure States implementation of ARMv8.
You could not change EL3(secure) -> EL1(non-secure) directly.
There are 2 possible ways to reach EL1:
Upvotes: 2