U-boot官方2013.10版本移植流程

来源:互联网 发布:cdn网络 多线程 编辑:程序博客网 时间:2024/04/25 13:55

今天开始移植U-boot的官方2013.10版本,做一下笔记,以备日后所需
移植的时候有一点感想,就是最好别注释掉不对的源码,定义的地方千万别动,尽量修改调用的地方,这样可以极大的避免出错;

0.取得合适版本的源码

  • 版本不是越新越好,因为越新越臃肿,所以选择的版本只要含有我们板子上的cpu相关的代码即可。此外U-boot的2013版本之后,其配置体系发生了极大的变化,它引入了kernel的配置体系(Kbuild、Kconfig、menuconfig),从而可以在图形界面下配置U-boot。虽然配置体系发生了变化,但是U-boot的代码本身没有太大的变化,在此我们选用2013.10版本进行移植
  • 取得源码后,如果运气好,里面会有和我们SOC相关的代码(可以在board文件夹下的厂商文件夹下找找名字)。但是一般情况下,厂商会把名字取的很晦涩,怎么寻找是一个问题。以三星的s5pv210为例,进入board/samsung下,发现根本找不到s5pv210这个名字,然后我们再去include目录下找找头文件,也挺难找的,不过最后还是在s5p_goni.h中找到了s5pc110的宏定义,由此可确认include/configs/s5p_goni.h,对应的board在uboot/board/samsung/goni

1.检查配置和编译部分

  • 上手检查主Makefile,发现前面大部分相同,可我们当make s5p_goni_config配置时需要的伪目标 s5p_goni_config在主Makefile中不存在!其实取而代之的是这样的一个伪目标:
    这里写图片描述
    目标行首的%代表是通配符,当我们输入任何xxx_config时,都会到这里执行此伪目标;操作行首的@代表静默执行,MKCONFIG这个变量代表的是源码目录下最关键的一个shell文件即mkconfig,这个shell文件负责了make之前的配置过程,这段代码在执行$(MKCONFIG)前还把-A 、$(@:_config=)这2个参数传给了mkconfig,其中参数$(@:_config=)是引用了一个替换函数,将该规则中的目标(用@表示)中的_config用空替换,故 $(@:_config=)的值为s5p_goni
  • 还有一个疑问,传这两个参数明显不够啊…以前的SOC、架构、板级等参数去哪里了?好了,那么到mkconfig脚本中再去检查检查,发现mkconfig开头的参数解析这里做了些手脚:
    这里写图片描述
    这堆代码比较复杂,还涉及到了正则表达式,我们不用管它的具体实现,只需知道它功能是根据输入的2个参数(其中有个是s5p_goni),去boards.cfg中搜索,找到和s5p_goni有关系的一行(其实就是以前的SOC、架构、板级等参数)
    这里写图片描述
    然后把那一行传给变量line,后面再将line中的内容以空格为间隔分别赋值给$1$2……$8,如此一来,mkconfig脚本中就有了SOC、架构、板级等参数。
    其实这一块的本质就是把以前的版本主Makefile中各种开发板的配置伪目标抽离出来,然后写到了一个独立文件boards.cfg中
  • 配置部分貌似没问题,接下来检查编译工具链,根据 可知U-boot配置及编译阶段流程宏观分析,交叉编译工具链配置在主Makefile的前端,检查后发现居然是空的
    这里写图片描述
    赶紧把我们安装的工具链的名称前缀加上去,CROSS_COMPILE = /usr/local/arm/arm-2009q3/bin/arm-none-linux-gnueabi-
  • 检查完编译和配置部分之后,输入make distclean 、 make s5p_goni_config 、 make 发现成功生成了u-boot.bin

2.解决烧录问题

  • 官方U-boot并没有sd卡的烧录工具,这个略坑,需要我们自己移植。在此,我们尝试移植三星原厂210的U-boot的烧录工具,先直接复制到目录下,然后直接使用sd_fusing脚本烧录
  • 上电后发现现象(左图),看似没什么,但其实里面有重要信息
    这里写图片描述这里写图片描述
    我们来对比一下未插SD卡的情况(右图),第一句error说的是inand,不用管,但是第二句有差别,我们这里显示的仍然是校验和失败,而未插卡情况则是SD卡识别识别。由此,我们可以断定,问题出在U-boot头部的校验和

  • 那哪个文件有问题呢?分析可知,烧录程序sd_fusing.sh中调用了mkbl1(即计算并填充校验和的程序),mkbl1的运行不依赖于其他外界环境,只需上层目录有一个u-boot.bin即可。也就是说,即便u-boot.bin的内容是垃圾,mkbl1也能计算并填充正确的校验和。那问题出在哪里?尝试把错误的u-boot.bin放到之前原厂代码的目录下,用原厂的环境去烧录,发现还是不行,这就说明了问题出在u-boot.bin上。我们使用查看二进制的软件打开u-boot.bin对比一下原厂和官方的,我们发现有一个细微的差别,官方的start.S头部没有留16字节的空白….终于找到了问题,打开start.S,发现头部直接是异常向量表,我们直接复制原厂头部的16字节占位过去
    这里写图片描述
    其实U-boot官方这么做还是可以理解的,因为start.S是所有armv7架构的cpu共用的,它为了兼容性不可能考虑到每一款SOC的细微特征(比如210的sd启动需要头校验)

  • 重新烧录运行,发现虽然没打印“OK”,但是烧录成功了:
    这里写图片描述

