找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
广告投放联系QQ68610888
查看: 155644|回复: 240

【U-Boot】U-Boot 基本编译教程

 火... [复制链接]
本帖最后由 hackpascal 于 2014-11-15 12:09 编辑

此教程为入门教程只讲解如何编译 U-Boot,不讲解代码修改及排错等。
此教程适用于 AR 及 QCA 系列的 ar71xx 平台。
若有疑问,或看不懂,请直接回帖,以便楼主修改教程。。

准备工作:

Linux 系统:
        32 位 / 64 位均可。楼主使用的是 Ubuntu 12.10 amd64。
        64 位环境下需要安装 32 位库。如 Ubuntu 下是运行 sudo apt-get install libc6:i386 libgcc1:i38 libstdc++5:i386 libstdc++6:i386

基础编译环境:
        一般来说每个发行版的 Linux 系统都有包管理器。
        例如在 Ubuntu 的 Shell 里运行 sudo apt-get install build-essential 就可安装好编译环境。
        其他的系统请自行百度。

基本的 Linux Shell 基础:
        这个只能自己想办法解决了。


入门级:

        入门级使用现成的工具链和编译脚本,使用最少的命令完成代码编译。
        使用 TL-MR3420 v1 的代码进行讲解。

        1. 下载源代码
                打开 http://www.tp-link.com/en/support/gpl/,在左侧选择 3G/4G Routers,然后在列表中选择 TL-MR3420 V1 的代码进行下载。




        2. 解压代码
                这个就随便了,一般解压在主目录。
                将下载的 mr3420_3220v1.tar.gz 移动到主目录。
                注意:这里压缩包扩展名有误,实际上是 bzip2 压缩的。
                打开终端,此时终端应该默认在主目录下,若不在主目录下,就运行 cd ~ 命令进入主目录。
                运行命令 tar -jxvf mr3420_3220v1.tar.gz 以解压代码。




        3. 编译代码

                运行命令 cd mr3420_3220v1/build 进入代码编译目录。
                运行命令 make BOARD_TYPE=ap99 fs_prep 以创建编译过程所需的目录。
                运行命令 make BOARD_TYPE=ap99 uboot 以开始编译 U-Boot。

        4. 刷机测试
                如果没有意外,那么编译结束后的输出应该是这样的:




                生成的 u-boot.bin 位于 mr3420_3220v1/images/ap99/ 目录下。
                此文件未被填充到 128K,需要手动填充。
                填充后的文件就可以进行刷机测试了。
                此文件是未压缩版,因此本身体积很大。此代码也不能生成压缩版的 U-Boot。

        5. 说明
                操作说明请参阅代码根目录下的 readme.txt。
                入门级的操作就这么简单{:soso_e120:}。


进阶级:

        进阶级依然使用 TL-MR3420 v1 的代码。
        进阶级将分离 U-Boot 代码及现成的工具链,搭建基本的交叉编译环境。

        1. 分离 U-Boot 代码
                U-Boot 代码位于 mr3420_3220v1/ap99/boot/u-boot 。
                将整个 u-boot 目录复制到一个地方,如用户主目录。
                此 u-boot 目录里的代码就是全部所需的代码了。之后的所有操作都在这里面完成。



        2. 复制工具链
                此工具链即为编译器,但是是针对 mips 架构的交叉编译器。
                工具链位于 mr3420_3220v1/build/gcc-3.4.4-2.16.1/build_mips。
                将整个 build_mips 复制到一个地方,如主目录。

        3. 搭建编译环境
                现在,mr3420_3220v1 目录已经没有用,可以删掉,因为所有需要的文件都已复制出来了。
                设置环境变量以让 Shell 能够访问到工具链。运行命令 export PATH=$PATH:~/build_mips/bin 即可。
                        此设置只在当前的 Shell 里有效。关于如何设置永久的环境变量,请自行百度。

        4. 清理代码
                此操作用于清理上次编译产生的临时文件及配置文件,为下次配置及编译做准备。
                进入 u-boot 目录:运行命令 cd ~/u-boot
                运行命令 make distclean 以执行完全清理。

        5. 编译代码
                这里是一个完整的编译步骤。

                a. 指定开发板类型
                        此操作表示我们要编译哪个开发板。AR7241 的开发板(这里其实叫参考板,reference board)名称为 ap99,因此运行 make ap99_config 以进行配置。
                        所有的开发板定义都写在 Makefile 里。其中以 ***_config: 开头的行,*** 就是开发板名称。
                        AR 及 QCA 系列的参考板名称会在最后列出。
                        有些开发板还有额外的参数可以定义,这里暂不讲解。




                b. 编译
                        运行命令 make CROSS_COMPILE=mips-linux- 进行编译。
                        CROSS_COMPILE=mips-linux- 指定了交叉编译器的文件名前缀为 mips-linux-。例如,要使用工具链中的 gcc 程序,那么就运行 mips-linux-gcc。这个可以看一下 build_mips/bin 目录下的文件名。
                        由于之前运行了 export PATH=$PATH:~/build_mips/bin 命令,所以 Shell 能够直接找到相关程序的位置。否则,CROSS_COMPILE= 需要指定为 ~/build_mips/bin/mips-linux-。

                c. 测试
                        编译成功后,u-boot 目录下会生成 u-boot.bin,这就是未填充到 128K 的文件。

        6. 常用编译命令。
                make ***_config    配置生成为指定的开发板(*** 为开发板名称)。
                make CROSS_COMPILE=***    编译代码,并指定交叉编译器文件名前缀为 ***。
                make clean    清理上次编译生成的临时文件。此命令不会删除开发板配置,下次编译可以直接运行 make 命令。
                make distclean    清理编译生成的临时文件及开发板配置。运行此命令后,需要再次指定开发板后才能进行编译。


