本文,使用开发板JZ2240配套的u-boot-1.1.6学习uboot移植,但是使用之前编译的toolchain。其eabi为新的EABI,老的toolchian为old eabi。
主Makefile添加:
1 2 | 100ask24x0_config : unconfig @$(MKCONFIG) $(@:_config=) arm arm920t 100ask24x0 NULL s3c24x0zh |
MKCONFIG是一个脚本mkconfig。
执行:
1 | make O= /work/s3c2440/object/u-boot-1 .1.6 100ask24x0_config |
O选项,配置编译目录,不在源码目录编译,mkconfig脚本,创建几个文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 | herbert@herbert-pc: /work/s3c2440/object/u-boot-1 .1.6$ tree . ├── include │ ├── asm -> asm-arm │ ├── asm-arm │ │ ├── arch -> ../.. /include2/asm/arch-s3c24x0 │ │ └── proc -> ../.. /include2/asm/proc-armv │ ├── config.h │ └── config.mk └── include2 └── asm -> /work/s3c2440/u-boot-1 .1.6 /include/asm-arm 7 directories, 2 files |
config.h头文件,包含真正的配置文件。
1 2 3 | herbert@herbert-pc: /work/s3c2440/object/u-boot-1 .1.6$ cat include /config .h /* Automatically generated - do not edit */ #include <configs/100ask24x0.h> |
config.mk的内容:
1 2 3 4 5 | herbert@herbert-pc: /work/s3c2440/object/u-boot-1 .1.6$ cat include /config .mk ARCH = arm CPU = arm920t BOARD = 100ask24x0 SOC = s3c24x0 |
因为主Makefile使用CROSS_COMPILE变量来存储交叉编译工具的前缀,arm默认为arm-linux-,和我自己的编译工具不同,所以,添加CROSS_COMPILE定义到config.mk
1 2 | echo "CROSS_COMPILE := /work/toolchain/arm-unknown-linux-gnueabi/bin/arm-unknown-linux-gnueabi-" \ > .. /object/u-boot-1 .1.6 /include/config .mk |
执行编译:
1 | make O= /work/s3c2440/object/u-boot-1 .1.6 2>&1 | tee .. /make .u-boot-1.1.6.log |
编译,最后链接阶段报错:
1 | /work/toolchain/arm-unknown-linux-gnueabi/bin/arm-unknown-linux-gnueabi-ld.bfd: error: Source object /work/toolchain/arm-unknown-linux-gnueabi/bin/../lib/gcc/arm-unknown-linux-gnueabi/6.3.0/libgcc.a(_udivdi3.o) has EABI version 5, but target u-boot has EABI version 0 |
EABI和libgcc.a的EABI不匹配,查看log发现,编译时有:-march=armv4 -mabi=apcs-gnu选项,这个选项导致不匹配,去掉这个选项,
往lib_arm/div0.c添加raise(),编译还是报错。可能是链接使用lib_arm.a导致的,后续在来学习ld符号解析的过程。
添加cpu/arm920t/raise.c,在里面添加raise()函数,Makefile添加 OBJS += cpu/$(CPU)/raise.o可正常编译。然而烧录后,起来无反应,后续来解决。
编译的目标:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | cpu/arm920t/start.o cpu/arm920t/raise.o lib_generic/libgeneric.a board/100ask24x0/lib100ask24x0.a cpu/arm920t/libarm920t.a cpu/arm920t/s3c24x0/libs3c24x0.a lib_arm/libarm.a fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a net/libnet.a disk/libdisk.a rtc/librtc.a dtt/libdtt.a drivers/libdrivers.a drivers/nand/libnand.a drivers/nand_legacy/libnand_legacy.a drivers/usb/libusb.a drivers/sk98lin/libsk98lin.a common/libcommon.a |
接下来分析源码。
相关初始化如下:
reset执行的操作
1 设置CPU模式为SVC模式。
2 关闭看门狗
3 屏蔽所有中断
5 cpu_init_crit
清除cache,disable MMU,跳转到lowlevel_init
lowlevel_init.S 初始化sdram
6 设置栈
7 clock_init (board/100ask24x0/boot_init.c)
8 CopyCode2Ram (board/100ask24x0/boot_init.c)
初始化nand,拷贝数据
9 bss段清0
10 跳到_start_armboot() c代码执行
定位到是韦东山同学添加的下述函数导致的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | int bBootFrmNORFlash( void ) { volatile unsigned int *pdw = ( volatile unsigned int *)0; unsigned int dwVal; /* * 无论是从NOR Flash还是从NAND Flash启动, * 地址0处为指令"b Reset", 机器码为0xEA00000B, * 对于从NAND Flash启动的情况,其开始4KB的代码会复制到CPU内部4K内存中, * 对于从NOR Flash启动的情况,NOR Flash的开始地址即为0。 * 对于NOR Flash,必须通过一定的命令序列才能写数据, * 所以可以根据这点差别来分辨是从NAND Flash还是NOR Flash启动: * 向地址0写入一个数据,然后读出来,如果没有改变的话就是NOR Flash */ dwVal = *pdw; *pdw = 0x12345678; if (*pdw != 0x12345678) { return 1; } else { *pdw = dwVal; return 0; } } |
这个函数向0地址写数据,在读出来比较。编译时使用的-Os选项,编译器把这个函数给优化了,反汇编结果如下:
1 2 3 4 | 000003b4 <bBootFrmNORFlash>: 3b4: e3a03000 mov r3, #0 3b8: e5933000 ldr r3, [r3] 3bc: e7f000f0 udf #0 |
这样判断从nor启动了,导致启动失败。
google后,针对该函数不优化。这是gcc的新特性,在函数前添加一行:
1 | int bBootFrmNORFlash( void ) __attribute__ ((optimize( "-O0" ))); |
个人认为判断启动方式,应该读取启动strap引脚的电平来判断。韦东山同学的这种方法不可取。用导致错误的方式(向nor写入数据)来判断,可移植性等比较差。