3.解决串口(供电锁存、时钟)问题

  • 由于上一步未打印“OK”,可能有两种情况,1.源码中本来就没有打印“OK”2.程序在串口初始化之前挂掉了
  • 先检查start.S,看看start.S的前半部分,发现全部是和架构有关的初始化(cache、cp15等)应该是不会有问题的,从bl cpu_init_crit开始是和SOC相关的初始化(上图),cpu_init_crit是一个汇编函数,其真正内容是去执行 b lowlevel_init(下图)
    这里写图片描述这里写图片描述
  • 有一点需要注意的,官方U-boot有时会很坑,比如有多个看上去很对的lowlevel_init.S,例如armv7文件夹下面就有一个,此时千万不要弄错,我们真正要用的是goni文件夹下面的。一般来说我们通过直觉来判断采用哪一个文件,其实可靠一点的方法是通过编译来判断,比如goni文件夹下产生了lowlevel_init.o文件,而armv7下没有,那么就可以确认goni文件夹下的lowlevel_init.S被采用了。此外如果默认选择的文件是错误的,比如我们想要更换lowlevel_init.S文件,只需修改goni和armv7文件夹下的子Makefile内编译选项即可
  • 浏览了一番lowlevel_init.S,发现里面代码的可读性真是差….它大概做了这么一些事情:关看门狗、禁止中断、设置SRAM、设置串口。发现没有初始化时钟(先不管),没有设置供电锁存,串口初始化也没有打印“OK”,于是乎只能手写汇编添加了,因为这也没法移植,毕竟移植牵涉到头文件,还不如直接手写汇编
  • 手动添加供电锁存到关看门狗后面
    ldr r0, =0xE010E81C /*sjh add*/    ldr r1, =0x301    str r1, [r0]
  • 检查串口是否初始化正确(其实没多大关系,反正IROM中BL0已经初始化过串口2了….),并添加打印”O”
    ldr r1, =0x4f4f4f4f                 /*sjh add*/    ldr r2, =0xE2900820                 /*sjh add*/    str r1, [r2]                @'O'
  • 上电,发现无任何反应,连开发板锁存都失败,这说明开发板锁存之前的代码挂掉了。此时,利用利用连续点灯定位bootloader的错误这个方法开始定位错误
  • 由led的现象,我们可以发现start.S中的led闪烁一切正常,但是一旦在lowlevel_init.S中点灯,就怎么都没有反应,即便放在lowlevel_init.S的最前面,也没有用。这只能说明一点,lowlevel_init.S没有被运行,而原因很可能是链接脚本没有把它放在BL1,我们找到我们用的那个u-boot.lds(在/arch/arm/cpu),发现:
    这里写图片描述
    果然没有考虑谁应该被链接在代码段最前面。于是,我们想要添加一些重要的文件(比如lowlevel_init.o),照着格式在里面加入 :
    board/samsung/goni/lowlevel_init.o (.text*),然后编译,报错:lowlevel_init重复定义了
  • 为什么会重复定义?因为lowlevel_init这个函数被连接时连接了2次。一次是board/samsung/goni这个目录下生成libgoni.o时连接了1次,第2次是连接脚本最终在连接生成u-boot时又连接了一次,所以重复定义了。解决方法有两种:1.直接在u-boot.lds添加libgoni.o,取代lowlevel_init.o 2.不让lowlevel_init链接进libgoni.o,让它只在最终连接u-boot时链接
  • 其中,第一种方法是不可取的,因为libgoni.o太大,BL1放不下。我们只能使用第二种方法。先打开board/samsung/goni这个目录下的子Makefile:
    这里写图片描述
    首先,要明白,如果直接屏蔽 SOBJS := lowlevel_init.o这一行是不行的,因为这样 lowlevel_init.o在Makefile中就没了….lowlevel_init.S根本不会被编译成lowlevel_init.o。我们在这里采取的手法是设置一个没有用的变量LOW,其值为lowlevel_init.o,并且让LOW成为目标all的依赖,这样lowlevel_init.o就会被编译而又不会被链接进libgoni.o了。修改如下图:
    这里写图片描述
    上电,发现供电锁存和串口终于成功了

