我正在尝试从 EL3 安全状态切换到 EL1 非安全状态。如果我不更改安全状态,而只执行从 EL3 到 EL1 的切换,如下所示:
el1_entry_aarch64:
NOP
NOP
NOP
// we can use the same vector table in this example, but in general
// each combination of Exception level, Security state, and Execution state
// will need a new vector table
LDR x0, =vectors
MSR VBAR_EL1, x0
//we must ensure that floating point register accesses are not trapped
//since the c library for AArch64-v8A uses them
MOV x0, #(0x3 << 20)
MSR CPACR_EL1, x0
B __main
secure_to_ns:
MOV w1, #0 // Initial value of register is unknown
ORR w1, w1, #(1 << 11) // set ST bit (disable trapping of timer control registers)
ORR w1, w1, #(1 << 10) // set RW bit (next lower EL in aarch64)
ORR w1, w1, #(1 << 3) // Set EA bit (SError routed to EL3)
ORR w1, w1, #(1 << 2) // Set FIQ bit (FIQs routed to EL3)
ORR w1, w1, #(1 << 1) // Set IRQ bit (IRQs routed to EL3)
MSR SCR_EL3, x1
MSR SCTLR_EL1, xzr
LDR x0, =el1_entry_aarch64
MOV x1, #0x5
MSR ELR_EL3, x0 // where to branch to when exception completes
MSR SPSR_EL3, x1 // set the program state for this point to a known value
ERET
执行ERET后,PC会跳转到
el1_entry_aarch64
,并从那里进入主函数。
除此之外,还有一些基本的堆栈初始化操作,我在这里省略了。
但是如果我添加切换安全状态的汇编代码,在el1_entry_aarch64中执行时,程序会进入异常。
secure_to_ns
的变化如下:
secure_to_ns:
MOV w1, #0 // Initial value of register is unknown
ORR w1, w1, #(1 << 11) // set ST bit (disable trapping of timer control registers)
ORR w1, w1, #(1 << 10) // set RW bit (next lower EL in aarch64)
ORR w1, w1, #(1 << 3) // Set EA bit (SError routed to EL3)
ORR w1, w1, #(1 << 2) // Set FIQ bit (FIQs routed to EL3)
ORR w1, w1, #(1 << 1) // Set IRQ bit (IRQs routed to EL3)
ORR w1, w1, #1 **// Set NS bit (lower EL in non Secure state)**
MSR SCR_EL3, x1
MSR SCTLR_EL1, xzr
LDR x0, =el1_entry_aarch64
MOV x1, #0x5
MSR ELR_EL3, x0 // where to branch to when exception completes
MSR SPSR_EL3, x1 // set the program state for this point to a known value
ERET
在el1_entry_aarch64函数内执行NOP指令时,程序进入异常。
我的程序运行在0xF8040000到0xF80AFFFF的内存范围内,也就是448KB的内存空间。通过某些配置,我将0xF8090000到0xF80B0000范围设置为非安全访问区域。
同时,我已经将相关的汇编代码放在了非安全区域中,也就是说eret指令之后执行的所有代码都在非安全区域内。
起初,我没有包含NOP汇编指令,但仍然遇到了这个问题。我一开始以为可能和不同段之间的跳转有关,所以加上了NOP指令。现在看来问题与跳转本身无关。
我认为从EL3直接切换到EL1是不可能的。根据 ARM 的官方裸机文档,您还应该设置虚拟机管理程序 (EL2)。特别是如果你想在 EL1 中使用定时器。这必须在 EL2 中完成。
在 EL3 中完成的 EL2 可能的(在 A53 上工作)设置:
el3_setup_el2:
msr SCTLR_EL2, xzr
msr HCR_EL2, xzr
mrs x0, SCR_EL3
orr x0, x0, #(1 << 10) /* Next lower exception level is AArch64 */
orr x0, x0, #(1 << 0) /* EL1/EL0 are in Non-secure state */
msr SCR_EL3, x0
el3_exit:
mov x0, xzr
orr x0, x0, #(1 << 8) /* Mask SError */
orr x0, x0, #(1 << 7) /* Mask IRQ */
orr x0, x0, #(1 << 6) /* Mask FIQ */
mov x1, #0b01001 /* EL2 is in handler mode */
orr x0, x0, x1
msr SPSR_EL3, x0
adr x0, el2_enter
msr ELR_EL3, x0
eret
在 EL2 中完成 EL1 的设置:
el2_setup_el1:
mrs x0, HCR_EL2
orr x0, x0, #(1 << 31) /* EL1 is AArch64 */
msr HCR_EL2, x0
/* Setup Stack for EL1 */
ldr x0, =_stack_cpu0_el1_e
msr SP_EL1, x0
/* Setup Stack for EL0 */
ldr x0, =_stack_cpu0_el0_e
msr SP_EL0, x0
mov x0, xzr
orr x0, x0, #(1 << 29) /* RES1 */
orr x0, x0, #(1 << 28) /* RES1 */
orr x0, x0, #(1 << 23) /* RES1 */
orr x0, x0, #(1 << 22) /* RES1 */
orr x0, x0, #(1 << 20) /* RES1 */
orr x0, x0, #(1 << 11) /* RES1 */
orr x0, x0, #(1 << 4) /* Enable SP aligment check for EL0 */
orr x0, x0, #(1 << 3) /* Enable SP aligment check for EL1 */
orr x0, x0, #(1 << 1) /* Enable aligment fault check */
msr SCTLR_EL1, x0
el2_timer:
/* Enable Timer for EL1 */
mrs x0, CNTHCTL_EL2
orr x0, x0, #(1 << 0)
orr x0, x0, #(1 << 1)
msr CNTHCTL_EL2, x0
msr CNTVOFF_EL2, xzr
el2_exit:
mov x0, xzr
orr x0, x0, #(1 << 8) /* Mask SError */
orr x0, x0, #(1 << 7) /* Mask IRQ */
orr x0, x0, #(1 << 6) /* Mask FIQ */
mov x1, #0b101 /* EL1 is in handler mode */
orr x0, x0, x1
msr SPSR_EL2, x0
adr x0, el1_enter
msr ELR_EL2, x0
eret
我还建议在(示例)之前检查当前的异常级别:
el3_enter:
mrs x0, CurrentEL
and x0, x0, #0xC
asr x0, x0, #2
cmp x0, #3
bne el2_enter