【精读Uboot】反汇编分析SPL的_main函数

1、简介

典型的Uboot启动分为两个阶段,bootrom->SPL(Secondary Program Loader)->ATF->OPTEE(可选)->Uboot。其中SPLBL2ATFBL31OPTEEBL32UbootBL33。其中bootrom是固化在芯片内部的代码,负责从各种外(sdcard、mmc、flash)中加载spl到芯片内部的SRAM;SPL的主要工作是初始化板载的DRAM和核心硬件;Uboot最主要的功能就是加载启动kernel。

【精读Uboot】反汇编分析SPL的_main函数_第1张图片

2、反汇编分析SPL

SPL代码是Uboot源码中一部分特殊的代码,其编译后的大小足以在片上RAM中运行。下面我们通过SPL的反汇编代码来了解实际的代码执行过程。

2.1、_start

_start 标签是程序的入口点。第一条指令 b 28 是一个无条件分支指令,将控制流跳转到 reset 标签处。reset函数中又直接跳转到了save_boot_params。对于i.MX平台, i.MX8各个系列的save_boot_params定义都不一样。下面我们将以i.MX8M平台的save_boot_params函数为例继续分析。

2.2、save_boot_params

  1. save_boot_params 函数开始,使用 adr 指令将 rom_pointer 的地址存储到寄存器 x0 中。rom_pointer是由optee固件提供的 ATAG/FDT 地址。本质是将armv8中的x1-x30寄存器保存到这个地址上,保存栈地址。
  2. 依次使用 stp 指令将 x1x30 的值存储到 x0 指向的内存地址,并且每次存储后 x0 自增 16 个字节。
  3. 使用 mov 指令将栈指针 sp 的值存储到寄存器 x30 中。
  4. 使用 str 指令将 x30 的值存储到 x0 指向的内存地址,并且 x0 自增 8 个字节。
  5. 跳转到 save_boot_params_ret 标签处。
.global save_boot_params
save_boot_params:
	/* The firmware provided ATAG/FDT address can be found in r2/x0 */
	adr	x0, rom_pointer
	stp	x1, x2, [x0], #16 //将x1和x2的值
	stp	x3, x4, [x0], #16
	stp	x5, x6, [x0], #16
	stp	x7, x8, [x0], #16
	stp	x9, x10, [x0], #16
	stp	x11, x12, [x0], #16
	stp	x13, x14, [x0], #16
	stp	x15, x16, [x0], #16
	stp	x17, x18, [x0], #16
	stp	x19, x20, [x0], #16
	stp	x21, x22, [x0], #16
	stp	x23, x24, [x0], #16
	stp	x25, x26, [x0], #16
	stp	x27, x28, [x0], #16
	stp	x29, x30, [x0], #16
	mov	x30, sp
	str	x30, [x0], #8

	/* Returns */
	b	save_boot_params_ret

2.3、save_boot_params_ret

save_boot_params_ret函数是通用函数。

1.adr 指令将地址 2049a800 存储在寄存器 x0 中。这个地址定义的是异常向量vectors位置。

2.mrs 指令将当前异常级别(Exception Level)的值存储在寄存器 x1 中。

3.cmp 指令将寄存器 x1 和常数 0xc0x80x4 进行比较,跳转到对应标签处。这三个常数分别代码异常等级EL3,EL2和EL1。标签实现的功能是设置各异常等级的vbar,src,cptr和cpacr寄存器。例如对于EL3,msr 指令将寄存器 x0 的值存储到 vbar_el3 寄存器中,这就设置了EL3的异常向量mov 指令将常数 0x33ff 移动到寄存器 x0 中。msr 指令将寄存器 x0 的值存储到 cptr_el2 寄存器中,打开FP/SIMD功能。b 指令无条件跳转到地址 isb指令。下面就是执行apply_core_erratalowlevel_init函数了。apply_core_errata不作分析。

ELn 应用范围
EL0 应用层
EL1 操作系统内核或者一些特权函数
EL2 Hypervisor虚拟化
EL3 Secure Monitor

