u-boot-2010.03分析(一)

本想直接学习驱动的,结果发现对于驱动的启动有很多困惑,包括,uboot怎么编译生成,怎么启动,怎么加载内核,内核是怎么把驱动放进去的。因此有了下面的uboot系列笔记,用于记录自己的解惑过程

这篇笔记包括如下内容:
第一部分是,u-boot.bin是怎么生成的。
第二部分是,u-boot.bin需要修改哪些关键代码才能支撑一个新平台。
第三部分是,解析源码看看,整个启动过程

u-boot-2010.03分析(一)

一.u-boot.bin 的生成过程

本文档里面使用的源码,来自于友善之臂对mini2440的移植,u-boot-2010-03。

本节内容:

  1. 通过对Makefile的逆向分析,查看u-boot.bin的生成过程。
  2. 根据生成过程,快速定位关键代码。

要想查看一个工程的所有Makefile文件,对于初次接触者来说,似乎太过困难。一般的方法是通过阅读工程提供的说明性文档,或者,前人的分析文档,获得。

此处使用Makefile的调试手段,查看u-boot.bin的生成过程。

进入工程目录,使用如下的命令:

make -n --debug=basic

上面的命令,会将目标的依赖以及执行的命令,打印出来。但是并不会执行真正的命令。

这些make选项,请参考前面,关于makefile的系列文章。

在未来将会有Android 系统的分析,其中对makefile的分析会更加详细,也会引入make的调试工具,用来确定,具体的make流程。

不过对于u-boot的make,使用上面的命令即可。

当运行完上面的命令,会在终端得到想要的结果。查看时倒着来。举例如下:

   Prerequisite 'u-boot' is newer than target 'u-boot.srec'.
  Must remake target 'u-boot.srec'.
arm-linux-objcopy -O srec u-boot u-boot.srec
  Successfully remade target file 'u-boot.srec'.
   Prerequisite 'u-boot' is newer than target 'u-boot.bin'.
  Must remake target 'u-boot.bin'.
arm-linux-objcopy --gap-fill=0xff -O binary u-boot u-boot.bin
  Successfully remade target file 'u-boot.bin'.
   Prerequisite 'u-boot' is newer than target 'System.map'.
  Must remake target 'System.map'.
arm-linux-nm u-boot | grep -v '\(compiled\)\|\(\.o$\)\|\( [aUw] \)\|\(\.\.ng$\)\|\(LASH[RL]DI\)' | LC_ALL=C sort > System.map
  Successfully remade target file 'System.map'.

上面打印了依赖u-boot的目标,有:u-boot.srec,u-boot.bin,System.map.
可以得出结论1:u-boot.bin由u-boot生成,生成的命令为:arm-linux-objcopy --gap-fill=0xff -O binary u-boot u-boot.bin

接下来查找u-boot的生成,继续倒着看。

原文如下:

Must remake target 'u-boot'.
UNDEF_SYM=
...因为太长此处省略
...
... -lgcc -Map u-boot.map -o u-boot
    Successfully remade target file 'u-boot'.

可以得出如下结论:
结论2:u-boot由很多的.a文件以及u-boot.lds文件生成,生成的命令使用arm-linux-ld,具体见终端输出。(因为太长,省略不写)

接下来查看u-boot.lds文件和各个.a文件的生成。

  1. u-boot.lds是直接由源码提供的,编译连接脚本,定义u-boot.bin的最终内存布局。lds脚本的写法,可以参考前面armlink系列文章。

  2. 对于各个.a文件的生成,方法同上,使用

make -n选项

终端输出中,会发现如下原文:

for dir in tools examples/standalone examples/api cpu/arm920t /media/wanbiao/disk1t/androidstudioproject/u-boot-2010.03/cpu/arm920t/ ; do \
		make -C $dir _depend ; done

原来会在上面的目录中,遍历其下的makefile。对于目录遍历的标准写法,以及注意事项。可以参考,前面的Makefile系列文章第二篇里面的:合理使用伪目标,提高编译效率。

