Hi,
I trying to switch from EL2 to EL1 on Cortex-A53. But it doesn't work.
Here is my current startup code:
#include <asm.h> IMPORT_ASM(_cpu_el3_vec_tbl_set) IMPORT_ASM(_cpu_el2_vec_tbl_set) IMPORT_ASM(_cpu_el1_vec_tbl_set) IMPORT_C(init) IMPORT_C(main) .text ENTRY(_start) mrs x0, MPIDR_EL1 and x0, x0, #0x3 cmp x0, #0 beq __elx __wfe_cpu1_3: wfe b __wfe_cpu1_3 __elx: __el3: mrs x0, CurrentEL and x0, x0, #0xC asr x0, x0, #2 cmp x0, #3 bne __el2 __el3_stack: ldr x0, =_stack_el3_e mov sp, x0 __el3_vector: bl _cpu_el3_vec_tbl_set msr SCTLR_EL2, xzr msr HCR_EL2, xzr mrs x0, SCR_EL3 orr x0, x0, #(1<<10) orr x0, x0, #(1<<0) msr SCR_EL3, x0 mov x0, #0b01001 msr SPSR_EL3, x0 adr x0, __el2 msr ELR_EL3, x0 eret __el2: mrs x0, CurrentEL and x0, x0, #0xC asr x0, x0, #2 cmp x0, #2 bne __el1 __el2_stack: ldr x0, =_stack_el2_e mov sp, x0 __el2_vector: bl _cpu_el2_vec_tbl_set msr SCTLR_EL1, xzr mov x0, xzr orr x0, x0, #(1 << 31) msr HCR_EL2, x0 /*orr x0, x0, #(7 << 6) */ adr x0, __el1 msr ELR_EL2, x0 mov x0, xzr orr x0, x0, #(1 << 2) orr x0, x0, #(1 << 0) msr SPSR_EL2, x0 eret __el1: __el1_stack: ldr x0, =_stack_el1_e mov sp, x0 __el1_vector: bl _cpu_el1_vec_tbl_set ldr x0, =_bss_s ldr x1, =_bss_e sub x1, x1, x0 mov x2, #0x0 cbz x1, __init __bss: strb w2, [x0], #1 sub x1, x1, #1 cbnz x1, __bss __init: bl init __main: bl main __main_wfe: wfe b __main_wfe .end
When I comment out from label __elx to __el1 everything works (the C functions init and main are called).
I also setup a vector table and handlers for EL2 and EL1. But there is no exception.
I have no idea what's going wrong.
What's the correct way change to a lower exception level?
Did you try to switch directly from EL3 to EL1?What SoC do you use? Do you have a JTAG debugger? This comes rather handy for this kind of problems.
I think, it's running on EL2. The C function init currently only contains a function which prints out the current exception level. I'm using U-Boot as bootloader with ARM Trusted Firmware. The SoC is the RK3328 from Rockchip. The board is the PINE64 ROCK64. No currently I don't own a JTAG Debugger.
I have also tried to place the init function before and after the ERET instruction. The result: Before the instruction the init functions prints out the exception level via UART. But after the ERET the CPU seems to hang.
I am not familiar with ATF (I work bare-metal w/o u-boot/atf), but shouldn't ATF boot into EL1?
Currently I don't know the answer. I'm quite new on AArch64 with ATF/U-Boot. When I read out (without trying to change the EL) CurrentEL, it' returns EL2. Not sure, if ATF or U-Boot set the HCR_EL.NV bit. Although I understand the ,documentation if this bit is set, CurrentEL always returns EL2 when read it from EL1.
But if U-Boot/ATF setup EL1, shouldn't be there an exception if I want to try access higher privileges register?
HCR_EL.NV is not implemented in CA53. Therefore, if you read "2" your are in EL2.
Linux needs to boot in EL2 if you want virtualisation (kvm) to work. I see RK3328 implements all ELs so I guess the non-secure world will boot in EL2 on this platform.
Maybe set also the I/F bits in SPSR_EL2 (6 and 7). To be sure, no interrupt happens.
My boot code also clears SCTRL_EL1 before switching.
But shouldn't be these exceptions catched in VBAR_EL2? I also setup a vector table and handlers for EL2 (bl _cpu_el2_vec_tbl_set).
The handler for EL2 are also called, when i force (quick and dirty) an exception.
Have tried it. But without success.
The current code:
__el2: mrs x0, CurrentEL and x0, x0, #0xC asr x0, x0, #2 cmp x0, #2 bne __el1 __el2_stack: ldr x0, =_stack_el2_e mov sp, x0 __el2_vector: bl _cpu_el2_vec_tbl_set msr SPsel, #1 mov x0, xzr orr x0, x0, #(1 << 11) orr x0, x0, #(1 << 20) orr x0, x0, #(1 << 22) orr x0, x0, #(1 << 28) orr x0, x0, #(1 << 29) msr SCTLR_EL1, x0 mov x0, xzr orr x0, x0, #(1 << 31) msr HCR_EL2, x0 adr x0, __el1 mov x1, xzr orr x1, x1, #(3 << 6) /* Mask IRQ/FIQ */ orr x1, x1, #(1 << 2) orr x1, x1, #(1 << 0) msr SPSR_EL2, x1 msr ELR_EL2, x0 eret __el1:
Now I used the initialization for SCTRL_EL1 from Linux.
But also not working. Still hangs.
mov x0, xzr orr x0, x0, #(1 << 11) orr x0, x0, #(1 << 20) orr x0, x0, #(1 << 22) orr x0, x0, #(1 << 28) orr x0, x0, #(1 << 29) msr SCTLR_EL1, x0
Please check your setup, you are on a CA53 (Armv8.0), so (f.e.) bit 28 is not defined in SCTLR. It should not matter, but it is no good idea to set reserved bits.Simply set SCTLR to zero seems best.
Okay, removed the setup of these bits. But still the same issue. :(
Can you output some feedback (UART, LED) directly after reaching EL1 (w/o doing anything else)?
Okay, EL1 will be entered. But I have some strange (for me) issues. First: The execution in EL1 is slower than in EL2. Second: Some parts (my sprintf implementation) are not called. But no issues when I use these function in EL2. Some configuration issue in HCR_EL2 and/or SCTLR_EL1?
krjdev said:Okay, EL1 will be entered
What was the reason? Please tell us (other might have the same problem).
Slower: Likely because a) no MMU setup and b) not I-Cache.
krjdev said:Some parts (my sprintf implementation) are not called.
This is very unspecific.