2.4、lowlevel_init(arch/arm/mach-imx/lowlevel.S)

  1. mrs 指令将当前异常级别(Exception Level)的值存储在寄存器 x0 中。

  2. cmp 指令将寄存器 x0 和常数 0xc 进行比较,确定是否处于EL3。

  3. b.eq 指令检查比较结果,如果相等,则跳转到地址 2049b594处。如果不相等,使用 ret 指令返回到调用 lowlevel_init 函数的位置。

    2049b594处的代码如下:

    	2049b594:	d50344ff 	msr	daifclr, #0x4
        2049b598:	d5033fdf 	isb
        2049b59c:	d65f03c0 	ret
    

    msr 指令将 0x4 存储到 daifclr 寄存器中,用于清除 DAIF 寄存器的指定位。isb 指令执行指令同步屏障操作。然后返回,使用 ret 指令返回到调用 lowlevel_init 函数的位置。下一步启动主核/多核,执行_main函数。

2.5、_main重定位到uboot

1.ldr0x96dff0(CONFIG_SPL_STACK)加载到x0中。

2.and 指令使用位掩码将寄存器 x0 和常数 0xfffffffffffffff0 进行按位与操作,并将结果存储在栈指针寄存器 sp 中,对栈指针进行对齐

3.mov 指令将栈指针寄存器 sp 中的值复制到寄存器 x0 中,设置对齐后的栈地址。(sp->x0)

4.bl 指令调用函数 board_init_f_alloc_reserve,将栈指针作为参数传递,并将返回值存储在寄存器 x0 中。board_init_f_alloc_reserve函数的目的是在顶部地址中预留一些内存。首先,根据配置项 CONFIG_VAL(SYS_MALLOC_F_LEN) 的值,可能会预留一部分内存作为早期 malloc 的内存池,从 传入的对齐后地址中减去该长度。接下来,通过将这个地址向下舍入到最接近的 16 字节的倍数,来保留一块内存用于存储全局数据结构 struct global_data,并确保其对齐。最后,将更新后的 x0 值返回。

5.mov 指令将寄存器 x0 中的值复制到栈指针寄存器 sp 中(x0->sp)。mov 指令将寄存器 x0 中的值复制到寄存器 x18 中,备份栈指针。

6.bl 指令调用函数 board_init_f(board/freescale/imx93_evk/spl.c)初始化必要的i.MX外设如串口、定时器、pmic和ddr,初始化内存分配系统,最后调用board_init_r(common/spl/spl.c)执行跳转。对于board_init_r来说,支持5种跳转方式,它们分别是①通过ATF跳转uboot;②通过optee跳转uboot;③通过RISCV OpenSBI跳转uboot;④直接跳转到Linux;⑤直接跳转uboot。**如果上面都不支持,就不进行跳转,转而使用ROM API将uboot image下载到ddr的指定位置。**实际的跳转流程是在ROM API下载image后,直接进入ATF,然后由ATF控制跳转进入Uboot。对于I.MX系列平台,在执行board_init_f函数的最后就直接跳转进入了ATF,board_init_f之后的代码(如spl_relocate_stack_gdclear_loop)不再被执行。这一点与大部分资料所述的执行步骤不同。

【精读Uboot】反汇编分析SPL的_main函数_第2张图片

附录:反汇编代码

start.S

u-boot-spl:     file format elf64-littleaarch64


Disassembly of section .text:

000000002049a000 <_start>:
    2049a000:	1400000a 	b	2049a028 
    2049a004:	d503201f 	nop

000000002049a008 <_TEXT_BASE>:
    2049a008:	80200000 	.inst	0x80200000 ; NYI
    2049a00c:	00000000 	udf	#0

000000002049a010 <_end_ofs>:
    2049a010:	0001a1e0 	.inst	0x0001a1e0 ; undefined
    2049a014:	00000000 	udf	#0

000000002049a018 <_bss_start_ofs>:
    2049a018:	00080000 	.inst	0x00080000 ; undefined
    2049a01c:	00000000 	udf	#0

000000002049a020 <_bss_end_ofs>:
    2049a020:	000800b0 	.inst	0x000800b0 ; undefined
    2049a024:	00000000 	udf	#0

000000002049a028 :
    2049a028:	140005b6 	b	2049b700 

