移植uboot2017.01(六)

上一小节已经分析完了 board_init_f的内容,接下来我们从他后面继续分析


	mov	r0, #0
	bl	board_init_f

#if ! defined(CONFIG_SPL_BUILD)        /* 没定义,所以要执行,详细的后面拆分开分析 */

/*
 * Set up intermediate environment (new sp and gd) and call
 * relocate_code(addr_moni). Trick here is that we'll return
 * 'here' but relocated. 
 */
    
	ldr	sp, [r9, #GD_START_ADDR_SP]	/* sp = gd->start_addr_sp */
#if defined(CONFIG_CPU_V7M)	/* v7M forbids using SP as BIC destination */
	mov	r3, sp
	bic	r3, r3, #7
	mov	sp, r3
#else
	bic	sp, sp, #7	/* 8-byte alignment for ABI compliance */
#endif
	ldr	r9, [r9, #GD_BD]		/* r9 = gd->bd */
	sub	r9, r9, #GD_SIZE		/* new GD is below bd */

	adr	lr, here
	ldr	r0, [r9, #GD_RELOC_OFF]		/* r0 = gd->reloc_off */
	add	lr, lr, r0					/* lr = new_uboot-old_uboot + here */
#if defined(CONFIG_CPU_V7M)
	orr	lr, #1				/* As required by Thumb-only */
#endif
	ldr	r0, [r9, #GD_RELOCADDR]		/* r0 = gd->relocaddr */
	b	relocate_code                                        /* 重定位代码段 */
here:
/*
 * now relocate vectors        
 */

	bl	relocate_vectors                                    /* 重定位中断向量地址 */

/* Set up final (full) environment */
  /* 建立C运行环境 */
	bl	c_runtime_cpu_setup	/* we still call old routine here */
#endif
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_FRAMEWORK)
# ifdef CONFIG_SPL_BUILD    /* 这个没定义 */
	/* Use a DRAM stack for the rest of SPL, if requested */
	bl	spl_relocate_stack_gd
	cmp	r0, #0
	movne	sp, r0
	movne	r9, r0
# endif
    /* 清bss段 */
	ldr	r0, =__bss_start	/* this is auto-relocated! */    

#ifdef CONFIG_USE_ARCH_MEMSET
	ldr	r3, =__bss_end		/* this is auto-relocated! */
	mov	r1, #0x00000000		/* prepare zero to clear BSS */

	subs	r2, r3, r0		/* r2 = memset len */
	bl	memset
#else
	ldr	r1, =__bss_end		/* this is auto-relocated! */
	mov	r2, #0x00000000		/* prepare zero to clear BSS */

clbss_l:cmp	r0, r1			/* while not at end of BSS */
#if defined(CONFIG_CPU_V7M)
	itt	lo
#endif
	strlo	r2, [r0]		/* clear 32-bit BSS word */
	addlo	r0, r0, #4		/* move to next */
	blo	clbss_l
#endif

#if ! defined(CONFIG_SPL_BUILD)    /* 点个灯,调试用 */
	bl coloured_LED_init
	bl red_led_on
#endif
	/* call board_init_r(gd_t *id, ulong dest_addr) */
    /* 把gd_t和新的uboot的起始地址传给board_init_r	 */
	mov     r0, r9                  /* gd_t */
	ldr	r1, [r9, #GD_RELOCADDR]	/* dest_addr */
	/* call board_init_r */
#if defined(CONFIG_SYS_THUMB_BUILD)
	ldr	lr, =board_init_r	/* this is auto-relocated! */
	bx	lr
#else
	ldr	pc, =board_init_r	/* this is auto-relocated! */
#endif
	/* we should not return here. */
#endif

ENDPROC(_main)

分析之前,先把上一节末尾的图拿出来,以备查询

1.1、先分析relocate之前的代码

	ldr	sp, [r9, #GD_START_ADDR_SP]	/* sp = gd->start_addr_sp,对照上图可以发现在fdt下面 */
#if defined(CONFIG_CPU_V7M)	/* v7M forbids using SP as BIC destination */
	mov	r3, sp
	bic	r3, r3, #7
	mov	sp, r3
#else
	bic	sp, sp, #7	/* 8-byte alignment for ABI compliance,8字节对齐sp */
#endif
	ldr	r9, [r9, #GD_BD]		/* r9 = gd->bd,先找到bd位置 */
	sub	r9, r9, #GD_SIZE		/* new GD is below bd,gd就在bd下面,从这后面开始r9,即gd指针已经是用新的重定位后的内存里的数据了*/


/* 下面三句代码是 很有意思的,我们来重点分析一下 */
/* adr是个位置无关的加载指令,及无论 here的链接地址是多少,adr加载的都是当前pc + 偏移(adr lr,here到here的偏移)
    然后得到新的规划的uboot和旧的uboot之家的偏移量给r0
    之后把偏移和旧的uboot的here加起来,最终得到新的uboot里面的here的地址,然后放进lr
    等进入下面的relocate_code函数,返回的时候就直接返回到新的uboot的代码段运行了
*/


	adr	lr, here
	ldr	r0, [r9, #GD_RELOC_OFF]		/* r0 = gd->reloc_off */
	add	lr, lr, r0					/* lr = new_uboot-old_uboot + here */
#if defined(CONFIG_CPU_V7M)
	orr	lr, #1				/* As required by Thumb-only */
#endif
	ldr	r0, [r9, #GD_RELOCADDR]		/* r0 = gd->relocaddr,给relocate的传参是将要重定位的起始地址 */
	b	relocate_code                /* 注意这里必须使用b跳转,这样返回时才能用前面设置的lr中国新的here地址 */
here:

在分析relocate_codez之前我们先分析一下uboot中各个段的含义

.text       代码段

.rodata  只读数据段

.data     数据段

.u_boot_list  uboot的命令

.efi_runtime/efi_runtime_rel  不懂,跳过

.rel.dyn   存放.text段中需要重定位地址的集合

.mmutable   存放页表的段

.bss             初始化为0的全局或静态变量

1.2、relocate_code  这里先放出来代码,接下来差分开来一段一段分析


ENTRY(relocate_code)
	ldr	r1, =__image_copy_start	/* r1 <- SRC &__image_copy_start */
	subs	r4, r0, r1		/* r4 <- relocation offset */
	beq	relocate_done		/* skip relocation */
	ldr	r2, =__image_copy_end	/* r2 <- SRC &__image_copy_end */

copy_loop:
	ldmia	r1!, {r10-r11}		/* copy from source address [r1]    */
	stmia	r0!, {r10-r11}		/* copy to   target address [r0]    */
	cmp	r1, r2			/* until source end address [r2]    */
	blo	copy_loop

	/*
	 * fix .rel.dyn relocations
	 */
	ldr	r2, =__rel_dyn_start	/* r2 <- SRC &__rel_dyn_start */
	ldr	r3, =__rel_dyn_end		/* r3 <- SRC &__rel_dyn_end */
fixloop:
	ldmia	r2!, {r0-r1}		/* (r0,r1) <- (SRC location,fixup) */
	/* r1 = r1 & 0xff */
	and	r1, r1, #0xff	
	/* 比较是否要更改 */
	cmp	r1, #R_ARM_RELATIVE
	bne	fixnext

	/* relative fix: increase location by offset */
	add	r0, r0, r4		
	ldr	r1, [r0]		
	add	r1, r1, r4	
	str	r1, [r0]		
fixnext:
	cmp	r2, r3			
	blo	fixloop

relocate_done:

#ifdef __XSCALE__
	/*
	 * On xscale, icache must be invalidated and write buffers drained,
	 * even with cache disabled - 4.2.7 of xscale core developer's manual
	 */
	mcr	p15, 0, r0, c7, c7, 0	/* invalidate icache */
	mcr	p15, 0, r0, c7, c10, 4	/* drain write buffer */
#endif

	/* ARMv4- don't know bx lr but the assembler fails to see that */

#ifdef __ARM_ARCH_4__
	mov	pc, lr
#else
	bx	lr
#endif

ENDPROC(relocate_code)

1.2.1、对比下图分析拷贝的内容

	ldr	r1, =__image_copy_start	/* r1 <- SRC &__image_copy_start,旧的uboot的起始地址 */
	subs	r4, r0, r1		/* r4 <- relocation offset,新的relocate_addr - 旧的uboot起始地址 给r4 */
	beq	relocate_done		/* skip relocation ,上面相减完,相等表示不用重定位了,显然我们不相等*/
	ldr	r2, =__image_copy_end	/* r2 <- SRC &__image_copy_end 旧的uboot的__image_copy_end	结束初*/

copy_loop:
	ldmia	r1!, {r10-r11}		/* copy from source address [r1]    */
	stmia	r0!, {r10-r11}		/* copy to   target address [r0]    */
	cmp	r1, r2			/* until source end address [r2]    */
	blo	copy_loop

下面画出前四句代码指定的r0,r1,r2,r4的位置

copy_loop:
	ldmia	r1!, {r10-r11}		/* copy from source address [r1] 把r1地址的数据放到r10,r1自增4,然后把r1地址的数据放到r11后把r1自增4   */
	stmia	r0!, {r10-r11}		/* copy to   target address [r0] 把r0地址的数据放到r0所在地址后r0自增4,把r11内容放到r0地址后r0自增4    */
    /* 比较image段是否拷贝结束,没有则继续拷贝至结束 */
	cmp	r1, r2			/* until source end address [r2],    */
	blo	copy_loop

接下来就要修改rel.dyn段了

	/*
	 * fix .rel.dyn relocations
	 */
	ldr	r2, =__rel_dyn_start	/* r2 <- SRC &__rel_dyn_start */
	ldr	r3, =__rel_dyn_end		/* r3 <- SRC &__rel_dyn_end */
fixloop:
	/* 把r2 指向内存块的连续值加载到r0和r1中,加载完后再增加8个字节 */
	ldmia	r2!, {r0-r1}		/* (r0,r1) <- (SRC location,fixup) */
	/* r1 = r1 & 0xff */
	and	r1, r1, #0xff	       
	/* 比较是否要更改 */
	cmp	r1, #R_ARM_RELATIVE
	bne	fixnext

	/* relative fix: increase location by offset */
	add	r0, r0, r4		/* r0 += r4  r4=新旧.text的偏移 */
	ldr	r1, [r0]		/* 得到新的.text段里面的数据 */
	add	r1, r1, r4		/* 数据偏移 */
	str	r1, [r0]		/* 写回偏移后新的数据 */
fixnext:
	cmp	r2, r3			/* 判断有处理完 */
	blo	fixloop

直行完前两行的代码后r2、r3如下

接下来我们来说明一个很重要的概念。

 u-boot在启动过程中,会把自己拷贝到RAM的顶端去执行。这一拷贝带来的问题是执行地址的混乱。代码的执行地址通常都是在编译时有链接地址指定的,如何保证拷贝前后都可以执行呢? 
  一个办法是使用拷贝到RAM后的地址作为编译时的链接地址,拷贝前所有函数与全局变量的调用都增加偏移量。(如VxWorks的bootloader)尽量减少拷贝前需要执行的代码量。 
  另一个办法是把image编译成与地址无关的程序,也就是PIC - Position independent code。编译器无法保证代码的独立性,它需要与加载器配合起来。U-boot自己加载自己,所以它自己就是加载器。PIC依赖于下面两种技术: 
1) 使用相对地址 
2) 加载器可以自动更新涉及到绝对地址的指令 
  对于PowerPC架构,u-boot只是在编译时使用了-fpic,这种方式会生成一个.got段来存储绝对地址符号。对于ARM架构,则是在编译时使用-mword-relocations,生成与位置无关代码,链接时使用-pie生成.rel.dyn段,该段中的每个条目被称为一个LABEL,用来存储绝对地址符号的地址。

为了理解地址表的概念我们分析一段代码。

借助一个工具

arm-none-linux-gnueabi-readelf help

因为我们这里关注relocate段,所以只查看relocate段

 arm-none-linux-gnueabi-readelf -r u-boot |less 

可以看到它的类型是可重定位段。

为了方便起见,我们就以第一个rel.dyn段的标号为例来分析它的作用。(.efi_runtime_rel段远离相同)

每个需要修改地址的信息占用8个字节。

可以看到第一个标号34800020是可重定位的地址,00000017是标明那种CPU,0x17代表ARM32位

现在我们看一下34800020位置到底方的什么

arm-none-linux-gnueabi-objdump -S u-boot | less

可以看到它放的是一个地址。0x34800060

对应我们vectors.S里面的下面的指令

反汇编后分析,发现它就是存放的这个子过程的入口地址

因为这个入口地址如果直接.text段拷贝过去,将来执行跳转的还是旧的uboot里面的undefined_instruction,而不是我们新uboot里面的undefined_instruction,所以这个要修改。

即要修改所有位置有关码的地址。

如何修改?

很简单,旧的地址的值是什么我们取出来加上新地址和旧地址的偏移。然后存入新的地址就可以了。

因为编译器已经帮我们剥离出来要修改的地址和它所属的类型,存放进了rel.dyn段,所以我们只要修改新地址的就可以。

经过上面的分析,下面的使用rel段,修改.text段就很简单了。

	ldr	r2, =__rel_dyn_start	/* r2 <- SRC &__rel_dyn_start */
	ldr	r3, =__rel_dyn_end		/* r3 <- SRC &__rel_dyn_end */
fixloop:
	/* 把r2 指向内存块的连续值加载到r0和r1中,同时 每次加载完一个寄存器就给r2加4 */
	ldmia	r2!, {r0-r1}		/* (r0,r1) <- (SRC location,fixup) */
	/* r1 = r1 & 0xff */
	and	r1, r1, #0xff	
	/* 比较是否属于可重定位的,当然我们是从rel段取出来的,所以肯定是 */
	cmp	r1, #R_ARM_RELATIVE
	bne	fixnext     /* 如果不是则调到后面 */

	/* relative fix: increase location by offset */
	add	r0, r0, r4		/* r0 += r4  r4=新旧.text的偏移 */
	ldr	r1, [r0]		/* 得到新的.text段里面的未修改的数据 */
	add	r1, r1, r4		/* 数据修改 */
	str	r1, [r0]		/* 写回偏移后新的数据 */
fixnext:
	cmp	r2, r3			/* 判断有处理完 */
	blo	fixloop

调进新的uboot

	blo	fixloop

relocate_done:

#ifdef __XSCALE__            /* 是否无效cache和我们关系不大,无所谓 */
	/*
	 * On xscale, icache must be invalidated and write buffers drained,
	 * even with cache disabled - 4.2.7 of xscale core developer's manual
	 */
	mcr	p15, 0, r0, c7, c7, 0	/* invalidate icache */
	mcr	p15, 0, r0, c7, c10, 4	/* drain write buffer */
#endif

	/* ARMv4- don't know bx lr but the assembler fails to see that */

#ifdef __ARM_ARCH_4__
	mov	pc, lr
#else
	bx	lr            /* 我们从这里返回,还记得lr是调用这个函数之前设置好的,新的uboot的here的地址吗,这里返回后就,运行在新的uboot里面了 */
#endif

ENDPROC(relocate_code)

.text段也好了

这里有一点小疑惑,为什么不把rel段也拷贝上去新的uboot上面,毕竟地方也留了。

如果拷贝上去,是不是也该把offset也修改成新的uboot中.text里面的地址

here:
/*
 * now relocate vectors
 */

	bl	relocate_vectors                  /* 重定位中断向量 */      

/* Set up final (full) environment */

	bl	c_runtime_cpu_setup	/* we still call old routine here,重新设置C运行时环境(因为可能cache里面存的是旧的uboot的数据和指令) */
#endif
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_FRAMEWORK)
# ifdef CONFIG_SPL_BUILD        /* 没定义 */
	/* Use a DRAM stack for the rest of SPL, if requested */
	bl	spl_relocate_stack_gd
	cmp	r0, #0
	movne	sp, r0
	movne	r9, r0
# endif
    /* 清bss段,很简单,直接写0就可以,要注意的是这些标号都是位置有关的,在前面的rel段已经修改过了,所以此时清的已经是新的uboot上没的bss段了 */
	ldr	r0, =__bss_start	/* this is auto-relocated! */

#ifdef CONFIG_USE_ARCH_MEMSET
	ldr	r3, =__bss_end		/* this is auto-relocated! */
	mov	r1, #0x00000000		/* prepare zero to clear BSS */

	subs	r2, r3, r0		/* r2 = memset len */
	bl	memset
#else
	ldr	r1, =__bss_end		/* this is auto-relocated! */
	mov	r2, #0x00000000		/* prepare zero to clear BSS */

clbss_l:cmp	r0, r1			/* while not at end of BSS */
#if defined(CONFIG_CPU_V7M)
	itt	lo
#endif
	strlo	r2, [r0]		/* clear 32-bit BSS word */
	addlo	r0, r0, #4		/* move to next */
	blo	clbss_l
#endif

#if ! defined(CONFIG_SPL_BUILD)    /* 点灯,调试,随便,想怎么点你就怎么点 */
	bl coloured_LED_init
	bl red_led_on
#endif
	/* call board_init_r(gd_t *id, ulong dest_addr) */
    /* gd的 地址和 当前新的uboot的起始地址传参给board_init_r	 */
	mov     r0, r9                  /* gd_t */
	ldr	r1, [r9, #GD_RELOCADDR]	/* dest_addr */
	/* call board_init_r */
#if defined(CONFIG_SYS_THUMB_BUILD)
	ldr	lr, =board_init_r	/* this is auto-relocated! */
	bx	lr
#else
	ldr	pc, =board_init_r	/* this is auto-relocated! ,这边使用pc直接赋值,说明决心很大,绝不可能return 之类回来的*/
#endif
	/* we should not return here. */
#endif

ENDPROC(_main)

中断向量表的重新布置。


ENTRY(relocate_vectors)

#ifdef CONFIG_CPU_V7M            /* 没定义 */
	/*
	 * On ARMv7-M we only have to write the new vector address
	 * to VTOR register.
	 */
	ldr	r0, [r9, #GD_RELOCADDR]	/* r0 = gd->relocaddr */
	ldr	r1, =V7M_SCB_BASE
	str	r0, [r1, V7M_SCB_VTOR]
#else
#ifdef CONFIG_HAS_VBAR        /* 定义了 */
	/*
	 * If the ARM processor has the security extensions,
	 * use VBAR to relocate the exception vectors.
	 */
    /* 重设中断向量的基地址为我们的新uboot的入口地址 */
	ldr	r0, [r9, #GD_RELOCADDR]	/* r0 = gd->relocaddr */
	mcr     p15, 0, r0, c12, c0, 0  /* Set VBAR */
#else
	/*
	 * Copy the relocated exception vectors to the
	 * correct address
	 * CP15 c1 V bit gives us the location of the vectors:
	 * 0x00000000 or 0xFFFF0000.
	 */
	ldr	r0, [r9, #GD_RELOCADDR]	/* r0 = gd->relocaddr */
	mrc	p15, 0, r2, c1, c0, 0	/* V bit (bit[13]) in CP15 c1 */
	ands	r2, r2, #(1 << 13)
	ldreq	r1, =0x00000000		/* If V=0 */
	ldrne	r1, =0xFFFF0000		/* If V=1 */
	ldmia	r0!, {r2-r8,r10}
	stmia	r1!, {r2-r8,r10}
	ldmia	r0!, {r2-r8,r10}
	stmia	r1!, {r2-r8,r10}
#endif
#endif
	bx	lr        /* 返回 */

ENDPROC(relocate_vectors)

ENTRY(c_runtime_cpu_setup)
/*
 * If I-cache is enabled invalidate it
 */
#ifndef CONFIG_SYS_ICACHE_OFF        /* 没定义,所以还是要无效cache的 */
	mcr	p15, 0, r0, c7, c5, 0	@ invalidate icache
	mcr     p15, 0, r0, c7, c10, 4	@ DSB
	mcr     p15, 0, r0, c7, c5, 4	@ ISB
#endif

	bx	lr

ENDPROC(c_runtime_cpu_setup)

总结:可以看到uboot的前半部分主要是把定位在0x34800000地址的uboot搬移到DDR的顶部位置,同时初始化了一些底层的配置,把板子的一些参数保存进了gd中,方便后面使用。

猜你喜欢

转载自blog.csdn.net/qq_16777851/article/details/81749077