其它说明:

        这里使用的 MR3420 v1 代码不能生成压缩代码。其他的,如 AR9331 AR934x 等代码需要在编译时加入 COMPRESSED_UBOOT=1 来进行编译,如 make CROSS_COMPILE=mips-linux- COMPRESSED_UBOOT=1

        编译 AR934x QCA95xx 的代码需要指定使用的以太网交换机 PHY 类型,即在编译时加入 ETH_CONFIG=_s27 或 ETH_CONFIG=_s17 。其中 _s27 为内置百兆 PHY,_s17 为外部千兆 PHY。

        具体路由型号的 GPL 代码里都有说明文件,可以根据说明文件来进行编译,并了解参考板名称。


常见参考板名称:

        AR7240   -   ap91
        AR7241   -   ap99
        AR9331   -   ap121
        AR9341   -   ap123
        AR9342   -   mi124
        AR9344   -   db12x
        QCA953x   -   ap143
        QCA9558   -   ap135

        其它的请在 https://wikidevi.com/wiki/Atheroshttps://wikidevi.com/wiki/Qualcomm_Atheros 上进行查找。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×

点评

你说的很对。: 5.0 我赞同你的说法。: 5.0
你说的很对。: 5 我赞同你的说法。: 5
爱上你了  发表于 2018-4-21 00:52
不顶你我都过意不去,激动的都打错字了  发表于 2014-9-7 08:12
我顶你我都觉得过意不去!  发表于 2014-9-7 08:12
论坛有您更光彩!  发表于 2014-5-23 23:47
楼主你太强大了,授人鱼不如授人渔,不过你那个不死uboot是不是做好了就不会更新了比如ar9344千兆那个uboot还会更新吗?  发表于 2014-5-4 12:00
好样的。你做得很对!更多的朋友想看你分享源代码的基本修改方法  发表于 2014-5-2 13:31
膜拜中.....  发表于 2014-5-1 11:07
楼主文武双全,文能写代码,武能修主板!佩服!!佩服!!!  发表于 2014-5-1 10:56

评分

参与人数 44恩山币 +63 收起 理由
Mar*** + 1 强大的恩山!(以下重复1万次)
ls*** + 1 我来恩山就是为了撩你!
reged*** + 1 亲,你太牛逼了,走,我请你吃鳇鱼头去~~~
a707*** + 1 我来恩山就是为了看你!
缘下*** + 1 亲,你这么厉害,我想做你的经纪人!
随风*** + 1 一看就是觉得高端、大气、上档次!
touc*** + 1 我来恩山就是为了看你!
Hugo*** + 1 土豪,我们做朋友吧!
jian*** + 1 强大的恩山!(以下重复1万次)
test*** + 3 一看就是觉得高端、大气、上档次!
hhh*** + 1 一看就是觉得高端、大气、上档次!
guzhi*** + 1 泥马,真给力,我要顶!
lizhe*** + 1 强大的恩山!(以下重复1万次)
libc*** + 1 城会玩
hzxbu*** + 1 虽然暂时还不能完全看懂,但仍觉得楼主实在是太牛13了,真是不明觉厉!
sl3*** + 1 怀揽白富美的屌丝露出了惊讶的眼神:哥,你太帅了!
pro_*** + 1 土豪,我们做朋友吧!
师*** + 1 楼主威武—我就想问问挖掘机技术哪家强?
wuzh*** + 1 土豪,我们做朋友吧!
fot*** + 1 楼主威武—我就想问问挖掘机技术哪家强?
prox*** + 3 一看就是觉得高端、大气、上档次!
sen*** + 1 怀揽白富美的屌丝露出了惊讶的眼神:哥,你太帅了!
selfi*** + 1 楼主威武—我就想问问挖掘机技术哪家强?
ho*** + 1 楼主已经被我列为中国之光~暖男
wsd*** + 3 拿去买糖吃吧!
right*** + 1 一看就是觉得高端、大气、上档次!
qzhan*** + 1 一看就是觉得高端、大气、上档次!
为侬消*** + 1 楼主好棒,我们做朋友吧
hj21*** + 1 楼主威武—我就想问问挖掘机技术哪家强?
榕*** + 1 楼主威武—我就想问问挖掘机技术哪家强?
wonde*** + 1 做人要低调 很强大~~~~~~~~~~~
sl*** + 1 强大的恩山!(以下重复1万次)
mike22*** + 1 怀揽白富美的屌丝露出了惊讶的眼神:哥,你太帅了!
pppen*** + 1 土豪,我们做朋友吧!
泽铭*** + 1 一看就是觉得高端、大气、上档次!
oywl*** + 3 什么时候更新下,期待啊。。。。。。。。
ha*** + 5 土豪,我们做朋友吧!
chen*** + 3 一看就是觉得高端、大气、上档次!
zhout*** + 2 奸商追捧的新星升起!
pass*** + 3 强大的恩山!(以下重复1万次)
ZHIZ*** + 3 一看就是觉得高端、大气、上档次!
mu*** + 1 楼主什么时候出进阶篇,想知道怎么修改出32m uboot啊
cg*** + 1 泥马,真给力,我要顶!
im*** + 1 怀揽白富美的屌丝露出了惊讶的眼神:哥,你太帅了!