至此,可以知道u-boot.bin的生成过程为:

  1. 首先遍历tools examples/standalone examples/api cpu/arm920t /media/wanbiao/disk1t/androidstudioproject/u-boot-2010.03/cpu/arm920t/这些目录,调用arm-linux-gcc生成各个.a文件;
  2. 然后配合u-boot.lds,链接生成u-boot中间文件。
  3. 最后使用arm-linux-objcopy,生成uboot.bin

对于上面每个步骤使用的命令,如arm-linux-gcc,arm-linux-ld,arm-linux-objcopy.不作介绍,因为它的用法跟,gcc,ld,objcopy几乎类似。而这些命令的使用,可直接google到。

至于编译过程中,生成的其他文件,此处不是我需要关心的地方。我的目标在于u-boot.bin如何生成的。现在知道了u-boot.bin的生成过程了。接下来是定位,各个.a文件,由那些源文件(.c,.S)生成。

二.u-boot.bin移植的关键代码是什么

对于uboot源代码来说,里面包含了各种硬件平台的源码,那么,怎么才能知道,哪些源文件被编译使用了呢?正常情况下,直接在上面列出的目录下,找到各自的Makefile文件,Readme文件,以及一些其他说明文件。但是这样往往需要花费大量的时间和精力。

为了节省时间和精力,这就需要根据目的来判断了,依据目的来找到为这些目的服务的源文件,然后进行分析。剩下的源文件,几乎可以使用同样的方法来进行分析。最后在将各个目的结合起来,几乎就是整个工程的源码文件了。

此处我的目的包括,u-boot是怎么启动?那么就以此为目的,找到对应的源文件有哪些。而uboot的启动却跟硬件平台相关,全部浏览完一个平台所有硬件,几乎也是耗时耗力的。同样的我再次引入一个目的为切入点,来查找对应的源代码。这个目的就是——将开发板提供的uboot源码,自己尝试移植一下。

移植之前先分析别人移植好的代码,然后尝试自己移植。

常见的方法是:

  1. 阅读文档,根据文档的指示,来修改需要的部分。
  2. 有人教你。

这里,我假定,没有任何文档可供参考,此时,该如何,知道这些源文件在哪里?

我将要使用的方法:

  1. 在常见的编程中,程序员为了便于维护,会定义各种有意义的名字,这样查看源码的人,会非常容易理解作者的思路。我就以此为基础,进行定位。当然,除了命名以外,还有很多其他的规则,比如各种设计模式的使用,常见的编程风格,注释,大小写等等。此为后话,在此不表。
  2. u-boot的生成,使用的是arm-linux-ld。那么就可以借助链接程序的调试手段,进行定位关键代码。链接程序的主要任务就是组织输入文件,将其按照合理的内存分布,组织成输出文件。那么查看其输出文件的内存映射,就能够非常快速的定位到关键的位置。

现在,从u-boot的生成来看看有没有什么引起注意的关键字。u-boot的生成如下。