4.移植DDR初始化代码

  • 继续往后看源码,发现居然没有DDR的初始化…但是却有重定位…这样只能移植DDR的初始化代码了。此时,这里的思路是移植现成的DDR初始化代码,通常可以从网络获得的,如果网上没有,那只能手写了…..在此我们用三星原厂的代码做演示,同时也演示了如何自由的向U-boot中添加文件
  • 首先将原厂中cpu/s5pc11x/s5pc110下的cpu_init.S复制到当前目录下board/samsung/goni 之所以移到goni文件夹下,是因为上一节已经演示过了通过修改子Makefile和u-boot.lds来自由控制编译和链接,现在只需如法炮制即可。
  • 具体操作:由于上一节已经对子Makefile做过一些操作,我们只需在LOW中添加cpu_init.o,即
    LOW := lowlevel_init.o cpu_init.o

    然后在u-boot.lds中的代码段加入:board/samsung/goni/cpu_init.o (.text*)
    由于cpu_init.S内有很多寄存器的宏,所以我们还要把cpu_init.S需要的s5pc110.h拿过来,直接丢到include目录下即可。还有注意一下,有些宏不仅仅在s5pc110.h中,故我们可以把它们复制到s5pc110.h里面
  • 我们来修改一下cpu_init.S的内部代码,里面有很多条件编译,可以直接删除条件编译,也可以手动添加条件编译需要的宏,在此我们将其条件编译删除(算是手工预编译…)。然后再来修剪一下s5pc110.h,里面也有很多依赖的宏,不用的地方也要注释掉
  • 然后再修改s5p_goni.h中和DDR有关的宏,修改为
    这里写图片描述
  • 移植完后别忘了再start.S内调用,调用完后再打印一个’K’,以确保初始化没挂掉。最后我们编译烧录,发现显示了“OK”,应该是移植成功了,但是具体DDR能不能用还是要靠重定位

5.移植重定位

  • 官方U-boot内部的重定位有很多问题,而且是armv7公用的,和SOC无关。这显然不能用,还是要靠我们自己移植
  • 不要删除官方原本的重定位和设置栈的代码,而是修改调用他们的地方,令它们不被调用
  • 先添加设置栈到DDR的代码,从原厂代码那边抄过来,注意要将链接地址改为CONFIG_SYS_TEXT_BASE,同理,再复制清bss段的代码
  • 最后将原厂的movi.c、movi.h搞过来,里面的代码复制具体的重定位,具体操作和上一节移植DDR相同,移植完后上电,发现:
    这里写图片描述
    有挺多错误的,但是至少U-boot第二阶段之前的所有初始化都正确了

6.移植时钟初始化

  • 由U-boot打印信息可知,时钟在BL0中被初始化为400Mhz而不是1Ghz,由于默认源码内没有初始化时钟,所以需要手动移植原厂代码,方法同移植DDR

7.(可选)修改U-boot的打印信息

  • 由之前U-boot打印信息可知,很多信息都是错的,这里可以选择将其修改,具体各个函数在U-boot初始化第二阶段的init_sequence数组中

8.检查mmc配置

  • 由之前U-boot打印信息可知,mmc的初始化是失败的,首先屏蔽onenand相关的代码,发现mmc初始化报错
  • 通过在源码内查询打印信息,可以定位是mmc初始化的驱动出了问题,解决方案有两种:1.参考现成可用的驱动修改当前的驱动 2.将现成可用的驱动移植过来。第二种方法比较现实一点

9.检查环境变量配置

  • 首先,在U-boot命令行下尝试环境变量的设置,设置后发现能断电保存,说明环境变量功能成功,但是我们还是要检查一下,环境变量存放的扇区是否安全,会不会和其他内容发生冲突
  • 关于环境变量分区的检查,以及默认环境变量的修改详见
    U-boot对启动介质的空间分配

10.移植网卡驱动

  • 在U-boot命令行下尝试ping主机,发现没有这个命令….可知官方U-boot默认没有网络命令的支持….
  • 首先我们要让U-boot初始化网络,检查源码,发现在board.c中:
    这里写图片描述
    “ Net: ”这句信息没有被打印,说明CONFIG_CMD_NET未被定义,于是我们在配置头文件内手动添加,同时还要检查有没有#undef CONFIG_CMD_NET,一定要去除,否则我们定义了也会被这句取消
  • 然后还要使能各种网络命令,检查源码,挖掘do_ping函数,发现在Cmd_net.c中:
    这里写图片描述
    于是我们配置头文件内手动添加CONFIG_CMD_PING。同理,添加tftp相关的命令
  • 接着开始配置网卡,发现官方U-boot里没有网卡初始化的,于是乎直接从原厂代码那边移植过来即可

11.检查内核引导部分

  • 对于2013版本的U-boot,机器码不仅仅在s5p_goni.h中,还有一个专门的Mach-types.h,里面有各个cpu对应的机器码,我们也需要修改相应的部分;这个Mach-types.h其实是从kernel中学来的,便于对机器码的集中管理
0 0
原创粉丝点击