查看全部评分

我的恩山、我的无线 The best wifi forum is right here.
 楼主| | 显示全部楼层
本帖最后由 hackpascal 于 2014-10-28 22:21 编辑

交叉编译工具链制作

有时候,编译 U-Boot 却没有合适的工具链,因此需要自己制作。
自己制作工具链并不复杂,而且可以根据自己的需要进行设置。

下载代码:
这里要用到的是 Buildroot,这是一个用于生成嵌入式 Linux 系统的编译环境。
说道这里,你可能会觉得,这跟 OpenWrt 的编译环境很像。事实上,OpenWrt 的编译环境正是基于 Buildroot 的。

但是为什么不使用 OpenWrt 而要使用 Buildroot 呢?
这是因为 OpenWrt 生成的工具链依赖度太高,而且缺少部分特性,容易导致编译失败。
另外,在没有任何现成工具链的时候,用 OpenWrt 制作工具链花费的时间比 Buildroot 多。

下载地址:
http://buildroot.uclibc.org/download.html
在 Download 下面选择一个下载,就行,如 buildroot-2014.08.tar.gz

配置:
解压代码,如解压到 ~/buildroot-2014.08
打开终端,进入 buildroot 目录,运行 make menuconfig 命令:



以 Atheros AR71XX 为例
AR71XX CPU 架构为 MIPS 32位,大端字节序 (Big-endian),指令集 MIPS32 Release 2 (mips32r2),不含浮点单元
Ralink 的 CPU 跟 AR71XX 基本相同,只是字节序为小端 (Little-endian)





Target options  ---> Target Architecture
选择 MIPS (big endian)

Target options  ---> Target Architecture Variant
选择 mips 32r2

Target options  ---> Use soft-float
选中

编译:
运行 make 即可开始编译。编译过程跟 OpenWrt 基本相同,即先下载代码吧,然后进行编译。
若要加快速度,可以加上多线程参数 -jN,其中 N 为 CPU 核心数
若要显示详细过程,可以加上 V=99。



获取工具链:
编译完成后,工具链位于 buildroot 目录下的 output/host/usr 下
将 usr 文件夹复制出来即可

重要提醒:
如果使用 buildroot 工具链来编译 U-Boot,需修改 U-Boot 中的一个文件,否则 U-Boot 可能会在代码重定位时卡死

cpu/mips/config.mk

在最后加上两行行
  1. PLATFORM_CPPFLAGS += -G 0 -mabicalls -fpic -msoft-float -fno-schedule-insns -fno-schedule-insns2
  2. PLATFORM_LDFLAGS       += -G 0 -static -n -nostdlib
复制代码

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×

点评

