ARMv8中,aarch64和aarch32是通过异常进行切换的。而A32和T32是通过bx指令进行切换的。如下图:
以下A64和A32混合编程,是在EL3为aarch64, EL2为aarch32条件下进行编程。在EL3,设置EL2的架构为aarch32,设置好返回地址,通过ERET指令,切换到EL2。
对于A64代码,使用aarch64编译工具链进行编译。
对于A32代码,使用arm编译工具链。
因为A32和A64所使用的编译器不是同一个,因此要分开进行编译,对于A64的代码,使用aarch64进行编译,,对于A32的代码,使用arm进行编译,最后使用aarch64的链接器,将生成的文件,链接成A64的elf程序,供fastmodel使用。如下图所示:
但是对于aarch64链接器,只能将A64的.o文件,链接到一起,不能链接A32的.o文件,因此这里要用到一个小技巧,将A32代码生成A32的可执行程序,然后使用arm工具链中的objcopy工具,将可执行程序转换为bin文件,然后aarch64链接器,将该bin文件和A64的.o文件进行链接。如下图所示:
demo代码目录:
◾a32.S: A32汇编代码
◾test32.lds: A32的链接脚本
◾boot-a64.S: A64汇编代码
◾test.lds: A64的链接脚本
◾Makefile: make脚本
◾load_file_ns: non-secure地址划分文件
◾load_file_s: secure地址划分文件
◾VAL_VAL_AEMv8A.cfg: fastmodel的配置文件,一个core,EL3为aarch64
一、boot-a64.S
fastmodel最开始执行程序,是在EL3,在EL3,设置scr_el3寄存器,设置EL2运行模式是non-secure,aarch32。
设置elr_el3,以及spsr_el3寄存器:
◾elf_el3寄存器保存,切换到EL2时取指令的地址
◾spsr_el3寄存器保存,切换到EL2后pstate的值。
对于spsr_el3,要设置正确,此时要参考AArch32的cpsr寄存器值进行设置。
第4bit要设置为1,第5bit设置为0,1都可。3-0bit,要设置为A32的特权模式。
二、test.lds
A64的链接脚本。
将A32代码生成的bin文件,直接插入到a32_begin开始的memory区域中。这里,要使用INPUT,指定a32.bin的位置。
三、a32.S
A32的代码,包括两部分,一部分是A32指令,另一部分是thumb指令。
对于A32指令,使用.code 32 或者 .arm 声明。
使用bx指令从A32切换到T32状态,跳转地址的最低位要为1。
对于T32指令,使用 .code 16 或者 .thumb 声明。
使用bx指令从T32状态,切换到A32状态,跳转地址的最低为要为0。
四、test32.lds
A32程序的链接脚本。
五、makefile
对于A64的程序,要使用aarch64-none-elf工具链,对于A32的程序,要使用arm-none-eabi工具链。
◾gcc:编译工具
◾ld: 链接工具
◾objdump: 反汇编工具
◾objcopy: 文件转换工具
执行 make;make run 命令
最后生成all.tarmac,就是A64和A32汇编程序的程序执行流的执行结果。
最开始是EL3,执行ERET后,切换到EL2,但是EL2是aarch32,因此切换到A32,执行A32程序。
在A32状态,执行bx指令,并且跳转地址最低位为1,切换到thumb状态。
在T32状态,执行bx指令,并且跳转指令最低为是0,切换到A32状态。
六、结论
EL2的A64和A32状态,由EL3决定,也就是SCR_EL3.RW寄存器决定。
EL1的A64和A32状态,由EL2决定,也就是HCR_EL3.RW寄存器决定。
EL0的A64和A32状态,由EL1决定,也就是CPSR.M[4] 位决定。
以上的demo是从EL3的A64切换到EL2的A32,其他EL的切换,道理是一样的。设置相关寄存器,设置spsr,设置elr。