前边4篇文章,成功将 u-boot2012 移植到了 2440 开发板上,但是它仅仅支持 norflash 启动并不够完善,下面我们设法让它支持两种启动方式。
首先,我们得先分析一下目前的启动流程:
链接地址为 0
第一阶段:start.S中的汇编部分,包括时钟、sdram 等初始化
第二阶段:board_init_f 先是调用了大量C函数进行串口什么的初始化,然后进行内存划分
第三阶段:根据第二阶段的内存划分 start.S 中进行代码重定位,清BSS,修正位置有关码,跳到sdram里去执行 board_init_r
那么,如果我们想 nandflash 启动,那么我们必须在前4K代码中就进行 nandflash 的初始化,用nandflash的读函数将代码进行重定位到sdram里,现在有一个矛盾摆在我们面前。首先是,原来的重定位的地方在第三阶段,而且第二阶段调用了大量的C函数,重定位的时候早就在 4K 之外了。如果我们把重定位放在第一阶段和第二阶段之间,那么那时内存还没有进行划分,我们不知道重定位到哪里去。
解决办法:采用 uboot1.1.6 那种老的方式,在第一阶段和第二阶段之间进行重定位,重定位到固定地址 0x33f00000 中去,第二阶段进行内存划分时,将u-boot的起始地址也固定为 0x33f0000 ,在链接时,指定链接地址为0x33f00000,这样,我们也可以去掉 *.rel 段,实际上是去掉编译时的 -pie 选项。
还有一个问题,我们nandflash启动时,norflash对cpu是不可见的,因此我们根本无法对Norflash进行初始化,更无法将环境变量保存在Norflash上了。
解决办法:判断如果是nandflash启动,不进行Norflash的初始化。无论那种方式启动,均将环境变量保存在nandflash上。
1、去掉 -pie 选项
grep "\-pie" * -nRw
arch/arm/config.mk:75:LDFLAGS_u-boot += -pie
直接注释掉即可。
2、指定链接地址
grep "Ttext" * -nRw
找到一个宏 CONFIG_SYS_TEXT_BASE 搜索这个宏,发现是在 smdk2440.h 中定义,修改为
#define CONFIG_SYS_TEXT_BASE 0x33f00000
3、在第二阶段内存划分时,固定u-boot的起始地址
在 arch/aem/lib/board.c 中的
addr -= gd->mon_len;
addr &= ~(4096 - 1); 这两行后边,新增一行
addr = 0x33f00000;//增加
4、实现判断 nor 启动还是 nand 启动的 C 函数
nor启动时,片内ram位于0x40000000处,可以直接读写,0-2M的空间可以直接读,但是没法直接写入。
nand启动时,片内ram位于0x0处,0-4k,可以直接读写。
我一开始尝试在 0x40000000 处写值,然后读取回来看是否成功,发现 nand 启动时访问0x40000000会触发异常直接死掉。那么只好尝试读写0地址了。
int isNandBoot(){
#define RAMSTART (*(volatile unsigned long *)0x00000000)
unsigned long temp = RAMSTART;
RAMSTART = 0xbadc0de;
if(RAMSTART == 0xbadc0de){
RAMSTART = temp;
return 1;
}else{
RAMSTART = temp;
return 0;
}
}
nand启动返回1,Nor启动返回0
5、将nandflash的初始化函数、读函数和上边的判断函数统一放入 smdk2440.c 中,同时,我们需要改变链接顺序,将 smdk2440.o 尽量靠前放,索性放在 start.o 的后边好了。但是经过我的尝试,smdk2440.c 和 lowlevel_init.S 会被链接成 libsmdk2440.o ,只把smdk2440.o放到前面的话,会造成函数的重定义,解决办法就是把 libsmdk2440.o 放到前边。
更改链接脚本:/arc/arm/cpu/u-boot.lds
在 CPUDIR/start.o (.text) 后边添加一行
board/samsung/smdk2440/libsmdk2440.o (.text)
尝试编译一下,正常应该没问题。
6、在第一阶段和第二阶段之间增加代码重定位等相关操作
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
/******************此行之前的代码保持不变,增加*************************/
ldr sp, =(CONFIG_SYS_INIT_SP_ADDR)
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */
/* 判断是 nand 还是 nor 启动 */
bl isNandBoot
mov r1,#0x01
cmp r0,r1
bne copy_nor /* 不相等nor 相等nand*/
copy_nand:
bl my_nand_init
ldr r0, =0x33f00000
mov r1, #0x00
ldr r2, =<span style="font-family: Arial, Helvetica, sans-serif;">_bss_start_ofs</span>
bl my_nand_read
bl clear_bss
copy_nor:
mov r0, #0x00
ldr r1, =0x33f00000 /* r1 <- scratch for copy_loop */
ldr r3, _bss_start_ofs
add r2, r0, r3 /* r2 <- source end address */
copy_loop:
ldmia r0!, {r9-r10} /* copy from source address [r0] */
stmia r1!, {r9-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end address [r2] */
blo copy_loop
bl clear_bss
/*****************call_board_init_f 中去掉sp的设置***********************/
call_board_init_f:
ldr r0,=0x00000000
bl board_init_f
/*************** 将 clear_bss 提升至 relocate_code 的前面****************/
clear_bss:
#ifndef CONFIG_SPL_BUILD
ldr r0, _bss_start_ofs
ldr r1, _bss_end_ofs
ldr r4, =0x33f00000 /* reloc addr */
add r0, r0, r4
add r1, r1, r4
mov r2, #0x00000000 /* clear */
clbss_l:str r2, [r0] /* clear loop... */
add r0, r0, #4
cmp r0, r1
bne clbss_l
ldr pc, =call_board_init_f // 增加跳转到sdram的指令
#endif
/*************** 去掉原有的代码重定位操作****************/
.globl relocate_code
relocate_code:
mov r4, r0 /* save addr_sp */
mov r5, r1 /* save addr of gd */
mov r6, r2 /* save addr of destination */
mov sp, r0 /* 设置栈 */
/* setup parameters for board_init_r */
mov r0, r5 /* gd_t */
mov r1, r6 /* dest_addr */
/* jump to it ... */
bl board_init_r
/*
***********************之后代码保持不变**********************************
*
* CPU_init_critical registers
*
* setup important registers
* setup memory timing
*
*************************************************************************
*/
7、在norflash初始化代码前,增加判断在 arch\arm\lib\board.c -> board_init_r 中将
#if !defined(CONFIG_SYS_NO_FLASH) 作用域之内的代码放入 if(!isNandBoot()) 条件语句。
8、将环境变量存储到 nandflash
首先,我查看了一下 common 目录下,env_flash.c 是被编译进u-boot了,但是 env_nand.c 并没有,看下makefile 它依赖于哪个宏。
COBJS-$(CONFIG_ENV_IS_IN_FLASH) += env_flash.o
COBJS-$(CONFIG_ENV_IS_IN_NAND) += env_nand.o
搜索一下,发现 CONFIG_ENV_IS_IN_FLASH 在 smdk2440.h 中定义
去掉 #define CONFIG_ENV_IS_IN_FLASH
增加 #defineCONFIG_ENV_IS_IN_NAND
实际在编译测试的过程中,只定义 CONFIG_ENV_IS_IN_NAND 是不够的,会打印出错误信息
Need to define CONFIG_ENV_OFFSET when using CONFIG_ENV_IS_IN_NAND
意思很明确,我们需要定义环境变量在 nandflash 中的偏移地址,如果对 u-boot 进行分区的话,环境变量应该是存在于 params 区域,为了和内核保持一致,我们干脆分区得了。
9、查看内核启动打印信息
0x00000000-0x00040000 : "bootloader"
0x00040000-0x00060000 : "params"
0x00060000-0x00260000 : "kernel"
0x00260000-0x10000000 : "root"
这是内核的分区划分,我们按照内核的分区,也给 u-boot 进行分区。
添加分区,smdk2440.h 中要有一下宏定义:
#define CONFIG_CMD_MTDPARTS
#define CONFIG_MTD_DEVICE
#define CONFIG_MTD_PARTITIONS
加入MTD分区信息:
#define MTDIDS_DEFAULT "nand0=2440_nand"
#define MTDPARTS_DEFAULT "mtdparts=2440_nand:256k@0(bootloader)," \
"128k(params)," \
"2M(kernel)," \
"-(root)"
这个内核比较小,因此内核分区只有2M,如果内核比较大的话,在修改就是了。
环境变量的位置确定了,我们需要在smdk2440.h中定义偏移地址
#define CONFIG_ENV_OFFSET0x40000
同时,在smdk2440.h 中定义的 CONFIG_ENV_SIZE 默认为 0x10000 修改为 0x20000
10、剪裁u-boot
我尝试nand启动时,发现启动不了,是因为我们的u-boot有400多K,而我们的 bootloader 只有256k,要么修改分区,要么剪裁u-boot,是它小于256k,如何剪裁,就是修改smdk2440.h宏咯。
在 smdk2440.h 中去掉
#define CONFIG_CMD_FAT
#define CONFIG_CMD_EXT2
#define CONFIG_CMD_UBI
#define CONFIG_CMD_UBIFS
#define CONFIG_YAFFS2
#define CONFIG_RBTREE
#define CONFIG_USB_OHCI
#define CONFIG_USB_KEYBOARD
#define CONFIG_USB_STORAGE
#define CONFIG_DOS_PARTITION
#define CONFIG_BOOTP_BOOTFILESIZE
#define CONFIG_BOOTP_BOOTPATH
#define CONFIG_BOOTP_GATEWAY
#define CONFIG_BOOTP_HOSTNAME
#define CONFIG_CMD_USB
#define CONFIG_CMD_BSP
make clean
make
编译完你会发现体积大大减小,不到200K
11、烧写测试,不出意外,我们的 nor nand 都能启动了