好东东。。慢慢学习一下。。。大大 能不能做个WDR5600用的 U-BOOT 呢。。ART文件专用的有吗?  详情 回复 发表于 2016-2-1 17:49
前辈,如您2楼所讲,编译过程为 make -j2 可是无奈下载工具的速度太慢,经常会卡死 若我自己下载的binutils-2.22文件,我应该怎么操作呢? make过程中的Saving to: `/home/chansane/buildroot-2014.08/output/bu  详情 回复 发表于 2014-11-19 19:38
我要来好好学习  详情 回复 发表于 2014-11-15 17:07
我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

 楼主| | 显示全部楼层
本帖最后由 hackpascal 于 2014-10-28 22:10 编辑

U-Boot 启动过程及简单代码分析

这里以 AR7241 的 U-Boot 为例

MIPS 架构简介
MIPS 地址空间:
这里说的是地址空间,不是内存空间。内存只是映射在一部分地址空间上而已。
内存分为4段 (Kuseg、Kseg0、Kseg1、Kseg2),其中 Kseg0 (0x80000000 ~ 0x9fffffff) 为缓存段,直接映射在物理地址段上。
Kseg1 (0xa0000000 ~ 0xbfffffff) 为非缓存段,直接映射在物理地址段上。
Kuseg 和 Kseg2 需要经过地址转换才能访问。
在 CPU 复位时,缓存未被初始化,只有 Kseg1 能够被直接访问。

AR71XX 物理段:
内存、Flash、IO寄存器等都被映射在物理段上
范围为 0~0x1fffffff,访问时需要通过宏 KSEG0ADDR(_addr) 来通过 Kseg0 访问或 KSEG1ADDR(_addr) 来通过 Kseg1 访问。
物理地址一般不能直接访问,都需要通过 Kseg0 或 Kseg1 来访问。

主要的映射范围:
DDR:0~0x0fffffff (这一段直接映射物理内存,一般通过带缓存的 Kseg0 来访问,以便加快速度)
I/O空间:0x10000000 ~ 0x1dffffff (这一段直接控制硬件,必须通过 Kseg1 来访问)
SPI Flash 空间:0x1f000000 ~ 0x1fffffff (这一段映射闪存的前 16MB 数据)

AR71XX 启动过程

CPU 上电时,物理段中,SPI Flash 的前4MB 被循环映射在 0x1f000000 上,因此会被重复 4 次。
执行地址为 KSEG1ADDR(0x1fc00000),即 0xbfc00000。

*注意:U-Boot 在编译时,实际设置的基址,即启动地址为 KSEG0ADDR(0x1f000000),即 0x9f000000。下面会讲到为什么要这么设置。

U-Boot 中的起始代码在 cpu/mips/start.S 中:

  1. _start:
  2. #ifndef COMPRESSED_UBOOT
  3.         RVECENT(reset,0)        /* U-boot entry point */
  4.         RVECENT(reset,1)        /* software reboot */
  5.         RVECENT(romReserved,2)
  6. ----------------------------------------------------省略----------------------------------------------------
  7.         RVECENT(romReserved,125)
  8.         RVECENT(romReserved,126)
  9.         RVECENT(romReserved,127)

  10.         /* We hope there are no more reserved vectors!
  11.          * 128 * 8 == 1024 == 0x400
  12.          * so this is address R_VEC+0x400 == 0xbfc00400
  13.          */
复制代码

以上代码设置异常向量,不需要理解
第一句 RVECENT(reset,0) 为一个跳转,直接转到下面的代码进行运行

  1.         .align 4
  2. reset:
  3.         /* set GPIO_OE  -- added by lsz to fix the LED issue 09.03.25 */
  4.         li                a1, AR7100_GPIO_OE
  5.         lw                v1, 0(a1)
  6.         li                v0, 0x3fdff        
  7.         or                v1, v1, v0
  8.         sw                v1, 0(a1)
  9.         lw                v0, 0(a1)
  10.         lw                v0, 0(a1)

  11. ----------------------------------------------------省略----------------------------------------------------

  12.         /* CONFIG0 register */
  13.         li        t0, CONF_CM_UNCACHED
  14.         mtc0        t0, CP0_CONFIG
复制代码

以上代码前半部分通过设置 GPIO 来点亮路由的 LED。后半部分设置 CPU 相关参数,不需要理解
以上代码没有包含使用相对地址的指令,也就是说,无论这段代码在 CPU 地址空间的何种位置,都能够正常执行
因此虽然 CPU 的执行地址为 0xbfc00000,以上代码依然能执行

  1.     /* Initialize GOT pointer.*/
  2.         bal     1f
  3.         nop
  4.         .word   _GLOBAL_OFFSET_TABLE_
  5.         1:
  6.         move    gp, ra
  7.         lw      t1, 0(ra)
  8.         move        gp, t1
复制代码

以上代码加载了用于 U-Boot 重定位的 GOT 指针,它是 U-Boot 在内存中进行自身移动的关键

  1.         la      t9, lowlevel_init
  2.         jalr    t9
  3.         nop
复制代码

以上代码进行 CPU 频率设置,超频就是在这里实现的

  1.         la        t0, rel_start
  2.         j        t0
  3.         nop

  4. rel_start:
复制代码

以上代码很关键,它实现了 U-Boot 的执行地址的转移
假设当前 CPU 的执行地址在 0xbfc00000 + x
那么执行此段代码后,CPU 执行地址将跳转到 0x9f000000 + x 处进行执行
前面提到了 CPU 在复位时,物理段 0x1f000000 处循环映射 4 次 Flash 的前 4MB,而 U-Boot 大小只有100KB多,
因此 0x1f000000 跟 0x1fc00000 处的数据是一样的,这就实现了运行地址的转移。
至于为什么不跳转到 0xbf000000 + x 处运行,是因为 AR71XX CPU 在复位时,缓存可用,因此通过 Kseg0 段执行,可以加快速度。

  1.         /* REMAP_DISABLE */
  2.         li        a0, AR7100_SPI_CLOCK
  3.         li        t0, 0x43
  4.         sw        t0, 0(a0)
复制代码

以上代码的作用是禁用物理段 0x1f000000 的 4MB Flash 数据循环映射,以便让 0x1f000000 能够映射 16MB 的 Flash 数据


  1.         /* Initialize caches...
  2.          */
  3.         /* 初始化缓存 */
  4.         la      t9, simple_mips_cache_reset
  5.         jalr    t9
  6.         nop

  7.         /* ... and enable them.
  8.          */
  9.         /* 启用缓存 */
  10.         li        t0, CONF_CM_CACHABLE_NONCOHERENT
  11.         mtc0        t0, CP0_CONFIG

  12. ----------------------------------------------------省略----------------------------------------------------

  13. #if defined(CONFIG_AR7100) || defined(CONFIG_AR7240)
  14.     /* 将缓存锁定在 Kseg0 低端处 */
  15.     la      t9, mips_cache_lock_24k
  16.     jalr    t9   
  17.     nop
  18. #endif
  19. #endif /* #ifndef COMPRESSED_UBOOT */
  20.         /* 将栈指针指向缓存 */
  21.         li        t0, CFG_SDRAM_BASE + CFG_INIT_SP_OFFSET
  22.         la        sp, 0(t0)

  23.         /* 进入 C 环境 */
  24.         la        t9, board_init_f
  25.         j        t9
  26.         nop
复制代码

这段代码的作用是初始化缓存,并将缓存锁定在 Kseg0 上,用作临时的栈空间
此时内存并未初始化, Kseg0 对应的 0x80000000 ~ 0x8fffffff 还不能被访问,因此需要用缓存代替。
然后跳转到 board_init_f 进行执行

下面开始在 C 环境中运行
文件:lib_mips/board.c

  1. void board_init_f(ulong bootflag)
  2. {
  3.         gd_t gd_data, *id;
  4.         bd_t *bd;
  5.         init_fnc_t **init_fnc_ptr;
  6.         ulong addr, addr_sp, len = (ulong)&uboot_end - CFG_MONITOR_BASE;
  7.         ulong *s;
  8. #ifdef COMPRESSED_UBOOT
  9.         char board_string[50];
  10. #endif
  11. #ifdef CONFIG_PURPLE
  12.         void copy_code (ulong);
  13. #endif


  14.         /* Pointer is writable since we allocated a register for it.
  15.          */
  16.         gd = &gd_data;
  17.         /* compiler optimization barrier needed for GCC >= 3.4 */
  18.         __asm__ __volatile__("": : :"memory");

  19.         memset ((void *)gd, 0, sizeof (gd_t));

  20.         /* 依次运行初始化函数 */
  21.         for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
  22.                 if ((*init_fnc_ptr)() != 0) {
  23.                         hang ();
  24.                 }
  25.         }

  26. #ifdef COMPRESSED_UBOOT
  27.         checkboard(board_string);
  28.         printf("%s\n\n",board_string);
  29.         gd->ram_size = bootflag;
  30.         puts ("DRAM:  ");
  31.         print_size (gd->ram_size, "\n");
  32. #endif

  33.         /*
  34.          * Now that we have DRAM mapped and working, we can
  35.          * relocate the code and continue running from DRAM.
  36.          */
  37.         addr = CFG_SDRAM_BASE + gd->ram_size;

  38.         /* We can reserve some RAM "on top" here.
  39.          */

  40.         /* round down to next 4 kB limit.
  41.          */
  42.         addr &= ~(4096 - 1);
  43.         debug ("Top of RAM usable for U-Boot at: %08lx\n", addr);

  44. ----------------------------------------------------省略----------------------------------------------------

  45.         /*
  46.          * Save local variables to board info struct
  47.          */
  48.         /* 记录内存基址及大小、串口波特率 */
  49.         bd->bi_memstart        = CFG_SDRAM_BASE;        /* start of  DRAM memory */
  50.         bd->bi_memsize        = gd->ram_size;                /* size  of  DRAM memory in bytes */
  51.         bd->bi_baudrate        = gd->baudrate;                /* Console Baudrate */

  52.         memcpy (id, (void *)gd, sizeof (gd_t));

  53.         /* On the purple board we copy the code in a special way
  54.          * in order to solve flash problems
  55.          */
  56. #ifdef CONFIG_PURPLE
  57.         copy_code(addr);
  58. #endif

  59.         /* 执行代码转移 */
  60.         relocate_code (addr_sp, id, addr);

  61.         /* NOTREACHED - relocate_code() does not return */
  62. }
复制代码

board_init_f 的作用为进行内存、时钟、串口等设备的初始化,并为 U-Boot 的运行划分内存。

初始化函数位于 board_init_f 函数上方

  1. init_fnc_t *init_sequence[] = {
  2. #ifndef COMPRESSED_UBOOT
  3.         timer_init,    /* 初始化时钟 */
  4. #endif
  5.         env_init,                /* initialize environment */ /* 初始化环境变量 */
  6. #ifdef CONFIG_INCA_IP
  7.         incaip_set_cpuclk,        /* set cpu clock according to environment variable */
  8. #endif
  9.         init_baudrate,                /* initialze baudrate settings */
  10. #ifndef COMPRESSED_UBOOT
  11.         serial_init,                /* serial communications setup */ /* 初始化串口,这样 TTL 才有输出 */
  12. #endif
  13.         console_init_f,   /* 初始化控制台环境 */
  14.         display_banner,                /* say that we are here */ /* 显示 U-Boot 1.1.4 ..... */
  15. #ifndef COMPRESSED_UBOOT
  16.         checkboard,   /* 显示 AP99 (ar7241 - Virian) U-boot */
  17.         init_func_ram,    /* 初始化内存 */
  18. #endif
  19.         NULL,
  20. };
复制代码

以上为初始化函数列表,依次运行

在初始化完成后,board_init_f 调用 relocate_code 将 U-Boot 代码从 Flash 中复制到内存中,再从内存中执行

回到 cpu/mips/start.S

  1.         .globl        relocate_code
  2.         .ent        relocate_code
  3. relocate_code:
  4.         move        sp, a0                /* Set new stack pointer                */

  5.         li        t0, CFG_MONITOR_BASE
  6.         la        t3, in_ram
  7.         lw        t2, -12(t3)        /* t2 <-- uboot_end_data        */
  8.         move        t1, a2

  9.         /*
  10.          * Fix GOT pointer:
  11.          *
  12.          * New GOT-PTR = (old GOT-PTR - CFG_MONITOR_BASE) + Destination Address
  13.          */
  14.         /* 根据传递的参数计算移动距离 */
  15.         move        t6, gp
  16.         sub        gp, CFG_MONITOR_BASE
  17.         add        gp, a2                        /* gp now adjusted                */
  18.         sub        t6, gp, t6                /* t6 <-- relocation offset        */

  19.         /*
  20.          * t0 = source address
  21.          * t1 = target address
  22.          * t2 = source end address
  23.          */
  24.         /* 复制代码到内存 */
  25. 1:
  26.         lw        t3, 0(t0)
  27.         sw        t3, 0(t1)
  28.         addu        t0, 4
  29.         ble        t0, t2, 1b
  30.         addu        t1, 4                        /* delay slot                        */

  31.         /* If caches were enabled, we would have to flush them here.
  32.          */

  33.         /* Jump to where we've relocated ourselves.
  34.          */
  35.         /* 跳转到移动后的代码继续执行 */
  36.         addi        t0, a2, in_ram - _start
  37.         j        t0
  38.         nop

  39.         .word        uboot_end_data
  40.         .word        uboot_end
  41.         .word        num_got_entries

  42. in_ram:
  43.         /* 此时已经在内存中了 */
  44.         /* Now we want to update GOT.
  45.          */
  46.         lw        t3, -4(t0)        /* t3 <-- num_got_entries        */
  47.         addi        t4, gp, 8        /* Skipping first two entries.        */
  48.         li        t2, 2
  49.         /* GOT 表重定位 */
  50. 1:
  51.         lw        t1, 0(t4)
  52.         beqz        t1, 2f
  53.         add        t1, t6
  54.         sw        t1, 0(t4)
  55. 2:
  56.         addi        t2, 1
  57.         blt        t2, t3, 1b
  58.         addi        t4, 4                /* delay slot                        */

  59.         /* Clear BSS.
  60.          */
  61.         lw        t1, -12(t0)        /* t1 <-- uboot_end_data        */
  62.         lw        t2, -8(t0)        /* t2 <-- uboot_end                */
  63.         add        t1, t6                /* adjust pointers                */
  64.         add        t2, t6

  65.         /* 清除 BSS 段,即全局变量清零 */
  66.         sub        t1, 4
  67. 1:        addi        t1, 4
  68.         bltl        t1, t2, 1b
  69.         sw        zero, 0(t1)        /* delay slot                        */

  70.         /* 移交控制权到 board_init_r */
  71.         move        a0, a1
  72.         la        t9, board_init_r
  73.         j        t9
  74.         move        a1, a2                /* delay slot                        */

  75.         .end        relocate_code
复制代码

relocate_code 的作用是将 U-Boot 代码移动到内存的最高端,并清空 BSS 段,设置栈指针,创造一个完整的 C 环境。

然后 C 环境就绪,进入 board_init_r 继续运行,此时 SPI Flash 不再被使用,U-Boot 完全在内存中运行了

回到 lib_mips/board.c:

  1. void board_init_r (gd_t *id, ulong dest_addr)
  2. {
  3.         cmd_tbl_t *cmdtp;
  4.         ulong size;
  5.         extern void malloc_bin_reloc (void);
  6. #ifndef CFG_ENV_IS_NOWHERE
  7.         extern char * env_name_spec;
  8. #endif
  9.         char *s, *e;
  10.         bd_t *bd;
  11.         int i;

  12.         gd = id;
  13.         gd->flags |= GD_FLG_RELOC;        /* tell others: relocation done */

  14.         debug ("Now running in RAM - U-Boot at: %08lx\n", dest_addr);

  15.         gd->reloc_off = dest_addr - CFG_MONITOR_BASE;  /* 记录 U-Boot 重定位时移动的偏移量 */

  16.         monitor_flash_len = (ulong)&uboot_end_data - dest_addr;

  17.         /*
  18.          * We have to relocate the command table manually
  19.          */
  20.         /* U-Boot 命令表,重定位所有字符串地址和函数地址 */
  21.          for (cmdtp = &__u_boot_cmd_start; cmdtp !=  &__u_boot_cmd_end; cmdtp++) {
  22.                 ulong addr;

  23.                 addr = (ulong) (cmdtp->cmd) + gd->reloc_off;
  24. #if 0
  25.                 printf ("Command "%s": 0x%08lx => 0x%08lx\n",
  26.                                 cmdtp->name, (ulong) (cmdtp->cmd), addr);
  27. #endif
  28.                 cmdtp->cmd =
  29.                         (int (*)(struct cmd_tbl_s *, int, int, char *[]))addr;

  30.                 addr = (ulong)(cmdtp->name) + gd->reloc_off;
  31.                 cmdtp->name = (char *)addr;

  32.                 if (cmdtp->usage) {
  33.                         addr = (ulong)(cmdtp->usage) + gd->reloc_off;
  34.                         cmdtp->usage = (char *)addr;
  35.                 }
  36. #ifdef        CFG_LONGHELP
  37.                 if (cmdtp->help) {
  38.                         addr = (ulong)(cmdtp->help) + gd->reloc_off;
  39.                         cmdtp->help = (char *)addr;
  40.                 }
  41. #endif
  42.         }
  43.         /* there are some other pointer constants we must deal with */
  44. #ifndef CFG_ENV_IS_NOWHERE
  45.         env_name_spec += gd->reloc_off;
  46. #endif

  47.         /* 熄灭路由的 LED */
  48.         /* turn off switch LED added by tiger 20091225 */
  49.         ar7240_gpio_sw_led();

  50. ----------------------------------------------------省略----------------------------------------------------

  51.         /* configure available FLASH banks */
  52.         /* 初始化 Flash,显示 Flash:XXXX (X MB) */
  53.         size = flash_init();
  54.         display_flash_config (size);

  55.         bd = gd->bd;
  56.         bd->bi_flashstart = CFG_FLASH_BASE;
  57.         bd->bi_flashsize = size;
  58. #if CFG_MONITOR_BASE == CFG_FLASH_BASE
  59.         bd->bi_flashoffset = monitor_flash_len;        /* reserved area for U-Boot */
  60. #else
  61.         bd->bi_flashoffset = 0;
  62. #endif

  63.         /* initialize malloc() area */
  64.         /* 初始化内存管理器,以便能够使用 malloc 等函数 */
  65.         mem_malloc_init();
  66.         malloc_bin_reloc();

  67.         /* relocate environment function pointers etc. */
  68.         /* 重定位环境变量相关函数和数据 */
  69.         env_relocate();

  70.         /* board MAC address */
  71.         s = getenv ("ethaddr");
  72.         for (i = 0; i < 6; ++i) {
  73.                 bd->bi_enetaddr[i] = s ? simple_strtoul (s, &e, 16) : 0;
  74.                 if (s)
  75.                         s = (*e) ? e + 1 : e;
  76.         }

  77.         /* IP Address */
  78.         bd->bi_ip_addr = getenv_IPaddr("ipaddr");

  79. #if defined(CONFIG_PCI)
  80.         /*
  81.          * Do pci configuration
  82.          */
  83.         /* PCI 设备初始化 */
  84.         pci_init();
  85. #endif

  86. /** leave this here (after malloc(), environment and PCI are working) **/
  87.         /* Initialize devices */
  88.         devices_init ();

  89.         jumptable_init ();

  90.         /* Initialize the console (after the relocation and devices init) */
  91.         console_init_r ();
  92. /** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **/

  93.         /* Initialize from environment */
  94.         if ((s = getenv ("loadaddr")) != NULL) {
  95.                 load_addr = simple_strtoul (s, NULL, 16);
  96.         }
  97. #if (CONFIG_COMMANDS & CFG_CMD_NET)
  98.         if ((s = getenv ("bootfile")) != NULL) {
  99.                 copy_filename (BootFile, s, sizeof (BootFile));
  100.         }
  101. #endif        /* CFG_CMD_NET */

  102. #if defined(CONFIG_MISC_INIT_R)
  103.         /* miscellaneous platform dependent initialisations */
  104.         misc_init_r ();
  105. #endif

  106. #if (CONFIG_COMMANDS & CFG_CMD_NET)
  107.         /* 初始化网络 */
  108. #if defined(CONFIG_NET_MULTI)
  109.         puts ("Net:   ");
  110. #endif
  111.         eth_initialize(gd->bd);
  112. #endif

  113.         /* main_loop() can return to retry autoboot, if so just run it again. */
  114.         /* 进入命令主循环,永不返回 */
  115.         for (;;) {
  116.                 main_loop ();
  117.         }

  118.         /* NOTREACHED - no way out of command loop except booting */
  119. }
复制代码

到此为止 U-Boot 就开始正常运行了
在处理交互命令前,U-Boot 会检查有没有启动命令

文件:common/main.c

  1. void main_loop (void)
  2. {
  3. ----------------------------------------------------省略----------------------------------------------------

  4. #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
  5.         char *s;
  6.         int bootdelay;
  7. #endif
  8. ----------------------------------------------------省略----------------------------------------------------

  9. #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
  10.         /* 获取启动延时 */
  11.         s = getenv ("bootdelay");
  12.         bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;

  13. //        debug ("### main_loop entered: bootdelay=%d\n\n", bootdelay);

  14. # ifdef CONFIG_BOOT_RETRY_TIME
  15.         init_cmd_timeout ();
  16. # endif        /* CONFIG_BOOT_RETRY_TIME */

  17. ----------------------------------------------------省略----------------------------------------------------

  18.                 /* 获取启动命令 */
  19.                 s = getenv ("bootcmd");
  20.        if (!s) {
  21.             /* 如果没有启动命令,就设置默认的命令 */
  22. #ifdef CONFIG_ROOTFS_FLASH
  23.            /* XXX if rootfs is in flash, expect uImage to be in flash */
  24. #ifdef CONFIG_AR7100
  25.            /* 这里设置的即为 AR71XX 的默认固件启动命令 */
  26.            setenv ("bootcmd", "bootm 0xbf200000");
  27. #else
  28.            setenv ("bootcmd", "bootm 0xbf450000");
  29. #endif /* CONFIG_AR7100 */
  30. #else
  31.            setenv ("bootcmd", "tftpboot 0x8022c090 uImage; bootm 0x8022c090");
  32. #endif
  33.        }
  34.                 /* 重新获取启动命令 */
  35.                 s = getenv ("bootcmd");

  36. //        debug ("### main_loop: bootcmd="%s"\n", s ? s : "<UNDEFINED>");

  37.         if (bootdelay >= 0 && s && !abortboot (bootdelay)) {
  38. # ifdef CONFIG_AUTOBOOT_KEYED
  39.                 int prev = disable_ctrlc(1);        /* disable Control C checking */
  40. # endif

  41. # ifndef CFG_HUSH_PARSER
  42.                 /* 执行启动命令 */
  43.                 run_command (s, 0);
  44. # else
  45.                 parse_string_outer(s, FLAG_PARSE_SEMICOLON |
  46.                                     FLAG_EXIT_FROM_LOOP);
  47. # endif

  48. # ifdef CONFIG_AUTOBOOT_KEYED
  49.                 disable_ctrlc(prev);        /* restore Control C checking */
  50. # endif
  51.         }

  52. ----------------------------------------------------省略----------------------------------------------------
  53. #endif        /* CONFIG_BOOTDELAY */

  54. #ifdef CONFIG_AMIGAONEG3SE
  55.         {
  56.             extern void video_banner(void);
  57.             video_banner();
  58.         }
  59. #endif

  60.         /* 启动命令执行完毕,则开始处理交互命令 */

  61.         /*
  62.          * Main Loop for Monitor Command Processing
  63.          */
  64. #ifdef CFG_HUSH_PARSER
  65.         parse_file_outer();
  66.         /* This point is never reached */
  67.         for (;;);
  68. #else
  69.         for (;;) {
  70. #ifdef CONFIG_BOOT_RETRY_TIME
  71.                 if (rc >= 0) {
  72.                         /* Saw enough of a valid command to
  73.                          * restart the timeout.
  74.                          */
  75.                         reset_cmd_timeout();
  76.                 }
  77. #endif
  78.                 len = readline (CFG_PROMPT);

  79.                 flag = 0;        /* assume no special flags for now */
  80.                 if (len > 0)
  81.                         strcpy (lastcommand, console_buffer);
  82.                 else if (len == 0)
  83.                         flag |= CMD_FLAG_REPEAT;
  84. #ifdef CONFIG_BOOT_RETRY_TIME
  85.                 else if (len == -2) {
  86.                         /* -2 means timed out, retry autoboot
  87.                          */
  88.                         puts ("\nTimed out waiting for command\n");
  89. # ifdef CONFIG_RESET_TO_RETRY
  90.                         /* Reinit board to run initialization code again */
  91.                         do_reset (NULL, 0, 0, NULL);
  92. # else
  93.                         return;                /* retry autoboot */
  94. # endif
  95.                 }
  96. #endif

  97.                 if (len == -1)
  98.                         puts ("<INTERRUPT>\n");
  99.                 else
  100.                         rc = run_command (lastcommand, flag);

  101.                 if (rc <= 0) {
  102.                         /* invalid command or not repeatable, forget it */
  103.                         lastcommand[0] = 0;
  104.                 }
  105.         }
  106. #endif /*CFG_HUSH_PARSER*/
  107. }
复制代码

至此,U-Boot 的启动流程就分析完毕了


相关代码位置:

AR7241 架构代码:

board/ar7240/ap99:
ap99.c 包含 USB 初始化等

board/ar7240/common:
ar7240_flash.c 包含 Flash 初始化代码
ar7240_pci.c 包含 PCI-E 设备初始化代码
ar7240_s26_phy.c:AR724X/AR9331 内置交换机初始化代码
lowlevel_init.S:lowlevel_init 函数所在的代码,用于初始化 CPU 频率

cpu/mips/ar7240:
ag7240.c:网络驱动
ar7240_serial.c:串口初始化及操作代码
meminit.c:内存初始化代码

U-Boot 通用代码:
common:
cmd_*.c: U-Boot 命令代码
env_*.c:环境变量底层模块
command.c:命令解释模块
console.c:控制台模块
dlmalloc.c:内存管理模块
environment.c:环境变量公用模块

net:
eth.c:网络初始化模块
net.c:网络功能实现及调度模块
tftp.c:tftp 功能模块

点评

您好! init_fnc_t *init_sequence[] = { #ifndef COMPRESSED_UBOOT timer_init, #endif env_init, /* initialize environment */ #ifdef CONFIG_INCA_IP incaip_set_cpuclk, /* set cpu clock according  详情 回复 发表于 2015-5-11 23:56
这个真不是一般人能学会的,有点复杂。看了头疼。  发表于 2015-1-22 23:14
这太厉害了 代码也解析了一遍  详情 回复 发表于 2014-11-18 16:48

评分

参与人数 2恩山币 +6 收起 理由
laom*** + 5 ganxiegongxiang
moon*** + 1 霸道

查看全部评分

我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

 楼主| | 显示全部楼层
再占一楼
我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

LZ好霸道,一下占那么多楼
我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

楼主,伟大!非常非常感谢!
我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

LZ厉害
我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

来自手机 | 显示全部楼层
牛逼。。。。
我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

教程终于出来了支持支持
我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

来自手机 | 显示全部楼层
多谢楼主,好贴!
我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

来自手机 | 显示全部楼层
支持楼主
我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

坐等更新
我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

来自手机 | 显示全部楼层
我按教程搭好了环境(ubuntu 13.04,操作系统64位),准备再跟。
我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

来自手机 | 显示全部楼层
感谢楼主分享
我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

感谢楼主做出杰出贡献
我的恩山、我的无线 The best wifi forum is right here.
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

有疑问请添加管理员QQ86788181|手机版|小黑屋|Archiver|恩山无线论坛(常州市恩山计算机开发有限公司版权所有) ( 苏ICP备05084872号 )

GMT+8, 2024-4-27 00:53

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

| 江苏省互联网有害信息举报中心 举报信箱:js12377 | @jischina.com.cn 举报电话:025-88802724 本站不良内容举报信箱:68610888@qq.com 举报电话:0519-86695797

快速回复 返回顶部 返回列表