arm-linux-ld -Bstatic -T u-boot.lds  -Ttext 0x33F80000 $UNDEF_SYM cpu/arm920t/start.o --start-group \
lib_generic/libgeneric.a \
lib_generic/lzma/liblzma.a \
lib_generic/lzo/liblzo.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 \
fs/yaffs2/libyaffs2.a \
fs/ubifs/libubifs.a \
net/libnet.a \
disk/libdisk.a \
drivers/bios_emulator/libatibiosemu.a \
drivers/block/libblock.a \
drivers/dma/libdma.a \
drivers/fpga/libfpga.a \
drivers/gpio/libgpio.a \
drivers/hwmon/libhwmon.a \
drivers/i2c/libi2c.a \
drivers/input/libinput.a \
drivers/misc/libmisc.a \
drivers/mmc/libmmc.a \
drivers/mtd/libmtd.a \
drivers/mtd/nand/libnand.a \
drivers/mtd/onenand/libonenand.a \
drivers/mtd/ubi/libubi.a \
drivers/mtd/spi/libspi_flash.a \
drivers/net/libnet.a \
drivers/net/phy/libphy.a \
drivers/pci/libpci.a \
drivers/pcmcia/libpcmcia.a \
drivers/power/libpower.a \
drivers/spi/libspi.a \
drivers/rtc/librtc.a \
drivers/serial/libserial.a \
drivers/twserial/libtws.a \
drivers/usb/gadget/libusb_gadget.a \
drivers/usb/host/libusb_host.a \
drivers/usb/musb/libusb_musb.a \
drivers/usb/slave/libusb_slave.a \
drivers/usb/phy/libusb_phy.a \
drivers/video/libvideo.a \
drivers/watchdog/libwatchdog.a \
common/libcommon.a \
libfdt/libfdt.a \
api/libapi.a \
post/libpost.a \
board/embedclub/smdk2440a/libsmdk2440a.a \
--end-group -L /home/wanbiao/arm-tools/gcc-3.4.5-glibc-2.3.6/arm-linux/bin/../lib/gcc/arm-linux/3.4.5 -lgcc -Map u-boot.map -o u-boot

从上面的关键字,几乎可以肯定的是:start.o,libarm920t.a,libs3c24x0.a,libsmdk2440a.a这四个文件有关。看到此处,真是太好了,原来移植一个u-boot只需要生成四个不同的文件即可。

这个只是从关键字来推测关键代码,那么具体是不是这四个文件,需要进一步确认。接下来使用方法二中提到u-boot.map文件,进行推测

u-boot.map文件的简单说明:

  1. 开头部分为:归档文件的某个成员,被谁使用
  2. 中间部分为:具体的内存映射。
  3. 结尾部分为:调试信息

我们要关注的是内存映射部分,它包括.text,.rodata,.data,.bss节。

u-boot是soc加载的第一个代码。那么芯片从哪个地址开始加载呢?

我选用的平台是友善之臂mini2440。cpu为三星s3c2440,根据前面的arm汇编系列文章可知。它从0x00000000开始运行。

在开始地址处的前面的一部分文件都应属于关键文件。到底这个“一部分”该如何界定,也没有明文规定,此处再次查看有意义的名字。只要我们认为是有意义的,就可以认定为关键文件。

那么查看内存映射时,就直接从0x00000000开始查看即可。原文如下:

Linker script and memory map

                0x00000000                . = 0x0
                0x00000000                . = ALIGN (0x4)

.text           0x33f80000    0x304c8
 cpu/arm920t/start.o(.text)
 .text          0x33f80000      0x500 cpu/arm920t/start.o
                0x33f80050                IRQ_STACK_START
                0x33f80048                _bss_start
                0x33f8004c                _bss_end
                0x33f80044                _armboot_start
                0x33f80000                _start
                0x33f80054                FIQ_STACK_START
 board/embedclub/smdk2440a/lowlevel_init.o(.text)
 .text          0x33f80500       0x64 board/embedclub/smdk2440a/lowlevel_init.o
                0x33f80504                lowlevel_init
 board/embedclub/smdk2440a/nand_read.o(.text)
 .text          0x33f80564      0x360 board/embedclub/smdk2440a/nand_read.o
                0x33f80658                nand_read_ll

从上面可知,关键文件有三:

  1. cpu/arm920t/start.o;
  2. board/embedclub/smdk2440a/lowlevel_init.o;
  3. board/embedclub/smdk2440a/nand_read.o;

接下来确定,上面三个关键文件被打包成哪一个归档文件。

进入board/embedclub/smdk2440a/文件夹,查看底下的Makefile文件。部分原文如下:

LIB	= $(obj)lib$(BOARD).a

#COBJS	:= sbc2410x.o flash.o
COBJS	:= nand_read.o smdk2440a.o flash.o
SOBJS	:= lowlevel_init.o

