总览
本文基于 u-boot-1.1.6, 使用jz2440开发板. 若要使用最新的u-boot版本见: u-boot官网 u-boot下载
u-boot 编译过程
解压缩 u-boot-1.1.6
打补丁 针对特定开发板发布的补丁. 打补丁方法uboot目录下$ patch -p1 < ../补丁文件
p1表示忽略补丁目标目录的第一层
配置 make 100ask24x0_config
编译: make
, 获取 u-boot.bin
文件
烧录, 多种方式, 可以用jlink烧录到 NOR Flash 中
u-boot功能:
本质是单片机程序
硬件相关初始化
关看门狗
初始化时钟
初始化SDRAM
从Flash读取内核
最终目的: 启动内核
为开发方便, 还支持:
u-boot的README 要了解 u-boot 的架构和设计思路, 建议先看自带的 README
文件. 重要信息如下:
include/configs/<board_name>.h
板级配置头文件
make NAME_config
加载配置, 准备编译
make
or make all
编译生成bin文件
Monitor Commands - Overview:
u-boot 指令概览
Environment Variables:
u-boot 环境变量
Linux HOWTO:
编译linux uImage
Boot Linux:
u-boot 启动Linux相关设置
自己设置板级配置的步骤: (位于 README line 2375)
Add a new configuration option for your board to the toplevel “Makefile” and to the “MAKEALL” script, using the existing entries as examples. Note that here and at many other places boards and other names are listed in alphabetical sort order. Please keep this order.
Create a new directory to hold your board specific code. Add any files you need. In your board directory, you will need at least the “Makefile”, a “.c”, “flash.c” and “u-boot.lds”.
Create a new configuration file “include/configs/.h” for your board
If you’re porting U-Boot to a new CPU, then also create a new directory to hold your CPU specific code. Add any files you need.
Run “make _config” with your new name.
Type “make”, and you should get a working “u-boot.srec” file to be installed on your target system.
Debug and solve any problems that might arise.
u-boot的Makefile 分析 100ask24x0
即jz2440板子的Makefile实现. Linux下可以使用grep搜索.
grep -n 100ask24x0 ./Makefile grep -nr 100ask24x0 * grep -nwr 100ask24x0 * grep -nd skip 100ask24x0 * find ./ -name "Makefile" | xargs grep -nw --color "uboot"
‘make 100ask24x0_config’ 指令分析 make 100ask24x0_config
其指令结构和 make clean
是一样的. 因此在 Makefile 里找到 100ask24x0_config
为目标的行即可:
1886 100ask24x0_config : unconfig 1887 @$(MKCONFIG) $(@:_config=) arm arm920t 100ask24x0 NULL s3c24x0 ./mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0
因此, 我们需要去当前文件夹下查找 mkconfig
这个脚本文件. 下面直接删减成最终执行的样子, 前面加上原来的行号便于学习比较
23 BOARD_NAME="$1 " 28 "Configuring for ${BOARD_NAME} board..." 46 cd ./include 47 rm -f asm 48 ln -s asm-$2 asm 51 rm -f asm-$2 /arch 56 ln -s ${LNPREFIX} arch-$6 asm-$2 /arch 60 rm -f asm-$2 /proc 61 ln -s ${LNPREFIX} proc-armv asm-$2 /proc 67 echo "ARCH = $2 " > config.mk 68 echo "CPU = $3 " >> config.mk 69 echo "BOARD = $4 " >> config.mk 71 [ "$5 " ] && [ "$5 " != "NULL" ] && echo "VENDOR = $5 " >> config.mk 73 [ "$6 " ] && [ "$6 " != "NULL" ] && echo "SOC = $6 " >> config.mk 82 > config.h 84 echo "/* Automatically generated - do not edit */" >>config.h 85 echo "#include <configs/$1 .h>" >>config.h
‘make’ 指令分析 make
实际上执行的是 make all
, Makefile 中比较重要的几行为:
116 # load ARCH, BOARD, and CPU configuration 117 include $(OBJTREE)/include/config.mk 118 export ARCH CPU BOARD VENDOR SOC # ./include/config.mk 这个文件由 `make 100ask24x0_config` 指令生成, 可获得如下变量 # ARCH = arm # CPU = arm920t # BOARD = 100ask24x0 # SOC = s3c24x0 164 include $(TOPDIR)/config.mk # u-boot-1.1.6 根目录下的配置文件, 里面有一些变量的定义 169 OBJS = cpu/$(CPU)/start.o 193 LIBS = lib_generic/libgeneric.a 194 LIBS += board/$(BOARDDIR)/lib$(BOARD).a 195 LIBS += cpu/$(CPU)/lib$(CPU).a 197 LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a 199 LIBS += lib_$(ARCH)/lib$(ARCH).a # 加载几个板级参数相关的变量值. 特别注意 start.o 这个文件, 是整个u-boot最先运行的文件 239 ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND) 241 all: $(ALL) # `make` 指令的入口就在这里. 可以根据 ALL 目标后面的依赖开始看这个 make 的过程 262 $(obj)u-boot: depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT) 263 UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\ 264 cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \ 265 --start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \ 266 -Map u-boot.map -o u-boot # 比较重要的一个指令. 可以运行 `make`, 在最后会看到这条指令的展开式, 用这种倒推的方式比较方便. 其展开如下: make[1]: Leaving directory '/home/draapho/jz2440/uboot/u-boot-1.1.6/common' # 这句不重要, 但这句的位置很明显, 所以写在这里了. UNDEF_SYM=`arm-linux-objdump -x 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 |sed -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\ # 整个语句有点复杂, 用了一系列管道指令, 将最终结果赋值给 UNDEF_SYM 这么一个变量 # 对 "UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\" 的展开 # 其中 "board/100ask24x0/lib100ask24x0.a cpu/arm920t/libarm920t.a cpu/arm920t/s3c24x0/libs3c24x0.a lib_arm/libarm.a" 就是对 $(LIBS) 的展开 cd /home/draapho/jz2440/uboot/u-boot-1.1.6 && # "cd $(LNDIR) &&" 的展开, 进入 u-boot-1.1.6 目录. arm-linux-ld -Bstatic -T /home/draapho/jz2440/uboot/u-boot-1.1.6/board/100ask24x0/u-boot.lds -Ttext 0x33F80000 $UNDEF_SYM cpu/arm920t/start.o \ # "$(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \" 的展开 # $(LD) 就是 arm-linux-ld 链接指令. 其中 "LD = $(CROSS_COMPILE)ld", 定义在 "./config.mk", $(CROSS_COMPILE) 在 Makefile 下面. # $(LDFLAGS) 给出了链接指令的参数, 定义在 "./config.mk", 形式为 "LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)" # 根据 "cpu/arm920t/start.o", 可以知道 start.s 的文件位置, 便于以后查看. (由u-boot.lds可知, 这是u-boot第一个运行的代码段) # !!! 其中 $(LDSCRIPT) 和 $(TEXT_BASE) 很重要 !!! # "LDSCRIPT := $(TOPDIR)/board/$(BOARDDIR)/u-boot.lds", 定义在 "./config.mk". 可以查看链接脚本 # "TEXT_BASE = 0x33F80000", 定义在 "./board/100ask24x0/config.mk". 这个参数明显是板级相关的. 也可以使用 0x33F80000 来搜索. --start-group 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 --end-group -L /usr/local/gcc-3.4.5-glibc-2.3.6/bin/../lib/gcc/arm-linux/3.4.5 -lgcc \ -Map u-boot.map -o u-boot # 依旧属于 arm-linux-ld 的指令, 这里就是对 "$(__LIBS)" 和 "$(PLATFORM_LIBS)" 的展开, 忽略这一段, 对理解没有影响.
‘u-boot.lds’ 链接脚本分析 根据对 Makefile 的分析, 可以知道uboot代码的偏移地址被设置成了 -Ttext 0x33F80000
这么一个值. 其含义就是, 给u-boot的代码段分配的空间位于SDRAM最顶部的512K. jz2440使用的SDRAM大小为 64M, 即 0x400_0000, 预留512K (0x8_0000)给u-boot代码, 得到地址 0x3F8_0000. 因为 s3c24x0 给SDRAM分配的地址是从 0x3000_0000 开始的, 所以有了 0x33F8_0000 这么一个值.
链接脚本 ./board/100ask24x0/u-boot.lds
也很重要. 从中可以知道u-boot整个代码段的分配情况. 下面来分析一下: 链接脚本的作用就是安排目标文件在可执行文件中的顺序, 便于链接器生成最终的可执行文件.
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") /*OUTPUT_FORMAT("elf32-arm", "elf32-arm", "elf32-arm")*/ OUTPUT_ARCH(arm) ENTRY(_start) SECTIONS { // . 表示当前位置, 设置当前位置为 0. 实际物理地址需要加上偏移量 0x33F80000 . = 0x00000000; . = ALIGN(4); // 4字节对齐 .text : // 代码段 { cpu/arm920t/start.o (.text) // 第一段代码放 start.s board/100ask24x0/boot_init.o (.text) // 第二段代码放 boot_init.c (非必须) *(.text) // 其它的代码段 } . = ALIGN(4); .rodata : { *(.rodata) } // 只读数据段, RO段 . = ALIGN(4); .data : { *(.data) } // 数据段, RW段 . = ALIGN(4); .got : { *(.got) } // uboot自定义, 非标准段 . = .; __u_boot_cmd_start = .; // 赋值 __u_boot_cmd_start, 命令段起始位置 .u_boot_cmd : { *(.u_boot_cmd) }// uboot 命令段, uboot通过宏定义, 将命令放在该段 __u_boot_cmd_end = .; // 赋值 __u_boot_cmd_end, 命令段结束位置 . = ALIGN(4); __bss_start = .; // 赋值 __bss_start .bss : { *(.bss) } // bss 段 _end = .; // 赋值 _end }
原创于 DRA&PHO