这章讲解BL2阶段的代码重定位和串口功能的实现.
在BL1阶段执行最后一句代码:
ldr pc, =CONFIG_SYS_TEXT_BASE
此时PC指针重新指向b reset命令,第二阶段无需重新做cpu,时钟之类的初始化,直接执行BL _main命令设置BL2模式下的sp.我将BL2模式下DDR的分布设置如下:
--------------------0x30000000 + 64M
u-boot.bin
--------------------0x30008000
.
.(not use)
--------------------0x30001000global data --------------------0x30001000 - 176GD
--------------------0x30001000 - 176 - GD_SIZE.SP..---------------------0x30000000</span>
设置完堆栈后执行
BL board_init_r,下面是详细分析这段函数的相关代码和注释:
gd->mon_len = (ulong)&__bss_end - (ulong)_start;//记录代码段起始地址到bss_end总大小
↓
↓
init_baud_rate();//设置串口波特率为115200
↓
↓
serial_init(); //设置uart时钟,新版的u-boot支持2440的时钟配置,只需将CONFIG_S3C2440 = 1;不用像u-boot-1.1.6那样重新写代码
↓
↓
get_current()->start();//相当于调用了serial_init_dev(0);函数,下面有分析
↓
↓
dev = default_serial_console();//我们需要调用serial_s3c24x0.c中的函数,查看MakeFile发现obj-$(CONFIG_S3C24X0_SERIAL) += serial_s3c24x0.o
//<strong>因此需要在yl2440.h中定义CONFIG_S3C24X0_SERIAL</strong>
↓
return &s3c24xx_serial0_device;//我们默认使用serial0,因此需要在yl2440.h中<strong>#define CONFIG_SERIAL1 1</strong>
而struct serial_device s3c24xx_serial0_device = INIT_S3C_SERIAL_STRUCTURE(0, "s3ser0"); 将 INIT_S3C_SERIAL_STRUCTURE展开即
struct serial_device s3c24xx_serial0_device =
{
.name = "s3ser0", \
.start = s3serial0_init, \
.stop = NULL, \
.setbrg = s3serial0_setbrg, \
.getc = s3serial0_getc, \
.tstc = s3serial0_tstc, \
.putc = s3serial0_putc, \
.puts = s3serial0_puts, \ /* printf()--->>> dev->puts() */
}
↓
↓
gd->ram_size = PHYS_SDRAM_1_SIZE; //在yl2440.h中定义成64M
↓
↓
setup_dest_addr(); //首先将gd->relocaddr设置为0x30000000+0x4000000(64M)
↓
↓
gd->relocaddr = 0x30000000+0x4000000;
↓
↓
reserve_uboot
↓
↓
gd->relocaddr -= gd->mon_len; //relocaddr指向代码重定位的地址
gd->start_addr_sp = gd->relocaddr;
↓
↓
reserve_malloc()
↓
↓
gd->start_addr_sp = gd->start_addr_sp - TOTAL_MALLOC_LEN;
↓
↓
reserve_board
↓
↓
gd->start_addr_sp -= sizeof(bd_t);
gd->bd = gd->start_addr_sp;
将bd区域清0
↓
↓
reserve_global_data()
↓
↓
gd->start_addr_sp -= sizeof(gd_t);
gd->new_gd = gd->start_addr_sp ;
↓
↓
reserve_stacks()
↓
↓
gd->start_addr_sp -= 16;
gd->start_addr_sp &= ~0xf;
↓
↓
setup_reloc();
↓
↓
gd->reloc_off = gd->relocaddr - CONFIG_SYS_TEXT_BASE;//gd->reloc_off = gd->relocaddr - 0x30008000;
memcpy(gd->new_gd, (char *)gd, sizeof(gd_t));// 0x30001000-176-GD_SIAZE ---->>>> gd->new_gd搬移
需要说明的是在该函数调用的board_early_init_f()中将设置PLL相关部分全部注释掉,因为之前的时钟设置已经在BL1阶段设置好了.
board_init_r退出重新设置新的sp指针:LDR SP [r9 #GD_START_ADDR_SP] /* sp = gd->start_addr_sp这个值已经之前设置好了 */
从新的gd地址找到新的gd->relocaddr即代码重定位的地址,调用b relocate_code后,此时新的DDR分布如下:
-------------0x304000000 sdram end(64M)
.
.
<strong>u-boot.bin + 动态链接库(不是很清楚,估计和相对地址有关)</strong>
.
-------------gd->relocaddr 0x304000000 - (gd->mon_len)
malloc lenth
-------------0x304000000 - (gd->mon_len + malloc_lenth)
bd(记录板子的信息,如bd->baudrate)
-------------0x304000000 - (gd->mon_len + malloc_lenth + bd)
gd(记录初始化参数,如gd->start_addr_sp)
-------------0x304000000 - (gd->mon_len + malloc_lenth + bd + gd)
16Byte
-------------gd->start_addr_sp 0x304000000 - (gd->mon_len + malloc_lenth + bd + gd + 16)
sp
-------------
.
.(not use)
-------------
kernel(2M)
-------------0x30800000
.
.boot param
-------------0x30000100
(not use)
-------------0x30000000
由于在4K IRAM内的中断向量表是BL1阶段的向量表,因而需要执行搬移BL2的向量表到4K IRAM内
bl relocate_vectors
最后执行
ldr pc, =board_init_r
进入板子的第二阶段初始化工作,此时在串口已经可以显示U-BOOT2015.10的信息了.串口功能调试完毕.
总结:
U-Boot第二阶段(BL2)最开始将位于用户定义的代码链接地址从0x30008000重定位到SDRAM顶端,并生成了一个动态链接库rel_dyn_start到rel_dyn_end.并重新设置sp,拷贝就得bd数据到新的bd区域,重定位向量表,最终执行第二阶段的板卡初始化.
这个阶段代码重定位完毕,串口已经可以跑起来了.
我们知道U-boot终极目的是引导内核,因此我们还需要以下功能:
功能1:u-boot支持tftp下载内核到DDR中,因此需要配置DM9000网卡
功能2:u-boot需要支持Nand读写,将内核及环境变量写到Nand Flash的相关位置,上电后将镜像读到DDR
功能3:u-boot能将位于DDR的内核进行引导,最终U-boot寿命结束,控制权交给内核
本章结束,下一章将介绍如何配置DM9000,实现网卡的下载功能.
遗留问题:动态链接库、中断重定位的细节