SRCS	:= $(SOBJS:.o=.S) $(COBJS:.o=.c)
OBJS	:= $(addprefix $(obj),$(COBJS))
SOBJS	:= $(addprefix $(obj),$(SOBJS))

$(LIB):	$(obj).depend $(OBJS) $(SOBJS)
	$(AR) $(ARFLAGS) $@ $(OBJS) $(SOBJS)

上面可知,COBJS代表c语言写的,SOBJS代表汇编语言写的。将其加上前缀,然后赋值给OBJS和SOBJS。最后依OBJS,SOBJS代表的源文件编译生成$(LIB).而此处,$(LIB)的值就为:libsmdk2440a.a

到此,再次,确认了libsmdk2440a.a为关键文件。

那么,到此时可以这样认为,关键文件为四个,分别为:

  1. cpu/arm920t/start.o
  2. cpu/arm920t/libarm920t.a
  3. cpu/arm920t/s3c24x0/libs3c24x0.a
  4. board/embedclub/smdk2440a/libsmdk2440a.a

注意,此处的start.o是直接参与编译的,并没有生成任何包之后,再参与编译。

接下来开始定位,这些文件分别由什么源文件生成。

依然使用makefile的debug选项。查看具体的依赖,选项如下:

make -n --debug=basic

对于start.o,debug信息如下:

Must remake target 'start.o'.
arm-linux-gcc   ....   -o start.o start.S -c
  Successfully remade target file 'start.o'.

哦原来是,start.S文件。

接下来确定libarm920t.a由谁生成。
debug信息如下

Must remake target 'cpu.o'.
arm-linux-gcc  ...   -o cpu.o cpu.c -c
    Successfully remade target file 'cpu.o'.
     ...
    Must remake target 'interrupts.o'.
arm-linux-gcc  ...   -o interrupts.o interrupts.c -c
    Successfully remade target file 'interrupts.o'.

  Must remake target 'libarm920t.a'.
arm-linux-ar crv libarm920t.a cpu.o interrupts.o
  Successfully remade target file 'libarm920t.a'.


原来由:cpu.o interrupts.o生成,而他们分别由cpu.c和interrupts.c生成。

接下来确定libs3c24x0.a,debug信息如下:

...
  Must remake target 'libs3c24x0.a'.
arm-linux-ar crv libs3c24x0.a interrupts.o speed.o timer.o usb.o usb_ohci.o mmc.o
  Successfully remade target file 'libs3c24x0.a'.

libs3c24x0.a由,interrupts.o speed.o timer.o usb.o usb_ohci.o mmc.o生成,而这些.o文件分别由interrupts.c speed.c timer.c usb.c usb_ohci.c mmc.c文件生成。

接下来确定libsmdk2440a.a,根据debug信息,可以得出由下面的源文件生成:
lowlevel_init.S,flash.c,smdk2440a.c,nand_read.c。

总结如下:

  1. start.o
    1.1 start.S
  2. libarm920t.a
    2.1 cpu.c和interrupts.c
  3. libs3c24x0.a
    3.1 interrupts.c speed.c timer.c usb.c usb_ohci.c mmc.c
  4. libsmdk2440a.a
    4.1 lowlevel_init.S,flash.c,smdk2440a.c,nand_read.c

现在,我们可以大胆假设,如果需要做移植,只需要修改上面列出的文件即可。总共需要13个文件。至于是不是这13个文件,那么改改试一下,就知道了。

接下来就是从官网上面下载一份uboot源码,然后进行移植了。为了降低难度,(主要是为了弄懂uboot怎么编译生成,怎么启动,怎么加载内核,并不是为了移植u-boot),选择u-boot-2010-03源码,进行移植,让其能够在mini2440开发板上面跑起来。

你可能感兴趣的:(uboot,uboot,uboot-makefile,uboot启动,uboot查找源文件,uboot.lds)