通过以上章节明了了使用qemu运行OP-TEE需要的相关image的编译过程以及如何启动。本文将开始介绍启动过程中bios.bin的加载过程。
通过调用qemu-system-arm启动qemu的时候,最终会去加载从bios_qemu_tz_arm目录中编译出来的bios.bin文件,bios.bin镜像是由linux kernel image, OP-TEE os image, rootfs image以及bios_qemu_tz_arm目录中的其他.o文件组成。而qemu执行bios.bin镜像的时候的入口函数是reset函数,该函数由汇编代码编写,在bior_qemu_tz_arm/bios/entry.S文件中。该文件主要内容如下:
.section .text.boot
//定义 _start函数,设定第一条指令跳转到reset函数执行
FUNC _start , :
b reset
b . /* Undef */
b . /* Syscall */
b . /* Prefetch abort */
b . /* Data abort */
b . /* Reserved */
b . /* IRQ */
b . /* FIQ */
END_FUNC _start
/*
* Binary is linked against BIOS_RAM_START, but starts to execute from
* address 0. The branching etc before relocation works because the
* assembly code is only using relative addressing.
*/
/* reset 函数 */
LOCAL_FUNC reset , :
read_sctlr r0
orr r0, r0, #SCTLR_A
write_sctlr r0
/* Setup vector */
/* 设置中断向量表 */
adr r0, _start
write_vbar r0
/* Relocate bios to RAM */
/* 重新设定bios在RAM中的地址 */
mov r0, #0
ldr r1, =__text_start
ldr r2, =__data_end
sub r2, r2, r1
bl copy_blob
/* Jump to new location in RAM */
/* 跳转到上面重新定位的Bios在RAM中的地址 */
ldr ip, =new_loc
bx ip
new_loc:
/* Setup vector again, now to the new location */
/* 重新设定中断向量 */
adr r0, _start
write_vbar r0
/* Zero bss */
/* 清空BSS段的数据 */
ldr r0, =__bss_start
ldr r1, =__bss_end
sub r1, r1, r0
bl zero_mem
/* Setup stack */
/* 设定堆栈空间 */
ldr ip, =main_stack_top;
ldr sp, [ip]
push {r0, r1, r2}
mov r0, sp
ldr ip, =main_init_sec //获取main_init_sec函数地址
blx ip //跳转到main_init_sec函数中执行,加载OP-TEE OS的image
pop {r0, r1, r2}
mov ip, r0 /* entry address */ // OP-TEE OS的入口地址
mov r0, r1 /* argument (address of pagable part if != 0) */
blx ip //跳转到OP-TEE OS的启动地址
/*
* Setup stack again as we're in non-secure mode now and have
* new registers.
*/
ldr ip, =main_stack_top;
ldr sp, [ip]
ldr ip, =main_init_ns //获取main_init_ns函数的地址
bx ip //跳转到main_init_ns函数,加载Linux kernel的image
END_FUNC reset
LOCAL_FUNC copy_blob , :
ldrb r4, [r0], #1
strb r4, [r1], #1
subs r2, r2, #1
bne copy_blob
bx lr
END_FUNC copy_blob
LOCAL_FUNC zero_mem , :
cmp r1, #0
bxeq lr
mov r4, #0
strb r4, [r0], #1
sub r1, r1, #1
b zero_mem
END_FUNC zero_mem
main_init_sec函数用来将linux kernel image, OP-TEE OS image, rootfs从镜像文件加载到RAM的对应位置。并且解析出OP-TEE OS的入口地址,linux kernel的加载地址,rootfs在RAM中的地址和其他相关信息。main_init_sec函数执行完成之后将会返回OP-TEE OS的入口地址以及device tree的地址。然后在汇编代码中通过调用blx指令进入OP-TEE OS的启动。
main_init_ns函数是用来启动linux kernel,在main_init_sec函数中将会设定Linux kernel的入口函数地址,device tree的相关信息。
上述两个函数都定义在bios_qemu_tz_arm/bios/main.c文件中。将各种Image拷贝到RAM的操作都是通过解析bios.bin镜像来实现,通过寻找特定的section来确定各image在bios.bin镜像中的位置。各section与各image之间的对应关系请参考
《4. OP-TEE+qemu的编译--bios.bin镜像的编译》