000000002049a02c :
    2049a02c:	10003ea0 	adr	x0, 2049a800 
    2049a030:	d5384241 	mrs	x1, currentel
    2049a034:	f100303f 	cmp	x1, #0xc
    2049a038:	540000a0 	b.eq	2049a04c   // b.none
    2049a03c:	f100203f 	cmp	x1, #0x8
    2049a040:	54000120 	b.eq	2049a064   // b.none
    2049a044:	f100103f 	cmp	x1, #0x4
    2049a048:	540001a0 	b.eq	2049a07c   // b.none
    2049a04c:	d51ec000 	msr	vbar_el3, x0
    2049a050:	d53e1100 	mrs	x0, scr_el3
    2049a054:	b2400c00 	orr	x0, x0, #0xf
    2049a058:	d51e1100 	msr	scr_el3, x0
    2049a05c:	d51e115f 	msr	cptr_el3, xzr
    2049a060:	1400000a 	b	2049a088 
    2049a064:	d53c1101 	mrs	x1, hcr_el2
    2049a068:	b71000a1 	tbnz	x1, #34, 2049a07c 
    2049a06c:	d51cc000 	msr	vbar_el2, x0
    2049a070:	d2867fe0 	mov	x0, #0x33ff                	// #13311
    2049a074:	d51c1140 	msr	cptr_el2, x0
    2049a078:	14000004 	b	2049a088 
    2049a07c:	d518c000 	msr	vbar_el1, x0
    2049a080:	d2a00600 	mov	x0, #0x300000              	// #3145728
    2049a084:	d5181040 	msr	cpacr_el1, x0
    2049a088:	d5033fdf 	isb
    2049a08c:	94000003 	bl	2049a098 
    2049a090:	9400053d 	bl	2049b584 

000000002049a094 :
    2049a094:	940002bf 	bl	2049ab90 <_main>

crt0_64.S

000000002049ab90 <_main>:
    2049ab90:	58000300 	ldr	x0, 2049abf0 
    2049ab94:	927cec1f 	and	sp, x0, #0xfffffffffffffff0
    2049ab98:	910003e0 	mov	x0, sp
    2049ab9c:	94000b86 	bl	2049d9b4 
    2049aba0:	9100001f 	mov	sp, x0
    2049aba4:	aa0003f2 	mov	x18, x0
    2049aba8:	94000b88 	bl	2049d9c8 
    2049abac:	d2800000 	mov	x0, #0x0                   	// #0
    2049abb0:	94000a8f 	bl	2049d5ec 
    2049abb4:	94000b7b 	bl	2049d9a0 
    2049abb8:	f100001f 	cmp	x0, #0x0
    2049abbc:	9a921012 	csel	x18, x0, x18, ne  // ne = any
    2049abc0:	910003e1 	mov	x1, sp
    2049abc4:	f100001f 	cmp	x0, #0x0
    2049abc8:	9a811000 	csel	x0, x0, x1, ne  // ne = any
    2049abcc:	9100001f 	mov	sp, x0
    2049abd0:	58000140 	ldr	x0, 2049abf8 
    2049abd4:	58000161 	ldr	x1, 2049ac00 

000000002049abd8 :
    2049abd8:	f800841f 	str	xzr, [x0], #8
    2049abdc:	eb01001f 	cmp	x0, x1
    2049abe0:	54ffffc3 	b.cc	2049abd8   // b.lo, b.ul, b.last
    2049abe4:	aa1203e0 	mov	x0, x18
    2049abe8:	f9403e41 	ldr	x1, [x18, #120]
    2049abec:	14000afa 	b	2049d7d4 
    2049abf0:	20519dd0 	.inst	0x20519dd0 ; undefined
    2049abf4:	00000000 	udf	#0
    2049abf8:	2051a000 	.inst	0x2051a000 ; undefined
    2049abfc:	00000000 	udf	#0
    2049ac00:	2051a0b0 	.inst	0x2051a0b0 ; undefined
    2049ac04:	00000000 	udf	#0

你可能感兴趣的:(深入理解uboot源代码,uboot,驱动开发,i.MX)