bootloader启动过程:
1.初始化硬件,关闭看门狗,设置时钟,设置SDRAM,初始化NAND FLASH
2.如果bootloader比较大,要将其重定位到SDRAM中
3.把内核从NAND FLASH读到SDRAM
4.设置要传给内核的参数
5.跳转执行内核
start.s文件
1.设置为管理模式;set the cpu to SVC32 mode
2.关看门狗turn off the watchdog
3.屏蔽所有中断;mask all IRQs by setting all bits in the INTMR - default
4.设置时钟比例;
5.设置内存控制器;Set stackpointer in internal RAM to call board_init_f
6.设置栈,调用c函数board_init_f
7.调用函数数组init_sequence的各个初始化函数
8.在7中的init_sequence调用函数之后会有重定位代码relocate_code
在u-boot中重定位代码,在FLASH中的代码会被复制到SDRAM中的链接地址(代码应该所处的位置),cpu在刚开始运行的时候是运行位置无关码(这段代码不管位于那个位置都可以执行)。
重定位代码:
将NOR FLASH中的代码复制到SDRAM中,复制的目标地址可以任选;由于程序的链接地址是0,访问全局变量,静态变量,调用函数时是使用基于0地址编译的到的地址,然而把程序复制到SDRAM中后,需要修改代码,把原来基于0地址编译得到的地址,改为新地址,需要在链接的时候使用PIE;
简单来说为什么需要重定位代码,就是因为链接地址和运行地址不同时CPU运行会产生错误,cpu在运行的时候是依靠芯片内部的SRAM,但是芯片内部的SRAM很小,不足以存储所有的代码。当芯片启动出现链接地址和运行地址不同时,就需要进行重定位,执行一段位置无关码,这段位置无关码的作用就是将原先的那一份代码复制到链接地址去,然后再跳转到新的那一份代码中进行运行;
relocated_code (addr_sp,id,addr)
addr_sp:设置栈的位置
id:变量gd_t的地址
addr:代码从FLASH中拷贝到的目标地址
9.清除bss
10.调用c函数board_init_r
汇编代码中使用到了ldr = 伪汇编指令,需要将代码进行反汇编获取u-boot.dis反汇编文件,在反汇编文件中获取地址信息
分析重定位,修改代码为新地址
/*
* fix .rel.dyn relocations
*/
ldr r0, _TEXT_BASE /* r0 <- Text base */ //ldr 没有等于号就是读内存指令 r0 = _TEXT_BASE = 0,代码中的基地址
sub r9, r6, r0 /* r9 <- relocation offset */ //r9 = r6-r0 = 0x33f41000 - 0 = 0x33f41000
ldr r10, _dynsym_start_ofs /* r10 <- sym table ofs */ //r10 = _dynsym_start_ofs =__dynsym_start - _start =00073608 动态变在反汇编代码中查看
add r10, r10, r0 /* r10 <- sym table in FLASH */ //r10 = r10+r0 = 00073608
ldr r2, _rel_dyn_start_ofs /* r2 <- rel dyn start ofs */ //r2 = _rel_dyn_start_ofs= __rel_dyn_start - _start = 0006b568
add r2, r2, r0 /* r2 <- rel dyn start in FLASH */ //r2 = r2+r0 = 0006b568
ldr r3, _rel_dyn_end_ofs /* r3 <- rel dyn end ofs */ //r3= _rel_dyn_end_ofs = __rel_dyn_end - _start = 00073608
add r3, r3, r0 /* r3 <- rel dyn end in FLASH */ //r3 = r3+r0 = 00073608
fixloop: //循环修改地址将原来地址改为新地址
ldr r0, [r2] /* r0 <- location to fix up, IN FLASH! */
//第1次循环 r0 = [0006b568] = 00000020
add r0, r0, r9 /* r0 <- location to fix up in RAM */
//第一次循环 r0 = r0+r9 = 00000020+0x33f41000 = 0x33f41020
ldr r1, [r2, #4]
//第一次循环:r1 = [0006b568+4] = 00000017根据反汇编得到
and r7, r1, #0xff
//第一次循环: r7= r1&0xff = 00000017
cmp r7, #23 /* relative fixup? */
//第一次循环:比较r7是否等同于23(0x17)
beq fixrel
//如果上一个比较命令相等跳到fixrel执行
cmp r7, #2 /* absolute fixup? */
beq fixabs
/* ignore unknown type of fixup */
b fixnext
fixabs:
/* absolute fix: set location to (offset) symbol value */
mov r1, r1, LSR #4 /* r1 <- symbol index in .dynsym */
add r1, r10, r1 /* r1 <- address of symbol in table */
ldr r1, [r1, #4] /* r1 <- symbol value */
add r1, r1, r9 /* r1 <- relocated sym addr */
b fixnext
fixrel:
/* relative fix: increase location by offset */
ldr r1, [r0]
//第一次循r1 = [r0] = 000001e0
add r1, r1, r9
//第一次循环r1 = r1+r9 = 000001e0 +0x33f41000 = 33F411E0
fixnext:
str r1, [r0]
//第一次循环: [r0]=r1 [0x33f41020] = 33F411E0
add r2, r2, #8 /* each rel.dyn entry is 8 bytes */
//第一次循环 r2 = r2+8 = 0006b568 + 8 = 6B570
cmp r2, r3
//第一次循环:比较r2 r4 如果相等则全部结束,如果不等则跳转fixloop
blo fixloop
#endif
程序中有些地址在链接时不能确定,要到运行前才能确定;fixabs
修改u-boot
建立一个单板:
1.进入单板目录将smdk2410复制一份为smdk2440
2.进入include/config中将smdk2410.h 复制一份为smdk2440.h
3.在u-boot源码目录下搜索smdk2410,按照smdk2410的配置同步smdk2440
修改boards.cfg
仿照65行命令添加66行命令
4.make smdk2440_config生成.config文件
5.make 编译生成单板
6.烧写看结果(如果板内的u-boot不能用,先烧写可以使用的u-boot,然后再将编译的u-boot烧录进去)
7.调试
观察时钟和内存设置
阅读代码发现问题:
(1).u-boot中先以60MHz的时钟计算参数来设置内存控制器,但是现在MPLL(系统时钟)还未设置
处理方法:把MPLL的设置放到start.s里面,取消board_early_init_f里对MPLL的设置
在start.s中设置时钟
#define S3C2440_MPLL_200MHZ ((0x5c<<12)|(0x01<<4)|(0x02))
/* 2. 设置时钟 */
ldr r0, =0x4c000014
mov r1, #0x03; // FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1
str r1, [r0]
/* 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */
mrc p15, 0, r1, c1, c0, 0 /* 读出控制寄存器 */
orr r1, r1, #0xc0000000 /* 设置为“asynchronous bus mode” */
mcr p15, 0, r1, c1, c0, 0 /* 写入控制寄存器 */
/* MPLLCON = S3C2440_MPLL_200MHZ */
ldr r0, =0x4c000004
ldr r1, =S3C2440_MPLL_200MHZ
str r1, [r0]
在lowlevel_init.s中修改内存
SMRDATA:
.long 0x22011110 //BWSCON
.long 0x00000700 //BANKCON0
.long 0x00000700 //BANKCON1
.long 0x00000700 //BANKCON2
.long 0x00000700 //BANKCON3
.long 0x00000700 //BANKCON4
.long 0x00000700 //BANKCON5
.long 0x00018005 //BANKCON6
.long 0x00018005 //BANKCON7
.long 0x008C04F4 // REFRESH
.long 0x000000B1 //BANKSIZE
.long 0x00000030 //MRSRB6
.long 0x00000030 //MRSRB7
(2).将修改后的文件放入服务器中进行编译再烧入开发板观察现象,发现乱码,查看是串口波特率设置出现问题,找到串口波特率设置函数_serial_setbrg里面的get_HCLK没有定义CONFIG_S3C2440这个宏;
处理方法:include/configs/smdk2440.h: 去掉#define CONFIG_s3c2410,加上#define CONFIG_S3C2440,
编译之后还是会报错s3c2410_nand,根据报错信息查看对应文件中,通过Makefile看错误依赖于宏CONFIG_CMD_NAND
去掉#define CONFIG_CMD_NAND再编译;
修改u-boot支持NAND启动:
原来的代码在链接的时候添加了-pie选项,使得u-boot.bin里多了"(.rel)","(.dynsym)"等信息使得程序很大,不利于从NAND启动(重定位之前的代码应该小于4K);所以需要去掉-pie
参考之前编写的bootloader中start.s和init.c来修改代码
添加编写bootlaoder时的init.c函数放入单板目录board/samsung/smdk2440中
根据bootloader中start.s设置u-boot中的nand_init_ll的汇编代码
1.bootloader中start.s重定位代码:
根据u-boot的start.s文件的起始位置修改汇编代码
修改后的代码:
设置起始位置为0x33f00000
2.根据链接地址文件将bootloader赋值过来的init.c清除bss段修改为和u-boot的函数名一样
修改后的代码
3.修改start,s文件,设置跳转清除bss和board_inti函数,
由于重定位已经完成过,所以将board_init_f中的relocate_code去掉
实现了自己的重定位代码和清除bss段之后,就可以将start.s文件中的relocate和清除bss代码段删掉;
第一阶段完成;
第二阶段
在start.s中代码运行到board_init_f之后就进入到第二阶段board_init_r
第二阶段的board_init_r函数
根据函数可知该函数需要两个参数,第一个参数是第一阶段中的id,所以设置board_init_f函数返回值为unsigned int ,函数末尾return id
将之前的void改为unsigned int
还要修改所在头文件common.h
返回id
board_init_r函数的第一个参数就是程序的连接地址;因此可以在start.s中添加汇编代码:
第二阶段完成;
修改board/samsung/smdk2440目录下的Makefile,将init.c放入Makefile
查找u-boot源码目录下-pie,去掉-pie
去掉arch/arm/config.mk 的75行
修改链接脚本:把start.s,init.c, lowlevel.s等文件放在前面
删除u-boot.lds再进行编译会自动生成链接脚本
使用make编译之后,find -name “u-boot.lds”
在找到的arch/arm/cpu/u-boot.lds中进行修改
根据lowlevel_init.s和init.c所在文件夹可以看出,lowlevel_init.s和init.c文件应该是在库smdk2440.o中
根据内存分布图需要将board.文件中的addr
addr = 0x33f00000(所以u-boot一定不能超过512k)
也可以利用之前修改过的宏对其进行赋值
修改u-boot支持Nor Flash
deviers/mts/jedec_flash.c加上新的型号
修改u-boot支持Nand Flash
修改smdk2440.h #define CONFIG_CMD_NAND
修改u-boot支持网卡支持DM9000
Dm9000在drivers/net中打开drivers/net中的Makefile,将Dm9000编译到u-boot中去
根据Makefile知道需要配置宏CONFIG_DRIVER_DM9000才能支持Dm9000;
在smdk2440.h中需要去掉cs8900加上宏CONFIG_DRIVER_DM9000
先进行编译,然后根据报错,观察其他的文件中如何给Dm9000进行设置,仿照其进行改写;
设置访问地址
根据电路原理图片选引脚nGCS4和芯片手册的内存控制器对应的片选引脚,设置基地址0x20000000;
CMD引脚为LADDR2,所以bit2为高电平(100也就是4)时可以读数据
按照芯片手册设置内存控制器
包括设置时序和位宽;
设置lowlevel_init 中的BANKCON4寄存器;
编译之后仍然有问题,找到问题所在地
board.c 中eth初始化
etn_initialize中的单板初始化board_eth_init
在board_eth_init中设置
仿照cs8900添加dm9000初始化函数
再对其进行编译,编译通过烧到开发板,设置IP地址和MAC地址,使用网线与电脑ping看是否ping通;
u-boot易用性修改裁剪以及制作补丁
裁剪u-boot,在smdk2440.h中去掉不需要使用的宏,让u-boot变小;
如usb等等
系统默认环境参数在env_common.c中;根据这些信息在smdk2440.h中设置宏
默认环境参数
在smdk2440.h中设置相关环境变量