AArch64 (Cortex-A53) - Switch to EL1 from EL2

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
krjdev
Posts: 7
Joined: Tue Jan 28, 2020 12:11 am
Location: Graz - Austria

AArch64 (Cortex-A53) - Switch to EL1 from EL2

Post by krjdev »

Hi,

I have some troubles from switching from EL2 to EL1. No idea what's going wrong.
Used this documentation from the ARM website.

Current startup code:

Code: Select all

#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_el2_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
    mrs x0, HCR_EL2
    orr x0, x0, #(1<<31)
    msr HCR_EL2, x0
    mov x0, xzr
    orr x0, x0, #(7 << 6)
    orr x0, x0, #(1 << 2)
    orr x0, x0, #(1 << 0)
    msr SPSR_EL2, x0
    ldr x0, =__el1
    msr ELR_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 the code from label __elx to __el1, the subsequent commands will be executed.
What's the correct way to switch to lower exception levels on AArch64?

I'm using U-Boot as bootloader on a PINE64 ROCK64 board.

Code: Select all

/* Beware of bugs in the above code;
I have only proved it correct, not tried it. */
Original quote by Donald E. Knuth
User avatar
bzt
Member
Member
Posts: 1584
Joined: Thu Oct 13, 2016 4:55 pm
Contact:

Re: AArch64 (Cortex-A53) - Switch to EL1 from EL2

Post by bzt »

krjdev wrote:I'm using U-Boot as bootloader
Are you sure that U-Boot doesn't set up EL1 for you?
krjdev wrote:What's the correct way to switch to lower exception levels on AArch64?
I can't say what's wrong with your code, it looks ok to me (haven't dig deep though). Try to set up VBAR with a simple handler that dumps the reason and position of the exception. That way you can debug what's wrong.

About the correct way,
* take a look at the ARM Trusted Firmware, or
* see how the RaspberryPi firmware does it,
* and finally here's how I do it in my boot loader (tested and known to work, does EL3 -> EL1 as well as EL2 -> EL1 transition because qemu and real RPi does not start on the same EL level).

Hope these help.

Cheers,
bzt
krjdev
Posts: 7
Joined: Tue Jan 28, 2020 12:11 am
Location: Graz - Austria

Re: AArch64 (Cortex-A53) - Switch to EL1 from EL2

Post by krjdev »

bzt wrote:
krjdev wrote:I'm using U-Boot as bootloader
Are you sure that U-Boot doesn't set up EL1 for you?
Currently I read out CurrentEL also in the C function init and print the value via UART. And it returns EL2, but not sure if U-Boot set the HCR_EL2.NV bit.
Although I understand the documentation from CurrentEL, if the HCR_EL.NV flag is set, CurrentEL always returning EL2 at EL1.
bzt wrote:I can't say what's wrong with your code, it looks ok to me (haven't dig deep though). Try to set up VBAR with a simple handler that dumps the reason and position of the exception. That way you can debug what's wrong.
I have setup the vector table (and exception handlers) for EL2 and EL1. But seems the CPU hangs. I doesn't execute the C function init (Currently it only outputs the current EL)
bzt wrote: About the correct way,
* take a look at the ARM Trusted Firmware, or
* see how the RaspberryPi firmware does it,
* and finally here's how I do it in my boot loader (tested and known to work, does EL3 -> EL1 as well as EL2 -> EL1 transition because qemu and real RPi does not start on the same EL level).

Hope these help.

Cheers,
bzt
Okay, thanks for the information.

Code: Select all

/* Beware of bugs in the above code;
I have only proved it correct, not tried it. */
Original quote by Donald E. Knuth
Post Reply